Sophie

Sophie

distrib > PLD > ac > amd64 > by-pkgid > 950ec4453099b5125884e99014f11757 > files > 18

kernel24-2.4.34-1.src.rpm

32_bttv
diff -up linux-2.4.26/drivers/media/video/Makefile linux/drivers/media/video/Makefile
--- linux-2.4.26/drivers/media/video/Makefile	2004-04-21 14:11:33.000000000 +0200
+++ linux/drivers/media/video/Makefile	2004-04-21 14:11:41.000000000 +0200
@@ -26,17 +26,19 @@ O_TARGET := video.o
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
 
 export-objs     :=	i2c-old.o videodev.o bttv-if.o cpia.o	\
-			v4l2-common.o v4l1-compat.o
+			v4l2-common.o v4l1-compat.o video-buf.o \
+			btcx-risc.o
 
 list-multi	:=	bttv.o zoran.o
-bttv-objs	:=	bttv-driver.o bttv-cards.o bttv-if.o
+bttv-objs	:=	bttv-driver.o bttv-cards.o bttv-if.o \
+			bttv-risc.o bttv-vbi.o bttv-i2c.o
 zoran-objs      :=	zr36120.o zr36120_i2c.o zr36120_mem.o
 
 obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o
 
 obj-$(CONFIG_BUS_I2C) += i2c-old.o
 obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \
-	tda7432.o tda9875.o tda9887.o tuner.o
+	tda7432.o tda9875.o tda9887.o tuner.o video-buf.o btcx-risc.o
 obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
 
 obj-$(CONFIG_VIDEO_ZR36120) += zoran.o i2c-old.o
diff -up linux-2.4.26/drivers/media/video/bt848.h linux/drivers/media/video/bt848.h
--- linux-2.4.26/drivers/media/video/bt848.h	2004-04-21 13:42:41.000000000 +0200
+++ linux/drivers/media/video/bt848.h	2004-04-21 14:11:41.000000000 +0200
@@ -158,6 +158,9 @@
 #define BT848_ADC_C_SLEEP      (1<<1)
 #define BT848_ADC_CRUSH        (1<<0)
 
+#define BT848_WC_UP            0x044
+#define BT848_WC_DOWN          0x078
+
 #define BT848_E_VTC            0x06C
 #define BT848_O_VTC            0x0EC
 #define BT848_VTC_HSFMT        (1<<7)
@@ -282,13 +285,16 @@
 #define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0)
 
 #define BT848_I2C              0x110
+#define BT878_I2C_MODE         (1<<7)
+#define BT878_I2C_RATE         (1<<6)
+#define BT878_I2C_NOSTOP       (1<<5)
+#define BT878_I2C_NOSTART      (1<<4)
 #define BT848_I2C_DIV          (0xf<<4)
 #define BT848_I2C_SYNC         (1<<3)
 #define BT848_I2C_W3B	       (1<<2)
 #define BT848_I2C_SCL          (1<<1)
 #define BT848_I2C_SDA          (1<<0)
 
-
 #define BT848_RISC_STRT_ADD    0x114
 #define BT848_GPIO_OUT_EN      0x118
 #define BT848_GPIO_REG_INP     0x11C
diff -up linux-2.4.26/drivers/media/video/btcx-risc.c linux/drivers/media/video/btcx-risc.c
--- linux-2.4.26/drivers/media/video/btcx-risc.c	2004-04-21 14:11:41.000000000 +0200
+++ linux/drivers/media/video/btcx-risc.c	2004-04-21 14:11:41.000000000 +0200
@@ -0,0 +1,257 @@
+/*
+    btcx-risc.c
+
+    bt848/bt878/cx2388x risc code generator.
+
+    (c) 2000-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    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/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "btcx-risc.h"
+
+MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug = 0;
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)");
+
+/* ---------------------------------------------------------- */
+/* allocate/free risc memory                                  */
+
+static int memcnt;
+
+void btcx_riscmem_free(struct pci_dev *pci,
+		       struct btcx_riscmem *risc)
+{
+	if (NULL == risc->cpu)
+		return;
+	pci_free_consistent(pci, risc->size, risc->cpu, risc->dma);
+	memset(risc,0,sizeof(*risc));
+	if (debug) {
+		memcnt--;
+		printk("btcx: riscmem free [%d]\n",memcnt);
+	}
+}
+
+int btcx_riscmem_alloc(struct pci_dev *pci,
+		       struct btcx_riscmem *risc,
+		       unsigned int size)
+{
+	u32 *cpu;
+	dma_addr_t dma;
+
+	if (NULL != risc->cpu && risc->size < size)
+		btcx_riscmem_free(pci,risc);
+	if (NULL == risc->cpu) {
+		cpu = pci_alloc_consistent(pci, size, &dma);
+		if (NULL == cpu)
+			return -ENOMEM;
+		risc->cpu  = cpu;
+		risc->dma  = dma;
+		risc->size = size;
+		if (debug) {
+			memcnt++;
+			printk("btcx: riscmem alloc size=%d [%d]\n",size,memcnt);
+		}
+	}
+	memset(risc->cpu,0,risc->size);
+	return 0;
+}
+
+/* ---------------------------------------------------------- */
+/* screen overlay helpers                                     */
+
+int
+btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win,
+		  struct v4l2_clip *clips, unsigned int n)
+{
+	if (win->left < 0) {
+		/* left */
+		clips[n].c.left = 0;
+		clips[n].c.top = 0;
+		clips[n].c.width  = -win->left;
+		clips[n].c.height = win->height;
+		n++;
+	}
+	if (win->left + win->width > swidth) {
+		/* right */
+		clips[n].c.left   = swidth - win->left;
+		clips[n].c.top    = 0;
+		clips[n].c.width  = win->width - clips[n].c.left;
+		clips[n].c.height = win->height;
+		n++;
+	}
+	if (win->top < 0) {
+		/* top */
+		clips[n].c.left = 0;
+		clips[n].c.top = 0;
+		clips[n].c.width  = win->width;
+		clips[n].c.height = -win->top;
+		n++;
+	}
+	if (win->top + win->height > sheight) {
+		/* bottom */
+		clips[n].c.left = 0;
+		clips[n].c.top = sheight - win->top;
+		clips[n].c.width  = win->width;
+		clips[n].c.height = win->height - clips[n].c.top;
+		n++;
+	}
+	return n;
+}
+
+int
+btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask)
+{
+	s32 nx,nw,dx;
+	unsigned int i;
+
+	/* fixup window */
+	nx = (win->left + mask) & ~mask;
+	nw = (win->width) & ~mask;
+	if (nx + nw > win->left + win->width)
+		nw -= mask+1;
+	dx = nx - win->left;
+	win->left  = nx;
+	win->width = nw;
+	if (debug)
+		printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n",
+		       win->width, win->height, win->left, win->top, dx);
+
+	/* fixup clips */
+	for (i = 0; i < n; i++) {
+		nx = (clips[i].c.left-dx) & ~mask;
+		nw = (clips[i].c.width) & ~mask;
+		if (nx + nw < clips[i].c.left-dx + clips[i].c.width)
+			nw += mask+1;
+		clips[i].c.left  = nx;
+		clips[i].c.width = nw;
+		if (debug)
+			printk(KERN_DEBUG "btcx:   clip align %dx%d+%d+%d\n",
+			       clips[i].c.width, clips[i].c.height,
+			       clips[i].c.left, clips[i].c.top);
+	}
+	return 0;
+}
+
+void
+btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips)
+{
+	struct v4l2_clip swap;
+	int i,j,n;
+
+	if (nclips < 2)
+		return;
+	for (i = nclips-2; i >= 0; i--) {
+		for (n = 0, j = 0; j <= i; j++) {
+			if (clips[j].c.left > clips[j+1].c.left) {
+				swap = clips[j];
+				clips[j] = clips[j+1];
+				clips[j+1] = swap;
+				n++;
+			}
+		}
+		if (0 == n)
+			break;
+	}
+}
+
+void
+btcx_calc_skips(int line, int width, unsigned int *maxy,
+		struct btcx_skiplist *skips, unsigned int *nskips,
+		const struct v4l2_clip *clips, unsigned int nclips)
+{
+	unsigned int clip,skip;
+	int end,maxline;
+	
+	skip=0;
+	maxline = 9999;
+	for (clip = 0; clip < nclips; clip++) {
+
+		/* sanity checks */
+		if (clips[clip].c.left + clips[clip].c.width <= 0)
+			continue;
+		if (clips[clip].c.left > (signed)width)
+			break;
+		
+		/* vertical range */
+		if (line > clips[clip].c.top+clips[clip].c.height-1)
+			continue;
+		if (line < clips[clip].c.top) {
+			if (maxline > clips[clip].c.top-1)
+				maxline = clips[clip].c.top-1;
+			continue;
+		}
+		if (maxline > clips[clip].c.top+clips[clip].c.height-1)
+			maxline = clips[clip].c.top+clips[clip].c.height-1;
+
+		/* horizontal range */
+		if (0 == skip || clips[clip].c.left > skips[skip-1].end) {
+			/* new one */
+			skips[skip].start = clips[clip].c.left;
+			if (skips[skip].start < 0)
+				skips[skip].start = 0;
+			skips[skip].end = clips[clip].c.left + clips[clip].c.width;
+			if (skips[skip].end > width)
+				skips[skip].end = width;
+			skip++;
+		} else {
+			/* overlaps -- expand last one */
+			end = clips[clip].c.left + clips[clip].c.width;
+			if (skips[skip-1].end < end)
+				skips[skip-1].end = end;
+			if (skips[skip-1].end > width)
+				skips[skip-1].end = width;
+		}
+	}
+	*nskips = skip;
+	*maxy = maxline;
+
+	if (debug) {
+		printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline);
+		for (skip = 0; skip < *nskips; skip++) {
+			printk(" %d-%d",skips[skip].start,skips[skip].end);
+		}
+		printk("\n");
+	}
+}
+
+/* ---------------------------------------------------------- */
+
+EXPORT_SYMBOL(btcx_riscmem_alloc);
+EXPORT_SYMBOL(btcx_riscmem_free);
+
+EXPORT_SYMBOL(btcx_screen_clips);
+EXPORT_SYMBOL(btcx_align);
+EXPORT_SYMBOL(btcx_sort_clips);
+EXPORT_SYMBOL(btcx_calc_skips);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -up linux-2.4.26/drivers/media/video/btcx-risc.h linux/drivers/media/video/btcx-risc.h
--- linux-2.4.26/drivers/media/video/btcx-risc.h	2004-04-21 14:11:41.000000000 +0200
+++ linux/drivers/media/video/btcx-risc.h	2004-04-21 14:11:41.000000000 +0200
@@ -0,0 +1,33 @@
+
+struct btcx_riscmem {
+	unsigned int   size;
+	u32            *cpu;
+	u32            *jmp;
+	dma_addr_t     dma;
+};
+
+struct btcx_skiplist {
+	int start;
+	int end;
+};
+
+int  btcx_riscmem_alloc(struct pci_dev *pci,
+			struct btcx_riscmem *risc,
+			unsigned int size);
+void btcx_riscmem_free(struct pci_dev *pci,
+		       struct btcx_riscmem *risc);
+
+int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win,
+		      struct v4l2_clip *clips, unsigned int n);
+int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips,
+	       unsigned int n, int mask);
+void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips);
+void btcx_calc_skips(int line, int width, unsigned int *maxy,
+		     struct btcx_skiplist *skips, unsigned int *nskips,
+		     const struct v4l2_clip *clips, unsigned int nclips);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -up linux-2.4.26/drivers/media/video/bttv-cards.c linux/drivers/media/video/bttv-cards.c
--- linux-2.4.26/drivers/media/video/bttv-cards.c	2004-04-21 13:44:00.000000000 +0200
+++ linux/drivers/media/video/bttv-cards.c	2004-04-21 14:11:41.000000000 +0200
@@ -53,8 +53,11 @@ static void winview_audio(struct bttv *b
 static void lt9415_audio(struct bttv *btv, struct video_audio *v, int set);
 static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v,
 				    int set);
+static void avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v,
+				      int set);
 static void terratv_audio(struct bttv *btv, struct video_audio *v, int set);
 static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set);
+static void gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set);
 static void winfast2000_audio(struct bttv *btv, struct video_audio *v, int set);
 static void pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set);
 static void fv2000s_audio(struct bttv *btv, struct video_audio *v, int set);
@@ -63,6 +66,10 @@ static void adtvk503_audio(struct bttv *
 static void rv605_muxsel(struct bttv *btv, unsigned int input);
 static void eagle_muxsel(struct bttv *btv, unsigned int input);
 static void xguard_muxsel(struct bttv *btv, unsigned int input);
+static void ivc120_muxsel(struct bttv *btv, unsigned int input);
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input);
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input);
 
 static int terratec_active_radio_upgrade(struct bttv *btv);
 static int tea5757_read(struct bttv *btv);
@@ -76,10 +83,11 @@ static unsigned int vsfx=0;
 static unsigned int latency = UNSET;
 unsigned int no_overlay=-1;
 
-static unsigned int card[BTTV_MAX]  = { [ 0 ... (BTTV_MAX-1) ] = UNSET};
-static unsigned int pll[BTTV_MAX]   = { [ 0 ... (BTTV_MAX-1) ] = UNSET};
-static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET};
-static unsigned int svhs[BTTV_MAX]  = { [ 0 ... (BTTV_MAX-1) ] = UNSET};
+static unsigned int card[BTTV_MAX]   = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int pll[BTTV_MAX]    = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int tuner[BTTV_MAX]  = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int svhs[BTTV_MAX]   = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
 #ifdef MODULE
 static unsigned int autoload = 1;
 #else
@@ -107,9 +115,13 @@ MODULE_PARM(tuner,"1-" __stringify(BTTV_
 MODULE_PARM_DESC(tuner,"specify installed tuner type");
 MODULE_PARM(autoload,"i");
 MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)");
+
+MODULE_PARM(svhs,"1-" __stringify(BTTV_MAX) "i");
+MODULE_PARM(remote,"1-" __stringify(BTTV_MAX) "i");
+
 MODULE_PARM(gpiomask,"i");
 MODULE_PARM(audioall,"i");
-MODULE_PARM(audiomux,"1-5i");
+MODULE_PARM(audiomux,"1-6i");
 
 /* kernel args */
 #ifndef MODULE
@@ -154,12 +166,13 @@ static struct CARD {
 	{ 0x00031002, BTTV_ATI_TVWONDERVE,"ATI TV Wonder/VE" },
 
 	{ 0x6606107d, BTTV_WINFAST2000,   "Leadtek WinFast TV 2000" },
-	{ 0x6607107d, BTTV_WINFAST2000,   "Leadtek WinFast VC 100" },
+	{ 0x6607107d, BTTV_WINFASTVC100,  "Leadtek WinFast VC 100" },
 	{ 0x263610b4, BTTV_STB2,          "STB TV PCI FM, Gateway P/N 6000704" },
 	{ 0x264510b4, BTTV_STB2,          "STB TV PCI FM, Gateway P/N 6000704" },
  	{ 0x402010fc, BTTV_GVBCTV3PCI,    "I-O Data Co. GV-BCTV3/PCI" },
 	{ 0x405010fc, BTTV_GVBCTV4PCI,    "I-O Data Co. GV-BCTV4/PCI" },
 	{ 0x407010fc, BTTV_GVBCTV5PCI,    "I-O Data Co. GV-BCTV5/PCI" },
+ 	{ 0xd01810fc, BTTV_GVBCTV5PCI,    "I-O Data Co. GV-BCTV5/PCI" },
 
 	{ 0x001211bd, BTTV_PINNACLE,      "Pinnacle PCTV" },
 	{ 0x001c11bd, BTTV_PINNACLESAT,   "Pinnacle PCTV Sat" },
@@ -174,16 +187,15 @@ static struct CARD {
 	{ 0x3002144f, BTTV_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH05x" },
 	{ 0x3005144f, BTTV_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH061/06L (T1/LC)" },
 	{ 0x5000144f, BTTV_MAGICTVIEW061, "Askey CPH050" },
-	
+	{ 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" },
+	{ 0x300214ff, BTTV_PHOEBE_TVMAS,  "Phoebe TV Master (CPH060)" },
+
 	{ 0x00011461, BTTV_AVPHONE98,     "AVerMedia TVPhone98" },
 	{ 0x00021461, BTTV_AVERMEDIA98,   "AVermedia TVCapture 98" },
 	{ 0x00031461, BTTV_AVPHONE98,     "AVerMedia TVPhone98" },
 	{ 0x00041461, BTTV_AVERMEDIA98,   "AVerMedia TVCapture 98" },
 	{ 0x03001461, BTTV_AVERMEDIA98,   "VDOMATE TV TUNER CARD" },
 
-	{ 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" },
-	{ 0x300214ff, BTTV_PHOEBE_TVMAS,  "Phoebe TV Master (CPH060)" },
-
 	{ 0x1117153b, BTTV_TERRATVALUE,   "Terratec TValue (Philips PAL B/G)" },
 	{ 0x1118153b, BTTV_TERRATVALUE,   "Terratec TValue (Temic PAL B/G)" },
 	{ 0x1119153b, BTTV_TERRATVALUE,   "Terratec TValue (Philips PAL I)" },
@@ -195,7 +207,8 @@ static struct CARD {
 	//{ 0x18521852, BTTV_TERRATV,     "Terratec TV+ (V1.10)"    },
 	{ 0x1134153b, BTTV_TERRATVALUE,   "Terratec TValue (LR102)" },
 	{ 0x1135153b, BTTV_TERRATVALUER,  "Terratec TValue Radio" }, // LR102
-	{ 0x5018153b, BTTV_TERRATVALUE,   "Terratec TValue" }, // ??
+	{ 0x5018153b, BTTV_TERRATVALUE,   "Terratec TValue" },       // ??
+	{ 0xff3b153b, BTTV_TERRATVALUER,  "Terratec TValue Radio" }, // ??
 
 	{ 0x400015b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
 	{ 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
@@ -218,14 +231,31 @@ static struct CARD {
 	{ 0x1466aa06, BTTV_PV150,         "Provideo PV150B-3" },
 	{ 0x1467aa07, BTTV_PV150,         "Provideo PV150B-4" },
 
-	{ 0xa1550000, BTTV_IVC200,        "IVC-200" },
-	{ 0xa1550001, BTTV_IVC200,        "IVC-200" },
-	{ 0xa1550002, BTTV_IVC200,        "IVC-200" },
-	{ 0xa1550003, BTTV_IVC200,        "IVC-200" },	
+	{ 0xa132ff00, BTTV_IVC100,        "IVC-100"  },
+	{ 0xa1550000, BTTV_IVC200,        "IVC-200"  },
+	{ 0xa1550001, BTTV_IVC200,        "IVC-200"  },
+	{ 0xa1550002, BTTV_IVC200,        "IVC-200"  },
+	{ 0xa1550003, BTTV_IVC200,        "IVC-200"  },	
 	{ 0xa1550100, BTTV_IVC200,        "IVC-200G" },
 	{ 0xa1550101, BTTV_IVC200,        "IVC-200G" },
 	{ 0xa1550102, BTTV_IVC200,        "IVC-200G" },
 	{ 0xa1550103, BTTV_IVC200,        "IVC-200G" },
+	{ 0xa182ff00, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff01, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff02, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff03, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff04, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff05, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff06, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff07, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff08, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff09, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff0a, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff0b, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff0c, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff0d, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff0e, BTTV_IVC120,        "IVC-120G" },
+	{ 0xa182ff0f, BTTV_IVC120,        "IVC-120G" },
 	
 	{ 0x41424344, BTTV_GRANDTEC,      "GrandTec Multi Capture" },
 	{ 0x01020304, BTTV_XGUARD,        "Grandtec Grand X-Guard" },
@@ -235,16 +265,30 @@ static struct CARD {
 	{ 0x18501851, BTTV_CHRONOS_VS2,   "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
 	{ 0x18511851, BTTV_FLYVIDEO98EZ,  "FlyVideo 98EZ (LR51)/ CyberMail AV" },
 	{ 0x18521852, BTTV_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" },
+	{ 0x41a0a051, BTTV_FLYVIDEO_98FM, "Lifeview FlyVideo 98 LR50 Rev Q" },
 	{ 0x10b42636, BTTV_HAUPPAUGE878,  "STB ???" },
 	{ 0x217d6606, BTTV_WINFAST2000,   "Leadtek WinFast TV 2000" },
+	{ 0xfff6f6ff, BTTV_WINFAST2000,   "Leadtek WinFast TV 2000" },
 	{ 0x03116000, BTTV_SENSORAY311,   "Sensoray 311" },
 	{ 0x00790e11, BTTV_WINDVR,        "Canopus WinDVR PCI" },
 	{ 0xa0fca1a0, BTTV_ZOLTRIX,       "Face to Face Tvmax" },
-	{ 0x01010071, BTTV_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
+	{ 0x20007063, BTTV_PC_HDTV,       "pcHDTV HD-2000 TV"},
+	{ 0x82b2aa6a, BTTV_SIMUS_GVC1100, "SIMUS GVC1100" },
+	{ 0x146caa0c, BTTV_PV951,         "ituner spectra8" },
+ 	{ 0x200a1295, BTTV_PXC200,        "ImageNation PXC200A" },
+
+	{ 0x40111554, BTTV_PV_BT878P_9B,  "Prolink Pixelview PV-BT" },
+	{ 0x17de0a01, BTTV_KWORLD,        "Mecer TV/FM/Video Tuner" },
 
 	// likely broken, vendor id doesn't match the other magic views ...
 	//{ 0xa0fca04f, BTTV_MAGICTVIEW063, "Guillemot Maxi TV Video 3" },
 	
+	// DVB cards (using pci function .1 for mpeg data xfer)
+	{ 0x01010071, BTTV_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
+	{ 0x002611bd, BTTV_TWINHAN_DST,   "Pinnacle PCTV SAT CI" },
+	{ 0x00011822, BTTV_TWINHAN_DST,   "Twinhan VisionPlus DVB-T" },
+	{ 0xfc00270f, BTTV_TWINHAN_DST,   "ChainTech digitop DST-1000 DVB-S" },
+	
 	{ 0, -1, NULL }
 };
 
@@ -334,6 +378,7 @@ struct tvcard bttv_tvcards[] = {
 	.needs_tvaudio	= 1,
 	.tuner_type	= -1,
 	.audio_hook	= avermedia_tvphone_audio,
+	.has_remote     = 1,
 },{
 	.name		= "MATRIX-Vision MV-Delta",
 	.video_inputs	= 5,
@@ -420,6 +465,7 @@ struct tvcard bttv_tvcards[] = {
 	.msp34xx_alt    = 1,
 	.pll		= PLL_28,
 	.tuner_type	= TUNER_PHILIPS_PAL,
+	.audio_hook     = avermedia_tv_stereo_audio,
 },{
 	.name		= "Aimslab Video Highway Xtreme (VHX)",
 	.video_inputs	= 3,
@@ -453,7 +499,13 @@ struct tvcard bttv_tvcards[] = {
 	.svhs		= 2,
 	.gpiomask	= 0x01fe00,
 	.muxsel		= { 2, 3, 1, 1},
+#if 0
+	// old
 	.audiomux	= { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 },
+#else
+	// 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru>
+	.audiomux       = { 0x001e00, 0, 0x018000, 0x014000, 0x002000, 0 },
+#endif
 	.needs_tvaudio	= 1,
 	.pll		= PLL_28,
 	.tuner_type	= -1,
@@ -552,6 +604,7 @@ struct tvcard bttv_tvcards[] = {
 	.needs_tvaudio	= 1,
 	.pll		= PLL_28,
 	.tuner_type	= -1,
+	.has_remote     = 1,
 },{
 	.name           = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar",
 	.video_inputs	= 3,
@@ -643,6 +696,8 @@ struct tvcard bttv_tvcards[] = {
 	.audiomux	= { 0 },
 	.needs_tvaudio	= 1,
 	.tuner_type	= -1,
+	.muxsel_hook    = PXC200_muxsel,
+
 },{
 	.name		= "Lifeview FlyVideo 98 LR50",
 	.video_inputs	= 4,
@@ -696,9 +751,15 @@ struct tvcard bttv_tvcards[] = {
 	.audio_inputs	= 1,
 	.tuner		= 0,
 	.svhs		= 2,
-	.gpiomask	= 0xc33000,
 	.muxsel		= { 2, 3, 1, 1, 0}, // TV, CVid, SVid, CVid over SVid connector
-	.audiomux	= { 0x422000,0x1000,0x0000,0x620000,0x800000},
+#if 0
+	.gpiomask	= 0xc33000,
+	.audiomux	= { 0x422000,0x1000,0x0000,0x620000,0x800000 },
+#else
+	/* Alexander Varakin <avarakin@hotmail.com> [stereo version] */
+	.gpiomask	= 0xb33000,
+	.audiomux	= { 0x122000,0x1000,0x0000,0x620000,0x800000 },
+#endif
 	/* Audio Routing for "WinFast 2000 XP" (no tv stereo !)
 		gpio23 -- hef4052:nEnable (0x800000)
 		gpio12 -- hef4052:A1
@@ -715,6 +776,7 @@ struct tvcard bttv_tvcards[] = {
 	.has_radio	= 1,
 	.tuner_type	= 5, // default for now, gpio reads BFFF06 for Pal bg+dk
 	.audio_hook	= winfast2000_audio,
+	.has_remote     = 1,
 },{
 	.name		= "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II",
 	.video_inputs	= 4,
@@ -764,6 +826,7 @@ struct tvcard bttv_tvcards[] = {
 	.needs_tvaudio	= 1,
 	.pll		= PLL_28,
 	.tuner_type	= 1,
+	.has_remote     = 1,
 },{
 	.name		= "Pinnacle PCTV Studio/Rave",
 	.video_inputs	= 3,
@@ -772,8 +835,8 @@ struct tvcard bttv_tvcards[] = {
 	.svhs		= 2,
 	.gpiomask	= 0x03000F,
 	.muxsel		= { 2, 3, 1, 1},
-	.audiomux	= { 2, 0, 0, 0, 1},
-	.needs_tvaudio	= 1,
+	.audiomux	= { 2, 0xd0001, 0, 0, 1},
+	.needs_tvaudio	= 0,
 	.pll		= PLL_28,
 	.tuner_type	= -1,
 },{
@@ -922,6 +985,7 @@ struct tvcard bttv_tvcards[] = {
 	.no_msp34xx	= 1,
 	.pll		= PLL_28,
 	.tuner_type	= TUNER_PHILIPS_PAL_I,
+	.has_remote	= 1,
 	/* GPIO wiring: (different from Rev.4C !)
 		GPIO17: U4.A0 (first hef4052bt)
 		GPIO19: U4.A1
@@ -963,14 +1027,14 @@ struct tvcard bttv_tvcards[] = {
 			   MUX2 (mask 0x30000):
 				0,2,3= from MSP34xx
 				1= FM stereo Radio from Tuner */
-	.needs_tvaudio  = 1,
+	.needs_tvaudio  = 0,
 	.pll            = PLL_28,
 	.tuner_type     = -1,
 },{
 	/* Claas Langbehn <claas@bigfoot.com>,
 	   Sven Grothklags <sven@upb.de> */
 	.name		= "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS",
-	.video_inputs	= 3,
+	.video_inputs	= 4,
 	.audio_inputs	= 3,
 	.tuner		= 0,
 	.svhs		= 2,
@@ -979,7 +1043,7 @@ struct tvcard bttv_tvcards[] = {
 	.audiomux	= { 0, 0, 0x10, 8, 4 },
 	.needs_tvaudio	= 1,
 	.pll		= PLL_28,
-	.tuner_type	= TUNER_PHILIPS_PAL_I,
+	.tuner_type	= TUNER_PHILIPS_PAL,
 	.has_radio	= 1,
 },{
 	/* Tim Röstermundt <rosterm@uni-muenster.de>
@@ -1230,6 +1294,7 @@ struct tvcard bttv_tvcards[] = {
         .needs_tvaudio  = 1,
         .pll            = PLL_28,
         .tuner_type     = 25,
+	.has_remote     = 1,
 	/* GPIO wiring:
 		GPIO0: U4.A0 (hef4052bt)
 		GPIO1: U4.A1
@@ -1265,6 +1330,7 @@ struct tvcard bttv_tvcards[] = {
 	.tuner_type	= 5,
 	.audio_hook	= pvbt878p9b_audio, // Note: not all cards have stereo
 	.has_radio	= 1,  // Note: not all cards have radio
+	.has_remote     = 1,
 	/* GPIO wiring:
 		GPIO0: A0 hef4052
 		GPIO1: A1 hef4052
@@ -1400,11 +1466,11 @@ struct tvcard bttv_tvcards[] = {
 	.svhs           = 2,
 	.gpiomask       = 0x0f0f80,
 	.muxsel         = {2, 3, 1, 0},
-	.audiomux       = {0x030000, 0x010000, 0x030000, 0, 0x020000, 0},
+	.audiomux       = {0x030000, 0x010000, 0, 0, 0x020000, 0},
 	.no_msp34xx     = 1,
 	.pll            = PLL_28,
 	.tuner_type     = TUNER_PHILIPS_NTSC_M,
-	.audio_hook     = gvbctv3pci_audio,
+	.audio_hook     = gvbctv5pci_audio,
 	.has_radio      = 1,
 },{
 	.name           = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */
@@ -1726,16 +1792,17 @@ struct tvcard bttv_tvcards[] = {
 
 	/* ---- card 0x68 ---------------------------------- */
 	.name           = "Nebula Electronics DigiTV",
-	.video_inputs   = 0,
-	.audio_inputs   = 0,
+	.video_inputs   = 1,
+        .tuner          = -1,
 	.svhs           = -1,
 	.muxsel         = { 2, 3, 1, 0},
-	.needs_tvaudio  = 0,
 	.no_msp34xx     = 1,
 	.no_tda9875     = 1,
 	.no_tda7432     = 1,
 	.pll            = PLL_28,
 	.tuner_type     = -1,
+	.has_dvb        = 1,
+	.no_gpioirq     = 1,
 },{
 	/* Jorge Boncompte - DTI2 <jorge@dti2.net> */
 	.name           = "ProVideo PV143",
@@ -1807,6 +1874,144 @@ struct tvcard bttv_tvcards[] = {
 	.needs_tvaudio  = 1,
 	.pll            = PLL_28,
 	.tuner_type     = -1,
+},{
+        .name           = "IVC-100",
+        .video_inputs   = 4,
+        .audio_inputs   = 0,
+        .tuner          = -1,
+        .tuner_type     = -1,
+        .svhs           = -1,
+        .gpiomask       = 0xdf,
+        .muxsel         = { 2, 3, 1, 0 },
+        .pll            = PLL_28,
+},{
+	/* IVC-120G - Alan Garfield <alan@fromorbit.com> */
+	.name           = "IVC-120G",
+	.video_inputs   = 16,
+	.audio_inputs   = 0,    /* card has no audio */
+	.tuner          = -1,   /* card has no tuner */
+	.tuner_type     = -1,
+	.svhs           = -1,   /* card has no svhs */
+	.needs_tvaudio  = 0,
+	.no_msp34xx     = 1,
+	.no_tda9875     = 1,
+	.no_tda7432     = 1,
+	.gpiomask       = 0x00,
+	.muxsel         = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 
+			    0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 },
+	.muxsel_hook    = ivc120_muxsel,
+	.pll            = PLL_28,
+},{
+
+	/* ---- card 0x70 ---------------------------------- */
+	.name           = "pcHDTV HD-2000 TV",
+	.video_inputs   = 4,
+	.audio_inputs   = 1,
+	.tuner          = 0,
+	.svhs           = 2,
+	.muxsel         = { 2, 3, 1, 0},
+	.tuner_type     = TUNER_PHILIPS_ATSC,
+},{
+	.name           = "Twinhan DST + clones",
+	.no_msp34xx     = 1,
+	.no_tda9875     = 1,
+	.no_tda7432     = 1,
+	.tuner_type     = TUNER_ABSENT,
+	.no_video       = 1,
+	.has_dvb        = 1,
+},{
+        .name           = "Winfast VC100",
+	.video_inputs   = 3,
+	.audio_inputs   = 0,
+	.svhs           = 1,
+	.tuner          = -1, // no tuner
+	.muxsel         = { 3, 1, 1, 3}, // Vid In, SVid In, Vid over SVid in connector
+        .no_msp34xx     = 1,
+        .no_tda9875     = 1,
+        .no_tda7432     = 1,
+        .tuner_type     = TUNER_ABSENT,
+        .no_video       = 1,
+	.pll            = PLL_28,
+},{
+	.name           = "Teppro TEV-560/InterVision IV-560",
+	.video_inputs   = 3,
+	.audio_inputs   = 1,
+	.tuner          = 0,
+	.svhs           = 2,
+	.gpiomask       = 3,
+	.muxsel         = { 2, 3, 1, 1},
+	.audiomux       = { 1, 1, 1, 1, 0},
+	.needs_tvaudio  = 1,
+	.tuner_type     = TUNER_PHILIPS_PAL,
+	.pll            = PLL_35,
+},{
+
+	/* ---- card 0x74 ---------------------------------- */
+        .name           = "SIMUS GVC1100",
+        .video_inputs   = 4,
+        .audio_inputs   = 0,
+        .tuner          = -1,
+        .svhs           = -1,
+        .tuner_type     = -1,
+        .pll            = PLL_28,
+        .muxsel         = { 2, 2, 2, 2},
+        .gpiomask       = 0x3F,
+	.muxsel_hook    = gvc1100_muxsel,
+},{
+        /* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */
+        .name           = "NGS NGSTV+",
+        .video_inputs   = 3,
+        .tuner          = 0,
+        .svhs           = 2,
+        .gpiomask       = 0x008007,
+        .muxsel         = {2, 3, 0, 0},
+        .audiomux       = {0, 0, 0, 0, 0x000003, 0},
+        .pll            = PLL_28,
+        .tuner_type     = TUNER_PHILIPS_PAL,
+        .has_remote     = 1,
+},{
+        /* http://linuxmedialabs.com */
+        .name           = "LMLBT4",
+        .video_inputs   = 4, /* IN1,IN2,IN3,IN4 */
+        .audio_inputs   = 0,
+        .tuner          = -1,
+        .svhs           = -1,
+        .muxsel         = { 2, 3, 1, 0 },
+        .no_msp34xx     = 1,
+        .no_tda9875     = 1,
+        .no_tda7432     = 1,
+        .needs_tvaudio  = 0,
+},{
+	/* Helmroos Harri <harri.helmroos@pp.inet.fi> */
+	.name           = "Tekram M205 PRO",
+	.video_inputs   = 3,
+	.audio_inputs   = 1,
+	.tuner          = 0,
+	.tuner_type     = TUNER_PHILIPS_PAL,
+	.svhs           = 2,
+	.needs_tvaudio  = 0,
+	.gpiomask       = 0x68,
+	.muxsel         = { 2, 3, 1},
+	.audiomux       = { 0x68, 0x68, 0x61, 0x61, 0x00 },
+	.pll            = PLL_28,
+},{
+
+	/* ---- card 0x78 ---------------------------------- */
+	/* Javier Cendan Ares <jcendan@lycos.es> */
+	/* bt878 TV + FM without subsystem ID */
+	.name           = "Conceptronic CONTVFMi",
+	.video_inputs   = 3,
+	.audio_inputs   = 1,
+	.tuner          = 0,
+	.svhs           = 2,
+	.gpiomask       = 0x008007,
+	.muxsel         = { 2, 3, 1, 1 },
+	.audiomux       = { 0, 1, 2, 2, 3 },
+	.needs_tvaudio  = 0,
+	.pll            = PLL_28,
+	.tuner_type     = TUNER_PHILIPS_PAL,
+	.has_remote     = 1,
+	.has_radio      = 1,
 }};
 
 const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
@@ -1825,9 +2030,9 @@ void __devinit bttv_idcard(struct bttv *
 	unsigned short tmp;
 
 	/* read PCI subsystem ID */
-	pci_read_config_word(btv->dev, PCI_SUBSYSTEM_ID, &tmp);
+	pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_ID, &tmp);
 	btv->cardid = tmp << 16;
-	pci_read_config_word(btv->dev, PCI_SUBSYSTEM_VENDOR_ID, &tmp);
+	pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_VENDOR_ID, &tmp);
 	btv->cardid |= tmp;
 
 	if (0 != btv->cardid && 0xffffffff != btv->cardid) {
@@ -1840,14 +2045,14 @@ void __devinit bttv_idcard(struct bttv *
 			/* found it */
 			printk(KERN_INFO "bttv%d: detected: %s [card=%d], "
 			       "PCI subsystem ID is %04x:%04x\n",
-			       btv->nr,cards[type].name,cards[type].cardnr,
+			       btv->c.nr,cards[type].name,cards[type].cardnr,
 			       btv->cardid & 0xffff,
 			       (btv->cardid >> 16) & 0xffff);
-			btv->type = cards[type].cardnr;
+			btv->c.type = cards[type].cardnr;
 		} else {
 			/* 404 */
 			printk(KERN_INFO "bttv%d: subsystem: %04x:%04x (UNKNOWN)\n",
-			       btv->nr, btv->cardid & 0xffff,
+			       btv->c.nr, btv->cardid & 0xffff,
 			       (btv->cardid >> 16) & 0xffff);
 			printk(KERN_DEBUG "please mail id, board name and "
 			       "the correct card= insmod option to kraxel@bytesex.org\n");
@@ -1855,13 +2060,13 @@ void __devinit bttv_idcard(struct bttv *
 	} 
 
 	/* let the user override the autodetected type */
-	if (card[btv->nr] < bttv_num_tvcards)
-		btv->type=card[btv->nr];
+	if (card[btv->c.nr] < bttv_num_tvcards)
+		btv->c.type=card[btv->c.nr];
 	
 	/* print which card config we are using */
-	printk(KERN_INFO "bttv%d: using: %s [card=%d,%s]\n",btv->nr,
-	       bttv_tvcards[btv->type].name, btv->type,
-	       card[btv->nr] < bttv_num_tvcards
+	printk(KERN_INFO "bttv%d: using: %s [card=%d,%s]\n",btv->c.nr,
+	       bttv_tvcards[btv->c.type].name, btv->c.type,
+	       card[btv->c.nr] < bttv_num_tvcards
 	       ? "insmod option" : "autodetected");
 
 	/* overwrite gpio stuff ?? */
@@ -1871,20 +2076,20 @@ void __devinit bttv_idcard(struct bttv *
 	if (UNSET != audiomux[0]) {
 		gpiobits = 0;
 		for (i = 0; i < 5; i++) {
-			bttv_tvcards[btv->type].audiomux[i] = audiomux[i];
+			bttv_tvcards[btv->c.type].audiomux[i] = audiomux[i];
 			gpiobits |= audiomux[i];
 		}
 	} else {
 		gpiobits = audioall;
 		for (i = 0; i < 5; i++) {
-			bttv_tvcards[btv->type].audiomux[i] = audioall;
+			bttv_tvcards[btv->c.type].audiomux[i] = audioall;
 		}
 	}
-	bttv_tvcards[btv->type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
+	bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits;
 	printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=",
-	       btv->nr,bttv_tvcards[btv->type].gpiomask);
+	       btv->c.nr,bttv_tvcards[btv->c.type].gpiomask);
 	for (i = 0; i < 5; i++) {
-		printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->type].audiomux[i]);
+		printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].audiomux[i]);
 	}
 	printk("\n");
 }
@@ -1898,7 +2103,7 @@ void identify_by_eeprom(struct bttv *btv
 {
 	int type = -1;
 	
-	if (0 == strncmp(eeprom_data,"GET.MM20xPCTV",13))
+	if (0 == strncmp(eeprom_data,"GET MM20xPCTV",13))
 		type = BTTV_MODTEC_205;
 	else if (0 == strncmp(eeprom_data+20,"Picolo",7))
 		type = BTTV_EURESYS_PICOLO;
@@ -1906,22 +2111,22 @@ void identify_by_eeprom(struct bttv *btv
                 type = BTTV_HAUPPAUGE; /* old bt848 */
 
 	if (-1 != type) {
-		btv->type = type;
+		btv->c.type = type;
 		printk("bttv%d: detected by eeprom: %s [card=%d]\n",
-		       btv->nr, bttv_tvcards[btv->type].name, btv->type);
+		       btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type);
 	}
 }
 
 static void flyvideo_gpio(struct bttv *btv)
 { 
-	int gpio,outbits,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821;
+	int gpio,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821;
 	int tuner=-1,ttype;
-	
-	outbits = btread(BT848_GPIO_OUT_EN);
-	btwrite(0x00, BT848_GPIO_OUT_EN);
+
+	gpio_inout(0xffffff, 0);
 	udelay(8);  // without this we would see the 0x1800 mask
-	gpio=btread(BT848_GPIO_DATA);
-	btwrite(outbits, BT848_GPIO_OUT_EN);
+	gpio = gpio_read();
+	/* FIXME: must restore OUR_EN ??? */
+
 	// all cards provide GPIO info, some have an additional eeprom
 	// LR50: GPIO coding can be found lower right CP1 .. CP9
 	//       CP9=GPIO23 .. CP1=GPIO15; when OPEN, the corresponding GPIO reads 1.
@@ -1946,7 +2151,7 @@ static void flyvideo_gpio(struct bttv *b
 	case 0xC: tuner=3; // Philips SECAM(+PAL) FQ1216ME or FI1216MF
 		break;
 	default:
-		printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->nr);
+		printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->c.nr);
 	}
 
 	has_remote          =   gpio & 0x800000;
@@ -1962,9 +2167,9 @@ static void flyvideo_gpio(struct bttv *b
 		tuner=4; // No tuner present 
 
 	printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", 
-	       btv->nr, has_radio? "yes":"no ", has_remote? "yes":"no ", tuner, gpio); 
+	       btv->c.nr, has_radio? "yes":"no ", has_remote? "yes":"no ", tuner, gpio); 
 	printk(KERN_INFO "bttv%d: FlyVideo  LR90=%s tda9821/tda9820=%s capture_only=%s\n",
-		btv->nr, is_lr90?"yes":"no ", has_tda9820_tda9821?"yes":"no ", 
+		btv->c.nr, is_lr90?"yes":"no ", has_tda9820_tda9821?"yes":"no ", 
 		is_capture_only?"yes":"no ");
 
 	if(tuner!= -1) // only set if known tuner autodetected, else let insmod option through
@@ -1988,8 +2193,8 @@ static void miro_pinnacle_gpio(struct bt
 	int id,msp,gpio;
 	char *info;
 
-	btwrite(0,BT848_GPIO_OUT_EN);
-        gpio = btread(BT848_GPIO_DATA);
+	gpio_inout(0xffffff, 0);
+        gpio = gpio_read();
 	id   = ((gpio>>10) & 63) -1;
 	msp  = bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx");
 	if (id < 32) {
@@ -2008,14 +2213,14 @@ static void miro_pinnacle_gpio(struct bt
 			btv->has_radio = 0;
 		}
 		if (-1 != msp) {
-			if (btv->type == BTTV_MIRO)
-				btv->type = BTTV_MIROPRO;
-			if (btv->type == BTTV_PINNACLE)
-				btv->type = BTTV_PINNACLEPRO;
+			if (btv->c.type == BTTV_MIRO)
+				btv->c.type = BTTV_MIROPRO;
+			if (btv->c.type == BTTV_PINNACLE)
+				btv->c.type = BTTV_PINNACLEPRO;
 		}
 		printk(KERN_INFO
 		       "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
-		       btv->nr, id+1, btv->tuner_type,
+		       btv->c.nr, id+1, btv->tuner_type,
 		       !btv->has_radio ? "no" :
 		       (btv->has_matchbox ? "matchbox" : "fmtuner"),
 		       (-1 == msp) ? "no" : "yes");
@@ -2044,15 +2249,18 @@ static void miro_pinnacle_gpio(struct bt
 		case 6:
 			info = "NTSC / stereo";
 			break;
+		case 7:
+			info = "PAL / stereo";
+			break;
 		default:
 			info = "oops: unknown card";
 			break;
 		}
 		if (-1 != msp)
-			btv->type = BTTV_PINNACLEPRO;
+			btv->c.type = BTTV_PINNACLEPRO;
 		printk(KERN_INFO
 		       "bttv%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n",
-		       btv->nr, id, info, btv->has_radio ? "yes" : "no");
+		       btv->c.nr, id, info, btv->has_radio ? "yes" : "no");
 		btv->tuner_type  = 33;
 		btv->pinnacle_id = id;
 	}
@@ -2063,16 +2271,14 @@ static void miro_pinnacle_gpio(struct bt
 
 static void init_ids_eagle(struct bttv *btv)
 {
-	btwrite(0xFFFF37, BT848_GPIO_OUT_EN);
-	btwrite(0x000000, BT848_GPIO_REG_INP);
-	
-	btwrite(0x200020, BT848_GPIO_DATA);
+	gpio_inout(0xffffff,0xFFFF37);
+	gpio_write(0x200020);
 	
 	/* flash strobe inverter ?! */
-	btwrite(0x200024, BT848_GPIO_DATA);
+	gpio_write(0x200024);
 	
 	/* switch sync drive off */
-	btor(LM1882_SYNC_DRIVE, BT848_GPIO_DATA);
+	gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
 	
 	/* set BT848 muxel to 2 */
 	btaor((2)<<5, ~(2<<5), BT848_IFORM);
@@ -2084,8 +2290,7 @@ static void init_ids_eagle(struct bttv *
 static void eagle_muxsel(struct bttv *btv, unsigned int input)
 {
 	btaor((2)<<5, ~(3<<5), BT848_IFORM);
-	btaor((bttv_tvcards[btv->type].muxsel[input&7]&3),
-	      ~3, BT848_GPIO_DATA);
+	gpio_bits(3,bttv_tvcards[btv->c.type].muxsel[input&7]);
 
 #if 0
        /* svhs */
@@ -2104,15 +2309,64 @@ static void eagle_muxsel(struct bttv *bt
 #endif
 
        /* switch sync drive off */
-       btor(LM1882_SYNC_DRIVE, BT848_GPIO_DATA);
+       gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE);
+}
+
+static void gvc1100_muxsel(struct bttv *btv, unsigned int input)
+{
+        static const int masks[] = {0x30, 0x01, 0x12, 0x23};
+	gpio_write(masks[input%4]);
+}
+
+/* LMLBT4x initialization - to allow access to GPIO bits for sensors input and
+   alarms output
+
+   GPIObit    | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+   assignment | TI | O3|INx| O2| O1|IN4|IN3|IN2|IN1|   |   |
+
+   IN - sensor inputs, INx - sensor inputs and TI XORed together
+   O1,O2,O3 - alarm outputs (relays)
+
+   OUT ENABLE   1    1   0  . 1  1   0   0 . 0   0   0    0   = 0x6C0
+
+*/
+
+static void init_lmlbt4x(struct bttv *btv)
+{
+	printk(KERN_DEBUG "LMLBT4x init\n");
+	btwrite(0x000000, BT848_GPIO_REG_INP);
+	gpio_inout(0xffffff, 0x0006C0);
+	gpio_write(0x000000);
 }
 
+
 /* ----------------------------------------------------------------------- */
 
+void bttv_reset_audio(struct bttv *btv)
+{
+	/*
+	 * BT878A has a audio-reset register.
+	 * 1. This register is an audio reset function but it is in
+	 *    function-0 (video capture) address space.
+	 * 2. It is enough to do this once per power-up of the card.
+	 * 3. There is a typo in the Conexant doc -- it is not at
+	 *    0x5B, but at 0x058. (B is an odd-number, obviously a typo!).
+	 * --//Shrikumar 030609
+	 */
+	if (btv->id != 878)
+		return;
+	
+	if (bttv_debug)
+		printk("bttv%d: BT878A ARESET\n",btv->c.nr);
+	btwrite((1<<7), 0x058);
+	udelay(10);
+	btwrite(     0, 0x058);
+}
+
 /* initialization part one -- before registering i2c bus */
 void __devinit bttv_init_card1(struct bttv *btv)
 {
-	switch (btv->type) {
+	switch (btv->c.type) {
 	case BTTV_HAUPPAUGE:
 	case BTTV_HAUPPAUGE878:
                 boot_msp34xx(btv,5);
@@ -2126,6 +2380,9 @@ void __devinit bttv_init_card1(struct bt
 	case BTTV_HAUPPAUGEPVR:
 		pvr_boot(btv);
 		break;
+	case BTTV_TWINHAN_DST:
+		btv->use_i2c_hw = 1;
+		break;
 	}
 }
 
@@ -2134,12 +2391,12 @@ void __devinit bttv_init_card2(struct bt
 {
         btv->tuner_type = -1;
 
-	if (BTTV_UNKNOWN == btv->type) {
+	if (BTTV_UNKNOWN == btv->c.type) {
 		bttv_readee(btv,eeprom_data,0xa0);
 		identify_by_eeprom(btv,eeprom_data);
 	}
 
-	switch (btv->type) {
+	switch (btv->c.type) {
 	case BTTV_MIRO:
 	case BTTV_MIROPRO:
 	case BTTV_PINNACLE:
@@ -2191,7 +2448,7 @@ void __devinit bttv_init_card2(struct bt
 	case BTTV_MAGICTVIEW061:
 		if (btv->cardid == 0x3002144f) {
 			btv->has_radio=1;
-			printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->nr);
+			printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->c.nr);
 		}
 		break;
        case BTTV_STB2:
@@ -2223,21 +2480,24 @@ void __devinit bttv_init_card2(struct bt
 		bttv_readee(btv,eeprom_data,0xa0);
 		modtec_eeprom(btv);
 		break;
+	case BTTV_LMLBT4:
+		init_lmlbt4x(btv);
+		break;
 	}
 
 	/* pll configuration */
         if (!(btv->id==848 && btv->revision==0x11)) {
 		/* defaults from card list */
-		if (PLL_28 == bttv_tvcards[btv->type].pll) {
+		if (PLL_28 == bttv_tvcards[btv->c.type].pll) {
 			btv->pll.pll_ifreq=28636363;
 			btv->pll.pll_crystal=BT848_IFORM_XT0;
 		}
-		if (PLL_35 == bttv_tvcards[btv->type].pll) {
+		if (PLL_35 == bttv_tvcards[btv->c.type].pll) {
 			btv->pll.pll_ifreq=35468950;
 			btv->pll.pll_crystal=BT848_IFORM_XT1;
 		}
 		/* insmod options can override */
-                switch (pll[btv->nr]) {
+                switch (pll[btv->c.nr]) {
                 case 0: /* none */
 			btv->pll.pll_crystal = 0;
 			btv->pll.pll_ifreq   = 0;
@@ -2260,27 +2520,33 @@ void __devinit bttv_init_card2(struct bt
 	btv->pll.pll_current = -1;
 
 	/* tuner configuration (from card list / autodetect / insmod option) */
- 	if (UNSET != bttv_tvcards[btv->type].tuner_type)
+ 	if (UNSET != bttv_tvcards[btv->c.type].tuner_type)
 		if(UNSET == btv->tuner_type) 
-                	btv->tuner_type = bttv_tvcards[btv->type].tuner_type;
-	if (UNSET != tuner[btv->nr])
-		btv->tuner_type = tuner[btv->nr];
-	printk("bttv%d: using tuner=%d\n",btv->nr,btv->tuner_type);
+                	btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type;
+	if (UNSET != tuner[btv->c.nr])
+		btv->tuner_type = tuner[btv->c.nr];
+	printk("bttv%d: using tuner=%d\n",btv->c.nr,btv->tuner_type);
 	if (btv->pinnacle_id != UNSET)
 		bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE,
 				      &btv->pinnacle_id);
 	if (btv->tuner_type != UNSET)
 		bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type);
-	btv->svhs = bttv_tvcards[btv->type].svhs;
-	if (svhs[btv->nr] != UNSET)
-		btv->svhs = svhs[btv->nr];
+	btv->svhs = bttv_tvcards[btv->c.type].svhs;
+	if (svhs[btv->c.nr] != UNSET)
+		btv->svhs = svhs[btv->c.nr];
+	if (remote[btv->c.nr] != UNSET)
+		btv->has_remote = remote[btv->c.nr];
 
-	if (bttv_tvcards[btv->type].has_radio)
+	if (bttv_tvcards[btv->c.type].has_radio)
 		btv->has_radio=1;
-	if (bttv_tvcards[btv->type].audio_hook)
-		btv->audio_hook=bttv_tvcards[btv->type].audio_hook;
+	if (bttv_tvcards[btv->c.type].has_remote)
+		btv->has_remote=1;
+	if (bttv_tvcards[btv->c.type].no_gpioirq)
+		btv->gpioirq=0;
+	if (bttv_tvcards[btv->c.type].audio_hook)
+		btv->audio_hook=bttv_tvcards[btv->c.type].audio_hook;
 
-	if (bttv_tvcards[btv->type].digital_mode == DIGITAL_MODE_CAMERA) {
+	if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) {
 		/* detect Bt832 chip for quartzsight digital camera */
 		if ((bttv_I2CRead(btv, I2C_BT832_ALT1, "Bt832") >=0) ||
 		    (bttv_I2CRead(btv, I2C_BT832_ALT2, "Bt832") >=0))
@@ -2288,31 +2554,31 @@ void __devinit bttv_init_card2(struct bt
 	}
 
 	/* try to detect audio/fader chips */
-	if (!bttv_tvcards[btv->type].no_msp34xx &&
+	if (!bttv_tvcards[btv->c.type].no_msp34xx &&
 	    bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) {
 		if (autoload)
 			request_module("msp3400");
 	}
 
-	if (bttv_tvcards[btv->type].msp34xx_alt &&
+	if (bttv_tvcards[btv->c.type].msp34xx_alt &&
 	    bttv_I2CRead(btv, I2C_MSP3400_ALT, "MSP34xx (alternate address)") >=0) {
 		if (autoload)
 			request_module("msp3400");
 	}
 
-	if (!bttv_tvcards[btv->type].no_tda9875 &&
+	if (!bttv_tvcards[btv->c.type].no_tda9875 &&
 	    bttv_I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) {
 		if (autoload)
 			request_module("tda9875");
 	}
 
-	if (!bttv_tvcards[btv->type].no_tda7432 && 
+	if (!bttv_tvcards[btv->c.type].no_tda7432 && 
 	    bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) {
 		if (autoload)
 			request_module("tda7432");
 	}
 
-	if (bttv_tvcards[btv->type].needs_tvaudio) {
+	if (bttv_tvcards[btv->c.type].needs_tvaudio) {
 		if (autoload)
 			request_module("tvaudio");
 	}
@@ -2375,8 +2641,8 @@ hauppauge_tuner[] __devinitdata = 
         { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" },
         { TUNER_PHILIPS_FQ1216ME,   "Philips FQ1216 ME" },
         { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" },
-        { TUNER_ABSENT,        "Philips TD1536" },
-        { TUNER_ABSENT,        "Philips TD1536D" },
+        { TUNER_PHILIPS_NTSC,        "Philips TD1536" },
+        { TUNER_PHILIPS_NTSC,        "Philips TD1536D" },
 	{ TUNER_PHILIPS_NTSC,  "Philips FMR1236" }, /* mono radio */
         { TUNER_ABSENT,        "Philips FI1256MP" },
         { TUNER_ABSENT,        "Samsung TCPQ9091P" },
@@ -2396,11 +2662,15 @@ static void modtec_eeprom(struct bttv *b
 {
 	if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) {
 		btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I;
-		printk("bttv Modtec: Tuner autodetected %s\n",
-		       &eeprom_data[0x1e]);
+		printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
+		       btv->c.nr,&eeprom_data[0x1e]);
+	} else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) {
+		btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I;
+		printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n",
+                       btv->c.nr,&eeprom_data[0x1e]);
 	} else {
-		printk("bttv Modtec: Unknown TunerString:%s\n",
-		       &eeprom_data[0x1e]);
+		printk("bttv%d: Modtec: Unknown TunerString: %s\n",
+		       btv->c.nr,&eeprom_data[0x1e]);
 	}
 }
 
@@ -2410,7 +2680,7 @@ static void __devinit hauppauge_eeprom(s
 
 	if (eeprom_data[0] != 0x84 || eeprom_data[2] != 0)
 		printk(KERN_WARNING "bttv%d: Hauppauge eeprom: invalid\n",
-		       btv->nr);
+		       btv->c.nr);
 
 	/* Block 2 starts after len+3 bytes header */
 	blk2 = eeprom_data[1] + 3;
@@ -2428,7 +2698,7 @@ static void __devinit hauppauge_eeprom(s
 	if (bttv_verbose)
 		printk(KERN_INFO "bttv%d: Hauppauge eeprom: model=%d, "
 		       "tuner=%s (%d), radio=%s\n",
-		       btv->nr, model, hauppauge_tuner[tuner].name,
+		       btv->c.nr, model, hauppauge_tuner[tuner].name,
 		       btv->tuner_type, radio ? "yes" : "no");
 }
 
@@ -2452,7 +2722,7 @@ static int terratec_active_radio_upgrade
 	tea5757_write(btv, 5 * freq + 0x358); // write 0x1ed8
 	if (0x1ed8 == tea5757_read(btv)) {
 		printk("bttv%d: Terratec Active Radio Upgrade found.\n",
-		       btv->nr);
+		       btv->c.nr);
 		btv->has_radio    = 1;
 		btv->has_matchbox = 1;
 	} else {
@@ -2483,36 +2753,35 @@ static int __devinit pvr_altera_load(str
 	u32 n;
   	u8 bits;
 	int i;
- 
-	btwrite(BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG,
-		BT848_GPIO_OUT_EN);
-	btwrite(0,BT848_GPIO_DATA);
+
+	gpio_inout(0xffffff,BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG);
+	gpio_write(0);
 	udelay(PVR_GPIO_DELAY);
 	
-	btwrite(BTTV_ALT_NCONFIG,BT848_GPIO_DATA);
+	gpio_write(BTTV_ALT_NCONFIG);
 	udelay(PVR_GPIO_DELAY);
 
 	for (n = 0; n < microlen; n++) {
 		bits = micro[n];
 		for ( i = 0 ; i < 8 ; i++ ) {
-			btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA);
-			if (bits & 0x01) 
-				btor(BTTV_ALT_DATA,BT848_GPIO_DATA);
+			gpio_bits(BTTV_ALT_DCLK,0);
+			if (bits & 0x01)
+				gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA);
 			else 
-				btand(~BTTV_ALT_DATA,BT848_GPIO_DATA);
-			btor(BTTV_ALT_DCLK,BT848_GPIO_DATA);
+				gpio_bits(BTTV_ALT_DATA,0);
+			gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
 			bits >>= 1;
 		}
 	}
-	btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA);
+	gpio_bits(BTTV_ALT_DCLK,0);
 	udelay(PVR_GPIO_DELAY);
 	
 	/* begin Altera init loop (Not necessary,but doesn't hurt) */
 	for (i = 0 ; i < 30 ; i++) {
-		btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA);
-		btor(BTTV_ALT_DCLK,BT848_GPIO_DATA);
+		gpio_bits(BTTV_ALT_DCLK,0);
+		gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK);
 	}
-	btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA);
+	gpio_bits(BTTV_ALT_DCLK,0);
 	return 0;
 }
 
@@ -2535,15 +2804,15 @@ int __devinit pvr_boot(struct bttv *btv)
 	microlen = mod_firmware_load(firm_altera, (char**) &micro);
 	if (!microlen) {
 		printk(KERN_WARNING "bttv%d: altera firmware not found [%s]\n",
-		       btv->nr, firm_altera);
+		       btv->c.nr, firm_altera);
 		return -1;
 	}
 	
 	printk(KERN_INFO "bttv%d: uploading altera firmware [%s] ...\n",
-	       btv->nr, firm_altera);
+	       btv->c.nr, firm_altera);
 	result = pvr_altera_load(btv, micro, microlen);
 	printk(KERN_INFO "bttv%d: ... upload %s\n",
-	       btv->nr, (result < 0) ? "failed" : "ok");
+	       btv->c.nr, (result < 0) ? "failed" : "ok");
 	vfree(micro);
 	return result;
 }
@@ -2555,15 +2824,15 @@ int __devinit pvr_boot(struct bttv *btv)
         const struct firmware *fw_entry;
 	int rc;
 
-	rc = request_firmware(&fw_entry, "hcwamc.rbf", pci_name(btv->dev));
+	rc = request_firmware(&fw_entry, "hcwamc.rbf", pci_name(btv->c.pci));
 	if (rc != 0) {
 		printk(KERN_WARNING "bttv%d: no altera firmware [via hotplug]\n",
-		       btv->nr);
+		       btv->c.nr);
                 return rc;
         }
 	rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size);
 	printk(KERN_INFO "bttv%d: altera firmware upload %s\n",
-	       btv->nr, (rc < 0) ? "failed" : "ok");
+	       btv->c.nr, (rc < 0) ? "failed" : "ok");
         release_firmware(fw_entry);
 	return rc;
 }	
@@ -2578,7 +2847,7 @@ static void __devinit osprey_eeprom(stru
        unsigned char *ee = eeprom_data;
        unsigned long serial = 0;
     
-       if (btv->type == 0) {
+       if (btv->c.type == 0) {
                /* this might be an antique... check for MMAC label in eeprom */
                if ((ee[0]=='M') && (ee[1]=='M') && (ee[2]=='A') && (ee[3]=='C')) {
                        unsigned char checksum = 0;
@@ -2586,7 +2855,7 @@ static void __devinit osprey_eeprom(stru
 			       checksum += ee[i];
                        if (checksum != ee[21])
 			       return;
-		       btv->type = BTTV_OSPREY1x0_848;
+		       btv->c.type = BTTV_OSPREY1x0_848;
 		       for (i = 12; i < 21; i++)
 			       serial *= 10, serial += ee[i] - '0';
                }
@@ -2615,49 +2884,49 @@ static void __devinit osprey_eeprom(stru
 
 	       /* 848 based */
 	       case 0x0004:
-		       btv->type = BTTV_OSPREY1x0_848;
+		       btv->c.type = BTTV_OSPREY1x0_848;
 		       break;
 	       case 0x0005:
-		       btv->type = BTTV_OSPREY101_848;
+		       btv->c.type = BTTV_OSPREY101_848;
 		       break;
 		       
                /* 878 based */
 	       case 0x0012:
 	       case 0x0013:
-		       btv->type = BTTV_OSPREY1x0;
+		       btv->c.type = BTTV_OSPREY1x0;
 		       break;
 	       case 0x0014:
 	       case 0x0015:
-		       btv->type = BTTV_OSPREY1x1;
+		       btv->c.type = BTTV_OSPREY1x1;
 		       break;
 	       case 0x0016:
 	       case 0x0017:
 	       case 0x0020:
-		       btv->type = BTTV_OSPREY1x1_SVID;
+		       btv->c.type = BTTV_OSPREY1x1_SVID;
 		       break;
 	       case 0x0018:
 	       case 0x0019:
 	       case 0x001E:
 	       case 0x001F:
-		       btv->type = BTTV_OSPREY2xx;
+		       btv->c.type = BTTV_OSPREY2xx;
 		       break;
 	       case 0x001A:
 	       case 0x001B:
-		       btv->type = BTTV_OSPREY2x0_SVID;
+		       btv->c.type = BTTV_OSPREY2x0_SVID;
 		       break;
 	       case 0x0040:
-		       btv->type = BTTV_OSPREY500;
+		       btv->c.type = BTTV_OSPREY500;
 		       break;
 	       case 0x0050:
 	       case 0x0056:
-		       btv->type = BTTV_OSPREY540;
+		       btv->c.type = BTTV_OSPREY540;
 		       /* bttv_osprey_540_init(btv); */
 		       break;
 	       case 0x0060:
 	       case 0x0070:
-		       btv->type = BTTV_OSPREY2x0;
+		       btv->c.type = BTTV_OSPREY2x0;
 		       //enable output on select control lines
-		       btwrite(0x000303, BT848_GPIO_OUT_EN);
+		       gpio_inout(0xffffff,0x000303);
 		       break;
 	       default:
 		       /* unknown...leave generic, but get serial # */
@@ -2670,7 +2939,7 @@ static void __devinit osprey_eeprom(stru
        }
        
        printk(KERN_INFO "bttv%d: osprey eeprom: card=%d name=%s serial=%ld\n",
-	      btv->nr, btv->type, bttv_tvcards[btv->type].name,serial);
+	      btv->c.nr, btv->c.type, bttv_tvcards[btv->c.type].name,serial);
 }	
 
 /* ----------------------------------------------------------------------- */
@@ -2715,7 +2984,7 @@ static void __devinit avermedia_eeprom(s
 			tuner = tuner_1_table[tuner_format];
 	
 	printk(KERN_INFO "bttv%d: Avermedia eeprom[0x%02x%02x]: tuner=",
-		btv->nr,eeprom_data[0x41],eeprom_data[0x42]);
+		btv->c.nr,eeprom_data[0x41],eeprom_data[0x42]);
 	if(tuner) {
 		btv->tuner_type=tuner;
 		printk("%d",tuner);
@@ -2741,8 +3010,8 @@ void bttv_tda9880_setnorm(struct bttv *b
 		dprintk("bttv_tda9880_setnorm to PAL\n");
 	}
 	// set GPIO according
-	btaor(bttv_tvcards[btv->type].audiomux[btv->audio],
-              ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
+	gpio_bits(bttv_tvcards[btv->c.type].gpiomask,
+		  bttv_tvcards[btv->c.type].audiomux[btv->audio]);
 }
 
 
@@ -2757,23 +3026,23 @@ static void __devinit boot_msp34xx(struc
 {
 	int mask = (1 << pin);
 
-        btaor(mask, ~mask, BT848_GPIO_OUT_EN);
-        btaor(0, ~mask, BT848_GPIO_DATA);
+	gpio_inout(mask,mask);
+	gpio_bits(mask,0);
         udelay(2500);
-        btaor(mask, ~mask, BT848_GPIO_DATA);
+	gpio_bits(mask,mask);
+
 	if (bttv_gpio)
 		bttv_gpio_tracking(btv,"msp34xx");
-
 	if (bttv_verbose)
 		printk(KERN_INFO "bttv%d: Hauppauge/Voodoo msp34xx: reset line "
-		       "init [%d]\n", btv->nr, pin);
+		       "init [%d]\n", btv->c.nr, pin);
 }
 
 static void __devinit boot_bt832(struct bttv *btv)
 {
-	int outbits,databits,resetbit=0;
+	int resetbit=0;
 
-	switch (btv->type) {
+	switch (btv->c.type) {
 	case BTTV_PXELVWPLTVPAK:
 		resetbit = 0x400000;
 		break;
@@ -2787,18 +3056,14 @@ static void __devinit boot_bt832(struct 
 	request_module("bt832");
 	bttv_call_i2c_clients(btv, BT832_HEXDUMP, NULL);
 
-	printk("bttv%d: Reset Bt832 [line=0x%x]\n",btv->nr,resetbit);
-	btwrite(0, BT848_GPIO_DATA);
-	outbits = btread(BT848_GPIO_OUT_EN);
-	databits= btread(BT848_GPIO_DATA);
-	btwrite(resetbit, BT848_GPIO_OUT_EN);
+	printk("bttv%d: Reset Bt832 [line=0x%x]\n",btv->c.nr,resetbit);
+	gpio_write(0);
+	gpio_inout(resetbit, resetbit);
 	udelay(5);
-	btwrite(resetbit, BT848_GPIO_DATA);
+	gpio_bits(resetbit, resetbit);
 	udelay(5);
-	btwrite(0, BT848_GPIO_DATA);
+	gpio_bits(resetbit, 0);
 	udelay(5);
-	btwrite(outbits, BT848_GPIO_OUT_EN);
-	btwrite(databits, BT848_GPIO_DATA);
 
 	// bt832 on pixelview changes from i2c 0x8a to 0x88 after
 	// being reset as above. So we must follow by this:
@@ -2821,13 +3086,13 @@ static void __devinit init_PXC200(struct
 	u32 val;
 	
 	/* Initialise GPIO-connevted stuff */
-	btwrite(1<<13,BT848_GPIO_OUT_EN); /* Reset pin only */
-	btwrite(0,BT848_GPIO_DATA);
+	gpio_inout(0xffffff, (1<<13));
+	gpio_write(0);
 	udelay(3);
-	btwrite(1<<13,BT848_GPIO_DATA);
+	gpio_write(1<<13);
 	/* GPIO inputs are pulled up, so no need to drive 
 	 * reset pin any longer */
-	btwrite(0,BT848_GPIO_OUT_EN);
+	gpio_bits(0xffffff, 0);
 	if (bttv_gpio)
 		bttv_gpio_tracking(btv,"pxc200");
 
@@ -2862,10 +3127,10 @@ static void __devinit init_PXC200(struct
 	 * device same as above for the reset line, but not the same
 	 * value sent to the GPIO-connected stuff
 	 * which one is the good one? */
-	btwrite( (1<<2), BT848_GPIO_OUT_EN); /* only the reset pin */
-	btwrite(0, BT848_GPIO_DATA);
+	gpio_inout(0xffffff,(1<<2));
+	gpio_write(0);
 	udelay(10);
-	btwrite(1<<2, BT848_GPIO_DATA);
+	gpio_write(1<<2);
 
        	for (i = 0; i < ARRAY_SIZE(vals); i++) {
 		tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1);
@@ -2892,17 +3157,16 @@ static void __devinit init_PXC200(struct
 void bus_low(struct bttv *btv, int bit)
 {
 	if (btv->mbox_ior) {
-		btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-		     BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
 		udelay(5);
 	}
 
-	btand(~(bit), BT848_GPIO_DATA);
+	gpio_bits(bit,0);
 	udelay(5);
 
 	if (btv->mbox_ior) {
-		btand(~(btv->mbox_iow | btv->mbox_csel),
-		      BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
 		udelay(5);
 	}
 }
@@ -2910,17 +3174,16 @@ void bus_low(struct bttv *btv, int bit)
 void bus_high(struct bttv *btv, int bit)
 {
 	if (btv->mbox_ior) {
-		btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-		     BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
 		udelay(5);
 	}
 
-	btor((bit), BT848_GPIO_DATA);
+	gpio_bits(bit,bit);
 	udelay(5);
 
 	if (btv->mbox_ior) {
-		btand(~(btv->mbox_iow | btv->mbox_csel),
-		      BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
 		udelay(5);
 	}
 }
@@ -2928,15 +3191,14 @@ void bus_high(struct bttv *btv, int bit)
 int bus_in(struct bttv *btv, int bit)
 {
 	if (btv->mbox_ior) {
-		btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-		     BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
 		udelay(5);
 
-		btand(~(btv->mbox_ior | btv->mbox_csel),
-		      BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_iow | btv->mbox_csel, 0);
 		udelay(5);
 	}
-	return btread(BT848_GPIO_DATA) & (bit);
+	return gpio_read() & (bit);
 }
 
 /* TEA5757 register bits */
@@ -2974,12 +3236,11 @@ static int tea5757_read(struct bttv *btv
 	int i;
 	
 	/* better safe than sorry */
-	btaor((btv->mbox_clk | btv->mbox_we),
-	      ~btv->mbox_mask, BT848_GPIO_OUT_EN);
+	gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we);
 
 	if (btv->mbox_ior) {
-		btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-		     BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
 		udelay(5);
 	}
 
@@ -2996,11 +3257,11 @@ static int tea5757_read(struct bttv *btv
 	while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout))
 		schedule();
 	if (bus_in(btv,btv->mbox_data)) {
-		printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->nr);
+		printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->c.nr);
 		return -1;
 	}
 
-	dprintk("bttv%d: tea5757:",btv->nr);
+	dprintk("bttv%d: tea5757:",btv->c.nr);
 	for(i = 0; i < 24; i++)
 	{
 		udelay(5);
@@ -3012,7 +3273,7 @@ static int tea5757_read(struct bttv *btv
 		value |= (bus_in(btv,btv->mbox_data) == 0)?0:1;  /* MSB first */
 		dprintk("%c", (bus_in(btv,btv->mbox_most) == 0)?'S':'M');
 	}
-	dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->nr, value);
+	dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->c.nr, value);
 	return value;
 }
 
@@ -3021,18 +3282,17 @@ static int tea5757_write(struct bttv *bt
 	int i;
 	int reg = value;
 	
-	btaor(btv->mbox_clk | btv->mbox_we | btv->mbox_data,
-	      ~btv->mbox_mask, BT848_GPIO_OUT_EN);
+	gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data);
 
 	if (btv->mbox_ior) {
-		btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
-		     BT848_GPIO_DATA);
+		gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel,
+			  btv->mbox_ior | btv->mbox_iow | btv->mbox_csel);
 		udelay(5);
 	}
 	if (bttv_gpio)
 		bttv_gpio_tracking(btv,"tea5757 write");
 
-	dprintk("bttv%d: tea5757: write 0x%X\n", btv->nr, value);
+	dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value);
 	bus_low(btv,btv->mbox_clk);
 	bus_high(btv,btv->mbox_we);
 	for(i = 0; i < 25; i++)
@@ -3058,7 +3318,7 @@ void tea5757_set_freq(struct bttv *btv, 
 #if 0
 	/* breaks Miro PCTV */
 	value = tea5757_read(btv);
-	dprintk("bttv%d: tea5757 readback=0x%x\n",btv->nr,value);
+	dprintk("bttv%d: tea5757 readback=0x%x\n",btv->c.nr,value);
 #endif
 }
 
@@ -3084,7 +3344,7 @@ void winview_audio(struct bttv *btv, str
 	/* tens */
 	bits_out |= (PT2254_DBS_IN_10>>(vol/5));
 	bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL;
-	data = btread(BT848_GPIO_DATA);
+	data = gpio_read();
 	data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA|
 		  WINVIEW_PT2254_STROBE);
 	for (loops = 17; loops >= 0 ; loops--) {
@@ -3092,20 +3352,20 @@ void winview_audio(struct bttv *btv, str
 			data |=  WINVIEW_PT2254_DATA;
 		else
 			data &= ~WINVIEW_PT2254_DATA;
-		btwrite(data, BT848_GPIO_DATA);
+		gpio_write(data);
 		udelay(5);
 		data |= WINVIEW_PT2254_CLK;
-		btwrite(data, BT848_GPIO_DATA);
+		gpio_write(data);
 		udelay(5);
 		data &= ~WINVIEW_PT2254_CLK;
-		btwrite(data, BT848_GPIO_DATA);
+		gpio_write(data);
 	}
 	data |=  WINVIEW_PT2254_STROBE;
 	data &= ~WINVIEW_PT2254_DATA;
-	btwrite(data, BT848_GPIO_DATA);
+	gpio_write(data);
 	udelay(10);                     
 	data &= ~WINVIEW_PT2254_STROBE;
-	btwrite(data, BT848_GPIO_DATA);
+	gpio_write(data);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -3118,7 +3378,7 @@ gvbctv3pci_audio(struct bttv *btv, struc
 	unsigned int con = 0;
 
 	if (set) {
-		btor(0x300, BT848_GPIO_OUT_EN);
+		gpio_inout(0x300, 0x300);
 		if (v->mode & VIDEO_SOUND_LANG1)
 			con = 0x000;
 		if (v->mode & VIDEO_SOUND_LANG2)
@@ -3127,13 +3387,68 @@ gvbctv3pci_audio(struct bttv *btv, struc
 			con = 0x200;
 //		if (v->mode & VIDEO_SOUND_MONO)
 //			con = 0x100;
-		btaor(con, ~0x300, BT848_GPIO_DATA);
+		gpio_bits(0x300, con);
 	} else {
 		v->mode = VIDEO_SOUND_STEREO |
 			  VIDEO_SOUND_LANG1  | VIDEO_SOUND_LANG2;
 	}
 }
 
+static void
+gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set)
+{
+	unsigned int val, con;
+
+#if BTTV_VERSION_CODE > KERNEL_VERSION(0,8,0)
+	if (btv->radio_user)
+		return;
+#else
+	if (btv->radio)
+		return;
+#endif
+
+	val = gpio_read();
+	if (set) {
+		con = 0x000;
+		if (v->mode & VIDEO_SOUND_LANG2) {
+			if (v->mode & VIDEO_SOUND_LANG1) {
+				/* LANG1 + LANG2 */
+				con = 0x100;
+			}
+			else {
+				/* LANG2 */
+				con = 0x300;
+			}
+		}
+		if (con != (val & 0x300)) {
+			gpio_bits(0x300, con);
+			if (bttv_gpio)
+				bttv_gpio_tracking(btv,"gvbctv5pci");
+		}
+	} else {
+		switch (val & 0x70) {
+		  case 0x10:
+			v->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+			break;
+		  case 0x30:
+			v->mode = VIDEO_SOUND_LANG2;
+			break;
+		  case 0x50:
+			v->mode = VIDEO_SOUND_LANG1;
+			break;
+		  case 0x60:
+			v->mode = VIDEO_SOUND_STEREO;
+			break;
+		  case 0x70:
+			v->mode = VIDEO_SOUND_MONO;
+			break;
+		  default:
+			v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
+				  VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+		}
+	}
+}
+
 /*
  * Mario Medina Nussbaum <medisoft@alohabbs.org.mx>
  *  I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo,
@@ -3153,12 +3468,12 @@ avermedia_tvphone_audio(struct bttv *btv
 	int val = 0;
 
 	if (set) {
-		if (v->mode & VIDEO_SOUND_LANG1)   /* SAP */
+		if (v->mode & VIDEO_SOUND_LANG2)   /* SAP */
 			val = 0x02;
 		if (v->mode & VIDEO_SOUND_STEREO)
 			val = 0x01;
 		if (val) {
-			btaor(val, ~0x03, BT848_GPIO_DATA);
+			gpio_bits(0x03,val);
 			if (bttv_gpio)
 				bttv_gpio_tracking(btv,"avermedia");
 		}
@@ -3169,13 +3484,33 @@ avermedia_tvphone_audio(struct bttv *btv
 	}
 }
 
+static void
+avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v, int set)
+{
+	int val = 0;
+	
+	if (set) {
+		if (v->mode & VIDEO_SOUND_LANG2)   /* SAP */
+			val = 0x01;
+		if (v->mode & VIDEO_SOUND_STEREO)  /* STEREO */
+			val = 0x02;
+		btaor(val, ~0x03, BT848_GPIO_DATA);
+		if (bttv_gpio)
+			bttv_gpio_tracking(btv,"avermedia");
+	} else {
+		v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
+			VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+		return;
+	}
+}
+
 /* Lifetec 9415 handling */
 static void
 lt9415_audio(struct bttv *btv, struct video_audio *v, int set)
 {
         int val = 0;
 
-        if (btread(BT848_GPIO_DATA) & 0x4000) {
+        if (gpio_read() & 0x4000) {
 		v->mode = VIDEO_SOUND_MONO;
 		return;
 	}
@@ -3188,7 +3523,7 @@ lt9415_audio(struct bttv *btv, struct vi
                 if ((v->mode & VIDEO_SOUND_LANG1) ||
 		    (v->mode & VIDEO_SOUND_MONO))
 			val = 0;
-                btaor(val, ~0x0880, BT848_GPIO_DATA);
+		gpio_bits(0x0880, val);
                 if (bttv_gpio)
                         bttv_gpio_tracking(btv,"lt9415");
         } else {
@@ -3206,12 +3541,12 @@ terratv_audio(struct bttv *btv, struct v
 	unsigned int con = 0;
 
 	if (set) {
-		btor(0x180000, BT848_GPIO_OUT_EN);
+		gpio_inout(0x180000,0x180000);
 		if (v->mode & VIDEO_SOUND_LANG2)
 			con = 0x080000;
 		if (v->mode & VIDEO_SOUND_STEREO)
 			con = 0x180000;
-		btaor(con, ~0x180000, BT848_GPIO_DATA);
+		gpio_bits(0x180000, con);
 		if (bttv_gpio)
 			bttv_gpio_tracking(btv,"terratv");
 	} else {
@@ -3236,7 +3571,7 @@ winfast2000_audio(struct bttv *btv, stru
 		if (v->mode & VIDEO_SOUND_STEREO)	/* Stereo */
 			val = 0x020000;
 		if (val) {
-			btaor(val, ~0x430000, BT848_GPIO_DATA);
+			gpio_bits(0x430000, val);
 			if (bttv_gpio)
 				bttv_gpio_tracking(btv,"winfast2000");
 		}
@@ -3276,7 +3611,7 @@ pvbt878p9b_audio(struct bttv *btv, struc
 			val = 0x02;
 		}
 		if (val) {
-			btaor(val, ~0x03, BT848_GPIO_DATA);
+			gpio_bits(0x03,val);
 			if (bttv_gpio)
 				bttv_gpio_tracking(btv,"pvbt878p9b");
 		}
@@ -3312,7 +3647,7 @@ fv2000s_audio(struct bttv *btv, struct v
 			val = 0x1080; //-dk-???: 0x0880, 0x0080, 0x1800 ...
 		}
 		if (val != 0xffff) {
-			btaor(val, ~0x1800, BT848_GPIO_DATA);
+			gpio_bits(0x1800, val);
 			if (bttv_gpio)
 				bttv_gpio_tracking(btv,"fv2000s");
 		}
@@ -3341,7 +3676,7 @@ windvr_audio(struct bttv *btv, struct vi
                 if (v->mode & VIDEO_SOUND_STEREO)
                         val = 0;
                 if (val) {
-                        btaor(val, ~0x140000, BT848_GPIO_DATA);
+			gpio_bits(0x140000, val);
                         if (bttv_gpio)
                                 bttv_gpio_tracking(btv,"windvr");
                 }
@@ -3373,7 +3708,7 @@ adtvk503_audio(struct bttv *btv, struct 
 		if (v->mode & VIDEO_SOUND_MONO)
 			con = 0x00060000;
 		if (con != 0xffffff) {
-			btaor(con, ~0x1e0000, BT848_GPIO_DATA);
+			gpio_bits(0x1e0000,con);
 			if (bttv_gpio)
 				bttv_gpio_tracking(btv, "adtvk503");
 		}
@@ -3410,16 +3745,16 @@ adtvk503_audio(struct bttv *btv, struct 
 static void rv605_muxsel(struct bttv *btv, unsigned int input)
 {
 	/* reset all conections */
-	btaor(0x200,~0x200, BT848_GPIO_DATA);
+	gpio_bits(0x200,0x200);
 	mdelay(1);
-	btaor(0x000,~0x200, BT848_GPIO_DATA);
+	gpio_bits(0x200,0x000);
 	mdelay(1);
 
 	/* create a new conection */
-	btaor(0x080,~0x480, BT848_GPIO_DATA);
-	btaor(0x480,~0x480, BT848_GPIO_DATA);
+	gpio_bits(0x480,0x080);
+	gpio_bits(0x480,0x480);
 	mdelay(1);
-	btaor(0x080,~0x480, BT848_GPIO_DATA);
+	gpio_bits(0x480,0x080);
 	mdelay(1);
 }
 
@@ -3449,9 +3784,75 @@ static void xguard_muxsel(struct bttv *b
                 ENB1, ENB1|IN01, ENB1|IN11, ENB1|IN01|IN11,
                 ENA1, ENA1|IN01, ENA1|IN11, ENA1|IN01|IN11,
 	};
-        btwrite(masks[input%16], BT848_GPIO_DATA);
+	gpio_write(masks[input%16]);
+}
+
+/*
+ * ivc120_muxsel [Added by Alan Garfield <alan@fromorbit.com>]
+ *
+ * The IVC120G security card has 4 i2c controlled TDA8540 matrix
+ * swichers to provide 16 channels to MUX0. The TDA8540's have 
+ * 4 indepedant outputs and as such the IVC120G also has the 
+ * optional "Monitor Out" bus. This allows the card to be looking 
+ * at one input while the monitor is looking at another.
+ *
+ * Since I've couldn't be bothered figuring out how to add an
+ * independant muxsel for the monitor bus, I've just set it to 
+ * whatever the card is looking at.
+ *
+ *  OUT0 of the TDA8540's is connected to MUX0         (0x03)
+ *  OUT1 of the TDA8540's is connected to "Monitor Out"        (0x0C)
+ *
+ *  TDA8540_ALT3 IN0-3 = Channel 13 - 16       (0x03)
+ *  TDA8540_ALT4 IN0-3 = Channel 1 - 4         (0x03)
+ *  TDA8540_ALT5 IN0-3 = Channel 5 - 8         (0x03)
+ *  TDA8540_ALT6 IN0-3 = Channel 9 - 12                (0x03)
+ *
+ */
+
+/* All 7 possible sub-ids for the TDA8540 Matrix Switcher */
+#define I2C_TDA8540        0x90
+#define I2C_TDA8540_ALT1   0x92
+#define I2C_TDA8540_ALT2   0x94
+#define I2C_TDA8540_ALT3   0x96
+#define I2C_TDA8540_ALT4   0x98
+#define I2C_TDA8540_ALT5   0x9a
+#define I2C_TDA8540_ALT6   0x9c
+
+static void ivc120_muxsel(struct bttv *btv, unsigned int input)
+{
+	// Simple maths
+	int key = input % 4;    
+	int matrix = input / 4;
+	
+	dprintk("bttv%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
+		btv->c.nr, input, matrix, key);
+	
+	// Handles the input selection on the TDA8540's
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x00,
+		      ((matrix == 3) ? (key | key << 2) : 0x00), 1);
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x00,
+		      ((matrix == 0) ? (key | key << 2) : 0x00), 1);
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x00,
+		      ((matrix == 1) ? (key | key << 2) : 0x00), 1);
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x00,
+		      ((matrix == 2) ? (key | key << 2) : 0x00), 1);
+	
+	// Handles the output enables on the TDA8540's
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x02,
+		      ((matrix == 3) ? 0x03 : 0x00), 1);  // 13 - 16
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x02,
+		      ((matrix == 0) ? 0x03 : 0x00), 1);  // 1-4
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x02,
+		      ((matrix == 1) ? 0x03 : 0x00), 1);  // 5-8 
+	bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02,
+		      ((matrix == 2) ? 0x03 : 0x00), 1);  // 9-12
+	
+	// Selects MUX0 for input on the 878
+	btaor((0)<<5, ~(3<<5), BT848_IFORM);
 }
 
+
 /* ----------------------------------------------------------------------- */
 /* motherboard chipset specific stuff                                      */
 
@@ -3511,12 +3912,12 @@ int __devinit bttv_handle_chipset(struct
 
 	if (bttv_verbose) {
 		if (triton1)
-			printk(KERN_INFO "bttv%d: enabling ETBF (430FX/VP3 compatibilty)\n",btv->nr);
+			printk(KERN_INFO "bttv%d: enabling ETBF (430FX/VP3 compatibilty)\n",btv->c.nr);
 		if (vsfx && btv->id >= 878)
-			printk(KERN_INFO "bttv%d: enabling VSFX\n",btv->nr);
+			printk(KERN_INFO "bttv%d: enabling VSFX\n",btv->c.nr);
 		if (UNSET != latency)
 			printk(KERN_INFO "bttv%d: setting pci timer to %d\n",
-			       btv->nr,latency);
+			       btv->c.nr,latency);
 	}
 
 	if (btv->id < 878) {
@@ -3525,19 +3926,99 @@ int __devinit bttv_handle_chipset(struct
 			btv->triton1 = BT848_INT_ETBF;
 	} else {
 		/* bt878 has a bit in the pci config space for it */
-                pci_read_config_byte(btv->dev, BT878_DEVCTRL, &command);
+                pci_read_config_byte(btv->c.pci, BT878_DEVCTRL, &command);
 		if (triton1)
 			command |= BT878_EN_TBFX;
 		if (vsfx)
 			command |= BT878_EN_VSFX;
-                pci_write_config_byte(btv->dev, BT878_DEVCTRL, command);
+                pci_write_config_byte(btv->c.pci, BT878_DEVCTRL, command);
         }
 	if (UNSET != latency)
-		pci_write_config_byte(btv->dev, PCI_LATENCY_TIMER, latency);
+		pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency);
 	return 0;
 }
 
 
+/* PXC200 muxsel helper 
+ * luke@syseng.anu.edu.au
+ * another transplant 
+ * from Alessandro Rubini (rubini@linux.it)
+ *
+ * There are 4 kinds of cards:
+ * PXC200L which is bt848 
+ * PXC200F which is bt848 with PIC controlling mux
+ * PXC200AL which is bt878 
+ * PXC200AF which is bt878 with PIC controlling mux
+ */
+#define PX_CFG_PXC200F 0x01
+#define PX_FLAG_PXC200A  0x00001000 /* a pxc200A is bt-878 based */
+#define PX_I2C_PIC       0x0f
+#define PX_PXC200A_CARDID 0x200a1295
+#define PX_I2C_CMD_CFG   0x00
+
+static void PXC200_muxsel(struct bttv *btv, unsigned int input)
+{
+        int rc;
+	long mux;
+	int bitmask;
+        unsigned char buf[2];
+
+	/* Read PIC config to determine if this is a PXC200F */
+	/* PX_I2C_CMD_CFG*/
+	buf[0]=0;
+	buf[1]=0;
+	rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1);
+	if (rc) {
+	  printk(KERN_DEBUG "bttv%d: PXC200_muxsel: pic cfg write failed:%d\n", btv->c.nr,rc);
+	  /* not PXC ? do nothing */
+	  return;
+	}
+
+	rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),0);
+	if (!(rc & PX_CFG_PXC200F)) {
+	  printk(KERN_DEBUG "bttv%d: PXC200_muxsel: not PXC200F rc:%d \n", btv->c.nr,rc);
+	  return;
+	}
+
+
+	/* The multiplexer in the 200F is handled by the GPIO port */
+	/* get correct mapping between inputs  */
+	/*  mux = bttv_tvcards[btv->type].muxsel[input] & 3; */
+	/* ** not needed!?   */
+	mux = input;
+  
+	/* make sure output pins are enabled */
+	/* bitmask=0x30f; */
+	bitmask=0x302; 
+	/* check whether we have a PXC200A */
+ 	if (btv->cardid == PX_PXC200A_CARDID)  {
+	   bitmask ^= 0x180; /* use 7 and 9, not 8 and 9 */
+	   bitmask |= 7<<4; /* the DAC */
+	}
+	btwrite(bitmask, BT848_GPIO_OUT_EN);
+
+	bitmask = btread(BT848_GPIO_DATA);
+ 	if (btv->cardid == PX_PXC200A_CARDID) 
+	  bitmask = (bitmask & ~0x280) | ((mux & 2) << 8) | ((mux & 1) << 7);
+	else /* older device */
+	  bitmask = (bitmask & ~0x300) | ((mux & 3) << 8);
+	btwrite(bitmask,BT848_GPIO_DATA);
+
+	/*
+	 * Was "to be safe, set the bt848 to input 0"
+	 * Actually, since it's ok at load time, better not messing
+	 * with these bits (on PXC200AF you need to set mux 2 here)
+	 * 
+	 * needed because bttv-driver sets mux before calling this function
+	 */
+ 	if (btv->cardid == PX_PXC200A_CARDID) 
+	  btaor(2<<5, ~BT848_IFORM_MUXSEL, BT848_IFORM);
+	else /* older device */
+	  btand(~BT848_IFORM_MUXSEL,BT848_IFORM);
+
+	printk(KERN_DEBUG "bttv%d: setting input channel to:%d\n", btv->c.nr,(int)mux);
+}
+
 /*
  * Local variables:
  * c-basic-offset: 8
diff -up linux-2.4.26/drivers/media/video/bttv-driver.c linux/drivers/media/video/bttv-driver.c
--- linux-2.4.26/drivers/media/video/bttv-driver.c	2004-04-21 13:45:25.000000000 +0200
+++ linux/drivers/media/video/bttv-driver.c	2004-04-21 14:11:41.000000000 +0200
@@ -1,97 +1,88 @@
 /*
     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-2003 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/init.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #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 <linux/ioport.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/pagemap.h>
+#include <linux/kdev_t.h>
 
 #include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
 #include <asm/byteorder.h>
 
 #include "bttvp.h"
 
-#define DEBUG(x)	/* Debug driver */
-#define MIN(a,b) (((a)>(b))?(b):(a))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
-/* fwd decl */
-static void bt848_set_risc_jmps(struct bttv *btv, int state);
-static void make_vbitab(struct bttv *btv);
-static void bt848_set_winsize(struct bttv *btv);
-
 unsigned 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 */
 #ifdef __BIG_ENDIAN
 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 gpint = 1;
 static unsigned int irq_debug = 0;
-static unsigned int gbuffers = 4;
-static unsigned int gbufsize = BTTV_MAX_FBUF;
+static unsigned int gbuffers = 8;
+static unsigned int gbufsize = 0x208000;
 
-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;
 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;
+static int debug_latency = 0;
+
+static unsigned int fdsr = 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;
+static unsigned int whitecrush_upper = 0xCF;
+static unsigned int whitecrush_lower = 0x7F;
+static unsigned int vcr_hack    = 0;
+static unsigned int irq_iswitch = 0;
+
+/* API features (turn on/off stuff for testing) */
+static unsigned int v4l2       = 1;
+
+
+/* insmod args */
 MODULE_PARM(radio,"1-" __stringify(BTTV_MAX) "i");
 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");
@@ -101,10 +92,16 @@
 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(gpint,"i");
+
+MODULE_PARM(video_nr,"i");
+MODULE_PARM(radio_nr,"i");
+MODULE_PARM(vbi_nr,"i");
+MODULE_PARM(debug_latency,"i");
+
+MODULE_PARM(fdsr,"i");
 
 MODULE_PARM(combfilter,"i");
 MODULE_PARM(lumafilter,"i");
@@ -114,12 +111,18 @@
 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(whitecrush_upper,"i");
+MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
+MODULE_PARM(whitecrush_lower,"i");
+MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
+MODULE_PARM(vcr_hack,"i");
+MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
+MODULE_PARM(irq_iswitch,"i");
+MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
 
-MODULE_PARM(video_nr,"i");
-MODULE_PARM(radio_nr,"i");
-MODULE_PARM(vbi_nr,"i");
+MODULE_PARM(v4l2,"i");
 
-MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards");
+MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
 MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
 MODULE_LICENSE("GPL");
 
@@ -129,303 +132,12 @@
 __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)
-
-#define BURSTOFFSET 76
-#define BTTV_ERRORS 5
-
-
-/*******************************/
-/* Memory management functions */
-/*******************************/
-
-
-static inline unsigned long kvirt_to_bus(unsigned long adr) 
-{
-        unsigned long kva;
-
-	kva = (unsigned long)page_address(vmalloc_to_page((void *)adr));
-	kva |= adr & (PAGE_SIZE-1); /* restore the offset */   
-	return virt_to_bus((void *)kva);
-}
-
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the
- * area and marking the pages as reserved.
- */
-static inline unsigned long kvirt_to_pa(unsigned long adr) 
-{
-        unsigned long kva;
-
-	kva = (unsigned long)page_address(vmalloc_to_page((void *)adr));
-	kva |= adr & (PAGE_SIZE-1); /* restore the offset */
-	return __pa(kva);
-}
-
-static void * rvmalloc(signed long size)
-{
-	struct page *page;
-	void * mem;
-	unsigned long adr;
-
-	mem=vmalloc_32(size);
-	if (NULL == mem)
-		printk(KERN_INFO "bttv: vmalloc_32(%ld) failed\n",size);
-	else {
-		/* Clear the ram out, no junk to the user */
-		memset(mem, 0, size);
-	        adr=(unsigned long) mem;
-		while (size > 0) {
-			page = vmalloc_to_page((void *)adr);
-			mem_map_reserve(page);
-			adr+=PAGE_SIZE;
-			size-=PAGE_SIZE;
-		}
-	}
-	return mem;
-}
-
-static void rvfree(void * mem, signed long size)
-{
-	struct page *page;
-        unsigned long adr;
-        
-	if (mem) {
-	        adr=(unsigned long) mem;
-		while (size > 0) {
-			page = vmalloc_to_page((void *)adr);
-			mem_map_unreserve(page);
-			adr+=PAGE_SIZE;
-			size-=PAGE_SIZE;
-		}
-		vfree(mem);
-	}
-}
-
-
-
-/*
- *	Create the giant waste of buffer space we need for now
- *	until we get DMA to user space sorted out (probably 2.3.x)
- *
- *	We only create this as and when someone uses mmap
- */
- 
-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;
-}
-
 /* ----------------------------------------------------------------------- */
+/* sysfs                                                                   */
 
-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)
-{
-	if (bttv_tvcards[btv->type].gpiomask)
-		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 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;
-	if (bttv_tvcards[btv->type].gpiomask)
-		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 (!in_interrupt())
-		bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode));
-}
-
-
-static inline void bt848_dma(struct bttv *btv, uint state)
-{
-	if (state)
-		btor(3, BT848_GPIO_DMA_CTL);
-	else
-		btand(~3, BT848_GPIO_DMA_CTL);
-}
-
-
-/* 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) 
-   PLL_C = Post divider (0=6, 1=4)
-   PLL_I = Integer input 
-   PLL_F = Fractional input 
-   
-   F_input = 28.636363 MHz: 
-   PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
-*/
-
-static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
-{
-        unsigned char fl, fh, fi;
-        
-        /* prevent overflows */
-        fin/=4;
-        fout/=4;
-
-        fout*=12;
-        fi=fout/fin;
-
-        fout=(fout%fin)*256;
-        fh=fout/fin;
-
-        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 void set_pll(struct bttv *btv)
-{
-        int i;
-
-        if (!btv->pll.pll_crystal)
-                return;
-
-	if (btv->pll.pll_ofreq == btv->pll.pll_current) {
-		dprintk("bttv%d: PLL: no change required\n",btv->nr);
-                return;
-        }
-
-        if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
-                /* no PLL needed */
-                if (btv->pll.pll_current == 0)
-                        return;
-		vprintk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n",
-			btv->nr,btv->pll.pll_ifreq);
-                btwrite(0x00,BT848_TGCTRL);
-                btwrite(0x00,BT848_PLL_XCI);
-                btv->pll.pll_current = 0;
-                return;
-        }
-
-	vprintk(KERN_INFO "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);
-
-        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;
-			vprintk(" ok\n");
-                        return;
-                }
-        }
-        btv->pll.pll_current = -1;
-	vprintk("failed\n");
-        return;
-}
-
-static void bt848_muxsel(struct bttv *btv, unsigned int input)
-{
-	dprintk("bttv%d: bt848_muxsel %d\n",btv->nr,input);
-
-	if (bttv_tvcards[btv->type].muxsel[input] < 0) {
-		dprintk("bttv%d: digital ccir muxsel\n", btv->nr);
-		btv->channel = input;
-		return;
-	}
-
-        /* needed by RemoteVideo MX */
-	if (bttv_tvcards[btv->type].gpiomask2)
-		btaor(bttv_tvcards[btv->type].gpiomask2,
-		      ~bttv_tvcards[btv->type].gpiomask2,
-		      BT848_GPIO_OUT_EN);
-
-#if 0
-	/* This seems to get rid of some synchronization problems */
-	btand(~(3<<5), BT848_IFORM);
-	mdelay(10);
-#endif
-
-	input %= bttv_tvcards[btv->type].video_inputs;
-	if (input == btv->svhs) {
-		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
-		btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
-	} else {
-		btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
-		btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
-	}
-
-	btaor((bttv_tvcards[btv->type].muxsel[input]&3)<<5, ~(3<<5), BT848_IFORM);
-	audio(btv, (input!=bttv_tvcards[btv->type].tuner) ?
-              AUDIO_EXTERN : AUDIO_TUNER);
-
-	if (bttv_tvcards[btv->type].gpiomask2)
-		btaor(bttv_tvcards[btv->type].muxsel[input]>>4,
-		      ~bttv_tvcards[btv->type].gpiomask2,
-		      BT848_GPIO_DATA);
-
-	/* card specific hook */
-	if (bttv_tvcards[btv->type].muxsel_hook)
-		bttv_tvcards[btv->type].muxsel_hook(btv, input);
-
-	if (bttv_gpio)
-		bttv_gpio_tracking(btv,"muxsel");
 
-	btv->channel=input;
-}
+/* ----------------------------------------------------------------------- */
+/* static data                                                             */
 
 /* special timing tables from conexant... */
 static u8 SRAM_Table[][60] =
@@ -461,24 +173,13 @@
 	}
 };
 
-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;
-	int sram; /* index into SRAM_Table */
-};
-
-static struct tvnorm tvnorms[] = {
+const struct bttv_tvnorm bttv_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 */
 	{
+		.v4l2_id        = V4L2_STD_PAL,
+		.name           = "PAL",
 		.Fsc            = 35468950,
 		.swidth         = 924,
 		.sheight        = 576,
@@ -489,15 +190,12 @@
 		.scaledtwidth   = 1135,
 		.hdelayx1       = 186,
 		.hactivex1      = 924,
-#ifdef VIDEODAT_HACK
-		.vdelay         = VBI_MAXLINES*2,
-#else
 		.vdelay         = 0x20,
-#endif
 		.vbipack        = 255,
 		.sram           = 0,
 	},{
-                /* NTSC */
+		.v4l2_id        = V4L2_STD_NTSC_M,
+		.name           = "NTSC",
 		.Fsc            = 28636363,
 		.swidth         = 768,
 		.sheight        = 480,
@@ -512,7 +210,8 @@
 		.vbipack        = 144,
 		.sram           = 1,
 	},{
-		/* SECAM L */
+		.v4l2_id        = V4L2_STD_SECAM,
+		.name           = "SECAM",
 		.Fsc            = 35468950,
 		.swidth         = 924,
 		.sheight        = 576,
@@ -527,7 +226,8 @@
 		.vbipack        = 255,
 		.sram           = 0, /* like PAL, correct? */
 	},{
-		/* PAL-NC */
+		.v4l2_id        = V4L2_STD_PAL_Nc,
+		.name           = "PAL-Nc",
 		.Fsc            = 28636363,
 		.swidth         = 640,
 		.sheight        = 576,
@@ -542,7 +242,8 @@
 		.vbipack        = 144,
 		.sram           = -1,
 	},{
-		/* PAL-M */
+		.v4l2_id        = V4L2_STD_PAL_M,
+		.name           = "PAL-M",
 		.Fsc            = 28636363,
 		.swidth         = 640,
 		.sheight        = 480,
@@ -557,8 +258,9 @@
 		.vbipack        = 144,
 		.sram           = -1,
 	},{
-		/* PAL-N */
-		.Fsc            = 35468950,
+ 		.v4l2_id        = V4L2_STD_PAL_N,
+ 		.name           = "PAL-N",
+ 		.Fsc            = 35468950,
 		.swidth         = 768,
 		.sheight        = 576,
 		.totalwidth     = 1135,
@@ -572,7 +274,8 @@
 		.vbipack        = 144,
 		.sram           = -1,
 	},{
-		/* NTSC-Japan */
+		.v4l2_id        = V4L2_STD_NTSC_M_JP,
+		.name           = "NTSC-JP",
 		.Fsc            = 28636363,
 		.swidth         = 640,
 		.sheight        = 480,
@@ -587,2241 +290,3236 @@
 		.vbipack        = 144,
 		.sram           = -1,
 	},{
-		/* Quartzsight digital camera 
-		 * From Bt832 datasheet: 393x304 pixel @30Hz,
-		 * Visible: 352x288 pixel
-		 */
-                .Fsc            = 27000000,
-                .swidth         = 352,
-                .sheight        = 576, //2*288 ?
-                .totalwidth     = 392,
-                .adelay         = 0x68,
-                .bdelay         = 0x5d,
-                .iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
-                .scaledtwidth   = 392,
-                .hdelayx1       = 0x20,
-                .hactivex1      = 352,
-                .vdelay         = 0x08,
-                .vbipack        = 0, //255
-                .sram           = 2,
-        }
+		/* that one hopefully works with the strange timing
+		 * which video recorders produce when playing a NTSC
+		 * tape on a PAL TV ... */
+		.v4l2_id        = V4L2_STD_PAL_60,
+		.name           = "PAL-60",
+		.Fsc            = 35468950,
+		.swidth         = 924,
+		.sheight        = 480,
+		.totalwidth     = 1135,
+		.adelay         = 0x7f,
+		.bdelay         = 0x72,
+		.iform          = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+		.scaledtwidth   = 1135,
+		.hdelayx1       = 186,
+		.hactivex1      = 924,
+		.vdelay         = 0x1a,
+		.vbipack        = 255,
+		.vtotal         = 524,
+		.sram           = -1,
+	}
 };
-#define TVNORMS ARRAY_SIZE(tvnorms)
-
-/* used to switch between the bt848's analog/digital video capture modes */
-void bt848A_set_timing(struct bttv *btv)
-{
-	int i, len;
-	int table_idx = tvnorms[btv->win.norm].sram;
-	int fsc       = tvnorms[btv->win.norm].Fsc;
-
-	if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0) {
-		dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
-			btv->nr,table_idx);
+const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
 
-		/* timing change...reset timing generator address */
-       		btwrite(0x00, BT848_TGCTRL);
-       		btwrite(0x02, BT848_TGCTRL);
-       		btwrite(0x00, BT848_TGCTRL);
+/* ----------------------------------------------------------------------- */
+/* 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 unsigned int BTTV_FORMATS = ARRAY_SIZE(bttv_formats);
 
-		len=SRAM_Table[table_idx][0];
-		for(i = 1; i <= len; i++)
-			btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
-		btv->pll.pll_ofreq = 27000000;
+/* ----------------------------------------------------------------------- */
 
-		set_pll(btv);
-		btwrite(0x11, BT848_TGCTRL);
-		btwrite(0x41, BT848_DVSIF);
-	} else {
-		btv->pll.pll_ofreq = fsc;
-		set_pll(btv);
-		btwrite(0x0, BT848_DVSIF);
+#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_VCR_HACK    (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER   (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER   (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_PRIVATE_LASTP1      (V4L2_CID_PRIVATE_BASE + 8)
+
+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,
+	},{
+		.id            = V4L2_CID_CONTRAST,
+		.name          = "Contrast",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 128,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_SATURATION,
+		.name          = "Saturation",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 128,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_HUE,
+		.name          = "Hue",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 256,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},
+	/* --- audio --- */
+	{
+		.id            = V4L2_CID_AUDIO_MUTE,
+		.name          = "Mute",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_AUDIO_VOLUME,
+		.name          = "Volume",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 65535,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_AUDIO_BALANCE,
+		.name          = "Balance",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_AUDIO_BASS,
+		.name          = "Bass",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_AUDIO_TREBLE,
+		.name          = "Treble",
+		.minimum       = 0,
+		.maximum       = 65535,
+		.step          = 65535/100,
+		.default_value = 32768,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},
+	/* --- private --- */
+	{
+		.id            = V4L2_CID_PRIVATE_CHROMA_AGC,
+		.name          = "chroma agc",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_COMBFILTER,
+		.name          = "combfilter",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_AUTOMUTE,
+		.name          = "automute",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_LUMAFILTER,
+		.name          = "luma decimation filter",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_AGC_CRUSH,
+		.name          = "agc crush",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_VCR_HACK,
+		.name          = "vcr hack",
+		.minimum       = 0,
+		.maximum       = 1,
+		.type          = V4L2_CTRL_TYPE_BOOLEAN,
+	},{
+		.id            = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
+		.name          = "whitecrush upper",
+		.minimum       = 0,
+		.maximum       = 255,
+		.step          = 1,
+		.default_value = 0xCF,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
+	},{
+		.id            = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
+		.name          = "whitecrush lower",
+		.minimum       = 0,
+		.maximum       = 255,
+		.step          = 1,
+		.default_value = 0x7F,
+		.type          = V4L2_CTRL_TYPE_INTEGER,
 	}
-}
 
-static void bttv_set_norm(struct bttv *btv, int new_norm)
+};
+const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls);
+
+/* ----------------------------------------------------------------------- */
+/* resource management                                                     */
+
+static
+int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit)
 {
-	unsigned long irq_flags;
+	if (fh->resources & bit)
+		/* have it already allocated */
+		return 1;
 
-	if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0 &&
-	    bttv_tvcards[btv->type].digital_mode == DIGITAL_MODE_CAMERA) {
-		//override norm by quartzsight mode
-		new_norm=7;
-		dprintk("bttv%d: set_norm fix-up digital: %d\n",
-			btv->nr, new_norm);
-	}
-	       
-	if (btv->win.norm != new_norm) {
-		btv->win.norm = new_norm;
-		
-		make_vbitab(btv);
-		spin_lock_irqsave(&btv->s_lock, irq_flags);
-		bt848_set_winsize(btv);
-		spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-		
-		bt848A_set_timing(btv);
-		switch (btv->type) {
-		case BTTV_VOODOOTV_FM:
-			bttv_tda9880_setnorm(btv,new_norm);
-			break;
-#if 0
-		case BTTV_OSPREY540:
-			osprey_540_set_norm(btv,new_norm);
-			break;
-#endif
-		}
+	/* 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;
 }
 
-#define VBI_SPL 2044
-/* RISC command to write one VBI data line */
-#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL
+static
+int check_btres(struct bttv_fh *fh, int bit)
+{
+	return (fh->resources & bit);
+}
 
-static void make_vbitab(struct bttv *btv)
+static
+int locked_btres(struct bttv *btv, int bit)
 {
-	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));
+	return (btv->resources & bit);
+}
 
-	*(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));
+static
+void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits)
+{
+#if 1 /* DEBUG */
+	if ((fh->resources & bits) != bits) {
+		/* trying to free ressources not allocated by us ... */
+		printk("bttv: BUG! (btres)\n");
 	}
-	*(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));
+#endif
+	down(&btv->reslock);
+	fh->resources  &= ~bits;
+	btv->resources &= ~bits;
+	up(&btv->reslock);
 }
 
-static unsigned int fmtbppx2[16] = {
-        8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 
-};
+/* ----------------------------------------------------------------------- */
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC          */
 
-static unsigned int palette2fmt[] = {
-	UNSET,
-	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,
-	UNSET,
-	UNSET,
-	UNSET,
-	BT848_COLOR_FMT_RAW,
-	BT848_COLOR_FMT_YCrCb422,
-	BT848_COLOR_FMT_YCrCb411,
-	BT848_COLOR_FMT_YCrCb422,
-	BT848_COLOR_FMT_YCrCb411,
-};
-#define PALETTEFMT_MAX ARRAY_SIZE(palette2fmt)
+/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C 
+   PLL_X = Reference pre-divider (0=1, 1=2) 
+   PLL_C = Post divider (0=6, 1=4)
+   PLL_I = Integer input 
+   PLL_F = Fractional input 
+   
+   F_input = 28.636363 MHz: 
+   PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
+*/
 
-static int make_rawrisctab(struct bttv *btv, u32 *ro, u32 *re, u32 *vbuf)
+static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
 {
-	u32 line;
-	u32 bpl=1024;		/* bytes per line */
-	unsigned long vadr=(unsigned long) vbuf;
+        unsigned char fl, fh, fi;
+        
+        /* prevent overflows */
+        fin/=4;
+        fout/=4;
 
-	*(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 ... */
+        fout*=12;
+        fi=fout/fin;
 
-	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);
-	
-	return 0;
+        fout=(fout%fin)*256;
+        fh=fout/fin;
+
+        fout=(fout%fin)*256;
+        fl=fout/fin;
+
+        btwrite(fl, BT848_PLL_F_LO);
+        btwrite(fh, BT848_PLL_F_HI);
+        btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
 }
 
-static int  make_prisctab(struct bttv *btv, u32 *ro, u32 *re, u32 *vbuf,
-			  u16 width, u16 height, u16 fmt)
+static void set_pll(struct bttv *btv)
 {
-	u16 line, lmask;
-	u32 bl, blcr, blcb, rcmd;
-	u32 todo;
-	u32 **rp;
-	int inter;
-	unsigned long cbadr, cradr;
-	unsigned long vadr=(unsigned long) vbuf;
-	u32 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;
+        int i;
 
-	        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));
+        if (!btv->pll.pll_crystal)
+                return;
 
-	return 0;
+	if (btv->pll.pll_ofreq == btv->pll.pll_current) {
+		dprintk("bttv%d: PLL: no change required\n",btv->c.nr);
+                return;
+        }
+
+        if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
+                /* no PLL needed */
+                if (btv->pll.pll_current == 0)
+                        return;
+		vprintk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n",
+			btv->c.nr,btv->pll.pll_ifreq);
+                btwrite(0x00,BT848_TGCTRL);
+                btwrite(0x00,BT848_PLL_XCI);
+                btv->pll.pll_current = 0;
+                return;
+        }
+
+	vprintk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr,
+		btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+	set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+
+        for (i=0; i<10; i++) {
+		/*  Let other people run while the PLL stabilizes */
+		vprintk(".");
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ/50);
+		
+                if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+			btwrite(0,BT848_DSTATUS);
+                } else {
+                        btwrite(0x08,BT848_TGCTRL);
+                        btv->pll.pll_current = btv->pll.pll_ofreq;
+			vprintk(" ok\n");
+                        return;
+                }
+        }
+        btv->pll.pll_current = -1;
+	vprintk("failed\n");
+        return;
 }
 
-static int  make_vrisctab(struct bttv *btv, u32 *ro, u32 *re, u32 *vbuf,
-			  u16 width, u16 height, u16 palette)
+/* used to switch between the bt848's analog/digital video capture modes */
+void bt848A_set_timing(struct bttv *btv)
 {
-	u16 line;
-	u32 bpl;  /* bytes per line */
-	u32 bl;
-	u32 todo;
-	u32 **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;
-	
-	*(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;
-		}
-	}
-	
-	*(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);
+	int i, len;
+	int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+	int fsc       = bttv_tvnorms[btv->tvnorm].Fsc;
 
-	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;
+	if (UNSET == bttv_tvcards[btv->c.type].muxsel[btv->input]) {
+		dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
+			btv->c.nr,table_idx);
+
+		/* timing change...reset timing generator address */
+       		btwrite(0x00, BT848_TGCTRL);
+       		btwrite(0x02, BT848_TGCTRL);
+       		btwrite(0x00, BT848_TGCTRL);
+
+		len=SRAM_Table[table_idx][0];
+		for(i = 1; i <= len; i++)
+			btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
+		btv->pll.pll_ofreq = 27000000;
+
+		set_pll(btv);
+		btwrite(0x11, BT848_TGCTRL);
+		btwrite(0x41, BT848_DVSIF);
+	} else {
+		btv->pll.pll_ofreq = fsc;
+		set_pll(btv);
+		btwrite(0x0, BT848_DVSIF);
+	}
 }
 
-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)
+static void bt848_bright(struct bttv *btv, int bright)
 {
-        unsigned char lmask, rmask, *p;
-        int W, l, r;
-	int i;
+	int value;
 
-	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;
-        }
-               
+	// printk("bttv: set bright: %d\n",bright); // DEBUG
+	btv->bright = bright;
 
+	/* We want -128 to 127 we get 0-65535 */
+	value = (bright >> 8) - 128;
+	btwrite(value & 0xff, BT848_BRIGHT);
 }
 
-static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr)
+static void bt848_hue(struct bttv *btv, int hue)
 {
-	u32 line, x, y, bpl, width, height, inter, maxw;
-	u32 bpp, dx, sx, **rp, *ro, *re, flags, len;
-	u32 adr;
-	int i;
-	unsigned char *clipmap, *clipline, cbit, lastbit, outofmem;
+	int value;
+	
+	btv->hue = hue;
 
-	/* 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);
-		return;
-	}
-	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);
+	/* -128 to 127 */
+	value = (hue >> 8) - 128;
+        btwrite(value & 0xff, BT848_HUE);
+}
+
+static void bt848_contrast(struct bttv *btv, int cont)
+{
+	int value,hibit;
 	
-	/* 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;
-	}
-
-	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);
-
-	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);
+	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);
 }
 
-/*
- *	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)
-{
-        u16 vscale, hscale;
-	u32 xsf, sr;
-	u16 hdelay;
-	u8 crop, vtc;
-
-	int inter = (height>tvn->sheight/2) ? 0 : 1;
-        int off = odd ? 0x80 : 0x00;
-
-	int swidth       = tvn->swidth;
-	int totalwidth   = tvn->totalwidth;
-	int scaledtwidth = tvn->scaledtwidth;
-
-	if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0) {
-		dprintk("bttv%d: DIGITAL_MODE_VIDEO override width\n",btv->nr);
-		swidth       = 720;
-		totalwidth   = 858;
-		scaledtwidth = 858;
-	}
-
-	xsf = (width*scaledtwidth)/swidth;
-	hscale = ((totalwidth*4096UL)/xsf-4096);
-	hdelay =  tvn->hdelayx1;
-	hdelay =  (hdelay*width)/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);
-	}
+static void bt848_sat(struct bttv *btv, int color)
+{
+	int val_u,val_v,hibits;
+	
+	btv->saturation = color;
 
-	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);
+	/* 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 void bt848_set_geo(struct bttv *btv)
+static int
+video_mux(struct bttv *btv, unsigned int input)
 {
-	u16 ewidth, eheight, owidth, oheight;
-	u16 format, bswap;
-	struct tvnorm *tvn;
-
-	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);
-	if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0)
-		btwrite(0x39, BT848_VBI_PACK_DEL);
-	else
-		btwrite(0x01, BT848_VBI_PACK_DEL);
+	int mux,mask2;
 
-        btv->pll.pll_ofreq = tvn->Fsc;
-	if (!in_interrupt())
-		set_pll(btv);
+	if (input >= bttv_tvcards[btv->c.type].video_inputs)
+		return -EINVAL;
 
-	btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0;
+        /* needed by RemoteVideo MX */
+	mask2 = bttv_tvcards[btv->c.type].gpiomask2;
+	if (mask2)
+		gpio_inout(mask2,mask2);
 
-	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 (input == btv->svhs)  {
+		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+		btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
 	} 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;
+		btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+		btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
 	}
+	mux = bttv_tvcards[btv->c.type].muxsel[input] & 3;
+	btaor(mux<<5, ~(3<<5), BT848_IFORM);
+	dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
+		btv->c.nr,input,mux);
 
-	/* program odd + even fields */
-	bt848_set_eogeo(btv, tvn, 1, owidth, oheight);
-	bt848_set_eogeo(btv, tvn, 0, ewidth, eheight);
-
-	btwrite(format, BT848_COLOR_FMT);
-	btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+	/* card specific hook */
+	if(bttv_tvcards[btv->c.type].muxsel_hook)
+		bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
+	return 0;
 }
 
-
-static int bpp2fmt[4] = {
-        BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16,
-        BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 
+static char *audio_modes[] = {
+	"audio: tuner", "audio: radio", "audio: extern",
+	"audio: intern", "audio: off"
 };
 
-static void bt848_set_winsize(struct bttv *btv)
+static int
+audio_mux(struct bttv *btv, int mode)
 {
-        unsigned short format;
+	int val,mux,i2c_mux,signal;
 
-	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);
+	gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+		   bttv_tvcards[btv->c.type].gpiomask);
+	signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
+
+	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;
+	}
+	i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio;
+	if (btv->opt_automute && !signal && !btv->radio_user)
+		mux = AUDIO_OFF;
+#if 0
+	printk("bttv%d: amux: mode=%d audio=%d signal=%s mux=%d/%d irq=%s\n",
+	       btv->c.nr, mode, btv->audio, signal ? "yes" : "no",
+	       mux, i2c_mux, in_interrupt() ? "yes" : "no");
+#endif
 
-        bt848_set_geo(btv);
+	val = bttv_tvcards[btv->c.type].audiomux[mux];
+	gpio_bits(bttv_tvcards[btv->c.type].gpiomask,val);
+	if (bttv_gpio)
+		bttv_gpio_tracking(btv,audio_modes[mux]);
+	if (!in_interrupt())
+		bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(i2c_mux));
+	return 0;
 }
 
-/*
- *	Grab into virtual memory.
- */
+static void
+i2c_vidiocschan(struct bttv *btv)
+{
+	struct video_channel c;
+
+	memset(&c,0,sizeof(c));
+	c.norm    = btv->tvnorm;
+	c.channel = btv->input;
+	bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
+	if (btv->c.type == BTTV_VOODOOTV_FM)
+		bttv_tda9880_setnorm(btv,c.norm);
+}
 
-static int vgrab(struct bttv *btv, struct video_mmap *mp)
+static int
+set_tvnorm(struct bttv *btv, unsigned int norm)
 {
-	u32 *ro, *re;
-	u32 *vbuf;
-	unsigned long flags;
-	
-	if(btv->fbuffer==NULL)
-	{
-		if(fbuffer_alloc(btv))
-			return -ENOBUFS;
-	}
+	const struct bttv_tvnorm *tvnorm;
 
-	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)
+	if (norm < 0 || norm >= BTTV_TVNORMS)
 		return -EINVAL;
 
-	if ((unsigned int)mp->height * mp->width *
-	    fmtbppx2[palette2fmt[mp->format]&0x0f]/2 > gbufsize)
-		return -EINVAL;
-	if (UNSET == palette2fmt[mp->format])
-		return -EINVAL;
+	btv->tvnorm = norm;
+	tvnorm = &bttv_tvnorms[norm];
 
-	/*
-	 *	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;
+	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);
+	bt848A_set_timing(btv);
+
+	switch (btv->c.type) {
+	case BTTV_VOODOOTV_FM:
+		bttv_tda9880_setnorm(btv,norm);
+		break;
+#if 0
+	case BTTV_OSPREY540:
+		osprey_540_set_norm(btv,norm);
+		break;
 #endif
-
-	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;
-
-	btor(3, BT848_CAP_CTL);
-	btor(3, BT848_GPIO_DMA_CTL);
-	spin_unlock_irqrestore(&btv->s_lock, flags);
+	}
 	return 0;
 }
 
-static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock)
+static void
+set_input(struct bttv *btv, unsigned int input)
 {
-	return -EINVAL;
+	unsigned long flags;
+	
+	btv->input = input;
+	if (irq_iswitch) {
+		spin_lock_irqsave(&btv->s_lock,flags);
+		if (btv->curr.irqflags) {
+			/* active capture -> delayed input switch */
+			btv->new_input = input;
+		} else {
+			video_mux(btv,input);
+		}
+		spin_unlock_irqrestore(&btv->s_lock,flags);
+	} else {
+		video_mux(btv,input);
+	}
+	audio_mux(btv,(input == bttv_tvcards[btv->c.type].tuner ?
+		       AUDIO_TUNER : AUDIO_EXTERN));
+	set_tvnorm(btv,btv->tvnorm);
 }
 
-static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+static void init_irqreg(struct bttv *btv)
 {
-	/* use bttv 0.9.x if you need capture via read() */
-	return -EINVAL;
-}
+	/* clear status */
+	btwrite(0xfffffUL, BT848_INT_STAT);
 
-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);
+	if (bttv_tvcards[btv->c.type].no_video) {
+		/* i2c only */
+		btwrite(BT848_INT_I2CDONE,
+			BT848_INT_MASK);
+	} else {
+		/* full video */
+		btwrite((btv->triton1)  |
+			(btv->gpioirq ? BT848_INT_GPINT : 0) |
+			BT848_INT_SCERR |
+			(fdsr ? BT848_INT_FDSR : 0) |
+			BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
+			BT848_INT_FMTCHG|BT848_INT_HLOCK|
+			BT848_INT_I2CDONE,
+			BT848_INT_MASK);
+	}
 }
 
-/*
- * 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)
+static void init_bt848(struct bttv *btv)
 {
-	unsigned int i;
-	spin_lock(&btv->s_lock);
-
-	/* 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);
+	int val;
+	
+	if (bttv_tvcards[btv->c.type].no_video) {
+		/* very basic init only */
+		init_irqreg(btv);
+		return;
+	}
 
-	/* flag the chip needs a restart */
-	btv->needs_restart = 1;
-	spin_unlock(&btv->s_lock);
+	btwrite(0x00, BT848_CAP_CTL);
+	btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+	btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
 
-	wake_up_interruptible(&btv->vbiq);
-	wake_up_interruptible(&btv->capq);
-}
+        /* 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);
 
-static void bt848_restart(struct bttv *btv)
+	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);
+
+	btwrite(whitecrush_upper, BT848_WC_UP);
+	btwrite(whitecrush_lower, BT848_WC_DOWN);
+
+	bt848_bright(btv,   btv->bright);
+	bt848_hue(btv,      btv->hue);
+	bt848_contrast(btv, btv->contrast);
+	bt848_sat(btv,      btv->saturation);
+
+	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);
+	}
+
+        /* interrupt */
+	init_irqreg(btv);
+}
+
+extern void bttv_reinit_bt848(struct bttv *btv)
 {
- 	unsigned long irq_flags;
+	unsigned long flags;
 
 	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);
-
-	/* enforce pll reprogramming */
-	btv->pll.pll_current = -1;
-	set_pll(btv);
-
-	spin_lock_irqsave(&btv->s_lock, irq_flags);
-	btv->errors = 0;
-	btv->needs_restart = 0;
-	bt848_set_geo(btv);
-	bt848_set_risc_jmps(btv,-1);
-	spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-}
+		printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->c.nr);
+	spin_lock_irqsave(&btv->s_lock,flags);
+	btv->errors=0;
+	bttv_set_dma(btv,0,0);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
 
-/*
- *	Open a bttv card. Right now the flags stuff is just playing
- */
+	init_bt848(btv);
+        btv->pll.pll_current = -1;
+	set_input(btv,btv->input);
+}
 
-static int bttv_open(struct video_device *dev, int flags)
+static int get_control(struct bttv *btv, struct v4l2_control *c)
 {
-	struct bttv *btv = (struct bttv *)dev;
-        unsigned int i;
-	int ret;
-
-	ret = -EBUSY;
-	if (bttv_debug)
-		printk("bttv%d: open called\n",btv->nr);
-
-	down(&btv->lock);
-	if (btv->user)
-		goto out_unlock;
+	struct video_audio va;
+	int i;
 	
-	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;
+	for (i = 0; i < BTTV_CTLS; i++)
+		if (bttv_ctls[i].id == c->id)
+			break;
+	if (i == BTTV_CTLS)
+		return -EINVAL;
+	if (i >= 4 && i <= 8) {
+		memset(&va,0,sizeof(va));
+		bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+		if (btv->audio_hook)
+			btv->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;
 
- out_unlock:
-	up(&btv->lock);
-	return ret;
+	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;
+
+	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;
+	case V4L2_CID_PRIVATE_VCR_HACK:
+		c->value = btv->opt_vcr_hack;
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+		c->value = btv->opt_whitecrush_upper;
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+		c->value = btv->opt_whitecrush_lower;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
 }
 
-static void bttv_close(struct video_device *dev)
+static int set_control(struct bttv *btv, struct v4l2_control *c)
 {
-	struct bttv *btv=(struct bttv *)dev;
- 	unsigned long irq_flags;
-	int need_wait;
+	struct video_audio va;
+	int i,val;
 
-	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 */
-	}
-	
-	/*
-	 *	We have allowed it to drain.
-	 */
-
-	if(btv->fbuffer)
-		rvfree((void *) btv->fbuffer, gbuffers*gbufsize);
-	btv->fbuffer=0;
-	up(&btv->lock);
+	for (i = 0; i < BTTV_CTLS; i++)
+		if (bttv_ctls[i].id == c->id)
+			break;
+	if (i == BTTV_CTLS)
+		return -EINVAL;
+	if (i >= 4 && i <= 8) {
+		memset(&va,0,sizeof(va));
+		bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+		if (btv->audio_hook)
+			btv->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;
+	case V4L2_CID_PRIVATE_VCR_HACK:
+		btv->opt_vcr_hack = c->value;
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+		btv->opt_whitecrush_upper = c->value;
+		btwrite(c->value, BT848_WC_UP);
+		break;
+	case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+		btv->opt_whitecrush_lower = c->value;
+		btwrite(c->value, BT848_WC_DOWN);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (i >= 4 && i <= 8) {
+		bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
+		if (btv->audio_hook)
+			btv->audio_hook(btv,&va,1);
+	}
+	return 0;
 }
 
+/* ----------------------------------------------------------------------- */
 
-/***********************************/
-/* ioctls and supporting functions */
-/***********************************/
+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->c.nr,outbits,data & outbits, data & ~outbits, comment);
+}
 
-static inline void bt848_bright(struct bttv *btv, uint bright)
+void bttv_field_count(struct bttv *btv)
 {
-	btwrite(bright&0xff, BT848_BRIGHT);
+	int need_count = 0;
+
+	if (btv->users)
+		need_count++;
+
+	if (need_count) {
+		/* start field counter */
+		btor(BT848_INT_VSYNC,BT848_INT_MASK);
+	} else {
+		/* stop field counter */
+		btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+		btv->field_count = 0;
+	}
 }
 
-static inline void bt848_hue(struct bttv *btv, uint hue)
+static const struct bttv_format*
+format_by_palette(int palette)
 {
-	btwrite(hue&0xff, BT848_HUE);
+	unsigned int i;
+
+	for (i = 0; i < BTTV_FORMATS; i++) {
+		if (-1 == bttv_formats[i].palette)
+			continue;
+		if (bttv_formats[i].palette == palette)
+			return bttv_formats+i;
+	}
+	return NULL;
 }
 
-static inline void bt848_contrast(struct bttv *btv, uint cont)
+static const struct bttv_format*
+format_by_fourcc(int fourcc)
 {
-	unsigned int conthi;
+	unsigned int i;
 
-	conthi=(cont>>6)&4;
-	btwrite(cont&0xff, BT848_CONTRAST_LO);
-	btaor(conthi, ~4, BT848_E_CONTROL);
-	btaor(conthi, ~4, BT848_O_CONTROL);
+	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;
 }
 
-static inline void bt848_sat_u(struct bttv *btv, unsigned long data)
+/* ----------------------------------------------------------------------- */
+/* misc helpers                                                            */
+
+static int
+bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+		    struct bttv_buffer *new)
 {
-	u32 datahi;
+	struct bttv_buffer *old;
+	unsigned long flags;
+	int retval = 0;
 
-	datahi=(data>>7)&2;
-	btwrite(data&0xff, BT848_SAT_U_LO);
-	btaor(datahi, ~2, BT848_E_CONTROL);
-	btaor(datahi, ~2, BT848_O_CONTROL);
+	dprintk("switch_overlay: enter [new=%p]\n",new);
+	if (new)
+		new->vb.state = STATE_DONE;
+	spin_lock_irqsave(&btv->s_lock,flags);
+	old = btv->screen;
+	btv->screen = new;
+	btv->curr.irqflags |= 1;
+	bttv_set_dma(btv, 0x03, btv->curr.irqflags);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+	if (NULL == new)
+		free_btres(btv,fh,RESOURCE_OVERLAY);
+	if (NULL != old) {
+		dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state);
+		bttv_dma_free(btv, old);
+		kfree(old);
+	}
+	dprintk("switch_overlay: done\n");
+	return retval;
 }
 
-static inline void bt848_sat_v(struct bttv *btv, unsigned long data)
+/* ----------------------------------------------------------------------- */
+/* video4linux (1) interface                                               */
+
+static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf,
+ 			       const struct bttv_format *fmt,
+			       unsigned int width, unsigned int height,
+			       enum v4l2_field field)
 {
-	u32 datahi;
+	int redo_dma_risc = 0;
+	int rc;
+	
+	/* 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;
+	}
+	
+	/* alloc + fill struct bttv_buffer (if changed) */
+	if (buf->vb.width != width || buf->vb.height != height ||
+	    buf->vb.field != field ||
+	    buf->tvnorm != btv->tvnorm || buf->fmt != fmt) {
+		buf->vb.width  = width;
+		buf->vb.height = height;
+		buf->vb.field  = field;
+		buf->tvnorm    = btv->tvnorm;
+		buf->fmt       = fmt;
+		redo_dma_risc = 1;
+	}
+
+	/* alloc risc memory */
+	if (STATE_NEEDS_INIT == buf->vb.state) {
+		redo_dma_risc = 1;
+		if (0 != (rc = videobuf_iolock(btv->c.pci,&buf->vb,&btv->fbuf)))
+			goto fail;
+	}
+
+	if (redo_dma_risc)
+		if (0 != (rc = bttv_buffer_risc(btv,buf)))
+			goto fail;
+
+	buf->vb.state = STATE_PREPARED;
+	return 0;
 
-	datahi=(data>>8)&1;
-	btwrite(data&0xff, BT848_SAT_V_LO);
-	btaor(datahi, ~1, BT848_E_CONTROL);
-	btaor(datahi, ~1, BT848_O_CONTROL);
+ fail:
+	bttv_dma_free(btv,buf);
+	return rc;
 }
 
-/*
- *	ioctl routine
- */
+static int
+buffer_setup(struct file *file, unsigned int *count, unsigned int *size)
+{
+	struct bttv_fh *fh = file->private_data;
+	
+	*size = fh->fmt->depth*fh->width*fh->height >> 3;
+	if (0 == *count)
+		*count = gbuffers;
+	while (*size * *count > gbuffers * gbufsize)
+		(*count)--;
+	return 0;
+}
 
-static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static int
+buffer_prepare(struct file *file, struct videobuf_buffer *vb,
+	       enum v4l2_field field)
 {
-	struct bttv *btv=(struct bttv *)dev;
- 	unsigned long irq_flags;
- 	int ret = 0;
+	struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+	struct bttv_fh *fh = file->private_data;
 
-	if (bttv_debug > 1)
-		printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd);
+	return bttv_prepare_buffer(fh->btv, buf, fh->fmt,
+				   fh->width, fh->height, field);
+}
 
+static void
+buffer_queue(struct file *file, struct videobuf_buffer *vb)
+{
+	struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+	struct bttv_fh *fh = file->private_data;
+
+	buf->vb.state = STATE_QUEUED;
+	list_add_tail(&buf->vb.queue,&fh->btv->capture);
+	fh->btv->curr.irqflags |= 1;
+	bttv_set_dma(fh->btv, 0x03, fh->btv->curr.irqflags);
+}
+
+static void buffer_release(struct file *file, struct videobuf_buffer *vb)
+{
+	struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+	struct bttv_fh *fh = file->private_data;
+
+	bttv_dma_free(fh->btv,buf);
+}
+
+static struct videobuf_queue_ops bttv_video_qops = {
+	.buf_setup    = buffer_setup,
+	.buf_prepare  = buffer_prepare,
+	.buf_queue    = buffer_queue,
+	.buf_release  = buffer_release,
+};
+
+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 ARRAY_SIZE(v4l1_ioctls)
+
+int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
+{
 	switch (cmd) {
-	case VIDIOCGCAP:
+        case BTTV_VERSION:
+                return BTTV_VERSION_CODE;
+
+	/* ***  v4l1  *** ************************************************ */
+	case VIDIOCGFREQ:
 	{
-		struct video_capability b;
-		strcpy(b.name,btv->video_dev.name);
-		b.type = VID_TYPE_CAPTURE|
-			((bttv_tvcards[btv->type].tuner != UNSET) ? VID_TYPE_TUNER : 0) |
-			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;
+		unsigned long *freq = arg;
+		*freq = btv->freq;
 		return 0;
 	}
-	case VIDIOCGCHAN:
+	case VIDIOCSFREQ:
 	{
-		struct video_channel v;
-		unsigned int  channel;
+		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;
+	}
 
-		if(copy_from_user(&v, arg,sizeof(v)))
-			return -EFAULT;
-		channel = v.channel;
-		if (channel>=bttv_tvcards[btv->type].video_inputs)
-			return -EINVAL;
-		v.flags=VIDEO_VC_AUDIO;
-		v.tuners=0;
-		v.type=VIDEO_TYPE_CAMERA;
-		v.norm = btv->win.norm;
-		if(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 (channel == btv->svhs) 
-			strcpy(v.name,"S-Video");
-		else if (bttv_tvcards[btv->type].muxsel[v.channel] < 0)
-			strcpy(v.name,"Digital Video");
-		else
-			sprintf(v.name,"Composite%d",v.channel);
+	case VIDIOCGTUNER:
+	{
+		struct video_tuner *v = arg;
 		
-		if(copy_to_user(arg,&v,sizeof(v)))
-			return -EFAULT;
+		if (UNSET == bttv_tvcards[btv->c.type].tuner)
+			return -EINVAL;
+		if (v->tuner) /* Only tuner 0 */
+			return -EINVAL;
+		strcpy(v->name, "Television");
+		v->rangelow  = 0;
+		v->rangehigh = 0x7FFFFFFF;
+		v->flags     = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
+		v->mode      = btv->tvnorm;
+		v->signal    = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
+		bttv_call_i2c_clients(btv,cmd,v);
 		return 0;
 	}
-	/*
-	 *	Each channel has 1 tuner
-	 */
-	case VIDIOCSCHAN:
+	case VIDIOCSTUNER:
 	{
-		struct video_channel v;
-		unsigned int  channel;
+		struct video_tuner *v = arg;
 
-		if(copy_from_user(&v, arg,sizeof(v)))
-			return -EFAULT;
-		channel = v.channel;
-		
-		if (channel>bttv_tvcards[btv->type].video_inputs)
+		if (v->tuner) /* Only tuner 0 */
+			return -EINVAL;
+		if (v->mode >= BTTV_TVNORMS)
 			return -EINVAL;
-		if (v.norm > TVNORMS)
-			return -EOPNOTSUPP;
 
-		bttv_call_i2c_clients(btv,cmd,&v);
 		down(&btv->lock);
-		bt848_muxsel(btv, channel);
-		bttv_set_norm(btv, v.norm);
+		set_tvnorm(btv,v->mode);
+		bttv_call_i2c_clients(btv,cmd,v);
 		up(&btv->lock);
 		return 0;
 	}
-	case VIDIOCGTUNER:
+	
+        case VIDIOCGCHAN:
+        {
+                struct video_channel *v = arg;
+		unsigned int channel = v->channel;
+
+                if (channel >= bttv_tvcards[btv->c.type].video_inputs)
+                        return -EINVAL;
+                v->tuners=0;
+                v->flags = VIDEO_VC_AUDIO;
+                v->type = VIDEO_TYPE_CAMERA;
+                v->norm = btv->tvnorm;
+		if (channel == bttv_tvcards[btv->c.type].tuner)  {
+                        strcpy(v->name,"Television");
+                        v->flags|=VIDEO_VC_TUNER;
+                        v->type=VIDEO_TYPE_TV;
+                        v->tuners=1;
+                } else if (channel == btv->svhs) {
+                        strcpy(v->name,"S-Video");
+                } else {
+                        sprintf(v->name,"Composite%d",channel);
+		}
+		return 0;
+        }
+        case VIDIOCSCHAN:
+        {
+                struct video_channel *v = arg;
+		unsigned int channel = v->channel;
+
+		if (channel >= bttv_tvcards[btv->c.type].video_inputs)
+			return -EINVAL;
+		if (v->norm >= BTTV_TVNORMS)
+			return -EINVAL;
+
+		down(&btv->lock);
+		if (channel == btv->input &&
+		    v->norm == btv->tvnorm) {
+			/* nothing to do */
+			up(&btv->lock);
+			return 0;
+		}
+
+		btv->tvnorm = v->norm;
+		set_input(btv,v->channel);
+		up(&btv->lock);
+		return 0;
+	}
+
+        case VIDIOCGAUDIO:
 	{
-		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_audio *v = arg;
+
+		memset(v,0,sizeof(*v));
+		strcpy(v->name,"Television");
+		v->flags |= VIDEO_AUDIO_MUTABLE;
+		v->mode  = VIDEO_SOUND_MONO;
+
+		down(&btv->lock);
+		bttv_call_i2c_clients(btv,cmd,v);
+
+		/* card specific hooks */
+		if (btv->audio_hook)
+			btv->audio_hook(btv,v,0);
+
+		up(&btv->lock);
+		return 0;
+	}
+	case VIDIOCSAUDIO:
+	{
+		struct video_audio *v = arg;
+		unsigned int audio = v->audio;
+
+		if (audio >= bttv_tvcards[btv->c.type].audio_inputs)
 			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(&btv->lock);
+		audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE);
+		bttv_call_i2c_clients(btv,cmd,v);
+
+		/* card specific hooks */
+		if (btv->audio_hook)
+			btv->audio_hook(btv,v,1);
+		
+		up(&btv->lock);
 		return 0;
 	}
-	/* We have but one tuner */
-	case VIDIOCSTUNER:
+
+	/* ***  v4l2  *** ************************************************ */
+	case VIDIOC_ENUMSTD:
 	{
-		struct video_tuner v;
-		unsigned int tuner;
+		struct v4l2_standard *e = arg;
+		unsigned int index = e->index;
 		
-		if(copy_from_user(&v, arg, sizeof(v)))
-			return -EFAULT;
-		tuner = v.tuner;
-		/* Only one channel has a tuner */
-		if(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) {
-                        down(&btv->lock);
-			bttv_set_norm(btv,v.mode);
-			up(&btv->lock);
-		}
+		if (index >= BTTV_TVNORMS)
+			return -EINVAL;
+		v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id,
+					 bttv_tvnorms[e->index].name);
+		e->index = index;
 		return 0;
 	}
-	case VIDIOCGPICT:
+	case VIDIOC_G_STD:
 	{
-		struct video_picture p=btv->picture;
-		if(copy_to_user(arg, &p, sizeof(p)))
-			return -EFAULT;
+		v4l2_std_id *id = arg;
+		*id = bttv_tvnorms[btv->tvnorm].v4l2_id;
 		return 0;
 	}
-	case VIDIOCSPICT:
+	case VIDIOC_S_STD:
 	{
-		struct video_picture p;
+		v4l2_std_id *id = arg;
+		unsigned int i;
 
-		if (copy_from_user(&p, arg,sizeof(p)))
-			return -EFAULT;
-		if (p.palette > PALETTEFMT_MAX)
+		for (i = 0; i < BTTV_TVNORMS; i++)
+			if (*id & bttv_tvnorms[i].v4l2_id)
+				break;
+		if (i == BTTV_TVNORMS)
 			return -EINVAL;
-		if (UNSET == palette2fmt[p.palette])
+
+		down(&btv->lock);
+		set_tvnorm(btv,i);
+		i2c_vidiocschan(btv);
+		up(&btv->lock);
+		return 0;
+	}
+	case VIDIOC_QUERYSTD:
+	{
+		v4l2_std_id *id = arg;
+		
+		if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+			*id = V4L2_STD_625_50;
+		else
+			*id = V4L2_STD_525_60;
+		return 0;
+	}
+
+	case VIDIOC_ENUMINPUT:
+	{
+		struct v4l2_input *i = arg;
+		unsigned int n;
+		
+		n = i->index;
+		if (n >= bttv_tvcards[btv->c.type].video_inputs)
+			return -EINVAL;
+		memset(i,0,sizeof(*i));
+		i->index    = n;
+		i->type     = V4L2_INPUT_TYPE_CAMERA;
+		i->audioset = 1;
+		if (i->index == bttv_tvcards[btv->c.type].tuner) {
+			sprintf(i->name, "Television");
+			i->type  = V4L2_INPUT_TYPE_TUNER;
+			i->tuner = 0;
+		} else if (i->index == btv->svhs) {
+			sprintf(i->name, "S-Video");
+		} else {
+                        sprintf(i->name,"Composite%d",i->index);
+		}
+		if (i->index == btv->input) {
+			__u32 dstatus = btread(BT848_DSTATUS);
+			if (0 == (dstatus & BT848_DSTATUS_PRES))
+				i->status |= V4L2_IN_ST_NO_SIGNAL;
+			if (0 == (dstatus & BT848_DSTATUS_HLOC))
+				i->status |= V4L2_IN_ST_NO_H_LOCK;
+		}
+		for (n = 0; n < BTTV_TVNORMS; n++)
+			i->std |= bttv_tvnorms[n].v4l2_id;
+		return 0;
+	}
+	case VIDIOC_G_INPUT:
+	{
+		int *i = arg;
+		*i = btv->input;
+		return 0;
+	}
+	case VIDIOC_S_INPUT:
+	{
+		unsigned int *i = arg;
+		
+		if (*i > bttv_tvcards[btv->c.type].video_inputs)
 			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;
+		set_input(btv,*i);
+		i2c_vidiocschan(btv);
 		up(&btv->lock);
 		return 0;
 	}
-	case VIDIOCSWIN:
+	
+	case VIDIOC_G_TUNER:
 	{
-		struct video_window vw;
-		struct video_clip *vcp = NULL;
-			
-		if(copy_from_user(&vw,arg,sizeof(vw)))
-			return -EFAULT;
+		struct v4l2_tuner *t = arg;
 
+		if (UNSET == bttv_tvcards[btv->c.type].tuner)
+			return -EINVAL;
+		if (0 != t->index)
+			return -EINVAL;
 		down(&btv->lock);
-		if(vw.flags || vw.width < 16 || vw.height < 16) 
+		memset(t,0,sizeof(*t));
+		strcpy(t->name, "Television");
+		t->type       = V4L2_TUNER_ANALOG_TV;
+		t->rangehigh  = 0xffffffffUL;
+		t->capability = V4L2_TUNER_CAP_NORM;
+		t->rxsubchans = V4L2_TUNER_SUB_MONO;
+		if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+			t->signal = 0xffff;
 		{
-			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;
-		}
-		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;
+			/* Hmmm ... */
+			struct video_audio va;
+			memset(&va, 0, sizeof(struct video_audio));
+			bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+			if (btv->audio_hook)
+				btv->audio_hook(btv,&va,0);
+			if(va.mode & VIDEO_SOUND_STEREO) {
+				t->audmode     = V4L2_TUNER_MODE_STEREO;
+				t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
 			}
-			if(copy_from_user(vcp, vw.clips,
-					  VIDEO_CLIPMAP_SIZE)) {
-				up(&btv->lock);
-				vfree(vcp);
-				return -EFAULT;
+			if(va.mode & VIDEO_SOUND_LANG1) {
+				t->audmode    = V4L2_TUNER_MODE_LANG1;
+				t->rxsubchans = V4L2_TUNER_SUB_LANG1
+					| V4L2_TUNER_SUB_LANG2;
 			}
-		} else if (vw.clipcount > 2048) {
-			up(&btv->lock);
+		}
+		/* FIXME: fill capability+audmode */
+		up(&btv->lock);
+		return 0;
+	}
+	case VIDIOC_S_TUNER:
+	{
+		struct v4l2_tuner *t = arg;
+
+		if (UNSET == bttv_tvcards[btv->c.type].tuner)
 			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;
-			}
+		if (0 != t->index)
+			return -EINVAL;
+		down(&btv->lock);
+		{
+			struct video_audio va;
+			memset(&va, 0, sizeof(struct video_audio));
+			bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+			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 (btv->audio_hook)
+				btv->audio_hook(btv,&va,1);
 		}
-		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);
 		return 0;
 	}
-	case VIDIOCGWIN:
+
+	case VIDIOC_G_FREQUENCY:
 	{
-		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_frequency *f = arg;
+
+		memset(f,0,sizeof(*f));
+		f->type = V4L2_TUNER_ANALOG_TV;
+		f->frequency = btv->freq;
 		return 0;
 	}
-	case VIDIOCCAPTURE:
+	case VIDIOC_S_FREQUENCY:
 	{
-		int v;
-		if(copy_from_user(&v, arg,sizeof(v)))
+		struct v4l2_frequency *f = arg;
+
+		if (unlikely(f->tuner != 0))
+			return -EINVAL;
+		if (unlikely(f->type != V4L2_TUNER_ANALOG_TV))
+			return -EINVAL;
+		down(&btv->lock);
+		btv->freq = f->frequency;
+		bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq);
+		if (btv->has_matchbox && btv->radio_user)
+			tea5757_set_freq(btv,btv->freq);
+		up(&btv->lock);
+		return 0;
+	}
+
+	default:
+		return -ENOIOCTLCMD;
+	
+	}
+	return 0;
+}
+
+static int verify_window(const struct bttv_tvnorm *tvn,
+			 struct v4l2_window *win, int fixup)
+{
+	enum v4l2_field field;
+	int maxw, maxh;
+
+	if (win->w.width  < 48 || win->w.height < 32)
+		return -EINVAL;
+	if (win->clipcount > 2048)
+		return -EINVAL;
+
+	field = win->field;
+	maxw  = tvn->swidth;
+	maxh  = tvn->sheight;
+
+	if (V4L2_FIELD_ANY == field) {
+		field = (win->w.height > maxh/2)
+			? V4L2_FIELD_INTERLACED
+			: V4L2_FIELD_TOP;
+	}
+	switch (field) {
+	case V4L2_FIELD_TOP:
+	case V4L2_FIELD_BOTTOM:
+		maxh = maxh / 2;
+		break;
+	case V4L2_FIELD_INTERLACED:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!fixup && (win->w.width > maxw || win->w.height > maxh))
+		return -EINVAL;
+
+	if (win->w.width > maxw)
+		win->w.width = maxw;
+	if (win->w.height > maxh)
+		win->w.height = maxh;
+	win->field = field;
+	return 0;
+}
+
+static int setup_window(struct bttv_fh *fh, struct bttv *btv,
+			struct v4l2_window *win, int fixup)
+{
+	struct v4l2_clip *clips = NULL;
+	int n,size,retval = 0;
+
+	if (NULL == fh->ovfmt)
+		return -EINVAL;
+	retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup);
+	if (0 != retval)
+		return retval;
+
+	/* copy clips  --  luckily v4l1 + v4l2 are binary
+	   compatible here ...*/
+	n = win->clipcount;
+	size = sizeof(*clips)*(n+4);
+	clips = kmalloc(size,GFP_KERNEL);
+	if (NULL == clips)
+		return -ENOMEM;
+	if (n > 0) {
+		if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
+			kfree(clips);
 			return -EFAULT;
-		if(btv->win.vidadr == 0)
+		}
+	}
+	/* clip against screen */
+	if (NULL != btv->fbuf.base)
+		n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
+				      &win->w, clips, n);
+	btcx_sort_clips(clips,n);
+
+	/* 4-byte alignments */
+	switch (fh->ovfmt->depth) {
+	case 8:
+	case 24:
+		btcx_align(&win->w, clips, n, 3);
+		break;
+	case 16:
+		btcx_align(&win->w, clips, n, 1);
+		break;
+	case 32:
+		/* no alignment fixups needed */
+		break;
+	default:
+		BUG();
+	}
+	
+	down(&fh->cap.lock);
+	if (fh->ov.clips)
+		kfree(fh->ov.clips);
+	fh->ov.clips    = clips;
+	fh->ov.nclips   = n;
+	
+	fh->ov.w        = win->w;
+	fh->ov.field    = win->field;
+	fh->ov.setup_ok = 1;
+	btv->init.ov.w.width   = win->w.width;
+	btv->init.ov.w.height  = win->w.height;
+	btv->init.ov.field     = win->field;
+	
+	/* update overlay if needed */
+	retval = 0;
+	if (check_btres(fh, RESOURCE_OVERLAY)) {
+		struct bttv_buffer *new;
+		
+		new = videobuf_alloc(sizeof(*new));
+		bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+		retval = bttv_switch_overlay(btv,fh,new);
+	}
+	up(&fh->cap.lock);
+	return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
+{
+	struct videobuf_queue* q = NULL;
+	
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		q = &fh->cap;
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		q = &fh->vbi;
+		break;
+	default:
+		BUG();
+	}
+	return q;
+}
+
+static int bttv_resource(struct bttv_fh *fh)
+{
+	int res = 0;
+	
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		res = RESOURCE_VIDEO;
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		res = RESOURCE_VBI;
+		break;
+	default:
+		BUG();
+	}
+	return res;
+}
+
+static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
+{
+	struct videobuf_queue *q = bttv_queue(fh);
+	int res = bttv_resource(fh);
+
+	if (check_btres(fh,res))
+		return -EBUSY;
+	if (videobuf_queue_is_busy(q))
+		return -EBUSY;
+	fh->type = type;
+	return 0;
+}
+
+static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+{
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format));
+		f->fmt.pix.width        = fh->width;
+		f->fmt.pix.height       = fh->height;
+		f->fmt.pix.field        = fh->cap.field;
+		f->fmt.pix.pixelformat  = fh->fmt->fourcc;
+		f->fmt.pix.bytesperline =
+			(f->fmt.pix.width * fh->fmt->depth) >> 3;
+		f->fmt.pix.sizeimage =
+			f->fmt.pix.height * f->fmt.pix.bytesperline;
+		return 0;
+	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+		memset(&f->fmt.win,0,sizeof(struct v4l2_window));
+		f->fmt.win.w     = fh->ov.w;
+		f->fmt.win.field = fh->ov.field;
+		return 0;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		bttv_vbi_get_fmt(fh,f);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv,
+			struct v4l2_format *f)
+{
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+	{
+		const struct bttv_format *fmt;
+		enum v4l2_field field;
+		unsigned int maxw,maxh;
+
+		fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+		if (NULL == fmt)
 			return -EINVAL;
-		if (btv->win.width==0 || btv->win.height==0)
+
+		/* fixup format */
+		maxw  = bttv_tvnorms[btv->tvnorm].swidth;
+		maxh  = bttv_tvnorms[btv->tvnorm].sheight;
+		field = f->fmt.pix.field;
+		if (V4L2_FIELD_ANY == field)
+			field = (f->fmt.pix.height > maxh/2)
+				? V4L2_FIELD_INTERLACED
+				: V4L2_FIELD_BOTTOM;
+		if (V4L2_FIELD_SEQ_BT == field)
+			field = V4L2_FIELD_SEQ_TB;
+		switch (field) {
+		case V4L2_FIELD_TOP:
+		case V4L2_FIELD_BOTTOM:
+		case V4L2_FIELD_ALTERNATE:
+			maxh = maxh/2;
+			break;
+		case V4L2_FIELD_INTERLACED:
+			break;
+		case V4L2_FIELD_SEQ_TB:
+			if (fmt->flags & FORMAT_FLAGS_PLANAR)
+				return -EINVAL;
+			break;
+		default:
 			return -EINVAL;
-		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);
+		}
+
+		/* update data for the application */
+		f->fmt.pix.field = field;
+		if (f->fmt.pix.width  < 48)
+			f->fmt.pix.width  = 48;
+		if (f->fmt.pix.height < 32)
+			f->fmt.pix.height = 32;
+		if (f->fmt.pix.width  > maxw)
+			f->fmt.pix.width = maxw;
+		if (f->fmt.pix.height > maxh)
+			f->fmt.pix.height = maxh;
+		f->fmt.pix.bytesperline =
+			(f->fmt.pix.width * fmt->depth) >> 3;
+		f->fmt.pix.sizeimage =
+			f->fmt.pix.height * f->fmt.pix.bytesperline;
+		
 		return 0;
 	}
+	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+		return verify_window(&bttv_tvnorms[btv->tvnorm],
+				     &f->fmt.win, 1);
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		bttv_vbi_try_fmt(fh,f);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv,
+		      struct v4l2_format *f)
+{
+	int retval;
+	
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+	{
+		const struct bttv_format *fmt;
+
+		retval = bttv_switch_type(fh,f->type);
+		if (0 != retval)
+			return retval;
+		retval = bttv_try_fmt(fh,btv,f);
+		if (0 != retval)
+			return retval;
+		fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+		
+		/* update our state informations */
+		down(&fh->cap.lock);
+		fh->fmt              = fmt;
+		fh->cap.field        = f->fmt.pix.field;
+		fh->cap.last         = V4L2_FIELD_NONE;
+		fh->width            = f->fmt.pix.width;
+		fh->height           = f->fmt.pix.height;
+		btv->init.fmt        = fmt;
+		btv->init.width      = f->fmt.pix.width;
+		btv->init.height     = f->fmt.pix.height;
+		up(&fh->cap.lock);
+		
+		return 0;
+	}
+	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+		return setup_window(fh, btv, &f->fmt.win, 1);
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		retval = bttv_switch_type(fh,f->type);
+		if (0 != retval)
+			return retval;
+		if (locked_btres(fh->btv, RESOURCE_VBI))
+                        return -EBUSY;
+		bttv_vbi_try_fmt(fh,f);
+		bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]);
+		bttv_vbi_get_fmt(fh,f);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int bttv_do_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, void *arg)
+{
+	struct bttv_fh *fh  = file->private_data;
+	struct bttv    *btv = fh->btv;
+	unsigned long flags;
+	int retval = 0;
+
+	if (bttv_debug > 1) {
+		switch (_IOC_TYPE(cmd)) {
+		case 'v':
+			printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n",
+			       btv->c.nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ?
+			       v4l1_ioctls[_IOC_NR(cmd)] : "???");
+			break;
+		case 'V':
+			printk("bttv%d: ioctl 0x%x (v4l2, %s)\n",
+			       btv->c.nr, cmd,  v4l2_ioctl_names[_IOC_NR(cmd)]);
+			break;
+		default:
+			printk("bttv%d: ioctl 0x%x (???)\n",
+			       btv->c.nr, cmd);
+		}
+	}
+	if (btv->errors)
+		bttv_reinit_bt848(btv);
+
+#ifdef VIDIOC_G_PRIORITY
+	switch (cmd) {
+        case VIDIOCSFREQ:
+        case VIDIOCSTUNER:
+        case VIDIOCSCHAN:
+	case VIDIOC_S_CTRL:
+	case VIDIOC_S_STD:
+	case VIDIOC_S_INPUT:
+	case VIDIOC_S_TUNER:
+	case VIDIOC_S_FREQUENCY:
+		retval = v4l2_prio_check(&btv->prio,&fh->prio);
+		if (0 != retval)
+			return retval;
+	};
+#endif
+	switch (cmd) {
+
+	/* ***  v4l1  *** ************************************************ */
+	case VIDIOCGCAP:
+	{
+                struct video_capability *cap = arg;
+
+		memset(cap,0,sizeof(*cap));
+                strcpy(cap->name,btv->video_dev->name);
+		if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+			/* vbi */
+			cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT;
+		} else {
+			/* others */
+			cap->type = VID_TYPE_CAPTURE|
+				VID_TYPE_TUNER|
+				VID_TYPE_OVERLAY|
+				VID_TYPE_CLIPPING|
+				VID_TYPE_SCALES;
+			cap->maxwidth  = bttv_tvnorms[btv->tvnorm].swidth;
+			cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
+			cap->minwidth  = 48;
+			cap->minheight = 32;
+		}
+		cap->channels  = bttv_tvcards[btv->c.type].video_inputs;
+		cap->audios    = bttv_tvcards[btv->c.type].audio_inputs;
+                return 0;
+	}
+
+	case VIDIOCGPICT:
+	{
+		struct video_picture *pic = arg;
+
+		memset(pic,0,sizeof(*pic));
+		pic->brightness = btv->bright;
+		pic->contrast   = btv->contrast;
+		pic->hue        = btv->hue;
+		pic->colour     = btv->saturation;
+		if (fh->fmt) {
+			pic->depth   = fh->fmt->depth;
+			pic->palette = fh->fmt->palette;
+		}
+		return 0;
+	}
+	case VIDIOCSPICT:
+	{
+		struct video_picture *pic = arg;
+		const struct bttv_format *fmt;
+		
+		fmt = format_by_palette(pic->palette);
+		if (NULL == fmt)
+			return -EINVAL;
+		down(&fh->cap.lock);
+		if (fmt->depth != pic->depth) {
+			retval = -EINVAL;
+			goto fh_unlock_and_return;
+		}
+		fh->ovfmt   = fmt;
+		fh->fmt     = fmt;
+		btv->init.ovfmt   = fmt;
+		btv->init.fmt     = fmt;
+		if (bigendian) {
+			/* dirty hack time:  swap bytes for overlay if the
+			   display adaptor is big endian (insmod option) */
+			if (fmt->palette == VIDEO_PALETTE_RGB555 ||
+			    fmt->palette == VIDEO_PALETTE_RGB565 ||
+			    fmt->palette == VIDEO_PALETTE_RGB32) {
+				fh->ovfmt = fmt+1;
+			}
+		}
+		bt848_bright(btv,pic->brightness);
+		bt848_contrast(btv,pic->contrast);
+		bt848_hue(btv,pic->hue);
+		bt848_sat(btv,pic->colour);
+		up(&fh->cap.lock);
+                return 0;
+	}
+
+	case VIDIOCGWIN:
+	{
+		struct video_window *win = arg;
+
+		memset(win,0,sizeof(*win));
+		win->x      = fh->ov.w.left;
+		win->y      = fh->ov.w.top;
+		win->width  = fh->ov.w.width;
+		win->height = fh->ov.w.height;
+		return 0;
+	}
+	case VIDIOCSWIN:
+	{
+		struct video_window *win = arg;
+		struct v4l2_window w2;
+
+		w2.field = V4L2_FIELD_ANY;
+		w2.w.left    = win->x;
+		w2.w.top     = win->y;
+		w2.w.width   = win->width;
+		w2.w.height  = win->height;
+		w2.clipcount = win->clipcount;
+		w2.clips     = (struct v4l2_clip*)win->clips;
+		retval = setup_window(fh, btv, &w2, 0);
+		if (0 == retval) {
+			/* on v4l1 this ioctl affects the read() size too */
+			fh->width  = fh->ov.w.width;
+			fh->height = fh->ov.w.height;
+			btv->init.width  = fh->ov.w.width;
+			btv->init.height = fh->ov.w.height;
+		}
+		return retval;
+	}
+
 	case VIDIOCGFBUF:
 	{
-		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 video_buffer *fbuf = arg;
+
+		fbuf->base          = btv->fbuf.base;
+		fbuf->width         = btv->fbuf.fmt.width;
+		fbuf->height        = btv->fbuf.fmt.height;
+		fbuf->bytesperline  = btv->fbuf.fmt.bytesperline;
+		if (fh->ovfmt)
+			fbuf->depth = fh->ovfmt->depth;
 		return 0;
-			
 	}
 	case VIDIOCSFBUF:
 	{
-		struct video_buffer v;
+		struct video_buffer *fbuf = 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)
-			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;
+                   !capable(CAP_SYS_RAWIO))
+                        return -EPERM;
+		end = (unsigned long)fbuf->base +
+			fbuf->height * fbuf->bytesperline;
+		down(&fh->cap.lock);
+		retval = -EINVAL;
+
+		switch (fbuf->depth) {
+		case 8:
+			fmt = format_by_palette(VIDEO_PALETTE_HI240);
+			break;
+		case 16:
+			fmt = format_by_palette(VIDEO_PALETTE_RGB565);
+			break;
+		case 24:
+			fmt = format_by_palette(VIDEO_PALETTE_RGB24);
+			break;
+		case 32:
+			fmt = format_by_palette(VIDEO_PALETTE_RGB32);
+			break;
+		case 15:
+			fbuf->depth = 16;
+			fmt = format_by_palette(VIDEO_PALETTE_RGB555);
+			break;
+		default:
+			fmt = NULL;
+			break;
 		}
-#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;		
+		if (NULL == fmt)
+			goto fh_unlock_and_return;
+
+		fh->ovfmt = fmt;
+		fh->fmt   = fmt;
+		btv->init.ovfmt = fmt;
+		btv->init.fmt   = fmt;
+		btv->fbuf.base             = fbuf->base;
+		btv->fbuf.fmt.width        = fbuf->width;
+		btv->fbuf.fmt.height       = fbuf->height;
+		if (fbuf->bytesperline)
+			btv->fbuf.fmt.bytesperline = fbuf->bytesperline;
+		else
+			btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fbuf->depth/8;
+		up(&fh->cap.lock);
+		return 0;
 	}
-	case VIDIOCKEY:
+
+	case VIDIOCCAPTURE:
+	case VIDIOC_OVERLAY:
 	{
-		/* Will be handled higher up .. */
-		return 0;
+		struct bttv_buffer *new;
+		int *on = arg;
+
+		if (*on) {
+			/* verify args */
+			if (NULL == btv->fbuf.base)
+				return -EINVAL;
+			if (!fh->ov.setup_ok) {
+				dprintk("bttv%d: overlay: !setup_ok\n",btv->c.nr);
+				return -EINVAL;
+			}
+		}
+
+		if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
+			return -EBUSY;
+		
+		down(&fh->cap.lock);
+		if (*on) {
+			fh->ov.tvnorm = btv->tvnorm;
+			new = videobuf_alloc(sizeof(*new));
+			bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+		} else {
+			new = NULL;
+		}
+
+		/* switch over */
+	        retval = bttv_switch_overlay(btv,fh,new);
+		up(&fh->cap.lock);
+		return retval;
 	}
-	case VIDIOCGFREQ:
+
+	case VIDIOCGMBUF:
 	{
-		unsigned long v=btv->win.freq;
-		if(copy_to_user(arg,&v,sizeof(v)))
-			return -EFAULT;
+		struct video_mbuf *mbuf = arg;
+		unsigned int i;
+
+		down(&fh->cap.lock);
+		retval = videobuf_mmap_setup(file,&fh->cap,gbuffers,gbufsize,
+					     V4L2_MEMORY_MMAP);
+		if (retval < 0)
+			goto fh_unlock_and_return;
+		memset(mbuf,0,sizeof(*mbuf));
+		mbuf->frames = gbuffers;
+		mbuf->size   = gbuffers * gbufsize;
+		for (i = 0; i < gbuffers; i++)
+			mbuf->offsets[i] = i * gbufsize;
+		up(&fh->cap.lock);
 		return 0;
 	}
-	case VIDIOCSFREQ:
+	case VIDIOCMCAPTURE:
 	{
-		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 video_mmap *vm = arg;
+		struct bttv_buffer *buf;
+		enum v4l2_field field;
+
+		if (vm->frame >= VIDEO_MAX_FRAME)
+			return -EINVAL;
+
+		down(&fh->cap.lock);
+		retval = -EINVAL;
+		buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame];
+		if (NULL == buf)
+			goto fh_unlock_and_return;
+		if (0 == buf->vb.baddr)
+			goto fh_unlock_and_return;
+		if (buf->vb.state == STATE_QUEUED ||
+		    buf->vb.state == STATE_ACTIVE)
+			goto fh_unlock_and_return;
+		
+		field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2)
+			? V4L2_FIELD_INTERLACED
+			: V4L2_FIELD_BOTTOM;
+		retval = bttv_prepare_buffer(btv,buf,
+					     format_by_palette(vm->format),
+					     vm->width,vm->height,field);
+		if (0 != retval)
+			goto fh_unlock_and_return;
+		spin_lock_irqsave(&btv->s_lock,flags);
+		buffer_queue(file,&buf->vb);
+		spin_unlock_irqrestore(&btv->s_lock,flags);
+		up(&fh->cap.lock);
 		return 0;
 	}
-	
-	case VIDIOCGAUDIO:
+	case VIDIOCSYNC:
 	{
-		struct video_audio v;
+		int *frame = arg;
+		struct bttv_buffer *buf;
+
+		if (*frame >= VIDEO_MAX_FRAME)
+			return -EINVAL;
 
-		v=btv->audio_dev;
-		v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE);
-		v.flags|=VIDEO_AUDIO_MUTABLE;
-		strcpy(v.name,"TV");
+		down(&fh->cap.lock);
+		retval = -EINVAL;
+		buf = (struct bttv_buffer *)fh->cap.bufs[*frame];
+		if (NULL == buf)
+			goto fh_unlock_and_return;
+		retval = videobuf_waiton(&buf->vb,0,1);
+		if (0 != retval)
+			goto fh_unlock_and_return;
+		switch (buf->vb.state) {
+		case STATE_ERROR:
+			retval = -EIO;
+			/* fall through */
+		case STATE_DONE:
+			videobuf_dma_pci_sync(btv->c.pci,&buf->vb.dma);
+			bttv_dma_free(btv,buf);
+			break;
+		default:
+			retval = -EINVAL;
+			break;
+		}
+		up(&fh->cap.lock);
+		return retval;
+	}
 
-		v.mode = VIDEO_SOUND_MONO;
-		bttv_call_i2c_clients(btv,cmd,&v);
+	case VIDIOCGVBIFMT:
+	{
+		struct vbi_format *fmt = (void *) arg;
+		struct v4l2_format fmt2;
 
-		/* card specific hooks */
-		if (btv->audio_hook)
-			btv->audio_hook(btv,&v,0);
+		if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) {
+			retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
+			if (0 != retval)
+				return retval;
+		}
+		bttv_vbi_get_fmt(fh, &fmt2);
+
+		memset(fmt,0,sizeof(*fmt));
+		fmt->sampling_rate    = fmt2.fmt.vbi.sampling_rate;
+		fmt->samples_per_line = fmt2.fmt.vbi.samples_per_line;
+		fmt->sample_format    = VIDEO_PALETTE_RAW;
+		fmt->start[0]         = fmt2.fmt.vbi.start[0];
+		fmt->count[0]         = fmt2.fmt.vbi.count[0];
+		fmt->start[1]         = fmt2.fmt.vbi.start[1];
+		fmt->count[1]         = fmt2.fmt.vbi.count[1];
+		if (fmt2.fmt.vbi.flags & VBI_UNSYNC)
+			fmt->flags   |= V4L2_VBI_UNSYNC;
+		if (fmt2.fmt.vbi.flags & VBI_INTERLACED)
+			fmt->flags   |= V4L2_VBI_INTERLACED;
+		return 0;
+	}
+	case VIDIOCSVBIFMT:
+	{
+		struct vbi_format *fmt = (void *) arg;
+		struct v4l2_format fmt2;
+
+		retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
+		if (0 != retval)
+			return retval;
+		bttv_vbi_get_fmt(fh, &fmt2);
+
+		if (fmt->sampling_rate    != fmt2.fmt.vbi.sampling_rate     ||
+		    fmt->samples_per_line != fmt2.fmt.vbi.samples_per_line  ||
+		    fmt->sample_format    != VIDEO_PALETTE_RAW              ||
+		    fmt->start[0]         != fmt2.fmt.vbi.start[0]          ||
+		    fmt->start[1]         != fmt2.fmt.vbi.start[1]          ||
+		    fmt->count[0]         != fmt->count[1]                  ||
+		    fmt->count[0]         <  1                              ||
+		    fmt->count[0]         >  32 /* VBI_MAXLINES */)
+			return -EINVAL;
 
-		if(copy_to_user(arg,&v,sizeof(v)))
-			return -EFAULT;
+		bttv_vbi_setlines(fh,btv,fmt->count[0]);
 		return 0;
 	}
+
+        case BTTV_VERSION:
+        case VIDIOCGFREQ:
+        case VIDIOCSFREQ:
+        case VIDIOCGTUNER:
+        case VIDIOCSTUNER:
+        case VIDIOCGCHAN:
+        case VIDIOCSCHAN:
+	case VIDIOCGAUDIO:
 	case VIDIOCSAUDIO:
+		return bttv_common_ioctls(btv,cmd,arg);
+
+	/* ***  v4l2  *** ************************************************ */
+	case VIDIOC_QUERYCAP:
 	{
-		struct video_audio v;
-		unsigned int n;
+		struct v4l2_capability *cap = arg;
 
-		if(copy_from_user(&v,arg, sizeof(v)))
-			return -EFAULT;
-		n = v.audio;
-		if(n >= bttv_tvcards[btv->type].audio_inputs)
+		if (0 == v4l2)
 			return -EINVAL;
-		down(&btv->lock);
-		if(v.flags&VIDEO_AUDIO_MUTE)
-			audio(btv, AUDIO_MUTE);
-		/* bt848_muxsel(btv,v.audio); */
-		if(!(v.flags&VIDEO_AUDIO_MUTE))
-			audio(btv, AUDIO_UNMUTE);
-
-		bttv_call_i2c_clients(btv,cmd,&v);
-		
-		/* card specific hooks */
-		if (btv->audio_hook)
-			btv->audio_hook(btv,&v,1);
-
-		btv->audio_dev=v;
-		up(&btv->lock);
+                strcpy(cap->driver,"bttv");
+                strlcpy(cap->card,btv->video_dev->name,sizeof(cap->card));
+		sprintf(cap->bus_info,"PCI:%s",pci_name(btv->c.pci));
+		cap->version = BTTV_VERSION_CODE;
+		cap->capabilities =
+			V4L2_CAP_VIDEO_CAPTURE |
+			V4L2_CAP_VIDEO_OVERLAY |
+			V4L2_CAP_VBI_CAPTURE |
+			V4L2_CAP_TUNER |
+			V4L2_CAP_READWRITE | 
+			V4L2_CAP_STREAMING;
 		return 0;
 	}
 
-	case VIDIOCSYNC:
+	case VIDIOC_ENUM_FMT:
 	{
-		DECLARE_WAITQUEUE(wait, current);
+		struct v4l2_fmtdesc *f = arg;
+		enum v4l2_buf_type type;
 		unsigned int i;
+		int index;
 
-		if(copy_from_user((void *)&i,arg,sizeof(int)))
-			return -EFAULT;
-		if (i >= gbuffers)
-			return -EINVAL;
-		switch (btv->gbuf[i].stat) {
-		case GBUFFER_UNUSED:
-			ret = -EINVAL;
-			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;
+		type  = f->type;
+		if (V4L2_BUF_TYPE_VBI_CAPTURE == type) {
+			/* vbi */
+			index = f->index;
+			if (0 != index)
+				return -EINVAL;
+			memset(f,0,sizeof(*f));
+			f->index       = index;
+			f->type        = type;
+			f->pixelformat = V4L2_PIX_FMT_GREY;
+			strcpy(f->description,"vbi data");
+			return 0;
+		}
+
+		/* video capture + overlay */
+		index = -1;
+		for (i = 0; i < BTTV_FORMATS; i++) {
+			if (bttv_formats[i].fourcc != -1)
+				index++;
+			if ((unsigned int)index == f->index)
+				break;
 		}
-		if (btv->needs_restart) {
-			down(&btv->lock);
-			bt848_restart(btv);
-			up(&btv->lock);
+		if (BTTV_FORMATS == i)
+			return -EINVAL;
+
+		switch (f->type) {
+		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+			break;
+		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+			if (!(bttv_formats[i].flags & FORMAT_FLAGS_PACKED))
+				return -EINVAL;
+			break;
+		default:
+			return -EINVAL;
 		}
-		return ret;
+		memset(f,0,sizeof(*f));
+		f->index       = index;
+		f->type        = type;
+		f->pixelformat = bttv_formats[i].fourcc;
+		strlcpy(f->description,bttv_formats[i].name,sizeof(f->description));
+		return 0;
 	}
 
-	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 VIDIOC_TRY_FMT:
+	{
+		struct v4l2_format *f = arg;
+		return bttv_try_fmt(fh,btv,f);
 	}
-
-	case VIDIOCMCAPTURE:
+	case VIDIOC_G_FMT:
 	{
-		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;
+		struct v4l2_format *f = arg;
+		return bttv_g_fmt(fh,f);
 	}
-		
-	case VIDIOCGMBUF:
+	case VIDIOC_S_FMT:
 	{
-		struct video_mbuf vm;
-		unsigned int i;
-		
-		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 v4l2_format *f = arg;
+		return bttv_s_fmt(fh,btv,f);
 	}
-		
-	case VIDIOCGUNIT:
+
+	case VIDIOC_G_FBUF:
 	{
-		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;
+		struct v4l2_framebuffer *fb = arg;
+
+		*fb = btv->fbuf;
+		fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+		if (fh->ovfmt)
+			fb->fmt.pixelformat  = fh->ovfmt->fourcc;
 		return 0;
 	}
+	case VIDIOC_S_FBUF:
+	{
+		struct v4l2_framebuffer *fb = arg;
+		const struct bttv_format *fmt;
+		
+		if(!capable(CAP_SYS_ADMIN) &&
+		   !capable(CAP_SYS_RAWIO))
+			return -EPERM;
+
+		/* check args */
+		fmt = format_by_fourcc(fb->fmt.pixelformat);
+		if (NULL == fmt)
+			return -EINVAL;
+		if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+			return -EINVAL;
+
+		down(&fh->cap.lock);
+		retval = -EINVAL;
+		if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+			if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth)
+				goto fh_unlock_and_return;
+			if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight)
+				goto fh_unlock_and_return;
+		}
+
+		/* ok, accept it */
+		btv->fbuf.base       = fb->base;
+		btv->fbuf.fmt.width  = fb->fmt.width;
+		btv->fbuf.fmt.height = fb->fmt.height;
+		if (0 != fb->fmt.bytesperline)
+			btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+		else
+			btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+		
+		retval = 0;
+		fh->ovfmt = fmt;
+		btv->init.ovfmt = fmt;
+		if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+			fh->ov.w.left   = 0;
+			fh->ov.w.top    = 0;
+			fh->ov.w.width  = fb->fmt.width;
+			fh->ov.w.height = fb->fmt.height;
+			btv->init.ov.w.width  = fb->fmt.width;
+			btv->init.ov.w.height = fb->fmt.height;
+			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;
 		
-	case BTTV_BURST_ON:
+				new = videobuf_alloc(sizeof(*new));
+				bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
+				retval = bttv_switch_overlay(btv,fh,new);
+			}
+		}
+		up(&fh->cap.lock);
+		return retval;
+	}
+
+	case VIDIOC_REQBUFS:
+		return videobuf_reqbufs(file,bttv_queue(fh),arg);
+
+	case VIDIOC_QUERYBUF:
+		return videobuf_querybuf(bttv_queue(fh),arg);
+
+	case VIDIOC_QBUF:
+		return videobuf_qbuf(file,bttv_queue(fh),arg);
+
+	case VIDIOC_DQBUF:
+		return videobuf_dqbuf(file,bttv_queue(fh),arg);
+
+	case VIDIOC_STREAMON:
 	{
-		burst(1);
+		int res = bttv_resource(fh);
+
+		if (!check_alloc_btres(btv,fh,res))
+			return -EBUSY;
+		return videobuf_streamon(file,bttv_queue(fh));
+	}
+	case VIDIOC_STREAMOFF:
+	{
+		int res = bttv_resource(fh);
+
+		retval = videobuf_streamoff(file,bttv_queue(fh));
+		if (retval < 0)
+			return retval;
+		free_btres(btv,fh,res);
 		return 0;
 	}
 
-	case BTTV_BURST_OFF:
+	case VIDIOC_QUERYCTRL:
 	{
-		burst(0);
+		struct v4l2_queryctrl *c = arg;
+		int i;
+
+		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 (i >= 4 && i <= 8) {
+			struct video_audio va;
+			memset(&va,0,sizeof(va));
+			bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+			if (btv->audio_hook)
+				btv->audio_hook(btv,&va,0);
+			switch (bttv_ctls[i].id) {
+			case V4L2_CID_AUDIO_VOLUME:
+				if (!(va.flags & VIDEO_AUDIO_VOLUME))
+					*c = no_ctl;
+				break;
+			case V4L2_CID_AUDIO_BALANCE:
+				if (!(va.flags & VIDEO_AUDIO_BALANCE))
+					*c = no_ctl;
+				break;
+			case V4L2_CID_AUDIO_BASS:
+				if (!(va.flags & VIDEO_AUDIO_BASS))
+					*c = no_ctl;
+				break;
+			case V4L2_CID_AUDIO_TREBLE:
+				if (!(va.flags & VIDEO_AUDIO_TREBLE))
+					*c = no_ctl;
+				break;
+			}
+		}
+		return 0;
+	}
+	case VIDIOC_G_CTRL:
+		return get_control(btv,arg);
+	case VIDIOC_S_CTRL:
+		return set_control(btv,arg);
+	case VIDIOC_G_PARM:
+	{
+		struct v4l2_streamparm *parm = arg;
+		struct v4l2_standard s;
+		if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		memset(parm,0,sizeof(*parm));
+		v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id,
+					 bttv_tvnorms[btv->tvnorm].name);
+		parm->parm.capture.timeperframe = s.frameperiod;
 		return 0;
 	}
 
-	case BTTV_VERSION:
+#ifdef VIDIOC_G_PRIORITY
+	case VIDIOC_G_PRIORITY:
 	{
-		return BTTV_VERSION_CODE;
+		enum v4l2_priority *p = arg;
+
+		*p = v4l2_prio_max(&btv->prio);
+		return 0;
 	}
-                        
-	case BTTV_PICNR:
+	case VIDIOC_S_PRIORITY:
 	{
-		/* return picture;*/
-		return  0;
+		enum v4l2_priority *prio = arg;
+
+		return v4l2_prio_change(&btv->prio, &fh->prio, *prio);
 	}
+#endif
+
+	
+	case VIDIOC_ENUMSTD:
+	case VIDIOC_G_STD:
+	case VIDIOC_S_STD:
+	case VIDIOC_ENUMINPUT:
+	case VIDIOC_G_INPUT:
+	case VIDIOC_S_INPUT:
+	case VIDIOC_G_TUNER:
+	case VIDIOC_S_TUNER:
+	case VIDIOC_G_FREQUENCY:
+	case VIDIOC_S_FREQUENCY:
+		return bttv_common_ioctls(btv,cmd,arg);
 
 	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 ... :-(
- */
-
-static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size)
-{
-        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(start, page, PAGE_SIZE, PAGE_SHARED))
-                        return -EAGAIN;
-                start+=PAGE_SIZE;
-                pos+=PAGE_SIZE;
-                size-=PAGE_SIZE;
-        }
-        return 0;
+ fh_unlock_and_return:
+	up(&fh->cap.lock);
+	return retval;
 }
 
-static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size)
+static int bttv_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg)
 {
-        struct bttv *btv=(struct bttv *)dev;
-        int r;
+	struct bttv_fh *fh  = file->private_data;
 
-        down(&btv->lock);
-        r=do_bttv_mmap(btv, adr, size);
-        up(&btv->lock);
-        return r;
+	switch (cmd) {
+	case BTTV_VBISIZE:
+		bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE);
+		return fh->lines * 2 * 2048;
+	default:
+		return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);
+	}
 }
 
+static ssize_t bttv_read(struct file *file, char *data,
+			 size_t count, loff_t *ppos)
+{
+	struct bttv_fh *fh = file->private_data;
+	int retval = 0;
 
-static struct video_device bttv_template=
-{
-	.owner		= THIS_MODULE,
-	.name		= "bttv video",
-	.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,
-};
+	if (fh->btv->errors)
+		bttv_reinit_bt848(fh->btv);
+	dprintk("bttv%d: read count=%d type=%s\n",
+		fh->btv->c.nr,(int)count,v4l2_type_names[fh->type]);
 
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (locked_btres(fh->btv,RESOURCE_VIDEO))
+			return -EBUSY;
+		retval = videobuf_read_one(file, &fh->cap, data, count, ppos);
+		break;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI))
+			return -EBUSY;
+		retval = videobuf_read_stream(file, &fh->vbi, data, count, ppos, 1);
+		break;
+	default:
+		BUG();
+	}
+	return retval;
+}
 
-static long vbi_read(struct video_device *v, char *buf, unsigned long count,
-		     int nonblock)
+static unsigned int bttv_poll(struct file *file, poll_table *wait)
 {
-	struct bttv *btv=(struct bttv *)(v-2);
-	unsigned long todo;
-	unsigned int q;
-	DECLARE_WAITQUEUE(wait, current);
-
-	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;
+	struct bttv_fh *fh = file->private_data;
+	struct bttv_buffer *buf;
+	enum v4l2_field field;
 
-		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;
+	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+		if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI))
+			return -EBUSY;
+		return videobuf_poll_stream(file, &fh->vbi, wait);
+	}
+
+	if (check_btres(fh,RESOURCE_VIDEO)) {
+		/* streaming capture */
+		if (list_empty(&fh->cap.stream))
+			return POLLERR;
+		buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
+	} else {
+		/* read() capture */
+		down(&fh->cap.lock);
+		if (NULL == fh->cap.read_buf) {
+			/* need to capture a new frame */
+			if (locked_btres(fh->btv,RESOURCE_VIDEO)) {
+				up(&fh->cap.lock);
+				return POLLERR;
+			}
+			fh->cap.read_buf = videobuf_alloc(fh->cap.msize);
+			if (NULL == fh->cap.read_buf) {
+				up(&fh->cap.lock);
+				return POLLERR;
 			}
-			schedule();
-			if(signal_pending(current))
-			{
-				remove_wait_queue(&btv->vbiq, &wait);
-                                current->state = TASK_RUNNING;
-				if(todo==count)
-					return -EINTR;
-				else
-					return count-todo;
+			fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
+			field = videobuf_next_field(&fh->cap);
+			if (0 != fh->cap.ops->buf_prepare(file,fh->cap.read_buf,field)) {
+				up(&fh->cap.lock);
+				return POLLERR;
 			}
+			fh->cap.ops->buf_queue(file,fh->cap.read_buf);
+			fh->cap.read_off = 0;
 		}
-		remove_wait_queue(&btv->vbiq, &wait);
-		current->state = TASK_RUNNING;
+		up(&fh->cap.lock);
+		buf = (struct bttv_buffer*)fh->cap.read_buf;
 	}
-	if (todo) 
-	{
-		if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
-			return -EFAULT;
-		btv->vbip+=todo;
-	}
-	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;
+	int minor = iminor(inode);
+	struct bttv *btv = NULL;
+	struct bttv_fh *fh;
+	enum v4l2_buf_type type = 0;
+	unsigned int i;
+
+	dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
+
+	for (i = 0; i < bttv_num; i++) {
+		if (bttvs[i].video_dev &&
+		    bttvs[i].video_dev->minor == minor) {
+			btv = &bttvs[i];
+			type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			break;
+		}
+		if (bttvs[i].vbi_dev &&
+		    bttvs[i].vbi_dev->minor == minor) {
+			btv = &bttvs[i];
+			type = V4L2_BUF_TYPE_VBI_CAPTURE;
+			break;
+		}
+	}
+	if (NULL == btv)
+		return -ENODEV;
 
-	poll_wait(file, &btv->vbiq, wait);
+	dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
+		btv->c.nr,v4l2_type_names[type]);
 
-	if (btv->vbip < VBIBUF_SIZE)
-		mask |= (POLLIN | POLLRDNORM);
+	/* allocate per filehandle data */
+	fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+	if (NULL == fh)
+		return -ENOMEM;
+	file->private_data = fh;
+	*fh = btv->init;
+	fh->type = type;
+	fh->ov.setup_ok = 0;
+#ifdef VIDIOC_G_PRIORITY
+	v4l2_prio_open(&btv->prio,&fh->prio);
+#endif
 
-	return mask;
+	videobuf_queue_init(&fh->cap, &bttv_video_qops,
+			    btv->c.pci, &btv->s_lock,
+			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
+			    V4L2_FIELD_INTERLACED,
+			    sizeof(struct bttv_buffer));
+	videobuf_queue_init(&fh->vbi, &bttv_vbi_qops,
+			    btv->c.pci, &btv->s_lock,
+			    V4L2_BUF_TYPE_VBI_CAPTURE,
+			    V4L2_FIELD_SEQ_TB,
+			    sizeof(struct bttv_buffer));
+	i2c_vidiocschan(btv);
+
+	btv->users++;
+	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
+		bttv_vbi_setlines(fh,btv,16);
+	bttv_field_count(btv);
+	return 0;
 }
 
-static int vbi_open(struct video_device *dev, int flags)
+static int bttv_release(struct inode *inode, struct file *file)
 {
-	struct bttv *btv=(struct bttv *)(dev-2);
- 	unsigned long irq_flags;
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
 
-        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);
+	/* turn off overlay */
+	if (check_btres(fh, RESOURCE_OVERLAY))
+		bttv_switch_overlay(btv,fh,NULL);
+	
+	/* stop video capture */
+	if (check_btres(fh, RESOURCE_VIDEO)) {
+		videobuf_streamoff(file,&fh->cap);
+		free_btres(btv,fh,RESOURCE_VIDEO);
+	}
+	if (fh->cap.read_buf) {
+		buffer_release(file,fh->cap.read_buf);
+		kfree(fh->cap.read_buf);
+	}
 
-	return 0;   
-}
+	/* stop vbi capture */
+	if (check_btres(fh, RESOURCE_VBI)) {
+		if (fh->vbi.streaming)
+			videobuf_streamoff(file,&fh->vbi);
+		if (fh->vbi.reading)
+			videobuf_read_stop(file,&fh->vbi);
+		free_btres(btv,fh,RESOURCE_VBI);
+	}
 
-static void vbi_close(struct video_device *dev)
-{
-	struct bttv *btv=(struct bttv *)(dev-2);
- 	unsigned long irq_flags;
+#ifdef VIDIOC_G_PRIORITY
+	v4l2_prio_close(&btv->prio,&fh->prio);
+#endif
+	file->private_data = NULL;
+	kfree(fh);
 
-	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);
+	btv->users--;
+	bttv_field_count(btv);
+	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;
 
-	switch (cmd) {	
-	case VIDIOCGCAP:
-	{
-		struct video_capability b;
-		strcpy(b.name,btv->vbi_dev.name);
-		b.type = ((bttv_tvcards[btv->type].tuner != UNSET) ? 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:
-		return -EINVAL;
-	}
-}
+	dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n",
+		fh->btv->c.nr, v4l2_type_names[fh->type],
+		vma->vm_start, vma->vm_end - vma->vm_start);
+	return videobuf_mmap_mapper(vma,bttv_queue(fh));
+}
+
+static struct file_operations bttv_fops =
+{
+	.owner	  = THIS_MODULE,
+	.open	  = bttv_open,
+	.release  = bttv_release,
+	.ioctl	  = bttv_ioctl,
+	.llseek	  = no_llseek,
+	.read	  = bttv_read,
+	.mmap	  = bttv_mmap,
+	.poll     = bttv_poll,
+};
 
-static struct video_device vbi_template=
+static struct video_device bttv_video_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,
+	.minor    = -1,
 };
 
-
-static int radio_open(struct video_device *dev, int flags)
+struct video_device bttv_vbi_template =
 {
-	struct bttv *btv = (struct bttv *)(dev-1);
+	.name     = "bt848/878 vbi",
+	.type     = VID_TYPE_TUNER|VID_TYPE_TELETEXT,
+	.hardware = VID_HARDWARE_BT848,
+	.fops     = &bttv_fops,
+	.minor    = -1,
+};
 
-        down(&btv->lock);
-	if (btv->user)
-		goto busy_unlock;
-	btv->user++;
-
-	btv->radio = 1;
-	bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
-	bt848_muxsel(btv,0);
-	up(&btv->lock);
+/* ----------------------------------------------------------------------- */
+/* radio interface                                                         */
 
-	return 0;   
+static int radio_open(struct inode *inode, struct file *file)
+{
+	int minor = iminor(inode);
+	struct bttv *btv = NULL;
+	unsigned int i;
 
- busy_unlock:
-	up(&btv->lock);
-	return -EBUSY;
-}
+	dprintk("bttv: open minor=%d\n",minor);
 
-static void radio_close(struct video_device *dev)
-{
-	struct bttv *btv=(struct bttv *)(dev-1);
+	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->c.nr);
 	down(&btv->lock);
-	btv->user--;
-	btv->radio = 0;
+	if (btv->radio_user) {
+		up(&btv->lock);
+		return -EBUSY;
+	}
+	btv->radio_user++;
+	file->private_data = btv;
+
+	i2c_vidiocschan(btv);
+        bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
+	audio_mux(btv,AUDIO_RADIO);
+
 	up(&btv->lock);
+        return 0;
 }
 
-static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+static int radio_release(struct inode *inode, struct file *file)
 {
-	return -EINVAL;
+	struct bttv    *btv = file->private_data;
+
+	btv->radio_user--;
+	return 0;
 }
 
-static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static int radio_do_ioctl(struct inode *inode, struct file *file,
+			  unsigned int cmd, void *arg)
 {
-        struct bttv *btv=(struct bttv *)(dev-1);
-	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 bttv    *btv = file->private_data;
+
+	switch (cmd) {
+	case VIDIOCGCAP:
 	{
-		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 int radio_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
 {
-	.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,
+	return video_usercopy(inode, file, cmd, arg, radio_do_ioctl);
+}
+
+static struct file_operations radio_fops =
+{
+	.owner	  = THIS_MODULE,
+	.open	  = radio_open,
+	.release  = radio_release,
+	.ioctl	  = radio_ioctl,
+	.llseek	  = no_llseek,
+};
+
+static struct video_device radio_template =
+{
+	.name     = "bt848/878 radio",
+	.type     = VID_TYPE_TUNER,
+	.hardware = VID_HARDWARE_BT848,
+	.fops     = &radio_fops,
+	.minor    = -1,
 };
 
+/* ----------------------------------------------------------------------- */
+/* irq handler                                                             */
+
+static char *irq_name[] = {
+	"FMTCHG",  // format change detected (525 vs. 625)
+	"VSYNC",   // vertical sync (new field)
+	"HSYNC",   // horizontal sync
+	"OFLOW",   // chroma/luma AGC overflow
+	"HLOCK",   // horizontal lock changed
+	"VPRES",   // video presence changed
+	"6", "7",
+	"I2CDONE", // hw irc operation finished
+	"GPINT",   // gpio port triggered irq
+	"10",
+	"RISCI",   // risc instruction triggered irq
+	"FBUS",    // pixel data fifo dropped data (high pci bus latencies)
+	"FTRGT",   // pixel data fifo overrun
+	"FDSR",    // fifo data stream resyncronisation
+	"PPERR",   // parity error (data transfer)
+	"RIPERR",  // parity error (read risc instructions)
+	"PABORT",  // pci abort
+	"OCERR",   // risc instruction error
+	"SCERR",   // syncronisation error
+};
 
-static void bt848_set_risc_jmps(struct bttv *btv, int flags)
+static void bttv_print_irqbits(u32 print, u32 mark)
 {
-	if (-1 == flags) {
-		/* defaults */
-		flags = 0;
-		if (btv->scr_on)
-			flags |= 0x03;
-		if (btv->vbi_on)
-			flags |= 0x0c;
-		if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0)
-			flags |= 0x0c;
-#if 0
-		/* Hmm ... */
-		if ((0 != btv->risc_cap_even) ||
-		    (0 != btv->risc_cap_odd))
-			flags |= 0x0c;
-#endif
+	unsigned int i;
+	
+	printk("bits:");
+	for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
+		if (print & (1 << i))
+			printk(" %s",irq_name[i]);
+		if (mark & (1 << i))
+			printk("*");
 	}
+}
 
-	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));
+static void bttv_print_riscaddr(struct bttv *btv)
+{
+	printk("  main: %08Lx\n",
+	       (unsigned long long)btv->main.dma);
+	printk("  vbi : o=%08Lx e=%08Lx\n",
+	       btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+	       btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+	printk("  cap : o=%08Lx e=%08Lx\n",
+	       btv->curr.top    ? (unsigned long long)btv->curr.top->top.dma : 0,
+	       btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+	printk("  scr : o=%08Lx e=%08Lx\n",
+	       btv->screen ? (unsigned long long)btv->screen->top.dma  : 0,
+	       btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+}
+
+static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
+{
+	printk("bttv%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+	       btv->c.nr,
+	       (unsigned long)btv->main.dma,
+	       (unsigned long)btv->main.cpu[RISC_SLOT_O_VBI+1],
+	       (unsigned long)btv->main.cpu[RISC_SLOT_O_FIELD+1],
+	       (unsigned long)rc);
+
+	if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
+		printk("bttv%d: Oh, there (temporarely?) is no input signal. "
+		       "Ok, then this is harmless, don't worry ;)\n",
+		       btv->c.nr);
+		return;
 	}
-
-
-	/* 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);
-
-	/* 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));
+	printk("bttv%d: Uhm. Looks like we have unusual high IRQ latencies.\n",
+	       btv->c.nr);
+	printk("bttv%d: Lets try to catch the culpit red-handed ...\n",
+	       btv->c.nr);
+	dump_stack();
+}
+
+static int
+bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
+{
+	struct bttv_buffer *item;
+
+	memset(set,0,sizeof(*set));
+
+	/* capture request ? */
+	if (!list_empty(&btv->capture)) {
+		set->irqflags = 1;
+		item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+		if (V4L2_FIELD_HAS_TOP(item->vb.field))
+			set->top    = item;
+		if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
+			set->bottom = item;
+
+		/* capture request for other field ? */
+		if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
+		    (item->vb.queue.next != &btv->capture)) {
+			item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+			if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
+				if (NULL == set->top &&
+				    V4L2_FIELD_TOP == item->vb.field) {
+					set->top = item;
+				}
+				if (NULL == set->bottom &&
+				    V4L2_FIELD_BOTTOM == item->vb.field) {
+					set->bottom = item;
+				}
+				if (NULL != set->top  &&  NULL != set->bottom)
+					set->topirq = 2;
+			}
+		}
 	}
 
-	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);
+	/* screen overlay ? */
+	if (NULL != btv->screen) {
+		if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
+			if (NULL == set->top && NULL == set->bottom) {
+				set->top    = btv->screen;
+				set->bottom = btv->screen;
+			}
+		} else {
+			if (V4L2_FIELD_TOP == btv->screen->vb.field &&
+			    NULL == set->top) {
+				set->top = btv->screen;
+			}
+			if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
+			    NULL == set->bottom) {
+				set->bottom = btv->screen;
+			}
+		}
 	}
-	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);
+	dprintk("bttv%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+		btv->c.nr,set->top, set->bottom,
+		btv->screen,set->irqflags,set->topirq);
+	return 0;
 }
 
-static int __devinit init_video_dev(struct bttv *btv)
-{
-	audio(btv, AUDIO_MUTE);
-        
-	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 (video_register_device(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) {
-	        video_unregister_device(&btv->video_dev);
-		return -1;
-	}
-	printk(KERN_INFO "bttv%d: registered device vbi%d\n",
-	       btv->nr,btv->vbi_dev.minor & 0x1f);
-	if (btv->has_radio) {
-		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;
+static void
+bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
+		      struct bttv_buffer_set *curr, unsigned int state)
+{
+	struct timeval ts;
+
+	do_gettimeofday(&ts);
+
+	if (wakeup->top == wakeup->bottom) {
+		if (NULL != wakeup->top && curr->top != wakeup->top) {
+			if (irq_debug > 1)
+				printk("bttv%d: wakeup: both=%p\n",btv->c.nr,wakeup->top);
+			wakeup->top->vb.ts = ts;
+			wakeup->top->vb.field_count = btv->field_count;
+			wakeup->top->vb.state = state;
+			wake_up(&wakeup->top->vb.done);
+		}
+	} else {
+		if (NULL != wakeup->top && curr->top != wakeup->top) {
+			if (irq_debug > 1)
+				printk("bttv%d: wakeup: top=%p\n",btv->c.nr,wakeup->top);
+			wakeup->top->vb.ts = ts;
+			wakeup->top->vb.field_count = btv->field_count;
+			wakeup->top->vb.state = state;
+			wake_up(&wakeup->top->vb.done);
+		}
+		if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
+			if (irq_debug > 1)
+				printk("bttv%d: wakeup: bottom=%p\n",btv->c.nr,wakeup->bottom);
+			wakeup->bottom->vb.ts = ts;
+			wakeup->bottom->vb.field_count = btv->field_count;
+			wakeup->bottom->vb.state = state;
+			wake_up(&wakeup->bottom->vb.done);
 		}
-		printk(KERN_INFO "bttv%d: registered device radio%d\n",
-		       btv->nr,btv->radio_dev.minor & 0x1f);
 	}
-        return 1;
 }
 
-static int __devinit init_bt848(struct bttv *btv)
+static void
+bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
+		    unsigned int state)
 {
-	int val;
-	unsigned int j;
- 	unsigned long irq_flags;
+	struct timeval ts;
 
-	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");
-		bttv_gpio_tracking(btv,"init #1");
-	}
-
-	/* reset the bt848 */
-	btwrite(0, BT848_SRESET);
-	
-	/* 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 (NULL == wakeup)
+		return;
 
-	btv->errors=0;
-	btv->needs_restart=0;
-	btv->has_radio=radio[btv->nr];
+	do_gettimeofday(&ts);
+	wakeup->vb.ts = ts;
+	wakeup->vb.field_count = btv->field_count;
+	wakeup->vb.state = state;
+	wake_up(&wakeup->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;
-	}
+static void bttv_irq_timeout(unsigned long data)
+{
+	struct bttv *btv = (struct bttv *)data;
+	struct bttv_buffer_set old,new;
+	struct bttv_buffer *ovbi;
+	struct bttv_buffer *item;
+	unsigned long flags;
 	
-	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");
-
-	btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO,
-		BT848_IFORM);
-
-	btwrite(0xd8, BT848_CONTRAST_LO);
-	bt848_bright(btv, 0x10);
+	if (bttv_verbose) {
+		printk(KERN_INFO "bttv%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+		       btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+		       btread(BT848_RISC_COUNT));
+		bttv_print_irqbits(btread(BT848_INT_STAT),0);
+		printk("\n");
+	}
+
+	spin_lock_irqsave(&btv->s_lock,flags);
+	
+	/* deactivate stuff */
+	memset(&new,0,sizeof(new));
+	old  = btv->curr;
+	ovbi = btv->cvbi;
+	btv->curr = new;
+	btv->cvbi = NULL;
+	bttv_buffer_activate_video(btv, &new);
+	bttv_buffer_activate_vbi(btv,   NULL);
+	bttv_set_dma(btv, 0, 0);
+
+	/* wake up */
+	bttv_irq_wakeup_video(btv, &old, &new, STATE_ERROR);
+	bttv_irq_wakeup_vbi(btv, ovbi, STATE_ERROR);
+
+	/* cancel all outstanding capture / vbi requests */
+	while (!list_empty(&btv->capture)) {
+		item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+		list_del(&item->vb.queue);
+		item->vb.state = STATE_ERROR;
+		wake_up(&item->vb.done);
+	}
+	while (!list_empty(&btv->vcapture)) {
+		item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+		list_del(&item->vb.queue);
+		item->vb.state = STATE_ERROR;
+		wake_up(&item->vb.done);
+	}
+	
+	btv->errors++;
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+}
 
-	btwrite(0x20, BT848_E_VSCALE_HI);
-	btwrite(0x20, BT848_O_VSCALE_HI);
-	btwrite(BT848_ADC_RESERVED | (adc_crush ? BT848_ADC_CRUSH : 0),
-		BT848_ADC);
+static void
+bttv_irq_wakeup_top(struct bttv *btv)
+{
+	struct bttv_buffer *wakeup = btv->curr.top;
 
-	if (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);
-	}
+	if (NULL == wakeup)
+		return;
 
-	btv->picture.colour     = 254<<7;
-	btv->picture.brightness = 128<<8;
-	btv->picture.hue        = 128<<8;
-	btv->picture.contrast   = 0xd8<<7;
-	btv->picture.palette    = VIDEO_PALETTE_RGB24;
+	spin_lock(&btv->s_lock);
+	btv->curr.topirq = 0;
+	btv->curr.top = NULL;
+	bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+
+	do_gettimeofday(&wakeup->vb.ts);
+	wakeup->vb.field_count = btv->field_count;
+	wakeup->vb.state = STATE_DONE;
+	wake_up(&wakeup->vb.done);
+	spin_unlock(&btv->s_lock);
+}
 
-	val = chroma_agc ? BT848_SCLOOP_CAGC : 0;
-        btwrite(val, BT848_E_SCLOOP);
-        btwrite(val, BT848_O_SCLOOP);
+static inline int is_active(struct btcx_riscmem *risc, u32 rc)
+{
+	if (rc < risc->dma)
+		return 0;
+	if (rc > risc->dma + risc->size)
+		return 0;
+	return 1;
+}
 
-	/* 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) |
-                (gpint   ? BT848_INT_GPINT : 0) |
-		BT848_INT_SCERR|
-		BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
-		BT848_INT_FMTCHG|BT848_INT_HLOCK,
-		BT848_INT_MASK);
-
-	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);
+static void
+bttv_irq_switch_video(struct bttv *btv)
+{
+	struct bttv_buffer_set new;
+	struct bttv_buffer_set old;
+	dma_addr_t rc;
 
-	/* needs to be done before i2c is registered */
-	bttv_init_card1(btv);
+	spin_lock(&btv->s_lock);
 
-	/* register i2c */
-        btv->tuner_type  = UNSET;
-        btv->pinnacle_id = UNSET;
-        init_bttv_i2c(btv);
+	/* new buffer set */
+	bttv_irq_next_video(btv, &new);
+	rc = btread(BT848_RISC_COUNT);
+	if ((btv->curr.top    && is_active(&btv->curr.top->top,       rc)) ||
+	    (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
+		btv->framedrop++;
+		if (debug_latency)
+			bttv_irq_debug_low_latency(btv, rc);
+		spin_unlock(&btv->s_lock);
+		return;
+	}
+	
+	/* switch over */
+	old = btv->curr;
+	btv->curr = new;
+	bttv_buffer_activate_video(btv, &new);
+	bttv_set_dma(btv, 0, new.irqflags);
+
+	/* switch input */
+	if (UNSET != btv->new_input) {
+		video_mux(btv,btv->new_input);
+		btv->new_input = UNSET;
+	}
 
-	/* some card-specific stuff (needs working i2c) */
-	bttv_init_card2(btv);
+	/* wake up finished buffers */
+	bttv_irq_wakeup_video(btv, &old, &new, STATE_DONE);
+	spin_unlock(&btv->s_lock);
+}
 
-	bt848_muxsel(btv, 1);
-	bt848_set_winsize(btv);
+static void
+bttv_irq_switch_vbi(struct bttv *btv)
+{
+	struct bttv_buffer *new = NULL;
+	struct bttv_buffer *old;
+	u32 rc;
 
-	/*
-	 *	Now add the template and register the device unit.
-	 */
-        init_video_dev(btv);
+	spin_lock(&btv->s_lock);
 
-	return 0;
+	if (!list_empty(&btv->vcapture))
+		new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+	old = btv->cvbi;
+	
+	rc = btread(BT848_RISC_COUNT);
+	if (NULL != old && (is_active(&old->top,    rc) ||
+			    is_active(&old->bottom, rc))) {
+		btv->framedrop++;
+		if (debug_latency)
+			bttv_irq_debug_low_latency(btv, rc);
+		spin_unlock(&btv->s_lock);
+		return;
+	}
+	
+	/* switch */
+	btv->cvbi = new;
+	bttv_buffer_activate_vbi(btv, new);
+	bttv_set_dma(btv, 0, btv->curr.irqflags);
+	
+	bttv_irq_wakeup_vbi(btv, old, STATE_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)
+static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
 {
 	u32 stat,astat;
 	u32 dstat;
 	int count;
 	struct bttv *btv;
+	int handled = 0;
 
 	btv=(struct bttv *)dev_id;
 	count=0;
-	while (1) 
-	{
+	while (1) {
 		/* get/clear interrupt status bits */
 		stat=btread(BT848_INT_STAT);
 		astat=stat&btread(BT848_INT_MASK);
 		if (!astat)
-			return;
+			break;
+		handled = 1;
 		btwrite(stat,BT848_INT_STAT);
 
 		/* get device status bits */
 		dstat=btread(BT848_DSTATUS);
 
 		if (irq_debug) {
-			unsigned int i;
-			printk(KERN_DEBUG "bttv%d: irq loop=%d risc=%x, bits:",
-			       btv->nr, count, stat>>28);
-			for (i = 0; i < ARRAY_SIZE(irq_name); 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->c.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");
@@ -2829,324 +3527,442 @@
 				printk("   PRES => %s", (dstat & BT848_DSTATUS_PRES)
 				       ? "yes" : "no");
 			if (stat & BT848_INT_FMTCHG)
-				printk("   NUML => %s", (dstat & BT848_DSTATUS_PRES)
+				printk("   NUML => %s", (dstat & BT848_DSTATUS_NUML)
 				       ? "625" : "525");
 			printk("\n");
 		}
 
-		if (astat&BT848_INT_GPINT)
-			wake_up_interruptible(&btv->gpioq);
-
 		if (astat&BT848_INT_VSYNC) 
-                        btv->field++;
+                        btv->field_count++;
 
-		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);
-				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_GPINT) {
+			wake_up(&btv->gpioq);
 		}
-		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);
-			}
 
-			/* 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);
-					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);
-					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);
-				btwrite(BT848_COLOR_CTL_GAMMA,
-					BT848_COLOR_CTL);
-				spin_unlock(&btv->s_lock);
-			}
+		if (astat & BT848_INT_I2CDONE) {
+			btv->i2c_done = stat;
+			wake_up(&btv->i2c_queue);
 		}
 
-		if (automute && (astat&BT848_INT_HLOCK)) {
-			if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio))
-				audio(btv, AUDIO_ON);
-			else
-				audio(btv, AUDIO_OFF);
+                if ((astat & BT848_INT_RISCI)  &&  (stat & (4<<28)))
+			bttv_irq_switch_vbi(btv);
+
+                if ((astat & BT848_INT_RISCI)  &&  (stat & (2<<28)))
+			bttv_irq_wakeup_top(btv);
+
+                if ((astat & BT848_INT_RISCI)  &&  (stat & (1<<28)))
+			bttv_irq_switch_video(btv);
+
+		if ((astat & BT848_INT_HLOCK)  &&  btv->opt_automute)
+			audio_mux(btv, -1);
+
+		if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+			printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->c.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->c.nr,btread(BT848_RISC_COUNT));
+			if (bttv_debug)
+				bttv_print_riscaddr(btv);
+		}
+
 		count++;
-		if (count > 20) {
+		if (count > 4) {
 			btwrite(0, BT848_INT_MASK);
 			printk(KERN_ERR 
-			       "bttv%d: IRQ lockup, cleared int mask\n", btv->nr);
-			bt848_offline(btv);
+			       "bttv%d: IRQ lockup, cleared int mask [", btv->c.nr);
+			bttv_print_irqbits(stat,astat);
+			printk("]\n");
 		}
 	}
+	btv->irq_total++;
+	if (handled)
+		btv->irq_me++;
+	return IRQ_RETVAL(handled);
 }
 
 
+/* ----------------------------------------------------------------------- */
+/* initialitation                                                          */
 
-/*
- *	Scan for a Bt848 card, request the irq and map the io memory 
- */
+static struct video_device *vdev_init(struct bttv *btv,
+				      struct video_device *template,
+				      char *type)
+{
+	struct video_device *vfd;
+
+	vfd = video_device_alloc();
+	if (NULL == vfd)
+		return NULL;
+	*vfd = *template;
+	vfd->minor   = -1;
+	snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+		 btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+		 type, bttv_tvcards[btv->c.type].name);
+	return vfd;
+}
+
+static void bttv_unregister_video(struct bttv *btv)
+{
+	if (btv->video_dev) {
+		if (-1 != btv->video_dev->minor)
+			video_unregister_device(btv->video_dev);
+		else
+			video_device_release(btv->video_dev);
+		btv->video_dev = NULL;
+	}
+	if (btv->vbi_dev) {
+		if (-1 != btv->vbi_dev->minor)
+			video_unregister_device(btv->vbi_dev);
+		else
+			video_device_release(btv->vbi_dev);
+		btv->vbi_dev = NULL;
+	}
+	if (btv->radio_dev) {
+		if (-1 != btv->radio_dev->minor)
+			video_unregister_device(btv->radio_dev);
+		else
+			video_device_release(btv->radio_dev);
+		btv->radio_dev = NULL;
+	}
+}
 
-static void bttv_remove(struct pci_dev *pci_dev)
+/* register video4linux devices */
+static int __devinit bttv_register_video(struct bttv *btv)
 {
-        u8 command;
-        unsigned int j;
-        struct bttv *btv = pci_get_drvdata(pci_dev);
+	/* video */
+	btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+        if (NULL == btv->video_dev)
+		goto err;
+	if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
+		goto err;
+	printk(KERN_INFO "bttv%d: registered device video%d\n",
+	       btv->c.nr,btv->video_dev->minor & 0x1f);
 
-	if (bttv_verbose)
-		printk("bttv%d: unloading\n",btv->nr);
+	/* vbi */
+	btv->vbi_dev = vdev_init(btv, &bttv_vbi_template, "vbi");
+        if (NULL == btv->vbi_dev)
+		goto err;
+        if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0)
+		goto err;
+	printk(KERN_INFO "bttv%d: registered device vbi%d\n",
+	       btv->c.nr,btv->vbi_dev->minor & 0x1f);
 
-        /* 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(~(u32)0,BT848_INT_STAT);
-        btwrite(0, BT848_GPIO_OUT_EN);
-	if (bttv_gpio)
-		bttv_gpio_tracking(btv,"cleanup");
+        if (!btv->has_radio)
+		return 0;
+	/* radio */
+	btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+        if (NULL == btv->radio_dev)
+		goto err;
+	if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0)
+		goto err;
+	printk(KERN_INFO "bttv%d: registered device radio%d\n",
+	       btv->c.nr,btv->radio_dev->minor & 0x1f);
 
-        /* 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->dev->irq,btv);
-        DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem));
-        if (btv->bt848_mem)
-                iounmap(btv->bt848_mem);
-
-        if (btv->video_dev.minor!=-1)
-                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);
+	/* all done */
+	return 0;
 
-	pci_set_drvdata(pci_dev, NULL);
-        return;
+ err:
+	bttv_unregister_video(btv);
+	return -1;
 }
 
 
-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
+}
+
+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;
-	
-	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));
+	memset(btv,0,sizeof(*btv));
+	btv->c.nr  = bttv_num;
+	sprintf(btv->c.name,"bttv%d",btv->c.nr);
+
+	/* initialize structs / fill in defaults */
+        init_MUTEX(&btv->lock);
+        init_MUTEX(&btv->reslock);
+        btv->s_lock    = SPIN_LOCK_UNLOCKED;
+        btv->gpio_lock = SPIN_LOCK_UNLOCKED;
+        init_waitqueue_head(&btv->gpioq);
+        init_waitqueue_head(&btv->i2c_queue);
+        INIT_LIST_HEAD(&btv->c.subs);
+        INIT_LIST_HEAD(&btv->capture);
+        INIT_LIST_HEAD(&btv->vcapture);
+#ifdef VIDIOC_G_PRIORITY
+	v4l2_prio_init(&btv->prio);
+#endif
+
+	init_timer(&btv->timeout);
+	btv->timeout.function = bttv_irq_timeout;
+	btv->timeout.data     = (unsigned long)btv;
 	
-        btv->id=dev->device;
-	btv->bt848_adr=pci_resource_start(dev,0);
+        btv->i2c_rc = -1;
+        btv->tuner_type  = UNSET;
+        btv->pinnacle_id = UNSET;
+	btv->new_input   = UNSET;
+	btv->gpioirq     = 1;
+	btv->has_radio=radio[btv->c.nr];
+	
+	/* pci stuff (init, get irq/mmio, ... */
+	btv->c.pci = dev;
+        btv->id  = dev->device;
 	if (pci_enable_device(dev)) {
                 printk(KERN_WARNING "bttv%d: Can't enable device.\n",
-		       btv->nr);
+		       btv->c.nr);
 		return -EIO;
 	}
         if (pci_set_dma_mask(dev, 0xffffffff)) {
                 printk(KERN_WARNING "bttv%d: No suitable DMA available.\n",
-		       btv->nr);
+		       btv->c.nr);
 		return -EIO;
         }
 	if (!request_mem_region(pci_resource_start(dev,0),
 				pci_resource_len(dev,0),
-				"bttv")) {
+				btv->c.name)) {
+                printk(KERN_WARNING "bttv%d: can't request iomem (0x%lx).\n",
+		       btv->c.nr, pci_resource_start(dev,0));
 		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%d: Oops: no 32bit PCI DMA ???\n", btv->c.nr);
+		result = -EIO;
+		goto fail1;
+	}
 
         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(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ",
+               bttv_num,btv->id, btv->revision, pci_name(dev));
         printk("irq: %d, latency: %d, mmio: 0x%lx\n",
-	       btv->dev->irq, lat, btv->bt848_adr);
+	       btv->c.pci->irq, lat, pci_resource_start(dev,0));
+	schedule();
 	
-	bttv_idcard(btv);
+	btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000);
+	if (NULL == ioremap(pci_resource_start(dev,0), 0x1000)) {
+		printk("bttv%d: ioremap() failed\n", btv->c.nr);
+		result = -EIO;
+		goto fail1;
+	}
 
-#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
+        /* identify card */
+	bttv_idcard(btv);
 
-#ifdef __sparc__
-	btv->bt848_mem=(unsigned char *)btv->bt848_adr;
-#else
-	btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
-#endif
-        
-        /* clear interrupt mask */
+        /* disable irqs, register irq handler */
 	btwrite(0, BT848_INT_MASK);
-
-        result = request_irq(btv->dev->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);
+        result = request_irq(btv->c.pci->irq, bttv_irq,
+                             SA_SHIRQ | SA_INTERRUPT,btv->c.name,(void *)btv);
+        if (result < 0) {
+                printk(KERN_ERR "bttv%d: can't get IRQ %d\n",
+		       bttv_num,btv->c.pci->irq);
 		goto fail1;
         }
-        if (result==-EBUSY)
-        {
-                printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\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;
-	}
-	
-        pci_set_master(dev);
-	pci_set_drvdata(dev,btv);
+        }
 
-	if(init_bt848(btv) < 0) {
-		bttv_remove(dev);
-		return -EIO;
+	/* 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;
+	btv->opt_vcr_hack   = vcr_hack;
+	btv->opt_whitecrush_upper  = whitecrush_upper;
+	btv->opt_whitecrush_lower  = whitecrush_lower;
+	
+	/* fill struct bttv with some useful defaults */
+	btv->init.btv         = btv;
+	btv->init.ov.w.width  = 320;
+	btv->init.ov.w.height = 240;
+	btv->init.fmt         = format_by_palette(VIDEO_PALETTE_RGB24);
+	btv->init.width       = 320;
+	btv->init.height      = 240;
+	btv->init.lines       = 16;
+	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_verbose)
+                bttv_gpio_tracking(btv,"init");
+
+        /* needs to be done before i2c is registered */
+        bttv_init_card1(btv);
+
+        /* register i2c + gpio */
+        init_bttv_i2c(btv);
+
+        /* some card-specific stuff (needs working i2c) */
+        bttv_init_card2(btv);
+	init_irqreg(btv);
+
+        /* register video4linux + input */
+	if (!bttv_tvcards[btv->c.type].no_video) {
+		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);
 	}
-	bttv_num++;
 
+
+	/* everything is fine */
+	bttv_num++;
         return 0;
 
  fail2:
-        free_irq(btv->dev->irq,btv);
+        free_irq(btv->c.pci->irq,btv);
+	
  fail1:
-	release_mem_region(pci_resource_start(btv->dev,0),
-			   pci_resource_len(btv->dev,0));
+	if (btv->bt848_mmio)
+		iounmap(btv->bt848_mmio);
+	release_mem_region(pci_resource_start(btv->c.pci,0),
+			   pci_resource_len(btv->c.pci,0));
+	pci_set_drvdata(dev,NULL);
 	return result;
 }
 
+static void __devexit bttv_remove(struct pci_dev *pci_dev)
+{
+        struct bttv *btv = pci_get_drvdata(pci_dev);
+
+	if (bttv_verbose)
+		printk("bttv%d: unloading\n",btv->c.nr);
+
+        /* shutdown everything (DMA+IRQs) */
+	btand(~15, BT848_GPIO_DMA_CTL);
+	btwrite(0, BT848_INT_MASK);
+	btwrite(~0x0, 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 + input */
+	fini_bttv_i2c(btv);
+
+	/* unregister video4linux */
+	bttv_unregister_video(btv);
+
+	/* free allocated memory */
+	btcx_riscmem_free(btv->c.pci,&btv->main);
+
+	/* free ressources */
+        free_irq(btv->c.pci->irq,btv);
+	iounmap(btv->bt848_mmio);
+        release_mem_region(pci_resource_start(btv->c.pci,0),
+                           pci_resource_len(btv->c.pci,0));
+
+	pci_set_drvdata(pci_dev, NULL);
+        return;
+}
+
+static int bttv_suspend(struct pci_dev *pci_dev, u32 state)
+{
+        struct bttv *btv = pci_get_drvdata(pci_dev);
+	struct bttv_buffer_set idle;
+	unsigned long flags;
+
+	printk("bttv%d: suspend %d\n", btv->c.nr, state);
+
+	/* stop dma + irqs */
+	spin_lock_irqsave(&btv->s_lock,flags);
+	memset(&idle, 0, sizeof(idle));
+	btv->state.video = btv->curr;
+	btv->state.vbi   = btv->cvbi;
+	btv->curr = idle;
+	bttv_buffer_activate_video(btv, &idle);
+	bttv_buffer_activate_vbi(btv, NULL);
+	bttv_set_dma(btv, 0, 0);
+	btwrite(0, BT848_INT_MASK);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+
+	/* save bt878 state */
+	btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
+	btv->state.gpio_data   = gpio_read();
+
+	/* save pci state */
+	pci_save_state(pci_dev, btv->state.pci_cfg);
+	if (0 != pci_set_power_state(pci_dev, state)) {
+		pci_disable_device(pci_dev);
+		btv->state.disabled = 1;
+	}
+	return 0;
+}
+
+static int bttv_resume(struct pci_dev *pci_dev)
+{
+        struct bttv *btv = pci_get_drvdata(pci_dev);
+	unsigned long flags;
+
+	printk("bttv%d: resume\n", btv->c.nr);
+
+	/* restore pci state */
+	if (btv->state.disabled) {
+		pci_enable_device(pci_dev);
+		btv->state.disabled = 0;
+	}
+	pci_set_power_state(pci_dev, 0);
+	pci_restore_state(pci_dev, btv->state.pci_cfg);
+
+	/* restore bt878 state */
+	bttv_reinit_bt848(btv);
+	gpio_inout(0xffffff, btv->state.gpio_enable);
+	gpio_write(btv->state.gpio_data);
+
+	/* restart dma */
+	spin_lock_irqsave(&btv->s_lock,flags);
+	btv->curr = btv->state.video;
+	btv->cvbi = btv->state.vbi;
+	bttv_buffer_activate_video(btv, &btv->curr);
+	bttv_buffer_activate_vbi(btv, btv->cvbi);
+	bttv_set_dma(btv, 0, btv->curr.irqflags);
+	spin_unlock_irqrestore(&btv->s_lock,flags);
+	return 0;
+}
+
 static struct pci_device_id bttv_pci_tbl[] = {
         {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -3165,7 +3981,10 @@
         .name     = "bttv",
         .id_table = bttv_pci_tbl,
         .probe    = bttv_probe,
-        .remove   = bttv_remove,
+        .remove   = __devexit_p(bttv_remove),
+
+	.suspend    = bttv_suspend,
+	.resume     = bttv_resume,
 };
 
 static int bttv_init_module(void)
@@ -3177,13 +3996,18 @@
 	       (BTTV_VERSION_CODE >> 16) & 0xff,
 	       (BTTV_VERSION_CODE >> 8) & 0xff,
 	       BTTV_VERSION_CODE & 0xff);
-	if (gbuffers < 2 || gbuffers > MAX_GBUFFERS)
+#ifdef SNAPSHOT
+	printk(KERN_INFO "bttv: snapshot date %04d-%02d-%02d\n",
+	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
+#endif
+	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();
 
diff -up linux-2.4.26/drivers/media/video/bttv-gpio.c linux/drivers/media/video/bttv-gpio.c
--- linux-2.4.26/drivers/media/video/bttv-gpio.c	2004-04-21 14:11:41.000000000 +0200
+++ linux/drivers/media/video/bttv-gpio.c	2004-04-21 14:11:41.000000000 +0200
@@ -0,0 +1,183 @@
+/*
+    bttv-gpio.c  --  gpio sub drivers
+
+    sysfs-based sub driver interface for bttv
+    mainly intented for gpio access
+
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+                           & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 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/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+/* ----------------------------------------------------------------------- */
+/* internal: the bttv "bus"                                                */
+
+static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct bttv_sub_driver *sub = to_bttv_sub_drv(drv);
+	int len = strlen(sub->wanted);
+
+	if (0 == strncmp(dev->bus_id, sub->wanted, len))
+		return 1;
+	return 0;
+}
+
+struct bus_type bttv_sub_bus_type = {
+	.name  = "bttv-sub",
+	.match = &bttv_sub_bus_match,
+};
+EXPORT_SYMBOL(bttv_sub_bus_type);
+
+static void release_sub_device(struct device *dev)
+{
+	struct bttv_sub_device *sub = to_bttv_sub_dev(dev);
+	kfree(sub);
+}
+
+int bttv_sub_add_device(struct bttv_core *core, char *name)
+{
+	struct bttv_sub_device *sub;
+
+	sub = kmalloc(sizeof(*sub),GFP_KERNEL);
+	if (NULL == sub)
+		return -ENOMEM;
+	memset(sub,0,sizeof(*sub));
+
+	sub->core        = core;
+	sub->dev.parent  = &core->pci->dev;
+	sub->dev.bus     = &bttv_sub_bus_type;
+	sub->dev.release = release_sub_device;
+	snprintf(sub->dev.bus_id,sizeof(sub->dev.bus_id),"%s%d",
+		 name, core->nr);
+
+	printk("bttv%d: add subdevice \"%s\"\n", core->nr, sub->dev.bus_id);
+	list_add_tail(&sub->list,&core->subs);
+	device_register(&sub->dev);
+	return 0;
+}
+
+int bttv_sub_del_devices(struct bttv_core *core)
+{
+	struct bttv_sub_device *sub;
+	struct list_head *item,*save;
+
+	list_for_each_safe(item,save,&core->subs) {
+		sub = list_entry(item,struct bttv_sub_device,list);
+		device_unregister(&sub->dev);
+	}
+	return 0;
+}
+
+void bttv_gpio_irq(struct bttv_core *core)
+{
+	struct bttv_sub_driver *drv;
+	struct bttv_sub_device *dev;
+	struct list_head *item;
+
+	list_for_each(item,&core->subs) {
+		dev = list_entry(item,struct bttv_sub_device,list);
+		drv = to_bttv_sub_drv(dev->dev.driver);
+		if (drv && drv->gpio_irq)
+			drv->gpio_irq(dev);
+	}
+}
+
+/* ----------------------------------------------------------------------- */
+/* external: sub-driver register/unregister                                */
+
+int bttv_sub_register(struct bttv_sub_driver *sub, char *wanted)
+{
+	sub->drv.bus = &bttv_sub_bus_type;
+	snprintf(sub->wanted,sizeof(sub->wanted),"%s",wanted);
+	driver_register(&sub->drv);
+	return 0;
+}
+EXPORT_SYMBOL(bttv_sub_register);
+
+int bttv_sub_unregister(struct bttv_sub_driver *sub)
+{
+	driver_unregister(&sub->drv);
+	return 0;
+}
+EXPORT_SYMBOL(bttv_sub_unregister);
+
+/* ----------------------------------------------------------------------- */
+/* external: gpio access functions                                         */
+
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+	unsigned long flags;
+	u32 data;
+
+	spin_lock_irqsave(&btv->gpio_lock,flags);
+	data = btread(BT848_GPIO_OUT_EN);
+	data = data & ~mask;
+	data = data | (mask & outbits);
+	btwrite(data,BT848_GPIO_OUT_EN);
+	spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+EXPORT_SYMBOL(bttv_gpio_inout);
+
+u32 bttv_gpio_read(struct bttv_core *core)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+	u32 value;
+
+	value = btread(BT848_GPIO_DATA);
+	return value;
+}
+EXPORT_SYMBOL(bttv_gpio_read);
+
+void bttv_gpio_write(struct bttv_core *core, u32 value)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+
+	btwrite(value,BT848_GPIO_DATA);
+}
+EXPORT_SYMBOL(bttv_gpio_write);
+
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits)
+{
+	struct bttv *btv = container_of(core, struct bttv, c);
+	unsigned long flags;
+	u32 data;
+
+	spin_lock_irqsave(&btv->gpio_lock,flags);
+	data = btread(BT848_GPIO_DATA);
+	data = data & ~mask;
+	data = data | (mask & bits);
+	btwrite(data,BT848_GPIO_DATA);
+	spin_unlock_irqrestore(&btv->gpio_lock,flags);
+}
+EXPORT_SYMBOL(bttv_gpio_bits);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -up linux-2.4.26/drivers/media/video/bttv-i2c.c linux/drivers/media/video/bttv-i2c.c
--- linux-2.4.26/drivers/media/video/bttv-i2c.c	2004-04-21 14:11:41.000000000 +0200
+++ linux/drivers/media/video/bttv-i2c.c	2004-04-21 14:11:41.000000000 +0200
@@ -0,0 +1,457 @@
+/*
+    bttv-i2c.c  --  all the i2c code is here
+
+    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-2003 Gerd Knorr <kraxel@bytesex.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 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/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "bttvp.h"
+
+static struct i2c_algo_bit_data bttv_i2c_algo_bit_template;
+static struct i2c_adapter bttv_i2c_adap_sw_template;
+static struct i2c_adapter bttv_i2c_adap_hw_template;
+static struct i2c_client bttv_i2c_client_template;
+
+static void bttv_inc_use(struct i2c_adapter *adap);
+static void bttv_dec_use(struct i2c_adapter *adap);
+static int attach_inform(struct i2c_client *client);
+
+static int i2c_debug = 0;
+static int i2c_hw = 0;
+MODULE_PARM(i2c_debug,"i");
+MODULE_PARM(i2c_hw,"i");
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - bitbanging adapter (software i2c)                       */
+
+void bttv_bit_setscl(void *data, int state)
+{
+	struct bttv *btv = (struct bttv*)data;
+
+	if (state)
+		btv->i2c_state |= 0x02;
+	else
+		btv->i2c_state &= ~0x02;
+	btwrite(btv->i2c_state, BT848_I2C);
+	btread(BT848_I2C);
+}
+
+void bttv_bit_setsda(void *data, int state)
+{
+	struct bttv *btv = (struct bttv*)data;
+
+	if (state)
+		btv->i2c_state |= 0x01;
+	else
+		btv->i2c_state &= ~0x01;
+	btwrite(btv->i2c_state, BT848_I2C);
+	btread(BT848_I2C);
+}
+
+static int bttv_bit_getscl(void *data)
+{
+	struct bttv *btv = (struct bttv*)data;
+	int state;
+	
+	state = btread(BT848_I2C) & 0x02 ? 1 : 0;
+	return state;
+}
+
+static int bttv_bit_getsda(void *data)
+{
+	struct bttv *btv = (struct bttv*)data;
+	int state;
+
+	state = btread(BT848_I2C) & 0x01;
+	return state;
+}
+
+static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = {
+	.setsda  = bttv_bit_setsda,
+	.setscl  = bttv_bit_setscl,
+	.getsda  = bttv_bit_getsda,
+	.getscl  = bttv_bit_getscl,
+	.udelay  = 16,
+	.mdelay  = 10,
+	.timeout = 200,
+};
+
+static struct i2c_adapter bttv_i2c_adap_sw_template = {
+	.inc_use           = bttv_inc_use,
+	.dec_use           = bttv_dec_use,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+	.class             = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+	I2C_DEVNAME("bt848"),
+	.id                = I2C_HW_B_BT848,
+	.client_register   = attach_inform,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - hardware i2c                                            */
+
+static int algo_control(struct i2c_adapter *adapter, 
+			unsigned int cmd, unsigned long arg)
+{
+	return 0;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL;
+}
+
+static int
+bttv_i2c_wait_done(struct bttv *btv)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int rc = 0;
+	
+	add_wait_queue(&btv->i2c_queue, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (0 == btv->i2c_done)
+		schedule_timeout(HZ/50+1);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&btv->i2c_queue, &wait);
+
+	if (0 == btv->i2c_done)
+		/* timeout */
+		rc = -EIO;
+	if (btv->i2c_done & BT848_INT_RACK)
+		rc = 1;
+	btv->i2c_done = 0;
+	return rc;
+}
+
+#define I2C_HW (BT878_I2C_MODE  | BT848_I2C_SYNC |\
+		BT848_I2C_SCL | BT848_I2C_SDA)
+
+static int
+bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+	u32 xmit;
+	int retval,cnt;
+
+	/* sanity checks */
+	if (0 == msg->len)
+		return -EINVAL;
+
+	/* start, address + first byte */
+	xmit = (msg->addr << 25) | (msg->buf[0] << 16) | I2C_HW;
+	if (msg->len > 1 || !last)
+		xmit |= BT878_I2C_NOSTOP;
+	btwrite(xmit, BT848_I2C);
+	retval = bttv_i2c_wait_done(btv);
+	if (retval < 0)
+		goto err;
+	if (retval == 0)
+		goto eio;
+	if (i2c_debug) {
+		printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
+		if (!(xmit & BT878_I2C_NOSTOP))
+			printk(" >\n");
+	}
+
+	for (cnt = 1; cnt < msg->len; cnt++ ) {
+		/* following bytes */
+		xmit = (msg->buf[cnt] << 24) | I2C_HW | BT878_I2C_NOSTART;
+		if (cnt < msg->len-1 || !last)
+			xmit |= BT878_I2C_NOSTOP;
+		btwrite(xmit, BT848_I2C);
+		retval = bttv_i2c_wait_done(btv);
+		if (retval < 0)
+			goto err;
+		if (retval == 0)
+			goto eio;
+		if (i2c_debug) {
+			printk(" %02x", msg->buf[cnt]);
+			if (!(xmit & BT878_I2C_NOSTOP))
+				printk(" >\n");
+		}
+	}
+	return msg->len;
+
+ eio:
+	retval = -EIO;
+ err:
+	if (i2c_debug)
+		printk(" ERR: %d\n",retval);
+	return retval;
+}
+
+static int
+bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+{
+	u32 xmit;
+	u32 cnt;
+	int retval;
+
+	for(cnt = 0; cnt < msg->len; cnt++) {
+		xmit = (msg->addr << 25) | (1 << 24) | I2C_HW;
+		if (cnt < msg->len-1)
+			xmit |= BT848_I2C_W3B;
+		if (cnt < msg->len-1 || !last)
+			xmit |= BT878_I2C_NOSTOP;
+		if (cnt)
+			xmit |= BT878_I2C_NOSTART;
+		btwrite(xmit, BT848_I2C);
+		retval = bttv_i2c_wait_done(btv);
+		if (retval < 0)
+			goto err;
+		if (retval == 0)
+			goto eio;
+		msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff;
+		if (i2c_debug) {
+			if (!(xmit & BT878_I2C_NOSTART))
+				printk(" <R %02x", (msg->addr << 1) +1);
+			printk(" =%02x", msg->buf[cnt]);
+			if (!(xmit & BT878_I2C_NOSTOP))
+				printk(" >\n");
+		}
+	}
+	return msg->len;
+
+ eio:
+	retval = -EIO;
+ err:
+	if (i2c_debug)
+		printk(" ERR: %d\n",retval);
+       	return retval;
+}
+
+int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+	struct bttv *btv = i2c_get_adapdata(i2c_adap);
+	int retval = 0;
+	int i;
+
+	if (i2c_debug)
+		printk("bt-i2c:");
+	btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT);
+	for (i = 0 ; i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD) {
+			/* read */
+			retval = bttv_i2c_readbytes(btv, &msgs[i], i+1 == num);
+			if (retval < 0)
+				goto err;
+		} else {
+			/* write */
+			retval = bttv_i2c_sendbytes(btv, &msgs[i], i+1 == num);
+			if (retval < 0)
+				goto err;
+		}
+	}
+	return num;
+
+ err:
+	return retval;
+}
+
+static struct i2c_algorithm bttv_algo = {
+	.name          = "bt878",
+	.id            = I2C_ALGO_BIT | I2C_HW_B_BT848 /* FIXME */,
+	.master_xfer   = bttv_i2c_xfer,
+	.algo_control  = algo_control,
+	.functionality = functionality,
+};
+
+static struct i2c_adapter bttv_i2c_adap_hw_template = {
+	.inc_use       = bttv_inc_use,
+	.dec_use       = bttv_dec_use,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+	.class         = I2C_ADAP_CLASS_TV_ANALOG,
+#endif
+	I2C_DEVNAME("bt878"),
+	.id            = I2C_ALGO_BIT | I2C_HW_B_BT848 /* FIXME */,
+	.algo          = &bttv_algo,
+	.client_register = attach_inform,
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions - common stuff                                            */
+
+static void bttv_inc_use(struct i2c_adapter *adap)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void bttv_dec_use(struct i2c_adapter *adap)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static int attach_inform(struct i2c_client *client)
+{
+        struct bttv *btv = i2c_get_adapdata(client->adapter);
+
+	if (btv->tuner_type != UNSET)
+		bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type);
+	if (btv->pinnacle_id != UNSET)
+		bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE,
+				      &btv->pinnacle_id);
+
+        if (bttv_debug)
+		printk("bttv%d: i2c attach [client=%s]\n",
+		       btv->c.nr, i2c_clientname(client));
+        return 0;
+}
+
+void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg)
+{
+	if (0 != btv->i2c_rc)
+		return;
+	i2c_clients_command(&btv->c.i2c_adap, cmd, arg);
+}
+
+void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg)
+{
+	if (card >= bttv_num)
+		return;
+	bttv_call_i2c_clients(&bttvs[card], cmd, arg);
+}
+
+static struct i2c_client bttv_i2c_client_template = {
+	I2C_DEVNAME("bttv internal"),
+        .id       = -1,
+};
+
+
+/* read I2C */
+int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) 
+{
+        unsigned char buffer = 0;
+
+	if (0 != btv->i2c_rc)
+		return -1;
+	if (bttv_verbose && NULL != probe_for)
+		printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ",
+		       btv->c.nr,probe_for,addr);
+        btv->i2c_client.addr = addr >> 1;
+        if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) {
+		if (NULL != probe_for) {
+			if (bttv_verbose)
+				printk("not found\n");
+		} else
+			printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n",
+			       btv->c.nr,addr);
+                return -1;
+	}
+	if (bttv_verbose && NULL != probe_for)
+		printk("found\n");
+        return buffer;
+}
+
+/* write I2C */
+int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+                    unsigned char b2, int both)
+{
+        unsigned char buffer[2];
+        int bytes = both ? 2 : 1;
+
+	if (0 != btv->i2c_rc)
+		return -1;
+        btv->i2c_client.addr = addr >> 1;
+        buffer[0] = b1;
+        buffer[1] = b2;
+        if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes))
+		return -1;
+        return 0;
+}
+
+/* read EEPROM content */
+void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr)
+{
+	int i;
+        
+	if (bttv_I2CWrite(btv, addr, 0, -1, 0)<0) {
+		printk(KERN_WARNING "bttv: readee error\n");
+		return;
+	}
+	btv->i2c_client.addr = addr >> 1;
+	for (i=0; i<256; i+=16) {
+		if (16 != i2c_master_recv(&btv->i2c_client,eedata+i,16)) {
+			printk(KERN_WARNING "bttv: readee error\n");
+			break;
+		}
+	}
+}
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_bttv_i2c(struct bttv *btv)
+{
+	memcpy(&btv->i2c_client, &bttv_i2c_client_template,
+	       sizeof(bttv_i2c_client_template));
+
+	if (i2c_hw)
+		btv->use_i2c_hw = 1;
+	if (btv->use_i2c_hw) {
+		/* bt878 */
+		memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_hw_template,
+		       sizeof(bttv_i2c_adap_hw_template));
+	} else {
+		/* bt848 */
+		memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_sw_template,
+		       sizeof(bttv_i2c_adap_sw_template));
+		memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template,
+		       sizeof(bttv_i2c_algo_bit_template));
+		btv->i2c_algo.data = btv;
+		btv->c.i2c_adap.algo_data = &btv->i2c_algo;
+	}
+
+	snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name),
+		 "bt%d #%d [%s]", btv->id, btv->c.nr,
+		 btv->use_i2c_hw ? "hw" : "sw");
+
+        i2c_set_adapdata(&btv->c.i2c_adap, btv);
+        btv->i2c_client.adapter = &btv->c.i2c_adap;
+
+	if (btv->use_i2c_hw) {
+		btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap);
+	} else {
+		bttv_bit_setscl(btv,1);
+		bttv_bit_setsda(btv,1);
+		btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap);
+	}
+	return btv->i2c_rc;
+}
+
+int __devexit fini_bttv_i2c(struct bttv *btv)
+{
+	if (0 != btv->i2c_rc)
+		return 0;
+
+	if (btv->use_i2c_hw) {
+		return i2c_del_adapter(&btv->c.i2c_adap);
+	} else {
+		return i2c_bit_del_bus(&btv->c.i2c_adap);
+	}
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
--- linux-2.4.29/drivers/media/video/bttv-if.c.orig	2005-01-19 15:09:56.000000000 +0100
+++ linux-2.4.29/drivers/media/video/bttv-if.c	2005-01-20 14:40:48.000000000 +0100
@@ -1,7 +1,7 @@
 /*
-    bttv-if.c  --  interfaces to other kernel modules
-	all the i2c code is here
-	also the gpio interface exported by bttv (used by lirc)
+    bttv-if.c  --  old gpio interface to other kernel modules
+                   don't use in new code, will go away in 2.7
+		   have a look at bttv-gpio.c instead.
 
     bttv - Bt848 frame grabber driver
 
@@ -27,15 +27,11 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
-
+#include <linux/delay.h>
 #include <asm/io.h>
 
 #include "bttvp.h"
 
-static struct i2c_algo_bit_data bttv_i2c_algo_template;
-static struct i2c_adapter bttv_i2c_adap_template;
-static struct i2c_client bttv_i2c_client_template;
-
 EXPORT_SYMBOL(bttv_get_cardinfo);
 EXPORT_SYMBOL(bttv_get_pcidev);
 EXPORT_SYMBOL(bttv_get_id);
@@ -55,7 +51,7 @@
 	if (card >= bttv_num) {
 		return -1;
 	}
-	*type   = bttvs[card].type;
+	*type   = bttvs[card].c.type;
 	*cardid = bttvs[card].cardid;
 	return 0;
 }
@@ -64,7 +60,7 @@
 {
 	if (card >= bttv_num)
 		return NULL;
-	return bttvs[card].dev;
+	return bttvs[card].c.pci;
 }
 
 int bttv_get_id(unsigned int card)
@@ -73,9 +69,19 @@
 	if (card >= bttv_num) {
 		return -1;
 	}
-	return bttvs[card].type;
+	return bttvs[card].c.type;
 }
 
+/* with 2.6.x not needed thanks to the driver model + sysfs */
+struct i2c_adapter *bttv_get_i2c_adap(unsigned int card)
+{
+	if (card >= bttv_num) {
+		return NULL;
+	}
+	return &bttvs[card].c.i2c_adap;
+}
+EXPORT_SYMBOL(bttv_get_i2c_adap);
+
 int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
 {
 	struct bttv *btv;
@@ -85,7 +91,7 @@
 	}
 	
 	btv = &bttvs[card];
-	btaor(data, ~mask, BT848_GPIO_OUT_EN);
+	gpio_inout(mask,data);
 	if (bttv_gpio)
 		bttv_gpio_tracking(btv,"extern enable");
 	return 0;
@@ -107,7 +113,7 @@
 
 /* prior setting BT848_GPIO_REG_INP is (probably) not needed 
    because we set direct input on init */
-	*data = btread(BT848_GPIO_DATA);
+	*data = gpio_read();
 	return 0;
 }
 
@@ -123,7 +129,7 @@
 
 /* prior setting BT848_GPIO_REG_INP is (probably) not needed 
    because direct input is set on init */
-	btaor(data & mask, ~mask, BT848_GPIO_DATA);
+	gpio_bits(mask,data);
 	if (bttv_gpio)
 		bttv_gpio_tracking(btv,"extern write");
 	return 0;
@@ -144,203 +150,6 @@
 	return &btv->gpioq;
 }
 
-
-/* ----------------------------------------------------------------------- */
-/* I2C functions                                                           */
-
-void bttv_bit_setscl(void *data, int state)
-{
-	struct bttv *btv = (struct bttv*)data;
-
-	if (state)
-		btv->i2c_state |= 0x02;
-	else
-		btv->i2c_state &= ~0x02;
-	btwrite(btv->i2c_state, BT848_I2C);
-	btread(BT848_I2C);
-}
-
-void bttv_bit_setsda(void *data, int state)
-{
-	struct bttv *btv = (struct bttv*)data;
-
-	if (state)
-		btv->i2c_state |= 0x01;
-	else
-		btv->i2c_state &= ~0x01;
-	btwrite(btv->i2c_state, BT848_I2C);
-	btread(BT848_I2C);
-}
-
-static int bttv_bit_getscl(void *data)
-{
-	struct bttv *btv = (struct bttv*)data;
-	int state;
-	
-	state = btread(BT848_I2C) & 0x02 ? 1 : 0;
-	return state;
-}
-
-static int bttv_bit_getsda(void *data)
-{
-	struct bttv *btv = (struct bttv*)data;
-	int state;
-
-	state = btread(BT848_I2C) & 0x01;
-	return state;
-}
-
-static void bttv_inc_use(struct i2c_adapter *adap)
-{
-	MOD_INC_USE_COUNT;
-}
-
-static void bttv_dec_use(struct i2c_adapter *adap)
-{
-	MOD_DEC_USE_COUNT;
-}
-
-static int attach_inform(struct i2c_client *client)
-{
-        struct bttv *btv = i2c_get_adapdata(client->adapter);
-
-	if (btv->tuner_type != UNSET)
-		bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type);
-	if (btv->pinnacle_id != UNSET)
-		bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE,
-				      &btv->pinnacle_id);
-
-        if (bttv_debug)
-		printk("bttv%d: i2c attach [client=%s]\n",
-		       btv->nr, i2c_clientname(client));
-        return 0;
-}
-
-void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg)
-{
-	if (0 != btv->i2c_rc)
-		return;
-	i2c_clients_command(&btv->i2c_adap, cmd, arg);
-}
-
-void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg)
-{
-	if (card >= bttv_num)
-		return;
-	bttv_call_i2c_clients(&bttvs[card], cmd, arg);
-}
-
-static struct i2c_algo_bit_data bttv_i2c_algo_template = {
-	.setsda  = bttv_bit_setsda,
-	.setscl  = bttv_bit_setscl,
-	.getsda  = bttv_bit_getsda,
-	.getscl  = bttv_bit_getscl,
-	.udelay  = 16,
-	.mdelay  = 10,
-	.timeout = 200,
-};
-
-static struct i2c_adapter bttv_i2c_adap_template = {
-	.inc_use           = bttv_inc_use,
-	.dec_use           = bttv_dec_use,
-#ifdef I2C_ADAP_CLASS_TV_ANALOG
-	.class             = I2C_ADAP_CLASS_TV_ANALOG,
-#endif
-	I2C_DEVNAME("bt848"),
-	.id                = I2C_HW_B_BT848,
-	.client_register   = attach_inform,
-};
-
-static struct i2c_client bttv_i2c_client_template = {
-	I2C_DEVNAME("bttv internal"),
-        .id       = -1,
-};
-
-
-/* read I2C */
-int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) 
-{
-        unsigned char buffer = 0;
-
-	if (0 != btv->i2c_rc)
-		return -1;
-	if (bttv_verbose && NULL != probe_for)
-		printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ",
-		       btv->nr,probe_for,addr);
-        btv->i2c_client.addr = addr >> 1;
-        if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) {
-		if (NULL != probe_for) {
-			if (bttv_verbose)
-				printk("not found\n");
-		} else
-			printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n",
-			       btv->nr,addr);
-                return -1;
-	}
-	if (bttv_verbose && NULL != probe_for)
-		printk("found\n");
-        return buffer;
-}
-
-/* write I2C */
-int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
-                    unsigned char b2, int both)
-{
-        unsigned char buffer[2];
-        int bytes = both ? 2 : 1;
-
-	if (0 != btv->i2c_rc)
-		return -1;
-        btv->i2c_client.addr = addr >> 1;
-        buffer[0] = b1;
-        buffer[1] = b2;
-        if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes))
-		return -1;
-        return 0;
-}
-
-/* read EEPROM content */
-void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr)
-{
-	int i;
-        
-	if (bttv_I2CWrite(btv, addr, 0, -1, 0)<0) {
-		printk(KERN_WARNING "bttv: readee error\n");
-		return;
-	}
-	btv->i2c_client.addr = addr >> 1;
-	for (i=0; i<256; i+=16) {
-		if (16 != i2c_master_recv(&btv->i2c_client,eedata+i,16)) {
-			printk(KERN_WARNING "bttv: readee error\n");
-			break;
-		}
-	}
-}
-
-/* init + register i2c algo-bit adapter */
-int __devinit init_bttv_i2c(struct bttv *btv)
-{
-	memcpy(&btv->i2c_adap, &bttv_i2c_adap_template,
-	       sizeof(struct i2c_adapter));
-	memcpy(&btv->i2c_algo, &bttv_i2c_algo_template,
-	       sizeof(struct i2c_algo_bit_data));
-	memcpy(&btv->i2c_client, &bttv_i2c_client_template,
-	       sizeof(struct i2c_client));
-
-	sprintf(btv->i2c_adap.name, "bt848 #%d", btv->nr);
-
-        btv->i2c_algo.data = btv;
-        i2c_set_adapdata(&btv->i2c_adap, btv);
-        btv->i2c_adap.algo_data = &btv->i2c_algo;
-        btv->i2c_client.adapter = &btv->i2c_adap;
-
-	bttv_bit_setscl(btv,1);
-	bttv_bit_setsda(btv,1);
-
-	btv->i2c_rc = i2c_bit_add_bus(&btv->i2c_adap);
-	return btv->i2c_rc;
-}
-
 /*
  * Local variables:
  * c-basic-offset: 8
diff -up linux-2.4.26/drivers/media/video/bttv-risc.c linux/drivers/media/video/bttv-risc.c
--- linux-2.4.26/drivers/media/video/bttv-risc.c	2004-04-21 14:11:41.000000000 +0200
+++ linux/drivers/media/video/bttv-risc.c	2004-04-21 14:11:41.000000000 +0200
@@ -0,0 +1,778 @@
+/*
+    bttv-risc.c  --  interfaces to other kernel modules
+
+    bttv risc code handling
+	- memory management
+	- generation
+
+    (c) 2000-2003 Gerd Knorr <kraxel@bytesex.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 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/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "bttvp.h"
+
+#define VCR_HACK_LINES 4
+
+/* ---------------------------------------------------------- */
+/* risc code generators                                       */
+
+int
+bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+		 struct scatterlist *sglist,
+		 unsigned int offset, unsigned int bpl,
+		 unsigned int padding, unsigned int lines)
+{
+	u32 instructions,line,todo;
+	struct scatterlist *sg;
+	u32 *rp;
+	int rc;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line + sync + jump (all 2 dwords) */
+	instructions  = (bpl * lines) / PAGE_SIZE + lines;
+	instructions += 2;
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0)
+		return rc;
+
+	/* sync instruction */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+	*(rp++) = cpu_to_le32(0);
+
+	/* scan lines */
+	sg = sglist;
+	for (line = 0; line < lines; line++) {
+		if ((btv->opt_vcr_hack) &&
+		    (line >= (lines - VCR_HACK_LINES)))
+			continue;
+		while (offset && offset >= sg_dma_len(sg)) {
+			offset -= sg_dma_len(sg);
+			sg++;
+		}
+		if (bpl <= sg_dma_len(sg)-offset) {
+			/* fits into current chunk */
+                        *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+					    BT848_RISC_EOL|bpl);
+                        *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+                        offset+=bpl;
+		} else {
+			/* scanline needs to be splitted */
+                        todo = bpl;
+                        *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+					    (sg_dma_len(sg)-offset));
+                        *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+                        todo -= (sg_dma_len(sg)-offset);
+                        offset = 0;
+                        sg++;
+                        while (todo > sg_dma_len(sg)) {
+                                *(rp++)=cpu_to_le32(BT848_RISC_WRITE|
+						    sg_dma_len(sg));
+                                *(rp++)=cpu_to_le32(sg_dma_address(sg));
+				todo -= sg_dma_len(sg);
+				sg++;
+			}
+                        *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|
+					    todo);
+			*(rp++)=cpu_to_le32(sg_dma_address(sg));
+			offset += todo;
+		}
+		offset += padding;
+	}
+	dprintk("bttv%d: risc planar: %d sglist elems\n", btv->c.nr, (int)(sg-sglist));
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	return 0;
+}
+
+int
+bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc,
+		 struct scatterlist *sglist,
+		 unsigned int yoffset,  unsigned int ybpl,
+		 unsigned int ypadding, unsigned int ylines,
+		 unsigned int uoffset,  unsigned int voffset,
+		 unsigned int hshift,   unsigned int vshift,
+		 unsigned int cpadding)
+{
+	unsigned int instructions,line,todo,ylen,chroma;
+	u32 *rp,ri;
+	struct scatterlist *ysg;
+	struct scatterlist *usg;
+	struct scatterlist *vsg;
+	int rc;
+
+	/* estimate risc mem: worst case is one write per page border +
+	   one write per scan line (5 dwords)
+	   plus sync + jump (2 dwords) */
+	instructions  = (ybpl * ylines * 2) / PAGE_SIZE + ylines;
+	instructions += 2;
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0)
+		return rc;
+
+	/* sync instruction */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
+	*(rp++) = cpu_to_le32(0);
+
+	/* scan lines */
+	ysg = sglist;
+	usg = sglist;
+	vsg = sglist;
+	for (line = 0; line < ylines; line++) {
+		if ((btv->opt_vcr_hack) &&
+		    (line >= (ylines - VCR_HACK_LINES)))
+			continue;
+		switch (vshift) {
+		case 0:  chroma = 1;           break;
+		case 1:  chroma = !(line & 1); break;
+		case 2:  chroma = !(line & 3); break;
+		default: chroma = 0;
+		}
+		for (todo = ybpl; todo > 0; todo -= ylen) {
+			/* go to next sg entry if needed */
+			while (yoffset && yoffset >= sg_dma_len(ysg)) {
+				yoffset -= sg_dma_len(ysg);
+				ysg++;
+			}
+			while (uoffset && uoffset >= sg_dma_len(usg)) {
+				uoffset -= sg_dma_len(usg);
+				usg++;
+			}
+			while (voffset && voffset >= sg_dma_len(vsg)) {
+				voffset -= sg_dma_len(vsg);
+				vsg++;
+			}
+
+			/* calculate max number of bytes we can write */
+			ylen = todo;
+			if (yoffset + ylen > sg_dma_len(ysg))
+				ylen = sg_dma_len(ysg) - yoffset;
+			if (chroma) {
+				if (uoffset + (ylen>>hshift) > sg_dma_len(usg))
+					ylen = (sg_dma_len(usg) - uoffset) << hshift;
+				if (voffset + (ylen>>hshift) > sg_dma_len(vsg))
+					ylen = (sg_dma_len(vsg) - voffset) << hshift;
+				ri = BT848_RISC_WRITE123;
+			} else {
+				ri = BT848_RISC_WRITE1S23;
+			}
+			if (ybpl == todo)
+				ri |= BT848_RISC_SOL;
+			if (ylen == todo)
+				ri |= BT848_RISC_EOL;
+
+			/* write risc instruction */
+                        *(rp++)=cpu_to_le32(ri | ylen);
+                        *(rp++)=cpu_to_le32(((ylen >> hshift) << 16) |
+					    (ylen >> hshift));
+			*(rp++)=cpu_to_le32(sg_dma_address(ysg)+yoffset);
+			yoffset += ylen;
+			if (chroma) {
+				*(rp++)=cpu_to_le32(sg_dma_address(usg)+uoffset);
+				uoffset += ylen >> hshift;
+				*(rp++)=cpu_to_le32(sg_dma_address(vsg)+voffset);
+				voffset += ylen >> hshift;
+			}
+		}
+		yoffset += ypadding;
+		if (chroma) {
+			uoffset += cpadding;
+			voffset += cpadding;
+		}
+	}
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	return 0;
+}
+
+int
+bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc,
+		  const struct bttv_format *fmt, struct bttv_overlay *ov,
+		  int skip_even, int skip_odd)
+{
+	int instructions,rc,line,maxy,start,end,skip,nskips;
+	struct btcx_skiplist *skips;
+	u32 *rp,ri,ra;
+	u32 addr;
+
+	/* skip list for window clipping */
+	if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL)))
+		return -ENOMEM;
+	
+	/* estimate risc mem: worst case is (clip+1) * lines instructions
+	   + sync + jump (all 2 dwords) */
+	instructions  = (ov->nclips + 1) *
+		((skip_even || skip_odd) ? ov->w.height>>1 :  ov->w.height);
+	instructions += 2;
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) {
+		kfree(skips);
+		return rc;
+	}
+
+	/* sync instruction */
+	rp = risc->cpu;
+	*(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+	*(rp++) = cpu_to_le32(0);
+
+	addr  = (unsigned long)btv->fbuf.base;
+	addr += btv->fbuf.fmt.bytesperline * ov->w.top;
+	addr += (fmt->depth >> 3)          * ov->w.left;
+
+	/* scan lines */
+	for (maxy = -1, line = 0; line < ov->w.height;
+	     line++, addr += btv->fbuf.fmt.bytesperline) {
+		if ((btv->opt_vcr_hack) &&
+		     (line >= (ov->w.height - VCR_HACK_LINES)))
+			continue;
+		if ((line%2) == 0  &&  skip_even)
+			continue;
+		if ((line%2) == 1  &&  skip_odd)
+			continue;
+
+		/* calculate clipping */
+		if (line > maxy)
+			btcx_calc_skips(line, ov->w.width, &maxy,
+					skips, &nskips, ov->clips, ov->nclips);
+
+		/* write out risc code */
+		for (start = 0, skip = 0; start < ov->w.width; start = end) {
+			if (skip >= nskips) {
+				ri  = BT848_RISC_WRITE;
+				end = ov->w.width;
+			} else if (start < skips[skip].start) {
+				ri  = BT848_RISC_WRITE;
+				end = skips[skip].start;
+			} else {
+				ri  = BT848_RISC_SKIP;
+				end = skips[skip].end;
+				skip++;
+			}
+			if (BT848_RISC_WRITE == ri)
+				ra = addr + (fmt->depth>>3)*start;
+			else
+				ra = 0;
+				
+			if (0 == start)
+				ri |= BT848_RISC_SOL;
+			if (ov->w.width == end)
+				ri |= BT848_RISC_EOL;
+			ri |= (fmt->depth>>3) * (end-start);
+
+			*(rp++)=cpu_to_le32(ri);
+			if (0 != ra)
+				*(rp++)=cpu_to_le32(ra);
+		}
+	}
+
+	/* save pointer to jmp instruction address */
+	risc->jmp = rp;
+	kfree(skips);
+	return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+void
+bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo,
+	      int width, int height, int interleaved, int norm)
+{
+	const struct bttv_tvnorm *tvnorm = &bttv_tvnorms[norm];
+        u32 xsf, sr;
+	int vdelay;
+
+	int swidth       = tvnorm->swidth;
+	int totalwidth   = tvnorm->totalwidth;
+	int scaledtwidth = tvnorm->scaledtwidth;
+
+	if (bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) {
+		swidth       = 720;
+		totalwidth   = 858;
+		scaledtwidth = 858;
+	}
+
+	vdelay = tvnorm->vdelay;
+#if 0 /* FIXME */
+	if (vdelay < btv->vbi.lines*2)
+		vdelay = btv->vbi.lines*2;
+#endif
+
+        xsf = (width*scaledtwidth)/swidth;
+        geo->hscale =  ((totalwidth*4096UL)/xsf-4096);
+        geo->hdelay =  tvnorm->hdelayx1;
+        geo->hdelay =  (geo->hdelay*width)/swidth;
+        geo->hdelay &= 0x3fe;
+        sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512;
+        geo->vscale =  (0x10000UL-sr) & 0x1fff;
+        geo->crop   =  ((width>>8)&0x03) | ((geo->hdelay>>6)&0x0c) |
+                ((tvnorm->sheight>>4)&0x30) | ((vdelay>>2)&0xc0);
+        geo->vscale |= interleaved ? (BT848_VSCALE_INT<<8) : 0;
+        geo->vdelay  =  vdelay;
+        geo->width   =  width;
+        geo->sheight =  tvnorm->sheight;
+	geo->vtotal  =  tvnorm->vtotal;
+
+        if (btv->opt_combfilter) {
+                geo->vtc  = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+                geo->comb = (width < 769) ? 1 : 0;
+        } else {
+                geo->vtc  = 0;
+                geo->comb = 0;
+        }
+}
+
+void
+bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd)
+{
+        int off = odd ? 0x80 : 0x00;
+
+	if (geo->comb)
+		btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+	else
+		btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+
+        btwrite(geo->vtc,             BT848_E_VTC+off);
+        btwrite(geo->hscale >> 8,     BT848_E_HSCALE_HI+off);
+        btwrite(geo->hscale & 0xff,   BT848_E_HSCALE_LO+off);
+        btaor((geo->vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
+        btwrite(geo->vscale & 0xff,   BT848_E_VSCALE_LO+off);
+        btwrite(geo->width & 0xff,    BT848_E_HACTIVE_LO+off);
+        btwrite(geo->hdelay & 0xff,   BT848_E_HDELAY_LO+off);
+        btwrite(geo->sheight & 0xff,  BT848_E_VACTIVE_LO+off);
+        btwrite(geo->vdelay & 0xff,   BT848_E_VDELAY_LO+off);
+        btwrite(geo->crop,            BT848_E_CROP+off);
+	btwrite(geo->vtotal>>8,       BT848_VTOTAL_HI);
+        btwrite(geo->vtotal & 0xff,   BT848_VTOTAL_LO);
+}
+
+/* ---------------------------------------------------------- */
+/* risc group / risc main loop / dma management               */
+
+void
+bttv_set_dma(struct bttv *btv, int override, int irqflags)
+{
+	unsigned long cmd;
+	int capctl;
+
+	btv->cap_ctl = 0;
+	if (NULL != btv->curr.top)      btv->cap_ctl |= 0x02;
+	if (NULL != btv->curr.bottom)   btv->cap_ctl |= 0x01;
+	if (NULL != btv->cvbi)          btv->cap_ctl |= 0x0c;
+
+	capctl  = 0;
+	capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00;  /* capture  */
+	capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00;  /* vbi data */
+	capctl |= override;
+
+	d2printk(KERN_DEBUG
+		 "bttv%d: capctl=%x irq=%d top=%08Lx/%08Lx even=%08Lx/%08Lx\n",
+		 btv->c.nr,capctl,irqflags,
+		 btv->cvbi         ? (unsigned long long)btv->cvbi->top.dma            : 0,
+		 btv->curr.top     ? (unsigned long long)btv->curr.top->top.dma        : 0,
+		 btv->cvbi         ? (unsigned long long)btv->cvbi->bottom.dma         : 0,
+		 btv->curr.bottom  ? (unsigned long long)btv->curr.bottom->bottom.dma  : 0);
+	
+	cmd = BT848_RISC_JUMP;
+	if (irqflags) {
+		cmd |= BT848_RISC_IRQ;
+		cmd |= (irqflags  & 0x0f) << 16;
+		cmd |= (~irqflags & 0x0f) << 20;
+	}
+	if (irqflags || btv->cvbi) {
+		mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT);
+	} else {
+		del_timer(&btv->timeout);
+	}
+        btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd);
+	
+	btaor(capctl, ~0x0f, BT848_CAP_CTL);
+	if (capctl) {
+		if (btv->dma_on)
+			return;
+		btwrite(btv->main.dma, BT848_RISC_STRT_ADD);
+		btor(3, BT848_GPIO_DMA_CTL);
+		btv->dma_on = 1;
+	} else {
+		if (!btv->dma_on)
+			return;
+                btand(~3, BT848_GPIO_DMA_CTL);
+		btv->dma_on = 0;
+	}
+	return;
+}
+
+int
+bttv_risc_init_main(struct bttv *btv)
+{
+	int rc;
+	
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0)
+		return rc;
+	dprintk(KERN_DEBUG "bttv%d: risc main @ %08Lx\n",
+		btv->c.nr,(unsigned long long)btv->main.dma);
+
+	btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+				       BT848_FIFO_STATUS_VRE);
+	btv->main.cpu[1] = cpu_to_le32(0);
+	btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2));
+
+	/* top field */
+	btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2));
+	btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[7] = cpu_to_le32(btv->main.dma + (8<<2));
+
+        btv->main.cpu[8] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+				       BT848_FIFO_STATUS_VRO);
+        btv->main.cpu[9] = cpu_to_le32(0);
+
+	/* bottom field */
+        btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2));
+        btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP);
+	btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2));
+
+	/* jump back to top field */
+	btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP);
+        btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2));
+
+	return 0;
+}
+
+int
+bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+	       int irqflags)
+{
+	unsigned long cmd;
+	unsigned long next = btv->main.dma + ((slot+2) << 2);
+
+	if (NULL == risc) {
+		d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=NULL\n",
+			 btv->c.nr,risc,slot);
+		btv->main.cpu[slot+1] = cpu_to_le32(next);
+	} else {
+		d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=%08Lx irq=%d\n",
+			 btv->c.nr,risc,slot,(unsigned long long)risc->dma,irqflags);
+		cmd = BT848_RISC_JUMP;
+		if (irqflags) {
+			cmd |= BT848_RISC_IRQ;
+			cmd |= (irqflags  & 0x0f) << 16;
+			cmd |= (~irqflags & 0x0f) << 20;
+		}
+		risc->jmp[0] = cpu_to_le32(cmd);
+		risc->jmp[1] = cpu_to_le32(next);
+		btv->main.cpu[slot+1] = cpu_to_le32(risc->dma);
+	}
+	return 0;
+}
+
+void
+bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf)
+{
+	if (in_interrupt())
+		BUG();
+	videobuf_waiton(&buf->vb,0,0);
+	videobuf_dma_pci_unmap(btv->c.pci, &buf->vb.dma);
+	videobuf_dma_free(&buf->vb.dma);
+	btcx_riscmem_free(btv->c.pci,&buf->bottom);
+	btcx_riscmem_free(btv->c.pci,&buf->top);
+	buf->vb.state = STATE_NEEDS_INIT;
+}
+
+int
+bttv_buffer_activate_vbi(struct bttv *btv,
+			 struct bttv_buffer *vbi)
+{
+	/* vbi capture */
+	if (vbi) {
+		vbi->vb.state = STATE_ACTIVE;
+		list_del(&vbi->vb.queue);
+		bttv_risc_hook(btv, RISC_SLOT_O_VBI, &vbi->top,    0);
+		bttv_risc_hook(btv, RISC_SLOT_E_VBI, &vbi->bottom, 4);
+	} else {
+		bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0);
+		bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0);
+	}
+	return 0;
+}
+
+int
+bttv_buffer_activate_video(struct bttv *btv,
+			   struct bttv_buffer_set *set)
+{
+	/* video capture */
+	if (NULL != set->top  &&  NULL != set->bottom) {
+		if (set->top == set->bottom) {
+			set->top->vb.state    = STATE_ACTIVE;
+			if (set->top->vb.queue.next)
+				list_del(&set->top->vb.queue);
+		} else {
+			set->top->vb.state    = STATE_ACTIVE;
+			set->bottom->vb.state = STATE_ACTIVE;
+			if (set->top->vb.queue.next)
+				list_del(&set->top->vb.queue);
+			if (set->bottom->vb.queue.next)
+				list_del(&set->bottom->vb.queue);
+		}
+		bttv_apply_geo(btv, &set->top->geo, 1);
+		bttv_apply_geo(btv, &set->bottom->geo,0);
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top, set->topirq);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom, 0);
+		btaor((set->top->btformat & 0xf0) | (set->bottom->btformat & 0x0f),
+		      ~0xff, BT848_COLOR_FMT);
+		btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05),
+		      ~0x0f, BT848_COLOR_CTL);
+	} else if (NULL != set->top) {
+		set->top->vb.state  = STATE_ACTIVE;
+		if (set->top->vb.queue.next)
+			list_del(&set->top->vb.queue);
+		bttv_apply_geo(btv, &set->top->geo,1);
+		bttv_apply_geo(btv, &set->top->geo,0);
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top, 0);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL,           0);
+		btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+		btaor(set->top->btswap & 0x0f,   ~0x0f, BT848_COLOR_CTL);
+	} else if (NULL != set->bottom) {
+		set->bottom->vb.state = STATE_ACTIVE;
+		if (set->bottom->vb.queue.next)
+			list_del(&set->bottom->vb.queue);
+		bttv_apply_geo(btv, &set->bottom->geo,1);
+		bttv_apply_geo(btv, &set->bottom->geo,0);
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL,                 0);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom, 0);
+		btaor(set->bottom->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+		btaor(set->bottom->btswap & 0x0f,   ~0x0f, BT848_COLOR_CTL);
+	} else {
+		bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+		bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+	}
+	return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
+{
+	const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm;
+
+	dprintk(KERN_DEBUG
+		"bttv%d: buffer field: %s  format: %s  size: %dx%d\n",
+		btv->c.nr, v4l2_field_names[buf->vb.field],
+		buf->fmt->name, buf->vb.width, buf->vb.height);
+
+	/* packed pixel modes */
+	if (buf->fmt->flags & FORMAT_FLAGS_PACKED) {
+		int bpl = (buf->fmt->depth >> 3) * buf->vb.width;
+		int bpf = bpl * (buf->vb.height >> 1);
+
+		bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height,
+			      V4L2_FIELD_HAS_BOTH(buf->vb.field),buf->tvnorm);
+		
+		switch (buf->vb.field) {
+		case V4L2_FIELD_TOP:
+			bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist,
+					 0,bpl,0,buf->vb.height);
+			break;
+		case V4L2_FIELD_BOTTOM:
+			bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist,
+					 0,bpl,0,buf->vb.height);
+			break;
+		case V4L2_FIELD_INTERLACED:
+			bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist,
+					 0,bpl,bpl,buf->vb.height >> 1);
+			bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist,
+					 bpl,bpl,bpl,buf->vb.height >> 1);
+			break;
+		case V4L2_FIELD_SEQ_TB:
+			bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist,
+					 0,bpl,0,buf->vb.height >> 1);
+			bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist,
+					 bpf,bpl,0,buf->vb.height >> 1);
+			break;
+		default:
+			BUG();
+		}
+	}
+
+	/* planar modes */
+	if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) {
+		int uoffset, voffset;
+		int ypadding, cpadding, lines;
+
+		/* calculate chroma offsets */
+		uoffset = buf->vb.width * buf->vb.height;
+		voffset = buf->vb.width * buf->vb.height;
+		if (buf->fmt->flags & FORMAT_FLAGS_CrCb) {
+			/* Y-Cr-Cb plane order */
+			uoffset >>= buf->fmt->hshift;
+			uoffset >>= buf->fmt->vshift;
+			uoffset  += voffset;
+		} else {
+			/* Y-Cb-Cr plane order */
+			voffset >>= buf->fmt->hshift;
+			voffset >>= buf->fmt->vshift;
+			voffset  += uoffset;
+		}
+
+		switch (buf->vb.field) {
+		case V4L2_FIELD_TOP:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,0,buf->tvnorm);
+			bttv_risc_planar(btv, &buf->top, buf->vb.dma.sglist,
+					 0,buf->vb.width,0,buf->vb.height,
+					 uoffset,voffset,buf->fmt->hshift,
+					 buf->fmt->vshift,0);
+			break;
+		case V4L2_FIELD_BOTTOM:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,0,buf->tvnorm);
+			bttv_risc_planar(btv, &buf->bottom, buf->vb.dma.sglist,
+					 0,buf->vb.width,0,buf->vb.height,
+					 uoffset,voffset,buf->fmt->hshift,
+					 buf->fmt->vshift,0);
+			break;
+		case V4L2_FIELD_INTERLACED:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,1,buf->tvnorm);
+			lines    = buf->vb.height >> 1;
+			ypadding = buf->vb.width;
+			cpadding = buf->vb.width >> buf->fmt->hshift;
+			bttv_risc_planar(btv,&buf->top,
+					 buf->vb.dma.sglist,
+					 0,buf->vb.width,ypadding,lines,
+					 uoffset,voffset,
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 cpadding);
+			bttv_risc_planar(btv,&buf->bottom,
+					 buf->vb.dma.sglist,
+					 ypadding,buf->vb.width,ypadding,lines,
+					 uoffset+cpadding,
+					 voffset+cpadding,
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 cpadding);
+			break;
+		case V4L2_FIELD_SEQ_TB:
+			bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+				      buf->vb.height,1,buf->tvnorm);
+			lines    = buf->vb.height >> 1;
+			ypadding = buf->vb.width;
+			cpadding = buf->vb.width >> buf->fmt->hshift;
+			bttv_risc_planar(btv,&buf->top,
+					 buf->vb.dma.sglist,
+					 0,buf->vb.width,0,lines,
+					 uoffset >> 1,
+					 voffset >> 1,
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 0);
+			bttv_risc_planar(btv,&buf->bottom,
+					 buf->vb.dma.sglist,
+					 lines * ypadding,buf->vb.width,0,lines,
+					 lines * ypadding + (uoffset >> 1),
+					 lines * ypadding + (voffset >> 1),
+					 buf->fmt->hshift,
+					 buf->fmt->vshift,
+					 0);
+			break;
+		default:
+			BUG();
+		}
+	}
+
+	/* raw data */
+	if (buf->fmt->flags & FORMAT_FLAGS_RAW) {
+		/* build risc code */
+		buf->vb.field = V4L2_FIELD_SEQ_TB;
+		bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight,
+			      1,buf->tvnorm);
+		bttv_risc_packed(btv, &buf->top,  buf->vb.dma.sglist,
+				 0, RAW_BPL, 0, RAW_LINES);
+		bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist,
+				 buf->vb.size/2 , RAW_BPL, 0, RAW_LINES);
+	}
+
+	/* copy format info */
+	buf->btformat = buf->fmt->btformat;
+	buf->btswap   = buf->fmt->btswap;
+	return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_overlay_risc(struct bttv *btv,
+		  struct bttv_overlay *ov,
+		  const struct bttv_format *fmt,
+		  struct bttv_buffer *buf)
+{
+	/* check interleave, bottom+top fields */
+	dprintk(KERN_DEBUG
+		"bttv%d: overlay fields: %s format: %s  size: %dx%d\n",
+		btv->c.nr, v4l2_field_names[buf->vb.field],
+		fmt->name,ov->w.width,ov->w.height);
+
+	/* calculate geometry */
+	bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height,
+		      V4L2_FIELD_HAS_BOTH(ov->field), ov->tvnorm);
+
+	/* build risc code */
+	switch (ov->field) {
+	case V4L2_FIELD_TOP:
+		bttv_risc_overlay(btv, &buf->top,    fmt, ov, 0, 0);
+		break;
+	case V4L2_FIELD_BOTTOM:
+		bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0);
+		break;
+	case V4L2_FIELD_INTERLACED:
+#if 0
+		bttv_risc_overlay(btv, &buf->top,    fmt, ov, 1, 0);
+		bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 1);
+#else
+		bttv_risc_overlay(btv, &buf->top,    fmt, ov, 0, 1);
+		bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0);
+#endif
+		break;
+	default:
+		BUG();
+	}
+
+	/* copy format info */
+	buf->btformat = fmt->btformat;
+	buf->btswap   = fmt->btswap;
+	buf->vb.field = ov->field;
+	return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -up linux-2.4.26/drivers/media/video/bttv-vbi.c linux/drivers/media/video/bttv-vbi.c
--- linux-2.4.26/drivers/media/video/bttv-vbi.c	2004-04-21 14:11:41.000000000 +0200
+++ linux/drivers/media/video/bttv-vbi.c	2004-04-21 14:11:41.000000000 +0200
@@ -0,0 +1,231 @@
+/*
+    bttv - Bt848 frame grabber driver
+    vbi interface
+    
+    (c) 2002 Gerd Knorr <kraxel@bytesex.org>
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 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/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include <asm/io.h>
+#include "bttvp.h"
+
+#define VBI_DEFLINES 16
+#define VBI_MAXLINES 32
+
+static unsigned int vbibufs = 4;
+static unsigned int vbi_debug = 0;
+
+MODULE_PARM(vbibufs,"i");
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32, default 4");
+MODULE_PARM(vbi_debug,"i");
+MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
+
+#ifdef dprintk
+# undef dprintk
+#endif
+#define dprintk(fmt, arg...)	if (vbi_debug) \
+	printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg)
+
+/* ----------------------------------------------------------------------- */
+/* vbi risc code + mm                                                      */
+
+static int
+vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf, int lines)
+{
+	int bpl = 2048;
+
+	bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist,
+			 0, bpl-4, 4, lines);
+	bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist,
+			 lines * bpl, bpl-4, 4, lines);
+	return 0;
+}
+
+static int vbi_buffer_setup(struct file *file,
+			    unsigned int *count, unsigned int *size)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+
+	if (0 == *count)
+		*count = vbibufs;
+	*size = fh->lines * 2 * 2048;
+	dprintk("setup: lines=%d\n",fh->lines);
+	return 0;
+}
+
+static int vbi_buffer_prepare(struct file *file, struct videobuf_buffer *vb,
+			      enum v4l2_field field)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+	struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+	int rc;
+	
+	buf->vb.size = fh->lines * 2 * 2048;
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+		return -EINVAL;
+
+	if (STATE_NEEDS_INIT == buf->vb.state) {
+		if (0 != (rc = videobuf_iolock(btv->c.pci, &buf->vb, NULL)))
+			goto fail;
+		if (0 != (rc = vbi_buffer_risc(btv,buf,fh->lines)))
+			goto fail;
+	}
+	buf->vb.state = STATE_PREPARED;
+	buf->vb.field = field;
+	dprintk("buf prepare %p: top=%p bottom=%p field=%s\n",
+		vb, &buf->top, &buf->bottom,
+		v4l2_field_names[buf->vb.field]);
+	return 0;
+
+ fail:
+	bttv_dma_free(btv,buf);
+	return rc;
+}
+
+static void
+vbi_buffer_queue(struct file *file, struct videobuf_buffer *vb)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+	struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+	
+	dprintk("queue %p\n",vb);
+	buf->vb.state = STATE_QUEUED;
+	list_add_tail(&buf->vb.queue,&btv->vcapture);
+	if (NULL == btv->cvbi) {
+		fh->btv->curr.irqflags |= 4;
+		bttv_set_dma(btv,0x0c,fh->btv->curr.irqflags);
+	}
+}
+
+static void vbi_buffer_release(struct file *file, struct videobuf_buffer *vb)
+{
+	struct bttv_fh *fh = file->private_data;
+	struct bttv *btv = fh->btv;
+	struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+	
+	dprintk("free %p\n",vb);
+	bttv_dma_free(fh->btv,buf);
+}
+
+struct videobuf_queue_ops bttv_vbi_qops = {
+	.buf_setup    = vbi_buffer_setup,
+	.buf_prepare  = vbi_buffer_prepare,
+	.buf_queue    = vbi_buffer_queue,
+	.buf_release  = vbi_buffer_release,
+};
+
+/* ----------------------------------------------------------------------- */
+
+void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines)
+{
+	int vdelay;
+
+	if (lines < 1)
+		lines = 1;
+	if (lines > VBI_MAXLINES)
+		lines = VBI_MAXLINES;
+	fh->lines = lines;
+
+	vdelay = btread(BT848_E_VDELAY_LO);
+	if (vdelay < lines*2) {
+		vdelay = lines*2;
+		btwrite(vdelay,BT848_E_VDELAY_LO);
+		btwrite(vdelay,BT848_O_VDELAY_LO);
+	}
+}
+
+void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+{
+	const struct bttv_tvnorm *tvnorm;
+	u32 start0,start1;
+	s32 count0,count1,count;
+
+	tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
+	f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+	f->fmt.vbi.sampling_rate    = tvnorm->Fsc;
+	f->fmt.vbi.samples_per_line = 2048;
+	f->fmt.vbi.sample_format    = V4L2_PIX_FMT_GREY;
+	f->fmt.vbi.offset           = 244;
+	f->fmt.vbi.flags            = 0;
+	switch (fh->btv->tvnorm) {
+	case 1: /* NTSC */
+		start0 = 10;
+		start1 = 273;
+		break;
+	case 0: /* PAL */
+	case 2: /* SECAM */
+	default:
+		start0 = 7;
+		start1 = 319;
+	}
+
+	count0 = (f->fmt.vbi.start[0] + f->fmt.vbi.count[0]) - start0;
+	count1 = (f->fmt.vbi.start[1] + f->fmt.vbi.count[1]) - start1;
+	count  = max(count0,count1);
+	if (count > VBI_MAXLINES)
+		count = VBI_MAXLINES;
+	if (count < 1)
+		count = 1;
+
+	f->fmt.vbi.start[0] = start0;
+	f->fmt.vbi.start[1] = start1;
+	f->fmt.vbi.count[0] = count;
+	f->fmt.vbi.count[1] = count;
+}
+
+void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f)
+{
+	const struct bttv_tvnorm *tvnorm;
+
+	tvnorm = &bttv_tvnorms[fh->btv->tvnorm];
+	memset(f,0,sizeof(*f));
+	f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+	f->fmt.vbi.sampling_rate    = tvnorm->Fsc;
+	f->fmt.vbi.samples_per_line = 2048;
+	f->fmt.vbi.sample_format    = V4L2_PIX_FMT_GREY;
+	f->fmt.vbi.offset           = 244;
+	f->fmt.vbi.count[0]         = fh->lines;
+	f->fmt.vbi.count[1]         = fh->lines;
+	f->fmt.vbi.flags            = 0;
+	switch (fh->btv->tvnorm) {
+	case 1: /* NTSC */
+		f->fmt.vbi.start[0] = 10;
+		f->fmt.vbi.start[1] = 273;
+		break;
+	case 0: /* PAL */
+	case 2: /* SECAM */
+	default:
+		f->fmt.vbi.start[0] = 7;
+		f->fmt.vbi.start[1] = 319;
+	}
+}
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -up linux-2.4.26/drivers/media/video/bttv.h linux/drivers/media/video/bttv.h
--- linux-2.4.26/drivers/media/video/bttv.h	2004-04-21 13:41:58.000000000 +0200
+++ linux/drivers/media/video/bttv.h	2004-04-21 14:11:41.000000000 +0200
@@ -14,6 +14,7 @@
 #define _BTTV_H_
 
 #include <linux/videodev.h>
+#include <linux/i2c.h>
 
 /* ---------------------------------------------------------- */
 /* exported by bttv-cards.c                                   */
@@ -115,6 +116,14 @@
 #define BTTV_XGUARD         0x67
 #define BTTV_NEBULA_DIGITV  0x68
 #define BTTV_PV143          0x69
+#define BTTV_IVC100         0x6e
+#define BTTV_IVC120         0x6f
+#define BTTV_PC_HDTV        0x70
+#define BTTV_TWINHAN_DST    0x71
+#define BTTV_WINFASTVC100   0x72
+#define BTTV_SIMUS_GVC1100  0x74
+#define BTTV_NGSTV_PLUS     0x75
+#define BTTV_LMLBT4         0x76
 
 /* i2c address list */
 #define I2C_TSA5522        0xc2
@@ -148,6 +157,18 @@
 #define DIGITAL_MODE_VIDEO 1
 #define DIGITAL_MODE_CAMERA 2
 
+struct bttv_core {
+	/* device structs */
+	struct pci_dev       *pci;
+	struct i2c_adapter   i2c_adap;
+	struct list_head     subs;     /* struct bttv_sub_device */
+
+	/* device config */
+        unsigned int         nr;       /* dev nr (for printk("bttv%d: ...");  */
+	unsigned int         type;     /* card type (pointer into tvcards[])  */
+	char                 name[8];  /* dev name */
+};
+
 struct bttv;
 
 struct tvcard
@@ -170,6 +191,12 @@ struct tvcard
 	unsigned int needs_tvaudio:1;
 	unsigned int msp34xx_alt:1;
 
+	/* flag: video pci function is unused */
+	unsigned int no_video:1;
+	unsigned int has_dvb:1;
+	unsigned int has_remote:1;
+	unsigned int no_gpioirq:1;
+
 	/* other settings */
 	unsigned int pll;
 #define PLL_NONE 0
@@ -203,7 +230,9 @@ extern int bttv_handle_chipset(struct bt
 
 /* ---------------------------------------------------------- */
 /* exported by bttv-if.c                                      */
-/* interface for gpio access by other modules                 */
+
+/* this obsolete -- please use the sysfs-based
+   interface below for new code */
 
 /* returns card type + card ID (for bt878-based ones)
    for possible values see lines below beginning with #define BTTV_UNKNOWN
@@ -250,8 +279,23 @@ extern wait_queue_head_t* bttv_get_gpio_
 */
 extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg);
 
+/* with 2.6.x not needed thanks to the driver model + sysfs, see below */
+extern struct i2c_adapter *bttv_get_i2c_adap(unsigned int card);
+
+
+/* ---------------------------------------------------------- */
+/* sysfs/driver-moded based gpio access interface             */
+
+
+#define gpio_inout(mask,bits)  btaor((mask)&(bits),~(mask),BT848_GPIO_OUT_EN)
+#define gpio_read()            btread(BT848_GPIO_DATA)
+#define gpio_write(value)      btwrite((value),BT848_GPIO_DATA)
+#define gpio_bits(mask,bits)   btaor((mask)&(bits),~(mask),BT848_GPIO_DATA)
+
+
+/* ---------------------------------------------------------- */
+/* i2c                                                        */
 
-/* i2c */
 extern void bttv_bit_setscl(void *data, int state);
 extern void bttv_bit_setsda(void *data, int state);
 extern void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg);
--- linux-2.4.34/drivers/media/video/bttvp.h.orig	2006-12-23 21:34:20.000000000 +0100
+++ linux-2.4.34/drivers/media/video/bttvp.h	2007-02-19 11:51:35.448134000 +0100
@@ -1,11 +1,10 @@
 /*
     bttv - Bt848 frame grabber driver
 
-    bttv's *private* header file  --  nobody else than bttv itself
+    bttv's *private* header file  --  nobody other than bttv itself
     should ever include this file.
 
-    Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
-    (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+    (c) 2000-2002 Gerd Knorr <kraxel@bytesex.org>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -26,131 +25,332 @@
 #define _BTTVP_H_
 
 #include <linux/version.h>
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,108)
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,15)
 
 #include <linux/types.h>
 #include <linux/wait.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
+#include <linux/videodev.h>
+#include <linux/pci.h>
+#include <linux/input.h>
+#include <asm/scatterlist.h>
+#include <asm/io.h>
 
-#include "bt848.h"
-#include "bttv.h"
+#include "video-buf.h"
 #include "audiochip.h"
 #include "tuner.h"
 #include "i2c-compat.h"
+#include "ir-common.h"
+#define strlcpy(dest,src,len) strncpy(dest,src,(len)-1)
+#define iminor(inode) minor(inode->i_rdev)
+
+#include "bt848.h"
+#include "bttv.h"
+#include "btcx-risc.h"
 
 #ifdef __KERNEL__
 
+#define FORMAT_FLAGS_DITHER       0x01
+#define FORMAT_FLAGS_PACKED       0x02
+#define FORMAT_FLAGS_PLANAR       0x04
+#define FORMAT_FLAGS_RAW          0x08
+#define FORMAT_FLAGS_CrCb         0x10
+
+#define RISC_SLOT_O_VBI        4
+#define RISC_SLOT_O_FIELD      6
+#define RISC_SLOT_E_VBI       10
+#define RISC_SLOT_E_FIELD     12
+#define RISC_SLOT_LOOP        14
+
+#define RESOURCE_OVERLAY       1
+#define RESOURCE_VIDEO         2
+#define RESOURCE_VBI           4
+
+#define RAW_LINES            640
+#define RAW_BPL             1024
+
+#define UNSET (-1U)
+
+/* ---------------------------------------------------------- */
+
+struct bttv_tvnorm {
+	int   v4l2_id;
+	char  *name;
+        u32   Fsc;
+        u16   swidth, sheight; /* scaled standard width, height */
+	u16   totalwidth;
+	u8    adelay, bdelay, iform;
+	u32   scaledtwidth;
+	u16   hdelayx1, hactivex1;
+	u16   vdelay;
+        u8    vbipack;
+	u16   vtotal;
+	int   sram;
+};
+extern const struct bttv_tvnorm bttv_tvnorms[];
+extern const unsigned int BTTV_TVNORMS;
+
+struct bttv_format {
+	char *name;
+	int  palette;         /* video4linux 1      */
+	int  fourcc;          /* video4linux 2      */
+	int  btformat;        /* BT848_COLOR_FMT_*  */
+	int  btswap;          /* BT848_COLOR_CTL_*  */
+	int  depth;           /* bit/pixel          */
+	int  flags;
+	int  hshift,vshift;   /* for planar modes   */
+};
+extern const struct bttv_format bttv_formats[];
+extern const unsigned int BTTV_FORMATS;
+
+/* ---------------------------------------------------------- */
+
+struct bttv_geometry {
+	u8  vtc,crop,comb;
+	u16 width,hscale,hdelay;
+	u16 sheight,vscale,vdelay,vtotal;
+};
+
+struct bttv_buffer {
+	/* common v4l buffer stuff -- must be first */
+	struct videobuf_buffer     vb;
+
+	/* bttv specific */
+	const struct bttv_format   *fmt;
+	int                        tvnorm;
+	int                        btformat;
+	int                        btswap;
+	struct bttv_geometry       geo;
+	struct btcx_riscmem        top;
+	struct btcx_riscmem        bottom;
+};
+
+struct bttv_buffer_set {
+	struct bttv_buffer     *top;       /* top field buffer    */
+	struct bttv_buffer     *bottom;    /* bottom field buffer */
+	unsigned int           irqflags;
+	unsigned int           topirq;
+};
+
+struct bttv_overlay {
+	int                    tvnorm;
+	struct v4l2_rect       w;
+	enum v4l2_field        field;
+	struct v4l2_clip       *clips;
+	int                    nclips;
+	int                    setup_ok;
+};
+
+struct bttv_fh {
+	struct bttv              *btv;
+	int resources;
+#ifdef VIDIOC_G_PRIORITY 
+	enum v4l2_priority       prio;
+#endif
+	enum v4l2_buf_type       type;
+
+	/* video capture */
+	struct videobuf_queue    cap;
+	const struct bttv_format *fmt;
+	int                      width;
+	int                      height;
+
+	/* current settings */
+	const struct bttv_format *ovfmt;
+	struct bttv_overlay      ov;
+
+	/* video overlay */
+	struct videobuf_queue    vbi;
+	int                      lines;
+};
+
+/* ---------------------------------------------------------- */
+/* bttv-risc.c                                                */
+
+/* risc code generators - capture */
+int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc,
+		     struct scatterlist *sglist,
+		     unsigned int offset, unsigned int bpl,
+		     unsigned int pitch, unsigned int lines);
+int bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc,
+		     struct scatterlist *sglist,
+		     unsigned int yoffset,  unsigned int ybpl,
+		     unsigned int ypadding, unsigned int ylines,
+		     unsigned int uoffset,  unsigned int voffset,
+		     unsigned int hshift,   unsigned int vshift,
+		     unsigned int cpadding);
+int bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc,
+		      const struct bttv_format *fmt,
+		      struct bttv_overlay *ov,
+		      int skip_top, int skip_bottom);
+
+/* calculate / apply geometry settings */
+void bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo,
+		   int width, int height, int interleaved, int norm);
+void bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int top);
+
+/* control dma register + risc main loop */
+void bttv_set_dma(struct bttv *btv, int override, int irqflags);
+int bttv_risc_init_main(struct bttv *btv);
+int bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc,
+		   int irqflags);
+
+/* capture buffer handling */
+int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf);
+int bttv_buffer_activate_video(struct bttv *btv,
+			       struct bttv_buffer_set *set);
+int bttv_buffer_activate_vbi(struct bttv *btv,
+			     struct bttv_buffer *vbi);
+void bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf);
+
+/* overlay handling */
+int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
+		      const struct bttv_format *fmt,
+		      struct bttv_buffer *buf);
+
+
+/* ---------------------------------------------------------- */
+/* bttv-vbi.c                                                 */
+
+void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f);
+void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f);
+void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines);
+
+extern struct videobuf_queue_ops bttv_vbi_qops;
+
+/* ---------------------------------------------------------- */
+/* bttv-gpio.c */
+
+
 /* ---------------------------------------------------------- */
 /* bttv-driver.c                                              */
 
-/* insmod options / kernel args */
-extern unsigned int no_overlay;
+/* insmod options */
 extern unsigned int bttv_verbose;
 extern unsigned int bttv_debug;
 extern unsigned int bttv_gpio;
 extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
 extern int init_bttv_i2c(struct bttv *btv);
+extern int fini_bttv_i2c(struct bttv *btv);
 extern int pvr_boot(struct bttv *btv);
 
-#define dprintk		if (bttv_debug)   printk
-#define vprintk		if (bttv_verbose) printk
+extern int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg);
+extern void bttv_reinit_bt848(struct bttv *btv);
+extern void bttv_field_count(struct bttv *btv);
+
+#define vprintk  if (bttv_verbose) printk
+#define dprintk  if (bttv_debug >= 1) printk
+#define d2printk if (bttv_debug >= 2) printk
 
+/* our devices */
 #define BTTV_MAX 16
-extern unsigned int bttv_num;			/* number of Bt848s in use */
-
-#define UNSET -1U
+extern unsigned int bttv_num;
 
-#ifdef VIDEODAT_HACK
-# define VBI_MAXLINES   19
-#else
-# define VBI_MAXLINES   16
-#endif
+#define BTTV_MAX_FBUF   0x208000
 #define VBIBUF_SIZE     (2048*VBI_MAXLINES*2)
-#define MAX_GBUFFERS	64
-#define RISCMEM_LEN	(32744*2)
+#define BTTV_TIMEOUT    (HZ/2) /* 0.5 seconds */
+#define BTTV_FREE_IDLE  (HZ)   /* one second */
 
-#define BTTV_MAX_FBUF	0x208000
-
-struct bttv_window 
-{
-	int x, y;
-	ushort width, height;
-	ushort bpp, bpl;
-	ushort swidth, sheight;
-	unsigned long vidadr;
-	ushort freq;
-	int norm;
-	int interlace;
-	int color_fmt;
-	ushort depth;
-};
 
 struct bttv_pll_info {
-	unsigned int pll_ifreq;	   /* PLL input frequency 	 */
-	unsigned int pll_ofreq;	   /* PLL output frequency       */
+	unsigned int pll_ifreq;    /* PLL input frequency        */
+	unsigned int pll_ofreq;    /* PLL output frequency       */
 	unsigned int pll_crystal;  /* Crystal used for input     */
 	unsigned int pll_current;  /* Currently programmed ofreq */
 };
 
-struct bttv_gbuf {
-	int stat;
-#define GBUFFER_UNUSED       0
-#define GBUFFER_GRABBING     1
-#define GBUFFER_DONE         2
-#define GBUFFER_ERROR        3
-	struct timeval tv;
-	
-	u16 width;
-	u16 height;
-	u16 fmt;
-	
-	u32 *risc;
-	unsigned long ro;
-	unsigned long re;
+/* for gpio-connected remote control */
+struct bttv_input {
+	struct input_dev      dev;
+	struct ir_input_state ir;
+	char                  name[32];
+	char                  phys[32];
+	u32                   mask_keycode;
+	u32                   mask_keydown;
+};
+
+struct bttv_suspend_state {
+	u32  pci_cfg[64 / sizeof(u32)];
+	u32  gpio_enable;
+	u32  gpio_data;
+	int  disabled;
+	struct bttv_buffer_set video;
+	struct bttv_buffer     *vbi;
 };
 
 struct bttv {
-	struct video_device video_dev;
-	struct video_device radio_dev;
-	struct video_device vbi_dev;
-	struct video_picture picture;		/* Current picture params */
-	struct video_audio audio_dev;		/* Current audio params */
+	struct bttv_core c;
 
-	spinlock_t s_lock;
-        struct semaphore lock;
-	unsigned int user;
+	/* pci device config */
+	unsigned short id;
+	unsigned char revision;
+	unsigned char *bt848_mmio;   /* pointer to mmio */
 
-	/* i2c */
-	struct i2c_adapter         i2c_adap;
+	/* card configuration info */
+	unsigned int cardid;   /* pci subsystem id (bt878 based ones) */
+        unsigned int tuner_type;  /* tuner chip type */
+        unsigned int pinnacle_id;
+	unsigned int svhs;
+	struct bttv_pll_info pll;
+	int triton1;
+	int gpioirq;
+	int use_i2c_hw;
+
+	/* old gpio interface */
+	wait_queue_head_t gpioq;
+	int shutdown;
+	void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
+
+	/* new gpio interface */
+	spinlock_t gpio_lock;
+
+	/* i2c layer */
 	struct i2c_algo_bit_data   i2c_algo;
 	struct i2c_client          i2c_client;
 	int                        i2c_state, i2c_rc;
+	int                        i2c_done;
+	wait_queue_head_t          i2c_queue;
 
-        unsigned int tuner_type;
-        unsigned int pinnacle_id;
-        unsigned int channel;
-	unsigned int svhs;
-        
-        unsigned int nr;
-	unsigned short id;
-	struct pci_dev *dev;
-	unsigned char revision;
-	unsigned long bt848_adr;      /* bus address of IO mem returned by PCI BIOS */
-	unsigned char *bt848_mem;   /* pointer to mapped IO memory */
-	unsigned long busriscmem; 
-	u32 *riscmem;
-  
-	unsigned char *vbibuf;
-	struct bttv_window win;
-	int fb_color_ctl;
-	int type;            /* card type  */
-	unsigned int cardid;
-	int audio;           /* audio mode */
-	int audio_chip;      /* set to one of the chips supported by bttv.c */
-	int radio;
-	int has_radio;
+	/* video4linux (1) */
+	struct video_device *video_dev;
+	struct video_device *radio_dev;
+	struct video_device *vbi_dev;
+
+	/* infrared remote */
 	int has_remote;
+	struct bttv_input *remote;
+
+	/* locking */
+	spinlock_t s_lock;
+        struct semaphore lock;
+	int resources;
+        struct semaphore reslock;
+#ifdef VIDIOC_G_PRIORITY 
+	struct v4l2_prio_state prio;
+#endif
+	
+	/* video state */
+	unsigned int input;
+	unsigned int audio;
+	unsigned long freq;
+	int tvnorm,hue,contrast,bright,saturation;
+	struct v4l2_framebuffer fbuf;
+	unsigned int field_count;
+
+	/* various options */
+	int opt_combfilter;
+	int opt_lumafilter;
+	int opt_automute;
+	int opt_chroma_agc;
+	int opt_adc_crush;
+	int opt_vcr_hack;
+	int opt_whitecrush_upper;
+	int opt_whitecrush_lower;
+
+	/* radio data/state */
+	int has_radio;
+	int radio_user;
 
 	/* miro/pinnacle + Aimslab VHX
 	   philips matchbox (tea5757 radio tuner) support */
@@ -166,70 +366,46 @@
 	int mbox_iow;
 	int mbox_csel;
 
-	u32 *risc_jmp;
-	u32 *vbi_odd;
-	u32 *vbi_even;
-	u32 bus_vbi_even;
-	u32 bus_vbi_odd;
-        wait_queue_head_t vbiq;
-	wait_queue_head_t capq;
-	unsigned int vbip;
-
-	u32 *risc_scr_odd;
-	u32 *risc_scr_even;
-	u32 risc_cap_odd;
-	u32 risc_cap_even;
-	int scr_on;
-	int vbi_on;
-	struct video_clip *cliprecs;
-
-	struct bttv_gbuf *gbuf;
-	int gqueue[MAX_GBUFFERS];
-	int gq_in,gq_out,gq_grab,gq_start;
-        char *fbuffer;
+	/* risc memory management data
+	   - must aquire s_lock before changing these
+	   - only the irq handler is supported to touch top + bottom + vcurr */
+	struct btcx_riscmem     main;
+	struct bttv_buffer      *screen;    /* overlay             */
+	struct list_head        capture;    /* video capture queue */
+	struct list_head        vcapture;   /* vbi capture queue   */
+	struct bttv_buffer_set  curr;       /* active buffers      */
+	struct bttv_buffer      *cvbi;      /* active vbi buffer   */
+	int                     new_input;
+
+	unsigned long cap_ctl;
+	unsigned long dma_on;
+	struct timer_list timeout;
+	struct bttv_suspend_state state;
+
+	/* stats */
+	unsigned int errors;
+	unsigned int framedrop;
+	unsigned int irq_total;
+	unsigned int irq_me;
 
-	struct bttv_pll_info pll;
-	unsigned int Fsc;
-	unsigned int field;
-	unsigned int last_field; /* number of last grabbed field */
-	int i2c_command;
-	int triton1;
-
-	int errors;
-	int needs_restart;
-
-	wait_queue_head_t gpioq;
-	int shutdown;
-        void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
+	unsigned int users;
+	struct bttv_fh init;
 };
 
+/* private ioctls */
+#define BTTV_VERSION            _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
+#define BTTV_VBISIZE            _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
+
 extern struct bttv bttvs[BTTV_MAX];
 #endif
 
-#define btwrite(dat,adr)    writel((dat), (char *) (btv->bt848_mem+(adr)))
-#define btread(adr)         readl(btv->bt848_mem+(adr))
+#define btwrite(dat,adr)    writel((dat), (char *) (btv->bt848_mmio+(adr)))
+#define btread(adr)         readl(btv->bt848_mmio+(adr))
 
 #define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
 #define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
 #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
 
-/* bttv ioctls */
-
-#define BTTV_READEE		_IOW('v',  BASE_VIDIOCPRIVATE+0, char [256])
-#define BTTV_WRITEE		_IOR('v',  BASE_VIDIOCPRIVATE+1, char [256])
-#define BTTV_FIELDNR		_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)
-#define BTTV_PLLSET		_IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info)
-#define BTTV_BURST_ON      	_IOR('v' , BASE_VIDIOCPRIVATE+4, int)
-#define BTTV_BURST_OFF     	_IOR('v' , BASE_VIDIOCPRIVATE+5, int)
-#define BTTV_VERSION  	        _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
-#define BTTV_PICNR		_IOR('v' , BASE_VIDIOCPRIVATE+7, int)
-#define BTTV_VBISIZE            _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
-
-#define TDA9850            0x01
-#define TDA9840            0x02
-#define TDA8425            0x03
-#define TEA6300            0x04
-
 #endif /* _BTTVP_H_ */
 
 /*