All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gerd Knorr <kraxel@bytesex.org>
To: Andrew Morton <akpm@osdl.org>,
	Kernel List <linux-kernel@vger.kernel.org>
Subject: [patch] cx88 update.
Date: Mon, 5 Apr 2004 14:33:39 +0200	[thread overview]
Message-ID: <20040405123339.GA30083@bytesex.org> (raw)

  Hi,

This is a update for the cx88 driver.  There are *lots* of changes:

  * vbi support was added.
  * plenty of fixes for audio support (there are still problems
    through).
  * new cards added.
  * serveral minor tweaks.

please apply,

  Gerd

diff -up linux-2.6.5/drivers/media/video/cx88/Makefile linux/drivers/media/video/cx88/Makefile
--- linux-2.6.5/drivers/media/video/cx88/Makefile	2004-04-05 10:39:31.257474664 +0200
+++ linux/drivers/media/video/cx88/Makefile	2004-04-05 10:49:59.012074060 +0200
@@ -1,5 +1,5 @@
 cx88xx-objs	:= cx88-cards.o cx88-core.o
-cx8800-objs	:= cx88-video.o cx88-tvaudio.o cx88-i2c.o
+cx8800-objs	:= cx88-video.o cx88-tvaudio.o cx88-i2c.o cx88-vbi.o
 
 obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
 
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-cards.c linux/drivers/media/video/cx88/cx88-cards.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-cards.c	2004-04-05 10:42:28.982940319 +0200
+++ linux/drivers/media/video/cx88/cx88-cards.c	2004-04-05 10:49:59.017073118 +0200
@@ -52,16 +52,19 @@ struct cx88_board cx88_boards[] = {
 		.input          = {{
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
-			//.gpio0  = 0xff03,
-			.gpio0  = 0xff01,
+			.gpio0  = 0xff00,  // internal decoder
 		},{
 			.type   = CX88_VMUX_DEBUG,
 			.vmux   = 0,
-			.gpio0  = 0xff00,
+			.gpio0  = 0xff01,  // mono from tuner chip
 		},{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
 			.gpio0  = 0xff02,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0xff02,
 		}},
 		.radio = {
 			.type   = CX88_RADIO,
@@ -92,21 +95,50 @@ struct cx88_board cx88_boards[] = {
 	},
 	[CX88_BOARD_ATI_WONDER_PRO] = {
 		.name           = "ATI TV Wonder Pro",
-		.tuner_type     = UNSET,
+		.tuner_type     = 44,
 		.input          = {{
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+
 		}},
 	},
         [CX88_BOARD_WINFAST2000XP] = {
                 .name           = "Leadtek Winfast 2000XP Expert",
-                .tuner_type     = 38,
+                .tuner_type     = 44,
                 .input          = {{
                         .type   = CX88_VMUX_TELEVISION,
                         .vmux   = 0,
+			.gpio0	= 0x00F5e700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5e700,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0	= 0x00F5c700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5c700,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0	= 0x00F5c700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5c700,
+			.gpio3  = 0x02000000,
                 }},
                 .radio = {
                         .type   = CX88_RADIO,
+			.gpio0	= 0x00F5d700,
+			.gpio1  = 0x00003004,
+			.gpio2  = 0x00F5d700,
+			.gpio3  = 0x02000000,
                 },
         },
 	[CX88_BOARD_AVERTV_303] = {
@@ -127,6 +159,10 @@ struct cx88_board cx88_boards[] = {
                         .type   = CX88_VMUX_COMPOSITE1,
                         .vmux   = 1,
 		},{
+			 // temporarly for testing ...
+                        .type   = CX88_VMUX_COMPOSITE2,
+                        .vmux   = 2,
+		},{
                         .type   = CX88_VMUX_SVIDEO,
                         .vmux   = 2,
                 }},
@@ -137,6 +173,7 @@ struct cx88_board cx88_boards[] = {
 	[CX88_BOARD_WINFAST_DV2000] = {
                 .name           = "Leadtek Winfast DV2000",
                 .tuner_type     = 38,
+		.needs_tda9887  = 1,
                 .input          = {{
                         .type   = CX88_VMUX_TELEVISION,
                         .vmux   = 0,
@@ -145,6 +182,24 @@ struct cx88_board cx88_boards[] = {
                         .type   = CX88_RADIO,
                 },
         },
+        [CX88_BOARD_LEADTEK_PVR2000] = {
+                .name           = "Leadtek PVR 2000",
+                .tuner_type     = 38,
+                .input          = {{
+                        .type   = CX88_VMUX_TELEVISION,
+                        .vmux   = 0,
+                },{
+                        .type   = CX88_VMUX_COMPOSITE1,
+                        .vmux   = 1,
+                },{
+                        .type   = CX88_VMUX_SVIDEO,
+                        .vmux   = 2,
+                }},
+                .radio = {
+                        .type   = CX88_RADIO,
+                },
+        },
+
 
 };
 const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
@@ -178,10 +233,18 @@ struct cx88_subid cx88_subids[] = {
                 .subdevice = 0x6611,
                 .card      = CX88_BOARD_WINFAST2000XP,
 	},{
+                .subvendor = 0x107d,
+                .subdevice = 0x6613,	/* NTSC */
+                .card      = CX88_BOARD_WINFAST2000XP,
+	},{
 		.subvendor = 0x107d,
                 .subdevice = 0x6620,
                 .card      = CX88_BOARD_WINFAST_DV2000,
         },{
+                .subvendor = 0x107d,
+                .subdevice = 0x663C,
+                .card      = CX88_BOARD_LEADTEK_PVR2000,
+        },{
 		.subvendor = 0x1461,
 		.subdevice = 0x000b,
 		.card      = CX88_BOARD_AVERTV_303,
@@ -193,6 +256,34 @@ struct cx88_subid cx88_subids[] = {
 };
 const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids);
 
+
+/* ----------------------------------------------------------------------- */
+/* some leadtek specific stuff                                             */
+
+static void __devinit leadtek_eeprom(struct cx8800_dev *dev, u8 *eeprom_data)
+{
+	/* This is just for the Winfast 2000 XP board ATM; I don't have data on
+	 * any others.
+	 *
+	 * Byte 0 is 1 on the NTSC board.
+	 */
+
+	if (eeprom_data[4] != 0x7d ||
+	    eeprom_data[5] != 0x10 || 
+	    eeprom_data[7] != 0x66) {
+		printk(KERN_WARNING "%s Leadtek eeprom invalid.\n", dev->name);
+		return;
+	}
+
+	dev->has_radio  = 1;
+	dev->tuner_type = (eeprom_data[6] == 0x13) ? 43 : 38;
+	
+	printk(KERN_INFO "%s: Leadtek Winfast 2000 XP config: "
+	       "tuner=%d, eeprom[0]=0x%02x\n",
+	       dev->name, dev->tuner_type, eeprom_data[0]);
+}
+
+
 /* ----------------------------------------------------------------------- */
 /* some hauppauge specific stuff                                           */
 
@@ -378,6 +469,11 @@ void __devinit cx88_card_setup(struct cx
 			i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
 		gdi_eeprom(dev,eeprom);
 		break;
+	case CX88_BOARD_WINFAST2000XP:
+		if (0 == dev->i2c_rc)
+			i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
+		leadtek_eeprom(dev,eeprom);
+		break;
 	}
 }
 
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-core.c linux/drivers/media/video/cx88/cx88-core.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-core.c	2004-04-05 10:39:53.528272081 +0200
+++ linux/drivers/media/video/cx88/cx88-core.c	2004-04-05 10:49:59.022072176 +0200
@@ -100,15 +100,6 @@ static const char *v4l2_ioctls[] = {
 };
 #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
 
-static const char *osspcm_ioctls[] = {
-	"RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT",
-	"CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS",
-	"GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER",
-	"GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO",
-	"SETDUPLEX", "GETODELAY"
-};
-#define OSSPCM_IOCTLS ARRAY_SIZE(v4l2_ioctls)
-
 void cx88_print_ioctl(char *name, unsigned int cmd)
 {
 	char *dir;
@@ -131,15 +122,6 @@ void cx88_print_ioctl(char *name, unsign
 		       name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ?
 		       v4l2_ioctls[_IOC_NR(cmd)] : "???");
 		break;
-	case 'P':
-		printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n",
-		       name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ?
-		       osspcm_ioctls[_IOC_NR(cmd)] : "???");
-		break;
-	case 'M':
-		printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n",
-		       name, cmd, dir, _IOC_NR(cmd));
-		break;
 	default:
 		printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n",
 		       name, cmd, dir, _IOC_NR(cmd));
@@ -296,6 +278,7 @@ struct sram_channel cx88_sram_channels[]
 		.name       = "video y / packed",
 		.cmds_start = 0x180040,
 		.ctrl_start = 0x180400,
+	        .cdt        = 0x180400 + 64,
 		.fifo_start = 0x180c00,
 		.fifo_size  = 0x002800,
 		.ptr1_reg   = MO_DMA21_PTR1,
@@ -307,6 +290,7 @@ struct sram_channel cx88_sram_channels[]
 		.name       = "video u",
 		.cmds_start = 0x180080,
 		.ctrl_start = 0x1804a0,
+	        .cdt        = 0x1804a0 + 64,
 		.fifo_start = 0x183400,
 		.fifo_size  = 0x000800,
 		.ptr1_reg   = MO_DMA22_PTR1,
@@ -318,6 +302,7 @@ struct sram_channel cx88_sram_channels[]
 		.name       = "video v",
 		.cmds_start = 0x1800c0,
 		.ctrl_start = 0x180540,
+	        .cdt        = 0x180540 + 64,
 		.fifo_start = 0x183c00,
 		.fifo_size  = 0x000800,
 		.ptr1_reg   = MO_DMA23_PTR1,
@@ -329,6 +314,7 @@ struct sram_channel cx88_sram_channels[]
 		.name       = "vbi",
 		.cmds_start = 0x180100,
 		.ctrl_start = 0x1805e0,
+	        .cdt        = 0x1805e0 + 64,
 		.fifo_start = 0x184400,
 		.fifo_size  = 0x001000,
 		.ptr1_reg   = MO_DMA24_PTR1,
@@ -340,6 +326,7 @@ struct sram_channel cx88_sram_channels[]
 		.name       = "audio from",
 		.cmds_start = 0x180140,
 		.ctrl_start = 0x180680,
+	        .cdt        = 0x180680 + 64,
 		.fifo_start = 0x185400,
 		.fifo_size  = 0x000200,
 		.ptr1_reg   = MO_DMA25_PTR1,
@@ -351,8 +338,9 @@ struct sram_channel cx88_sram_channels[]
 		.name       = "audio to",
 		.cmds_start = 0x180180,
 		.ctrl_start = 0x180720,
-		.fifo_start = 0x185600,
-		.fifo_size  = 0x000200,
+	        .cdt        = 0x180680 + 64,  /* same as audio IN */
+		.fifo_start = 0x185400,       /* same as audio IN */
+		.fifo_size  = 0x000200,       /* same as audio IN */
 		.ptr1_reg   = MO_DMA26_PTR1,
 		.ptr2_reg   = MO_DMA26_PTR2,
 		.cnt1_reg   = MO_DMA26_CNT1,
@@ -368,7 +356,7 @@ int cx88_sram_channel_setup(struct cx880
 	u32 cdt;
 
 	bpl   = (bpl + 7) & ~7; /* alignment */
-	cdt   = ch->ctrl_start + 64;
+	cdt   = ch->cdt;
 	lines = ch->fifo_size / bpl;
 	if (lines > 6)
 		lines = 6;
@@ -429,13 +417,13 @@ int cx88_risc_decode(u32 risc)
 	};
 	int i;
 
-	printk("0x%08x [ %s", risc, instr[risc >> 28] ?
-				instr[risc >> 28] : "INVALID");
+	printk("0x%08x [ %s", risc,
+	       instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
 	for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
 		if (risc & (1 << (i + 12)))
 			printk(" %s",bits[i]);
 	printk(" count=%d ]\n", risc & 0xfff);
-	return incr[risc >> 28] ? 1 : incr[risc >> 28];
+	return incr[risc >> 28] ? incr[risc >> 28] : 1;
 }
 
 void cx88_risc_disasm(struct cx8800_dev *dev,
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-i2c.c linux/drivers/media/video/cx88/cx88-i2c.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-i2c.c	2004-04-05 10:40:12.331723896 +0200
+++ linux/drivers/media/video/cx88/cx88-i2c.c	2004-04-05 10:49:59.025071610 +0200
@@ -152,7 +152,7 @@ int __devinit cx8800_i2c_init(struct cx8
 	       sizeof(dev->i2c_client));
 
 	dev->i2c_adap.dev.parent = &dev->pci->dev;
-	strcpy(dev->i2c_adap.name,dev->name);
+	strlcpy(dev->i2c_adap.name,dev->name,sizeof(dev->i2c_adap.name));
         dev->i2c_algo.data = dev;
         i2c_set_adapdata(&dev->i2c_adap,dev);
         dev->i2c_adap.algo_data = &dev->i2c_algo;
@@ -162,6 +162,8 @@ int __devinit cx8800_i2c_init(struct cx8
 	cx8800_bit_setsda(dev,1);
 
 	dev->i2c_rc = i2c_bit_add_bus(&dev->i2c_adap);
+	printk("%s: i2c register %s\n", dev->name,
+	       (0 == dev->i2c_rc) ? "ok" : "FAILED");
 	return dev->i2c_rc;
 }
 
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-reg.h linux/drivers/media/video/cx88/cx88-reg.h
--- linux-2.6.5/drivers/media/video/cx88/cx88-reg.h	2004-04-05 10:40:32.037005625 +0200
+++ linux/drivers/media/video/cx88/cx88-reg.h	2004-04-05 10:49:59.029070857 +0200
@@ -146,6 +146,8 @@
 #define MO_INPUT_FORMAT     0x310104
 #define MO_AGC_BURST        0x31010c
 #define MO_CONTR_BRIGHT     0x310110
+#define MO_UV_SATURATION    0x310114
+#define MO_HUE              0x310118
 #define MO_HTOTAL           0x310120
 #define MO_HDELAY_EVEN      0x310124
 #define MO_HDELAY_ODD       0x310128
@@ -175,6 +177,7 @@
 #define MO_VBI_PACKET       0x310188 // vbi packet size / delay
 #define MO_FIELD_COUNT      0x310190 // field counter
 #define MO_VIP_CONFIG       0x310194
+#define MO_VBOS_CONTROL	    0x3101a8
 
 #define MO_AGC_BACK_VBI     0x310200
 #define MO_AGC_SYNC_TIP1    0x310208
@@ -406,7 +409,7 @@
 #define AUD_PDF_DDS_CNST_BYTE1   0x320d02
 #define AUD_PDF_DDS_CNST_BYTE0   0x320d03
 #define AUD_PHACC_FREQ_8MSB      0x320d2a
-#define AUD_PHACC_FREQ_8LSB      0x320d23
+#define AUD_PHACC_FREQ_8LSB      0x320d2b
 #define AUD_QAM_MODE             0x320d04
 
 
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-tvaudio.c linux/drivers/media/video/cx88/cx88-tvaudio.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-tvaudio.c	2004-04-05 10:40:27.843796851 +0200
+++ linux/drivers/media/video/cx88/cx88-tvaudio.c	2004-04-05 10:49:59.034069915 +0200
@@ -14,7 +14,7 @@
     Some of this comes from party done linux driver sources I got from
     [undocumented].
 
-    Some comes from the dscaler sources, the dscaler driver guy works
+    Some comes from the dscaler sources, one of the dscaler driver guy works
     for Conexant ...
     
     -----------------------------------------------------------------------
@@ -51,7 +51,7 @@
 
 #include "cx88.h"
 
-static unsigned int audio_debug = UNSET;
+static unsigned int audio_debug = 1;
 MODULE_PARM(audio_debug,"i");
 MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]");
 
@@ -70,30 +70,81 @@ static void set_audio_registers(struct c
 {
 	int i;
 
-	for (i = 0; l[i].reg; i++)
-		cx_write(l[i].reg, l[i].val);
+	for (i = 0; l[i].reg; i++) {
+		switch (l[i].reg) {
+		case AUD_PDF_DDS_CNST_BYTE2:
+		case AUD_PDF_DDS_CNST_BYTE1:
+		case AUD_PDF_DDS_CNST_BYTE0:
+		case AUD_QAM_MODE:
+		case AUD_PHACC_FREQ_8MSB:
+		case AUD_PHACC_FREQ_8LSB:
+			cx_writeb(l[i].reg, l[i].val);
+			break;
+		default:
+			cx_write(l[i].reg, l[i].val);
+			break;
+		}
+	}
+}
+
+static void set_audio_start(struct cx8800_dev *dev,
+			    u32 mode, u32 ctl)
+{
+	// mute
+	cx_write(AUD_VOL_CTL,       (1 << 6));
+
+	//  increase level of input by 12dB
+	cx_write(AUD_AFE_12DB_EN,   0x0001);
+
+	// start programming
+	cx_write(AUD_CTL,           0x0000);
+	cx_write(AUD_INIT,          mode);
+	cx_write(AUD_INIT_LD,       0x0001);
+	cx_write(AUD_SOFT_RESET,    0x0001);
+
+	cx_write(AUD_CTL,           ctl);
+}
+
+static void set_audio_finish(struct cx8800_dev *dev)
+{
+	u32 volume;
+
+	// finish programming
+	cx_write(AUD_SOFT_RESET, 0x0000);
+
+	// start audio processing
+	cx_set(AUD_CTL, EN_DAC_ENABLE);
+
+	// unmute
+	volume = cx_sread(SHADOW_AUD_VOL_CTL);
+	cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume);
 }
 
+/* ----------------------------------------------------------- */
+
 static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap)
 {
-	dprintk("set_audio_standard_BTSC() [TODO]\n");
+	static const struct rlist btsc[] = {
+		/* Magic stuff from leadtek driver + datasheet.*/
+		{ AUD_DBX_IN_GAIN,   0x4734 },
+		{ AUD_DBX_WBE_GAIN,  0x4640 },
+		{ AUD_DBX_SE_GAIN,   0x8D31 },
+		{ AUD_DEEMPH0_G0,    0x1604 },
+		{ AUD_PHASE_FIX_CTL, 0x0020 },
+
+                { /* end of list */ },
+	};
+
+	dprintk("%s (status: unknown)\n",__FUNCTION__);
+	set_audio_start(dev, 0x0001,
+			EN_BTSC_AUTO_STEREO);
+        set_audio_registers(dev, btsc);
+	set_audio_finish(dev);
 }
 
 static void set_audio_standard_NICAM(struct cx8800_dev *dev)
 {
 	static const struct rlist nicam[] = {
-		//  increase level of input by 12dB
-		{ AUD_AFE_12DB_EN,         0x00000001 },
-
-    		// initialize NICAM                 
-    		{ AUD_INIT,                0x00000010 },
-    		{ AUD_INIT_LD,             0x00000001 },
-    		{ AUD_SOFT_RESET,          0x00000001 },
-    
-   		// WARNING!!!! Stereo mode is FORCED!!!!
-    		{ AUD_CTL,                 EN_DAC_ENABLE | EN_DMTRX_LR | EN_NICAM_FORCE_STEREO },
-    
-    		{ AUD_SOFT_RESET,          0x00000001 },
     		{ AUD_RATE_ADJ1,           0x00000010 },
     		{ AUD_RATE_ADJ2,           0x00000040 },
     		{ AUD_RATE_ADJ3,           0x00000100 },
@@ -101,43 +152,235 @@ static void set_audio_standard_NICAM(str
     		{ AUD_RATE_ADJ5,           0x00001000 },
     //		{ AUD_DMD_RA_DDS,          0x00c0d5ce },
 
+		// setup QAM registers
+		{ AUD_PDF_DDS_CNST_BYTE2,  0x06 },
+		{ AUD_PDF_DDS_CNST_BYTE1,  0x82 },
+		{ AUD_PDF_DDS_CNST_BYTE0,  0x16 },
+		{ AUD_QAM_MODE,            0x05 },
+		{ AUD_PHACC_FREQ_8MSB,     0x34 },
+		{ AUD_PHACC_FREQ_8LSB,     0x4c },
+
                 { /* end of list */ },
         };
 
-        printk("set_audio_standard_NICAM()\n");
+	dprintk("%s (status: unknown)\n",__FUNCTION__);
+        set_audio_start(dev, 0x0010,
+			EN_DMTRX_LR | EN_NICAM_FORCE_STEREO);
         set_audio_registers(dev, nicam);
+        set_audio_finish(dev);
+}
 
-    	// setup QAM registers
-    	cx_write(0x320d01,                0x06);
-    	cx_write(0x320d02,                0x82);
-    	cx_write(0x320d03,                0x16);
-    	cx_write(0x320d04,                0x05);
-    	cx_write(0x320d2a,                0x34);
-    	cx_write(0x320d2b,                0x4c);
-
-    	// setup Audio PLL
-    	//cx_write(AUD_PLL_PRESCALE,        0x0002);
-    	//cx_write(AUD_PLL_INT,             0x001f);
+static void set_audio_standard_NICAM_L(struct cx8800_dev *dev)
+{
+	/* This is officially wierd.. register dumps indicate windows
+	 * uses audio mode 4.. A2. Let's operate and find out. */
 
-    	// de-assert Audio soft reset
-    	cx_write(AUD_SOFT_RESET,          0x00000000);  // Causes a pop every time
+	static const struct rlist nicam_l[] = {
+		// setup QAM registers
+		{ AUD_PDF_DDS_CNST_BYTE2,	   0x48 },
+		{ AUD_PDF_DDS_CNST_BYTE1,          0x3d },
+		{ AUD_PDF_DDS_CNST_BYTE0,          0xf5 },
+		{ AUD_QAM_MODE,                    0x00 },
+		{ AUD_PHACC_FREQ_8MSB,             0x3a },
+		{ AUD_PHACC_FREQ_8LSB,             0x4a },
+
+		{ AUD_POLY0_DDS_CONSTANT,          0x000e4db2 },
+		{ AUD_IIR1_0_SEL,                  0x00000000 },                
+		{ AUD_IIR1_1_SEL,                  0x00000002 },                
+		{ AUD_IIR1_2_SEL,                  0x00000023 },                
+		{ AUD_IIR1_3_SEL,                  0x00000004 },                
+		{ AUD_IIR1_4_SEL,                  0x00000005 },                
+		{ AUD_IIR1_5_SEL,                  0x00000007 },                
+		{ AUD_IIR1_0_SHIFT,                0x00000007 },                
+		{ AUD_IIR1_1_SHIFT,                0x00000000 },              
+		{ AUD_IIR1_2_SHIFT,                0x00000000 },                
+		{ AUD_IIR1_3_SHIFT,                0x00000007 },              
+		{ AUD_IIR1_4_SHIFT,                0x00000007 },              
+		{ AUD_IIR1_5_SHIFT,                0x00000007 },              
+		{ AUD_IIR2_0_SEL,                  0x00000002 },              
+		{ AUD_IIR2_1_SEL,                  0x00000003 },
+		{ AUD_IIR2_2_SEL,                  0x00000004 },
+		{ AUD_IIR2_3_SEL,                  0x00000005 },         
+		{ AUD_IIR3_0_SEL,                  0x00000007 },         
+		{ AUD_IIR3_1_SEL,                  0x00000023 },         
+		{ AUD_IIR3_2_SEL,                  0x00000016 },         
+		{ AUD_IIR4_0_SHIFT,                0x00000000 },         
+		{ AUD_IIR4_1_SHIFT,                0x00000000 },         
+		{ AUD_IIR3_2_SHIFT,                0x00000002 },         
+		{ AUD_IIR4_0_SEL,                  0x0000001d },         
+		{ AUD_IIR4_1_SEL,                  0x00000019 },         
+		{ AUD_IIR4_2_SEL,                  0x00000008 },         
+		{ AUD_IIR4_0_SHIFT,                0x00000000 },         
+		{ AUD_IIR4_1_SHIFT,                0x00000007 },         
+		{ AUD_IIR4_2_SHIFT,                0x00000007 },         
+		{ AUD_IIR4_0_CA0,                  0x0003e57e },         
+		{ AUD_IIR4_0_CA1,                  0x00005e11 },         
+		{ AUD_IIR4_0_CA2,                  0x0003a7cf },         
+		{ AUD_IIR4_0_CB0,                  0x00002368 },     
+		{ AUD_IIR4_0_CB1,                  0x0003bf1b },         
+		{ AUD_IIR4_1_CA0,                  0x00006349 },         
+		{ AUD_IIR4_1_CA1,                  0x00006f27 },         
+		{ AUD_IIR4_1_CA2,                  0x0000e7a3 },         
+		{ AUD_IIR4_1_CB0,                  0x00005653 },
+		{ AUD_IIR4_1_CB1,                  0x0000cf97 },         
+		{ AUD_IIR4_2_CA0,                  0x00006349 },         
+		{ AUD_IIR4_2_CA1,                  0x00006f27 },         
+		{ AUD_IIR4_2_CA2,                  0x0000e7a3 },         
+		{ AUD_IIR4_2_CB0,                  0x00005653 },         
+		{ AUD_IIR4_2_CB1,                  0x0000cf97 },         
+		{ AUD_HP_MD_IIR4_1,                0x00000001 },           
+		{ AUD_HP_PROG_IIR4_1,              0x0000001a },         
+		{ AUD_DN0_FREQ,                    0x00000000 },
+		{ AUD_DN1_FREQ,                    0x00003318 },         
+		{ AUD_DN1_SRC_SEL,                 0x00000017 },         
+		{ AUD_DN1_SHFT,                    0x00000007 },         
+		{ AUD_DN1_AFC,                     0x00000000 },         
+		{ AUD_DN1_FREQ_SHIFT,              0x00000000 },         
+		{ AUD_DN2_FREQ,                    0x00003551 },         
+		{ AUD_DN2_SRC_SEL,                 0x00000001 },         
+		{ AUD_DN2_SHFT,                    0x00000000 },         
+		{ AUD_DN2_AFC,                     0x00000002 },         
+		{ AUD_DN2_FREQ_SHIFT,              0x00000000 },         
+		{ AUD_PDET_SRC,                    0x00000014 },         
+		{ AUD_PDET_SHIFT,                  0x00000000 },         
+		{ AUD_DEEMPH0_SRC_SEL,             0x00000011 },         
+		{ AUD_DEEMPH1_SRC_SEL,             0x00000011 },         
+		{ AUD_DEEMPH0_SHIFT,               0x00000000 },         
+		{ AUD_DEEMPH1_SHIFT,               0x00000000 },         
+		{ AUD_DEEMPH0_G0,                  0x00007000 },         
+		{ AUD_DEEMPH0_A0,                  0x00000000 },         
+		{ AUD_DEEMPH0_B0,                  0x00000000 },         
+		{ AUD_DEEMPH0_A1,                  0x00000000 },         
+		{ AUD_DEEMPH0_B1,                  0x00000000 },         
+		{ AUD_DEEMPH1_G0,                  0x00007000 },         
+		{ AUD_DEEMPH1_A0,                  0x00000000 },         
+		{ AUD_DEEMPH1_B0,                  0x00000000 },         
+		{ AUD_DEEMPH1_A1,                  0x00000000 },         
+		{ AUD_DEEMPH1_B1,                  0x00000000 },         
+		{ AUD_DMD_RA_DDS,                  0x00f5c285 },         
+		{ AUD_RATE_ADJ1,                   0x00000100 },         
+		{ AUD_RATE_ADJ2,                   0x00000200 },         
+		{ AUD_RATE_ADJ3,                   0x00000300 },         
+		{ AUD_RATE_ADJ4,                   0x00000400 },         
+		{ AUD_RATE_ADJ5,                   0x00000500 },              
+		{ AUD_C2_UP_THR,                   0x00005400 },              
+		{ AUD_C2_LO_THR,                   0x00003000 },              
+		{ AUD_C1_UP_THR,                   0x00007000 },              
+		{ AUD_C2_LO_THR,                   0x00005400 },              
+		{ AUD_CTL,                         0x0000100c },    
+		{ AUD_DCOC_0_SRC,                  0x00000021 },        
+		{ AUD_DCOC_1_SRC,                  0x00000003 },      
+		{ AUD_DCOC1_SHIFT,                 0x00000000 },      
+		{ AUD_DCOC_1_SHIFT_IN0,            0x0000000a },          
+		{ AUD_DCOC_1_SHIFT_IN1,            0x00000008 },         
+		{ AUD_DCOC_PASS_IN,                0x00000000 },           
+		{ AUD_DCOC_2_SRC,                  0x0000001b },             
+		{ AUD_IIR4_0_SEL,                  0x0000001d },            
+		{ AUD_POLY0_DDS_CONSTANT,          0x000e4db2 },         
+		{ AUD_PHASE_FIX_CTL,               0x00000000 },        
+		{ AUD_CORDIC_SHIFT_1,              0x00000007 },
+		{ AUD_PLL_EN,                      0x00000000 },        
+		{ AUD_PLL_PRESCALE,                0x00000002 },
+		{ AUD_PLL_INT,                     0x0000001e },            
+		{ AUD_OUT1_SHIFT,                  0x00000000 },          
+
+		{ /* end of list */ },
+	};
+
+	dprintk("%s (status: unknown)\n",__FUNCTION__);
+        set_audio_start(dev, 0x0004,
+			0 /* FIXME */);
+	set_audio_registers(dev, nicam_l);
+        set_audio_finish(dev);
 }
 
 static void set_audio_standard_A2(struct cx8800_dev *dev)
 {
+	/* from dscaler cvs */
 	static const struct rlist a2[] = {
-		//  increase level of input by 12dB
-		{ AUD_AFE_12DB_EN,         0x00000001 },
+		{ AUD_PDF_DDS_CNST_BYTE2,     0x06 },
+		{ AUD_PDF_DDS_CNST_BYTE1,     0x82 },
+		{ AUD_PDF_DDS_CNST_BYTE0,     0x12 },
+		{ AUD_QAM_MODE,		      0x05 },
+		{ AUD_PHACC_FREQ_8MSB,	      0x34 },
+		{ AUD_PHACC_FREQ_8LSB,	      0x4c },
+		
+		{ AUD_RATE_ADJ1,	0x00001000 },
+		{ AUD_RATE_ADJ2,	0x00002000 },
+		{ AUD_RATE_ADJ3,	0x00003000 },
+		{ AUD_RATE_ADJ4,	0x00004000 },
+		{ AUD_RATE_ADJ5,	0x00005000 },
+		{ AUD_THR_FR,		0x00000000 },
+		{ AAGC_HYST,		0x0000001a },
+		{ AUD_PILOT_BQD_1_K0,	0x0000755b },
+		{ AUD_PILOT_BQD_1_K1,	0x00551340 },
+		{ AUD_PILOT_BQD_1_K2,	0x006d30be },
+		{ AUD_PILOT_BQD_1_K3,	0xffd394af },
+		{ AUD_PILOT_BQD_1_K4,	0x00400000 },
+		{ AUD_PILOT_BQD_2_K0,	0x00040000 },
+		{ AUD_PILOT_BQD_2_K1,	0x002a4841 },
+		{ AUD_PILOT_BQD_2_K2,	0x00400000 },
+		{ AUD_PILOT_BQD_2_K3,	0x00000000 },
+		{ AUD_PILOT_BQD_2_K4,	0x00000000 },
+		{ AUD_MODE_CHG_TIMER,	0x00000040 },
+		{ AUD_START_TIMER,	0x00000200 },
+		{ AUD_AFE_12DB_EN,	0x00000000 },
+		{ AUD_CORDIC_SHIFT_0,	0x00000007 },
+		{ AUD_CORDIC_SHIFT_1,	0x00000007 },
+		{ AUD_DEEMPH0_G0,	0x00000380 },
+		{ AUD_DEEMPH1_G0,	0x00000380 },
+		{ AUD_DCOC_0_SRC,	0x0000001a },
+		{ AUD_DCOC0_SHIFT,	0x00000000 },
+		{ AUD_DCOC_0_SHIFT_IN0,	0x0000000a },
+		{ AUD_DCOC_0_SHIFT_IN1,	0x00000008 },
+		{ AUD_DCOC_PASS_IN,	0x00000003 },
+		{ AUD_IIR3_0_SEL,	0x00000021 },
+		{ AUD_DN2_AFC,		0x00000002 },
+		{ AUD_DCOC_1_SRC,	0x0000001b },
+		{ AUD_DCOC1_SHIFT,	0x00000000 },
+		{ AUD_DCOC_1_SHIFT_IN0,	0x0000000a },
+		{ AUD_DCOC_1_SHIFT_IN1,	0x00000008 },
+		{ AUD_IIR3_1_SEL,	0x00000023 },
+		{ AUD_RDSI_SEL,		0x00000017 },
+		{ AUD_RDSI_SHIFT,	0x00000000 },
+		{ AUD_RDSQ_SEL,		0x00000017 },
+		{ AUD_RDSQ_SHIFT,	0x00000000 },
+		{ AUD_POLYPH80SCALEFAC,	0x00000001 },
+		
+		// Table 1
+		{ AUD_DMD_RA_DDS,	0x002a73bd },
+		{ AUD_C1_UP_THR,	0x00007000 },
+		{ AUD_C1_LO_THR,	0x00005400 },
+		{ AUD_C2_UP_THR,	0x00005400 },
+		{ AUD_C2_LO_THR,	0x00003000 },
+
+#if 0
+		// found this in WDM-driver for A2, must country spec.
+		// Table 2
+		{ AUD_DMD_RA_DDS,	0x002a73bd },
+		{ AUD_C1_UP_THR,	0x00007000 },
+		{ AUD_C1_LO_THR,	0x00005400 },
+		{ AUD_C2_UP_THR,	0x00005400 },
+		{ AUD_C2_LO_THR,	0x00003000 },
+		{ AUD_DN0_FREQ,		0x00003a1c },
+		{ AUD_DN2_FREQ,		0x0000d2e0 },
+
+		// Table 3
+		{ AUD_DMD_RA_DDS,	0x002a2873 },
+		{ AUD_C1_UP_THR,	0x00003c00 },
+		{ AUD_C1_LO_THR,	0x00003000 },
+		{ AUD_C2_UP_THR,	0x00006000 },
+		{ AUD_C2_LO_THR,	0x00003c00 },
+		{ AUD_DN0_FREQ,		0x00002836 },
+		{ AUD_DN1_FREQ,		0x00003418 },
+		{ AUD_DN2_FREQ,		0x000029c7 },
+		{ AUD_POLY0_DDS_CONSTANT, 0x000a7540 },
+#endif
 
-		//  initialize A2
-		{ AUD_INIT,                0x00000004 },
-		{ AUD_INIT_LD,             0x00000001 },
-		{ AUD_SOFT_RESET,          0x00000001 },
-    
-		// ; WARNING!!! A2 STEREO DEMATRIX HAS TO BE
-		// ; SET MANUALLY!!!  Value sould be 0x100c
-		{ AUD_CTL, EN_DAC_ENABLE | EN_DMTRX_SUMR | EN_A2_AUTO_STEREO },
+		{ /* end of list */ },
+	};
 
+	static const struct rlist a2_old[] = {
 		{ AUD_DN0_FREQ,            0x0000312b },
 		{ AUD_POLY0_DDS_CONSTANT,  0x000a62b4 },
 		{ AUD_IIR1_0_SEL,          0x00000000 },
@@ -245,35 +488,45 @@ static void set_audio_standard_A2(struct
 		{ AUD_DN2_SRC_SEL,         0x00000001 },
 		{ AUD_DN2_FREQ,            0x00003551 },
 
-
 		//  setup Audio PLL
 		{ AUD_PLL_PRESCALE,        0x00000002 },
 		{ AUD_PLL_INT,             0x0000001f },
 
-		//  de-assert Audio soft reset
-		{ AUD_SOFT_RESET,          0x00000000 },
-
 		{ /* end of list */ },
 	};
 
-	dprintk("set_audio_standard_A2()\n");
-	set_audio_registers(dev, a2);
+
+	dprintk("%s (status: WorksForMe[tm])\n",__FUNCTION__);
+
+	if (0) {
+		/* old code */
+		set_audio_start(dev, 0x0004, EN_DMTRX_SUMR | EN_A2_AUTO_STEREO);
+		set_audio_registers(dev, a2_old);
+		set_audio_finish(dev);
+	} else {
+		/* new code */
+		set_audio_start(dev, 0x0004, EN_DMTRX_LR | EN_A2_AUTO_STEREO);
+		set_audio_registers(dev, a2);
+		set_audio_finish(dev);
+	}
 }
 
 static void set_audio_standard_EIAJ(struct cx8800_dev *dev)
 {
-	dprintk("set_audio_standard_EIAJ() [TODO]\n");
+	static const struct rlist eiaj[] = {
+		/* TODO: eiaj register settings are not there yet ... */
+
+		{ /* end of list */ },
+	};
+	dprintk("%s (status: unknown)\n",__FUNCTION__);
+
+	set_audio_start(dev, 0x0002, EN_EIAJ_AUTO_STEREO);
+	set_audio_registers(dev, eiaj);
+	set_audio_finish(dev);
 }
 
 static void set_audio_standard_FM(struct cx8800_dev *dev)
 {
-	dprintk("set_audio_standard_FM\n");
-
-	// initialize FM Radio
-	cx_write(AUD_INIT,0x0020);
-	cx_write(AUD_INIT_LD,0x0001);
-	cx_write(AUD_SOFT_RESET,0x0001);
-
 #if 0 /* FIXME */
 	switch (dev->audio_properties.FM_deemphasis)
 	{
@@ -311,19 +564,19 @@ static void set_audio_standard_FM(struct
 	}
 #endif
 
-	// de-assert Audio soft reset
-	cx_write(AUD_SOFT_RESET,0x0000);
+	dprintk("%s (status: unknown)\n",__FUNCTION__);
+	set_audio_start(dev, 0x0020, EN_FMRADIO_AUTO_STEREO);
 
 	// AB: 10/2/01: this register is not being reset appropriately on occasion.
 	cx_write(AUD_POLYPH80SCALEFAC,3);
+
+	set_audio_finish(dev);
 }
 
 /* ----------------------------------------------------------- */
 
 void cx88_set_tvaudio(struct cx8800_dev *dev)
 {
-	cx_write(AUD_CTL, 0x00);
-
 	switch (dev->tvaudio) {
 	case WW_BTSC:
 		set_audio_standard_BTSC(dev,0);
@@ -343,22 +596,21 @@ void cx88_set_tvaudio(struct cx8800_dev 
 	case WW_FM:
 		set_audio_standard_FM(dev);
 		break;
+	case WW_SYSTEM_L_AM:
+		set_audio_standard_NICAM_L(dev);
+		break;
 	case WW_NONE:
 	default:
 		printk("%s: unknown tv audio mode [%d]\n",
 		       dev->name, dev->tvaudio);
 		break;
 	}
-
-	// unmute
-	cx_set(AUD_CTL, EN_DAC_ENABLE);
-	cx_write(AUD_VOL_CTL, 0x00);
 	return;
 }
 
 void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
 {
-	static char *m[] = {"mono", "dual mono", "stereo", "sap"};
+	static char *m[] = {"stereo", "dual mono", "mono", "sap"};
 	static char *p[] = {"no pilot", "pilot c1", "pilot c2", "?"};
 	u32 reg,mode,pilot;
 
@@ -367,7 +619,7 @@ void cx88_get_stereo(struct cx8800_dev *
 	pilot = (reg >> 2) & 0x03;
 	dprintk("AUD_STATUS: %s / %s [status=0x%x,ctl=0x%x,vol=0x%x]\n",
 		m[mode], p[pilot], reg,
-		cx_read(AUD_CTL), cx_read(AUD_VOL_CTL));
+		cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));
 
 	t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
 		V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
@@ -376,20 +628,20 @@ void cx88_get_stereo(struct cx8800_dev *
 
 	switch (dev->tvaudio) {
 	case WW_A2_BG:
- 		if (2 == pilot) {
+ 		if (1 == pilot) {
 			/* stereo */
 			t->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
-			if (2 == mode)
+			if (0 == mode)
 				t->audmode = V4L2_TUNER_MODE_STEREO;
 		}
- 		if (1 == pilot) {
+ 		if (2 == pilot) {
 			/* dual language -- FIXME */
 			t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
 			t->audmode = V4L2_TUNER_MODE_LANG1;
 		}
 		break;
 	case WW_NICAM_BGDKL:
-		if (2 == mode)
+		if (0 == mode)
 			t->audmode = V4L2_TUNER_MODE_STEREO;
 		break;
 	default:
@@ -455,12 +707,12 @@ void cx88_set_stereo(struct cx8800_dev *
 
 	if (UNSET != ctl) {
 		cx_write(AUD_SOFT_RESET, 0x0001);
-		cx_andor(AUD_CTL, mask, ctl);
+		cx_andor(AUD_CTL, mask,  ctl);
 		cx_write(AUD_SOFT_RESET, 0x0000);
 		dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x "
 			"[status=0x%x,ctl=0x%x,vol=0x%x]\n",
 			mask, ctl, cx_read(AUD_STATUS),
-			cx_read(AUD_CTL), cx_read(AUD_VOL_CTL));
+			cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));
 	}
 	return;
 }
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-vbi.c linux/drivers/media/video/cx88/cx88-vbi.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-vbi.c	2004-04-05 10:49:59.038069161 +0200
+++ linux/drivers/media/video/cx88/cx88-vbi.c	2004-04-05 10:49:59.038069161 +0200
@@ -0,0 +1,228 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include "cx88.h"
+
+static unsigned int vbibufs = 4;
+MODULE_PARM(vbibufs,"i");
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug = 0;
+MODULE_PARM(vbi_debug,"i");
+MODULE_PARM_DESC(vbi_debug,"enable debug messages [video]");
+
+#define dprintk(level,fmt, arg...)	if (vbi_debug >= level) \
+	printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f)
+{
+	memset(&f->fmt.vbi,0,sizeof(f->fmt.vbi));
+
+	f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+	f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+	f->fmt.vbi.offset = 244;
+	f->fmt.vbi.count[0] = VBI_LINE_COUNT;
+	f->fmt.vbi.count[1] = VBI_LINE_COUNT;
+
+	switch (dev->tvnorm->id) {
+	case V4L2_STD_NTSC_M:
+	case V4L2_STD_NTSC_M_JP:
+		f->fmt.vbi.sampling_rate = 28636363;
+		f->fmt.vbi.start[0] = 10 -1;
+		f->fmt.vbi.start[1] = 273 -1;
+		break;
+	case V4L2_STD_PAL_BG:
+	case V4L2_STD_PAL_DK:
+	case V4L2_STD_PAL_I:
+	case V4L2_STD_SECAM:
+		f->fmt.vbi.sampling_rate = 35468950;
+		f->fmt.vbi.start[0] = 7 -1;
+		f->fmt.vbi.start[1] = 319 -1;
+	}
+}
+
+int cx8800_start_vbi_dma(struct cx8800_dev    *dev,
+			 struct cx88_dmaqueue *q,
+			 struct cx88_buffer   *buf)
+{
+	/* setup fifo + format */
+	cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH24],
+				buf->vb.width, buf->risc.dma);
+
+	cx_write(MO_VBOS_CONTROL, ( (1 << 18) |  // comb filter delay fixup
+				    (1 << 15) |  // enable vbi capture
+				    (1 << 11) ));
+
+	/* reset counter */
+	cx_write(MO_VBI_GPCNTRL,0x3);
+	q->count = 1;
+
+	/* enable irqs */
+	cx_set(MO_PCI_INTMSK, 0x00fc01);
+	cx_set(MO_VID_INTMSK, 0x0f0088);
+	
+	/* enable capture */
+	cx_set(VID_CAPTURE_CONTROL,0x18);
+	
+	/* start dma */
+	cx_set(MO_DEV_CNTRL2, (1<<5));
+	cx_set(MO_VID_DMACNTRL, 0x88);
+
+	return 0;
+}
+
+int cx8800_restart_vbi_queue(struct cx8800_dev    *dev,
+			     struct cx88_dmaqueue *q)
+{
+	struct cx88_buffer *buf;
+	struct list_head *item;
+	
+	if (list_empty(&q->active))
+		return 0;
+
+	buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+	dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+		buf, buf->vb.i);
+	cx8800_start_vbi_dma(dev, q, buf);
+	list_for_each(item,&q->active) {
+		buf = list_entry(item, struct cx88_buffer, vb.queue);
+		buf->count = q->count++;
+	}
+	mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+	return 0;
+}
+
+void cx8800_vbi_timeout(unsigned long data)
+{
+	struct cx8800_dev *dev = (struct cx8800_dev*)data;
+	struct cx88_dmaqueue *q = &dev->vbiq;
+	struct cx88_buffer *buf;
+	unsigned long flags;
+
+	cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH24]);
+	
+	cx_clear(MO_VID_DMACNTRL, 0x88);
+	cx_clear(VID_CAPTURE_CONTROL, 0x18);
+
+	spin_lock_irqsave(&dev->slock,flags);
+	while (!list_empty(&q->active)) {
+		buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+		list_del(&buf->vb.queue);
+		buf->vb.state = STATE_ERROR;
+		wake_up(&buf->vb.done);
+		printk("%s: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
+		       buf, buf->vb.i, (unsigned long)buf->risc.dma);
+	}
+	cx8800_restart_vbi_queue(dev,q);
+	spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int
+vbi_setup(struct file *file, unsigned int *count, unsigned int *size)
+{
+	*size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+	if (0 == *count)
+		*count = vbibufs;
+	if (*count < 2)
+		*count = 2;
+	if (*count > 32)
+		*count = 32;
+	return 0;
+}
+
+static int
+vbi_prepare(struct file *file, struct videobuf_buffer *vb,
+	    enum v4l2_field field)
+{
+	struct cx8800_fh   *fh  = file->private_data;
+	struct cx8800_dev  *dev = fh->dev;
+	struct cx88_buffer *buf = (struct cx88_buffer*)vb;
+	unsigned int size;
+	int rc;
+
+	size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
+	if (0 != buf->vb.baddr  &&  buf->vb.bsize < size)
+		return -EINVAL;
+
+	if (STATE_NEEDS_INIT == buf->vb.state) {
+		buf->vb.width  = VBI_LINE_LENGTH;
+		buf->vb.height = VBI_LINE_COUNT;
+		buf->vb.size   = size;
+		buf->vb.field  = V4L2_FIELD_SEQ_TB;
+
+		if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL)))
+			goto fail;
+		cx88_risc_buffer(dev->pci, &buf->risc,
+				 buf->vb.dma.sglist,
+				 0, buf->vb.width * buf->vb.height,
+				 buf->vb.width, 0, 
+				 buf->vb.height);
+	}
+	buf->vb.state = STATE_PREPARED;
+	return 0;
+
+ fail:
+	cx88_free_buffer(dev->pci,buf);
+	return rc;
+}
+
+static void
+vbi_queue(struct file *file, struct videobuf_buffer *vb)
+{
+	struct cx88_buffer    *buf  = (struct cx88_buffer*)vb;
+	struct cx88_buffer    *prev;
+	struct cx8800_fh      *fh   = file->private_data;
+	struct cx8800_dev     *dev  = fh->dev;
+	struct cx88_dmaqueue  *q    = &dev->vbiq;
+
+	/* add jump to stopper */
+	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | 0x10000);
+	buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+
+	if (list_empty(&q->active)) {
+		list_add_tail(&buf->vb.queue,&q->active);
+		cx8800_start_vbi_dma(dev, q, buf);
+		buf->vb.state = STATE_ACTIVE;
+		buf->count    = q->count++;
+		mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+		dprintk(2,"[%p/%d] vbi_queue - first active\n",
+			buf, buf->vb.i);
+
+	} else {
+		prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
+		list_add_tail(&buf->vb.queue,&q->active);
+		buf->vb.state = STATE_ACTIVE;
+		buf->count    = q->count++;
+		prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+		dprintk(2,"[%p/%d] buffer_queue - append to active\n",
+			buf, buf->vb.i);
+	}
+}
+
+static void vbi_release(struct file *file, struct videobuf_buffer *vb)
+{
+	struct cx88_buffer *buf = (struct cx88_buffer*)vb;
+	struct cx8800_fh   *fh  = file->private_data;
+
+	cx88_free_buffer(fh->dev->pci,buf);
+}
+
+struct videobuf_queue_ops cx8800_vbi_qops = {
+	.buf_setup    = vbi_setup,
+	.buf_prepare  = vbi_prepare,
+	.buf_queue    = vbi_queue,
+	.buf_release  = vbi_release,
+};
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-video.c linux/drivers/media/video/cx88/cx88-video.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-video.c	2004-04-05 10:41:11.667527842 +0200
+++ linux/drivers/media/video/cx88/cx88-video.c	2004-04-05 10:49:59.048067277 +0200
@@ -42,6 +42,10 @@ static unsigned int video_nr[] = {[0 ...
 MODULE_PARM(video_nr,"1-" __stringify(CX88_MAXBOARDS) "i");
 MODULE_PARM_DESC(video_nr,"video device numbers");
 
+static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+MODULE_PARM(vbi_nr,"1-" __stringify(CX88_MAXBOARDS) "i");
+MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
+
 static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
 MODULE_PARM(radio_nr,"1-" __stringify(CX88_MAXBOARDS) "i");
 MODULE_PARM_DESC(radio_nr,"radio device numbers");
@@ -131,45 +135,69 @@ static unsigned int inline norm_htotal(s
 	return (norm->id & V4L2_STD_625_50) ? 1135 : 910;
 }
 
+static unsigned int inline norm_vbipack(struct cx8800_tvnorm *norm)
+{
+	return (norm->id & V4L2_STD_625_50) ? 511 : 288;
+}
+
 static struct cx8800_tvnorm tvnorms[] = {
 	{
 		.name      = "NTSC-M",
 		.id        = V4L2_STD_NTSC_M,
 		.cxiformat = VideoFormatNTSC,
+		.cxoformat = 0x181f0008,
 	},{
 		.name      = "NTSC-JP",
 		.id        = V4L2_STD_NTSC_M_JP,
 		.cxiformat = VideoFormatNTSCJapan,
+		.cxoformat = 0x181f0008,
 #if 0
 	},{
 		.name      = "NTSC-4.43",
 		.id        = FIXME,
 		.cxiformat = VideoFormatNTSC443,
+		.cxoformat = 0x181f0008,
 #endif
 	},{
-		.name      = "PAL",
-		.id        = V4L2_STD_PAL,
+		.name      = "PAL-BG",
+		.id        = V4L2_STD_PAL_BG,
+		.cxiformat = VideoFormatPAL,
+		.cxoformat = 0x181f0008,
+	},{
+		.name      = "PAL-DK",
+		.id        = V4L2_STD_PAL_DK,
 		.cxiformat = VideoFormatPAL,
+		.cxoformat = 0x181f0008,
+	},{
+		.name      = "PAL-I",
+		.id        = V4L2_STD_PAL_I,
+		.cxiformat = VideoFormatPAL,
+		.cxoformat = 0x181f0008,
         },{
 		.name      = "PAL-M",
 		.id        = V4L2_STD_PAL_M,
 		.cxiformat = VideoFormatPALM,
+		.cxoformat = 0x1c1f0008,
 	},{
 		.name      = "PAL-N",
 		.id        = V4L2_STD_PAL_N,
 		.cxiformat = VideoFormatPALN,
+		.cxoformat = 0x1c1f0008,
 	},{
 		.name      = "PAL-Nc",
 		.id        = V4L2_STD_PAL_Nc,
 		.cxiformat = VideoFormatPALNC,
+		.cxoformat = 0x1c1f0008,
 	},{
 		.name      = "PAL-60",
 		.id        = V4L2_STD_PAL_60,
 		.cxiformat = VideoFormatPAL60,
+		.cxoformat = 0x181f0008,
 	},{
 		.name      = "SECAM",
 		.id        = V4L2_STD_SECAM,
 		.cxiformat = VideoFormatSECAM,
+		.cxoformat = 0x181f0008,
 	}
 };
 
@@ -266,10 +294,10 @@ static struct cx88_ctrl cx8800_ctls[] = 
 			.default_value = 0,
 			.type          = V4L2_CTRL_TYPE_INTEGER,
 		},
-		.off             = 128,
-		.reg             = MO_CONTR_BRIGHT,
-		.mask            = 0x00ff,
-		.shift           = 0,
+		.off                   = 128,
+		.reg                   = MO_CONTR_BRIGHT,
+		.mask                  = 0x00ff,
+		.shift                 = 0,
 	},{
 		.v = {
 			.id            = V4L2_CID_CONTRAST,
@@ -280,12 +308,42 @@ static struct cx88_ctrl cx8800_ctls[] = 
 			.default_value = 0,
 			.type          = V4L2_CTRL_TYPE_INTEGER,
 		},
-		.reg             = MO_CONTR_BRIGHT,
-		.mask            = 0xff00,
-		.shift           = 8,
+		.reg                   = MO_CONTR_BRIGHT,
+		.mask                  = 0xff00,
+		.shift                 = 8,
+	},{
+		.v = {
+			.id            = V4L2_CID_HUE,
+			.name          = "Hue",
+			.minimum       = 0,
+			.maximum       = 0xff,
+			.step          = 1,
+			.default_value = 0,
+			.type          = V4L2_CTRL_TYPE_INTEGER,
+		},
+		.off                   = 0,
+		.reg                   = MO_HUE,
+		.mask                  = 0x00ff,
+		.shift                 = 0,
+	},{
+		/* strictly, this only describes only U saturation.
+		 * V saturation is handled specially through code.
+		 */
+		.v = {
+			.id            = V4L2_CID_SATURATION,
+			.name          = "Saturation", 
+			.minimum       = 0,
+			.maximum       = 0xff,
+			.step          = 1,
+			.default_value = 0,
+			.type          = V4L2_CTRL_TYPE_INTEGER,
+		},
+		.off                   = 0,
+		.reg                   = MO_UV_SATURATION,
+		.mask                  = 0x00ff,
+		.shift                 = 0,
 	},{
 	/* --- audio --- */
-#if 0
 		.v = {
 			.id            = V4L2_CID_AUDIO_MUTE,
 			.name          = "Mute",
@@ -293,11 +351,11 @@ static struct cx88_ctrl cx8800_ctls[] = 
 			.maximum       = 1,
 			.type          = V4L2_CTRL_TYPE_BOOLEAN,
 		},
-		.reg             = AUD_VOL_CTL,
-		.mask            = (1 << 6),
-		.shift           = 6,
+		.reg                   = AUD_VOL_CTL,
+		.sreg                  = SHADOW_AUD_VOL_CTL,
+		.mask                  = (1 << 6),
+		.shift                 = 6,
 	},{
-#endif
 		.v = {
 			.id            = V4L2_CID_AUDIO_VOLUME,
 			.name          = "Volume",
@@ -307,9 +365,24 @@ static struct cx88_ctrl cx8800_ctls[] = 
 			.default_value = 0,
 			.type          = V4L2_CTRL_TYPE_INTEGER,
 		},
-		.reg             = AUD_VOL_CTL,
-		.mask            = 0x3f,
-		.shift           = 0,
+		.reg                   = AUD_VOL_CTL,
+		.sreg                  = SHADOW_AUD_VOL_CTL,
+		.mask                  = 0x3f,
+		.shift                 = 0,
+	},{
+		.v = {
+			.id            = V4L2_CID_AUDIO_BALANCE,
+			.name          = "Balance",
+			.minimum       = 0,
+			.maximum       = 0x7f,
+			.step          = 1,
+			.default_value = 0x40,
+			.type          = V4L2_CTRL_TYPE_INTEGER,
+		},
+		.reg                   = AUD_BAL_CTL,
+		.sreg                  = SHADOW_AUD_BAL_CTL,
+		.mask                  = 0x7f,
+		.shift                 = 0,
 	}
 };
 const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls);
@@ -344,13 +417,11 @@ int res_check(struct cx8800_fh *fh, unsi
 	return (fh->resources & bit);
 }
 
-#if 0
 static
 int res_locked(struct cx8800_dev *dev, unsigned int bit)
 {
 	return (dev->resources & bit);
 }
-#endif
 
 static
 void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
@@ -412,21 +483,39 @@ static int set_tvaudio(struct cx8800_dev
 	if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type)
 		return 0;
 
-
-	dev->tvaudio = 0;
-	if (dev->tvnorm->id & V4L2_STD_PAL) {
-		if (nicam)
-			dev->tvaudio = WW_NICAM_BGDKL;
-		else
-			dev->tvaudio = WW_A2_BG;
-	}
-	if (0 == dev->tvaudio)
+	switch (dev->tvnorm->id) {
+	case V4L2_STD_PAL_BG:
+		dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG;
+		break;
+	case V4L2_STD_PAL_DK:
+		dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK;
+		break;
+	case V4L2_STD_PAL_I:
+		dev->tvaudio = WW_NICAM_I;
+		break;
+	case V4L2_STD_SECAM:
+		dev->tvaudio = WW_SYSTEM_L_AM;  /* FIXME: fr != ru */
+		break;
+	case V4L2_STD_NTSC_M:
+		dev->tvaudio = WW_BTSC;
+		break;
+	case V4L2_STD_NTSC_M_JP:
+		dev->tvaudio = WW_EIAJ;
+		break;
+	default:
+		dprintk(1,"tvaudio support needs work for this tv norm [%s], sorry\n",
+			dev->tvnorm->name);
+		dev->tvaudio = 0;
 		return 0;
+	}
 
-	cx_andor(MO_AFECFG_IO,    0x1f, 0x0);
+	cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
 	cx88_set_tvaudio(dev);
-	//cx88_set_stereo(dev,norm->tvaudio, V4L2_TUNER_MODE_MONO);
-	//cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */
+	cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO);
+
+	cx_write(MO_AUDD_LNGTH, 128/8);  /* fifo size */
+	cx_write(MO_AUDR_LNGTH, 128/8);  /* fifo size */
+	cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */
 	return 0;
 }
 
@@ -455,8 +544,8 @@ static int set_tvnorm(struct cx8800_dev 
 #if 1
 	// FIXME: as-is from DScaler
 	dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
-		0x1c1f0008, cx_read(MO_OUTPUT_FORMAT));
-	cx_write(MO_OUTPUT_FORMAT, 0x1c1f0008);
+		norm->cxoformat, cx_read(MO_OUTPUT_FORMAT));
+	cx_write(MO_OUTPUT_FORMAT, norm->cxoformat);
 #endif
 
 	// MO_SCONV_REG = adc clock / video dec clock * 2^17
@@ -494,6 +583,10 @@ static int set_tvnorm(struct cx8800_dev 
 	dprintk(1,"set_tvnorm: MO_HTOTAL        0x%08x [old=0x%08x,htotal=%d]\n",
 		htotal, cx_read(MO_HTOTAL), (u32)tmp64);
 	cx_write(MO_HTOTAL, htotal);
+
+	// vbi stuff
+	cx_write(MO_VBI_PACKET, ((1 << 11) | /* (norm_vdelay(norm)   << 11) | */
+				 norm_vbipack(norm)));
 	
 	// audio
 	set_tvaudio(dev);
@@ -552,9 +645,14 @@ static int set_scale(struct cx8800_dev *
 
 	// setup filters
 	value = 0;
-	value |= (1 << 19);  // CFILT (default)
+	value |= (1 << 19);        // CFILT (default)
 	if (interlaced)
 		value |= (1 << 3); // VINT (interlaced vertical scaling)
+	if (width < 385)
+		value |= (1 << 0); // 3-tap interpolation
+	if (width < 193)
+		value |= (1 << 1); // 5-tap interpolation
+
 	cx_write(MO_FILTER_EVEN,  value);
 	cx_write(MO_FILTER_ODD,   value);
 	dprintk(1,"set_scale: filter  0x%04x\n", value);
@@ -564,11 +662,25 @@ static int set_scale(struct cx8800_dev *
 
 static int video_mux(struct cx8800_dev *dev, unsigned int input)
 {
-	dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x]\n",
-		input, INPUT(input)->vmux, INPUT(input)->gpio0);
+	dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",
+		input, INPUT(input)->vmux,
+		INPUT(input)->gpio0,INPUT(input)->gpio1,
+		INPUT(input)->gpio2,INPUT(input)->gpio3);
 	dev->input = input;
 	cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input)->vmux << 14);
 	cx_write(MO_GP0_IO, INPUT(input)->gpio0);
+	cx_write(MO_GP1_IO, INPUT(input)->gpio1);
+	cx_write(MO_GP2_IO, INPUT(input)->gpio2);
+	cx_write(MO_GP3_IO, INPUT(input)->gpio3);
+
+	switch (INPUT(input)->type) {
+	case CX88_VMUX_SVIDEO:
+		cx_andor(MO_AFECFG_IO, 0x01, 0x01);
+		break;
+	default:
+		cx_andor(MO_AFECFG_IO, 0x01, 0x00);
+		break;
+	}
 	return 0;
 }
 
@@ -721,6 +833,20 @@ buffer_prepare(struct file *file, struct
 					 buf->bpl, buf->bpl,
 					 buf->vb.height >> 1);
 			break;
+		case V4L2_FIELD_SEQ_TB:
+			cx88_risc_buffer(dev->pci, &buf->risc,
+					 buf->vb.dma.sglist,
+					 0, buf->bpl * (buf->vb.height >> 1),
+					 buf->bpl, 0,
+					 buf->vb.height >> 1);
+			break;
+		case V4L2_FIELD_SEQ_BT:
+			cx88_risc_buffer(dev->pci, &buf->risc,
+					 buf->vb.dma.sglist,
+					 buf->bpl * (buf->vb.height >> 1), 0,
+					 buf->bpl, 0,
+					 buf->vb.height >> 1);
+			break;
 		default:
 			BUG();
 		}
@@ -795,7 +921,7 @@ static void buffer_release(struct file *
 	cx88_free_buffer(fh->dev->pci,buf);
 }
 
-static struct videobuf_queue_ops cx8800_video_qops = {
+struct videobuf_queue_ops cx8800_video_qops = {
 	.buf_setup    = buffer_setup,
 	.buf_prepare  = buffer_prepare,
 	.buf_queue    = buffer_queue,
@@ -1032,19 +1158,51 @@ static int setup_window(struct cx8800_de
 
 /* ------------------------------------------------------------------ */
 
+static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
+{
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &fh->vidq;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		return &fh->vbiq;
+	default:
+		BUG();
+		return NULL;
+	}
+}
+
+static int get_ressource(struct cx8800_fh *fh)
+{
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return RESOURCE_VIDEO;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		return RESOURCE_VBI;
+	default:
+		BUG();
+		return 0;
+	}
+}
+
 static int video_open(struct inode *inode, struct file *file)
 {
 	int minor = iminor(inode);
 	struct cx8800_dev *h,*dev = NULL;
 	struct cx8800_fh *fh;
 	struct list_head *list;
-	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	enum v4l2_buf_type type = 0;
 	int radio = 0;
 	
 	list_for_each(list,&cx8800_devlist) {
 		h = list_entry(list, struct cx8800_dev, devlist);
-		if (h->video_dev->minor == minor)
-			dev = h;
+		if (h->video_dev->minor == minor) {
+			dev  = h;
+			type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		}
+		if (h->vbi_dev->minor == minor) {
+			dev  = h;
+			type = V4L2_BUF_TYPE_VBI_CAPTURE;
+		}
 		if (h->radio_dev &&
 		    h->radio_dev->minor == minor) {
 			radio = 1;
@@ -1075,11 +1233,20 @@ static int video_open(struct inode *inod
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_INTERLACED,
 			    sizeof(struct cx88_buffer));
+	videobuf_queue_init(&fh->vbiq, &cx8800_vbi_qops,
+			    dev->pci, &dev->slock,
+			    V4L2_BUF_TYPE_VBI_CAPTURE,
+			    V4L2_FIELD_SEQ_TB,
+			    sizeof(struct cx88_buffer));
 	init_MUTEX(&fh->vidq.lock);
+	init_MUTEX(&fh->vbiq.lock);
 
 	if (fh->radio) {
 		dprintk(1,"video_open: setting radio device\n");
 		cx_write(MO_GP0_IO, cx88_boards[dev->board].radio.gpio0);
+		cx_write(MO_GP1_IO, cx88_boards[dev->board].radio.gpio1);
+		cx_write(MO_GP2_IO, cx88_boards[dev->board].radio.gpio2);
+		cx_write(MO_GP3_IO, cx88_boards[dev->board].radio.gpio3);
 		dev->tvaudio = WW_FM;
 		cx88_set_tvaudio(dev);
 		cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO);
@@ -1094,12 +1261,30 @@ video_read(struct file *file, char *data
 {
 	struct cx8800_fh *fh = file->private_data;
 
-	return videobuf_read_one(file, &fh->vidq, data, count, ppos);
+	switch (fh->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (res_locked(fh->dev,RESOURCE_VIDEO))
+			return -EBUSY;
+		return videobuf_read_one(file, &fh->vidq, data, count, ppos);
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		if (!res_get(fh->dev,fh,RESOURCE_VBI))
+			return -EBUSY;
+		return videobuf_read_stream(file, &fh->vbiq, data, count, ppos, 1);
+	default:
+		BUG();
+		return 0;
+	}
 }
 
 static unsigned int
 video_poll(struct file *file, struct poll_table_struct *wait)
 {
+	struct cx8800_fh *fh = file->private_data;
+
+	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
+		return videobuf_poll_stream(file, &fh->vbiq, wait);
+
+	/* FIXME */
 	return POLLERR;
 }
 
@@ -1124,6 +1309,15 @@ static int video_release(struct inode *i
 		kfree(fh->vidq.read_buf);
 	}
 
+	/* stop vbi capture */
+	if (res_check(fh, RESOURCE_VBI)) {
+		if (fh->vbiq.streaming)
+			videobuf_streamoff(file,&fh->vbiq);
+		if (fh->vbiq.reading)
+			videobuf_read_stop(file,&fh->vbiq);
+		res_free(dev,fh,RESOURCE_VBI);
+	}
+
 	file->private_data = NULL;
 	kfree(fh);
 	return 0;
@@ -1134,7 +1328,7 @@ video_mmap(struct file *file, struct vm_
 {
 	struct cx8800_fh *fh = file->private_data;
 
-	return videobuf_mmap_mapper(vma,&fh->vidq);
+	return videobuf_mmap_mapper(vma, get_queue(fh));
 }
 
 /* ------------------------------------------------------------------ */
@@ -1151,14 +1345,22 @@ static int get_control(struct cx8800_dev
 	if (NULL == c)
 		return -EINVAL;
 
-	value = cx_read(c->reg);
-	ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
+	value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg);
+	switch (ctl->id) {
+	case V4L2_CID_AUDIO_BALANCE:
+		ctl->value = (value & 0x40) ? (value & 0x3f) : (0x40 - (value & 0x3f));
+		break;
+	default:
+		ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
+		break;
+	}
 	return 0;
 }
 
 static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
 {
 	struct cx88_ctrl *c = NULL;
+        u32 v_sat_value;
 	u32 value;
 	int i;
 
@@ -1172,11 +1374,48 @@ static int set_control(struct cx8800_dev
 		return -ERANGE;
 	if (ctl->value > c->v.maximum)
 		return -ERANGE;
-	value = ((ctl->value - c->off) << c->shift) & c->mask;
-	cx_andor(c->reg, c->mask, value);
+	switch (ctl->id) {
+	case V4L2_CID_AUDIO_BALANCE:
+		value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value;
+		break;
+	case V4L2_CID_SATURATION:
+		/* special v_sat handling */
+		v_sat_value = ctl->value - (0x7f - 0x5a);
+		if (v_sat_value > 0xff)
+			v_sat_value = 0xff;
+		if (v_sat_value < 0x00)
+			v_sat_value = 0x00;
+		cx_andor(MO_UV_SATURATION, 0xff00, v_sat_value << 8);
+		/* fall through to default route for u_sat */
+	default:
+		value = ((ctl->value - c->off) << c->shift) & c->mask;
+		break;
+	}
+	dprintk(1,"set_control id=0x%X reg=0x%x val=0x%x%s\n",
+		ctl->id, c->reg, value, c->sreg ? " [shadowed]" : "");
+	if (c->sreg) {
+		cx_sandor(c->sreg, c->reg, c->mask, value);
+	} else {
+		cx_andor(c->reg, c->mask, value);
+	}
 	return 0;
 }
 
+static void init_controls(struct cx8800_dev *dev)
+{
+	static struct v4l2_control mute = {
+		.id    = V4L2_CID_AUDIO_MUTE,
+		.value = 1,
+	};
+	static struct v4l2_control volume = {
+		.id    = V4L2_CID_AUDIO_VOLUME,
+		.value = 0,
+	};
+
+	set_control(dev,&mute);
+	set_control(dev,&volume);
+}
+
 /* ------------------------------------------------------------------ */
 
 static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
@@ -1194,6 +1433,9 @@ static int cx8800_g_fmt(struct cx8800_de
 		f->fmt.pix.sizeimage =
 			f->fmt.pix.height * f->fmt.pix.bytesperline;
 		return 0;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		cx8800_vbi_fmt(dev, f);
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -1253,6 +1495,9 @@ static int cx8800_try_fmt(struct cx8800_
 
 		return 0;
 	}
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		cx8800_vbi_fmt(dev, f);
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -1274,6 +1519,9 @@ static int cx8800_s_fmt(struct cx8800_de
 		fh->height     = f->fmt.pix.height;
 		fh->vidq.field = f->fmt.pix.field;
 		return 0;
+	case V4L2_BUF_TYPE_VBI_CAPTURE:
+		cx8800_vbi_fmt(dev, f);
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -1311,9 +1559,9 @@ static int video_do_ioctl(struct inode *
 			V4L2_CAP_VIDEO_CAPTURE |
 			V4L2_CAP_READWRITE     |
 			V4L2_CAP_STREAMING     |
+			V4L2_CAP_VBI_CAPTURE   |
 #if 0
 			V4L2_CAP_VIDEO_OVERLAY |
-			V4L2_CAP_VBI_CAPTURE   |
 #endif
 			0;
 		if (UNSET != dev->tuner_type)
@@ -1541,30 +1789,58 @@ static int video_do_ioctl(struct inode *
 	}
 
 	/* --- streaming capture ------------------------------------- */
+	case VIDIOCGMBUF:
+	{
+		struct video_mbuf *mbuf = arg;
+		struct videobuf_queue *q;
+		struct v4l2_requestbuffers req;
+		unsigned int i;
+
+		q = get_queue(fh);
+		memset(&req,0,sizeof(req));
+		req.type   = q->type;
+		req.count  = 8;
+		req.memory = V4L2_MEMORY_MMAP;
+		err = videobuf_reqbufs(file,q,&req);
+		if (err < 0)
+			return err;
+		memset(mbuf,0,sizeof(*mbuf));
+		mbuf->frames = req.count;
+		mbuf->size   = 0;
+		for (i = 0; i < mbuf->frames; i++) {
+			mbuf->offsets[i]  = q->bufs[i]->boff;
+			mbuf->size       += q->bufs[i]->bsize;
+		}
+		return 0;
+	}
 	case VIDIOC_REQBUFS:
-		return videobuf_reqbufs(file,&fh->vidq,arg);
+		return videobuf_reqbufs(file, get_queue(fh), arg);
 
 	case VIDIOC_QUERYBUF:
-		return videobuf_querybuf(&fh->vidq,arg);
+		return videobuf_querybuf(get_queue(fh), arg);
 
 	case VIDIOC_QBUF:
-		return videobuf_qbuf(file,&fh->vidq,arg);
+		return videobuf_qbuf(file, get_queue(fh), arg);
 
 	case VIDIOC_DQBUF:
-		return videobuf_dqbuf(file,&fh->vidq,arg);
+		return videobuf_dqbuf(file, get_queue(fh), arg);
 
 	case VIDIOC_STREAMON:
 	{
-                if (!res_get(dev,fh,RESOURCE_VIDEO))
+		int res = get_ressource(fh);
+
+                if (!res_get(dev,fh,res))
 			return -EBUSY;
-		return videobuf_streamon(file,&fh->vidq);
+		return videobuf_streamon(file, get_queue(fh));
 	}
 	case VIDIOC_STREAMOFF:
 	{
-		err = videobuf_streamoff(file,&fh->vidq);
+		int res = get_ressource(fh);
+		
+		err = videobuf_streamoff(file, get_queue(fh));
 		if (err < 0)
 			return err;
-		res_free(dev,fh,RESOURCE_VIDEO);
+		res_free(dev,fh,res);
 		return 0;
 	}
 
@@ -1726,9 +2002,34 @@ static void cx8800_vid_timeout(unsigned 
 	spin_unlock_irqrestore(&dev->slock,flags);
 }
 
-static void cx8800_vid_irq(struct cx8800_dev *dev)
+static void cx8800_wakeup(struct cx8800_dev *dev,
+			  struct cx88_dmaqueue *q, u32 count)
 {
 	struct cx88_buffer *buf;
+
+	for (;;) {
+		if (list_empty(&q->active))
+			break;
+		buf = list_entry(q->active.next,
+				 struct cx88_buffer, vb.queue);
+		if (buf->count > count)
+			break;
+		do_gettimeofday(&buf->vb.ts);
+		dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
+			count, buf->count);
+		buf->vb.state = STATE_DONE;
+		list_del(&buf->vb.queue);
+		wake_up(&buf->vb.done);
+	}
+	if (list_empty(&q->active)) {
+		del_timer(&q->timeout);
+	} else {
+		mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+	}
+}
+
+static void cx8800_vid_irq(struct cx8800_dev *dev)
+{
 	u32 status, mask, count;
 
 	status = cx_read(MO_VID_INTSTAT);
@@ -1752,35 +2053,33 @@ static void cx8800_vid_irq(struct cx8800
 	if (status & 0x01) {
 		spin_lock(&dev->slock);
 		count = cx_read(MO_VIDY_GPCNT);
-		for (;;) {
-			if (list_empty(&dev->vidq.active))
-				break;
-			buf = list_entry(dev->vidq.active.next,
-					 struct cx88_buffer, vb.queue);
-			if (buf->count > count)
-				break;
-			do_gettimeofday(&buf->vb.ts);
-			dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
-				count, buf->count);
-			buf->vb.state = STATE_DONE;
-			list_del(&buf->vb.queue);
-			wake_up(&buf->vb.done);
-		}
-		if (list_empty(&dev->vidq.active)) {
-			del_timer(&dev->vidq.timeout);
-		} else {
-			mod_timer(&dev->vidq.timeout, jiffies+BUFFER_TIMEOUT);
-		}
+		cx8800_wakeup(dev, &dev->vidq, count);
+		spin_unlock(&dev->slock);
+	}
+
+	/* risc1 vbi */
+	if (status & 0x08) {
+		spin_lock(&dev->slock);
+		count = cx_read(MO_VBI_GPCNT);
+		cx8800_wakeup(dev, &dev->vbiq, count);
 		spin_unlock(&dev->slock);
 	}
 
 	/* risc2 y */
 	if (status & 0x10) {
-		dprintk(2,"stopper\n");
+		dprintk(2,"stopper video\n");
 		spin_lock(&dev->slock);
 		restart_video_queue(dev,&dev->vidq);
 		spin_unlock(&dev->slock);
 	}
+
+	/* risc2 vbi */
+	if (status & 0x80) {
+		dprintk(2,"stopper vbi\n");
+		spin_lock(&dev->slock);
+		cx8800_restart_vbi_queue(dev,&dev->vbiq);
+		spin_unlock(&dev->slock);
+	}
 }
 
 static irqreturn_t cx8800_irq(int irq, void *dev_id, struct pt_regs *regs)
@@ -1831,8 +2130,16 @@ static struct file_operations video_fops
 struct video_device cx8800_video_template =
 {
 	.name          = "cx8800-video",
-	.type          = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY|
-	                 VID_TYPE_CLIPPING|VID_TYPE_SCALES,
+	.type          = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES,
+	.hardware      = 0,
+	.fops          = &video_fops,
+	.minor         = -1,
+};
+
+struct video_device cx8800_vbi_template =
+{
+	.name          = "cx8800-vbi",
+	.type          = VID_TYPE_TELETEXT|VID_TYPE_TUNER,
 	.hardware      = 0,
 	.fops          = &video_fops,
 	.minor         = -1,
@@ -1958,6 +2265,13 @@ static void cx8800_unregister_video(stru
 			video_device_release(dev->radio_dev);
 		dev->radio_dev = NULL;
 	}
+	if (dev->vbi_dev) {
+		if (-1 != dev->vbi_dev->minor)
+			video_unregister_device(dev->vbi_dev);
+		else
+			video_device_release(dev->vbi_dev);
+		dev->vbi_dev = NULL;
+	}
 	if (dev->video_dev) {
 		if (-1 != dev->video_dev->minor)
 			video_unregister_device(dev->video_dev);
@@ -1967,6 +2281,11 @@ static void cx8800_unregister_video(stru
 	}
 }
 
+/* debug that damn oops ... */
+static unsigned int oops = 0;
+MODULE_PARM(oops,"i");
+#define OOPS(msg) if (oops) printk("%s: %s\n",__FUNCTION__,msg);
+
 static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
 				    const struct pci_device_id *pci_id)
 {
@@ -1980,6 +2299,7 @@ static int __devinit cx8800_initdev(stru
 	memset(dev,0,sizeof(*dev));
 
 	/* pci init */
+	OOPS("pci init");
 	dev->pci = pci_dev;
 	if (pci_enable_device(pci_dev)) {
 		err = -EIO;
@@ -1988,6 +2308,7 @@ static int __devinit cx8800_initdev(stru
 	sprintf(dev->name,"cx%x[%d]",pci_dev->device,cx8800_devcount);
 
 	/* pci quirks */
+	OOPS("pci quirks");
 	cx88_pci_quirks(dev->name, dev->pci, &latency);
 	if (UNSET != latency) {
 		printk(KERN_INFO "%s: setting pci latency timer to %d\n",
@@ -1996,6 +2317,7 @@ static int __devinit cx8800_initdev(stru
 	}
 
 	/* print pci info */
+	OOPS("pci info");
 	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
         pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
         printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
@@ -2011,6 +2333,7 @@ static int __devinit cx8800_initdev(stru
 	}
 
 	/* board config */
+	OOPS("board config");
 	dev->board = card[cx8800_devcount];
 	for (i = 0; UNSET == dev->board  &&  i < cx88_idcount; i++) 
 		if (pci_dev->subsystem_vendor == cx88_subids[i].subvendor &&
@@ -2029,6 +2352,7 @@ static int __devinit cx8800_initdev(stru
 		dev->tuner_type = cx88_boards[dev->board].tuner_type;
 
 	/* get mmio */
+	OOPS("get mmio");
 	if (!request_mem_region(pci_resource_start(pci_dev,0),
 				pci_resource_len(pci_dev,0),
 				dev->name)) {
@@ -2039,13 +2363,15 @@ static int __devinit cx8800_initdev(stru
 	}
 	dev->lmmio = ioremap(pci_resource_start(pci_dev,0),
 			     pci_resource_len(pci_dev,0));
+	dev->bmmio = (u8*)dev->lmmio;
 
 	/* initialize driver struct */
+	OOPS("init structs");
         init_MUTEX(&dev->lock);
 	dev->slock = SPIN_LOCK_UNLOCKED;
 	dev->tvnorm = tvnorms;
 
-	/* init dma queues */
+	/* init video dma queues */
 	INIT_LIST_HEAD(&dev->vidq.active);
 	INIT_LIST_HEAD(&dev->vidq.queued);
 	dev->vidq.timeout.function = cx8800_vid_timeout;
@@ -2053,11 +2379,22 @@ static int __devinit cx8800_initdev(stru
 	init_timer(&dev->vidq.timeout);
 	cx88_risc_stopper(dev->pci,&dev->vidq.stopper,
 			  MO_VID_DMACNTRL,0x11,0x00);
-	
+
+	/* init vbi dma queues */
+	INIT_LIST_HEAD(&dev->vbiq.active);
+	INIT_LIST_HEAD(&dev->vbiq.queued);
+	dev->vbiq.timeout.function = cx8800_vbi_timeout;
+	dev->vbiq.timeout.data     = (unsigned long)dev;
+	init_timer(&dev->vbiq.timeout);
+	cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
+			  MO_VID_DMACNTRL,0x88,0x00);
+
 	/* initialize hardware */
+	OOPS("reset hardware");
 	cx8800_reset(dev);
 
 	/* get irq */
+	OOPS("install irq handler");
 	err = request_irq(pci_dev->irq, cx8800_irq,
 			  SA_SHIRQ | SA_INTERRUPT, dev->name, dev);
 	if (err < 0) {
@@ -2067,16 +2404,22 @@ static int __devinit cx8800_initdev(stru
 	}
 
 	/* register i2c bus + load i2c helpers */
+	OOPS("i2c setup");
 	cx8800_i2c_init(dev);
+	OOPS("card setup");
 	cx88_card_setup(dev);
 
 	/* load and configure helper modules */
+	OOPS("configure i2c clients");
 	if (TUNER_ABSENT != dev->tuner_type)
 		request_module("tuner");
+	if (cx88_boards[dev->board].needs_tda9887)
+		request_module("tda9887");
 	if (dev->tuner_type != UNSET)
 		cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
 
 	/* register v4l devices */
+	OOPS("register video");
 	dev->video_dev = vdev_init(dev,&cx8800_video_template,"video");
 	err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
 				    video_nr[cx8800_devcount]);
@@ -2088,7 +2431,20 @@ static int __devinit cx8800_initdev(stru
 	printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
 	       dev->name,dev->video_dev->minor & 0x1f);
 
+	OOPS("register vbi");
+	dev->vbi_dev = vdev_init(dev,&cx8800_vbi_template,"vbi");
+	err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+				    vbi_nr[cx8800_devcount]);
+	if (err < 0) {
+		printk(KERN_INFO "%s: can't register vbi device\n",
+		       dev->name);
+		goto fail3;
+	}
+	printk(KERN_INFO "%s: registered device vbi%d\n",
+	       dev->name,dev->vbi_dev->minor & 0x1f);
+
 	if (dev->has_radio) {
+		OOPS("register radio");
 		dev->radio_dev = vdev_init(dev,&cx8800_radio_template,"radio");
 		err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
 					    radio_nr[cx8800_devcount]);
@@ -2102,26 +2458,32 @@ static int __devinit cx8800_initdev(stru
 	}
 
 	/* everything worked */
+	OOPS("finalize");
 	list_add_tail(&dev->devlist,&cx8800_devlist);
 	pci_set_drvdata(pci_dev,dev);
 	cx8800_devcount++;
 
 	/* initial device configuration */
+	OOPS("init device");
 	down(&dev->lock);
+	init_controls(dev);
 	set_tvnorm(dev,tvnorms);
 	video_mux(dev,0);
 	up(&dev->lock);
 	return 0;
 
  fail3:
+	OOPS("fail3");
 	cx8800_unregister_video(dev);
 	if (0 == dev->i2c_rc)
 		i2c_bit_del_bus(&dev->i2c_adap);
 	free_irq(pci_dev->irq, dev);
  fail2:
+	OOPS("fail2");
 	release_mem_region(pci_resource_start(pci_dev,0),
 			   pci_resource_len(pci_dev,0));
  fail1:
+	OOPS("fail1");
 	kfree(dev);
 	return err;
 }
diff -up linux-2.6.5/drivers/media/video/cx88/cx88.h linux/drivers/media/video/cx88/cx88.h
--- linux-2.6.5/drivers/media/video/cx88/cx88.h	2004-04-05 10:43:37.336044658 +0200
+++ linux/drivers/media/video/cx88/cx88.h	2004-04-05 10:50:14.833092462 +0200
@@ -1,7 +1,7 @@
 /*
- * v4l2 device driver for philips saa7134 based TV cards
+ * v4l2 device driver for cx2388x based TV cards
  *
- * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@
 #include "cx88-reg.h"
 
 #include <linux/version.h>
-#define CX88_VERSION_CODE KERNEL_VERSION(0,0,1)
+#define CX88_VERSION_CODE KERNEL_VERSION(0,0,3)
 
 #ifndef TRUE
 # define TRUE (1==1)
@@ -50,6 +50,14 @@
 #define FORMAT_FLAGS_PACKED       0x01
 #define FORMAT_FLAGS_PLANAR       0x02
 
+#define VBI_LINE_COUNT              17
+#define VBI_LINE_LENGTH           2048
+
+/* need "shadow" registers for some write-only ones ... */
+#define SHADOW_AUD_VOL_CTL           1
+#define SHADOW_AUD_BAL_CTL           2
+#define SHADOW_MAX                   2
+
 /* ----------------------------------------------------------- */
 /* static data                                                 */
 
@@ -72,6 +80,7 @@ struct cx88_ctrl {
 	struct v4l2_queryctrl  v;
 	u32                    off;
 	u32                    reg;
+	u32                    sreg;
 	u32                    mask;
 	u32                    shift;
 };
@@ -91,6 +100,7 @@ struct sram_channel {
 	char *name;
 	u32  cmds_start;
 	u32  ctrl_start;
+	u32  cdt;
 	u32  fifo_start;
 	u32  fifo_size;
 	u32  ptr1_reg;
@@ -113,6 +123,7 @@ extern struct sram_channel cx88_sram_cha
 #define CX88_BOARD_AVERTV_303        6
 #define CX88_BOARD_MSI_TVANYWHERE    7
 #define CX88_BOARD_WINFAST_DV2000    8
+#define CX88_BOARD_LEADTEK_PVR2000   9
 
 
 enum cx88_itype {
@@ -129,12 +140,13 @@ enum cx88_itype {
 struct cx88_input {
 	enum cx88_itype type;
 	unsigned int    vmux;
-	u32             gpio0;
+	u32             gpio0, gpio1, gpio2, gpio3;
 };
 
 struct cx88_board {
 	char                    *name;
 	unsigned int            tuner_type;
+	int                     needs_tda9887:1;
 	struct cx88_input       input[8];
 	struct cx88_input       radio;
 };
@@ -195,6 +207,9 @@ struct cx8800_fh {
 	struct cx8800_fmt          *fmt;
 	unsigned int               width,height;
 	struct videobuf_queue      vidq;
+
+	/* vbi capture */
+	struct videobuf_queue      vbiq;
 };
 
 struct cx8800_suspend_state {
@@ -211,6 +226,7 @@ struct cx8800_dev {
 	/* various device info */
 	unsigned int               resources;
 	struct video_device        *video_dev;
+	struct video_device        *vbi_dev;
 	struct video_device        *radio_dev;
 
 	/* pci i/o */
@@ -218,6 +234,7 @@ struct cx8800_dev {
 	struct pci_dev             *pci;
 	unsigned char              pci_rev,pci_lat;
         u32                        *lmmio;
+        u8                         *bmmio;
 
 	/* config info */
 	unsigned int               board;
@@ -236,6 +253,7 @@ struct cx8800_dev {
 
 	/* capture queues */
 	struct cx88_dmaqueue       vidq;
+	struct cx88_dmaqueue       vbiq;
 
 	/* various v4l controls */
 	struct cx8800_tvnorm       *tvnorm;
@@ -244,6 +262,7 @@ struct cx8800_dev {
 	u32                        freq;
 
 	/* other global state info */
+	u32                         shadow[SHADOW_MAX];
 	struct cx8800_suspend_state state;
 };
 
@@ -251,6 +270,7 @@ struct cx8800_dev {
 
 #define cx_read(reg)             readl(dev->lmmio + ((reg)>>2))
 #define cx_write(reg,value)      writel((value), dev->lmmio + ((reg)>>2));
+#define cx_writeb(reg,value)     writeb((value), dev->bmmio + (reg));
 
 #define cx_andor(reg,mask,value) \
   writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
@@ -258,8 +278,16 @@ struct cx8800_dev {
 #define cx_set(reg,bit)          cx_andor((reg),(bit),(bit))
 #define cx_clear(reg,bit)        cx_andor((reg),(bit),0)
 
-#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d);}
+#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); }
 
+/* shadow registers */
+#define cx_sread(sreg)		    (dev->shadow[sreg])
+#define cx_swrite(sreg,reg,value) \
+  (dev->shadow[sreg] = value, \
+   writel(dev->shadow[sreg], dev->lmmio + ((reg)>>2)))
+#define cx_sandor(sreg,reg,mask,value) \
+  (dev->shadow[sreg] = (dev->shadow[sreg] & ~(mask)) | ((value) & (mask)), \
+   writel(dev->shadow[sreg], dev->lmmio + ((reg)>>2)))
 
 /* ----------------------------------------------------------- */
 /* cx88-core.c                                                 */
@@ -294,6 +322,19 @@ extern int cx88_pci_quirks(char *name, s
 			   unsigned int *latency);
 
 /* ----------------------------------------------------------- */
+/* cx88-vbi.c                                                  */
+
+void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f);
+int cx8800_start_vbi_dma(struct cx8800_dev    *dev,
+			 struct cx88_dmaqueue *q,
+			 struct cx88_buffer   *buf);
+int cx8800_restart_vbi_queue(struct cx8800_dev    *dev,
+			     struct cx88_dmaqueue *q);
+void cx8800_vbi_timeout(unsigned long data);
+
+extern struct videobuf_queue_ops cx8800_vbi_qops;
+
+/* ----------------------------------------------------------- */
 /* cx88-i2c.c                                                  */
 
 extern int cx8800_i2c_init(struct cx8800_dev *dev);
@@ -310,7 +351,7 @@ extern const unsigned int cx88_bcount;
 extern struct cx88_subid cx88_subids[];
 extern const unsigned int cx88_idcount;
 
-extern void cx88_card_setup(struct cx8800_dev *dev);
+extern void __devinit cx88_card_setup(struct cx8800_dev *dev);
 
 /* ----------------------------------------------------------- */
 /* cx88-tvaudio.c                                              */

-- 
http://bigendian.bytesex.org

                 reply	other threads:[~2004-04-05 12:45 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20040405123339.GA30083@bytesex.org \
    --to=kraxel@bytesex.org \
    --cc=akpm@osdl.org \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.