diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/Makefile 780-bttv/drivers/media/video/Makefile
--- 770-tuner/drivers/media/video/Makefile	Tue Sep  2 09:55:45 2003
+++ 780-bttv/drivers/media/video/Makefile	Fri Jan  9 23:11:34 2004
@@ -3,7 +3,7 @@
 #
 
 bttv-objs	:=	bttv-driver.o bttv-cards.o bttv-if.o \
-			bttv-risc.o bttv-vbi.o
+			bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o
 zoran-objs      :=	zr36120.o zr36120_i2c.o zr36120_mem.o
 zr36067-objs	:=	zoran_procfs.o zoran_device.o \
 			zoran_driver.o zoran_card.o
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bt848.h 780-bttv/drivers/media/video/bt848.h
--- 770-tuner/drivers/media/video/bt848.h	Mon Nov 17 18:29:29 2003
+++ 780-bttv/drivers/media/video/bt848.h	Fri Jan  9 23:11:34 2004
@@ -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)
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv-cards.c 780-bttv/drivers/media/video/bttv-cards.c
--- 770-tuner/drivers/media/video/bttv-cards.c	Mon Dec  8 09:55:51 2003
+++ 780-bttv/drivers/media/video/bttv-cards.c	Fri Jan  9 23:11:34 2004
@@ -53,6 +53,8 @@ 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 winfast2000_audio(struct bttv *btv, struct video_audio *v, int set);
@@ -64,6 +66,7 @@ static void rv605_muxsel(struct bttv *bt
 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 int terratec_active_radio_upgrade(struct bttv *btv);
 static int tea5757_read(struct bttv *btv);
@@ -77,10 +80,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
@@ -108,6 +112,10 @@ 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");
@@ -155,12 +163,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_GVBCTV3PCI,    "I-O Data Co. GV-BCTV3/PCI" },
 
 	{ 0x001211bd, BTTV_PINNACLE,      "Pinnacle PCTV" },
 	{ 0x001c11bd, BTTV_PINNACLESAT,   "Pinnacle PCTV Sat" },
@@ -175,16 +184,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)" },
@@ -253,16 +261,24 @@ 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" },
 
 	// 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" },
+	
 	{ 0, -1, NULL }
 };
 
@@ -352,6 +368,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,
@@ -438,6 +455,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,
@@ -471,7 +489,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,
@@ -733,6 +757,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,
@@ -1248,6 +1273,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
@@ -1283,6 +1309,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
@@ -1751,7 +1778,8 @@ struct tvcard bttv_tvcards[] = {
 	.no_tda7432     = 1,
 	.pll            = PLL_28,
 	.tuner_type     = -1,
-	.no_video       = 1,
+	.has_dvb        = 1,
+	.no_gpioirq     = 1,
 },{
 	/* Jorge Boncompte - DTI2 <jorge@dti2.net> */
 	.name           = "ProVideo PV143",
@@ -1850,6 +1878,62 @@ struct tvcard bttv_tvcards[] = {
 			    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,
 }};
 
 const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
@@ -1868,9 +1952,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) {
@@ -1883,14 +1967,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");
@@ -1898,13 +1982,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 ?? */
@@ -1914,20 +1998,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");
 }
@@ -1941,7 +2025,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;
@@ -1949,22 +2033,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.
@@ -1989,7 +2073,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;
@@ -2005,9 +2089,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
@@ -2031,8 +2115,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) {
@@ -2051,14 +2135,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");
@@ -2092,10 +2176,10 @@ static void miro_pinnacle_gpio(struct bt
 			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;
 	}
@@ -2106,16 +2190,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);
@@ -2127,8 +2209,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 */
@@ -2147,7 +2228,13 @@ 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]);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -2167,7 +2254,7 @@ void bttv_reset_audio(struct bttv *btv)
 		return;
 	
 	if (bttv_debug)
-		printk("bttv%d: BT878A ARESET\n",btv->nr);
+		printk("bttv%d: BT878A ARESET\n",btv->c.nr);
 	btwrite((1<<7), 0x058);
 	udelay(10);
 	btwrite(     0, 0x058);
@@ -2176,7 +2263,7 @@ void bttv_reset_audio(struct bttv *btv)
 /* 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);
@@ -2198,12 +2285,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:
@@ -2255,7 +2342,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:
@@ -2292,16 +2379,16 @@ void __devinit bttv_init_card2(struct bt
 	/* 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;
@@ -2324,27 +2411,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))
@@ -2352,31 +2445,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");
 	}
@@ -2460,11 +2553,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]);
 	}
 }
 
@@ -2474,7 +2571,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;
@@ -2492,7 +2589,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");
 }
 
@@ -2516,7 +2613,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 {
@@ -2547,36 +2644,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;
 }
 
@@ -2599,15 +2695,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;
 }
@@ -2619,15 +2715,15 @@ int __devinit pvr_boot(struct bttv *btv)
         const struct firmware *fw_entry;
 	int rc;
 
-	rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->dev->dev);
+	rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev);
 	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;
 }	
@@ -2642,7 +2738,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;
@@ -2650,7 +2746,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';
                }
@@ -2679,49 +2775,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 # */
@@ -2734,7 +2830,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);
 }	
 
 /* ----------------------------------------------------------------------- */
@@ -2779,7 +2875,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);
@@ -2805,8 +2901,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]);
 }
 
 
@@ -2821,23 +2917,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;
@@ -2851,18 +2947,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:
@@ -2885,13 +2977,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_bits(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");
 
@@ -2926,10 +3018,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);
@@ -2956,17 +3048,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);
 	}
 }
@@ -2974,17 +3065,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);
 	}
 }
@@ -2992,15 +3082,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 */
@@ -3038,12 +3127,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);
 	}
 
@@ -3060,11 +3148,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);
@@ -3076,7 +3164,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;
 }
 
@@ -3085,18 +3173,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++)
@@ -3122,7 +3209,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
 }
 
@@ -3148,7 +3235,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--) {
@@ -3156,20 +3243,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);
 }
 
 /* ----------------------------------------------------------------------- */
@@ -3182,7 +3269,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)
@@ -3191,7 +3278,7 @@ 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;
@@ -3217,12 +3304,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");
 		}
@@ -3233,13 +3320,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;
 	}
@@ -3252,7 +3359,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 {
@@ -3270,12 +3377,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 {
@@ -3300,7 +3407,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");
 		}
@@ -3340,7 +3447,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");
 		}
@@ -3376,7 +3483,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");
 		}
@@ -3405,7 +3512,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");
                 }
@@ -3437,7 +3544,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");
 		}
@@ -3474,16 +3581,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);
 }
 
@@ -3513,7 +3620,7 @@ 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]);
 }
 
 /*
@@ -3555,7 +3662,7 @@ static void ivc120_muxsel(struct bttv *b
 	int matrix = input / 4;
 	
 	dprintk("bttv%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n",
-		btv->nr, input, matrix, key);
+		btv->c.nr, input, matrix, key);
 	
 	// Handles the input selection on the TDA8540's
 	bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x00,
@@ -3641,12 +3748,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) {
@@ -3655,15 +3762,15 @@ 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;
 }
 
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv-driver.c 780-bttv/drivers/media/video/bttv-driver.c
--- 770-tuner/drivers/media/video/bttv-driver.c	Mon Nov 17 18:29:48 2003
+++ 780-bttv/drivers/media/video/bttv-driver.c	Fri Jan  9 23:11:34 2004
@@ -68,11 +68,12 @@ 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 sloppy     = 0;
 static unsigned int v4l2       = 1;
 
 
@@ -108,12 +109,15 @@ MODULE_PARM(chroma_agc,"i");
 MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
 MODULE_PARM(adc_crush,"i");
 MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+MODULE_PARM(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(sloppy,"i");
 MODULE_PARM(v4l2,"i");
 
 MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
@@ -133,7 +137,7 @@ static ssize_t show_card(struct class_de
 {
 	struct video_device *vfd = to_video_device(cd);
 	struct bttv *btv = dev_get_drvdata(vfd->dev);
-	return sprintf(buf, "%d\n", btv ? btv->type : UNSET);
+	return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
 }
 static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
 
@@ -479,7 +483,9 @@ const unsigned int BTTV_FORMATS = ARRAY_
 #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_LASTP1      (V4L2_CID_PRIVATE_BASE + 6)
+#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",
@@ -597,7 +603,24 @@ static const struct v4l2_queryctrl bttv_
 		.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,
 	}
+
 };
 const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls);
 
@@ -695,7 +718,7 @@ static void set_pll(struct bttv *btv)
                 return;
 
 	if (btv->pll.pll_ofreq == btv->pll.pll_current) {
-		dprintk("bttv%d: PLL: no change required\n",btv->nr);
+		dprintk("bttv%d: PLL: no change required\n",btv->c.nr);
                 return;
         }
 
@@ -704,22 +727,22 @@ static void set_pll(struct bttv *btv)
                 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);
+			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->nr,
+	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(".");
-		current->state = TASK_INTERRUPTIBLE;
-		schedule_timeout(HZ/10);
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ/50);
 		
                 if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
 			btwrite(0,BT848_DSTATUS);
@@ -742,9 +765,9 @@ void bt848A_set_timing(struct bttv *btv)
 	int table_idx = bttv_tvnorms[btv->tvnorm].sram;
 	int fsc       = bttv_tvnorms[btv->tvnorm].Fsc;
 
-	if (UNSET == bttv_tvcards[btv->type].muxsel[btv->input]) {
+	if (UNSET == bttv_tvcards[btv->c.type].muxsel[btv->input]) {
 		dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
-			btv->nr,table_idx);
+			btv->c.nr,table_idx);
 
 		/* timing change...reset timing generator address */
        		btwrite(0x00, BT848_TGCTRL);
@@ -828,13 +851,13 @@ video_mux(struct bttv *btv, unsigned int
 {
 	int mux,mask2;
 
-	if (input >= bttv_tvcards[btv->type].video_inputs)
+	if (input >= bttv_tvcards[btv->c.type].video_inputs)
 		return -EINVAL;
 
         /* needed by RemoteVideo MX */
-	mask2 = bttv_tvcards[btv->type].gpiomask2;
+	mask2 = bttv_tvcards[btv->c.type].gpiomask2;
 	if (mask2)
-		btaor(mask2,~mask2,BT848_GPIO_OUT_EN);
+		gpio_inout(mask2,mask2);
 
 	if (input == btv->svhs)  {
 		btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
@@ -843,14 +866,14 @@ video_mux(struct bttv *btv, unsigned int
 		btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
 		btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
 	}
-	mux = bttv_tvcards[btv->type].muxsel[input] & 3;
+	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->nr,input,mux);
+		btv->c.nr,input,mux);
 
 	/* card specific hook */
-	if(bttv_tvcards[btv->type].muxsel_hook)
-		bttv_tvcards[btv->type].muxsel_hook (btv, input);
+	if(bttv_tvcards[btv->c.type].muxsel_hook)
+		bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
 	return 0;
 }
 
@@ -863,9 +886,9 @@ static int
 audio_mux(struct bttv *btv, int mode)
 {
 	int val,mux,i2c_mux,signal;
-	
-	btaor(bttv_tvcards[btv->type].gpiomask,
-	      ~bttv_tvcards[btv->type].gpiomask,BT848_GPIO_OUT_EN);
+
+	gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+		   bttv_tvcards[btv->c.type].gpiomask);
 	signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
 
 	switch (mode) {
@@ -887,12 +910,12 @@ audio_mux(struct bttv *btv, int mode)
 		mux = AUDIO_OFF;
 #if 0
 	printk("bttv%d: amux: mode=%d audio=%d signal=%s mux=%d/%d irq=%s\n",
-	       btv->nr, mode, btv->audio, signal ? "yes" : "no",
+	       btv->c.nr, mode, btv->audio, signal ? "yes" : "no",
 	       mux, i2c_mux, in_interrupt() ? "yes" : "no");
 #endif
 
-	val = bttv_tvcards[btv->type].audiomux[mux];
-	btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
+	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())
@@ -909,7 +932,7 @@ i2c_vidiocschan(struct bttv *btv)
 	c.norm    = btv->tvnorm;
 	c.channel = btv->input;
 	bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
-	if (btv->type == BTTV_VOODOOTV_FM)
+	if (btv->c.type == BTTV_VOODOOTV_FM)
 		bttv_tda9880_setnorm(btv,c.norm);
 }
 
@@ -932,7 +955,7 @@ set_tvnorm(struct bttv *btv, unsigned in
 	btwrite(1, BT848_VBI_PACK_DEL);
 	bt848A_set_timing(btv);
 
-	switch (btv->type) {
+	switch (btv->c.type) {
 	case BTTV_VOODOOTV_FM:
 		bttv_tda9880_setnorm(btv,norm);
 		break;
@@ -963,7 +986,7 @@ set_input(struct bttv *btv, unsigned int
 	} else {
 		video_mux(btv,input);
 	}
-	audio_mux(btv,(input == bttv_tvcards[btv->type].tuner ?
+	audio_mux(btv,(input == bttv_tvcards[btv->c.type].tuner ?
 		       AUDIO_TUNER : AUDIO_EXTERN));
 	set_tvnorm(btv,btv->tvnorm);
 }
@@ -972,7 +995,14 @@ static void init_bt848(struct bttv *btv)
 {
 	int val;
 	
-	btwrite(0, BT848_SRESET);
+	if (bttv_tvcards[btv->c.type].no_video) {
+		/* very basic init only */
+		btwrite(0xfffffUL, BT848_INT_STAT);
+		btwrite(BT848_INT_I2CDONE,
+			BT848_INT_MASK);
+		return;
+	}
+
 	btwrite(0x00, BT848_CAP_CTL);
 	btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
 	btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
@@ -995,6 +1025,9 @@ static void init_bt848(struct bttv *btv)
         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);
+
 	if (btv->opt_lumafilter) {
 		btwrite(0, BT848_E_CONTROL);
 		btwrite(0, BT848_O_CONTROL);
@@ -1006,11 +1039,12 @@ static void init_bt848(struct bttv *btv)
         /* interrupt */
         btwrite(0xfffffUL, BT848_INT_STAT);
         btwrite((btv->triton1)  |
-                BT848_INT_GPINT |
+                (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_FMTCHG|BT848_INT_HLOCK|
+		BT848_INT_I2CDONE,
                 BT848_INT_MASK);
 }
 
@@ -1019,7 +1053,7 @@ extern void bttv_reinit_bt848(struct btt
 	unsigned long flags;
 
 	if (bttv_verbose)
-		printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->nr);
+		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);
@@ -1094,6 +1128,12 @@ static int get_control(struct bttv *btv,
 	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;
 	}
@@ -1182,6 +1222,14 @@ static int set_control(struct bttv *btv,
 	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;
 	}
@@ -1201,7 +1249,7 @@ void bttv_gpio_tracking(struct bttv *btv
 	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);
+	       btv->c.nr,outbits,data & outbits, data & ~outbits, comment);
 }
 
 void bttv_field_count(struct bttv *btv)
@@ -1325,7 +1373,7 @@ static int bttv_prepare_buffer(struct bt
 	/* alloc risc memory */
 	if (STATE_NEEDS_INIT == buf->vb.state) {
 		redo_dma_risc = 1;
-		if (0 != (rc = videobuf_iolock(btv->dev,&buf->vb,&btv->fbuf)))
+		if (0 != (rc = videobuf_iolock(btv->c.pci,&buf->vb,&btv->fbuf)))
 			goto fail;
 	}
 
@@ -1428,7 +1476,7 @@ int bttv_common_ioctls(struct bttv *btv,
 	{
 		struct video_tuner *v = arg;
 		
-		if (UNSET == bttv_tvcards[btv->type].tuner)
+		if (UNSET == bttv_tvcards[btv->c.type].tuner)
 			return -EINVAL;
 		if (v->tuner) /* Only tuner 0 */
 			return -EINVAL;
@@ -1462,13 +1510,13 @@ int bttv_common_ioctls(struct bttv *btv,
                 struct video_channel *v = arg;
 		unsigned int channel = v->channel;
 
-                if (channel >= bttv_tvcards[btv->type].video_inputs)
+                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->type].tuner)  {
+		if (channel == bttv_tvcards[btv->c.type].tuner)  {
                         strcpy(v->name,"Television");
                         v->flags|=VIDEO_VC_TUNER;
                         v->type=VIDEO_TYPE_TV;
@@ -1485,7 +1533,7 @@ int bttv_common_ioctls(struct bttv *btv,
                 struct video_channel *v = arg;
 		unsigned int channel = v->channel;
 
-		if (channel >= bttv_tvcards[btv->type].video_inputs)
+		if (channel >= bttv_tvcards[btv->c.type].video_inputs)
 			return -EINVAL;
 		if (v->norm >= BTTV_TVNORMS)
 			return -EINVAL;
@@ -1528,7 +1576,7 @@ int bttv_common_ioctls(struct bttv *btv,
 		struct video_audio *v = arg;
 		unsigned int audio = v->audio;
 
-		if (audio >= bttv_tvcards[btv->type].audio_inputs)
+		if (audio >= bttv_tvcards[btv->c.type].audio_inputs)
 			return -EINVAL;
 
 		down(&btv->lock);
@@ -1596,13 +1644,13 @@ int bttv_common_ioctls(struct bttv *btv,
 		unsigned int n;
 		
 		n = i->index;
-		if (n >= bttv_tvcards[btv->type].video_inputs)
+		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->type].tuner) {
+		if (i->index == bttv_tvcards[btv->c.type].tuner) {
 			sprintf(i->name, "Television");
 			i->type  = V4L2_INPUT_TYPE_TUNER;
 			i->tuner = 0;
@@ -1632,7 +1680,7 @@ int bttv_common_ioctls(struct bttv *btv,
 	{
 		unsigned int *i = arg;
 		
-		if (*i > bttv_tvcards[btv->type].video_inputs)
+		if (*i > bttv_tvcards[btv->c.type].video_inputs)
 			return -EINVAL;
 		down(&btv->lock);
 		set_input(btv,*i);
@@ -1645,7 +1693,7 @@ int bttv_common_ioctls(struct bttv *btv,
 	{
 		struct v4l2_tuner *t = arg;
 
-		if (UNSET == bttv_tvcards[btv->type].tuner)
+		if (UNSET == bttv_tvcards[btv->c.type].tuner)
 			return -EINVAL;
 		if (0 != t->index)
 			return -EINVAL;
@@ -1683,7 +1731,7 @@ int bttv_common_ioctls(struct bttv *btv,
 	{
 		struct v4l2_tuner *t = arg;
 
-		if (UNSET == bttv_tvcards[btv->type].tuner)
+		if (UNSET == bttv_tvcards[btv->c.type].tuner)
 			return -EINVAL;
 		if (0 != t->index)
 			return -EINVAL;
@@ -2061,16 +2109,16 @@ static int bttv_do_ioctl(struct inode *i
 		switch (_IOC_TYPE(cmd)) {
 		case 'v':
 			printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n",
-			       btv->nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ?
+			       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->nr, cmd,  v4l2_ioctl_names[_IOC_NR(cmd)]);
+			       btv->c.nr, cmd,  v4l2_ioctl_names[_IOC_NR(cmd)]);
 			break;
 		default:
 			printk("bttv%d: ioctl 0x%x (???)\n",
-			       btv->nr, cmd);
+			       btv->c.nr, cmd);
 		}
 	}
 	if (btv->errors)
@@ -2110,8 +2158,8 @@ static int bttv_do_ioctl(struct inode *i
 				VID_TYPE_OVERLAY|
 				VID_TYPE_CLIPPING|
 				VID_TYPE_SCALES;
-			cap->channels  = bttv_tvcards[btv->type].video_inputs;
-			cap->audios    = bttv_tvcards[btv->type].audio_inputs;
+			cap->channels  = bttv_tvcards[btv->c.type].video_inputs;
+			cap->audios    = bttv_tvcards[btv->c.type].audio_inputs;
 			cap->maxwidth  = bttv_tvnorms[btv->tvnorm].swidth;
 			cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
 			cap->minwidth  = 48;
@@ -2144,7 +2192,7 @@ static int bttv_do_ioctl(struct inode *i
 		if (NULL == fmt)
 			return -EINVAL;
 		down(&fh->cap.lock);
-		if (fmt->depth != pic->depth && !sloppy) {
+		if (fmt->depth != pic->depth) {
 			retval = -EINVAL;
 			goto fh_unlock_and_return;
 		}
@@ -2228,43 +2276,35 @@ static int bttv_do_ioctl(struct inode *i
 			fbuf->height * fbuf->bytesperline;
 		down(&fh->cap.lock);
 		retval = -EINVAL;
-		if (sloppy) {
-			/* also set the default palette -- for backward
-			   compatibility with older versions */
-			switch (fbuf->depth) {
-			case 8:
-				fmt = format_by_palette(VIDEO_PALETTE_HI240);
-				break;
-			case 16:
-				fmt = format_by_palette(VIDEO_PALETTE_RGB565);
-				break;
-			case 24:
-				fmt = format_by_palette(VIDEO_PALETTE_RGB24);
-				break;
-			case 32:
-				fmt = format_by_palette(VIDEO_PALETTE_RGB32);
-				break;
-			case 15:
-				fbuf->depth = 16;
-				fmt = format_by_palette(VIDEO_PALETTE_RGB555);
-				break;
-			default:
-				fmt = NULL;
-				break;
-			}
-			if (NULL == fmt)
-				goto fh_unlock_and_return;
-			fh->ovfmt = fmt;
-			fh->fmt   = fmt;
-			btv->init.ovfmt = fmt;
-			btv->init.fmt   = fmt;
-		} else {
-			if (15 == fbuf->depth)
-				fbuf->depth = 16;
-			if (fbuf->depth !=  8 && fbuf->depth != 16 &&
-			    fbuf->depth != 24 && fbuf->depth != 32)
-				goto fh_unlock_and_return;
+
+		switch (fbuf->depth) {
+		case 8:
+			fmt = format_by_palette(VIDEO_PALETTE_HI240);
+			break;
+		case 16:
+			fmt = format_by_palette(VIDEO_PALETTE_RGB565);
+			break;
+		case 24:
+			fmt = format_by_palette(VIDEO_PALETTE_RGB24);
+			break;
+		case 32:
+			fmt = format_by_palette(VIDEO_PALETTE_RGB32);
+			break;
+		case 15:
+			fbuf->depth = 16;
+			fmt = format_by_palette(VIDEO_PALETTE_RGB555);
+			break;
+		default:
+			fmt = NULL;
+			break;
 		}
+		if (NULL == fmt)
+			goto fh_unlock_and_return;
+
+		fh->ovfmt = fmt;
+		fh->fmt   = fmt;
+		btv->init.ovfmt = fmt;
+		btv->init.fmt   = fmt;
 		btv->fbuf.base             = fbuf->base;
 		btv->fbuf.fmt.width        = fbuf->width;
 		btv->fbuf.fmt.height       = fbuf->height;
@@ -2287,7 +2327,7 @@ static int bttv_do_ioctl(struct inode *i
 			if (NULL == btv->fbuf.base)
 				return -EINVAL;
 			if (!fh->ov.setup_ok) {
-				dprintk("bttv%d: overlay: !setup_ok\n",btv->nr);
+				dprintk("bttv%d: overlay: !setup_ok\n",btv->c.nr);
 				return -EINVAL;
 			}
 		}
@@ -2383,7 +2423,7 @@ static int bttv_do_ioctl(struct inode *i
 			retval = -EIO;
 			/* fall through */
 		case STATE_DONE:
-			videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
+			videobuf_dma_pci_sync(btv->c.pci,&buf->vb.dma);
 			bttv_dma_free(btv,buf);
 			break;
 		default:
@@ -2464,7 +2504,7 @@ static int bttv_do_ioctl(struct inode *i
 			return -EINVAL;
                 strcpy(cap->driver,"bttv");
                 strlcpy(cap->card,btv->video_dev->name,sizeof(cap->card));
-		sprintf(cap->bus_info,"PCI:%s",pci_name(btv->dev));
+		sprintf(cap->bus_info,"PCI:%s",pci_name(btv->c.pci));
 		cap->version = BTTV_VERSION_CODE;
 		cap->capabilities =
 			V4L2_CAP_VIDEO_CAPTURE |
@@ -2768,7 +2808,7 @@ static ssize_t bttv_read(struct file *fi
 	if (fh->btv->errors)
 		bttv_reinit_bt848(fh->btv);
 	dprintk("bttv%d: read count=%d type=%s\n",
-		fh->btv->nr,(int)count,v4l2_type_names[fh->type]);
+		fh->btv->c.nr,(int)count,v4l2_type_names[fh->type]);
 
 	switch (fh->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
@@ -2849,12 +2889,14 @@ static int bttv_open(struct inode *inode
 	dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
 
 	for (i = 0; i < bttv_num; i++) {
-		if (bttvs[i].video_dev->minor == minor) {
+		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->minor == minor) {
+		if (bttvs[i].vbi_dev &&
+		    bttvs[i].vbi_dev->minor == minor) {
 			btv = &bttvs[i];
 			type = V4L2_BUF_TYPE_VBI_CAPTURE;
 			break;
@@ -2864,7 +2906,7 @@ static int bttv_open(struct inode *inode
 		return -ENODEV;
 
 	dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n",
-		btv->nr,v4l2_type_names[type]);
+		btv->c.nr,v4l2_type_names[type]);
 
 	/* allocate per filehandle data */
 	fh = kmalloc(sizeof(*fh),GFP_KERNEL);
@@ -2879,12 +2921,12 @@ static int bttv_open(struct inode *inode
 #endif
 
 	videobuf_queue_init(&fh->cap, &bttv_video_qops,
-			    btv->dev, &btv->s_lock,
+			    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->dev, &btv->s_lock,
+			    btv->c.pci, &btv->s_lock,
 			    V4L2_BUF_TYPE_VBI_CAPTURE,
 			    V4L2_FIELD_SEQ_TB,
 			    sizeof(struct bttv_buffer));
@@ -2942,7 +2984,7 @@ bttv_mmap(struct file *file, struct vm_a
 	struct bttv_fh *fh = file->private_data;
 
 	dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n",
-		fh->btv->nr, v4l2_type_names[fh->type],
+		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));
 }
@@ -2998,7 +3040,7 @@ static int radio_open(struct inode *inod
 	if (NULL == btv)
 		return -ENODEV;
 
-	dprintk("bttv%d: open called (radio)\n",btv->nr);
+	dprintk("bttv%d: open called (radio)\n",btv->c.nr);
 	down(&btv->lock);
 	if (btv->radio_user) {
 		up(&btv->lock);
@@ -3196,7 +3238,7 @@ bttv_irq_next_set(struct bttv *btv, stru
 
 	dprintk("bttv%d: next set: top=%p bottom=%p vbi=%p "
 		"[screen=%p,irq=%d,%d]\n",
-		btv->nr,set->top, set->bottom, set->vbi,
+		btv->c.nr,set->top, set->bottom, set->vbi,
 		btv->screen,set->irqflags,set->topirq);
 	return 0;
 }
@@ -3218,7 +3260,7 @@ bttv_irq_wakeup_set(struct bttv *btv, st
 	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->nr,wakeup->top);
+				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;
@@ -3227,7 +3269,7 @@ bttv_irq_wakeup_set(struct bttv *btv, st
 	} else {
 		if (NULL != wakeup->top && curr->top != wakeup->top) {
 			if (irq_debug > 1)
-				printk("bttv%d: wakeup: top=%p\n",btv->nr,wakeup->top);
+				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;
@@ -3235,7 +3277,7 @@ bttv_irq_wakeup_set(struct bttv *btv, st
 		}
 		if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
 			if (irq_debug > 1)
-				printk("bttv%d: wakeup: bottom=%p\n",btv->nr,wakeup->bottom);
+				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;
@@ -3249,15 +3291,16 @@ static void bttv_irq_timeout(unsigned lo
 	struct bttv *btv = (struct bttv *)data;
 	struct bttv_buffer_set old,new;
 	struct bttv_buffer *item;
+	unsigned long flags;
 	
 	if (bttv_verbose) {
 		printk(KERN_INFO "bttv%d: timeout: risc=%08x, ",
-		       btv->nr,btread(BT848_RISC_COUNT));
+		       btv->c.nr,btread(BT848_RISC_COUNT));
 		bttv_print_irqbits(btread(BT848_INT_STAT),0);
 		printk("\n");
 	}
 
-	spin_lock(&btv->s_lock);
+	spin_lock_irqsave(&btv->s_lock,flags);
 	
 	/* deactivate stuff */
 	memset(&new,0,sizeof(new));
@@ -3284,7 +3327,7 @@ static void bttv_irq_timeout(unsigned lo
 	}
 	
 	btv->errors++;
-	spin_unlock(&btv->s_lock);	
+	spin_unlock_irqrestore(&btv->s_lock,flags);
 }
 
 static void
@@ -3321,8 +3364,11 @@ bttv_irq_switch_fields(struct bttv *btv)
 	rc = btread(BT848_RISC_COUNT);
 	if (rc < btv->main.dma || rc > btv->main.dma + 0x100) {
 		if (1 /* irq_debug */)
-			printk("bttv%d: skipped frame. no signal? high irq latency?\n",
-			       btv->nr);
+			printk("bttv%d: skipped frame. no signal? high irq latency? "
+			       "[main=%lx,o_vbi=%lx,rc=%lx]\n", btv->c.nr,
+			       (unsigned long)btv->main.dma,
+			       (unsigned long)btv->main.cpu[RISC_SLOT_O_VBI+1],
+			       (unsigned long)rc);
 		spin_unlock(&btv->s_lock);
 		return;
 	}
@@ -3369,7 +3415,7 @@ static irqreturn_t bttv_irq(int irq, voi
 		if (irq_debug) {
 			printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d "
 			       "riscs=%x, riscc=%08x, ",
-			       btv->nr, count, btv->field_count,
+			       btv->c.nr, count, btv->field_count,
 			       stat>>28, btread(BT848_RISC_COUNT));
 			bttv_print_irqbits(stat,astat);
 			if (stat & BT848_INT_HLOCK)
@@ -3388,11 +3434,13 @@ static irqreturn_t bttv_irq(int irq, voi
                         btv->field_count++;
 
 		if (astat & BT848_INT_GPINT) {
-#ifdef CONFIG_VIDEO_IR
-			if (btv->remote)
-				bttv_input_irq(btv);
-#endif
 			wake_up(&btv->gpioq);
+			bttv_gpio_irq(&btv->c);
+		}
+
+		if (astat & BT848_INT_I2CDONE) {
+			btv->i2c_done = stat;
+			wake_up(&btv->i2c_queue);
 		}
 
                 if ((astat & BT848_INT_RISCI)  &&  (stat & (2<<28)))
@@ -3405,7 +3453,7 @@ static irqreturn_t bttv_irq(int irq, voi
 			audio_mux(btv, -1);
 
 		if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
-			printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->nr,
+			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));
@@ -3416,7 +3464,7 @@ static irqreturn_t bttv_irq(int irq, voi
 		}
 		if (fdsr && astat & BT848_INT_FDSR) {
 			printk(KERN_INFO "bttv%d: FDSR @ %08x\n",
-			       btv->nr,btread(BT848_RISC_COUNT));
+			       btv->c.nr,btread(BT848_RISC_COUNT));
 			if (bttv_debug)
 				bttv_print_riscaddr(btv);
 		}
@@ -3425,7 +3473,7 @@ static irqreturn_t bttv_irq(int irq, voi
 		if (count > 4) {
 			btwrite(0, BT848_INT_MASK);
 			printk(KERN_ERR 
-			       "bttv%d: IRQ lockup, cleared int mask [", btv->nr);
+			       "bttv%d: IRQ lockup, cleared int mask [", btv->c.nr);
 			bttv_print_irqbits(stat,astat);
 			printk("]\n");
 		}
@@ -3448,11 +3496,11 @@ static struct video_device *vdev_init(st
 		return NULL;
 	*vfd = *template;
 	vfd->minor   = -1;
-	vfd->dev     = &btv->dev->dev;
+	vfd->dev     = &btv->c.pci->dev;
 	vfd->release = video_device_release;
 	snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
 		 btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
-		 type, bttv_tvcards[btv->type].name);
+		 type, bttv_tvcards[btv->c.type].name);
 	return vfd;
 }
 
@@ -3491,7 +3539,7 @@ static int __devinit bttv_register_video
 	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->nr,btv->video_dev->minor & 0x1f);
+	       btv->c.nr,btv->video_dev->minor & 0x1f);
 	video_device_create_file(btv->video_dev, &class_device_attr_card);
 
 	/* vbi */
@@ -3501,7 +3549,7 @@ static int __devinit bttv_register_video
         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->nr,btv->vbi_dev->minor & 0x1f);
+	       btv->c.nr,btv->vbi_dev->minor & 0x1f);
 
         if (!btv->has_radio)
 		return 0;
@@ -3512,7 +3560,7 @@ static int __devinit bttv_register_video
 	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->nr,btv->radio_dev->minor & 0x1f);
+	       btv->c.nr,btv->radio_dev->minor & 0x1f);
 
 	/* all done */
 	return 0;
@@ -3548,14 +3596,17 @@ static int __devinit bttv_probe(struct p
 	printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
         btv=&bttvs[bttv_num];
 	memset(btv,0,sizeof(*btv));
-	btv->nr  = bttv_num;
-	sprintf(btv->name,"bttv%d",btv->nr);
+	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->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
@@ -3570,33 +3621,34 @@ static int __devinit bttv_probe(struct p
         btv->tuner_type  = UNSET;
         btv->pinnacle_id = UNSET;
 	btv->new_input   = UNSET;
-	btv->has_radio=radio[btv->nr];
+	btv->gpioirq     = 1;
+	btv->has_radio=radio[btv->c.nr];
 	
 	/* pci stuff (init, get irq/mmio, ... */
-	btv->dev = dev;
+	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),
-				btv->name)) {
+				btv->c.name)) {
                 printk(KERN_WARNING "bttv%d: can't request iomem (0x%lx).\n",
-		       btv->nr, pci_resource_start(dev,0));
+		       btv->c.nr, pci_resource_start(dev,0));
 		return -EBUSY;
 	}
         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->nr);
+		printk("bttv%d: Oops: no 32bit PCI DMA ???\n", btv->c.nr);
 		result = -EIO;
 		goto fail1;
 	}
@@ -3606,12 +3658,12 @@ static int __devinit bttv_probe(struct p
         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, pci_resource_start(dev,0));
+	       btv->c.pci->irq, lat, pci_resource_start(dev,0));
 	schedule();
 	
 	btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000);
 	if (NULL == ioremap(pci_resource_start(dev,0), 0x1000)) {
-		printk("bttv%d: ioremap() failed\n", btv->nr);
+		printk("bttv%d: ioremap() failed\n", btv->c.nr);
 		result = -EIO;
 		goto fail1;
 	}
@@ -3621,11 +3673,11 @@ static int __devinit bttv_probe(struct p
 
         /* disable irqs, register irq handler */
 	btwrite(0, BT848_INT_MASK);
-        result = request_irq(btv->dev->irq, bttv_irq,
-                             SA_SHIRQ | SA_INTERRUPT,btv->name,(void *)btv);
+        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->dev->irq);
+		       bttv_num,btv->c.pci->irq);
 		goto fail1;
         }
 
@@ -3641,6 +3693,8 @@ static int __devinit bttv_probe(struct p
 	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;
@@ -3657,31 +3711,26 @@ static int __devinit bttv_probe(struct p
                 bttv_gpio_tracking(btv,"pre-init");
 
 	bttv_risc_init_main(btv);
-	if (!bttv_tvcards[btv->type].no_video)
-		init_bt848(btv);
+	init_bt848(btv);
 
 	/* gpio */
         btwrite(0x00, BT848_GPIO_REG_INP);
         btwrite(0x00, BT848_GPIO_OUT_EN);
-        if (bttv_gpio)
+        if (bttv_verbose)
                 bttv_gpio_tracking(btv,"init");
 
         /* needs to be done before i2c is registered */
         bttv_init_card1(btv);
 
-        /* register i2c */
+        /* register i2c + gpio */
         init_bttv_i2c(btv);
 
         /* some card-specific stuff (needs working i2c) */
         bttv_init_card2(btv);
 
         /* register video4linux + input */
-	if (!bttv_tvcards[btv->type].no_video) {
+	if (!bttv_tvcards[btv->c.type].no_video) {
 		bttv_register_video(btv);
-#ifdef CONFIG_VIDEO_IR
-		bttv_input_init(btv);
-#endif
-
 		bt848_bright(btv,32768);
 		bt848_contrast(btv,32768);
 		bt848_hue(btv,32768);
@@ -3690,18 +3739,24 @@ static int __devinit bttv_probe(struct p
 		set_input(btv,0);
 	}
 
+	/* add subdevices */
+	if (btv->has_remote)
+		bttv_sub_add_device(&btv->c, "remote");
+	if (bttv_tvcards[btv->c.type].has_dvb)
+		bttv_sub_add_device(&btv->c, "dvb");
+
 	/* everything is fine */
 	bttv_num++;
         return 0;
 
  fail2:
-        free_irq(btv->dev->irq,btv);
+        free_irq(btv->c.pci->irq,btv);
 	
  fail1:
 	if (btv->bt848_mmio)
 		iounmap(btv->bt848_mmio);
-	release_mem_region(pci_resource_start(btv->dev,0),
-			   pci_resource_len(btv->dev,0));
+	release_mem_region(pci_resource_start(btv->c.pci,0),
+			   pci_resource_len(btv->c.pci,0));
 	pci_set_drvdata(dev,NULL);
 	return result;
 }
@@ -3711,7 +3766,7 @@ static void __devexit bttv_remove(struct
         struct bttv *btv = pci_get_drvdata(pci_dev);
 
 	if (bttv_verbose)
-		printk("bttv%d: unloading\n",btv->nr);
+		printk("bttv%d: unloading\n",btv->c.nr);
 
         /* shutdown everything (DMA+IRQs) */
 	btand(~15, BT848_GPIO_DMA_CTL);
@@ -3724,29 +3779,92 @@ static void __devexit bttv_remove(struct
 	/* tell gpio modules we are leaving ... */
 	btv->shutdown=1;
 	wake_up(&btv->gpioq);
-
+	bttv_sub_del_devices(&btv->c);
+	
         /* unregister i2c_bus + input */
 	fini_bttv_i2c(btv);
-#ifdef CONFIG_VIDEO_IR
-	bttv_input_fini(btv);
-#endif
 
 	/* unregister video4linux */
 	bttv_unregister_video(btv);
 
 	/* free allocated memory */
-	btcx_riscmem_free(btv->dev,&btv->main);
+	btcx_riscmem_free(btv->c.pci,&btv->main);
 
 	/* free ressources */
-        free_irq(btv->dev->irq,btv);
+        free_irq(btv->c.pci->irq,btv);
 	iounmap(btv->bt848_mmio);
-        release_mem_region(pci_resource_start(btv->dev,0),
-                           pci_resource_len(btv->dev,0));
+        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.set = btv->curr;
+	btv->curr = idle;
+	bttv_buffer_set_activate(btv, &idle);
+	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);
+
+	bt848_bright(btv,   btv->bright);
+	bt848_hue(btv,      btv->hue);
+	bt848_contrast(btv, btv->contrast);
+	bt848_sat(btv,      btv->saturation);
+
+	/* restart dma */
+	spin_lock_irqsave(&btv->s_lock,flags);
+	btv->curr = btv->state.set;
+	bttv_buffer_set_activate(btv, &btv->curr);
+	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},
@@ -3766,6 +3884,9 @@ static struct pci_driver bttv_pci_driver
         .id_table = bttv_pci_tbl,
         .probe    = bttv_probe,
         .remove   = __devexit_p(bttv_remove),
+
+	.suspend    = bttv_suspend,
+	.resume     = bttv_resume,
 };
 
 static int bttv_init_module(void)
@@ -3777,6 +3898,10 @@ static int bttv_init_module(void)
 	       (BTTV_VERSION_CODE >> 16) & 0xff,
 	       (BTTV_VERSION_CODE >> 8) & 0xff,
 	       BTTV_VERSION_CODE & 0xff);
+#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)
@@ -3788,6 +3913,7 @@ static int bttv_init_module(void)
 
 	bttv_check_chipset();
 
+	bus_register(&bttv_sub_bus_type);
 	rc = pci_module_init(&bttv_pci_driver);
 	if (-ENODEV == rc) {
 		/* plenty of people trying to use bttv for the cx2388x ... */
@@ -3800,6 +3926,7 @@ static int bttv_init_module(void)
 static void bttv_cleanup_module(void)
 {
 	pci_unregister_driver(&bttv_pci_driver);
+	bus_unregister(&bttv_sub_bus_type);
 	return;
 }
 
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv-gpio.c 780-bttv/drivers/media/video/bttv-gpio.c
--- 770-tuner/drivers/media/video/bttv-gpio.c	Wed Dec 31 16:00:00 1969
+++ 780-bttv/drivers/media/video/bttv-gpio.c	Fri Jan  9 23:11:34 2004
@@ -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 -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv-i2c.c 780-bttv/drivers/media/video/bttv-i2c.c
--- 770-tuner/drivers/media/video/bttv-i2c.c	Wed Dec 31 16:00:00 1969
+++ 780-bttv/drivers/media/video/bttv-i2c.c	Fri Jan  9 23:11:34 2004
@@ -0,0 +1,471 @@
+/*
+    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;
+
+#ifndef I2C_PEC
+static void bttv_inc_use(struct i2c_adapter *adap);
+static void bttv_dec_use(struct i2c_adapter *adap);
+#endif
+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 = {
+#ifdef I2C_PEC
+	.owner             = THIS_MODULE,
+#else
+	.inc_use           = bttv_inc_use,
+	.dec_use           = bttv_dec_use,
+#endif
+#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 = {
+#ifdef I2C_PEC
+	.owner         = THIS_MODULE,
+#else
+	.inc_use       = bttv_inc_use,
+	.dec_use       = bttv_dec_use,
+#endif
+#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                                            */
+
+#ifndef I2C_PEC
+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;
+}
+#endif
+
+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)
+{
+	int use_hw = (btv->id == 878) && i2c_hw;
+
+	memcpy(&btv->i2c_client, &bttv_i2c_client_template,
+	       sizeof(bttv_i2c_client_template));
+
+	if (use_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;
+	}
+
+	btv->c.i2c_adap.dev.parent = &btv->c.pci->dev;
+	snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name),
+		 "bt%d #%d [%s]", btv->id, btv->c.nr, use_hw ? "hw" : "sw");
+
+        i2c_set_adapdata(&btv->c.i2c_adap, btv);
+        btv->i2c_client.adapter = &btv->c.i2c_adap;
+
+	if (use_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)
+{
+	int use_hw = (btv->id == 878) && i2c_hw;
+
+	if (0 != btv->i2c_rc)
+		return 0;
+
+	if (use_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:
+ */
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv-if.c 780-bttv/drivers/media/video/bttv-if.c
--- 770-tuner/drivers/media/video/bttv-if.c	Mon Dec  8 09:55:51 2003
+++ 780-bttv/drivers/media/video/bttv-if.c	Fri Jan  9 23:11:34 2004
@@ -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
 
@@ -28,22 +28,10 @@
 #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;
-
-#ifndef I2C_PEC
-static void bttv_inc_use(struct i2c_adapter *adap);
-static void bttv_dec_use(struct i2c_adapter *adap);
-#endif
-static int attach_inform(struct i2c_client *client);
-
 EXPORT_SYMBOL(bttv_get_cardinfo);
 EXPORT_SYMBOL(bttv_get_pcidev);
 EXPORT_SYMBOL(bttv_get_id);
@@ -53,11 +41,6 @@ EXPORT_SYMBOL(bttv_write_gpio);
 EXPORT_SYMBOL(bttv_get_gpio_queue);
 EXPORT_SYMBOL(bttv_i2c_call);
 
-static int i2c_debug = 0;
-static int i2c_hw = 0;
-MODULE_PARM(i2c_debug,"i");
-MODULE_PARM(i2c_hw,"i");
-
 /* ----------------------------------------------------------------------- */
 /* Exported functions - for other modules which want to access the         */
 /*                      gpio ports (IR for example)                        */
@@ -68,7 +51,7 @@ int bttv_get_cardinfo(unsigned int card,
 	if (card >= bttv_num) {
 		return -1;
 	}
-	*type   = bttvs[card].type;
+	*type   = bttvs[card].c.type;
 	*cardid = bttvs[card].cardid;
 	return 0;
 }
@@ -77,7 +60,7 @@ struct pci_dev* bttv_get_pcidev(unsigned
 {
 	if (card >= bttv_num)
 		return NULL;
-	return bttvs[card].dev;
+	return bttvs[card].c.pci;
 }
 
 int bttv_get_id(unsigned int card)
@@ -86,7 +69,7 @@ int bttv_get_id(unsigned int card)
 	if (card >= bttv_num) {
 		return -1;
 	}
-	return bttvs[card].type;
+	return bttvs[card].c.type;
 }
 
 
@@ -99,7 +82,7 @@ int bttv_gpio_enable(unsigned int card, 
 	}
 	
 	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;
@@ -121,7 +104,7 @@ int bttv_read_gpio(unsigned int card, un
 
 /* 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;
 }
 
@@ -137,7 +120,7 @@ int bttv_write_gpio(unsigned int card, u
 
 /* 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;
@@ -156,418 +139,6 @@ wait_queue_head_t* bttv_get_gpio_queue(u
 		return NULL;
 	}
 	return &btv->gpioq;
-}
-
-
-/* ----------------------------------------------------------------------- */
-/* 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 = {
-#ifdef I2C_PEC
-	.owner             = THIS_MODULE,
-#else
-	.inc_use           = bttv_inc_use,
-	.dec_use           = bttv_dec_use,
-#endif
-#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)
-{
-	u32 stat;
-	unsigned long timeout;
-
-	timeout = jiffies + HZ/100 + 1; /* 10ms */
-	for (;;) {
-		stat = btread(BT848_INT_STAT);
-		if (stat & BT848_INT_I2CDONE)
-			break;
-		if (time_after(jiffies,timeout))
-			return -EIO;
-		udelay(10);
-	}
-	btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT);
-	return ((stat & BT848_INT_RACK) ? 1 : 0);
-}
-
-#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;
-
-	/* 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 = {
-#ifdef I2C_PEC
-	.owner         = THIS_MODULE,
-#else
-	.inc_use       = bttv_inc_use,
-	.dec_use       = bttv_dec_use,
-#endif
-#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                                            */
-
-#ifndef I2C_PEC
-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;
-}
-#endif
-
-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_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)
-{
-	int use_hw = (btv->id == 878) && i2c_hw;
-
-	memcpy(&btv->i2c_client, &bttv_i2c_client_template,
-	       sizeof(bttv_i2c_client_template));
-
-	if (use_hw) {
-		/* bt878 */
-		memcpy(&btv->i2c_adap, &bttv_i2c_adap_hw_template,
-		       sizeof(bttv_i2c_adap_hw_template));
-	} else {
-		/* bt848 */
-		memcpy(&btv->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->i2c_adap.algo_data = &btv->i2c_algo;
-	}
-
-	btv->i2c_adap.dev.parent = &btv->dev->dev;
-	snprintf(btv->i2c_adap.name, sizeof(btv->i2c_adap.name),
-		 "bt%d #%d [%s]", btv->id, btv->nr, use_hw ? "hw" : "sw");
-
-        i2c_set_adapdata(&btv->i2c_adap, btv);
-        btv->i2c_client.adapter = &btv->i2c_adap;
-
-	if (use_hw) {
-		btv->i2c_rc = i2c_add_adapter(&btv->i2c_adap);
-	} else {
-		bttv_bit_setscl(btv,1);
-		bttv_bit_setsda(btv,1);
-		btv->i2c_rc = i2c_bit_add_bus(&btv->i2c_adap);
-	}
-	return btv->i2c_rc;
-}
-
-int __devexit fini_bttv_i2c(struct bttv *btv)
-{
-	int use_hw = (btv->id == 878) && i2c_hw;
-
-	if (0 != btv->i2c_rc)
-		return 0;
-
-	if (use_hw) {
-		return i2c_del_adapter(&btv->i2c_adap);
-	} else {
-		return i2c_bit_del_bus(&btv->i2c_adap);
-	}
 }
 
 /*
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv-risc.c 780-bttv/drivers/media/video/bttv-risc.c
--- 770-tuner/drivers/media/video/bttv-risc.c	Mon Nov 17 18:29:29 2003
+++ 780-bttv/drivers/media/video/bttv-risc.c	Fri Jan  9 23:11:34 2004
@@ -53,7 +53,7 @@ bttv_risc_packed(struct bttv *btv, struc
 	   one write per scan line + sync + jump (all 2 dwords) */
 	instructions  = (bpl * lines) / PAGE_SIZE + lines;
 	instructions += 2;
-	if ((rc = btcx_riscmem_alloc(btv->dev,risc,instructions*8)) < 0)
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0)
 		return rc;
 
 	/* sync instruction */
@@ -100,7 +100,7 @@ bttv_risc_packed(struct bttv *btv, struc
 		}
 		offset += padding;
 	}
-	dprintk("bttv%d: risc planar: %d sglist elems\n", btv->nr, (int)(sg-sglist));
+	dprintk("bttv%d: risc planar: %d sglist elems\n", btv->c.nr, (int)(sg-sglist));
 
 	/* save pointer to jmp instruction address */
 	risc->jmp = rp;
@@ -128,7 +128,7 @@ bttv_risc_planar(struct bttv *btv, struc
 	   plus sync + jump (2 dwords) */
 	instructions  = (ybpl * ylines * 2) / PAGE_SIZE + ylines;
 	instructions += 2;
-	if ((rc = btcx_riscmem_alloc(btv->dev,risc,instructions*4*5)) < 0)
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0)
 		return rc;
 
 	/* sync instruction */
@@ -227,7 +227,7 @@ bttv_risc_overlay(struct bttv *btv, stru
 	instructions  = (ov->nclips + 1) *
 		((skip_even || skip_odd) ? ov->w.height>>1 :  ov->w.height);
 	instructions += 2;
-	if ((rc = btcx_riscmem_alloc(btv->dev,risc,instructions*8)) < 0) {
+	if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) {
 		kfree(skips);
 		return rc;
 	}
@@ -247,9 +247,6 @@ bttv_risc_overlay(struct bttv *btv, stru
 		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)
 		if ((line%2) == 0  &&  skip_even)
 			continue;
 		if ((line%2) == 1  &&  skip_odd)
@@ -310,7 +307,7 @@ bttv_calc_geo(struct bttv *btv, struct b
 	int totalwidth   = tvnorm->totalwidth;
 	int scaledtwidth = tvnorm->scaledtwidth;
 
-	if (bttv_tvcards[btv->type].muxsel[btv->input] < 0) {
+	if (bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) {
 		swidth       = 720;
 		totalwidth   = 858;
 		scaledtwidth = 858;
@@ -391,7 +388,7 @@ bttv_set_dma(struct bttv *btv, int overr
 
 	d2printk(KERN_DEBUG
 		 "bttv%d: capctl=%x irq=%d top=%08Lx/%08Lx even=%08Lx/%08Lx\n",
-		 btv->nr,capctl,irqflags,
+		 btv->c.nr,capctl,irqflags,
 		 btv->curr.vbi     ? (unsigned long long)btv->curr.vbi->top.dma        : 0,
 		 btv->curr.top     ? (unsigned long long)btv->curr.top->top.dma        : 0,
 		 btv->curr.vbi     ? (unsigned long long)btv->curr.vbi->bottom.dma     : 0,
@@ -429,10 +426,10 @@ bttv_risc_init_main(struct bttv *btv)
 {
 	int rc;
 	
-	if ((rc = btcx_riscmem_alloc(btv->dev,&btv->main,PAGE_SIZE)) < 0)
+	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->nr,(unsigned long long)btv->main.dma);
+		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);
@@ -472,11 +469,11 @@ bttv_risc_hook(struct bttv *btv, int slo
 
 	if (NULL == risc) {
 		d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=NULL\n",
-			 btv->nr,risc,slot);
+			 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->nr,risc,slot,(unsigned long long)risc->dma,irqflags);
+			 btv->c.nr,risc,slot,(unsigned long long)risc->dma,irqflags);
 		cmd = BT848_RISC_JUMP;
 		if (irqflags) {
 			cmd |= BT848_RISC_IRQ;
@@ -496,10 +493,10 @@ bttv_dma_free(struct bttv *btv, struct b
 	if (in_interrupt())
 		BUG();
 	videobuf_waiton(&buf->vb,0,0);
-	videobuf_dma_pci_unmap(btv->dev, &buf->vb.dma);
+	videobuf_dma_pci_unmap(btv->c.pci, &buf->vb.dma);
 	videobuf_dma_free(&buf->vb.dma);
-	btcx_riscmem_free(btv->dev,&buf->bottom);
-	btcx_riscmem_free(btv->dev,&buf->top);
+	btcx_riscmem_free(btv->c.pci,&buf->bottom);
+	btcx_riscmem_free(btv->c.pci,&buf->top);
 	buf->vb.state = STATE_NEEDS_INIT;
 }
 
@@ -577,7 +574,7 @@ bttv_buffer_risc(struct bttv *btv, struc
 
 	dprintk(KERN_DEBUG
 		"bttv%d: buffer field: %s  format: %s  size: %dx%d\n",
-		btv->nr, v4l2_field_names[buf->vb.field],
+		btv->c.nr, v4l2_field_names[buf->vb.field],
 		buf->fmt->name, buf->vb.width, buf->vb.height);
 
 	/* packed pixel modes */
@@ -731,7 +728,7 @@ bttv_overlay_risc(struct bttv *btv,
 	/* check interleave, bottom+top fields */
 	dprintk(KERN_DEBUG
 		"bttv%d: overlay fields: %s format: %s  size: %dx%d\n",
-		btv->nr, v4l2_field_names[buf->vb.field],
+		btv->c.nr, v4l2_field_names[buf->vb.field],
 		fmt->name,ov->w.width,ov->w.height);
 
 	/* calculate geometry */
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv-vbi.c 780-bttv/drivers/media/video/bttv-vbi.c
--- 770-tuner/drivers/media/video/bttv-vbi.c	Mon Nov 17 18:29:29 2003
+++ 780-bttv/drivers/media/video/bttv-vbi.c	Fri Jan  9 23:11:34 2004
@@ -44,7 +44,7 @@ MODULE_PARM_DESC(vbi_debug,"vbi code deb
 # undef dprintk
 #endif
 #define dprintk(fmt, arg...)	if (vbi_debug) \
-	printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->nr, ## arg)
+	printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg)
 
 /* ----------------------------------------------------------------------- */
 /* vbi risc code + mm                                                      */
@@ -87,7 +87,7 @@ static int vbi_buffer_prepare(struct fil
 		return -EINVAL;
 
 	if (STATE_NEEDS_INIT == buf->vb.state) {
-		if (0 != (rc = videobuf_iolock(btv->dev, &buf->vb, NULL)))
+		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;
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttv.h 780-bttv/drivers/media/video/bttv.h
--- 770-tuner/drivers/media/video/bttv.h	Mon Nov 17 18:29:29 2003
+++ 780-bttv/drivers/media/video/bttv.h	Fri Jan  9 23:11:34 2004
@@ -14,6 +14,7 @@
 #define _BTTV_H_
 
 #include <linux/videodev.h>
+#include <linux/i2c.h>
 
 /* ---------------------------------------------------------- */
 /* exported by bttv-cards.c                                   */
@@ -117,6 +118,10 @@
 #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
 
 /* i2c address list */
 #define I2C_TSA5522        0xc2
@@ -150,6 +155,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
@@ -173,7 +190,10 @@ struct tvcard
 	unsigned int msp34xx_alt:1;
 
 	/* flag: video pci function is unused */
-	unsigned int no_video;
+	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;
@@ -208,7 +228,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
@@ -256,7 +278,43 @@ extern wait_queue_head_t* bttv_get_gpio_
 extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg);
 
 
-/* i2c */
+
+/* ---------------------------------------------------------- */
+/* sysfs/driver-moded based gpio access interface             */
+
+
+struct bttv_sub_device {
+	struct device    dev;
+	struct bttv_core *core;
+	struct list_head list;
+};
+#define to_bttv_sub_dev(x) container_of((x), struct bttv_sub_device, dev)
+
+struct bttv_sub_driver {
+	struct device_driver   drv;
+	char                   wanted[BUS_ID_SIZE];
+	void                   (*gpio_irq)(struct bttv_sub_device *sub);
+};
+#define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv)
+
+int bttv_sub_register(struct bttv_sub_driver *drv, char *wanted);
+int bttv_sub_unregister(struct bttv_sub_driver *drv);
+
+/* gpio access functions */
+void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits);
+u32 bttv_gpio_read(struct bttv_core *core);
+void bttv_gpio_write(struct bttv_core *core, u32 value);
+void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits);
+
+#define gpio_inout(mask,bits)  bttv_gpio_inout(&btv->c, mask, bits)
+#define gpio_read()            bttv_gpio_read(&btv->c)
+#define gpio_write(value)      bttv_gpio_write(&btv->c, value)
+#define gpio_bits(mask,bits)   bttv_gpio_bits(&btv->c, mask, bits)
+
+
+/* ---------------------------------------------------------- */
+/* 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);
diff -aurpN -X /home/fletch/.diff.exclude 770-tuner/drivers/media/video/bttvp.h 780-bttv/drivers/media/video/bttvp.h
--- 770-tuner/drivers/media/video/bttvp.h	Mon Nov 17 18:29:29 2003
+++ 780-bttv/drivers/media/video/bttvp.h	Fri Jan  9 23:11:34 2004
@@ -41,13 +41,11 @@
 #include <media/video-buf.h>
 #include <media/audiochip.h>
 #include <media/tuner.h>
+#include <media/ir-common.h>
 
 #include "bt848.h"
 #include "bttv.h"
 #include "btcx-risc.h"
-#ifdef CONFIG_VIDEO_IR
-#include "ir-common.h"
-#endif
 
 #ifdef __KERNEL__
 
@@ -219,11 +217,14 @@ void bttv_vbi_setlines(struct bttv_fh *f
 extern struct videobuf_queue_ops bttv_vbi_qops;
 
 /* ---------------------------------------------------------- */
-/* bttv-input.c                                               */
+/* bttv-gpio.c */
+
+
+extern struct bus_type bttv_sub_bus_type;
+int bttv_sub_add_device(struct bttv_core *core, char *name);
+int bttv_sub_del_devices(struct bttv_core *core);
+void bttv_gpio_irq(struct bttv_core *core);
 
-int bttv_input_init(struct bttv *btv);
-void bttv_input_fini(struct bttv *btv);
-void bttv_input_irq(struct bttv *btv);
 
 /* ---------------------------------------------------------- */
 /* bttv-driver.c                                              */
@@ -263,7 +264,6 @@ struct bttv_pll_info {
 	unsigned int pll_current;  /* Currently programmed ofreq */
 };
 
-#ifdef CONFIG_VIDEO_IR
 /* for gpio-connected remote control */
 struct bttv_input {
 	struct input_dev      dev;
@@ -273,36 +273,46 @@ struct bttv_input {
 	u32                   mask_keycode;
 	u32                   mask_keydown;
 };
-#endif
+
+struct bttv_suspend_state {
+	u32  pci_cfg[64 / sizeof(u32)];
+	u32  gpio_enable;
+	u32  gpio_data;
+	int  disabled;
+	struct bttv_buffer_set set;
+};
 
 struct bttv {
+	struct bttv_core c;
+
 	/* pci device config */
-	struct pci_dev *dev;
 	unsigned short id;
 	unsigned char revision;
 	unsigned char *bt848_mmio;   /* pointer to mmio */
 
 	/* card configuration info */
-        unsigned int nr;       /* dev nr (for printk("bttv%d: ...");  */
-	char name[8];          /* dev name */
 	unsigned int cardid;   /* pci subsystem id (bt878 based ones) */
-	unsigned int type;     /* card type (pointer into tvcards[])  */
         unsigned int tuner_type;  /* tuner chip type */
         unsigned int pinnacle_id;
 	unsigned int svhs;
 	struct bttv_pll_info pll;
 	int triton1;
+	int gpioirq;
 
-	/* gpio interface */
+	/* 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_adapter         i2c_adap;
 	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;
 
 	/* video4linux (1) */
 	struct video_device *video_dev;
@@ -311,9 +321,7 @@ struct bttv {
 
 	/* infrared remote */
 	int has_remote;
-#ifdef CONFIG_VIDEO_IR
 	struct bttv_input *remote;
-#endif
 
 	/* locking */
 	spinlock_t s_lock;
@@ -339,6 +347,8 @@ struct bttv {
 	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;
@@ -372,6 +382,7 @@ struct bttv {
 	unsigned long dma_on;
 	struct timer_list timeout;
 	unsigned int errors;
+	struct bttv_suspend_state state;
 
 	unsigned int users;
 	struct bttv_fh init;