diff -upN reference/drivers/media/Kconfig current/drivers/media/Kconfig
--- reference/drivers/media/Kconfig	2004-02-04 16:24:06.000000000 -0800
+++ current/drivers/media/Kconfig	2004-03-30 21:42:12.000000000 -0800
@@ -34,14 +34,15 @@ source "drivers/media/common/Kconfig"
 
 config VIDEO_TUNER
 	tristate
-	default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_MXB=y || VIDEO_CX88=y
-	default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_MXB=m || VIDEO_CX88=m
+	default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_MXB=y || VIDEO_CX88=y || VIDEO_IVTV=y
+	default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_MXB=m || VIDEO_CX88=m || VIDEO_IVTV=m
+
 	depends on VIDEO_DEV
 
 config VIDEO_BUF
 	tristate
-	default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_SAA7146=y || VIDEO_CX88=y
-	default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_SAA7146=m || VIDEO_CX88=m
+	default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_SAA7146=y || VIDEO_CX88=y || VIDEO_IVTV=y
+	default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_SAA7146=m || VIDEO_CX88=m || VIDEO_IVTV=m
 	depends on VIDEO_DEV
 
 config VIDEO_BTCX
diff -upN reference/drivers/media/video/Kconfig current/drivers/media/video/Kconfig
--- reference/drivers/media/video/Kconfig	2004-03-11 14:34:34.000000000 -0800
+++ current/drivers/media/video/Kconfig	2004-03-30 21:42:12.000000000 -0800
@@ -22,6 +22,25 @@ config VIDEO_BT848
 	  To compile this driver as a module, choose M here: the
 	  module will be called bttv.
 
+config VIDEO_IVTV
+	tristate "IVTV Video For Linux"
+	depends on VIDEO_DEV && PCI && I2C_ALGOBIT && SOUND
+	---help---
+	  Support for Hauppauge WinTv PVR 250 and 350 boards.
+
+	  If you say Y or M here, you need to say Y or M to "I2C support" and
+	  "I2C bit-banging interfaces" in the character device section.
+
+	  Saying M here will compile this driver as a module (ivtv).
+
+config VIDEO_IVTV_FB
+	tristate "IVTV Video For Linux Framebuffer"
+	depends on VIDEO_IVTV && FB && PCI
+	---help---
+	  Support for Hauppauge WinTv PVR 350 boards TV Out via framebuffer.
+
+	  Saying M here will compile this driver as a module (ivtv-fb).
+
 config VIDEO_PMS
 	tristate "Mediavision Pro Movie Studio Video For Linux"
 	depends on VIDEO_DEV && ISA
diff -upN reference/drivers/media/video/Makefile current/drivers/media/video/Makefile
--- reference/drivers/media/video/Makefile	2004-03-11 14:34:35.000000000 -0800
+++ current/drivers/media/video/Makefile	2004-03-30 21:42:12.000000000 -0800
@@ -8,12 +8,16 @@ zoran-objs      :=	zr36120.o zr36120_i2c
 zr36067-objs	:=	zoran_procfs.o zoran_device.o \
 			zoran_driver.o zoran_card.o
 
+ivtv-objs	:=  ivtv-driver.o ivtv-i2c.o ivtv-api.o
+
 obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o
 
 obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \
 	tda7432.o tda9875.o ir-kbd-i2c.o ir-kbd-gpio.o
 obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
 
+obj-$(CONFIG_VIDEO_IVTV) += msp3400.o saa7115.o tveeprom.o ivtv.o saa7127.o
+obj-$(CONFIG_VIDEO_IVTV_FB) += ivtv-fb.o
 obj-$(CONFIG_VIDEO_ZR36120) += zoran.o
 obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
 obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
diff -upN /dev/null current/drivers/media/video/ivtv-api.c
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/ivtv-api.c	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,3151 @@
+/* License: GPL
+ * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
+ *
+ * This file will hold API related functions, both internal (firmware api)
+ * and external (v4l2, etc)
+ *
+ */
+
+#include "ivtv.h"
+
+/* Fix the v4l2 api breakage - need to define if still using the old api */
+#ifndef VIDIOC_OVERLAY_OLD
+#define VIDIOC_OVERLAY_OLD		_IOWR ('V', 14, int)
+#define VIDIOC_S_PARM_OLD		_IOW  ('V', 22, struct v4l2_streamparm)
+#define VIDIOC_S_CTRL_OLD		_IOW  ('V', 28, struct v4l2_control)
+#define VIDIOC_G_AUDIO_OLD		_IOWR ('V', 33, struct v4l2_audio)
+#define VIDIOC_G_AUDOUT_OLD		_IOWR ('V', 49, struct v4l2_audioout)
+#endif
+
+/* FIXME need to find a good value */
+#define V4L2_PIX_FMT_CMP_MPG2 77777
+#define IVTV_V4L2_MAX_MINOR 15
+
+static int ivtv_v4l2_init(struct video_device *v);
+static int ivtv_v4l2_close(struct inode *inode, struct file *filp);
+static int ivtv_v4l2_open(struct inode *inode, struct file *filp);
+static int ivtv_v4l2_read(struct file *filp, char *buf, size_t count,
+			  loff_t * pos);
+static ssize_t ivtv_v4l2_write(struct file *filp, const char *buf, size_t count,
+			       loff_t * pos);
+static int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+static int ivtv_v4l2_pre_init(struct ivtv *itv);
+
+struct file_operations ivtv_v4l2_fops = {
+      owner:THIS_MODULE,
+      read:ivtv_v4l2_read,
+      write:ivtv_v4l2_write,
+      open:ivtv_v4l2_open,
+      ioctl:ivtv_v4l2_ioctl,
+      release:ivtv_v4l2_close,
+      poll:ivtv_poll,
+};
+
+/* FIXME  Static variables for the various card types go here */
+
+static struct video_device tmk_v4l2dev = {	/*values that work with the author's card */
+	.owner = THIS_MODULE,
+	.name = "Vanilla iTVC15 card",
+	.type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_OVERLAY |
+	    VID_TYPE_CLIPPING | VID_TYPE_SCALES,
+	.fops = &ivtv_v4l2_fops,
+	.minor = -1,
+};
+
+/* some tuner table values can change, so allocate this dynamically when you use it*/
+struct v4l2_tuner tmk_tuners[2] = {
+	{
+	 .index = 0,
+	 .name = "ivtv TV Tuner",
+	 .type = V4L2_TUNER_ANALOG_TV,
+	 .capability = (V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO),
+	 .rxsubchans = (V4L2_TUNER_SUB_STEREO),
+	 .audmode = V4L2_TUNER_MODE_STEREO,
+	 .signal = 0,
+	 .afc = 0,
+	 .reserved = {0, 0, 0, 0}
+	 }, {
+	     .index = 1,
+	     .name = "ivtv Radio",
+	     .type = V4L2_TUNER_RADIO,
+	     .capability = (V4L2_TUNER_CAP_STEREO),
+	     .rxsubchans = 0,
+	     .audmode = V4L2_TUNER_MODE_STEREO,
+	     .signal = 0,
+	     .afc = 0,
+	     .reserved = {0, 0, 0, 0}
+	     }
+};
+
+struct v4l2_standard tmk_standards[3] = {
+	{
+	 .index = 0,
+	 .id = V4L2_STD_NTSC,
+	 .name = "NTSC",
+	 .frameperiod = {.numerator = 1001,
+			 .denominator = 30000},
+	 .framelines = 525,
+	 .reserved = {0, 0, 0, 0}
+	 }, {
+	     .index = 1,
+	     .id = V4L2_STD_PAL,
+	     .name = "PAL",
+	     .frameperiod = {.numerator = 1,
+			     .denominator = 25},
+	     .framelines = 625,
+	     .reserved = {0, 0, 0, 0}
+	     }, {
+		 .index = 2,
+		 .id = V4L2_STD_SECAM,
+		 .name = "SECAM",
+		 .frameperiod = {.numerator = 1,
+				 .denominator = 25},
+		 .framelines = 625,
+		 .reserved = {0, 0, 0, 0}
+		 }
+};
+
+struct v4l2_input tmk_inputs[10] = {	/*values that work with the author's card */
+	{
+	 .index = 0,
+	 .name = "Composite 0",
+	 .type = V4L2_INPUT_TYPE_CAMERA,
+	 .audioset = 1,
+	 .tuner = 0,
+	 .status = 0,
+	 }, {
+	     .index = 1,
+	     .name = "Composite 1",
+	     .type = V4L2_INPUT_TYPE_CAMERA,
+	     .audioset = 1,
+	     .tuner = 0,
+	     .status = 0,
+	     }, {
+		 .index = 2,
+		 .name = "Composite 2",
+		 .type = V4L2_INPUT_TYPE_CAMERA,
+		 .audioset = 1,
+		 .tuner = 0,
+		 .status = 0,
+		 }, {
+		     .index = 3,
+		     .name = "Composite 3",
+		     .type = V4L2_INPUT_TYPE_CAMERA,
+		     .audioset = 1,
+		     .tuner = 0,
+		     .status = 0,
+		     }, {
+			 .index = 4,
+			 .name = "Tuner 0",
+			 .type = V4L2_INPUT_TYPE_TUNER,
+			 .audioset = 0,
+			 .tuner = 0,
+			 .status = 0,
+			 }, {
+			     .index = 5,
+			     .name = "Composite 4",
+			     .type = V4L2_INPUT_TYPE_CAMERA,
+			     .audioset = 1,
+			     .tuner = 0,
+			     .status = 0,
+			     }, {
+				 .index = 6,
+				 .name = "S-Video 0",
+				 .type = V4L2_INPUT_TYPE_CAMERA,
+				 .audioset = 1,
+				 .tuner = 0,
+				 .status = 0,
+				 }, {
+				     .index = 7,
+				     .name = "S-Video 1",
+				     .type = V4L2_INPUT_TYPE_CAMERA,
+				     .audioset = 1,
+				     .tuner = 0,
+				     .status = 0,
+				     }, {
+					 .index = 8,
+					 .name = "S-Video 2",
+					 .type = V4L2_INPUT_TYPE_CAMERA,
+					 .audioset = 1,
+					 .tuner = 0,
+					 .status = 0,
+					 }, {
+					     .index = 9,
+					     .name = "S-Video 3",
+					     .type = V4L2_INPUT_TYPE_CAMERA,
+					     .audioset = 1,
+					     .tuner = 0,
+					     .status = 0,
+					     }
+};
+
+//FIXME capability and mode might be wrong
+struct v4l2_audio tmk_audio_inputs[2] = {
+	{0, "Tuner Audio In", 0, 0,},
+	{1, "Audio Line In", 0, 0,},
+};
+
+int tmk_audio_mapping[] = {
+	0, 3,			/* Input 0 is msp input 3 */
+	1, 1,			/* input 1 is msp input 1 */
+	0, 0			/* you're at end of list! */
+};
+
+struct v4l2_queryctrl ivtv_ctrl_menu_freq = {
+	.id = V4L2_CID_IVTV_FREQ,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Frequency",
+	.minimum = 0,
+	.maximum = 2,
+	.step = 1,
+	.default_value = 2,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_freq[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_FREQ, 0, "32kHz", 0},
+	{V4L2_CID_IVTV_FREQ, 1, "44.1kHz", 0},
+	{V4L2_CID_IVTV_FREQ, 2, "48kHz", 0}
+};
+
+u32 ivtv_audio_tbl_freq[] = {
+	/* setting */
+	0x2 /* 32kHz   binary 10 */ ,
+	0x0 /* 44.1kHz binary 00 */ ,
+	0x1			/* 48kHz   binary 01 */
+};
+
+u32 ivtv_audio_mask_freq = 0x3;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_enc = {
+	.id = V4L2_CID_IVTV_ENC,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Encoding",
+	.minimum = 0,
+	.maximum = 2,
+	.step = 1,
+	.default_value = 1,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_enc[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_ENC, 0, "Layer 1", 0},
+	{V4L2_CID_IVTV_ENC, 1, "Layer 2", 0},
+	{V4L2_CID_IVTV_ENC, 2, "Layer 3(?)", 0}
+};
+
+u32 ivtv_audio_tbl_enc[] = {
+	/* setting */
+	0x1 << 2 /* Layer 1     binary 0100 */ ,
+	0x2 << 2 /* Layer 2     binary 1000 */ ,
+	0x3 << 2		/* Layer 3(?)  binary 1100 */
+};
+
+u32 ivtv_audio_mask_enc = 0xC;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_bitrate = {
+	.id = V4L2_CID_IVTV_BITRATE,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Audio Bitrate",
+	.minimum = 0,
+	.maximum = 14,
+	.step = 1,
+	.default_value = 14,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_bitrate[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_BITRATE, 0, "[L1/L2] Free fmt", 0},
+	{V4L2_CID_IVTV_BITRATE, 1, "[L1/L2] 32k/32k", 0},
+	{V4L2_CID_IVTV_BITRATE, 2, "[L1/L2] 64k/48k", 0},
+	{V4L2_CID_IVTV_BITRATE, 3, "[L1/L2] 96k/56k", 0},
+	{V4L2_CID_IVTV_BITRATE, 4, "[L1/L2] 128k/64k", 0},
+	{V4L2_CID_IVTV_BITRATE, 5, "[L1/L2] 160k/80k", 0},
+	{V4L2_CID_IVTV_BITRATE, 6, "[L1/L2] 192k/96k", 0},
+	{V4L2_CID_IVTV_BITRATE, 7, "[L1/L2] 224k/112k", 0},
+	{V4L2_CID_IVTV_BITRATE, 8, "[L1/L2] 256k/128k", 0},
+	{V4L2_CID_IVTV_BITRATE, 9, "[L1/L2] 288k/160k", 0},
+	{V4L2_CID_IVTV_BITRATE, 10, "[L1/L2] 320k/192k", 0},
+	{V4L2_CID_IVTV_BITRATE, 11, "[L1/L2] 352k/224k", 0},
+	{V4L2_CID_IVTV_BITRATE, 12, "[L1/L2] 384k/256k", 0},
+	{V4L2_CID_IVTV_BITRATE, 13, "[L1/L2] 416k/320k", 0},
+	{V4L2_CID_IVTV_BITRATE, 14, "[L1/L2] 448k/384k", 0},
+};
+
+u32 ivtv_audio_tbl_bitrate[] = {
+	/* setting */
+	0x0 << 4 /* [L1/L2] Free fmt    binary 0000 */ ,
+	0x1 << 4 /* [L1/L2] 32k/32k,   binary 0001 */ ,
+	0x2 << 4 /* [L1/L2] 64k/48k,   binary 0010 */ ,
+	0x3 << 4 /* [L1/L2] 96k/56k,   binary 0011 */ ,
+	0x4 << 4 /* [L1/L2] 128k/64k,  binary 0100 */ ,
+	0x5 << 4 /* [L1/L2] 160k/80k,  binary 0101 */ ,
+	0x6 << 4 /* [L1/L2] 192k/96k,  binary 0110 */ ,
+	0x7 << 4 /* [L1/L2] 224k/112k, binary 0111 */ ,
+	0x8 << 4 /* [L1/L2] 256k/128k, binary 1000 */ ,
+	0x9 << 4 /* [L1/L2] 288k/160k, binary 1001 */ ,
+	0xA << 4 /* [L1/L2] 320k/192k, binary 1010 */ ,
+	0xB << 4 /* [L1/L2] 352k/224k, binary 1011 */ ,
+	0xC << 4 /* [L1/L2] 384k/256k, binary 1100 */ ,
+	0xD << 4 /* [L1/L2] 416k/320k, binary 1101 */ ,
+	0xE << 4		/* [L1/L2] 448k/384k, binary 1110 */
+};
+
+u32 ivtv_audio_mask_bitrate = 0xF0;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_mono = {
+	.id = V4L2_CID_IVTV_MONO,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Mono/Stereo",
+	.minimum = 0,
+	.maximum = 3,
+	.step = 1,
+	.default_value = 0,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_mono[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_MONO, 0, "Stereo", 0},
+	{V4L2_CID_IVTV_MONO, 1, "JointStereo", 0},
+	{V4L2_CID_IVTV_MONO, 2, "Dual", 0},
+	{V4L2_CID_IVTV_MONO, 3, "Mono", 0}
+};
+
+u32 ivtv_audio_tbl_mono[] = {
+	/* setting */
+	0x0 << 8 /* Stereo,      binary 00 */ ,
+	0x1 << 8 /* JointStereo, binary 01 */ ,
+	0x2 << 8 /* Dual,        binary 10 */ ,
+	0x3 << 8		/* Mono,        binary 11 */
+};
+
+u32 ivtv_audio_mask_mono = 0x300;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_joint = {
+	.id = V4L2_CID_IVTV_JOINT,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Joint extension",
+	.minimum = 0,
+	.maximum = 3,
+	.step = 1,
+	.default_value = 0,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_joint[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_JOINT, 0, "Subbands 4-31/bound=4", 0},
+	{V4L2_CID_IVTV_JOINT, 1, "Subbands 8-31/bound=8", 0},
+	{V4L2_CID_IVTV_JOINT, 2, "Subbands 12-31/bound=12", 0},
+	{V4L2_CID_IVTV_JOINT, 3, "Subbands 16-31/bound=16", 0}
+};
+
+u32 ivtv_audio_tbl_joint[] = {
+	/* setting */
+	0x0 << 10 /* Subbands 4-31/bound=4,   binary 00 */ ,
+	0x1 << 10 /* Subbands 8-31/bound=8,   binary 01 */ ,
+	0x2 << 10 /* Subbands 12-31/bound=12, binary 10 */ ,
+	0x3 << 10		/* Subbands 16-31/bound=16, binary 11 */
+};
+
+u32 ivtv_audio_mask_joint = 0xc00;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_emphasis = {
+	.id = V4L2_CID_IVTV_EMPHASIS,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Emphasis",
+	.minimum = 0,
+	.maximum = 2,
+	.step = 1,
+	.default_value = 0,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_emphasis[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_EMPHASIS, 0, "None", 0},
+	{V4L2_CID_IVTV_EMPHASIS, 1, "50/15uS", 0},
+	{V4L2_CID_IVTV_EMPHASIS, 2, "CCITT J.17", 0}
+};
+
+u32 ivtv_audio_tbl_emphasis[] = {
+	/* setting */
+	0x0 << 12 /* None,       binary 00 */ ,
+	0x1 << 12 /* 50/15uS,    binary 01 */ ,
+	0x3 << 12		/* CCITT J.17, binary 11 */
+};
+
+u32 ivtv_audio_mask_emphasis = 0x3000;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_crc = {
+	.id = V4L2_CID_IVTV_CRC,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Audio CRC",
+	.minimum = 0,
+	.maximum = 1,
+	.step = 1,
+	.default_value = 0,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_crc[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_CRC, 0, "off", 0},
+	{V4L2_CID_IVTV_CRC, 1, "on", 0}
+};
+
+u32 ivtv_audio_tbl_crc[] = {
+	/* setting */
+	0x0 << 14 /* off, binary 0 */ ,
+	0x1 << 14		/* on,  binary 1 */
+};
+
+u32 ivtv_audio_mask_crc = 0x4000;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_copyright = {
+	.id = V4L2_CID_IVTV_COPYRIGHT,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Copyright",
+	.minimum = 0,
+	.maximum = 1,
+	.step = 1,
+	.default_value = 0,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_copyright[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_COPYRIGHT, 0, "off", 0},
+	{V4L2_CID_IVTV_COPYRIGHT, 1, "on", 0}
+};
+
+u32 ivtv_audio_tbl_copyright[] = {
+	/* setting */
+	0x0 << 15 /* off, binary 0 */ ,
+	0x1 << 15		/* on,  binary 1 */
+};
+
+u32 ivtv_audio_mask_copyright = 0x8000;
+
+struct v4l2_queryctrl ivtv_ctrl_menu_generation = {
+	.id = V4L2_CID_IVTV_GEN,
+	.type = V4L2_CTRL_TYPE_MENU,
+	.name = "Generation",
+	.minimum = 0,
+	.maximum = 1,
+	.step = 1,
+	.default_value = 0,
+	.flags = 0,
+	.reserved = {0, 0}
+};
+
+struct v4l2_querymenu ivtv_ctrl_query_generation[] = {
+	/* ID, Index, Name, Reserved */
+	{V4L2_CID_IVTV_GEN, 0, "copy", 0},
+	{V4L2_CID_IVTV_GEN, 1, "original", 0}
+};
+
+u32 ivtv_audio_tbl_generation[] = {
+	/* setting */
+	0x0 << 16 /* copy,     binary 0 */ ,
+	0x1 << 16		/* original, binary 1 */
+};
+
+u32 ivtv_audio_mask_generation = 0x10000;
+
+/* 3 stream types: mpeg, yuv, passthru */
+struct ivtv_v4l2_stream tmk_mpg_stream = {
+	 /*MPEG*/.s_flags = 0,
+	.id = -1,
+	.v4l_reg_type = VFL_TYPE_GRABBER,
+	.format = {
+		   .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		   .fmt = {
+			   .pix = {
+				   .width = 720,
+				   .height = 480,
+				   .field = V4L2_FIELD_INTERLACED,
+				   .sizeimage = (128 * 1024),
+				   }
+			   },
+		   },
+	.controlcount = 0,
+	.controls = NULL
+};
+
+struct ivtv_v4l2_stream tmk_yuv_stream = {
+	 /*YUV*/.s_flags = 0,
+	.id = -1,
+	.v4l_reg_type = VFL_TYPE_GRABBER,
+	.format = {
+		   .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		   .fmt = {
+			   .pix = {
+				   .width = 720,
+				   .height = 480,
+				   .field = V4L2_FIELD_INTERLACED,
+				   .sizeimage = (720 * 720),
+				   }
+			   },
+		   },
+	.controlcount = 0,
+	.controls = NULL
+};
+
+//FIXME these settings are way wrong
+struct ivtv_v4l2_stream tmk_vbi_stream = {
+	.s_flags = 0,
+	.id = -1,
+	.v4l_reg_type = VFL_TYPE_VBI,
+	.format = {
+		   .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+		   .fmt = {
+			   .pix = {
+				   .width = 720,
+				   .height = 480,
+				   .field = V4L2_FIELD_INTERLACED,
+				   .sizeimage = (128 * 1024),
+				   }
+			   },
+		   },
+	.controlcount = 0,
+	.controls = NULL
+};
+
+struct ivtv_v4l2_stream dec_mpg_stream = {
+/*Decoder MPG*/
+	.s_flags = 0,
+	.id = -1,
+	.v4l_reg_type = VFL_TYPE_GRABBER,
+	.format = {
+		   .type = V4L2_BUF_TYPE_VIDEO_OUTPUT,
+		   .fmt = {
+			   .pix = {
+				   .width = 720,
+				   .height = 480,
+				   .field = V4L2_FIELD_INTERLACED,
+				   .sizeimage = (128 * 1024),
+				   }
+			   },
+		   },
+	.controlcount = 0,
+	.controls = NULL
+};
+
+struct ivtv_v4l2_stream dec_yuv_stream = {
+/*Decoder YUV*/
+	.s_flags = 0,
+	.id = -1,
+	.v4l_reg_type = VFL_TYPE_GRABBER,
+	.format = {
+		   .type = V4L2_BUF_TYPE_VIDEO_OUTPUT,
+		   .fmt = {
+			   .pix = {
+				   .width = 720,
+				   .height = 480,
+				   .field = V4L2_FIELD_INTERLACED,
+				   .sizeimage = (720 * 720),
+				   }
+			   },
+		   },
+	.controlcount = 0,
+	.controls = NULL
+};
+
+/* Initialize v4l2 variables and register v4l2 device */
+int ivtv_v4l2_setup(struct ivtv *itv)
+{
+	int x, cont, retval;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 setup\n");
+
+	//switch based on card type
+	//  and fill in appropriate v4l2 device
+	switch (itv->card_type) {
+	case IVTV_350_V1:
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Configuring 350rev1 card\n");
+		itv->v4l2.streamcount = IVTV_350_V1_STREAMS;
+		/* Disable dec yuv buffers if requested */
+		if (itv->options.dec_yuv_buffers == 0)
+			itv->v4l2.streamcount--;
+		/* FIXME wrong values */
+		itv->v4l2.capabilities =
+		    (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
+		     V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_OUTPUT);
+		break;
+	case IVTV_250_V2:
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Configuring 250rev2 card\n");
+		itv->v4l2.streamcount = IVTV_250_V2_STREAMS;
+		/* FIXME wrong values */
+		itv->v4l2.capabilities =
+		    (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
+		     V4L2_CAP_READWRITE);
+		break;
+	case IVTV_250_V1:
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Configuring 250rev1 card\n");
+	default:		/* shouldn't happen, treat like V1 */
+		itv->v4l2.streamcount = IVTV_250_V1_STREAMS;
+		itv->v4l2.capabilities =
+		    (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
+		     V4L2_CAP_READWRITE);
+
+		break;
+	}
+
+	/* Initial settings */
+	itv->v4l2.codec.bitrate_mode = 0;
+	itv->v4l2.codec.bitrate = 8000000;
+	itv->v4l2.codec.bitrate_peak = 16000000;
+	itv->v4l2.codec.stream_type = IVTV_STREAM_PS;
+	itv->v4l2.codec.bframes = 3;
+	itv->v4l2.codec.gop_closure = 0;
+	itv->v4l2.codec.dnr_mode = 0;
+	itv->v4l2.codec.dnr_type = 0;
+	itv->v4l2.codec.dnr_spatial = 0;
+	itv->v4l2.codec.dnr_temporal = 0;
+	itv->v4l2.codec.aspect = 2;
+
+	itv->dec_options.hide_last_frame = 1;
+	itv->dec_options.pts_low = 0;
+	itv->dec_options.pts_hi = 0;
+	itv->dec_options.gop_offset = 0;
+	itv->dec_options.mute_frames = 0;
+
+	/* Ctrls */
+	itv->dec_options.speed.mute = 1;
+	itv->dec_options.speed.aud_mute = 0;
+	itv->dec_options.speed.smooth = 1;
+	itv->dec_options.speed.fr_mask = 2;
+	itv->dec_options.speed.fr_field = 1;
+	itv->dec_options.decbuffers = 1;
+	itv->dec_options.prebuffer = 1;
+
+	/* Allocate streams */
+	itv->v4l2.streams = (struct ivtv_v4l2_stream *)
+	    kmalloc((itv->v4l2.streamcount *
+		     sizeof(struct ivtv_v4l2_stream)), GFP_KERNEL);
+	if (NULL == itv->v4l2.streams) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Couldn't allocate v4l2 streams\n");
+		retval = -ENOMEM;
+		goto ivtv_stream_fail;
+	}
+
+	/* pre-init */
+	retval = ivtv_v4l2_pre_init(itv);
+	if (retval < 0) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error in pre-init\n");
+		goto ivtv_pre_init_fail;
+	}
+
+	/* Fill in streams with some defaults */
+	memcpy(&itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG], &tmk_mpg_stream,
+	       sizeof(struct ivtv_v4l2_stream));
+
+	memcpy(&itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV], &tmk_yuv_stream,
+	       sizeof(struct ivtv_v4l2_stream));
+
+	memcpy(&itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI], &tmk_vbi_stream,
+	       sizeof(struct ivtv_v4l2_stream));
+
+	/* Set some card-specific per-stream stuff here */
+	switch (itv->card_type) {
+	case IVTV_350_V1:
+		memcpy(&itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG],
+		       &dec_mpg_stream, sizeof(struct ivtv_v4l2_stream));
+
+		if (itv->options.dec_yuv_buffers != 0) {
+			memcpy(&itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV],
+			       &dec_yuv_stream,
+			       sizeof(struct ivtv_v4l2_stream));
+		}
+		break;
+	case IVTV_250_V2:
+		break;
+	case IVTV_250_V1:
+	default:		/* shouldn't happen, treat like V1 */
+		break;
+	}
+
+	for (x = 0; x < itv->v4l2.streamcount; x++) {
+		init_waitqueue_head(&itv->v4l2.streams[x].waitq);
+
+		itv->v4l2.streams[x].v4l2dev = video_device_alloc();
+		if (NULL == itv->v4l2.streams[x].v4l2dev) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "Couldn't allocate v4l2 video_device\n");
+			retval = -ENOMEM;
+			goto ivtv_videodev_fail;
+		}
+
+		memcpy(itv->v4l2.streams[x].v4l2dev,
+		       &tmk_v4l2dev, sizeof(struct video_device));
+		itv->v4l2.streams[x].v4l2dev->priv = itv;
+		itv->v4l2.streams[x].ubytes = 0;
+		itv->v4l2.streams[x].free_q.vdev = itv->v4l2.streams[x].v4l2dev;
+		itv->v4l2.streams[x].full_q.vdev = itv->v4l2.streams[x].v4l2dev;
+		itv->v4l2.streams[x].dma_q.vdev = itv->v4l2.streams[x].v4l2dev;
+		INIT_LIST_HEAD(&itv->v4l2.streams[x].free_q.list);
+		INIT_LIST_HEAD(&itv->v4l2.streams[x].full_q.list);
+		INIT_LIST_HEAD(&itv->v4l2.streams[x].dma_q.list);
+
+		retval = ivtv_init_queue(itv, &itv->v4l2.streams[x].full_q, 0,
+					 itv->v4l2.streams[x].format.type);
+		if (retval < 0) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "Error on init_queue 1\n");
+			goto ivtv_initq_fail;
+		}
+
+		retval = ivtv_init_queue(itv, &itv->v4l2.streams[x].dma_q, 0,
+					 itv->v4l2.streams[x].format.type);
+		if (retval < 0) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "Error on init_queue 2\n");
+			goto ivtv_initq_fail;
+		}
+
+		itv->v4l2.streams[x].v4l2dev->dev = &itv->dev->dev;
+		itv->v4l2.streams[x].v4l2dev->release = video_device_release;
+	}
+
+	/* Some streams have specific values */
+	x = ivtv_init_queue(itv,
+			    &itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG].free_q,
+			    itv->options.mpg_buffers,
+			    itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG].format.
+			    type);
+	x = ivtv_init_queue(itv,
+			    &itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV].free_q,
+			    itv->options.yuv_buffers,
+			    itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV].format.
+			    type);
+	x = ivtv_init_queue(itv,
+			    &itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI].free_q,
+			    itv->options.vbi_buffers,
+			    itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI].format.
+			    type);
+
+	/* set default minors */
+	itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG].v4l2dev->minor = itv->num;
+	itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV].v4l2dev->minor =
+	    itv->num + IVTV_V4L2_YUV_OFFSET;
+	//vbi will get offset by v4l, so no offset needed by us
+	itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI].v4l2dev->minor = itv->num;
+
+	/* Set any card-specific per-stream stuff here */
+	switch (itv->card_type) {
+	case IVTV_350_V1:
+		/* allocate buffers for decoder */
+		x = ivtv_init_queue(itv,
+				    &itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG]
+				    .free_q, itv->options.dec_mpg_buffers,
+				    itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG].
+				    format.type);
+
+		itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG].v4l2dev->minor =
+		    itv->num + IVTV_V4L2_DEC_OFFSET;
+
+		if (itv->options.dec_yuv_buffers != 0) {
+			x = ivtv_init_queue(itv,
+					    &itv->v4l2.
+					    streams[IVTV_DEC_STREAM_TYPE_YUV]
+					    .free_q,
+					    itv->options.dec_yuv_buffers,
+					    itv->v4l2.
+					    streams[IVTV_DEC_STREAM_TYPE_YUV].
+					    format.type);
+
+			itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV].v4l2dev->
+			    minor =
+			    itv->num + IVTV_V4L2_YUV_OFFSET +
+			    IVTV_V4L2_DEC_OFFSET;
+		}
+
+		/* Set poll for decoder parts */
+		itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG].v4l2dev->fops->
+		    poll = ivtv_dec_poll;
+		if (itv->options.dec_yuv_buffers)
+			itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV].v4l2dev->
+			    fops->poll = ivtv_dec_poll;
+
+		break;
+	case IVTV_250_V2:
+		break;
+	case IVTV_250_V1:
+	default:		/* shouldn't happen, treat like V1 */
+		break;
+	}
+
+	/* allocate minor, register, loop until works or out of range */
+	for (x = 0; x < itv->v4l2.streamcount; x++) {
+		cont = 0;
+		do {
+			if (video_register_device(itv->v4l2.streams[x].v4l2dev,
+						  itv->v4l2.streams[x].
+						  v4l_reg_type,
+						  itv->v4l2.streams[x].v4l2dev->
+						  minor)) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "Device or minor %d not accepted\n",
+					   itv->v4l2.streams[x].v4l2dev->minor);
+				itv->v4l2.streams[x].v4l2dev->minor++;
+			} else {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "Registered v4l2 device, minor %d\n",
+					   itv->v4l2.streams[x].v4l2dev->minor);
+				cont = 1;
+				ivtv_v4l2_init(itv->v4l2.streams[x].v4l2dev);
+			}
+		} while ((0 == cont) &&
+			 (itv->v4l2.streams[x].v4l2dev->minor <=
+			  IVTV_V4L2_MAX_MINOR));
+		if (0 == cont) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "Couldn't register v4l2 device!\n");
+			/* invalidate so we don't try to unload the device */
+			itv->v4l2.streams[x].v4l2dev->minor = -1;
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+
+      ivtv_pre_init_fail:
+	/* needs lots of queue cleanup here -axboe */
+      ivtv_videodev_fail:
+	for (x = 0; x < itv->v4l2.streamcount; x++)
+		if (itv->v4l2.streams[x].v4l2dev != NULL)
+			video_device_release(itv->v4l2.streams[x].v4l2dev);
+	/* needs lots of queue cleanup here -axboe */
+      ivtv_initq_fail:
+	kfree(itv->v4l2.streams);
+      ivtv_stream_fail:
+	return retval;
+}
+
+/* After setting the audio.active param, call this to
+ *  get the right input.. think of it as a resolver */
+int ivtv_set_audio(struct ivtv *itv, int *map)
+{
+	int input, msp_input;
+	struct msp_matrix mspm;
+
+	do {
+		input = *(map++);
+		msp_input = *(map++);
+		if (input == itv->v4l2.audio.active) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "Setting audio to input %d\n", msp_input);
+			mspm.input = msp_input;
+			mspm.output = itv->v4l2.audio_output;
+
+			ivtv_call_i2c_client(itv,
+					     IVTV_MSP3400_I2C_ADDR,
+					     MSP_SET_MATRIX, &mspm);
+			return 0;
+		}
+	} while ((msp_input != 0) || (input != 0));
+
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "Invalid audio input, shouldn't happen!\n");
+
+	return -EINVAL;
+}
+
+u32 ivtv_pause_encoder(struct ivtv * itv, int cmd)
+{
+	u32 data[16], result = 0;
+	int x;
+
+	data[0] = 0;		/* 0 = pause, 1 = unpause */
+	if (cmd)
+		data[0] = 1;
+
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_PAUSE_ENCODER,
+		     &result, 1, &data[0]);
+	return result;
+}
+
+/* Called if v4l2 registration is successful. Set video mode here, at least
+ * that is required on PAL cards */
+int ivtv_v4l2_init(struct video_device *v)
+{
+	struct ivtv *ivtv = v->priv;
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int x;
+
+	/*
+	 * only set it on minor 0
+	 */
+	if (v->minor != 0)
+		return 0;
+
+	memset(data, 0, sizeof(data));
+	/* set display standard */
+	if (ivtv_pal)
+		data[0] = 1;
+	else
+		data[0] = 0;
+
+	x = ivtv_api(ivtv->dec_mbox, &ivtv->dec_msem,
+		     IVTV_API_DEC_DISP_STANDARD, &result, 1, &data[0]);
+
+	return 0;
+}
+
+/* Called before v4l2 registration */
+int ivtv_v4l2_pre_init(struct ivtv *itv)
+{
+	int x, temp, retval = -1;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 init\n");
+	//Allocate based on card type
+	//  allocate capabilities and such based on device type
+
+	/* FIXME too much hardcoding? */
+	//inputs
+	itv->v4l2.input.active = 4;
+	itv->v4l2.input.count = 10;
+	itv->v4l2.input.table.input = tmk_inputs;
+
+	itv->v4l2.audio_output = 1;
+
+	//audio inputs
+	itv->v4l2.audio.active = 0;
+	itv->v4l2.audio.count = 2;
+	itv->v4l2.audio.table.audio = tmk_audio_inputs;
+
+	//outputs .. none yet (no real 350 support anyways)
+	itv->v4l2.output.active = 0;
+	itv->v4l2.output.count = 0;
+	itv->v4l2.output.table.output = NULL;
+
+	//standards (NTSC, PAL, SECAM)
+	if (ivtv_pal)
+		itv->v4l2.standard.active = 1;
+	else
+		itv->v4l2.standard.active = 0;
+	itv->v4l2.standard.count = 3;
+	itv->v4l2.standard.table.std = tmk_standards;
+
+	if (itv->v4l2.standard.active == 0) {
+		itv->v4l2.codec.framespergop = 15;	// NTSC
+		itv->v4l2.codec.framerate = 0;	// NTSC 30fps
+	} else {
+		itv->v4l2.codec.framespergop = 12;	// PAL
+		itv->v4l2.codec.framerate = 1;	// PAL  25fps
+
+		/* set pal height in stream defaults */
+		tmk_mpg_stream.format.fmt.pix.height = 576;
+		tmk_yuv_stream.format.fmt.pix.height = 576;
+		tmk_vbi_stream.format.fmt.pix.height = 576;
+		dec_mpg_stream.format.fmt.pix.height = 576;
+		dec_yuv_stream.format.fmt.pix.height = 576;
+	}
+
+	//tuner
+	itv->v4l2.tuner.active = 0;
+	if (itv->card_type == IVTV_350_V1) {
+		itv->v4l2.tuner.count = 2;
+	} else {
+		itv->v4l2.tuner.count = 1;
+	}
+
+	itv->v4l2.tuner.table.tuner = (struct v4l2_tuner *)
+	    kmalloc((itv->v4l2.tuner.count *
+		     sizeof(struct v4l2_tuner)), GFP_KERNEL);
+
+	if (itv->v4l2.tuner.table.tuner == NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Couldn't allocate v4l2 tuner\n");
+		return -ENOMEM;
+	}
+
+	memcpy(itv->v4l2.tuner.table.tuner, &tmk_tuners[0],
+	       (itv->v4l2.tuner.count * sizeof(struct v4l2_tuner)));
+
+	/* Setup audio */
+	/* V4L2_CID_IVTV_FREQ */
+	itv->v4l2.audio_meta[0].ctrl = &ivtv_ctrl_menu_freq;
+	itv->v4l2.audio_meta[0].menu = ivtv_ctrl_query_freq;
+	itv->v4l2.audio_meta[0].mask = ivtv_audio_mask_freq;
+	itv->v4l2.audio_meta[0].setting = ivtv_ctrl_menu_freq.default_value;
+	itv->v4l2.audio_meta[0].table = &ivtv_audio_tbl_freq[0];
+
+	/* V4L2_CID_IVTV_ENC */
+	itv->v4l2.audio_meta[1].ctrl = &ivtv_ctrl_menu_enc;
+	itv->v4l2.audio_meta[1].menu = ivtv_ctrl_query_enc;
+	itv->v4l2.audio_meta[1].mask = ivtv_audio_mask_enc;
+	itv->v4l2.audio_meta[1].setting = ivtv_ctrl_menu_enc.default_value;
+	itv->v4l2.audio_meta[1].table = &ivtv_audio_tbl_enc[0];
+
+	/* V4L2_CID_IVTV_BITRATE */
+	itv->v4l2.audio_meta[2].ctrl = &ivtv_ctrl_menu_bitrate;
+	itv->v4l2.audio_meta[2].menu = ivtv_ctrl_query_bitrate;
+	itv->v4l2.audio_meta[2].mask = ivtv_audio_mask_bitrate;
+	itv->v4l2.audio_meta[2].setting = ivtv_ctrl_menu_bitrate.default_value;
+	itv->v4l2.audio_meta[2].table = &ivtv_audio_tbl_bitrate[0];
+
+	/* V4L2_CID_IVTV_MONO */
+	itv->v4l2.audio_meta[3].ctrl = &ivtv_ctrl_menu_mono;
+	itv->v4l2.audio_meta[3].menu = ivtv_ctrl_query_mono;
+	itv->v4l2.audio_meta[3].mask = ivtv_audio_mask_mono;
+	itv->v4l2.audio_meta[3].setting = ivtv_ctrl_menu_mono.default_value;
+	itv->v4l2.audio_meta[3].table = &ivtv_audio_tbl_mono[0];
+
+	/* V4L2_CID_IVTV_JOINT */
+	itv->v4l2.audio_meta[4].ctrl = &ivtv_ctrl_menu_joint;
+	itv->v4l2.audio_meta[4].menu = ivtv_ctrl_query_joint;
+	itv->v4l2.audio_meta[4].mask = ivtv_audio_mask_joint;
+	itv->v4l2.audio_meta[4].setting = ivtv_ctrl_menu_joint.default_value;
+	itv->v4l2.audio_meta[4].table = &ivtv_audio_tbl_joint[0];
+
+	/* V4L2_CID_IVTV_EMPHASIS */
+	itv->v4l2.audio_meta[5].ctrl = &ivtv_ctrl_menu_emphasis;
+	itv->v4l2.audio_meta[5].menu = ivtv_ctrl_query_emphasis;
+	itv->v4l2.audio_meta[5].mask = ivtv_audio_mask_emphasis;
+	itv->v4l2.audio_meta[5].setting = ivtv_ctrl_menu_emphasis.default_value;
+	itv->v4l2.audio_meta[5].table = &ivtv_audio_tbl_emphasis[0];
+
+	/* V4L2_CID_IVTV_CRC */
+	itv->v4l2.audio_meta[6].ctrl = &ivtv_ctrl_menu_crc;
+	itv->v4l2.audio_meta[6].menu = ivtv_ctrl_query_crc;
+	itv->v4l2.audio_meta[6].mask = ivtv_audio_mask_crc;
+	itv->v4l2.audio_meta[6].setting = ivtv_ctrl_menu_crc.default_value;
+	itv->v4l2.audio_meta[6].table = &ivtv_audio_tbl_crc[0];
+
+	/* V4L2_CID_IVTV_COPYRIGHT */
+	itv->v4l2.audio_meta[7].ctrl = &ivtv_ctrl_menu_copyright;
+	itv->v4l2.audio_meta[7].menu = ivtv_ctrl_query_copyright;
+	itv->v4l2.audio_meta[7].mask = ivtv_audio_mask_copyright;
+	itv->v4l2.audio_meta[7].setting =
+	    ivtv_ctrl_menu_copyright.default_value;
+	itv->v4l2.audio_meta[7].table = &ivtv_audio_tbl_copyright[0];
+
+	/* V4L2_CID_IVTV_GEN */
+	itv->v4l2.audio_meta[8].ctrl = &ivtv_ctrl_menu_generation;
+	itv->v4l2.audio_meta[8].menu = ivtv_ctrl_query_generation;
+	itv->v4l2.audio_meta[8].mask = ivtv_audio_mask_generation;
+	itv->v4l2.audio_meta[8].setting =
+	    ivtv_ctrl_menu_generation.default_value;
+	itv->v4l2.audio_meta[8].table = &ivtv_audio_tbl_generation[0];
+
+	itv->v4l2.codec.audio_bitmap = 0;
+	for (x = 0; x < IVTV_V4L2_AUDIO_MENUCOUNT; x++) {
+		temp = itv->v4l2.audio_meta[x].setting;
+		itv->v4l2.codec.audio_bitmap |=
+		    itv->v4l2.audio_meta[x].table[temp];
+	}
+
+	retval = ivtv_set_audio(itv, tmk_audio_mapping);
+	if (retval) {
+		kfree(itv->v4l2.tuner.table.tuner);
+		return retval;
+	}
+	//FIXME Setup components here? tuner channel etc
+	return 0;
+}
+
+int ivtv_start_v4l2_stream(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int x, vsize, vsync, hsize;
+	int type, subtype;
+	unsigned int dig;
+
+	/* sem_lock must be held */
+	IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv start v4l2 stream\n");
+
+	/* NTSC/PAL switching */
+	vsize = itv->v4l2.streams[0].format.fmt.pix.height;
+	vsync = (int)itv->v4l2.streams[0].format.fmt.pix.height / 2;
+	hsize = itv->v4l2.streams[0].format.fmt.pix.width;
+
+	type = id->type;
+
+	switch (type) {
+	case 2:		/* VBI, may be the wrong value */
+		subtype = 4;
+	case 4:		/* Radio, probably not applicable */
+		subtype = 2;
+		break;
+	default:
+		subtype = 3;
+		break;
+	}
+
+	/* clear queues */
+	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].full_q,
+			&itv->v4l2.streams[id->type].free_q);
+	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].dma_q,
+			&itv->v4l2.streams[id->type].free_q);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "fullq size %d\n",
+		   itv->v4l2.streams[id->type].full_q.elements);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "freeq size %d\n",
+		   itv->v4l2.streams[id->type].free_q.elements);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "dmaq size %d\n",
+		   itv->v4l2.streams[id->type].dma_q.elements);
+
+	/*assign dma block len */
+	/* FIXME this needs a flag */
+	data[0] = 1;		/* num bytes in block */
+	data[1] = 1;		/* use info from sg instead */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_DMA_BLOCKLEN, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 1. Code %d\n", x);
+
+	/*assign program index info */
+	/* FIXME need more info on this call */
+	data[0] = 0;		/*Mask    0:Disable */
+	data[1] = 0;		/*Num_req 0:??/ */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_PGM_INDEX_INFO, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 2. Code %d\n", x);
+
+	/*assign stream type */
+	data[0] = itv->v4l2.codec.stream_type;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_STREAM_TYPE,
+		     &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 3. Code %d\n", x);
+
+	/*assign output port */
+	data[0] = 0;		/*0:Memory */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_OUTPUT_PORT,
+		     &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 4. Code %d\n", x);
+
+	/*assign framerate */
+	data[0] = itv->v4l2.codec.framerate;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_FRAMERATE,
+		     &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 5. Code %d\n", x);
+
+	/*assign frame size */
+	data[0] = vsize;	/* height */
+	data[1] = hsize;	/* width */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_FRAME_SIZE,
+		     &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 6. Code %d\n", x);
+
+	/*assign aspect ratio */
+	data[0] = itv->v4l2.codec.aspect;	/*mpeg spec sez 2 */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_ASPECT_RATIO, &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 7. Code %d\n", x);
+
+	/*assign bitrates */
+	/*FIXME i think these settings are valid for compressed only */
+	data[0] = itv->v4l2.codec.bitrate_mode;	/*mode */
+	data[1] = itv->v4l2.codec.bitrate;	/* bps */
+	data[2] = itv->v4l2.codec.bitrate_peak / 400;	/* peak/400 */
+	data[3] = 0;		/*??? */
+	data[4] = 0x70;		/*??? */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_BITRATES,
+		     &result, 5, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 8. Code %d\n", x);
+
+	/*assign gop properties */
+	data[0] = itv->v4l2.codec.framespergop;
+	data[1] = itv->v4l2.codec.bframes;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_GOP_PROPERTIES, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 9. Code %d\n", x);
+
+	/*assign 3 2 pulldown */
+	data[0] = itv->v4l2.codec.pulldown;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_3_2_PULLDOWN, &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 10. Code %d\n", x);
+
+	/*assign gop closure */
+	data[0] = itv->v4l2.codec.gop_closure;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_GOP_CLOSURE,
+		     &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 11. Code %d\n", x);
+
+	/*assign audio properties */
+	data[0] = itv->v4l2.codec.audio_bitmap;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_AUDIO_PROPERTIES, &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 12. Code %d\n", x);
+
+	/*assign dnr filter mode */
+	data[0] = itv->v4l2.codec.dnr_mode;
+	data[1] = itv->v4l2.codec.dnr_type;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_DNR_FILTER_MODE, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 13. Code %d\n", x);
+
+	/*assign dnr filter props */
+	data[0] = itv->v4l2.codec.dnr_spatial;
+	data[1] = itv->v4l2.codec.dnr_temporal;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_DNR_FILTER_PROPS, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 14. Code %d\n", x);
+
+	/*assign coring levels */
+	data[0] = 0;		/*luma_h */
+	data[1] = 255;		/*luma_l */
+	data[2] = 0;		/*chroma_h */
+	data[3] = 255;		/*chroma_l */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_CORING_LEVELS, &result, 4, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 15. Code %d\n", x);
+
+	/*assign spatial filter type */
+	data[0] = 1;		/*luma_t: 1 = horiz_only */
+	data[1] = 1;		/*chroma_t: 1 = horiz_only */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 16. Code %d\n", x);
+
+	/*assign frame drop rate */
+	data[0] = 0;
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_FRAME_DROP_RATE, &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 17. Code %d\n", x);
+
+	/*assign placeholder */
+	data[0] = 0;		/* type: 0 = Extension/UserData */
+	data[1] = 0;		/*period */
+	data[2] = 0;		/*size_t */
+	data[3] = 0;		/*arg0 */
+	data[4] = 0;		/*arg1 */
+	data[5] = 0;		/*arg2 */
+	data[6] = 0;		/*arg3 */
+	data[7] = 0;		/*arg4 */
+	data[8] = 0;		/*arg5 */
+	data[9] = 0;		/*arg6 */
+	data[10] = 0;		/*arg7 */
+	data[11] = 0;		/*arg8 */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ASSIGN_PLACEHOLDER,
+		     &result, 12, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 18. Code %d\n\n", x);
+
+	/* assign num vsync lines */
+	data[0] = vsync;	/*??? */
+	data[1] = vsync;	/* ??? */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_ASSIGN_NUM_VSYNC_LINES, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 20. Code %d\n", x);
+
+	if (atomic_read(&itv->capturing) == 0) {
+
+		itv->trans_id = 0;
+		itv->first_read = 1;
+
+		/* Clear pending interrupts */
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Clearing Interrupts\n");
+		writel((readl(itv->reg_mem + IVTV_REG_IRQSTATUS) & 0xC8000000),
+		       (IVTV_REG_IRQSTATUS + itv->reg_mem));
+
+#if 0
+		/* event notification (on) */
+		data[0] = 0;	/*type: 0 = refresh */
+		data[1] = 1;	/*on/off: 1 = on */
+		data[2] = 0x10000000;	/*intr_bit: 0x10000000 = digitizer */
+		data[3] = -1;	/*mbox_id: -1: none */
+		x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+			     IVTV_API_EVENT_NOTIFICATION, &result, 4, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "startcap error 2. Code %d\n", x);
+#endif
+
+		/* Disable digitizer (saa7115) */
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Disabling digitizer\n");
+		dig = 0;
+		ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR,
+				     DECODER_ENABLE_OUTPUT, &dig);
+
+		/*initialize input (no args) */
+		x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+			     IVTV_API_INITIALIZE_INPUT, &result, 0, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 19. Code %d\n\n",
+				   x);
+
+		/* enable digitizer (saa7115) */
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Enabling digitizer\n");
+		dig = 1;
+		ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR,
+				     DECODER_ENABLE_OUTPUT, &dig);
+
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 100ms\n");
+		ivtv_sleep_timeout(HZ / 10);
+	}
+
+	/* FIXME this is for mpg captures only i think */
+	clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+
+	/* begin_capture */
+	data[0] = type;		/*type: 0 = mpeg */
+	data[1] = subtype;	/*subtype: 3 = video+audio */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_BEGIN_CAPTURE,
+		     &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "startcap error 1. Code %d\n", x);
+
+	if (atomic_read(&itv->capturing) == 0) {
+		/*Clear the following Interrupt mask bits: 0xd8000000 */
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "IRQ Mask is now: 0x%08x\n",
+			   itv->irqmask);
+	}
+
+	/* you're live! sit back and await interrupts :) */
+	atomic_inc(&itv->capturing);
+	return 0;
+}
+
+int ivtv_api_dec_playback_speed(struct ivtv *itv, int fastspeed, int factor,
+				int forward, int mpeg_frame_type_mask,
+				int bframes_per_gop, int mute_audio,
+				int display_fields)
+{
+
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+
+	data[0] = (fastspeed << 31) | (factor & 0xff);
+	data[1] = forward;
+	data[2] = mpeg_frame_type_mask;
+	data[3] = bframes_per_gop;
+	data[4] = mute_audio;
+	data[5] = display_fields;
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_PLAYBACK_SPEED,
+		 &result, 6, &data[0]);
+	return result;
+}
+
+int ivtv_start_v4l2_decode(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	struct ivtv_v4l2_stream *stream;
+	int x;
+	int type;
+	int standard = 0;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Start v4l2_decode \n");
+	/* sem_lock must be held */
+	IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0);
+
+	if ((id->type != IVTV_DEC_STREAM_TYPE_MPG) &&
+	    (id->type != IVTV_DEC_STREAM_TYPE_YUV)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Write on read-only interface\n");
+		return -EINVAL;
+	}
+
+	stream = &itv->v4l2.streams[id->type];
+	if ((stream->id == -1) && !test_bit(IVTV_F_S_CAP, &stream->s_flags)) {
+		set_bit(IVTV_F_S_CAP, &stream->s_flags);
+		stream->id = id->open_id;
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Granting ownership to id %d\n",
+			   id->open_id);
+	} else {
+		if (id->open_id != stream->id) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder busy\n");
+			return -EBUSY;
+		}
+		if (test_bit(IVTV_F_S_CAP, &stream->s_flags)) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "Decoder doesn't need init\n");
+			return 0;
+		}
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Starting v4l2_decode \n");
+
+	type = id->type;
+
+	if (itv->v4l2.standard.active != 0) {	/* if not NTSC */
+		standard = 1;	/* PAL */
+	}
+
+/* this isn't needed until we use buffers for decoding */
+	/* clear queues */
+	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].full_q,
+			&itv->v4l2.streams[id->type].free_q);
+	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].dma_q,
+			&itv->v4l2.streams[id->type].free_q);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder fullq size %d\n",
+		   itv->v4l2.streams[id->type].full_q.elements);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder freeq size %d\n",
+		   itv->v4l2.streams[id->type].free_q.elements);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder dmaq size %d\n",
+		   itv->v4l2.streams[id->type].dma_q.elements);
+
+	if (atomic_read(&itv->decoding) == 0) {
+		/* Clear pending interrupts */
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Clearing Interrupts\n");
+		writel((readl(itv->reg_mem + IVTV_REG_IRQSTATUS) & 0xC8000000),
+		       (IVTV_REG_IRQSTATUS + itv->reg_mem));
+	}
+
+	if (test_bit(IVTV_F_S_UNINIT, &id->itv->v4l2.streams[id->type].s_flags)) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "Setting some initial decoder settings\n");
+
+		/* set display standard */
+		data[0] = standard;	/* 0 = NTSC, 1 = PAL */
+		x = ivtv_api(itv->dec_mbox,
+			     &itv->dec_msem,
+			     IVTV_API_DEC_DISP_STANDARD, &result, 1, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "COULDN'T SET DISPLAY STD %d\n", x);
+
+		/* set audio mode */
+		data[0] = 0;	/* Dual mono-mode action: ??? */
+		data[1] = 0;	/* stereo mode action: 0=stereo, 1=left,
+				   2=right, 3=mono */
+		x = ivtv_api(itv->dec_mbox,
+			     &itv->dec_msem,
+			     IVTV_API_DEC_SELECT_AUDIO, &result, 2, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "COULDN'T SET AUDIO MODE %d\n", x);
+
+		/* set number of internal decoder buffers */
+		data[0] = itv->dec_options.decbuffers;	/* 0 = 6 buffers,
+							   1 = 9 buffers */
+		x = ivtv_api(itv->dec_mbox,
+			     &itv->dec_msem,
+			     IVTV_API_DEC_DISPLAY_BUFFERS,
+			     &result, 1, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "COULDN'T INITIALIZE # OF DISPLAY BUFFERS %d\n",
+				   x);
+
+		/* prebufferring */
+		data[0] = itv->dec_options.prebuffer;	/* 0 = no prebuffering,
+							   1 = enabled, see docs */
+		x = ivtv_api(itv->dec_mbox,
+			     &itv->dec_msem,
+			     IVTV_API_DEC_BUFFER, &result, 1, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "COULDN't INITIALIZE PREBUFFER %d\n", x);
+
+		/* set decoder source settings */
+		data[0] = id->type;	/* Data type: 0 = mpeg from host,
+					   1 = yuv from encoder,
+					   2 = yuv_from_host */
+		data[1] = 720;	/* YUV source width */
+		if (itv->v4l2.standard.active == 1) {
+			data[2] = 576;	/* YUV source height */
+		} else {
+			data[2] = 480;	/* YUV source height */
+		}
+		data[3] = itv->v4l2.codec.audio_bitmap;	/* Audio settings to use,
+							   bitmap. see docs. */
+		x = ivtv_api(itv->dec_mbox,
+			     &itv->dec_msem,
+			     IVTV_API_DEC_DECODE_SOURCE, &result, 4, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "COULDN'T INITIALIZE DECODER SOURCE %d\n",
+				   x);
+
+		clear_bit(IVTV_F_S_UNINIT,
+			  &id->itv->v4l2.streams[id->type].s_flags);
+	} else {
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "Decoder already configured, skipping extra setup\n");
+	}
+#if 0
+	/* select event notification */
+	data[0] = 0;		/* Event: 0 = audio change between stereo and mono */
+	data[1] = 1;		/* Enable/Disable: 0 = disabled, 1 = enabled */
+	data[2] = 0x00010000;	/* Bit: interrupt bit to fire */
+	data[3] = -1;		/* Mailbox to use: -1 = no mailbox needed */
+	x = ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		     IVTV_API_DEC_EVENT_NOTIFICATION, &result, 4, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "COULDN'T INITIALIZE EVENT NOTIFICATION %d\n", x);
+#endif
+#if 0
+	/* set stream input port */
+	data[0] = 0;		/* 0 = memory, 1 = streaming */
+	x = ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_STREAM_INPUT,
+		     &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "COULDN'T INITIALIZE STREAM INPUT %d\n", x);
+
+	/* A/V sync delay */
+	data[0] = 0;		/* Delay in 90khz ticks. 0 = synced, negative = audio lags, positive = video lags */
+	x = ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_SET_AV_DELAY,
+		     &result, 1, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "COULDN't INITIALIZE Audio/Vid sync delay %d\n", x);
+#endif
+
+	if (atomic_read(&itv->decoding) == 0) {
+		memset(&itv->dec_timestamp, 0,
+		       sizeof(struct ivtv_ioctl_framesync));
+		writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]);
+		writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]);
+		writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]);
+		writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]);
+		writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[4]);
+
+		/*Clear the following Interrupt mask bits: 0xd8000000 */
+		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "IRQ Mask is now: 0x%08x\n",
+			   itv->irqmask);
+	}
+
+	/* start playback */
+	data[0] = itv->dec_options.gop_offset;	/* frame to start from (in GOP) */
+	data[1] = itv->dec_options.mute_frames;	/* # of audio frames to mute */
+	x = ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		     IVTV_API_DEC_START_PLAYBACK, &result, 2, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T START PLAYBACK %d\n", x);
+
+	/*you're live! sit back and await interrupts :) */
+	atomic_inc(&itv->decoding);
+	return 0;
+}
+
+void ivtv_v4l2_cleanup(struct ivtv *itv)
+{
+	int x;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 unregister\n");
+
+	if (atomic_read(&itv->capturing) >= 0)
+		ivtv_stop_all_captures(itv);
+	if (itv->v4l2.tuner.table.tuner)
+		kfree(itv->v4l2.tuner.table.tuner);
+	for (x = 0; x < itv->v4l2.streamcount; x++) {
+		/* Catch a possible kernel panic */
+		if (itv->v4l2.streams[x].v4l2dev->minor != -1) {
+			video_unregister_device(itv->v4l2.streams[x].v4l2dev);
+		} else {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "invalid v4l2 registration on unload\n");
+		}
+	}
+	if (itv->v4l2.streams)
+		kfree(itv->v4l2.streams);
+}
+
+int ivtv_v4l2_open(struct inode *inode, struct file *filp)
+{
+	int x, y = 0, minor;
+	struct ivtv_open_id *item;
+	struct ivtv *itv = NULL;
+
+	minor = MINOR(inode->i_rdev);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 open on minor %d\n", minor);
+
+	/* Find which card this open was on */
+	spin_lock_irq(&ivtv_lock);
+	for (x = 0; x < ivtv_cards_active; x++) {
+
+		/* find out which stream this open was on */
+		for (y = 0; y < ivtv_cards[x].v4l2.streamcount; y++) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "current minor %d\n",
+				   ivtv_cards[x].v4l2.streams[y].v4l2dev->
+				   minor);
+			if (ivtv_cards[x].v4l2.streams[y].v4l2dev->minor ==
+			    minor) {
+				itv =
+				    ivtv_cards[x].v4l2.streams[y].v4l2dev->priv;
+				break;
+			}
+		}
+		/* FIXME ugly :( */
+		if (itv != NULL)
+			break;
+	}
+	spin_unlock_irq(&ivtv_lock);
+
+	/* FIXME temporary 
+	   if (y == 2) {
+	   IVTV_DEBUG(IVTV_DEBUG_ERR, "VBI not supported yet \n");
+	   return -EINVAL;
+	   }
+	 */
+	if (itv != NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "opened card # %d, stream %d\n", x,
+			   y);
+		//allocate memory
+		item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
+		if (NULL == item) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "nomem on v4l2 open\n");
+			return -ENOMEM;
+		}
+		item->itv = itv;
+		item->type = y;
+
+		INIT_LIST_HEAD(&item->list);
+
+		down(&itv->sem_lock);
+
+		item->open_id = item->itv->open_id++;
+
+		list_add_tail(&item->list, &item->itv->client_list);
+
+		up(&itv->sem_lock);
+
+		filp->private_data = item;
+
+		return 0;
+	}
+
+	/* Couldnt find a device registered on that minor, shouldn't happen! */
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "Device on minor %d not found!\n", minor);
+
+	return -ENXIO;
+}
+
+int ivtv_v4l2_read(struct file *filp, char *buf, size_t count, loff_t * pos)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	struct ivtv *itv = id->itv;
+	struct ivtv_v4l2_stream *stream;
+	int ret = 0;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 read\n");
+
+	if (down_interruptible(&itv->sem_lock))
+		return -ERESTARTSYS;
+
+	stream = &itv->v4l2.streams[id->type];
+
+	if (!test_bit(IVTV_F_S_CAP, &stream->s_flags) && stream->id == -1) {
+		set_bit(IVTV_F_S_CAP, &stream->s_flags);
+		stream->id = id->open_id;
+
+		ret = ivtv_start_v4l2_stream(id);
+		if (ret) {
+			stream->id = -1;
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "Error in v4l2 stream init\n");
+		}
+		stream->seq = 0;
+		stream->ubytes = 0;
+	} else {
+		if (id->open_id != stream->id)
+			ret = -EBUSY;
+	}
+
+	up(&itv->sem_lock);
+
+	if (ret)
+		return ret;
+
+	ret = ivtv_read(id, buf, count, !(filp->f_flags & O_NONBLOCK));
+
+	if (ret > 0)
+		*pos += ret;
+
+	if (ret == 0) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 read returning 0\n");
+	}
+
+	return ret;
+}
+
+ssize_t ivtv_v4l2_write(struct file * filp, const char *buf, size_t count,
+			loff_t * pos)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	struct ivtv *itv = id->itv;
+
+	int ret = 0;
+
+	if (down_interruptible(&itv->sem_lock))
+		return -ERESTARTSYS;
+
+	ret = ivtv_start_v4l2_decode(id);
+
+	up(&itv->sem_lock);
+
+	if (ret)
+		return ret;
+
+	/* do all the work */
+	return ivtv_write(id, buf, count, !(filp->f_flags & O_NONBLOCK));
+}
+
+int ivtv_v4l2_streamoff(struct ivtv_open_id *id)
+{
+
+	if (down_interruptible(&id->itv->sem_lock))
+		return -ERESTARTSYS;
+
+	if (id->open_id != id->itv->v4l2.streams[id->type].id) {
+		return -EINVAL;
+	} else {
+		ivtv_stop_capture(id);
+	}
+
+	up(&id->itv->sem_lock);
+
+	return 0;
+}
+
+int ivtv_v4l2_close(struct inode *inode, struct file *filp)
+{
+	struct ivtv_open_id *id = filp->private_data;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 close\n");
+
+	if (NULL == id) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "invalid id on v4l2 close\n");
+		return -ENODEV;
+	}
+
+	if (down_interruptible(&id->itv->sem_lock))
+		return -ERESTARTSYS;
+
+	if (id->itv->v4l2.streams[id->type].id == -1) {
+		clear_bit(IVTV_F_S_CAP,
+			  &id->itv->v4l2.streams[id->type].s_flags);
+		set_bit(IVTV_F_S_UNINIT,
+			&id->itv->v4l2.streams[id->type].s_flags);
+		ivtv_flush_queues(id);
+	} else if (id->open_id == id->itv->v4l2.streams[id->type].id) {
+		ivtv_close(id);
+
+		clear_bit(IVTV_F_S_CAP,
+			  &id->itv->v4l2.streams[id->type].s_flags);
+		set_bit(IVTV_F_S_UNINIT,
+			&id->itv->v4l2.streams[id->type].s_flags);
+		id->itv->v4l2.streams[id->type].id = -1;
+		ivtv_flush_queues(id);
+	}
+
+	up(&id->itv->sem_lock);
+
+	list_del(&id->list);
+
+	kfree(id);
+
+	return 0;
+}
+
+/* direct from the latest v4l2 patch */
+static unsigned int video_fix_command(unsigned int cmd)
+{
+	switch (cmd) {
+	case VIDIOC_OVERLAY_OLD:
+		cmd = VIDIOC_OVERLAY;
+		break;
+	case VIDIOC_S_PARM_OLD:
+		cmd = VIDIOC_S_PARM;
+		break;
+	case VIDIOC_S_CTRL_OLD:
+		cmd = VIDIOC_S_CTRL;
+		break;
+	case VIDIOC_G_AUDIO_OLD:
+		cmd = VIDIOC_G_AUDIO;
+		break;
+	case VIDIOC_G_AUDOUT_OLD:
+		cmd = VIDIOC_G_AUDOUT;
+		break;
+	default:
+		break;
+	}
+
+	return cmd;
+}
+
+int ivtv_change_speed(struct ivtv *itv, struct ivtv_speed speed)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+
+	if ((speed.scale < 0) || (speed.scale > 50))
+		return -EINVAL;
+
+	if ((speed.speed < 0) || (speed.speed > 1))
+		return -EINVAL;
+
+	data[0] = speed.scale;
+
+	if (speed.smooth)	/* smooth ff */
+		data[0] |= 0x40000000;
+
+	if (speed.speed)	/* fast forward */
+		data[0] |= 0x80000000;
+
+	if (speed.direction) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "DEC: Reverse not supported\n");
+		return -EINVAL;
+	}
+
+	data[1] = speed.direction;	/* Forward. Reverse not supported */
+
+	switch (speed.fr_mask) {
+	case 2:
+	default:
+		data[2] |= 4;	/* B */
+	case 1:
+		data[2] |= 2;	/* P */
+	case 0:
+		data[2] |= 1;	/* I */
+		break;
+	}
+
+	data[3] = itv->v4l2.codec.framespergop;
+	data[4] = speed.aud_mute;	/* mute while fast/slow */
+	data[5] = speed.fr_field;	/* frame or field at a time */
+	data[6] = speed.mute;	/* # of frames to mute on normal speed resume */
+
+	if (ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		     IVTV_API_DEC_PLAYBACK_SPEED, &result, 7, &data[0])) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "DEC: error changing speed\n");
+		return (int)result;
+	}
+
+	/* Save speed options if call succeeded */
+	memcpy(&itv->dec_options.speed, &speed, sizeof(speed));
+
+	return 0;
+}
+
+int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+
+	struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
+	struct ivtv *itv = id->itv;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl 0x%08x\n", cmd);
+
+	cmd = video_fix_command(cmd);
+
+	switch (cmd) {
+#ifdef SAA7115_REGTEST
+		/* ioctls to allow direct access to the saa7115 registers for testing */
+	case SAA7115_GET_REG:{
+			struct saa7115_reg_t *saa7115_reg =
+			    (struct saa7115_reg_t *)arg;
+
+			ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR,
+					     SAA7115_GET_REG, saa7115_reg);
+			break;
+		}
+	case SAA7115_SET_REG:{
+			struct saa7115_reg_t *saa7115_reg =
+			    (struct saa7115_reg_t *)arg;
+
+			ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR,
+					     SAA7115_SET_REG, saa7115_reg);
+			break;
+		}
+#endif
+	case IVTV_IOC_ZCOUNT:{
+			/* Zeroes out usage count so it can be unloaded in case of
+			 * drastic error */
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv ioctl: ZCOUNT\n");
+
+			break;
+		}
+	case IVTV_IOC_GET_FB:{
+			if (itv->fb_id < 0)
+				return -EINVAL;
+			if (copy_to_user
+			    ((int *)arg, &itv->fb_id, sizeof(itv->fb_id)))
+				return -EFAULT;
+
+			break;
+		}
+	case IVTV_IOC_FWAPI:{
+			struct ivtv_ioctl_fwapi fwapi;
+			int x;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv ioctl: FWAPI\n");
+
+			if (copy_from_user
+			    (&fwapi, (struct ivtv_ioctl_fwapi *)arg,
+			     sizeof(struct ivtv_ioctl_fwapi)))
+				return -EFAULT;
+
+			/* Encoder
+			   x = ivtv_api(itv->enc_mbox, &itv->enc_msem, fwapi.cmd,
+			   &fwapi.result, fwapi.args, &fwapi.data[0]);
+			 */
+
+			/* Decoder */
+			x = ivtv_api(itv->dec_mbox, &itv->dec_msem, fwapi.cmd,
+				     &fwapi.result, fwapi.args, &fwapi.data[0]);
+
+			if (copy_to_user((struct ivtv_ioctl_fwapi *)arg, &fwapi,
+					 sizeof(struct ivtv_ioctl_fwapi)))
+				return -EFAULT;
+
+			return x;
+		}
+	case IVTV_IOC_FRAMESYNC:{
+			interruptible_sleep_on(&itv->vsync_w);
+
+			if (signal_pending(current))
+				return -ERESTARTSYS;
+
+			if (copy_to_user((void *)arg, &itv->dec_timestamp,
+					 sizeof(itv->dec_timestamp))) {
+				return -EFAULT;
+			}
+
+			break;
+		}
+	case IVTV_IOC_PLAY:{
+			u32 data[IVTV_MBOX_MAX_DATA], result;
+			data[0] = 0;	/* 0-based frame # to start from (in GOP) */
+			data[1] = 0;	/* # of audio frames to mute */
+			if (ivtv_api(itv->dec_mbox, &itv->dec_msem,
+				     IVTV_API_DEC_START_PLAYBACK, &result, 2,
+				     &data[0]))
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "DEC: error starting playback\n");
+
+			break;
+
+		}
+	case IVTV_IOC_START_DECODE:
+	case IVTV_IOC_S_START_DECODE:{
+			struct ivtv_cfg_start_decode sd;
+			int ret = 0;
+
+			if (copy_from_user
+			    (&sd, (struct ivtv_cfg_start_decode *)arg,
+			     sizeof(sd)))
+				return -EFAULT;
+
+			if ((sd.gop_offset < 0) || (sd.gop_offset > 15))
+				return -EINVAL;
+			if (sd.muted_audio_frames < 0)
+				return -EINVAL;
+
+			itv->dec_options.gop_offset = sd.gop_offset;
+			itv->dec_options.mute_frames = sd.muted_audio_frames;
+
+			if (cmd == IVTV_IOC_S_START_DECODE)
+				break;
+
+			if (down_interruptible(&itv->sem_lock))
+				return -ERESTARTSYS;
+
+			ret = ivtv_start_v4l2_decode(id);
+
+			up(&itv->sem_lock);
+
+			return ret;
+			break;
+		}
+	case IVTV_IOC_STOP_DECODE:
+	case IVTV_IOC_S_STOP_DECODE:{
+			struct ivtv_cfg_stop_decode sd;
+
+			if (copy_from_user
+			    (&sd, (struct ivtv_cfg_stop_decode *)arg,
+			     sizeof(sd)))
+				return -EFAULT;
+
+			if ((sd.hide_last < 0) || (sd.hide_last > 1))
+				return -EINVAL;
+			itv->dec_options.hide_last_frame = sd.hide_last;
+
+			itv->dec_options.pts_low =
+			    (u32) (sd.pts_stop & 0xFFFFFFFF);
+			itv->dec_options.pts_hi = (u32) (sd.pts_stop >> 32);
+
+			if (cmd == IVTV_IOC_S_STOP_DECODE)
+				break;
+
+			if (down_interruptible(&id->itv->sem_lock))
+				return -ERESTARTSYS;
+
+			if (id->open_id == id->itv->v4l2.streams[id->type].id) {
+				ivtv_stop_decode(id);
+				clear_bit(IVTV_F_S_CAP,
+					  &id->itv->v4l2.streams[id->type].
+					  s_flags);
+				id->itv->v4l2.streams[id->type].id = -1;
+			}
+
+			up(&id->itv->sem_lock);
+
+			break;
+		}
+	case IVTV_IOC_DEC_FLUSH:{
+			if ((id->open_id == id->itv->v4l2.streams[id->type].id)
+			    || (id->itv->v4l2.streams[id->type].id == -1)) {
+				if (down_interruptible(&id->itv->sem_lock))
+					return -ERESTARTSYS;
+
+				ivtv_flush_queues(id);
+
+				up(&id->itv->sem_lock);
+			} else {
+				return -EBUSY;
+			}
+		}
+	case IVTV_IOC_DEC_STEP:{
+			int howfar, *fieldsel = (int *)arg;
+			u32 data[IVTV_MBOX_MAX_DATA], result;
+
+			get_user(howfar, fieldsel);
+
+			if (howfar < 0 || howfar > 2)
+				return -EINVAL;
+
+			data[0] = howfar;	/* 0 = 1 frame, 1 = top field, 2 = bottom field */
+			if (ivtv_api(itv->dec_mbox, &itv->dec_msem,
+				     IVTV_API_DEC_STEP_VIDEO, &result, 1,
+				     &data[0])) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "DEC: error stepping\n");
+			}
+
+			break;
+		}
+	case IVTV_IOC_G_SPEED:{
+			if (copy_to_user((void *)arg,
+					 &itv->dec_options.speed,
+					 sizeof(itv->dec_options.speed))) {
+				return -EFAULT;
+			}
+
+			break;
+		}
+	case IVTV_IOC_S_SPEED:{
+			struct ivtv_speed speed;
+			int ret = 0;
+
+			if (copy_from_user(&speed, (struct ivtv_speed *)arg,
+					   sizeof(speed)))
+				return -EFAULT;
+
+			ret = ivtv_change_speed(itv, speed);
+			if (ret) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "DEC: error in slow/fast mode\n");
+				return ret;
+			}
+
+			break;
+		}
+	case IVTV_IOC_S_SLOW_FAST:{
+			struct ivtv_slow_fast sf;
+			struct ivtv_speed speed;
+			int ret;
+
+			if (copy_from_user(&sf, (struct ivtv_slow_fast *)arg,
+					   sizeof(sf)))
+				return -EFAULT;
+
+			if ((sf.scale < 0) || (sf.scale > 50))
+				return -EINVAL;
+			if ((sf.speed < 0) || (sf.speed > 1))
+				return -EINVAL;
+
+			memcpy(&speed, &itv->dec_options.speed, sizeof(speed));
+			speed.scale = sf.scale;
+			speed.speed = sf.speed;
+
+			ret = ivtv_change_speed(itv, speed);
+			if (ret) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "DEC: error in set slow/fast mode\n");
+				return ret;
+			}
+
+			break;
+		}
+	case IVTV_IOC_PAUSE:{
+			u32 data[IVTV_MBOX_MAX_DATA], result;
+			data[0] = 0;
+			if (ivtv_api(itv->dec_mbox, &itv->dec_msem,
+				     IVTV_API_DEC_PAUSE_PLAYBACK, &result, 1,
+				     &data[0])) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "DEC: error pausing\n");
+			}
+
+			break;
+		}
+	case IVTV_IOC_GET_TIMING:{
+			struct ivtv_ioctl_framesync timing;
+
+			if (atomic_read(&itv->decoding) == 0) {
+				memset(&timing, 0, sizeof(timing));
+			} else {
+				memcpy(&timing, &itv->dec_timestamp,
+				       sizeof(timing));
+			}
+
+			if (copy_to_user((void *)arg, &timing, sizeof(timing))) {
+				return -EFAULT;
+			}
+
+			break;
+		}
+
+	case VIDIOC_QUERYMENU:{
+			//FIXME copy_from_user needed
+			struct v4l2_querymenu *qmenu =
+			    (struct v4l2_querymenu *)arg;
+
+			if (qmenu->id >= V4L2_CID_PRIVATE_BASE) {
+				int off = qmenu->id - V4L2_CID_PRIVATE_BASE;
+				if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
+					u32 i = qmenu->index;
+					if ((i >=
+					     itv->v4l2.audio_meta[off].ctrl->
+					     minimum)
+					    && (i <=
+						itv->v4l2.audio_meta[off].ctrl->
+						maximum)) {
+						memcpy(qmenu,
+						       &itv->v4l2.
+						       audio_meta[off].menu[i],
+						       sizeof(struct
+							      v4l2_querymenu));
+					} else {
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "qmenu: invalid index\n");
+						return -EINVAL;
+					}
+				} else {
+					IVTV_DEBUG(IVTV_DEBUG_ERR,
+						   "qmenu: id out of range\n");
+					return -EINVAL;
+				}
+
+			}
+
+			break;
+		}
+
+	case VIDIOC_QUERYCTRL:{
+			//FIXME copy_from_user needed
+			struct v4l2_queryctrl *qctrl =
+			    (struct v4l2_queryctrl *)arg;
+
+			if (qctrl->id >= V4L2_CID_PRIVATE_BASE) {
+				int off = qctrl->id - V4L2_CID_PRIVATE_BASE;
+				if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
+					memcpy(qctrl,
+					       itv->v4l2.audio_meta[off].ctrl,
+					       sizeof(struct v4l2_queryctrl));
+				} else {
+					switch (qctrl->id) {
+					case V4L2_CID_IVTV_DEC_SMOOTH_FF:
+						qctrl->type =
+						    V4L2_CTRL_TYPE_BOOLEAN;
+						strncpy(qctrl->name,
+							"Smooth Slow/FF", 32);
+						qctrl->minimum = 0;
+						qctrl->maximum = 1;
+						qctrl->default_value = 1;
+						qctrl->flags = 0;
+						qctrl->reserved[0] = 0;
+						qctrl->reserved[1] = 0;
+						break;
+					case V4L2_CID_IVTV_DEC_FR_MASK:
+						qctrl->type =
+						    V4L2_CTRL_TYPE_INTEGER;
+						strncpy(qctrl->name,
+							"Frame Mask", 32);
+						qctrl->minimum = 0;
+						qctrl->maximum = 2;
+						qctrl->default_value = 2;
+						qctrl->flags = 0;
+						qctrl->reserved[0] = 0;
+						qctrl->reserved[1] = 0;
+						break;
+					case V4L2_CID_IVTV_DEC_SP_MUTE:
+						qctrl->type =
+						    V4L2_CTRL_TYPE_BOOLEAN;
+						strncpy(qctrl->name,
+							"Mute during slow/fast",
+							32);
+						qctrl->minimum = 0;
+						qctrl->maximum = 1;
+						qctrl->default_value = 1;
+						qctrl->flags = 0;
+						qctrl->reserved[0] = 0;
+						qctrl->reserved[1] = 0;
+						break;
+					case V4L2_CID_IVTV_DEC_FR_FIELD:
+						qctrl->type =
+						    V4L2_CTRL_TYPE_BOOLEAN;
+						strncpy(qctrl->name,
+							"Toggle frame/field",
+							32);
+						qctrl->minimum = 0;
+						qctrl->maximum = 1;
+						qctrl->default_value = 1;
+						qctrl->flags = 0;
+						qctrl->reserved[0] = 0;
+						qctrl->reserved[1] = 0;
+						break;
+					case V4L2_CID_IVTV_DEC_AUD_SKIP:
+						qctrl->type =
+						    V4L2_CTRL_TYPE_INTEGER;
+						strncpy(qctrl->name,
+							"Mute audio frames",
+							32);
+						qctrl->minimum = 0;
+						qctrl->maximum = 15;
+						qctrl->default_value = 0;
+						qctrl->flags = 0;
+						qctrl->reserved[0] = 0;
+						qctrl->reserved[1] = 0;
+						break;
+					case V4L2_CID_IVTV_DEC_NUM_BUFFERS:
+						qctrl->type =
+						    V4L2_CTRL_TYPE_BOOLEAN;
+						strncpy(qctrl->name,
+							"Number of decoder buffers",
+							32);
+						qctrl->minimum = 0;
+						qctrl->maximum = 1;
+						qctrl->default_value = 1;
+						qctrl->flags = 0;
+						qctrl->reserved[0] = 0;
+						qctrl->reserved[1] = 1;
+						break;
+					case V4L2_CID_IVTV_DEC_PREBUFFER:
+						qctrl->type =
+						    V4L2_CTRL_TYPE_BOOLEAN;
+						strncpy(qctrl->name,
+							"Decoder prebuffer",
+							32);
+						qctrl->minimum = 0;
+						qctrl->maximum = 1;
+						qctrl->default_value = 1;
+						qctrl->flags = 0;
+						qctrl->reserved[0] = 0;
+						qctrl->reserved[1] = 1;
+						break;
+					default:
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "qctrl: invalid control\n");
+						return -EINVAL;
+						break;
+					}
+				}
+
+				break;
+			}
+
+			switch (qctrl->id) {
+			case V4L2_CID_BRIGHTNESS:
+				qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+				strncpy(qctrl->name, "Brightness", 32);
+				qctrl->minimum = 0;
+				qctrl->maximum = 255;
+				qctrl->step = 0;
+				qctrl->default_value = 128;
+				qctrl->flags = 0;
+				qctrl->reserved[0] = 0;
+				qctrl->reserved[1] = 0;
+				break;
+			case V4L2_CID_HUE:
+				qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+				strncpy(qctrl->name, "Hue", 32);
+				qctrl->minimum = -128;
+				qctrl->maximum = 127;
+				qctrl->step = 0;
+				qctrl->default_value = 0;
+				qctrl->flags = 0;
+				qctrl->reserved[0] = 0;
+				qctrl->reserved[1] = 0;
+				break;
+			case V4L2_CID_SATURATION:
+				qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+				strncpy(qctrl->name, "Saturation", 32);
+				qctrl->minimum = 0;
+				qctrl->maximum = 127;
+				qctrl->step = 0;
+				qctrl->default_value = 64;
+				qctrl->flags = 0;
+				qctrl->reserved[0] = 0;
+				qctrl->reserved[1] = 0;
+				break;
+			case V4L2_CID_CONTRAST:
+				qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+				strncpy(qctrl->name, "Contrast", 32);
+				qctrl->minimum = 0;
+				qctrl->maximum = 127;
+				qctrl->step = 0;
+				qctrl->default_value = 64;
+				qctrl->flags = 0;
+				qctrl->reserved[0] = 0;
+				qctrl->reserved[1] = 0;
+				break;
+			case V4L2_CID_AUDIO_VOLUME:
+				qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+				strncpy(qctrl->name, "Volume", 32);
+				qctrl->minimum = 0;
+				qctrl->maximum = 65535;
+				qctrl->step = 0;
+				qctrl->default_value = 65535;
+				qctrl->flags = 0;
+				qctrl->reserved[0] = 0;
+				qctrl->reserved[1] = 0;
+				break;
+			case V4L2_CID_AUDIO_MUTE:
+				qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+				strncpy(qctrl->name, "Mute", 32);
+				qctrl->minimum = 0;
+				qctrl->maximum = 1;
+				qctrl->step = 0;
+				qctrl->default_value = 1;
+				qctrl->flags = 0;
+				qctrl->reserved[0] = 0;
+				qctrl->reserved[1] = 0;
+				break;
+			default:
+				IVTV_DEBUG(IVTV_DEBUG_INFO,
+					   "v4l2 ioctl: invalid control\n");
+				return -EINVAL;
+			}
+			break;
+		}
+	case VIDIOC_S_CTRL:{
+			//FIXME copy_from_user needed
+			struct v4l2_control *vctrl = (struct v4l2_control *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: set control\n");
+
+			if (vctrl->id >= V4L2_CID_PRIVATE_BASE) {
+				int off = vctrl->id - V4L2_CID_PRIVATE_BASE;
+				s32 v = vctrl->value;
+				if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
+					if ((v <=
+					     itv->v4l2.audio_meta[off].ctrl->
+					     maximum)
+					    && (v >=
+						itv->v4l2.audio_meta[off].ctrl->
+						minimum)) {
+						itv->v4l2.audio_meta[off].
+						    setting = v;
+						/* presumably value has changed.
+						 * we should update the bitmap */
+						itv->v4l2.codec.audio_bitmap &=
+						    ~itv->v4l2.audio_meta[off].
+						    mask;
+						itv->v4l2.codec.audio_bitmap |=
+						    itv->v4l2.audio_meta[off].
+						    table[v];
+
+						/* Also upade the digitizer setting */
+						if (0 == off) {	/* audio input bitrate */
+							int vrate = (int)v;
+							/* FIXME not obvious how this works
+							 *  (see ivtv_ctrl_query_freq[]) */
+							ivtv_call_i2c_client
+							    (itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_SET_AUDIO,
+							     &vrate);
+						}
+					} else {
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: value out of range\n");
+						return -ERANGE;
+					}
+				} else {
+					switch (vctrl->id) {
+					case V4L2_CID_IVTV_DEC_SMOOTH_FF:
+						if ((v < 0) || (v > 1))
+							return -ERANGE;
+						itv->dec_options.speed.smooth =
+						    vctrl->value;
+						break;
+					case V4L2_CID_IVTV_DEC_FR_MASK:
+						if ((v < 0) || (v > 2))
+							return -ERANGE;
+						itv->dec_options.speed.fr_mask =
+						    vctrl->value;
+						break;
+					case V4L2_CID_IVTV_DEC_SP_MUTE:
+						if ((v < 0) || (v > 1))
+							return -ERANGE;
+						itv->dec_options.speed.
+						    aud_mute = vctrl->value;
+						break;
+					case V4L2_CID_IVTV_DEC_FR_FIELD:
+						if ((v < 0) || (v > 1))
+							return -ERANGE;
+						itv->dec_options.speed.
+						    fr_field = vctrl->value;
+						break;
+					case V4L2_CID_IVTV_DEC_AUD_SKIP:
+						if ((v < 0) || (v > 15))
+							return -ERANGE;
+						itv->dec_options.mute_frames =
+						    vctrl->value;
+						break;
+					case V4L2_CID_IVTV_DEC_NUM_BUFFERS:
+						if ((v < 0) || (v > 1))
+							return -ERANGE;
+						itv->dec_options.decbuffers =
+						    vctrl->value;
+						break;
+					case V4L2_CID_IVTV_DEC_PREBUFFER:
+						if ((v < 0) || (v > 1))
+							return -ERANGE;
+						itv->dec_options.prebuffer =
+						    vctrl->value;
+						break;
+					default:
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: invalid control\n");
+						return -EINVAL;
+					}
+				}
+
+				break;
+			}
+
+			switch (vctrl->id) {
+			case V4L2_CID_BRIGHTNESS:{
+					struct saa7114 pic;
+
+					if (vctrl->value < 0
+					    || vctrl->value > 255) {
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: invalid brightness value: %d\n",
+							   vctrl->value);
+						return -EINVAL;
+					}
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					pic.bright = vctrl->value;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_SET_PICTURE,
+							     &pic);
+					break;
+				}
+			case V4L2_CID_HUE:{
+					struct saa7114 pic;
+
+					if (vctrl->value < -128
+					    || vctrl->value > 127) {
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: invalid hue value: %d\n",
+							   vctrl->value);
+						return -EINVAL;
+					}
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					pic.hue = vctrl->value;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_SET_PICTURE,
+							     &pic);
+					break;
+				}
+			case V4L2_CID_SATURATION:{
+					struct saa7114 pic;
+
+					if (vctrl->value < 0
+					    || vctrl->value > 127) {
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: invalid saturation value: %d\n",
+							   vctrl->value);
+						return -EINVAL;
+					}
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					pic.sat = vctrl->value;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_SET_PICTURE,
+							     &pic);
+					break;
+				}
+			case V4L2_CID_CONTRAST:{
+					struct saa7114 pic;
+
+					if (vctrl->value < 0
+					    || vctrl->value > 127) {
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: invalid contrast value: %d\n",
+							   vctrl->value);
+						return -EINVAL;
+					}
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					pic.contrast = vctrl->value;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_SET_PICTURE,
+							     &pic);
+					break;
+				}
+			case V4L2_CID_AUDIO_VOLUME:{
+					struct video_audio va;
+
+					if (vctrl->value > 65535
+					    || vctrl->value < 0) {
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: invalid value for volume: %d",
+							   vctrl->value);
+						return -EINVAL;
+					}
+					ivtv_call_i2c_client(itv,
+							     IVTV_MSP3400_I2C_ADDR,
+							     VIDIOCGAUDIO, &va);
+					va.volume = vctrl->value;
+					ivtv_call_i2c_client(itv,
+							     IVTV_MSP3400_I2C_ADDR,
+							     VIDIOCSAUDIO, &va);
+					break;
+				}
+			case V4L2_CID_AUDIO_MUTE:{
+					struct video_audio va;
+					ivtv_call_i2c_client(itv,
+							     IVTV_MSP3400_I2C_ADDR,
+							     VIDIOCGAUDIO, &va);
+					if (vctrl->value)
+						va.flags |= VIDEO_AUDIO_MUTE;
+					else
+						va.flags =
+						    (va.
+						     flags &
+						     ~(VIDEO_AUDIO_MUTE));
+					ivtv_call_i2c_client(itv,
+							     IVTV_MSP3400_I2C_ADDR,
+							     VIDIOCSAUDIO, &va);
+					break;
+				}
+			default:
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "ctrl: invalid control\n");
+				return -EINVAL;
+			}
+
+			break;
+		}
+	case VIDIOC_G_CTRL:{
+			//FIXME copy_from_user needed
+			struct v4l2_control *vctrl = (struct v4l2_control *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: get control\n");
+
+			if (vctrl->id >= V4L2_CID_PRIVATE_BASE) {
+				int off = vctrl->id - V4L2_CID_PRIVATE_BASE;
+				if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
+					vctrl->value =
+					    itv->v4l2.audio_meta[off].setting;
+				} else {
+					switch (vctrl->id) {
+					case V4L2_CID_IVTV_DEC_SMOOTH_FF:
+						vctrl->value =
+						    itv->dec_options.speed.
+						    smooth;
+						break;
+					case V4L2_CID_IVTV_DEC_FR_MASK:
+						vctrl->value =
+						    itv->dec_options.speed.
+						    fr_mask;
+						break;
+					case V4L2_CID_IVTV_DEC_SP_MUTE:
+						vctrl->value =
+						    itv->dec_options.speed.
+						    aud_mute;
+						break;
+					case V4L2_CID_IVTV_DEC_FR_FIELD:
+						vctrl->value =
+						    itv->dec_options.speed.
+						    fr_field;
+						break;
+					case V4L2_CID_IVTV_DEC_AUD_SKIP:
+						vctrl->value =
+						    itv->dec_options.
+						    mute_frames;
+						break;
+					case V4L2_CID_IVTV_DEC_NUM_BUFFERS:
+						vctrl->value =
+						    itv->dec_options.decbuffers;
+						break;
+					case V4L2_CID_IVTV_DEC_PREBUFFER:
+						vctrl->value =
+						    itv->dec_options.prebuffer;
+						break;
+					default:
+						IVTV_DEBUG(IVTV_DEBUG_ERR,
+							   "ctrl: invalid control\n");
+						return -EINVAL;
+					}
+				}
+
+				break;
+			}
+
+			switch (vctrl->id) {
+			case V4L2_CID_BRIGHTNESS:{
+					struct saa7114 pic;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					vctrl->value = pic.bright;
+					break;
+				}
+			case V4L2_CID_HUE:{
+					struct saa7114 pic;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					vctrl->value = pic.hue;
+					break;
+				}
+			case V4L2_CID_SATURATION:{
+					struct saa7114 pic;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					vctrl->value = pic.sat;
+					break;
+				}
+			case V4L2_CID_CONTRAST:{
+					struct saa7114 pic;
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_GET_PICTURE,
+							     &pic);
+					vctrl->value = pic.contrast;
+					break;
+				}
+			case V4L2_CID_AUDIO_VOLUME:{
+					struct video_audio va;
+					ivtv_call_i2c_client(itv,
+							     IVTV_MSP3400_I2C_ADDR,
+							     VIDIOCGAUDIO, &va);
+					vctrl->value = va.volume;
+					break;
+				}
+			case V4L2_CID_AUDIO_MUTE:{
+					struct video_audio va;
+					ivtv_call_i2c_client(itv,
+							     IVTV_MSP3400_I2C_ADDR,
+							     VIDIOCGAUDIO, &va);
+					vctrl->value =
+					    (va.flags & VIDEO_AUDIO_MUTE);
+					break;
+				}
+			default:
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "ctrl: invalid control\n");
+				return -EINVAL;
+			}
+			break;
+		}
+	case VIDIOC_QUERYCAP:{
+			//FIXME copy_from_user needed
+			struct v4l2_capability *vcap =
+			    (struct v4l2_capability *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: querycap\n");
+
+			/* driver name */
+			strcpy(vcap->driver, IVTV_DRIVER_NAME);
+
+			/* card type */
+			strcpy(vcap->card,
+			       id->itv->v4l2.streams[id->type].v4l2dev->name);
+
+			/* bus info.. card # will do */
+			sprintf(vcap->bus_info, "%d", itv->num);
+
+			/* version */
+			vcap->version = IVTV_DRIVER_VERSION;
+
+			/* capabilities */
+			vcap->capabilities = itv->v4l2.capabilities;
+
+			/* reserved.. must set to 0! */
+			vcap->reserved[0] = vcap->reserved[1] =
+			    vcap->reserved[2] = vcap->reserved[3] = 0;
+			break;
+		}
+	case VIDIOC_ENUMINPUT:{
+			//FIXME copy_from_user needed
+			struct v4l2_input *vin = (struct v4l2_input *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: enuminput\n");
+
+			if ((vin->index < 0)
+			    || (vin->index >= itv->v4l2.input.count))
+				return -EINVAL;
+
+			/* set it to defaults from our table */
+			memcpy(vin,
+			       &itv->v4l2.input.table.input[vin->index],
+			       sizeof(struct v4l2_input));
+
+			/* set the standard to whatever our overall standard is */
+			vin->std = tmk_standards[itv->v4l2.standard.active].id;
+			vin->status = 0;	/*FIXME status isn't always ok... */
+
+			break;
+		}
+
+	case VIDIOC_G_FMT:{
+			//FIXME copy_from_user needed
+			struct v4l2_format *vfmt = (struct v4l2_format *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: get format\n");
+
+			/* FIXME switch on stream type */
+			memcpy(vfmt, &itv->v4l2.streams[0].format,
+			       sizeof(struct v4l2_format));
+			break;
+		}
+	case VIDIOC_S_FMT:{
+			//FIXME copy_from_user needed
+			struct v4l2_format *vfmt = (struct v4l2_format *)arg;
+			struct video_window wind;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: set format\n");
+
+			/* FIXME only sets resolution for now */
+			wind.width = vfmt->fmt.pix.width;
+			wind.height = vfmt->fmt.pix.height;
+			ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR,
+					     DECODER_SET_SIZE, &wind);
+
+			/* FIXME switch on stream type, bounds checking */
+			memcpy(&itv->v4l2.streams[0].format, vfmt,
+			       sizeof(struct v4l2_format));
+			/* Adjust res in YUV also */
+			itv->v4l2.streams[1].format.fmt.pix.height =
+			    vfmt->fmt.pix.height;
+			itv->v4l2.streams[1].format.fmt.pix.width =
+			    vfmt->fmt.pix.width;
+
+			break;
+		}
+	case VIDIOC_G_INPUT:{
+			//FIXME copy_from_user needed
+			int *inp = (int *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: get input\n");
+
+			*inp = itv->v4l2.input.active;
+			break;
+		}
+	case VIDIOC_S_INPUT:{
+			//FIXME copy_from_user needed
+			int a_in, inp = *(int *)arg;
+			struct msp_matrix mspm;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: set input\n");
+
+			if ((inp < 0) || (inp >= itv->v4l2.input.count))
+				return -EINVAL;
+
+			if (inp == itv->v4l2.input.active) {
+				IVTV_DEBUG(IVTV_DEBUG_INFO,
+					   "Input unchanged\n");
+			} else {
+				IVTV_DEBUG(IVTV_DEBUG_INFO,
+					   "Changing input from %d to %d\n",
+					   itv->v4l2.input.active, inp);
+
+				itv->v4l2.input.active = inp;
+				itv->v4l2.audio.active =
+				    itv->v4l2.input.table.input[inp].audioset;
+
+				/* Mute sound to avoid pop */
+				mspm.input = 8;
+				mspm.output = itv->v4l2.audio_output;
+				ivtv_call_i2c_client(itv, IVTV_MSP3400_I2C_ADDR,
+						     MSP_SET_MATRIX, &mspm);
+
+				if (0 != ivtv_pause_encoder(itv, 0))
+					IVTV_DEBUG(IVTV_DEBUG_ERR,
+						   "Input: Error pausing stream\n");
+
+				ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR,
+						     DECODER_SET_INPUT, &inp);
+
+				/* Pause to let sound calm down */
+				ivtv_sleep_timeout(HZ / 33);
+
+				if (0 != ivtv_pause_encoder(itv, 1))
+					IVTV_DEBUG(IVTV_DEBUG_ERR,
+						   "Input: Error unpausing stream\n");
+
+				/* FIXME Needs to be card-specific */
+				a_in = ivtv_set_audio(itv, tmk_audio_mapping);
+				if (a_in < 0)
+					return a_in;
+			}
+			break;
+		}
+	case VIDIOC_G_FREQUENCY:{
+			//FIXME copy_from_user needed
+			struct v4l2_frequency *vf =
+			    (struct v4l2_frequency *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: get frequency\n");
+
+			if ((vf->tuner < 0)
+			    || (vf->tuner >= itv->v4l2.tuner.count))
+				return -EINVAL;
+			vf->frequency = itv->v4l2.freq.frequency;
+			break;
+		}
+	case VIDIOC_S_FREQUENCY:{
+			//FIXME copy_from_user needed
+			struct v4l2_frequency *vf =
+			    (struct v4l2_frequency *)arg;
+			struct msp_matrix mspm;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: set frequency\n");
+
+			if ((vf->tuner < 0)
+			    || (vf->tuner >= itv->v4l2.tuner.count))
+				return -EINVAL;
+			itv->v4l2.freq.frequency = vf->frequency;
+
+			/* Mute sound to avoid pop */
+			mspm.input = 8;
+			mspm.output = itv->v4l2.audio_output;
+			ivtv_call_i2c_client(itv, IVTV_MSP3400_I2C_ADDR,
+					     MSP_SET_MATRIX, &mspm);
+
+			if (0 != ivtv_pause_encoder(itv, 0))
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "Freq: Error pausing stream\n");
+
+			/* Set frequency */
+			ivtv_call_i2c_client(itv, IVTV_TUNER_I2C_ADDR,
+					     VIDIOCSFREQ,
+					     &itv->v4l2.freq.frequency);
+
+			/* Pause to let sound calm down */
+			ivtv_sleep_timeout(HZ / 33);
+
+			if (0 != ivtv_pause_encoder(itv, 1))
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "Freq: Error unpausing stream\n");
+
+			/* Unmute */
+			ivtv_set_audio(itv, tmk_audio_mapping);
+
+			break;
+		}
+	case VIDIOC_ENUMSTD:{
+			//FIXME copy_from_user needed
+			struct v4l2_standard *vs = (struct v4l2_standard *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: enum standard\n");
+
+			if ((vs->index < 0)
+			    || (vs->index >= itv->v4l2.standard.count))
+				return -EINVAL;
+
+			memcpy(vs, &itv->v4l2.standard.table.std[vs->index],
+			       sizeof(struct v4l2_standard));
+
+			break;
+		}
+	case VIDIOC_G_STD:{
+			//FIXME copy_from_user needed
+			v4l2_std_id *vs = (v4l2_std_id *) arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: get standard\n");
+
+			*vs =
+			    itv->v4l2.standard.table.std[itv->v4l2.standard.
+							 active].id;
+			break;
+		}
+	case VIDIOC_S_STD:{
+			//FIXME copy_from_user needed
+			v4l2_std_id *vs = (v4l2_std_id *) arg;
+			struct video_channel v;
+			int x;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: set standard\n");
+
+			for (x = 0; x < itv->v4l2.standard.count; x++) {
+				if (itv->v4l2.standard.table.std[x].id & *vs) {
+					IVTV_DEBUG(IVTV_DEBUG_INFO,
+						   "Switching standard to %s.\n",
+						   itv->v4l2.standard.table.
+						   std[x].name);
+					itv->v4l2.standard.active = x;
+					/* fixme set standard here */
+					switch (itv->v4l2.standard.active) {
+					case 0:	/* NTSC */
+						v.norm = VIDEO_MODE_NTSC;
+						break;
+					case 1:	/* PAL */
+						v.norm = VIDEO_MODE_PAL;
+						break;
+					case 2:	/* SECAM */
+						v.norm = VIDEO_MODE_SECAM;
+						break;
+					default:
+						break;
+					}
+
+					/* Tuner */
+					ivtv_call_i2c_client(itv,
+							     IVTV_TUNER_I2C_ADDR,
+							     VIDIOCSCHAN, &v);
+					/* Tuner Audio */
+					ivtv_call_i2c_client(itv,
+							     IVTV_MSP3400_I2C_ADDR,
+							     VIDIOCSCHAN, &v);
+					/* Digitizer */
+					ivtv_call_i2c_client(itv,
+							     IVTV_SAA7115_I2C_ADDR,
+							     DECODER_SET_NORM,
+							     &v.norm);
+
+					if (itv->v4l2.standard.active == 0) {	// NTSC
+						itv->v4l2.codec.framespergop =
+						    15;
+						itv->v4l2.codec.framerate = 0;
+					} else {	// PAL
+						itv->v4l2.codec.framespergop =
+						    12;
+						itv->v4l2.codec.framerate = 1;
+					}
+
+					return 0;
+				}
+			}
+			return -EINVAL;
+		}
+	case VIDIOC_S_TUNER:{	/* Setting tuner can only set audio mode */
+			//FIXME copy_from_user needed
+			struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: set tuner\n");
+
+			if ((vt->index < 0)
+			    || (vt->index >= itv->v4l2.tuner.count))
+				return -EINVAL;
+			/* looks like tuner.c doesn't support selection 
+			 * fallback to stereo... */
+			vt->audmode = V4L2_TUNER_MODE_STEREO;
+
+			break;
+		}
+	case VIDIOC_G_TUNER:{
+			//FIXME copy_from_user needed
+			struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+			int sig = 0;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: get tuner\n");
+
+			if ((vt->index < 0)
+			    || (vt->index >= itv->v4l2.tuner.count))
+				return -EINVAL;
+
+			memcpy(vt, &itv->v4l2.tuner.table.tuner[vt->index],
+			       sizeof(struct v4l2_tuner));
+
+			ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR,
+					     DECODER_GET_STATUS, &sig);
+
+			if (sig & DECODER_STATUS_GOOD) {
+				vt->signal = 65535;	/* best possible signal */
+			} else {
+				vt->signal = 0;
+			}
+			break;
+		}
+	case MSP_SET_MATRIX:{
+			//FIXME copy_from_user needed
+			struct msp_matrix *mspm = (struct msp_matrix *)arg;
+
+			/* FIXME hardcoding! */
+			if ((mspm->input < 1) || (mspm->input > 8)) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "Invalid audio input!\n");
+				return -EINVAL;
+			}
+			if ((mspm->output < 0) || (mspm->output > 3)) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "Invalid audio output!\n");
+				return -EINVAL;
+			}
+
+			itv->v4l2.audio_output = mspm->output;
+
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "v4l2 ioctl: set matrix in=%d,out=%d\n",
+				   mspm->input, mspm->output);
+
+			ivtv_call_i2c_client(itv, IVTV_MSP3400_I2C_ADDR,
+					     MSP_SET_MATRIX, mspm);
+			break;
+		}
+	case IVTV_IOC_G_CODEC:{
+			//FIXME copy_from_user needed
+			struct ivtv_ioctl_codec *codec =
+			    (struct ivtv_ioctl_codec *)arg;
+
+			/* FIXME: bounds check? */
+			memcpy(codec, &(itv->v4l2.codec),
+			       sizeof(struct ivtv_ioctl_codec));
+			break;
+		}
+	case IVTV_IOC_S_CODEC:{
+			//FIXME copy_from_user needed
+			struct ivtv_ioctl_codec *codec =
+			    (struct ivtv_ioctl_codec *)arg;
+
+			/* FIXME: insert abundant parameter validation here */
+			if ((codec->bitrate == 0) || (codec->bitrate_peak == 0)
+			    || (codec->bitrate > codec->bitrate_peak)) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "ivtv ioctl: set "
+					   "bitrate=%u < peak=%u: failed\n",
+					   codec->bitrate, codec->bitrate_peak);
+				return -EINVAL;
+			} else {
+				/* Passed the garbage check */
+				memcpy(&(itv->v4l2.codec), codec,
+				       sizeof(struct ivtv_ioctl_codec));
+			}
+
+			/* VCD streamtype has some quirks. Handle them here */
+			if ((codec->stream_type == IVTV_STREAM_VCD) ||
+			    (codec->stream_type == IVTV_STREAM_MPEG1)) {
+				struct v4l2_format *vfmt =
+				    (struct v4l2_format *)arg;
+				struct video_window wind;
+				int tmpsize = 480;
+
+				if (itv->v4l2.standard.active == 1)
+					tmpsize = 576;
+
+				IVTV_DEBUG(IVTV_DEBUG_INFO,
+					   "ivtv ioctl: mpeg1_stream "
+					   "size %d\n", tmpsize);
+
+				/* so far it looks like you can change width at will     *
+				 * but the compressor is unhappy when the height changes *
+				 * to anything other than 240 */
+				wind.width = 352;
+				wind.height = tmpsize;
+				vfmt->fmt.pix.width = 352;
+				vfmt->fmt.pix.height = tmpsize / 2;
+
+				ivtv_call_i2c_client(itv,
+						     IVTV_SAA7115_I2C_ADDR,
+						     DECODER_SET_SIZE, &wind);
+				memcpy(&itv->v4l2.streams[0].format, vfmt,
+				       sizeof(struct v4l2_format));
+			}
+
+			break;
+		}
+	case IVTV_IOCTL_GET_DEBUG_LEVEL:{
+			//FIXME copy_from_user needed
+			int *dbg_level = (int *)arg;
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "IVTV_IOCTL_GET_DEBUG_LEVEL ivtv_debug = "
+				   "0x%08x\n", ivtv_debug);
+			if (dbg_level) {
+				put_user(ivtv_debug, dbg_level);
+			} else {
+				printk
+				    ("ivtv: Error: IVTV_IOCTL_GET_DEBUG_LEVEL called with "
+				     "NULL\n");
+			}
+			break;
+		}
+	case IVTV_IOCTL_SET_DEBUG_LEVEL:{
+			//FIXME copy_from_user needed
+			int *dbg_level = (int *)arg;
+			int old_debug_level = ivtv_debug;
+			get_user(ivtv_debug, dbg_level);
+			if (!(ivtv_debug & IVTV_DEBUG_ERR))
+				ivtv_debug |= IVTV_DEBUG_ERR;
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "IVTV_IOCTL_SET_DEBUG_LEVEL ivtv_debug = "
+				   "0x%08x (new) 0x%08x (old)\n",
+				   ivtv_debug, old_debug_level);
+			put_user(ivtv_debug, dbg_level);
+			break;
+		}
+	case VIDIOC_STREAMOFF:{
+			ivtv_v4l2_streamoff(id);
+			break;
+		}
+
+	case 0x00005401:	/* Handle isatty() calls */
+		return -EINVAL;
+	default:
+		/* If it got here, it's probably not supported.. */
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "ivtv-api.c: unknown ioctl 0x%08x\n",
+			   cmd);
+		return -ENOTTY;
+	}
+	return 0;
+}
diff -upN /dev/null current/drivers/media/video/ivtv-driver.c
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/ivtv-driver.c	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,3073 @@
+/* Main Driver file for the ivtv project:
+ * Driver for the iTVC15 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.sourceforge.net/projects/ivtv/
+ */
+/* Hack - this file needs to be converted to use the firmware api */
+#define __KERNEL_SYSCALLS__
+
+#include "ivtv.h"
+
+// Version info
+#define IVTV_VERSION_NUMBER(name) name##_version_int
+#define IVTV_VERSION_STRING(name) name##_version_string
+#define IVTV_VERSION_COMMENT(name) name##_comment_string
+
+#define IVTV_DEFINE_VERSION_INTERNAL(name, major, minor, patchlevel, comment) \
+unsigned int IVTV_VERSION_NUMBER(name) = ((major << 16) | (minor << 8) | (patchlevel)); \
+const char * const IVTV_VERSION_STRING(name) = #major"."#minor"."#patchlevel;\
+const char * const IVTV_VERSION_COMMENT(name) = comment;
+
+#define IVTV_VERSION_MAJOR(name) (0xFF & (IVTV_VERSION_NUMBER(name) >> 16))
+#define IVTV_VERSION_MINOR(name) (0xFF & (IVTV_VERSION_NUMBER(name) >> 8))
+#define IVTV_VERSION_PATCHLEVEL(name) (0xFF & (IVTV_VERSION_NUMBER(name)))
+
+#define IVTV_DEFINE_VERSION(name, major, minor, patchlevel, comment) IVTV_DEFINE_VERSION_INTERNAL(name, major, minor, patchlevel, comment)
+
+IVTV_DEFINE_VERSION(ivtv_rev,
+		    IVTV_DRIVER_VERSION_MAJOR,
+		    IVTV_DRIVER_VERSION_MINOR,
+		    IVTV_DRIVER_VERSION_PATCHLEVEL, "release");
+
+/* mini header */
+
+/* var to keep track of the number of array elements in use */
+int ivtv_cards_active = 0;
+
+/* Master variable for all ivtv info */
+struct ivtv ivtv_cards[IVTV_MAX_CARDS];
+
+/* for the global data */
+spinlock_t ivtv_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+
+/* add your revision and whatnot here */
+static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+static void ivtv_irq_dec_vsync(struct ivtv *itv);
+static irqreturn_t ivtv_irq_handler(int irq, void *dev_id,
+				    struct pt_regs *regs);
+static void ivtv_DMA_done(struct ivtv *itv);
+static void ivtv_sched_DMA(struct ivtv *itv);
+static void ivtv_dec_DMA_done(struct ivtv *itv);
+static void ivtv_dec_sched_DMA(struct ivtv *itv);
+
+static u32 ivtv_firm_search_id[] =
+    { 0x12345678, 0x34567812, 0x56781234, 0x78123456 };
+
+/* Parameter declarations */
+static int num_devices = IVTV_DEFAULT_NUM_CARDS;
+static int yuv_buffers = IVTV_DEFAULT_YUV_BUFFERS;
+static int mpg_buffers = IVTV_DEFAULT_MPG_BUFFERS;
+static int vbi_buffers = IVTV_DEFAULT_VBI_BUFFERS;
+static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
+static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
+static int dec_mpg_qlen = IVTV_DEFAULT_DEC_MPG_QLEN;
+static int dec_yuv_qlen = IVTV_DEFAULT_DEC_YUV_QLEN;
+#ifdef YUV_FIXUP
+static int yuv_fixup;
+#endif
+
+int ivtv_pal = 0;
+
+/* low debugging by default */
+#if 0
+int debug = (IVTV_DEBUG_ERR | IVTV_DEBUG_INFO | IVTV_DEBUG_API
+	     | IVTV_DEBUG_DMA | IVTV_DEBUG_IOCTL | IVTV_DEBUG_I2C
+	     | IVTV_DEBUG_IRQ);
+#endif
+int ivtv_debug = IVTV_DEBUG_ERR;
+
+/* tuner.h tuner type for ivtv card */
+int tuner = -1;
+
+int errno;
+
+#define EXPAND_TO_STRING_INTERNAL(arg) #arg
+#define EXPAND_TO_STRING(arg) EXPAND_TO_STRING_INTERNAL(arg)
+
+#ifdef YUV_FIXUP
+MODULE_PARM(yuv_fixup, "i");
+MODULE_PARM_DESC(yuv_fixup,
+		 "\nToggles conversion of Hauppauge Macroblock NV12 to NV12\n");
+#endif
+
+MODULE_PARM(tuner, "i");
+MODULE_PARM_DESC(tuner, "\nTuner type selection, see tuner.h for values");
+
+MODULE_PARM(yuv_buffers, "i");
+MODULE_PARM_DESC(yuv_buffers,
+		 "\nNumber of 32K buffers for copying YUV.\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_YUV_BUFFERS) ", "
+		 "Min: " EXPAND_TO_STRING(IVTV_MIN_YUV_BUFFERS) " "
+		 "Max: " EXPAND_TO_STRING(IVTV_MAX_YUV_BUFFERS));
+
+MODULE_PARM(mpg_buffers, "i");
+MODULE_PARM_DESC(mpg_buffers,
+		 "\nNumber of 32K buffers for copying mpg.\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_MPG_BUFFERS) ", "
+		 "Min: " EXPAND_TO_STRING(IVTV_MIN_MPG_BUFFERS) " "
+		 "Max: " EXPAND_TO_STRING(IVTV_MAX_MPG_BUFFERS));
+
+MODULE_PARM(vbi_buffers, "i");
+MODULE_PARM_DESC(vbi_buffers,
+		 "\nNumber of 32K buffers for copying VBI.\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_VBI_BUFFERS) ", "
+		 "Min: " EXPAND_TO_STRING(IVTV_MIN_VBI_BUFFERS) " "
+		 "Max: " EXPAND_TO_STRING(IVTV_MAX_VBI_BUFFERS));
+
+MODULE_PARM(num_devices, "i");
+MODULE_PARM_DESC(num_devices, "\nNumber of supported devices (1-9).\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_NUM_CARDS));
+
+MODULE_PARM(dec_mpg_buffers, "i");
+MODULE_PARM_DESC(dec_mpg_buffers,
+		 "\nNumber of 32K buffers for decoding MPG.\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_MPG_BUFFERS) ", "
+		 "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_MPG_BUFFERS) " "
+		 "Max: " EXPAND_TO_STRING(IVTV_MAX_DEC_MPG_BUFFERS));
+
+MODULE_PARM(dec_yuv_buffers, "i");
+MODULE_PARM_DESC(dec_yuv_buffers,
+		 "\nNumber of 32K buffers for decoding YUV.\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_YUV_BUFFERS) ", "
+		 "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_YUV_BUFFERS) " "
+		 "Max: " EXPAND_TO_STRING(IVTV_MAX_DEC_YUV_BUFFERS) ", "
+		 "0 to disable");
+
+MODULE_PARM(dec_mpg_qlen, "i");
+MODULE_PARM_DESC(dec_mpg_qlen,
+		 "\nNumber of 32K buffers to queue before dispatching to decoder\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_MPG_QLEN) ", "
+		 "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_MPG_QLEN) " "
+		 "Max: <dec_mpg_buffers>");
+
+MODULE_PARM(dec_yuv_qlen, "i");
+MODULE_PARM_DESC(dec_yuv_qlen,
+		 "\nNumber of 32K buffers to queue before dispatching to decoder\n"
+		 "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_YUV_QLEN) ", "
+		 "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_YUV_QLEN) " "
+		 "Max: <dec_yuv_buffers>");
+
+MODULE_PARM(ivtv_debug, "i");
+MODULE_PARM_DESC(ivtv_debug, "\nDebug level (bitmask), default, errors only\n"
+		 "(debug=127 gives full debuging)");
+
+MODULE_PARM(ivtv_pal, "i");
+MODULE_PARM_DESC(ivtv_pal, "\nUse PAL as default video mode instead of NTSC");
+
+MODULE_AUTHOR("Kevin Thayer");
+MODULE_DESCRIPTION("Alpha iTVC15 driver");
+MODULE_SUPPORTED_DEVICE("iTVC15/16 mpg2 encoder (aka WinTV PVR 250/350)");
+MODULE_LICENSE("GPL");
+
+static int SGarray_size;
+static int DSGarray_size;
+
+void ivtv_sleep_timeout(int timeout)
+{
+	int sleep = timeout;
+
+	do {
+		set_current_state(TASK_INTERRUPTIBLE);
+		sleep = schedule_timeout(sleep);
+
+	} while (sleep && !signal_pending(current));
+}
+
+/* ceiling function for ints.. */
+int ivtv_ceil(int x, int y)
+{
+	int floor = (int)(x / y);
+
+	if ((floor * y) < x)
+		return floor + 1;
+	return floor;
+}
+
+/* Release ioremapped memory */
+static void ivtv_iounmap(struct ivtv *itv)
+{
+	if (itv == NULL)
+		return;
+
+	/* Release io memory */
+	if (itv->io_mem != NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing iomem\n");
+		iounmap(itv->io_mem);
+		itv->io_mem = NULL;
+	}
+
+	/* Release registers memory */
+	if (itv->reg_mem != NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing regmem\n");
+		iounmap(itv->reg_mem);
+		itv->reg_mem = NULL;
+	}
+
+	/* Release encoder mailboxes */
+	if (itv->enc_mbox != NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing encmbox\n");
+		iounmap(itv->enc_mbox);
+		itv->enc_mbox = NULL;
+	}
+
+	/* Release decoder mailboxes */
+	if (itv->dec_mbox != NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing decmbox\n");
+		iounmap(itv->dec_mbox);
+		itv->dec_mbox = NULL;
+	}
+}
+
+/* must only be used as hints, not as a definitive answer. the answer could
+ * be wrong as soon as we return */
+int ivtv_get_free_elements(struct ivtv *itv, struct ivtv_buffer_list *queue)
+{
+	unsigned long flags;
+	int elements;
+
+	spin_lock_irqsave(&itv->lock, flags);
+	elements = queue->elements;
+	spin_unlock_irqrestore(&itv->lock, flags);
+
+	return elements;
+}
+
+inline void __ivtv_enq_buf(struct ivtv_buffer_list *queue,
+			   struct ivtv_buffer *buf)
+{
+	WARN_ON(!list_empty(&buf->list));
+	list_add_tail(&buf->list, &queue->list);
+	queue->elements++;
+}
+
+/* Adds buffers to the tail, effectively making a queue */
+int ivtv_enq_buf(struct ivtv *itv, struct ivtv_buffer_list *queue,
+		 struct ivtv_buffer *buf)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&itv->lock, flags);
+	__ivtv_enq_buf(queue, buf);
+	spin_unlock_irqrestore(&itv->lock, flags);
+
+	return 0;
+}
+
+inline void __ivtv_del_buf(struct ivtv_buffer_list *queue,
+			   struct ivtv_buffer *buffer)
+{
+	WARN_ON(list_empty(&buffer->list));
+	list_del_init(&buffer->list);
+	queue->elements--;
+}
+
+/* called to remove the buffer returned by _peek_ functions */
+void ivtv_del_buf(struct ivtv *itv, struct ivtv_buffer_list *queue,
+		  struct ivtv_buffer *buffer)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&itv->lock, flags);
+	__ivtv_del_buf(queue, buffer);
+	spin_unlock_irqrestore(&itv->lock, flags);
+}
+
+void ivtv_move_buf(struct ivtv *itv, struct ivtv_buffer_list *from,
+		   struct ivtv_buffer_list *to, struct ivtv_buffer *buffer)
+{
+	unsigned long flags;
+
+	WARN_ON(list_empty(&buffer->list));
+
+	spin_lock_irqsave(&itv->lock, flags);
+	list_move_tail(&buffer->list, &to->list);
+	from->elements--;
+	to->elements++;
+	spin_unlock_irqrestore(&itv->lock, flags);
+}
+
+/* returns first item in queue, doesn't dequeue */
+struct ivtv_buffer *__ivtv_deq_peek_head(struct ivtv_buffer_list *queue)
+{
+
+	/* make sure list has something to DeQ */
+	if (!list_empty(&queue->list))
+		return list_entry(queue->list.next, struct ivtv_buffer, list);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DeQ from empty list\n");
+	queue->elements = 0;
+	return NULL;
+}
+
+struct ivtv_buffer *ivtv_deq_peek_head(struct ivtv *itv,
+				       struct ivtv_buffer_list *queue)
+{
+	unsigned long flags;
+	struct ivtv_buffer *buffer;
+
+	spin_lock_irqsave(&itv->lock, flags);
+	buffer = __ivtv_deq_peek_head(queue);
+	spin_unlock_irqrestore(&itv->lock, flags);
+
+	return buffer;
+}
+
+/* removes buffer from the head */
+struct ivtv_buffer *__ivtv_deq_buf(struct ivtv_buffer_list *queue)
+{
+	struct ivtv_buffer *buf;
+
+	/* make sure list has something to DeQ */
+	if (!list_empty(&queue->list)) {
+		buf = list_entry(queue->list.next, struct ivtv_buffer, list);
+		list_del_init(queue->list.next);
+		queue->elements--;
+		return buf;
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DeQ from empty list!\n");
+	queue->elements = 0;
+	return NULL;
+}
+
+struct ivtv_buffer *ivtv_deq_buf(struct ivtv *itv,
+				 struct ivtv_buffer_list *queue)
+{
+	struct ivtv_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&itv->lock, flags);
+	buf = __ivtv_deq_buf(queue);
+	spin_unlock_irqrestore(&itv->lock, flags);
+
+	return buf;
+}
+
+struct ivtv_buffer *ivtv_init_buffer(int gfp_mask)
+{
+	struct ivtv_buffer *ibuf;
+
+	ibuf = kmalloc(sizeof(struct ivtv_buffer), gfp_mask);
+	if (ibuf == NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "No mem on ibuf alloc!\n");
+		return NULL;
+	}
+
+	(void *)ibuf->buffer.m.userptr = kmalloc(IVTV_DMA_BUF_SIZE, gfp_mask);
+	if ((void *)ibuf->buffer.m.userptr == NULL) {
+		kfree(ibuf);
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "No mem on buf alloc!\n");
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&ibuf->list);
+	ibuf->buffer.length = IVTV_DMA_BUF_SIZE;
+	ibuf->buffer.bytesused = 0;
+	ibuf->readpos = 0;
+
+	return ibuf;
+}
+
+#define IVTV_DMA_UNMAPPED	((u32) -1)
+
+void ivtv_free_buffer(struct ivtv *itv, struct ivtv_buffer *item)
+{
+	if (item->dma_handle != IVTV_DMA_UNMAPPED)
+		pci_unmap_single(itv->dev, item->dma_handle, IVTV_DMA_BUF_SIZE,
+				 PCI_DMA_TODEVICE);
+	if (item->buffer.m.userptr) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing buf %d!\n",
+			   item->buffer.index);
+		kfree((void *)item->buffer.m.userptr);
+	}
+	kfree(item);
+}
+
+int ivtv_free_queue(struct ivtv_buffer_list *queue)
+{
+	struct ivtv_buffer *item;
+	unsigned long flags;
+	struct ivtv *itv;
+	int x;
+
+	if (queue == NULL) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Free on NULL list!\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ivtv_lock, flags);
+
+	/* FIXME ugly */
+	/* verify ivtv before continuing */
+	itv = NULL;
+	for (x = 0; x < ivtv_cards_active; x++) {
+		if (queue->vdev->priv == &ivtv_cards[x]) {
+			itv = queue->vdev->priv;
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&ivtv_lock, flags);
+
+	if (itv == NULL)
+		return -ENODEV;
+
+	while ((item = ivtv_deq_buf(itv, queue)))
+		ivtv_free_buffer(itv, item);
+
+	return 0;
+}
+
+/* NOTE: This returns the # of buffers allocated */
+int ivtv_init_queue(struct ivtv *itv, struct ivtv_buffer_list *queue,
+		    int length, enum v4l2_buf_type type)
+{
+	int x;
+	struct ivtv_buffer *item;
+
+	/* Just in case */
+	INIT_LIST_HEAD(&queue->list);
+
+	for (x = 0; x < length; x++) {
+		/* allocate buffer */
+		item = ivtv_init_buffer(GFP_KERNEL);
+		if (item == NULL) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "Buffer alloc failed!\n");
+			return x;
+		}
+
+		/* setup buffer */
+		item->buffer.index = x;
+		item->buffer.type = type;
+		item->buffer.field = V4L2_FIELD_INTERLACED;
+		item->buffer.memory = V4L2_MEMORY_MMAP;
+
+		/* enqueue buffer */
+		ivtv_enq_buf(itv, queue, item);
+	}
+
+	return x;
+}
+
+int ivtv_move_queue(struct ivtv *itv, struct ivtv_buffer_list *src,
+		    struct ivtv_buffer_list *dst)
+{
+	struct ivtv_buffer *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&itv->lock, flags);
+
+	while ((buf = __ivtv_deq_buf(src)))
+		__ivtv_enq_buf(dst, buf);
+
+	spin_unlock_irqrestore(&itv->lock, flags);
+	return 0;
+}
+
+static int load_fw_direct(const char *fn, char *mem)
+{
+	int fd;
+	long l;
+	mm_segment_t fs = get_fs();
+
+	set_fs(get_ds());
+
+	if ((fd = open(fn, 0, 0)) == -1) {
+		printk(KERN_INFO "Unable to open '%s'.\n", fn);
+		l = -EINVAL;
+		goto out;
+	}
+	/* the 2 means SEEK_END */
+	l = lseek(fd, 0L, 2);
+
+	if (l <= 0 || l > IVTV_FIRM_IMAGE_SIZE) {
+		printk(KERN_INFO "Firmware image too large '%s'\n", fn);
+		l = -ENOMEM;
+		goto out;
+	}
+
+	/* the 2 means SEEK_SET */
+	lseek(fd, 0L, 0);
+
+	if (read(fd, mem, l) != l) {
+		printk(KERN_INFO "Failed to read '%s'.\n", fn);
+		l = -ENOMEM;
+	}
+
+      out:
+	close(fd);
+	set_fs(fs);
+
+	return (int)l;
+}
+
+int ivtv_firmware_copy(struct ivtv *itv)
+{
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Loading encoder image\n");
+
+	if (load_fw_direct(IVTV_FIRM_ENC_FILENAME,
+			   (char *)(itv->io_mem + IVTV_ENC_MEM_START)) !=
+	    IVTV_FIRM_IMAGE_SIZE) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "failed loading encoder firmware\n");
+		return -3;
+	}
+
+	if (itv->card_type != IVTV_250_V2) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Loading decoder firmware\n");
+		if (load_fw_direct(IVTV_FIRM_DEC_FILENAME,
+				   (char *)(itv->io_mem +
+					    IVTV_DEC_MEM_START)) !=
+		    IVTV_FIRM_IMAGE_SIZE) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "failed loading decoder firmware\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int ivtv_stop_firmware(struct ivtv *itv)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int x = 0;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping firmware\n");
+
+	if (atomic_read(&itv->capturing)) {
+		x = ivtv_stop_all_captures(itv);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 1. Code %d\n",
+				   x);
+	}
+
+	/*Stop decoder_playback */
+	data[0] = 1;		/* 0: render last frame, 1: stop NOW! :) */
+	data[1] = 0;		/* "low 4 bytes of stop index" */
+	data[2] = 0;		/* 0: stop immedeately */
+	x = ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_STOP_PLAYBACK,
+		     &result, 3, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 2. Code %d\n", x);
+
+	/*halt enc firmware */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ENC_HALT_FW,
+		     &result, 0, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 3. Code %d\n", x);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n");
+	ivtv_sleep_timeout(HZ / 100);
+
+	/*halt dec firmware */
+	if (IVTV_250_V2 != itv->card_type) {
+		x = ivtv_api(itv->dec_mbox, &itv->dec_msem,
+			     IVTV_API_DEC_HALT_FW, &result, 0, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 4. Code %d\n",
+				   x);
+
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n");
+		ivtv_sleep_timeout(HZ / 100);
+	}
+
+	return 0;
+}
+
+int ivtv_firmware_init(struct ivtv *itv)
+{
+	int x;
+
+	/* check that we're not RE-loading firmware */
+	/*  a sucessful load will have detected HW  */
+	/*  mailboxes. */
+
+	/* FIXME i dont think this will ever get called */
+	if (NULL != itv->enc_mbox) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "readying card for firmware upload\n");
+		x = ivtv_stop_firmware(itv);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "Error %d, stopping firmware\n", x);
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping VDM\n");
+	writel(IVTV_CMD_VDM_STOP, (IVTV_REG_VDM + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping AO\n");
+	writel(IVTV_CMD_AO_STOP, (IVTV_REG_AO + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "pinging (?) APU\n");
+	writel(IVTV_CMD_APU_PING, (IVTV_REG_APU + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping VPU\n");
+	if (IVTV_250_V2 == itv->card_type) {
+		writel(IVTV_CMD_VPU_STOP16, (IVTV_REG_VPU + itv->reg_mem));
+	} else {
+		writel(IVTV_CMD_VPU_STOP15, (IVTV_REG_VPU + itv->reg_mem));
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Resetting Hw Blocks\n");
+	writel(IVTV_CMD_HW_BLOCKS_RST, (IVTV_REG_HW_BLOCKS + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping SPU\n");
+	writel(IVTV_CMD_SPU_STOP, (IVTV_REG_SPU + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n");
+	ivtv_sleep_timeout(HZ / 100);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "init Encoder SDRAM pre-charge\n");
+	writel(IVTV_CMD_SDRAM_PRECHARGE_INIT,
+	       (IVTV_REG_ENC_SDRAM_PRECHARGE + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "init Encoder SDRAM refresh to 1us\n");
+	writel(IVTV_CMD_SDRAM_REFRESH_INIT,
+	       (IVTV_REG_ENC_SDRAM_REFRESH + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "init Decoder SDRAM pre-charge\n");
+	writel(IVTV_CMD_SDRAM_PRECHARGE_INIT,
+	       (IVTV_REG_DEC_SDRAM_PRECHARGE + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "init Decoder SDRAM refresh to 1us\n");
+	writel(IVTV_CMD_SDRAM_REFRESH_INIT,
+	       (IVTV_REG_DEC_SDRAM_REFRESH + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for %dms (600 recommended)\n",
+		   (int)IVTV_SDRAM_SLEEPTIME);
+	ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Card ready for firmware!\n");
+	x = ivtv_firmware_copy(itv);
+	if (x) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error loading firmware!\n");
+		return x;
+	}
+
+	/*I guess this is read-modify-write :) */
+	writel((readl(itv->reg_mem + IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE),
+	       (IVTV_REG_SPU + itv->reg_mem));
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 1 sec\n");
+	ivtv_sleep_timeout(HZ);
+
+	/*I guess this is read-modify-write :) */
+	if (IVTV_250_V2 == itv->card_type) {
+		writel((readl(itv->reg_mem + IVTV_REG_VPU) &
+			IVTV_MASK_VPU_ENABLE16), (IVTV_REG_VPU + itv->reg_mem));
+	} else {
+		writel((readl(itv->reg_mem + IVTV_REG_VPU) &
+			IVTV_MASK_VPU_ENABLE15), (IVTV_REG_VPU + itv->reg_mem));
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 1 sec\n");
+	ivtv_sleep_timeout(HZ);
+
+	/* FIXME Send Status API commands to encoder and decoder to verify! */
+
+	return 0;
+}
+
+int ivtv_find_firmware_mailbox(struct ivtv *itv)
+{
+	u32 *searchptr, *result;
+	int match = 0;
+
+	searchptr = NULL;
+	result = NULL;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Searching for encoder mailbox\n");
+	searchptr = (u32 *) (IVTV_FIRM_SEARCH_ENCODER_START + itv->io_mem);
+
+	while (searchptr < (u32 *) (IVTV_FIRM_SEARCH_ENCODER_END + itv->io_mem)) {
+		if (ivtv_firm_search_id[match] == readl(searchptr)) {
+			(u32) result = (u32) searchptr + 4;	/* avoid pointer aritmetic */
+			match++;
+			while ((match > 0) && (match < 4)) {
+				IVTV_DEBUG(IVTV_DEBUG_INFO, "match: 0x%08x at "
+					   "0x%08x. match: %d\n", *result,
+					   (u32) result, match);
+				if (ivtv_firm_search_id[match] == readl(result)) {
+					match++;
+					/* FIXME change to just "result++;" ? */
+					(u32) result = (u32) result + 4;
+				} else
+					match = 0;
+			}
+		} else {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, ".");
+		}
+		if (4 == match) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "found encoder mailbox!\n");
+			itv->enc_mbox = (struct ivtv_mailbox *)result;
+			break;
+		}
+		(u32) searchptr += IVTV_FIRM_SEARCH_STEP;
+	}
+	if (itv->enc_mbox == NULL)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Encoder mailbox not found\n");
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Searching for decoder mailbox\n");
+	match = 0;
+	searchptr = (u32 *) (IVTV_FIRM_SEARCH_DECODER_START + itv->io_mem);
+
+	while (searchptr < (u32 *) (IVTV_FIRM_SEARCH_DECODER_END + itv->io_mem)) {
+		if (ivtv_firm_search_id[match] == readl(searchptr)) {
+			(u32) result = (u32) searchptr + 4;	/* avoid pointer aritmetic */
+			match++;
+			while ((match > 0) && (match < 4)) {
+				IVTV_DEBUG(IVTV_DEBUG_INFO,
+					   "match: 0x%08x at 0x%08x. match: %d\n",
+					   *result, (u32) result, match);
+				if (ivtv_firm_search_id[match] == readl(result)) {
+					match++;
+					/* FIXME change to just "result++;" ? */
+					(u32) result = (u32) result + 4;
+				} else
+					match = 0;
+			}
+		} else {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, ".");
+		}
+		if (4 == match) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "found decoder mailbox!\n");
+			itv->dec_mbox = (struct ivtv_mailbox *)result;
+			break;
+		}
+		(u32) searchptr += IVTV_FIRM_SEARCH_STEP;
+	}
+	if (itv->dec_mbox == 0)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Decoder mailbox not found\n");
+
+	return 0;
+}
+
+int ivtv_get_free_mailbox(struct ivtv_mailbox *mbox)
+{
+	int i = 0;
+	if (NULL == mbox) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Can't get mailbox from NULL\n");
+		return -ENODEV;
+	}
+
+	/* FIXME hardcoded cause i'm not sure what changing API_BOXES will do */
+	//for (i = 0; i < IVTV_MBOX_API_BOXES; i++) {
+	for (i = 0; i < 2; i++) {
+		if (mbox[i].flags & IVTV_MBOX_FIRMWARE_DONE) {
+			switch (mbox[i].cmd) {
+			case IVTV_API_SCHED_DMA_TO_HOST:
+			case IVTV_API_DEC_DMA_FROM_HOST:
+				IVTV_DEBUG(IVTV_DEBUG_API,
+					   "recycled mailbox: %d\n", i);
+				writel(IVTV_MBOX_IN_USE, &mbox[i].flags);
+				return i;
+				break;
+			default:
+				IVTV_DEBUG(IVTV_DEBUG_API,
+					   "Mailbox %d in use, skipping\n", i);
+				break;
+			}
+			/* FIXME using 'else' may leak mailboxes in some situations */
+		} else if (!test_and_set_bit(0, &mbox[i].flags)) {
+			IVTV_DEBUG(IVTV_DEBUG_API, "got free mailbox: %d\n", i);
+			return i;
+		}
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "no free mailboxes!\n");
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "mbox 0: 0x%08x, mbox 1 0x%08x!\n",
+		   mbox[0].cmd, mbox[1].cmd);
+	return -ENODEV;
+}
+
+void ivtv_clear_irq_mask(struct ivtv *itv, unsigned long mask)
+{
+	itv->irqmask &= ~mask;
+	writel(itv->irqmask, (itv->reg_mem + IVTV_REG_IRQMASK));
+	/* pci posting */
+	readl(itv->reg_mem + IVTV_REG_IRQMASK);
+}
+
+void ivtv_set_irq_mask(struct ivtv *itv, unsigned long mask)
+{
+	itv->irqmask |= mask;
+	writel(itv->irqmask, (itv->reg_mem + IVTV_REG_IRQMASK));
+	/* pci posting */
+	readl(itv->reg_mem + IVTV_REG_IRQMASK);
+}
+
+/** 
+ * Call ivtv api function using given mailbox, without locking sem.
+ */
+int __ivtv_api_call(struct ivtv_mailbox *mbox, u32 cmd, int elements,
+		    const u32 * data)
+{
+	int x;
+	if (NULL == mbox) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "invalid api mailbox\n");
+		return -ENODEV;
+	}
+
+	/* "if mailbox is available" */
+	if ((mbox->flags & IVTV_MBOX_FIRMWARE_DONE) ||
+	    (!test_and_set_bit(0, &mbox->flags))) {
+		/* I'm too lazy to invert the condition ;) */
+	} else {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Mailbox busy (unexpected)\n");
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "cmd 0x%08x, m.cmd 0x%08x\n", cmd, mbox->cmd);
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "d0 0x%08x, d1 0x%08x, d2 0x%08x\n",
+			   mbox->data[0], mbox->data[1], mbox->data[2]);
+		return -EBUSY;
+	}
+
+	readl(&mbox->flags);
+	writel(cmd, &mbox->cmd);
+	writel(IVTV_API_STD_TIMEOUT, &mbox->timeout);
+
+	for (x = 0; x < IVTV_MBOX_MAX_DATA; x++) {
+		if (x < elements) {
+			writel(data[x], &mbox->data[x]);
+		} else {
+			writel(0, &mbox->data[x]);
+		}
+	}
+
+	writel((IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_IN_USE), &mbox->flags);
+	readl(&mbox->flags);
+
+	return 0;
+}
+
+/* This one is for stuff that can't sleep.. irq handlers, etc.. */
+int ivtv_api_getresult_nosleep(struct ivtv_mailbox *mbox, u32 * result,
+			       u32 data[])
+{
+	u32 readdata;
+	int count = 0;
+
+	if (NULL == mbox) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "invalid api mailbox\n");
+		return -ENODEV;
+	}
+
+	readdata = readl(&mbox->flags);
+
+	*result = readl(&mbox->retval);
+	for (count = 0; count < IVTV_MBOX_MAX_DATA; count++)
+		data[count] = readl(&mbox->data[count]);
+
+	return 0;
+}
+
+int __ivtv_api_getresult(struct ivtv_mailbox *mbox, u32 * result, u32 data[],
+			 int api_timeout)
+{
+	u32 readdata;
+	int count = 0;
+
+	readdata = readl(&mbox->flags);
+
+	while (!(readdata & IVTV_MBOX_FIRMWARE_DONE)) {
+		IVTV_DEBUG(IVTV_DEBUG_API,
+			   "[%d]result not ready, waiting 10 ms\n", count);
+		ivtv_sleep_timeout(HZ / 100);
+		readdata = readl(&mbox->flags);
+
+		if (count++ > api_timeout) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "%d ms time out waiting for firmware\n",
+				   api_timeout);
+			return -EBUSY;
+		}
+	}
+
+	*result = readl(&mbox->retval);
+	for (count = 0; count < IVTV_MBOX_MAX_DATA; count++)
+		data[count] = readl(&mbox->data[count]);
+
+	return 0;
+}
+
+int ivtv_api(struct ivtv_mailbox *mbox, struct semaphore *sem, int cmd,
+	     u32 * result, int args, u32 data[])
+{
+	int x = 0, gotsem = 0, needsresult = 1;
+	int die = 0, api_timeout = 100;
+	struct ivtv_mailbox *local_box;
+
+	IVTV_DEBUG(IVTV_DEBUG_API, "API Call: 0x%08x\n", cmd);
+
+	local_box = mbox;
+
+	/* check args */
+	if (args > IVTV_MBOX_MAX_DATA)
+		return -EINVAL;
+
+	switch (cmd) {
+	case IVTV_API_SCHED_DMA_TO_HOST:
+	case IVTV_API_DEC_DMA_FROM_HOST:
+		needsresult = 0;
+		if (down_trylock(sem)) {	/* box 0 was busy */
+			gotsem = 0;
+			local_box = &mbox[1];
+		} else {
+			gotsem = 1;
+		}
+		if ((x = __ivtv_api_call(local_box, cmd, args, data))) {
+			if (local_box == mbox) {
+				IVTV_DEBUG(IVTV_DEBUG_API,
+					   "Trying alternate mailbox\n");
+				x = __ivtv_api_call(&mbox[1], cmd, args, data);
+			}
+		}
+		goto ivtv_api_done;
+		break;
+		/* adjust api timeout for these 2 calls */
+	case IVTV_API_END_CAPTURE:
+	case IVTV_API_EVENT_NOTIFICATION:
+		api_timeout = 1000;
+	default:
+		if (down_interruptible(sem))
+			return -ERESTARTSYS;
+		gotsem = 1;
+		break;
+	}
+
+	/* wait 200ms for mailbox to become free */
+	x = __ivtv_api_call(local_box, cmd, args, data);
+	while ((x == -EBUSY) && (die < 20)) {
+		die++;
+		ivtv_sleep_timeout(HZ / 100);
+		x = __ivtv_api_call(local_box, cmd, args, data);
+		IVTV_DEBUG(IVTV_DEBUG_API, "die: %d\n", die);
+	}
+
+	if (x == -EBUSY) {
+		/* dilemma here:
+		   if the command that currently has the mailbox
+		   is lost, then it'll never free up the box, and 
+		   we'll lose our only 'general purpose' box forever.
+		   But if it comes back, we run the risk of squishing
+		   something important!. */
+		switch (local_box->cmd) {
+		case IVTV_API_DEC_DMA_FROM_HOST:
+			/* if we got here, it's because the call to xfer the dma 
+			   was finished, and the command that wants to run has 
+			   already slept for 20ms. It's probably safe to take over
+			 */
+			IVTV_DEBUG(IVTV_DEBUG_API,
+				   "Forcibly freeing mailbox\n");
+			writel(0x00000000, &mbox->flags);
+			x = __ivtv_api_call(local_box, cmd, args, data);
+			break;
+		default:
+			/* do nothing */
+			break;
+		}
+	}
+
+	if (x) {
+		IVTV_DEBUG(IVTV_DEBUG_API, "Error running command 0x%08x\n",
+			   cmd);
+		goto ivtv_api_done;
+	}
+
+	if (needsresult) {
+		x = __ivtv_api_getresult(local_box, result, &data[0],
+					 api_timeout);
+		IVTV_DEBUG(IVTV_DEBUG_API, "retval: 0x%08x\n", *result);
+		if (x == -EBUSY)
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "api call 0x%08x\n", cmd);
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_API, "Releasing mailbox (before 0x%08x, ",
+		   readl(&mbox->flags));
+	writel(0x00000000, &mbox->flags);
+	IVTV_DEBUG(IVTV_DEBUG_API, "after 0x%08x )\n", readl(&mbox->flags));
+
+      ivtv_api_done:
+	if (gotsem) {
+		up(sem);
+	}
+
+	return x;
+}
+
+int ivtv_firmware_versions(struct ivtv *itv)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int x;
+
+	/* Encoder */
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Getting encoder firmware rev.\n");
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ENC_GETVER,
+		     &result, 0, &data[0]);
+	if (x) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "error getting Encoder firmware version\n");
+	} else {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Encoder revision: 0x%08x\n", data[0]);
+	}
+
+	if (itv->card_type != IVTV_250_V2) {
+		/* Decoder */
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Getting decoder firmware rev.\n");
+		x = ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_GETVER,
+			     &result, 0, &data[0]);
+		if (x) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "error getting Decoder firmware version\n");
+		} else {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "Decoder revision: 0x%08x\n", data[0]);
+		}
+	}
+
+	return 0;
+}
+
+void ivtv_flush_queues(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_v4l2_stream *st = &itv->v4l2.streams[id->type];
+	struct ivtv_buffer *buf;
+
+	/* move free_q to full_q to clean up partially-filled buffers */
+	while ((buf = ivtv_deq_buf(itv, &st->free_q)))
+		ivtv_enq_buf(itv, &st->full_q, buf);
+
+	while ((buf = ivtv_deq_buf(itv, &itv->v4l2.streams[id->type].full_q))) {
+		buf->buffer.bytesused = 0;
+		buf->readpos = 0;
+		ivtv_enq_buf(itv, &st->free_q, buf);
+	}
+
+	while ((buf = ivtv_deq_buf(itv, &itv->v4l2.streams[id->type].dma_q))) {
+		buf->buffer.bytesused = 0;
+		buf->readpos = 0;
+		ivtv_enq_buf(itv, &st->free_q, buf);
+	}
+
+	return;
+}
+
+int ivtv_stop_all_captures(struct ivtv *itv)
+{
+	struct ivtv_open_id id;
+	int x;
+	id.itv = itv;
+
+	down(&itv->sem_lock);
+
+	for (x = 0; x < itv->v4l2.streamcount; x++) {
+		if (test_bit(IVTV_F_S_CAP, &itv->v4l2.streams[x].s_flags)) {
+			id.type = x;
+			ivtv_stop_capture(&id);
+		}
+	}
+
+	up(&itv->sem_lock);
+	return 0;
+}
+
+int ivtv_stop_capture(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	DECLARE_WAITQUEUE(wait, current);
+	int type, subtype, then;
+	int x;
+
+	/* This function assumes that you are allowed to stop the capture
+	   and that we are actually capturing */
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stop Capture\n");
+
+	/* sem_lock must be held */
+	IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0);
+
+	type = id->type;
+	if (type == 1) {
+		subtype = 3;	//FIXME temp
+	} else {
+		subtype = 3;
+	}
+
+#if 0
+	/* only run these if we're shutting down the last cap */
+	if (atomic_read(&itv->capturing) - 1 == 0) {
+		/* event notification (off) */
+		data[0] = 0;	/*type: 0 = refresh */
+		data[1] = 0;	/*on/off: 0 = off */
+		data[2] = 0x10000000;	/*intr_bit: 0x10000000 = digitizer */
+		data[3] = -1;	/*mbox_id: -1: none */
+		x = ivtv_api(itv->enc_mbox, &itv->enc_msem,
+			     IVTV_API_EVENT_NOTIFICATION, &result, 4, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "stopcap error 1. Code %d\n",
+				   x);
+
+	}
+#endif
+
+	/* end_capture */
+	data[0] = 1;		/*when: 0 =  end of GOP    1 = NOW! */
+	data[1] = type;		/*type: 0 = mpeg */
+	data[2] = subtype;	/*subtype: 3 = video+audio */
+	x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_END_CAPTURE,
+		     &result, 3, &data[0]);
+	if (x)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "stopcap error 2. Code %d\n", x);
+
+	then = jiffies;
+
+	add_wait_queue(&itv->v4l2.streams[type].waitq, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	do {
+		/* check if DMA is pending */
+		if (!test_bit(IVTV_F_S_DMAP, &itv->v4l2.streams[type].s_flags)) {
+			break;
+		}
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "dma still pending!\n");
+		schedule_timeout(HZ / 100);
+	} while (((then + HZ) < jiffies) && !signal_pending(current));
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&itv->v4l2.streams[type].waitq, &wait);
+
+	if (test_bit(IVTV_F_S_DMAP, &itv->v4l2.streams[type].s_flags)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "giving up waiting for DMA pending clear\n");
+	}
+/* only needed if we're searching for an EOS.. currently disabled */
+#if 0
+	/* only run these if we're shutting down the last cap */
+	if (atomic_read(&itv->capturing) - 1 == 0) {
+		add_wait_queue(&itv->cap_w, &wait);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		/* wait 2s for EOS interrupt */
+		while ((!test_bit(IVTV_F_I_EOS, &itv->i_flags)) &&
+		       (jiffies < then + 2 * HZ)) {
+			schedule_timeout(HZ);
+		}
+		then = jiffies - then;
+
+		if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "EOS interrupt not received! stopping anyway.\n");
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "waitied %d ms: %d\n", (1000 / HZ) * then);
+		} else {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "EOS took %d ms to occur.\n",
+				   (1000 / HZ) * then);
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&itv->cap_w, &wait);
+	}
+#endif
+	clear_bit(IVTV_F_S_OVERFLOW, &itv->v4l2.streams[type].s_flags);
+	clear_bit(IVTV_F_S_CAP, &itv->v4l2.streams[type].s_flags);
+
+	atomic_dec(&itv->capturing);
+	if (atomic_read(&itv->capturing))
+		return 0;
+
+	/*Set the following Interrupt mask bits: 0xd8000000 */
+	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+	IVTV_DEBUG(IVTV_DEBUG_IRQ, "IRQ Mask is now: 0x%08x\n", itv->irqmask);
+
+	return 0;
+}
+
+int ivtv_stop_decode(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int x;
+
+	/* sem_lock must be held */
+	IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0);
+
+	/* FIXME set 'die' ?? */
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder stop.\n");
+
+	/* only run these if we're shutting down the last cap */
+	if (atomic_read(&itv->decoding) - 1 == 0) {
+#if 0
+		/* event notification (off) */
+		data[0] = 0;	/* Event: 0 = audio change between stereo and mono */
+		data[1] = 0;	/* Enable/Disable: 0 = disabled, 1 = enabled */
+		data[2] = 0x00010000;	/* Bit: interrupt bit to fire */
+		data[3] = -1;	/* Mailbox to use: -1 = no mailbox needed */
+		x = ivtv_api(itv->dec_mbox, &itv->dec_msem,
+			     IVTV_API_DEC_EVENT_NOTIFICATION, &result, 4,
+			     &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "stopDEC error 1. Code %d\n",
+				   x);
+#endif
+
+		/* end_capture */
+		data[0] = itv->dec_options.hide_last_frame;	/*  0 = last frame,
+								   1 = black */
+		data[1] = itv->dec_options.pts_low;	/* when: pts low */
+		data[2] = itv->dec_options.pts_hi;	/* when: pts hi */
+		x = ivtv_api(itv->dec_mbox, &itv->dec_msem,
+			     IVTV_API_DEC_STOP_PLAYBACK, &result, 3, &data[0]);
+		if (x)
+			IVTV_DEBUG(IVTV_DEBUG_ERR, "stopDEC error 2. Code %d\n",
+				   x);
+	}
+
+	/* FIXME turn off relevant irqmask here */
+	ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+
+	/* stop decoder interrupt timeout */
+	del_timer_sync(&itv->dec_timeout);
+
+	if (test_and_clear_bit
+	    (IVTV_F_S_DMAP, &itv->v4l2.streams[id->type].s_flags))
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: clearing dma_pending\n");
+
+	/* Clean up some possibly loose-ends */
+	clear_bit(IVTV_F_I_BUSY, &itv->i_flags);
+	atomic_dec(&itv->decoding);
+	wake_up(&itv->dec_master_w);
+
+	return 0;
+}
+
+void ivtv_dec_timeout(unsigned long arg)
+{
+	struct ivtv *itv = (struct ivtv *)arg;
+
+	/* FIXME mpg only :/ */
+	struct ivtv_v4l2_stream *stream =
+	    &itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG];
+	unsigned long flags;
+
+	if (!test_bit(IVTV_F_S_DMAP, &stream->s_flags))
+		return;
+
+	IVTV_DEBUG(IVTV_DEBUG_ERR,
+		   "ivtv_dec_timeout: lost IRQ; resetting...\n");
+	spin_lock_irqsave(&itv->lock, flags);
+	ivtv_dec_DMA_done(itv);
+	/* kick it off again! */
+	set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags);
+	ivtv_dec_sched_DMA(itv);
+	spin_unlock_irqrestore(&itv->lock, flags);
+}
+
+#if 0
+static void ivtv_show_irq_status(struct ivtv *itv, u32 irqstat, u32 irqmask,
+				 u32 dmastat)
+{
+	struct ivtv_mailbox *mbox8 = &itv->dec_mbox[8];
+	struct ivtv_mailbox *mbox9 = &itv->dec_mbox[9];
+
+#if 0
+	// Make it less verbose...
+	if ((irqstat & ~4) == IVTV_IRQ_DEC_VSYNC)
+		return;
+#endif
+
+	printk("ivtv: irqstat [ "
+	       "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s], "
+	       "frame %d, pts %d, scr %d, type %d, offset %08x, max %d, full %d\n",
+	       (irqstat & IVTV_IRQ_ENC_START_CAP) ? "StartCap " : "",
+	       (irqstat & IVTV_IRQ_ENC_EOS) ? "EndOfStream " : "",
+	       (irqstat & IVTV_IRQ_ENC_VBI_CAP) ? "VBICap " : "",
+	       (irqstat & IVTV_IRQ_ENC_VIM_RST) ? "VIMReset " : "",
+	       (irqstat & IVTV_IRQ_ENC_DMA_COMPLETE) ? "EncDMAComplete " : "",
+	       (irqstat & (1 << 26)) ? "26 " : "",
+	       (irqstat & IVTV_IRQ_DEC_COPY_PROTECT) ? "CopyProt " : "",
+	       (irqstat & IVTV_IRQ_DEC_AUD_MODE_CHG) ? "AudioMode " : "",
+	       (irqstat & (1 << 23)) ? "23 " : "",
+	       (irqstat & IVTV_IRQ_DEC_DATA_REQ) ? "DecDataReq " : "",
+	       (irqstat & IVTV_IRQ_DEC_IFRAME_DONE) ? "IFrameDone " : "",
+	       (irqstat & IVTV_IRQ_DEC_DMA_COMPLETE) ? "DecDMAComplete " : "",
+	       (irqstat & IVTV_IRQ_DEC_VBI_RE_INSERT) ? "VBIReInsert " : "",
+	       (irqstat & IVTV_IRQ_DEC_DMA_ERR) ? "DecDMAError " : "",
+	       (irqstat & (1 << 17)) ? "17 " : "",
+	       (irqstat & (1 << 16)) ? "16 " : "",
+	       (irqstat & (1 << 15)) ? "15 " : "",
+	       (irqstat & (1 << 14)) ? "14 " : "",
+	       (irqstat & (1 << 13)) ? "13 " : "",
+	       (irqstat & (1 << 12)) ? "12 " : "",
+	       (irqstat & (1 << 11)) ? "11 " : "",
+	       (irqstat & IVTV_IRQ_DEC_VSYNC) ? "DecVSync " : "",
+	       (irqstat & (1 << 9)) ? "9 " : "",
+	       (irqstat & (1 << 8)) ? "8 " : "",
+	       (irqstat & (1 << 7)) ? "7 " : "",
+	       (irqstat & (1 << 6)) ? "6 " : "",
+	       (irqstat & (1 << 5)) ? "5 " : "",
+	       (irqstat & (1 << 4)) ? "4 " : "",
+	       (irqstat & (1 << 3)) ? "3 " : "",
+	       (irqstat & (1 << 2)) ? "2 " : "",
+	       (irqstat & (1 << 1)) ? "1 " : "",
+	       (irqstat & (1 << 0)) ? "0 " : "",
+	       readl(&mbox8->data[0]),
+	       readl(&mbox8->data[1]),
+	       readl(&mbox8->data[3]),
+	       readl(&mbox9->data[0]),
+	       readl(&mbox9->data[1]),
+	       readl(&mbox9->data[2]), readl(&mbox9->data[3]));
+}
+#endif
+
+static irqreturn_t ivtv_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+
+	u32 stat = 0;
+	u32 combo = 0;
+	struct ivtv *itv = (struct ivtv *)dev_id;
+
+	spin_lock(&itv->lock);
+
+	/* get contents of irq status register */
+	stat = readl(itv->reg_mem + IVTV_REG_IRQSTATUS);
+
+	combo = ~itv->irqmask & stat;
+
+	if (0 == combo) {
+		/* wasn't for us */
+		spin_unlock(&itv->lock);
+		return IRQ_NONE;
+	}
+
+/*
+	ivtv_show_irq_status(itv, stat, itv->irqmask,
+			     readl(itv->reg_mem + IVTV_REG_DMASTATUS));
+*/
+
+	IVTV_DEBUG(IVTV_DEBUG_IRQ, "======= valid IRQ bits: 0x%08x ======\n",
+		   combo);
+
+	writel(combo, (itv->reg_mem + IVTV_REG_IRQSTATUS));
+
+	if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
+		ivtv_DMA_done(itv);
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "Processed DMA-complete\n");
+	}
+	if (combo & IVTV_IRQ_ENC_START_CAP) {
+		ivtv_sched_DMA(itv);
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "Processed enc-startcap\n");
+	}
+	if (combo & IVTV_IRQ_ENC_EOS) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "Encoder End Of Stream\n");
+		set_bit(IVTV_F_I_EOS, &itv->i_flags);
+		wake_up(&itv->cap_w);
+	}
+	if (combo & IVTV_IRQ_ENC_VBI_CAP) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb3\n");
+	}
+	if (combo & IVTV_IRQ_ENC_VIM_RST) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "VIM Restart\n");
+	}
+	if (combo & IVTV_IRQ_DEC_COPY_PROTECT) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb6\n");
+	}
+	if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb7\n");
+	}
+	if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "Decoder DMA Done\n");
+		ivtv_dec_DMA_done(itv);
+	}
+	if (combo & IVTV_IRQ_DEC_DATA_REQ) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "Decoder Data Request\n");
+		set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags);
+		ivtv_dec_sched_DMA(itv);
+	}
+	if (combo & IVTV_IRQ_DEC_IFRAME_DONE) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb9\n");
+	}
+	if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb11\n");
+	}
+	if (combo & IVTV_IRQ_DEC_DMA_ERR) {
+		IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb12\n");
+	}
+	if (combo & IVTV_IRQ_DEC_VSYNC) {
+		ivtv_irq_dec_vsync(itv);
+	}
+	/*
+	   stat = readl(itv->reg_mem + IVTV_REG_IRQSTATUS);
+	   IVTV_DEBUG(IVTV_DEBUG_IRQ, "IVTV IRQ STATUS REG AFTER INTERRUPT 0x%08x", stat);
+	   if (combo & ~IVTV_IRQ_DEBUG_KLUGE) {
+	   IVTV_DEBUG(IVTV_DEBUG_ERR,
+	   "unknown irq 0x%08x, mask:0x%08x, combo:0x%08x\n",
+	   stat, itv->irqmask, combo);
+	   }
+	 */
+	spin_unlock(&itv->lock);
+	return IRQ_HANDLED;
+}
+
+static void ivtv_irq_dec_vsync(struct ivtv *itv)
+{
+	u32 *data = itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data;
+
+	u32 newframe = readl(&data[0]);
+	u64 newpts = ((u64) readl(&data[2]) << 32) | (u64) (readl(&data[1]));
+	u64 newscr = ((u64) readl(&data[4]) << 32) | (u64) (readl(&data[3]));
+
+	itv->dec_timestamp.pts = newpts;
+	itv->dec_timestamp.scr = newscr;
+	if (newframe != itv->dec_timestamp.frame) {
+		itv->dec_timestamp.frame = newframe;
+		wake_up(&itv->vsync_w);
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_IRQ,
+		   "ivtv_irq_dec_vsync: frames %d, pts %ld, scr %ld\n",
+		   itv->dec_timestamp.frame, (long int)itv->dec_timestamp.pts,
+		   (long int)itv->dec_timestamp.scr);
+}
+
+static void ivtv_show_debug_flags(struct ivtv *itv)
+{
+	int y;
+
+	printk(KERN_DEBUG "ivtv: i_flags=%lx", itv->i_flags);
+	for (y = IVTV_DEC_STREAM_TYPE_MPG; y < itv->v4l2.streamcount; y++)
+		printk(", %d s_sflags=%lx", y, itv->v4l2.streams[y].s_flags);
+	printk("\n");
+}
+
+static void ivtv_DMA_done(struct ivtv *itv)
+{
+	u32 result;
+	int y, stmtype = -1;
+	struct ivtv_v4l2_stream *stream = NULL;
+	struct ivtv_buffer *buf;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DMA Done tasklet\n");
+
+	for (y = 0; y < itv->v4l2.streamcount; y++) {
+		if (test_and_clear_bit
+		    (IVTV_F_S_DMAP, &itv->v4l2.streams[y].s_flags)) {
+			stmtype = y;
+			break;
+		}
+	}
+
+	if (stmtype < 0) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Got DMA-done, but not expecting one\n");
+		ivtv_show_debug_flags(itv);
+		return;
+	}
+
+	stream = &itv->v4l2.streams[stmtype];
+
+	/* check DMA status register */
+	result = readl(itv->reg_mem + IVTV_REG_DMASTATUS);
+
+	if (!(result & IVTV_DMA_SUCCESS)) {
+		if (result & IVTV_DMA_WRITE_ERR)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "DMA write error. Result=0x%08x\n", result);
+		if (result & IVTV_DMA_READ_ERR)
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "DMA read error. Result=0x%08x\n", result);
+		return;
+	}
+
+	/* DMA was fine if we made it this far */
+
+	/* remove from dma_pending queue */
+	while ((buf = __ivtv_deq_buf(&stream->dma_q))) {
+		IVTV_ASSERT(buf->dma_handle != IVTV_DMA_UNMAPPED);
+		pci_unmap_single(itv->dev, buf->dma_handle,
+				 IVTV_DMA_BUF_SIZE, PCI_DMA_TODEVICE);
+		buf->dma_handle = IVTV_DMA_UNMAPPED;
+		/* byteswap ABCD -> DCBA for MPG data */
+		if (stmtype == 0) {
+			for (y = 0; y < buf->buffer.bytesused; y += 4) {
+				swab32s((u32 *) ((u32) buf->buffer.m.userptr +
+						 y));
+			}
+		}
+		/* put in the 'done' queue */
+		__ivtv_enq_buf(&stream->full_q, buf);
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DMA Done tasklet5\n");
+	/*wake up client */
+	wake_up(&stream->waitq);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DMA Done tasklet6\n");
+}
+
+/* must hold itv->lock */
+static int ivtv_ignore_DMA_req(struct ivtv *itv, u32 type)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int ret = 0;
+
+	data[0] = 0;
+	data[1] = 0;		/* ACK the DMA and continue */
+	data[2] = type;		// AEW - API docs say type goes here
+	if (ivtv_api(itv->enc_mbox, &itv->enc_msem,
+		     IVTV_API_SCHED_DMA_TO_HOST, &result, 3, &data[0])) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "error sending DMA info\n");
+		ret = -EIO;
+	}
+
+	if (!ret)
+		set_bit(IVTV_F_S_DMAP, &itv->v4l2.streams[type].s_flags);
+
+	return ret;
+}
+
+/* FIXME this function is getting too long. split it up? */
+static void ivtv_sched_DMA(struct ivtv *itv)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	u32 type, size, offset;
+	u32 UVsize = 0, UVoffset = 0, pts_stamp = 0;
+	struct ivtv_v4l2_stream *st;
+	int x, bufs_needed;
+	int uvflag = 0;
+	struct ivtv_buffer *buf;
+	LIST_HEAD(free_list);
+	long sequence;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Sched DMA tasklet\n");
+
+	/* Get DMA destination and size arguments from card */
+	x = ivtv_api_getresult_nosleep(&itv->enc_mbox[9], &result, &data[0]);
+	if (x) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "error:%d getting DMA info\n", x);
+		return;
+	}
+
+	type = data[0];
+
+	/* FIXME should check for null on the stream element */
+	if (itv->v4l2.streamcount <= type) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "No stream handler for type %d\n",
+			   type);
+		ivtv_ignore_DMA_req(itv, type);
+		return;
+	}
+
+	switch (type) {
+	case 0:		/* MPEG */
+		offset = data[1];
+		size = data[2];
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "DMA/MPG type 0x%08x,size 0x%08x,offset 0x%08x\n",
+			   type, size, offset);
+		bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE);
+		break;
+	case 1:		/* YUV */
+		offset = data[1];
+		size = data[2];
+		UVoffset = data[3];
+		UVsize = data[4];
+		pts_stamp = data[6];
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "DMA/YUV type 0x%08x,Ysize 0x%08x,Yoffset 0x%08x,"
+			   "UVsize 0x%08x,UVoffset 0x%08x,PTS 0x%08x\n",
+			   type, size, offset, UVsize, UVoffset, pts_stamp);
+		bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE);
+		bufs_needed += ivtv_ceil(UVsize, IVTV_DMA_BUF_SIZE);
+		break;
+
+	case 2:		/* PCM (audio) */
+		offset = data[1];
+		size = data[2];
+		pts_stamp = data[6];
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "DMA/PCM type 0x%08x,size 0x%08x,offset 0x%08x "
+			   "PTS 0x%08x\n", type, size, offset, pts_stamp);
+		bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE);
+		ivtv_ignore_DMA_req(itv, type);
+		return;
+	case 3:		/* VBI */
+		offset = data[1];
+		size = data[2];
+		bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE);
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "DMA/VBI type 0x%08x, size 0x%08x, offset 0x%08x"
+			   "EXPERIMENTAL\n", type, size, offset);
+		break;
+	default:
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "DMA/UNKNOWN type 0x%08x, NOT SUPPORTED\n", type);
+		ivtv_ignore_DMA_req(itv, type);
+		return;
+	}
+
+	if (bufs_needed > SGarray_size) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "INTERNAL ERROR: ivtv_sched_DMA_tasklet: "
+			   "bufs_needed = %d but SGarray_size = %d\n",
+			   bufs_needed, SGarray_size);
+		return;
+	}
+
+	st = &itv->v4l2.streams[type];
+
+	/* gather the needed buffers first, so we don't have to bail
+	 * in mid-air. put them on a list on the stack */
+	for (x = 0; x < bufs_needed; x++) {
+		buf = __ivtv_deq_buf(&st->free_q);
+		if (!buf)
+			break;
+
+		list_add_tail(&buf->list, &free_list);
+	}
+
+	/* damn, failed */
+	if (x < bufs_needed) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "DMA buffer DeQueue failed! got %d, want %d\n",
+			   x + 1, bufs_needed + 1);
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "SCHED: free_q: %d elements\n",
+			   st->free_q.elements);
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "SCHED: dma_q: %d elements\n",
+			   st->dma_q.elements);
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "SCHED: full_q: %d elements\n",
+			   st->full_q.elements);
+		while (!list_empty(&free_list)) {
+			buf =
+			    list_entry(free_list.next, struct ivtv_buffer,
+				       list);
+			list_del_init(&buf->list);
+			__ivtv_enq_buf(&st->free_q, buf);
+		}
+		/* mark overflow condition, next free will restart dma req */
+		set_bit(IVTV_F_S_OVERFLOW, &st->s_flags);
+		return;
+	}
+
+	/* increment the sequence # */
+	sequence = ++st->seq;
+
+	for (x = 0; x < bufs_needed; x++) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "size: %d 0x%08x\n", size, size);
+
+		if ((size == 0) && (type == 1) && (uvflag == 0)) {	/* YUV */
+			/* process the UV section */
+			offset = UVoffset;
+			size = UVsize;
+			uvflag = 1;
+		}
+
+		/* extract the buffers we procured earlier */
+		buf = list_entry(free_list.next, struct ivtv_buffer, list);
+		list_del_init(&buf->list);
+
+		buf->readpos = 0;
+		buf->buffer.index = x;
+		buf->buffer.sequence = sequence;
+		buf->ts = jiffies;
+
+		if (size < (IVTV_DMA_BUF_SIZE & 0xffffff00)) {
+			buf->buffer.bytesused = size;
+			/* bytecount must be multiple of 0x100 (256) */
+			itv->SGarray[x].size =
+			    (0xffffff00 & (buf->buffer.bytesused + 0xFF));
+			size = 0;
+		} else {
+			buf->buffer.bytesused = IVTV_DMA_BUF_SIZE;
+			itv->SGarray[x].size = IVTV_DMA_BUF_SIZE;
+			size -= IVTV_DMA_BUF_SIZE;
+		}
+
+		itv->SGarray[x].src = offset;
+		offset += buf->buffer.bytesused;
+
+		/* unfortunately the pci dma api wasn't properly defined
+		 * for handling mapping errors (running out of iommu space,
+		 * for instance). 0 can be a valid bus address. */
+		buf->dma_handle = pci_map_single(itv->dev,
+						 (void *)buf->buffer.m.userptr,
+						 buf->buffer.bytesused,
+						 PCI_DMA_FROMDEVICE);
+
+		itv->SGarray[x].dst = buf->dma_handle;
+
+		/* FIXME need to add pts stuff, index, etc. */
+		__ivtv_enq_buf(&st->dma_q, buf);
+	}
+
+	/* This should wrap gracefully */
+	/* FIXME obselete? */
+	itv->trans_id++;
+
+	itv->SGarray[bufs_needed - 1].size |= 0x80000000;
+
+	/*FIXME unlock */
+
+	data[0] = itv->SG_handle;
+	/* 3 elements * 4 bytes per element * num_elements */
+	data[1] = (3 * 4 * bufs_needed);
+	data[2] = type;
+	data[3] = 0x0;
+
+	for (x = 0; x < bufs_needed; x++)
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "SGarray[%d]: 0x%08x, 0x%08x 0x%08x\n", x,
+			   itv->SGarray[x].src, itv->SGarray[x].dst,
+			   itv->SGarray[x].size);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "Sched dma: addr: 0x%08x, array_size 0x%08x,"
+		   " type 0x%08x\n", data[0], data[1], data[2]);
+
+	set_bit(IVTV_F_S_DMAP, &st->s_flags);
+	ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_SCHED_DMA_TO_HOST,
+		 &result, 4, &data[0]);
+}
+
+static void ivtv_sched_DMA_tasklet(unsigned long arg)
+{
+	struct ivtv *itv = (struct ivtv *)arg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&itv->lock, flags);
+	ivtv_sched_DMA(itv);
+	spin_unlock_irqrestore(&itv->lock, flags);
+}
+
+/* FIXME this function does way more than it should */
+static int __devinit ivtv_probe(struct pci_dev *dev,
+				const struct pci_device_id *pci_id)
+{
+	int retval = 0;
+	unsigned char pci_latency;
+	struct ivtv *itv;
+	struct video_channel v;
+	unsigned long freq;
+	u16 cmd;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Found card #%d\n", ivtv_cards_active);
+
+	spin_lock_irq(&ivtv_lock);
+
+	/* Make sure we've got a place for this card */
+	if (ivtv_cards_active == IVTV_MAX_CARDS) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   ":Maximum # of cards already detected (%d).\n",
+			   ivtv_cards_active);
+		spin_unlock_irq(&ivtv_lock);
+		return -ENOMEM;
+	}
+
+	itv = &ivtv_cards[ivtv_cards_active];
+	itv->dev = dev;
+	itv->num = ivtv_cards_active;
+
+	ivtv_cards_active++;
+
+	spin_unlock_irq(&ivtv_lock);
+
+	/* always remember what you think the irq mask should be */
+	itv->irqmask = 0;
+
+#ifdef YUV_FIXUP
+	itv->options.yuv_fixup = yuv_fixup;
+#endif
+	itv->options.dec_yuv_buffers = dec_yuv_buffers;
+	itv->options.dec_mpg_buffers = mpg_buffers;
+	itv->options.yuv_buffers = yuv_buffers;
+	itv->options.mpg_buffers = mpg_buffers;
+	itv->options.vbi_buffers = vbi_buffers;
+	itv->options.num_devices = num_devices;
+	itv->options.dec_mpg_qlen = dec_mpg_qlen;
+	itv->options.dec_yuv_qlen = dec_yuv_qlen;
+
+	/* Set FrameBuffer-ID to invalid */
+	itv->fb_id = -1;
+
+	switch (dev->subsystem_device) {
+	case IVTV_PCI_ID_250_V2:
+	case IVTV_PCI_ID_250_V4:
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Found an iTVC16 based chip\n");
+		itv->card_type = IVTV_250_V2;
+		break;
+	case IVTV_PCI_ID_350_V1:
+	case IVTV_PCI_ID_350_V2:
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Found an iTVC15 based chip\n");
+		itv->card_type = IVTV_350_V1;
+		break;
+	case IVTV_PCI_ID_250_V1:
+	case IVTV_PCI_ID_250_V3:
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Found an iTVC15 based chip\n");
+		itv->card_type = IVTV_250_V1;
+		break;
+	default:		/* Default to 250 v1 style */
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Found an unknown chip, treating it like an iTVC15\n");
+		itv->card_type = IVTV_250_V1;
+		break;
+	}
+
+	init_MUTEX(&itv->enc_msem);
+	init_MUTEX(&itv->dec_msem);
+	init_MUTEX(&itv->sem_lock);
+	spin_lock_init(&itv->lock);
+	itv->base_addr = pci_resource_start(dev, 0);
+	itv->enc_mbox = NULL;
+	itv->dec_mbox = NULL;
+	itv->io_mem = NULL;
+	itv->reg_mem = NULL;
+	itv->i_flags = 0;
+	atomic_set(&itv->capturing, 0);
+	atomic_set(&itv->decoding, 0);
+	itv->user_dma_to_device_state = NULL;
+
+	/* Prepare list for action! */
+	INIT_LIST_HEAD(&itv->client_list);
+
+	init_waitqueue_head(&itv->cap_w);
+	init_waitqueue_head(&itv->vsync_w);
+	init_waitqueue_head(&itv->dec_master_w);
+	init_timer(&itv->dec_timeout);
+	itv->dec_timeout.function = ivtv_dec_timeout;
+	itv->dec_timeout.data = (unsigned long)itv;
+
+	tasklet_init(&itv->dma_sched_tq, ivtv_sched_DMA_tasklet,
+		     (unsigned long)itv);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "base addr: 0x%08x\n", itv->base_addr);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Enabling pci device\n");
+	if (pci_enable_device(dev)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Can't enable device %d!\n",
+			   itv->num);
+		retval = -EIO;
+		goto err;
+	}
+	if (pci_set_dma_mask(dev, 0xffffffff)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   KERN_WARNING
+			   "No suitable DMA available on card %d.\n", itv->num);
+		retval = -EIO;
+		goto err;
+	}
+	if (!request_mem_region
+	    (pci_resource_start(dev, 0), IVTV_IOREMAP_SIZE, IVTV_DEVNAME)) {
+		retval = -EIO;
+		goto err;
+	}
+
+	/* Check for bus mastering */
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	if (!(cmd & PCI_COMMAND_MASTER)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Bus Mastering is not enabled\n");
+		retval = -ENXIO;
+		goto free_mem;
+	} else {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Bus Mastering Enabled.");
+	}
+
+	pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev);
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "%d (rev %d) at %02x:%02x.%x, ",
+		   itv->dev->device, itv->card_rev, dev->bus->number,
+		   PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "     irq: %d, latency: %d, memory: 0x%lx\n", itv->dev->irq,
+		   pci_latency, (unsigned long)itv->base_addr);
+
+	/*map io memory */
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "attempting ioremap at 0x%08x len 0x%08x\n",
+		   itv->base_addr, IVTV_ENCDEC_SIZE);
+	itv->io_mem = ioremap_nocache(itv->base_addr, IVTV_ENCDEC_SIZE);
+	if (!itv->io_mem) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, IVTV_IOREMAP_ERROR);
+		retval = -ENOMEM;
+		goto free_mem;
+	}
+
+	/*map registers memory */
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "attempting ioremap at 0x%08x len 0x%08x\n",
+		   itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	itv->reg_mem =
+	    ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+	if (!itv->reg_mem) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, IVTV_IOREMAP_ERROR);
+		retval = -ENOMEM;
+		goto free_io;
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_IRQ, "Masking interrupts\n");
+	/* clear interrupt mask, effectively disabling interrupts */
+	ivtv_set_irq_mask(itv, 0xffffffff);
+
+	retval = request_irq(itv->dev->irq, ivtv_irq_handler,
+			     SA_SHIRQ | SA_INTERRUPT, IVTV_DEVNAME,
+			     (void *)itv);
+	if (retval) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "failed to register irq %d\n",
+			   retval);
+		goto free_io;
+	}
+
+	/* save itv in the pci struct for later use */
+	pci_set_drvdata(dev, itv);
+
+	/* active i2c  */
+	itv->i2c_command = (I2C_TIMING);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "activating i2c..\n");
+	if (init_ivtv_i2c(itv)) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "i2c died! unloading\n");
+		goto free_irq;
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Active card count: %d.\n",
+		   ivtv_cards_active);
+
+	/*write firmware */
+	retval = ivtv_firmware_init(itv);
+	if (retval) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error initializing.\n");
+		retval = -ENOMEM;
+		goto free_i2c;
+	}
+
+	/*search for encoder/decoder mailboxes */
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "About to search for mailboxes\n");
+	ivtv_find_firmware_mailbox(itv);
+
+	if ((itv->enc_mbox == NULL) && (itv->dec_mbox == NULL)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error locating firmware.\n");
+		retval = -ENOMEM;
+		goto free_i2c;
+	}
+
+	/*releasing unneeded iomapped memory (encoder+decoder) */
+	//iounmap(itv->io_mem);
+
+	/*remapping only needed io memory (mailboxes) */
+	itv->enc_mbox =
+	    ioremap(itv->base_addr +
+		    ((u8 *) itv->enc_mbox - (u8 *) itv->io_mem),
+		    IVTV_MBOX_MAX_BOXES * IVTV_MBOX_SIZE);
+	itv->dec_mbox =
+	    ioremap(itv->base_addr +
+		    ((u8 *) itv->dec_mbox - (u8 *) itv->io_mem),
+		    IVTV_MBOX_MAX_BOXES * IVTV_MBOX_SIZE);
+
+	/* clearing pointers */
+	//itv->io_mem = NULL ;
+
+	/*Try and get firmware versions */
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Getting firmware version..\n");
+	retval = ivtv_firmware_versions(itv);
+	if (retval) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "error %d getting version #!\n",
+			   retval);
+		goto free_i2c;
+	}
+
+	/* Allocate scatter-gather arrays */
+
+	//++MTY NASTY little bug!!! If user changes dec_mpg_buffers, 
+	// memory corruption results with the old way!
+
+	/* encoder */
+	itv->SGarray = (struct ivtv_SG_element *)
+	    kmalloc(sizeof(struct ivtv_SG_element) * SGarray_size, GFP_KERNEL);
+	if (!(itv->SGarray)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error allocating SGarray[].\n");
+		retval = -ENOMEM;
+		goto free_i2c;
+	}
+
+	itv->SG_handle = pci_map_single(itv->dev,
+					(void *)&itv->SGarray[0],
+					(sizeof(struct ivtv_SG_element) *
+					 SGarray_size), PCI_DMA_TODEVICE);
+
+	if (itv->card_type == IVTV_350_V1) {
+		/* decoder */
+		itv->DSGarray = (struct ivtv_SG_element *)
+		    kmalloc(sizeof(struct ivtv_SG_element) *
+			    DSGarray_size, GFP_KERNEL);
+		if (!(itv->DSGarray)) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "Error allocating DSGarray[].\n");
+			retval = -ENOMEM;
+			goto free_sg;
+		}
+
+		itv->DSG_handle = pci_map_single(itv->dev,
+						 (void *)&itv->DSGarray[0],
+						 (sizeof(struct ivtv_SG_element)
+						  * DSGarray_size),
+						 PCI_DMA_TODEVICE);
+	}
+
+	/* FIXME -temporary- setup tuner */
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Setting Tuner\n");
+
+	if (tuner > -1) {
+		ivtv_call_i2c_client(itv,
+				     IVTV_TUNER_I2C_ADDR,
+				     TUNER_SET_TYPE, &tuner);
+	}
+
+	/* set the standard */
+	if (!ivtv_pal)
+		v.norm = VIDEO_MODE_NTSC;
+	else
+		v.norm = VIDEO_MODE_PAL;
+
+	ivtv_call_i2c_client(itv, IVTV_TUNER_I2C_ADDR, VIDIOCSCHAN, &v);
+
+	if (!ivtv_pal) {
+		/* set the channel */
+		freq = 1076;	/* ch. 4 67250*16/1000 */
+		ivtv_call_i2c_client(itv, IVTV_TUNER_I2C_ADDR, VIDIOCSFREQ,
+				     &freq);
+	}
+
+	retval = ivtv_v4l2_setup(itv);
+	if (retval) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Problem starting v4l2\n");
+		goto ivtv_v4l2_fail;
+	}
+
+	return 0;
+
+      ivtv_v4l2_fail:
+	pci_unmap_single(itv->dev,
+			 itv->DSG_handle,
+			 (sizeof(struct ivtv_SG_element) *
+			  DSGarray_size), PCI_DMA_TODEVICE);
+	kfree(itv->DSGarray);
+      free_sg:
+	pci_unmap_single(itv->dev,
+			 itv->SG_handle,
+			 (sizeof(struct ivtv_SG_element) *
+			  SGarray_size), PCI_DMA_TODEVICE);
+	kfree(itv->SGarray);
+      free_i2c:
+	exit_ivtv_i2c(itv);
+      free_irq:
+	free_irq(itv->dev->irq, (void *)itv);
+      free_io:
+	ivtv_iounmap(itv);
+      free_mem:
+	release_mem_region(pci_resource_start(itv->dev, 0), IVTV_IOREMAP_SIZE);
+      err:
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "Error %d on init\n", retval);
+
+	spin_lock_irq(&ivtv_lock);
+	ivtv_cards_active--;
+	spin_unlock_irq(&ivtv_lock);
+	return retval;
+}
+
+static void ivtv_remove(struct pci_dev *pci_dev)
+{
+	struct ivtv *itv = pci_get_drvdata(pci_dev);
+	int x = 0;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Disabling interrupts!\n");
+	ivtv_set_irq_mask(itv, 0xffffffff);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping thread\n");
+	atomic_set(&itv->decoding, 0);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping card parts\n");
+	x = ivtv_stop_firmware(itv);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing buffers\n");
+
+	for (x = 0; x < itv->v4l2.streamcount; x++) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing stream %d!\n", x);
+		ivtv_free_queue(&itv->v4l2.streams[x].free_q);
+		ivtv_free_queue(&itv->v4l2.streams[x].full_q);
+		ivtv_free_queue(&itv->v4l2.streams[x].dma_q);
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Unregistering v4l devices!\n");
+	ivtv_v4l2_cleanup(itv);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing dma resources\n");
+
+	pci_unmap_single(itv->dev, itv->SG_handle,
+			 (sizeof(struct ivtv_SG_element) * SGarray_size),
+			 PCI_DMA_TODEVICE);
+	kfree(itv->SGarray);
+
+	pci_unmap_single(itv->dev, itv->DSG_handle,
+			 (sizeof(struct ivtv_SG_element) * DSGarray_size),
+			 PCI_DMA_TODEVICE);
+	kfree(itv->DSGarray);
+
+	exit_ivtv_i2c(itv);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing irq\n");
+	free_irq(itv->dev->irq, (void *)itv);
+
+	if (itv->dev) {
+		ivtv_iounmap(itv);
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing mem\n");
+	if (itv)
+		release_mem_region(pci_resource_start(itv->dev, 0),
+				   IVTV_IOREMAP_SIZE);
+
+	/* FIXME free v4l2 stuff! */
+	/* FIXME am i leaking kernel mem? */
+
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver ivtv_pci_driver = {
+      name:"ivtv: iTVC15/16 mpg2 encoder card",
+      id_table:ivtv_pci_tbl,
+      probe:ivtv_probe,
+      remove:ivtv_remove,
+};
+
+#ifdef YUV_FIXUP
+static int ivtv_YUV_fixup(struct ivtv_v4l2_stream *st, int count,
+			  char *ubuf, struct ivtv_buffer *buf)
+{
+/*  
+ * int count = # of bytes to transfer to client 
+ * st->ubytes = # of bytes written this frame
+ * ubuf = buffer to write to (user's buffer)
+ * buf = read buffer
+ *
+ */
+	int src_width = 720;	/* all known formats have src width of 720 */
+	int Hoff, Voff;		/* collectors for offsets to read position */
+	int width, height;	/* resolution of the capture stream */
+	int curline;		/* vertical line currently being processed */
+	int maxline;		/* height of combined frame */
+	int cur_m_block;	/* current horizontal offset of working mblock in this row */
+	int maxblock;		/* # of macroblocks in a row */
+	int Hbytes;		/* # of bytes to write to user this time around */
+	int retval = 0;		/* accumulator for total bytes written */
+	int start;		/* position in buf to read from */
+	int buf_start;		/* byte offset of first byte in this *buf */
+
+	height = st->format.fmt.pix.height;
+	width = st->format.fmt.pix.width;
+	maxblock = (width + 0xf) >> 4;
+	maxline = (int)(1.5 * height);	/* 1 for Y, .5 for UV */
+	/* Offset is always bufsize * buffer index 
+	   buf_start = (st->ubytes - buf->readpos);  tested/works */
+
+	buf_start = IVTV_DMA_BUF_SIZE * buf->buffer.index;
+
+	/* FIXME it may not be possible to get YUV width > 720 */
+	// if (width > src_width) src_width=width;
+
+	curline = (int)(st->ubytes / width);
+
+	while (curline < maxline) {
+//      printk(" cl: %d, ml: %d\n", curline, maxline);
+		Voff = 16 * (curline & 0xf) +	/* Voffset within MBlock */
+		    ((curline & 0xfff0) * src_width);	/* Voffset of Mblock */
+
+		cur_m_block = (st->ubytes - (curline * width)) >> 4;
+
+/*	printk("voff %d, macroVoff %d, Voff %d, cmb %d\n", (16 * (curline & 0xf)), 
+			((curline & 0xff0) * src_width), Voff, cur_m_block);
+*/
+
+		while ((cur_m_block < maxblock) && (count > 0)) {
+			Hoff = (cur_m_block * 256) +	/* mblock offset within line */
+			    /* Hoffset within mblock, usually 0 */
+			    ((st->ubytes - (curline * width)) & 0xf);
+			Hbytes = 16 - ((st->ubytes - (curline * width)) & 0xf);
+
+			if (Hbytes > count)
+				Hbytes = count;
+
+			start = Hoff + Voff;
+
+			if (copy_to_user((char *)((u32) ubuf + retval),
+					 (u32 *) ((u32) buf->buffer.m.userptr +
+						  (start - buf_start)),
+					 Hbytes)) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "copy to user failed\n");
+				return -EFAULT;
+			}
+
+			count -= Hbytes;
+			retval += Hbytes;
+			st->ubytes += Hbytes;
+
+			cur_m_block++;
+		}
+
+		/* if user can't handle anymore data or buffer empty */
+		curline++;
+		if ((count == 0))	/*|| ((curline * src_width) % IVTV_DMA_BUF_SIZE) == 0) */
+			return retval;
+	}
+
+	/* FIXME i don't think you should ever get here */
+	IVTV_DEBUG(IVTV_DEBUG_ERR,
+		   "You've just sailed off the edge of this function\n");
+	return retval;
+}
+#endif
+
+long ivtv_read(struct ivtv_open_id *id, char *ubuf, size_t count, int block)
+{
+	int x, sleepctr, datalen, retval = 0, freed = 0;
+	struct ivtv *itv = id->itv;
+	size_t newcount;
+	unsigned long tid;
+	struct ivtv_buffer *buf;
+	struct ivtv_v4l2_stream *st = &itv->v4l2.streams[id->type];
+	DECLARE_WAITQUEUE(wait, current);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, " Read stream.. \n");
+
+	if (atomic_read(&itv->capturing) == 0 && (st->id == -1)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Stream not initialized before read(shouldn't happen)\n");
+		return -EIO;
+	}
+
+	/* FIXME find a way to gracefully exit capture */
+
+	sleepctr = retval = 0;
+	buf = NULL;
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	add_wait_queue(&st->waitq, &wait);
+	do {
+		if ((itv->trans_id == 0) && (sleepctr >= IVTV_MAX_DATA_SLEEP)) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "Timeout waiting for data!\n");
+			retval = -EIO;
+			break;
+		}
+
+		buf = ivtv_deq_peek_head(itv, &st->full_q);
+		if (buf) {
+			break;
+		} else {
+
+			/* Done capturing? */
+			if (!test_bit(IVTV_F_S_CAP, &st->s_flags)) {
+				IVTV_DEBUG(IVTV_DEBUG_INFO,
+					   "No more data to send, returning 0\n");
+				set_current_state(TASK_RUNNING);
+				remove_wait_queue(&st->waitq, &wait);
+				return 0;
+			}
+		}
+
+		if (!block) {
+			retval = -EAGAIN;
+			break;
+		}
+
+		ivtv_sleep_timeout(IVTV_SLEEP_WAIT);
+
+		if (signal_pending(current))
+			retval = -ERESTARTSYS;
+
+		sleepctr++;
+	} while (!retval);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&st->waitq, &wait);
+
+	/* an error (or signal) occured */
+	if (retval) {
+		return retval;
+	}
+
+	/* Skip the first 4 bytes of mpg streams to help out
+	 * finicky decoders.. but not for iTVC16 */
+	if ((id->type == 0) && (itv->first_read == 1) &&
+	    (itv->card_type != IVTV_250_V2)) {
+		for (x = 0; x < buf->buffer.bytesused - 4; x++) {
+			unsigned char *p;
+			itv->first_read = 0;
+			p = (unsigned char *)buf->buffer.m.userptr +
+			    buf->readpos + x;
+			if (!p[0] && !p[1] && p[2] == 1) {
+				IVTV_DEBUG(IVTV_DEBUG_INFO,
+					   "Stripping first 4 bytes\n");
+				buf->buffer.bytesused -= x;
+				buf->readpos += x;
+				break;
+			}
+		}
+	}
+
+	/* data ready */
+	/* copy it to the client */
+	while ((count > 0) && (buf->buffer.bytesused > 0)) {
+		newcount = count;
+		datalen = buf->buffer.bytesused;
+
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "datalen 0x%08x\n", datalen);
+
+		if (newcount > datalen)
+			newcount = datalen;
+
+#ifdef YUV_FIXUP
+		if ((id->type == 1) && (itv->options.yuv_fixup)) {
+			newcount =
+			    ivtv_YUV_fixup(st, newcount, ubuf + retval, buf);
+			if (newcount < 0) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "Error fixing up YUV!\n");
+				return newcount;
+			}
+		} else {
+#endif
+			if (copy_to_user((char *)((u32) ubuf + retval),
+					 (u32 *) ((u32) buf->buffer.m.userptr +
+						  buf->readpos), newcount)) {
+				IVTV_DEBUG(IVTV_DEBUG_ERR,
+					   "copy to user failed\n");
+				return -EFAULT;
+			}
+#ifdef YUV_FIXUP
+		}
+#endif
+
+		buf->readpos += newcount;
+		retval += newcount;
+		count -= newcount;
+		buf->buffer.bytesused -= newcount;
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "new datalen 0x%08x\n", buf->buffer.bytesused);
+
+		/* if buffer is empty or we've read the whole frame */
+		if ((buf->buffer.bytesused == 0)) {
+			ivtv_move_buf(itv, &st->full_q, &st->free_q, buf);
+			freed++;
+
+			buf = ivtv_deq_peek_head(itv, &st->full_q);
+			if (buf) {
+				tid = buf->buffer.sequence;
+				if (buf->buffer.sequence != tid) {
+					/* end of frame! */
+					st->ubytes = 0;
+					break;
+				}
+			} else {
+				/* user wanted more than we had. Since
+				 * queues are filled in irq time, 
+				 * that means end of frame */
+				st->ubytes = 0;
+				break;
+			}
+		}
+	}			/* end of while */
+
+	/* if we put some buffers back in the free queue, kick off dma
+	 * scheduling if card was stopped due to overflow before */
+	if (freed && test_and_clear_bit(IVTV_F_S_OVERFLOW, &st->s_flags)) {
+		spin_lock_irq(&itv->lock);
+		ivtv_sched_DMA(itv);
+		spin_unlock_irq(&itv->lock);
+	}
+
+	/*FIXME unlock */
+	if (retval != 0) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Returning %d\n", retval);
+		return retval;
+	}
+
+	/* Shouldn't ever get here */
+	return -EIO;
+}
+
+static void ivtv_swap_copy(const char *buf, const char *ubuf, size_t count)
+{
+	u32 *src, *dst;
+
+	src = (u32 *) ubuf;
+	dst = (u32 *) buf;
+
+#ifdef CONFIG_X86
+	while ((u32) src <= (u32) ubuf + count) {	/* byteswap while copying */
+		__asm__ __volatile__("bswap %0":"=r"(*dst):"0"(*src));
+		src++;
+		dst++;
+	}
+#else
+	{
+		int y;
+		/* Old slow memcpy then swab */
+		memcpy((void *)buf, (void *)ubuf, count);
+		for (y = 0; y < count; y += 4) {
+			swab32s((u32 *) ((u32) buf + y));
+		}
+	}
+#endif
+}
+
+static int ivtv_fill_dec_buffers(struct ivtv_open_id *id, const char *ubuf,
+				 size_t count, int block)
+{
+	struct ivtv *itv = id->itv;
+	struct ivtv_v4l2_stream *stream = &itv->v4l2.streams[id->type];
+	struct ivtv_buffer *buf;
+	int copybytes = 0, bytesread = 0, retval = 0;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_fill_dec_buffers, %d bytes\n", count);
+
+	/* Read what the user gives us. queue it for DMA after each buffer
+	 * also enqueue partly-full buffers. */
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "DEC: free_q: %d elements\n", stream->free_q.elements);
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "DEC: dma_q: %d elements\n", stream->dma_q.elements);
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "DEC: full_q: %d elements\n", stream->full_q.elements);
+
+	/* FIXME will only do one write. Has underlying code to handle more
+	 *      than one, just need loop control logic for it, if it's 
+	 *      deemed necessary. */
+	while (bytesread == 0) {
+		DECLARE_WAITQUEUE(wait, current);
+		unsigned long flags;
+
+		buf = NULL;
+		add_wait_queue(&stream->waitq, &wait);
+		do {
+			set_current_state(TASK_INTERRUPTIBLE);
+			buf = ivtv_deq_peek_head(itv, &stream->free_q);
+			if (buf)
+				break;
+
+			if (!block) {
+				retval = -EAGAIN;
+				break;
+			}
+
+			schedule();
+
+			if (signal_pending(current)) {
+				retval = -ERESTARTSYS;
+				break;
+			}
+
+			spin_lock_irqsave(&itv->lock, flags);
+			ivtv_dec_sched_DMA(id->itv);
+			spin_unlock_irqrestore(&itv->lock, flags);
+		} while (!buf);
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&stream->waitq, &wait);
+
+		if (retval)
+			return retval;
+
+		/* bytes left to send > free bytes in current buffer */
+		if ((count - bytesread) >
+		    (IVTV_DMA_DEC_BUF_SIZE - buf->buffer.bytesused)) {
+			copybytes =
+			    IVTV_DMA_DEC_BUF_SIZE - buf->buffer.bytesused;
+		} else {
+			copybytes = count - bytesread;
+		}
+
+		/* copy data */
+		/* FIXME */
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "copying %d bytes to 0x%08x"
+			   " (buffer free = %d, used = %d)\n",
+			   copybytes,
+			   (int)(buf->buffer.m.userptr +
+				 buf->buffer.bytesused),
+			   (int)(IVTV_DMA_DEC_BUF_SIZE -
+				 buf->buffer.bytesused), buf->buffer.bytesused);
+
+		ivtv_swap_copy((char *)(buf->buffer.m.userptr +
+					buf->buffer.bytesused),
+			       (char *)((u32) ubuf + bytesread), copybytes);
+
+		bytesread += copybytes;
+		buf->buffer.bytesused += copybytes;
+
+		/* enq buffer when full */
+		if (buf->buffer.bytesused == IVTV_DMA_DEC_BUF_SIZE)
+			ivtv_move_buf(itv, &stream->free_q, &stream->full_q,
+				      buf);
+	}
+
+	return bytesread;
+}
+
+/*
+ * Schedule host -> hardware DMA of one buffer from the stream (MPEG or YUV) 
+ * with the most recent request for more data, but only if dec->dec_needs_data
+ * is set.
+ *
+ * This code can be called from both interrupt context as well as userspace;
+ * it does the right things in either case. If called from userspace, it may
+ * block only when the same call is in progress in interupt context (since 
+ * interrupt context is not allowed to block.) 
+ *
+ * @returns 0 if the buffer was queued to dma_q and the DMA was initiated.
+ *
+ *          -EAGAIN if either the full_q queue is empty or the function is
+ *                  already in progress in interrupt context.
+ *
+ *          -ENOSPC if there is no space remaining in the hardware's buffer.
+ *                  This should never happen if proper flow control is used.
+ *
+ *          -EINVAL if the most recent "data needed" interrupt requested an
+ *                  unknown stream type (should really never happen!)
+ *
+ *          -EBUSY if a DMA on the same queue is already in progress (should
+ *                 never happen)
+ *
+ */
+static void ivtv_dec_sched_DMA(struct ivtv *itv)
+{
+	int ret = 0, x = 0, bytes_written = 0, type = 0, max = 2;
+	struct ivtv_buffer *buf;
+	struct ivtv_v4l2_stream *stream = NULL;
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	u32 mem_offset, mem_size, hw_stream_type, buffer_bytes;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_dec_sched_DMA\n");
+
+	/* fancy way of saying "if (ivtv->dec_needs_data == 0)" */
+	if (!test_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags)) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: no data needed\n");
+		return;
+	}
+
+	/* Get Card Mem Dst address from mailbox 10 */
+	ret = ivtv_api_getresult_nosleep(&itv->dec_mbox[9], &result, &data[0]);
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "DEC: Mailbox 10: 0x%08x 0x%08x 0x%08x 0x%08x\n", data[0],
+		   data[1], data[2], data[3]);
+
+	hw_stream_type = data[0];
+
+	switch (hw_stream_type) {
+	case 0:		/* MPG */
+		type = IVTV_DEC_STREAM_TYPE_MPG;
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: mpg data\n");
+		break;
+	case 2:		/* YUV */
+		type = IVTV_DEC_STREAM_TYPE_YUV;
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: yuv data\n");
+		break;
+	default:
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "DEC: unknown stream type %d\n",
+			   data[0]);
+		max = 0;
+		return;
+	}
+
+	stream = &itv->v4l2.streams[type];
+
+	if (test_bit(IVTV_F_I_BUSY, &itv->i_flags)) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: decoder busy, delaying\n");
+		set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags);
+		return;
+	}
+
+	/* If we got this far, we have data to send and it wants it */
+	clear_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags);
+
+	/* Get card mem addr and size from data array */
+	mem_offset = data[1];
+	mem_size = data[2];
+	buffer_bytes = data[3];	/* # bytes in card's buffer */
+
+	while ((max > x) && (mem_size > bytes_written)) {	/* send a maximum of 
+								   'max' buffers */
+		buf = __ivtv_deq_peek_head(&stream->full_q);
+		if (buf == NULL) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO,
+				   "DEC: No more buffers to send\n");
+			break;
+		}
+#if 1
+		if (mem_size < buf->buffer.bytesused) {
+			itv->DSGarray[x].size = mem_size;
+		} else {
+			itv->DSGarray[x].size = buf->buffer.bytesused;
+		}
+#else
+		/* just send the whole buffer */
+		itv->DSGarray[x].size = buf->buffer.bytesused;
+#endif
+		buf->dma_handle = pci_map_single(itv->dev,
+						 (void *)(buf->buffer.m.
+							  userptr +
+							  buf->readpos),
+						 itv->DSGarray[x].size,
+						 PCI_DMA_TODEVICE);
+
+		itv->DSGarray[x].src = buf->dma_handle;
+		itv->DSGarray[x].dst = (mem_offset + bytes_written +
+					IVTV_FIRM_SEARCH_DECODER_START);
+
+		buf->readpos += itv->DSGarray[x].size;
+		bytes_written += itv->DSGarray[x].size;
+		buf->buffer.bytesused -= itv->DSGarray[x].size;
+
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "1st 32bits of buffer %d are 0x%08x\n",
+			   buf->buffer.index, *(u32 *) buf->buffer.m.userptr);
+		IVTV_DEBUG(IVTV_DEBUG_INFO,
+			   "DSGarray[%d]: 0x%08x, 0x%08x 0x%08x\n", x,
+			   itv->DSGarray[x].src, itv->DSGarray[x].dst,
+			   itv->DSGarray[x].size);
+
+		/* buffer is empty? */
+		if (buf->buffer.bytesused == 0) {
+			__ivtv_del_buf(&stream->full_q, buf);
+			__ivtv_enq_buf(&stream->dma_q, buf);
+		}
+		x++;
+	}
+
+	if (x == 0) {		/* no full buffers */
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: Nothing to send\n");
+		set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags);
+		return;
+	}
+	//Set Last Element Bit
+	itv->DSGarray[x - 1].size |= 0x80000000;
+
+	//Schedule DMA XFER
+	data[0] = itv->DSG_handle;
+	data[1] = bytes_written;
+	data[2] = hw_stream_type;
+
+	/* note that we're DMA'ing */
+	mod_timer(&itv->dec_timeout, jiffies + DEC_DMA_TIMEOUT);
+	set_bit(IVTV_F_S_DMAP, &stream->s_flags);
+	set_bit(IVTV_F_I_BUSY, &itv->i_flags);
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_DMA_FROM_HOST,
+		 &result, 3, &data[0]);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "Sched DEC dma: addr: 0x%08x, array_size 0x%08x, type 0x%08x\n",
+		   data[0], data[1], data[2]);
+}
+
+static void ivtv_dec_DMA_done(struct ivtv *itv)
+{
+	struct ivtv_v4l2_stream *stream = NULL;
+	struct ivtv_buffer *buf;
+	int y, stmtype = -1, freed = 0;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: DMA Done tasklet\n");
+
+	if (!test_and_clear_bit(IVTV_F_I_BUSY, &itv->i_flags)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "DMAP not set\n");
+		ivtv_show_debug_flags(itv);
+		return;
+	}
+#if 0
+	del_timer(&itv->dec_timeout);
+#else
+	mod_timer(&itv->dec_timeout, jiffies + DEC_DMA_TIMEOUT);
+#endif
+
+	for (y = IVTV_DEC_STREAM_TYPE_MPG; y < itv->v4l2.streamcount; y++) {
+		if (test_and_clear_bit
+		    (IVTV_F_S_DMAP, &itv->v4l2.streams[y].s_flags)) {
+			stmtype = y;
+			break;
+		}
+	}
+
+	/* Handle OSD DMA */
+	if (test_and_clear_bit(IVTV_F_I_OSD_DMA, &itv->i_flags)) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "OSD: DMA Done\n");
+
+		/* wake all the normal streams, in case they fell asleep */
+		for (y = IVTV_DEC_STREAM_TYPE_MPG; y < itv->v4l2.streamcount;
+		     y++) {
+			wake_up(&itv->v4l2.streams[y].waitq);
+		}
+
+		wake_up(&itv->dec_master_w);
+		return;
+	}
+
+	if (stmtype < 0) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "DEC: Got DMA-done, not expecting one\n");
+		ivtv_show_debug_flags(itv);
+		return;
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: Stream %d dma-done\n", y);
+	stream = &itv->v4l2.streams[y];
+
+	while ((buf = __ivtv_deq_buf(&stream->dma_q)) != NULL) {
+		IVTV_ASSERT(buf->dma_handle != IVTV_DMA_UNMAPPED);
+		pci_unmap_single(itv->dev, buf->dma_handle,
+				 IVTV_DMA_DEC_BUF_SIZE, PCI_DMA_TODEVICE);
+		buf->dma_handle = IVTV_DMA_UNMAPPED;
+		buf->buffer.bytesused = 0;
+		buf->readpos = 0;
+
+		/* put in the 'done' queue */
+		__ivtv_enq_buf(&stream->free_q, buf);
+		freed++;
+	}
+
+	/* if we put some buffers back in the free queue, kick off dma
+	 * scheduling if card was stopped due to overflow before */
+	if (freed && test_and_clear_bit(IVTV_F_S_OVERFLOW, &stream->s_flags))
+		ivtv_sched_DMA(itv);
+
+	/* wake up queue filler */
+	wake_up(&stream->waitq);
+	wake_up(&itv->dec_master_w);
+}
+
+int ivtv_get_timing_info(struct ivtv *itv, struct ivtv_ioctl_framesync *info)
+{
+	u32 ret, result, data[IVTV_MBOX_MAX_DATA];
+
+	int suicidecounter = 0;
+
+	memset(info, 0x00, sizeof(struct ivtv_ioctl_framesync));
+
+	/* Occasionally, we'll get a wierd, invalid number for 
+	 * frames played. fortunately, it sets the SCR timestamp to 0
+	 * in that case, which it never is otherwise. cool, huh */
+	while (info->scr == 0) {	/* eliminate bogus values, FIXME ugly */
+		ret = ivtv_api(itv->dec_mbox, &itv->dec_msem,
+			       IVTV_API_DEC_TIMING_INFO, &result, 0, &data[0]);
+		if (ret) {
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "DEC: err sending timing info\n");
+			return ret;
+		}
+
+		info->frame = data[0];
+		info->pts = ((u64) data[2] << 32) | (u64) data[1];
+		info->scr = ((u64) data[4] << 32) | (u64) data[3];
+
+		if (suicidecounter++ > 10) {	/* endless loops are bad! */
+			IVTV_DEBUG(IVTV_DEBUG_ERR,
+				   "Timeout getting frames played\n");
+			return -1;
+		}
+		if (info->scr == 0)
+			ivtv_sleep_timeout(HZ / 50);
+	}
+
+	return 0;
+}
+
+ssize_t ivtv_write(struct ivtv_open_id * id, const char *ubuf, size_t count,
+		   int block)
+{
+	int bytes_written = 0, ret = 0;
+	unsigned long flags;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_write\n");
+
+	while (bytes_written < count) {	/* completely use up user data 
+					 * before returning */
+		/* buffer the data - this may block waiting on free buffers */
+		ret = ivtv_fill_dec_buffers(id, ubuf + bytes_written,
+					    (count - bytes_written), block);
+
+		/* FIXME temporary hack to make sure non-blocking works */
+		/* send it! it'll return right away if no data needed */
+		spin_lock_irqsave(&id->itv->lock, flags);
+		ivtv_dec_sched_DMA(id->itv);
+		spin_unlock_irqrestore(&id->itv->lock, flags);
+
+		if (ret < 0) {
+			break;
+		} else {
+			bytes_written += ret;
+		}
+	}
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: returning %d\n", bytes_written);
+	return bytes_written ? bytes_written : ret;
+}
+
+unsigned int ivtv_dec_poll(struct file *filp, poll_table * wait)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	unsigned int mask = 0;
+	unsigned long flags;
+
+	/* add stream's waitq to the poll list */
+	poll_wait(filp, &id->itv->v4l2.streams[id->type].waitq, wait);
+
+	/* FIXME temporary hack to restart DMA in case the decoder's
+	   been busy for a while and the application is using poll to
+	   see if it can write */
+	spin_lock_irqsave(&id->itv->lock, flags);
+	ivtv_dec_sched_DMA(id->itv);
+	spin_unlock_irqrestore(&id->itv->lock, flags);
+
+	if (ivtv_deq_peek_head
+	    (id->itv, &id->itv->v4l2.streams[id->type].free_q))
+		mask |= POLLOUT | POLLWRNORM;	/* Writable */
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: dec_poll returning 0x%x\n", mask);
+	if (!mask) {
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder fullq size %d\n",
+			   id->itv->v4l2.streams[id->type].full_q.elements);
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder freeq size %d\n",
+			   id->itv->v4l2.streams[id->type].free_q.elements);
+		IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder dmaq size %d\n",
+			   id->itv->v4l2.streams[id->type].dma_q.elements);
+	}
+	return mask;
+}
+
+unsigned int ivtv_poll(struct file *filp, poll_table * wait)
+{
+	struct ivtv_open_id *id = filp->private_data;
+	unsigned int mask = 0;
+
+	/* add stream's waitq to the poll list */
+	poll_wait(filp, &id->itv->v4l2.streams[id->type].waitq, wait);
+
+#if 0
+	if (down_interruptible(&id->itv->sem_lock))
+		return -ERESTARTSYS;
+
+	if (ivtv_get_free_elements
+	    (id->itv, &id->itv->v4l2.streams[id->type].full_q))
+		mask |= POLLIN | POLLRDNORM;	/* readable */
+
+	up(&id->itv->sem_lock);
+#else
+	mask |= POLLIN | POLLRDNORM;
+#endif
+	return mask;
+}
+
+#if 0
+static void ivtv_print_boxes(struct ivtv_mailbox *mbox)
+{
+
+	int x, y;
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n");
+	ivtv_sleep_timeout(HZ / 100);
+
+	if (NULL == mbox) {
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "Mailboxes not initialized!\n");
+		return;
+	}
+
+	for (x = 0; x <= IVTV_MBOX_MAX_BOXES; x++) {
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "mbox: 0x%08x, # %d\n", (u32) mbox,
+			   x);
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "flags:   0x%08x   ",
+			   (u32) readl(&mbox->flags));
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "cmd:     0x%08x\n",
+			   readl(&mbox->cmd));
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "result:  0x%08x   ",
+			   readl(&mbox->retval));
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "timeout: 0x%08x\n",
+			   readl(&mbox->timeout));
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "Data:\n");
+		for (y = 0; y < IVTV_MBOX_MAX_DATA; y++) {
+			IVTV_DEBUG(IVTV_DEBUG_IOCTL, "[%02d]0x%08x, ", y,
+				   readl(&mbox->data[y]));
+			if (2 == y % 3)
+				IVTV_DEBUG(IVTV_DEBUG_IOCTL, "\n");
+		}
+		/*Since mbox has type ptr, this should step it up */
+		/* to the start of the next mbox */
+		mbox++;
+		IVTV_DEBUG(IVTV_DEBUG_IOCTL, "\n");
+	}
+}
+#endif
+
+int ivtv_close(struct ivtv_open_id *id)
+{
+	struct ivtv *itv = id->itv;
+	int ret = 0;
+
+	/* sem_lock must be held */
+	IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0);
+
+	switch (id->type) {
+	case IVTV_DEC_STREAM_TYPE_MPG:	/* decoder streams */
+	case IVTV_DEC_STREAM_TYPE_YUV:
+		if (atomic_read(&itv->decoding)) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "close stopping decode\n");
+			ret = ivtv_stop_decode(id);
+		}
+		break;
+	default:		/* encoder streams */
+		if (atomic_read(&itv->capturing)) {
+			IVTV_DEBUG(IVTV_DEBUG_INFO, "close stopping capture\n");
+			ret = ivtv_stop_capture(id);
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int module_start(void)
+{
+	int loop_a;
+
+	printk("ivtv: version %s (%s) loading\n", IVTV_VERSION_STRING(ivtv_rev),
+	       IVTV_VERSION_COMMENT(ivtv_rev));
+
+	memset(&ivtv_cards[0], 0, IVTV_MAX_CARDS * sizeof(struct ivtv));
+
+	/* Validate parameters */
+	if (((yuv_buffers > IVTV_MAX_YUV_BUFFERS)
+	     || (yuv_buffers < IVTV_MIN_YUV_BUFFERS))
+	    && (yuv_buffers != 0)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! yuv_buffers must be between 40 and 500\n");
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+#ifdef YUV_FIXUP
+	if ((yuv_fixup != 0) && (yuv_fixup != 1)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error! yuv_fixup must be 0 or 1\n");
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+#endif
+	if ((dec_mpg_buffers > IVTV_MAX_DEC_MPG_BUFFERS)
+	    || (dec_mpg_buffers < IVTV_MIN_DEC_MPG_BUFFERS)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! dec_mpg_buffers must be between 5 and 100\n");
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+
+	if (((dec_yuv_buffers > IVTV_MAX_DEC_YUV_BUFFERS)
+	     || (dec_yuv_buffers < IVTV_MIN_DEC_YUV_BUFFERS))
+	    && (dec_yuv_buffers != 0)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! dec_yuv_buffers must be between 17 and 500\n");
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+
+	if ((mpg_buffers > IVTV_MAX_MPG_BUFFERS)
+	    || (mpg_buffers < IVTV_MIN_MPG_BUFFERS)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! mpg_buffers must be between 15 and 100\n");
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+
+	if ((dec_yuv_qlen > dec_yuv_buffers)
+	    || (dec_yuv_qlen < IVTV_MIN_DEC_YUV_QLEN)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! dec_yuv_qlen must be between %d and %d\n",
+			   IVTV_MIN_DEC_YUV_QLEN, dec_yuv_buffers);
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+
+	if ((dec_mpg_qlen > dec_mpg_buffers)
+	    || (dec_mpg_qlen < IVTV_MIN_DEC_MPG_QLEN)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! dec_mpg_qlen must be between %d and %d\n",
+			   IVTV_MIN_DEC_MPG_QLEN, dec_mpg_buffers);
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+
+	if ((vbi_buffers > IVTV_MAX_VBI_BUFFERS)
+	    || (vbi_buffers < IVTV_MIN_VBI_BUFFERS)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! vbi_buffers must be between 3 and 100\n");
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+
+	if ((num_devices > IVTV_MAX_CARDS) || (num_devices < 1)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR,
+			   "Error! num_devices must be between 1 and 9 (not working yet)\n");
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n");
+		return -1;
+	}
+
+	if (ivtv_debug < 0)
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "debug value must be >= 0!\n");
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "Loading, I'll try to detect %d devices!\n", num_devices);
+	IVTV_DEBUG(IVTV_DEBUG_INFO, " .. running on kernel %s\n", UTS_RELEASE);
+	IVTV_DEBUG(IVTV_DEBUG_INFO,
+		   "Setting some variables to invalid for detection\n");
+
+	for (loop_a = 0; loop_a < IVTV_MAX_CARDS; loop_a++) {
+		ivtv_cards[loop_a].num = -1;
+		ivtv_cards[loop_a].dev = NULL;
+	}
+
+	SGarray_size = (mpg_buffers + yuv_buffers + vbi_buffers) * 2;
+	DSGarray_size = (dec_mpg_buffers + dec_yuv_buffers) * 2;
+
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "SGarray_size = %d, DSGarray_size = %d\n",
+		   SGarray_size, DSGarray_size);
+
+	IVTV_DEBUG(IVTV_DEBUG_INFO, "Scanning PCI bus..\n");
+	if (pci_module_init(&ivtv_pci_driver)) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error detecting PCI card\n");
+		return -ENODEV;
+	}
+
+	printk("ivtv: loaded\n");
+	return 0;
+}
+
+static void module_cleanup(void)
+{
+
+	pci_unregister_driver(&ivtv_pci_driver);
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "You've not seen the last of willy!\n");
+#ifdef AEW_DEBUG
+	DUMP_BAD_ALLOC_TABLE;
+#endif				// AEW_DEBUG
+}
+
+EXPORT_SYMBOL(ivtv_pal);
+EXPORT_SYMBOL(ivtv_set_irq_mask);
+EXPORT_SYMBOL(ivtv_cards_active);
+EXPORT_SYMBOL(ivtv_cards);
+EXPORT_SYMBOL(ivtv_api);
+EXPORT_SYMBOL(ivtv_clear_irq_mask);
+EXPORT_SYMBOL(ivtv_debug);
+
+module_init(module_start);
+module_exit(module_cleanup);
diff -upN /dev/null current/drivers/media/video/ivtv-fb.c
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/ivtv-fb.c	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,1039 @@
+/*
+ * iTVC15 Framebuffer driver
+ *
+ * This module presents the iTVC15 OSD (onscreen display) framebuffer memory 
+ * as a standard Linux /dev/fb style framebuffer device. The framebuffer has
+ * a 32 bpp packed pixel format with full alpha channel support. Depending
+ * on the TV standard configured in the ivtv module at load time, resolution
+ * is fixed at either 720x480 (NTSC) or 720x576 (PAL).
+ *
+ * Copyright (c) 2003 Matt T. Yourst <yourst@yourst.com>
+ *
+ * Derived from drivers/video/vesafb.c
+ * Portions (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ * This file is licensed under the GNU General Public License, version 2.
+ *
+ */
+
+/*
+#
+# Instructions for making ivtv-fb work with XFree86:
+# Add the following sections and parts thereof to /etc/X11/XF86Config:
+#
+
+#
+# NOTE: The monitor section is obtainable by running:
+# fbset -fb /dev/fb1 -x
+# (or /dev/fbX for whatever framebuffer ivtv-fb is on)
+#
+Section "Monitor"
+    Identifier  "NTSC Monitor"
+    HorizSync  30-68
+    VertRefresh 50-120
+    Mode "720x480"
+      # D: 34.563 MHz, H: 37.244 kHz, V: 73.897 Hz
+      DotClock 34.564
+      HTimings 720 752 840 928
+      VTimings 480 484 488 504
+      Flags    "-HSync" "-VSync"
+    EndMode
+EndSection
+
+Section "Device"
+    Identifier  "Hauppauge PVR 350 iTVC15 Framebuffer"
+    Driver      "fbdev"
+    Option      "fbdev" "/dev/fb1"      # <-- modify if using another device
+    BusID "0:10:0"
+EndSection
+
+Section "Screen"
+  Identifier  "TV Screen"
+  Device      "Hauppauge PVR 350 iTVC15 Framebuffer"
+  Monitor     "NTSC Monitor"
+  DefaultDepth 24
+  DefaultFbbpp 32
+  Subsection "Display"
+    Depth 24
+    FbBpp 32
+    Modes "720x480"
+  EndSubsection
+EndSection
+
+Section "ServerLayout"
+  ...
+
+  Screen 0 "Screen 1"                      # << (your computer monitor)
+
+  # (add the following line)
+  Screen 1 "TV Screen" RightOf "Screen 1"  # << (TV screen)
+
+  ...
+EndSection
+
+#
+# Then start X as usual; both your normal (computer) monitor and the
+# NTSC or PAL TV monitor should display the default X background.
+#
+# Note the "RightOf" clause above: if you move the mouse off the right
+# side of the computer screen, the pointer should appear on your TV
+# screen. Keyboard events will go to windows in either screen.
+#
+# To start a program (e.g., xterm) on the TV only:
+#
+# export DISPLAY=:0.1         (i.e., X server #0, screen #1 = TV)
+# xterm&
+#
+# There is also a way to join both the computer monitor and TV into
+# one giant virtual screen using the Xinerama extension, but I haven't 
+# tried it. Doing so may not be such a good idea anyway, as you obviously
+# wouldn't want random X windows getting moved over the TV picture.
+
+A note on unloading the fb driver:
+
+If you want to be able to unload the framebuffer driver (and you aren't
+already using fbcon),  add this to your lilo config:
+
+video=vc:x-y
+
+where x is the first fb device to allocate and y is the second. If you 
+already have a fb driver loaded, fiddle with the numbers so all the consoles
+are already allocated. For me, i just set it to 0-0, ie:
+
+in lilo.conf:
+
+image=/vmlinuz
+        label=linux
+        read-only
+        append="root=/dev/hda1 video=vc:0-0"
+
+--OR--
+on bootup, do this
+LILO: linux video=vc:0-0
+
+according to how i read /usr/src/linux/drivers/video/fbmem.c and
+/usr/src/linux/drivers/char/console.c, that should disable the
+console hijacks, and allow you to unload the driver.
+
+-tmk
+#
+#
+#
+#
+#
+#
+#
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/bitops.h>
+#include <linux/pagemap.h>
+
+#include <asm/io.h>
+#include <asm/ioctl.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "ivtv.h"
+
+/*
+ * card parameters
+ */
+
+static int ivtv_fb_card_id;
+
+/* Card selected as framebuffer for this module instance: */
+static struct ivtv *ivtv_fb;
+
+/* card */
+static unsigned long video_base;	/* physical addr */
+static unsigned long video_rel_base;	/* address relative to base of decoder memory */
+static int video_size;
+static char *video_vbase;	/* mapped */
+
+/* mode */
+static int video_width;
+static int video_height;
+static int video_height_virtual;
+static int video_linelength;
+static unsigned long shadow_framebuf_offset;
+static unsigned long shadow_framebuf_size;
+
+/*
+ * ivtv API calls for framebuffer related support
+ */
+
+static inline int ivtv_api_fb_get_framebuffer(struct ivtv *itv,
+					      void **fbbase, int *fblength)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int rc;
+
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_framebuffer\n");
+
+	rc = ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		      IVTV_API_FB_GET_FRAMEBUFFER, &result, 0, &data[0]);
+	*fbbase = (void *)data[0];
+	*fblength = data[1];
+	return rc;
+}
+
+static inline int ivtv_api_fb_get_pixel_format(struct ivtv *itv)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_pixel_format\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_PIXEL_FORMAT,
+		 &result, 0, &data[0]);
+	return data[0];
+}
+
+static inline int ivtv_api_fb_set_pixel_format(struct ivtv *itv, int format)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	data[0] = format;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_pixel_format\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		 IVTV_API_FB_SET_PIXEL_FORMAT, &result, 1, &data[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_get_state(struct ivtv *itv)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_state\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_STATE,
+		 &result, 0, &data[0]);
+	return data[0];
+}
+
+static inline int ivtv_api_fb_set_state(struct ivtv *itv, int enabled)
+{
+	u32 params[IVTV_MBOX_MAX_DATA], result;
+	params[0] = enabled;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_state\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_SET_STATE,
+		 &result, 1, &params[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_set_framebuffer_window(struct ivtv *itv,
+						     int left, int top,
+						     int width, int height)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_framebuffer_window\n");
+	data[0] = width;
+	data[1] = height;
+	data[2] = left;
+	data[3] = top;
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		 IVTV_API_FB_SET_FRAMEBUFFER_WINDOW, &result, 4, &data[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_get_osd_coords(struct ivtv *itv,
+					     struct ivtv_osd_coords *osd)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_osd_coords\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_OSD_COORDS,
+		 &result, 0, &data[0]);
+
+	osd->offset = data[0] - video_rel_base;
+	osd->max_offset = video_size;
+	osd->pixel_stride = data[1];
+	osd->lines = data[2];
+	osd->x = data[3];
+	osd->y = data[4];
+
+	return result;
+}
+
+static inline int ivtv_api_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords
+					     *osd)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_osd_coords\n");
+	data[0] = osd->offset;
+	data[1] = osd->pixel_stride;
+	data[2] = osd->lines;
+	data[3] = osd->x;
+	data[4] = osd->y;
+
+	// FIXME maybe wait on vsync?
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_SET_OSD_COORDS,
+		 &result, 5, &data[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_get_screen_coords(struct ivtv *itv,
+						struct rectangle *r)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_screen_coords\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_SCREEN_COORDS,
+		 &result, 0, &data[0]);
+
+	r->x0 = data[0];
+	r->y0 = data[1];
+	r->x1 = data[2];
+	r->y1 = data[3];
+
+	return result;
+}
+
+static inline int ivtv_api_fb_set_screen_coords(struct ivtv *itv,
+						const struct rectangle *r)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_screen_coords\n");
+	data[0] = r->x0;
+	data[1] = r->y0;
+	data[2] = r->x1;
+	data[3] = r->y1;
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		 IVTV_API_FB_SET_SCREEN_COORDS, &result, 4, &data[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_get_global_alpha(struct ivtv *itv)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_global_alpha\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_GLOBAL_ALPHA,
+		 &result, 0, &data[0]);
+	return data[1];
+}
+
+static inline int ivtv_api_fb_set_global_alpha(struct ivtv *itv,
+					       int enable_global,
+					       int alpha, int enable_local)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_global_alpha\n");
+	data[0] = enable_global;
+	data[1] = alpha;
+	data[2] = !enable_local;
+	ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		 IVTV_API_FB_SET_GLOBAL_ALPHA, &result, 3, &data[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_get_flicker_state(struct ivtv *itv)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_flicker_state\n");
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_FLICKER_STATE,
+		 &result, 0, &data[0]);
+	return data[0];
+}
+
+static inline int ivtv_api_fb_set_flicker_state(struct ivtv *itv, int enabled)
+{
+	u32 params[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_flicker_state\n");
+	params[0] = enabled;
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem,
+		 IVTV_API_FB_SET_FLICKER_STATE, &result, 1, &params[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_blt_fill(struct ivtv *itv, int rasterop,
+				       int alpha_mode, int alpha_mask_mode,
+				       int width, int height, int destmask,
+				       u32 destaddr, int deststride, u32 value)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_blt_fill\n");
+	data[0] = rasterop;
+	data[1] = alpha_mode;
+	data[2] = alpha_mask_mode;
+	data[3] = width;
+	data[4] = height;
+	data[5] = destmask;
+	data[6] = destaddr;
+	data[7] = deststride;
+	data[8] = value;
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_BLT_FILL, &result,
+		 9, &data[0]);
+	return result;
+}
+
+static inline int ivtv_api_fb_blt_copy(struct ivtv *itv, int rasterop,
+				       int alpha_mode, int alpha_mask_mode,
+				       int width, int height, int destmask,
+				       u32 destaddr, int deststride,
+				       int sourcestride, int sourceaddr)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO,
+		      "ivtv_api_fb_blt_copy: width = %d, height = %d, destaddr = %d, deststride = %d, sourcestride = %d, sourceaddr = %d\n",
+		      width, height, destaddr, deststride, sourcestride,
+		      sourceaddr);
+
+	data[0] = rasterop;
+	data[1] = alpha_mode;
+	data[2] = alpha_mask_mode;
+	data[3] = width;
+	data[4] = height;
+	data[5] = destmask;
+	data[6] = destaddr;
+	data[7] = deststride;
+	data[8] = sourcestride;
+	data[9] = sourceaddr;
+
+	ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_BLT_COPY, &result,
+		 10, &data[0]);
+	return result;
+}
+
+MODULE_PARM(ivtv_fb_card_id, "i");
+MODULE_PARM_DESC(ivtv_fb_card_id,
+		 "ID number of ivtv card to use as framebuffer device (0-2)");
+
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------- */
+
+static struct fb_var_screeninfo ivtvfb_defined = {
+	0, 0, 0, 0,		/* W,H, W, H (virtual) load xres,xres_virtual */
+	0, 0,			/* virtual -> visible no offset */
+	32,			/* depth -> load bits_per_pixel */
+	0,			/* greyscale ? */
+	{0, 0, 0},		/* R */
+	{0, 0, 0},		/* G */
+	{0, 0, 0},		/* B */
+	{0, 0, 0},		/* transparency */
+	0,			/* standard pixel format */
+	FB_ACTIVATE_NOW,
+	-1, -1,
+	0,
+	0L, 0L, 0L, 0L, 0L,
+	0L, 0L, 0,		/* No sync info */
+	FB_VMODE_NONINTERLACED,
+	0,
+	{0, 0, 0, 0, 0}
+};
+
+static struct fb_info fb_info;
+
+#ifdef CONFIG_MTRR
+static int mtrr = 1;		//++MTY
+static unsigned long fb_start_aligned_physaddr;	/* video_base rounded down as required by hardware MTRRs */
+static unsigned long fb_end_aligned_physaddr;	/* video_base rounded up as required by hardware MTRRs */
+#endif
+
+/* --------------------------------------------------------------------- */
+static int _ivtvfb_set_var(struct fb_var_screeninfo *var)
+{
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "_ivtvfb_set_var\n");
+
+	if (var->xres != ivtvfb_defined.xres ||
+	    var->yres != ivtvfb_defined.yres ||
+	    var->xres_virtual != ivtvfb_defined.xres_virtual ||
+	    var->yres_virtual > video_height_virtual ||
+	    var->yres_virtual < video_height ||
+	    var->xoffset ||
+	    var->bits_per_pixel != ivtvfb_defined.bits_per_pixel ||
+	    var->nonstd) {
+		return -EINVAL;
+	}
+	return 0;
+
+}
+static int _ivtvfb_get_fix(struct fb_fix_screeninfo *fix)
+{
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	strcpy(fix->id, "iTVC15 TV out");
+	fix->smem_start = video_base;
+	fix->smem_len = video_size;
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->visual = FB_VISUAL_TRUECOLOR;
+	fix->xpanstep = 0;
+	fix->ypanstep = 0;
+	fix->ywrapstep = 0;
+	fix->line_length = video_linelength;
+	return 0;
+}
+
+static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_fb_check_var\n");
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_set_var\n");
+	return (_ivtvfb_set_var(&info->var));
+}
+static int ivtvfb_set_par(struct fb_info *info)
+{
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_set_var\n");
+	return (_ivtvfb_set_var(&info->var));
+}
+static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			    unsigned blue, unsigned transp,
+			    struct fb_info *info)
+{
+	return (0);
+}
+
+static int ivtv_fb_blt_copy(struct ivtv *itv, int x, int y, int width,
+			    int height, int source_offset, int source_stride)
+{
+	int rc;
+	unsigned long destaddr = ((y * video_width) + x) * 4;
+
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_fb_blt_copy\n");
+	source_offset += shadow_framebuf_offset;
+
+	rc = ivtv_api_fb_blt_copy(ivtv_fb, 0xa, 0x1, 0x0, width, height,
+				  0xffffffff, destaddr, video_width,
+				  source_stride, source_offset);
+	return rc;
+}
+
+#if 0				/* looks like this is unused. */
+/*
+ * Returns the physical location of the PTE associated with a given virtual address.
+ */
+static inline pte_t *virt_to_pte(struct mm_struct *mm, void *addr)
+{
+	return
+	    pte_offset(pmd_offset
+		       (pgd_offset(mm, (unsigned long)addr),
+			(unsigned long)addr), (unsigned long)addr);
+}
+#endif
+
+struct ivtvfb_user_dma_to_device ivtvfb_current_fb_dma;
+
+// 4MB max buffer size (on IA32 at least: 1024 pages x 4KB/page = 4MB):
+#define IVTV_MAX_FB_DMA_PAGES 1024
+
+int ivtvfb_alloc_user_dma_to_device(struct ivtvfb_user_dma_to_device *dma)
+{
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_alloc_user_dma_to_device\n");
+	dma->page_count = 0;
+	dma->sglist =
+	    kmalloc(sizeof(struct ivtv_SG_element) * IVTV_MAX_FB_DMA_PAGES,
+		    GFP_KERNEL);
+	if (!dma->sglist) {
+		printk(KERN_ERR
+		       "ivtvfb: cannot allocate scatter/gather list for %d pages\n",
+		       IVTV_MAX_FB_DMA_PAGES);
+		return -ENOMEM;
+	}
+
+	dma->map =
+	    kmalloc(sizeof(struct page *) * IVTV_MAX_FB_DMA_PAGES, GFP_KERNEL);
+	if (!dma->map) {
+		IVTV_DEBUG_FB(IVTV_DEBUG_ERR, "can't alloc dma page array\n");
+		kfree(dma->sglist);
+		return -ENOMEM;
+	}
+
+	dma->sg_dma_handle =
+	    pci_map_single(ivtv_fb->dev, (void *)dma->sglist,
+			   (sizeof(struct ivtv_SG_element) *
+			    IVTV_MAX_FB_DMA_PAGES), PCI_DMA_TODEVICE);
+
+	return 0;
+}
+
+//++MTY This is pretty fast - fast enough to do around 30+ frames per second at NTSC 720x480x4 or 27 frames per second at PAL 720x576x4
+int ivtvfb_prep_user_dma_to_device(struct ivtvfb_user_dma_to_device *dma,
+				   unsigned long ivtv_dest_addr,
+				   char *userbuf, int size_in_bytes)
+{
+	int i, offset;
+	unsigned long uaddr;
+	int size_in_pages = (size_in_bytes + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+	IVTV_DEBUG_FB(IVTV_DEBUG_DMA,
+		      "ivtvfb_prep_user_dma_to_device, dst: 0x%08x\n",
+		      (unsigned int)ivtv_dest_addr);
+
+	uaddr = ((unsigned long)userbuf & PAGE_MASK);
+	offset = uaddr & ~PAGE_MASK;
+
+	down_read(&current->mm->mmap_sem);
+	size_in_pages =
+	    get_user_pages(current, current->mm, uaddr, size_in_pages, 0, 0,
+			   dma->map, NULL);
+	up_read(&current->mm->mmap_sem);
+
+	if (size_in_pages < 0) {
+		IVTV_DEBUG_FB(IVTV_DEBUG_ERR, "failed to map user pages\n");
+		return size_in_pages;
+	}
+
+	dma->page_count = size_in_pages;
+	for (i = 0; i < size_in_pages; i++) {
+		dma->sglist[i].size = PAGE_SIZE;
+		dma->sglist[i].src =
+		    pci_map_page(ivtv_fb->dev, dma->map[i], 0, offset,
+				 PCI_DMA_TODEVICE);
+		dma->sglist[i].dst = ivtv_dest_addr + i * PAGE_SIZE + offset;
+		offset = 0;
+	}
+
+	// Indicate the last element to the hardware, so we get an interrupt on completion...
+	dma->sglist[size_in_pages - 1].size |= 0x80000000;
+
+#ifdef IVTVFB_DEBUG_PER_FRAME
+	printk(KERN_INFO
+	       "ivtvfb: Allocated scatter/gather list of %d bytes (%d pages) at kva 0x%08x = physaddr 0x%08x:\n",
+	       size_in_bytes, size_in_pages, dma->sglist, dma->sg_dma_handle);
+	for (i = 0; i < size_in_pages; i++) {
+		printk(KERN_INFO
+		       "ivtvfb:   [%d] src 0x%08x -> dest 0x%08x, size 0x%08x bytes\n",
+		       i, dma->sglist[i].src, dma->sglist[i].dst,
+		       dma->sglist[i].size);
+	}
+#endif
+
+	return 0;
+}
+
+int ivtvfb_free_user_dma_to_device(struct ivtvfb_user_dma_to_device *dma)
+{
+	int i;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_free_user_dma_to_device\n");
+
+	for (i = 0; i < dma->page_count; i++) {
+		pci_unmap_page(ivtv_fb->dev, dma->sglist[i].src, PAGE_SIZE,
+			       PCI_DMA_TODEVICE);
+		page_cache_release(dma->map[i]);
+	}
+	kfree(dma->sglist);
+	kfree(dma->map);
+	dma->page_count = 0;
+	return 0;
+}
+
+int ivtvfb_execute_user_dma_to_device(struct ivtvfb_user_dma_to_device
+				      *dma)
+{
+	u32 data[IVTV_MBOX_MAX_DATA], result;
+	int rc;
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_execute_user_dma_to_device\n");
+
+	data[0] = dma->sg_dma_handle;
+	data[1] = dma->page_count;
+	data[2] = 0x1;		// 0x1 = OSD data
+
+	IVTV_DEBUG_FB(IVTV_DEBUG_DMA,
+		      "Schedule FB DMA: physical address 0x%08x, "
+		      "arraysize 0x%08x, type 0x%08x\n", data[0], data[1],
+		      data[2]);
+
+	// Enable DMA complete interrupt:
+	ivtv_clear_irq_mask(ivtv_fb, IVTV_IRQ_DEC_DMA_COMPLETE);
+
+	set_bit(IVTV_F_I_OSD_DMA, &ivtv_fb->i_flags);
+
+	rc = ivtv_api(ivtv_fb->dec_mbox, &ivtv_fb->dec_msem,
+		      IVTV_API_DEC_DMA_FROM_HOST, &result, 3, &data[0]);
+
+	if (rc) {
+		IVTV_DEBUG_FB(IVTV_DEBUG_ERR, "error sending DMA info\n");
+		clear_bit(IVTV_F_I_BUSY, &ivtv_fb->i_flags);
+	}
+
+	IVTV_DEBUG_FB(IVTV_DEBUG_DMA, "OK, scheduled FB DMA!");
+	return 0;
+}
+
+static inline int ivtv_fb_prep_frame(struct ivtv *itv,
+				     unsigned long destaddr, void *srcaddr,
+				     int count)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int rc;
+
+	//if (!srcaddr || verify_area(...)) ...
+	if ((destaddr + count) > video_size)
+		return -E2BIG;
+
+	rc = 0;
+	add_wait_queue(&ivtv_fb->dec_master_w, &wait);
+	do {
+		set_current_state(TASK_INTERRUPTIBLE);
+		/* FIXME mini-race still .. need to port to 'stream' format */
+		if (!test_and_set_bit(IVTV_F_I_BUSY, &ivtv_fb->i_flags))
+			break;
+
+		schedule();
+
+		if (signal_pending(current))
+			rc = -ERESTARTSYS;
+	} while (!rc);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&ivtv_fb->dec_master_w, &wait);
+
+	if (rc)
+		goto out_dma_lock;
+
+	destaddr = IVTV_DEC_MEM_START + video_rel_base + destaddr;
+
+	if (0 !=
+	    (rc =
+	     ivtvfb_prep_user_dma_to_device(&ivtvfb_current_fb_dma, destaddr,
+					    (char *)srcaddr, count))) {
+		IVTV_DEBUG_FB(IVTV_DEBUG_DMA,
+			      "err prep user dma to device=%x\n", rc);
+		goto out_dma_lock;
+	}
+	if (0 !=
+	    (rc = ivtvfb_execute_user_dma_to_device(&ivtvfb_current_fb_dma))) {
+		IVTV_DEBUG_FB(IVTV_DEBUG_DMA,
+			      "err exec user dma to device=%x\n", rc);
+		goto out_dma_lock;
+	}
+
+	return 0;
+      out_dma_lock:
+	clear_bit(IVTV_F_I_BUSY, &ivtv_fb->i_flags);
+	wake_up(&ivtv_fb->dec_master_w);
+	return rc;
+}
+
+int ivtv_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		  unsigned long arg, struct fb_info *info)
+{
+
+	int rc;
+
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_fb_ioctl\n");
+	switch (cmd) {
+	case IVTVFB_IOCTL_GET_STATE:{
+			struct ivtvfb_ioctl_state_info state;
+			state.status = (ivtv_api_fb_get_state(ivtv_fb) & 0x7);
+			state.status |=
+			    (ivtv_api_fb_get_flicker_state(ivtv_fb) << 3);
+			state.alpha = ivtv_api_fb_get_global_alpha(ivtv_fb);
+			IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL,
+				      "IVTVFB_IOCTL_GET_STATE: status = %lu, alpha = %lu\n",
+				      state.status, state.alpha);
+			if (copy_to_user((void *)arg, &state, sizeof(state)))
+				return -EFAULT;
+			return 0;
+		}
+	case IVTVFB_IOCTL_SET_STATE:{
+			struct ivtvfb_ioctl_state_info state;
+			if (copy_from_user(&state, (void *)arg, sizeof(state)))
+				return -EFAULT;
+			IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL,
+				      "IVTVFB_IOCTL_SET_STATE: status = %lu, alpha = %lu\n",
+				      state.status, state.alpha);
+			ivtv_api_fb_set_state(ivtv_fb,
+					      (state.status
+					       && IVTVFB_STATUS_ENABLED));
+			ivtv_api_fb_set_global_alpha(ivtv_fb,
+						     (state.
+						      status &
+						      IVTVFB_STATUS_GLOBAL_ALPHA)
+						     ? 1 : 0, state.alpha,
+						     (state.
+						      status &
+						      IVTVFB_STATUS_LOCAL_ALPHA)
+						     ? 1 : 0);
+			ivtv_api_fb_set_flicker_state(ivtv_fb,
+						      (state.
+						       status &
+						       IVTVFB_STATUS_FLICKER_REDUCTION)
+						      ? 1 : 0);
+			IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL, "new state = %d\n",
+				      ivtv_api_fb_get_state(ivtv_fb));
+			IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL,
+				      "global alpha now = %d\n",
+				      ivtv_api_fb_get_global_alpha(ivtv_fb));
+			return 0;
+		}
+	case IVTVFB_IOCTL_PREP_FRAME:{
+			struct ivtvfb_ioctl_dma_host_to_ivtv_args args;
+			if (copy_from_user(&args, (void *)arg, sizeof(args)))
+				return -EFAULT;
+			return ivtv_fb_prep_frame(ivtv_fb, args.dest_offset,
+						  args.source, args.count);
+		}
+	case IVTVFB_IOCTL_BLT_COPY:{
+			struct ivtvfb_ioctl_blt_copy_args args;
+			if (copy_from_user(&args, (void *)arg, sizeof(args)))
+				return -EFAULT;
+
+			return ivtv_fb_blt_copy(ivtv_fb, args.x, args.y,
+						args.width, args.height,
+						args.source_stride,
+						args.source_offset);
+		}
+	case IVTVFB_IOCTL_GET_ACTIVE_BUFFER:{
+			struct ivtv_osd_coords bufinfo;
+			rc = ivtv_api_fb_get_osd_coords(ivtv_fb, &bufinfo);
+			return copy_to_user((void *)arg, &bufinfo,
+					    sizeof(bufinfo));
+		}
+	case IVTVFB_IOCTL_SET_ACTIVE_BUFFER:{
+			struct ivtv_osd_coords bufinfo;
+			if (copy_from_user
+			    (&bufinfo, (void *)arg, sizeof(bufinfo)))
+				return -EFAULT;
+			return ivtv_api_fb_set_osd_coords(ivtv_fb, &bufinfo);
+		}
+	case IVTVFB_IOCTL_GET_FRAME_BUFFER:{
+			struct ivtvfb_ioctl_get_frame_buffer getfb;
+			getfb.mem = (void *)video_vbase;
+			getfb.bytes = video_size;
+			getfb.sizex = video_width;
+			getfb.sizey = video_height;
+
+			return copy_to_user((void *)arg, &getfb, sizeof(getfb));
+		}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct fb_ops ivtvfb_ops = {
+      owner:THIS_MODULE,
+      fb_check_var:ivtvfb_check_var,
+      fb_set_par:ivtvfb_set_par,
+      fb_setcolreg:ivtvfb_setcolreg,
+      fb_ioctl:ivtv_fb_ioctl,
+      fb_pan_display:NULL,
+};
+
+int __init ivtvfb_init(void)
+{
+	int rc;
+	u32 fbbase;
+	u32 fblength;
+	struct ivtv_osd_coords osd;
+	struct rectangle rect;
+
+	if ((ivtv_fb_card_id < 0) || (ivtv_fb_card_id >= ivtv_cards_active)) {
+		printk(KERN_ERR
+		       "Error! ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: 0-%d)\n",
+		       ivtv_cards_active - 1);
+		return -1;
+	}
+
+	ivtv_fb = &ivtv_cards[ivtv_fb_card_id];
+	if (!ivtv_fb || (ivtv_fb->card_type != IVTV_350_V1)) {
+		printk(KERN_ERR
+		       "Error! ivtv-fb: Specified card (id %d) is either not present or does not support TV out (PVR350 only)\n",
+		       ivtv_fb_card_id);
+		return -1;
+	}
+
+	printk(KERN_INFO
+	       "ivtv-fb: Framebuffer module loaded (attached to ivtv card id %d)\n",
+	       ivtv_fb_card_id);
+
+	rc = ivtv_api_fb_set_pixel_format(ivtv_fb, 4);	// 4 = AlphaRGB 8:8:8:8
+
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "Current pixel format = %d\n",
+		      ivtv_api_fb_get_pixel_format(ivtv_fb));
+
+	rc = ivtv_api_fb_get_framebuffer(ivtv_fb, (void **)&fbbase, &fblength);
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO,
+		      "Framebuffer is at decoder-relative address 0x%08x and has %d bytes.\n",
+		      fbbase, fblength);
+
+	rc = ivtv_api_fb_get_osd_coords(ivtv_fb, &osd);
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO,
+		      "OSD: offset = 0x%08x (max offset = 0x%08x), pixel_stride = %d, lines = %d, x = %d, y = %d\n",
+		      (u32) osd.offset, (u32) osd.max_offset, osd.pixel_stride,
+		      osd.lines, osd.x, osd.y);
+
+	/* setup OSD and screen for PAL */
+	if (ivtv_pal) {
+		osd.lines = 576;
+		rc = ivtv_api_fb_set_osd_coords(ivtv_fb, &osd);
+		if (rc)
+			IVTV_DEBUG_FB(IVTV_DEBUG_ERR,
+				      "failed setting PAL osd\n");
+
+		rect.x0 = 0;
+		rect.x1 = 720;
+		rect.y0 = 0;
+		rect.y1 = 576;
+		rc = ivtv_api_fb_set_screen_coords(ivtv_fb, &rect);
+		if (rc)
+			IVTV_DEBUG_FB(IVTV_DEBUG_ERR,
+				      "failed setting PAL screen\n");
+	}
+
+	rc = ivtv_api_fb_get_screen_coords(ivtv_fb, &rect);
+	printk(KERN_INFO "ivtv-fb: screen coords: [%d %d] -> [%d %d]\n",
+	       rect.x0, rect.y0, rect.x1, rect.y1);
+
+	printk(KERN_INFO "ivtv-fb: original global alpha = %d\n",
+	       ivtv_api_fb_get_global_alpha(ivtv_fb));
+
+	/*
+	 * Normally a 32-bit RGBA framebuffer would be fine, however XFree86's fbdev 
+	 * driver doesn't understand the concept of alpha channel and always sets
+	 * bits 24-31 to zero when using a 24bpp-on-32bpp framebuffer device. We fix
+	 * this behavior by enabling the iTVC15's global alpha feature, which causes 
+	 * the chip to ignore the per-pixel alpha data and instead use one value (e.g.,
+	 * full brightness = 255) for the entire framebuffer. The local alpha is also
+	 * disabled in this step.
+	 *
+	 *++MTY Need to update http://ivtv.sourceforge.net/ivtv/firmware-api.html 
+	 *      call 0x4b: param[2] says 1 = enable local alpha, when in reality
+	 *      it means *disable* local alpha...
+	 *      
+	 */
+	ivtv_api_fb_set_global_alpha(ivtv_fb, 1, 255, 0);
+	printk(KERN_INFO "ivtv-fb: new global alpha = %d\n",
+	       ivtv_api_fb_get_global_alpha(ivtv_fb));
+
+	rc = ivtv_api_fb_set_state(ivtv_fb, 1);	// 1 = enabled
+	printk(KERN_INFO "ivtv-fb: current OSD state = %d\n",
+	       ivtv_api_fb_get_state(ivtv_fb));
+
+	video_rel_base = fbbase;
+	video_base = ivtv_fb->base_addr + IVTV_DEC_MEM_START + video_rel_base;
+	video_width = rect.x1 - rect.x0;
+	video_height = rect.y1 - rect.y0;
+	video_linelength = 4 * osd.pixel_stride;
+	video_size = fblength;
+
+	shadow_framebuf_size = (video_width * video_height * 4);
+	shadow_framebuf_offset = (video_size - shadow_framebuf_size) & ~3;
+
+	if (!request_mem_region(video_base, video_size, "ivtvfb")) {
+		printk(KERN_WARNING
+		       "ivtv-fb: warning: cannot reserve video memory at 0x%lx\n",
+		       video_base);
+		/* We cannot make this fatal. Sometimes this comes from magic spaces our resource handlers simply don't know about */
+	}
+
+	video_vbase = ioremap(video_base, video_size);
+	if (!video_vbase) {
+		release_mem_region(video_base, video_size);
+		printk(KERN_ERR
+		       "ivtv-fb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n",
+		       video_size, video_base);
+		return -EIO;
+	}
+
+	printk(KERN_INFO
+	       "ivtv-fb: framebuffer at 0x%lx, mapped to 0x%p, size %dk\n",
+	       video_base, video_vbase, video_size / 1024);
+	printk(KERN_INFO "ivtv-fb: mode is %dx%dx%d, linelength=%d\n",
+	       video_width, video_height, 32, video_linelength);
+
+	ivtvfb_defined.xres = video_width;
+	ivtvfb_defined.yres = video_height;
+	ivtvfb_defined.xres_virtual = video_width;
+	ivtvfb_defined.yres_virtual = video_height;
+	ivtvfb_defined.bits_per_pixel = 32;
+	video_height_virtual = ivtvfb_defined.yres_virtual;
+
+	/* some dummy values for timing to make fbset happy */
+	ivtvfb_defined.pixclock = 10000000 / video_width * 1000 / video_height;
+	ivtvfb_defined.left_margin = (video_width / 8) & 0xf8;
+	ivtvfb_defined.right_margin = 32;
+	ivtvfb_defined.upper_margin = 16;
+	ivtvfb_defined.lower_margin = 4;
+	ivtvfb_defined.hsync_len = (video_width / 8) & 0xf8;
+	ivtvfb_defined.vsync_len = 4;
+
+	ivtvfb_defined.red.offset = 0;
+	ivtvfb_defined.red.length = 8;
+	ivtvfb_defined.green.offset = 8;
+	ivtvfb_defined.green.length = 8;
+	ivtvfb_defined.blue.offset = 16;
+	ivtvfb_defined.blue.length = 8;
+	ivtvfb_defined.transp.offset = 24;
+	ivtvfb_defined.transp.length = 8;
+
+#ifdef CONFIG_MTRR
+	if (mtrr) {
+		/* Find the largest power of two that maps the whole buffer */
+		int size_shift = 31;
+		while (!(video_size & (1 << size_shift))) {
+			size_shift--;
+		}
+		size_shift++;
+
+		fb_start_aligned_physaddr =
+		    video_base & ~((1 << size_shift) - 1);
+		fb_end_aligned_physaddr =
+		    (video_base + (1 << size_shift) - 1) & ~((1 << size_shift) -
+							     1);
+		if (mtrr_add
+		    (fb_start_aligned_physaddr,
+		     (fb_end_aligned_physaddr - fb_start_aligned_physaddr),
+		     MTRR_TYPE_WRCOMB, 1) < 0) {
+			printk(KERN_WARNING
+			       "ivtv-fb: warning: mtrr_add() failed to add write combining region 0x%08x-0x%08x\n",
+			       (unsigned int)fb_start_aligned_physaddr,
+			       (unsigned int)fb_end_aligned_physaddr);
+		}
+	}
+#endif
+
+	fb_info.node = -1;
+	fb_info.flags = FBINFO_FLAG_DEFAULT;
+	fb_info.fbops = &ivtvfb_ops;
+
+	struct fb_fix_screeninfo fix;
+	_ivtvfb_get_fix(&fix);
+	fb_info.var = ivtvfb_defined;
+	fb_info.fix = fix;
+	fb_info.screen_base = video_vbase;
+	fb_info.fbops = &ivtvfb_ops;
+	fb_alloc_cmap(&fb_info.cmap, 0, 0);
+
+	if (register_framebuffer(&fb_info) < 0)
+		return -EINVAL;
+
+	ivtv_fb->fb_id = fb_info.node;
+
+	printk(KERN_INFO "fb%d: %s frame buffer device\n",
+	       ivtv_fb->fb_id, fix.id);
+
+	/* Set up DMA and BLT copy structures */
+	ivtvfb_alloc_user_dma_to_device(&ivtvfb_current_fb_dma);
+	ivtv_fb->user_dma_to_device_state = &ivtvfb_current_fb_dma;
+	return 0;
+}
+
+static void ivtvfb_cleanup(void)
+{
+	IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "Unloading framebuffer module\n");
+	unregister_framebuffer(&fb_info);
+	iounmap(video_vbase);
+#ifdef CONFIG_MTRR
+	mtrr_del(-1, fb_start_aligned_physaddr,
+		 (fb_end_aligned_physaddr - fb_start_aligned_physaddr));
+#endif
+	ivtv_fb->user_dma_to_device_state = NULL;
+	ivtvfb_free_user_dma_to_device(&ivtvfb_current_fb_dma);
+	ivtv_fb->fb_id = -1;
+	//release_mem_region(video_base, video_size);
+}
+
+module_init(ivtvfb_init);
+module_exit(ivtvfb_cleanup);
diff -upN /dev/null current/drivers/media/video/ivtv-i2c.c
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/ivtv-i2c.c	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,221 @@
+#include "ivtv.h"
+
+/* i2c implementation for iTVC15 chip, ivtv project.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.sourceforge.net/projects/ivtv/
+ */
+
+/* moved here from ivtv.h */
+static int writeregs(struct i2c_client *client, const unsigned char *regs);
+static int attach_inform(struct i2c_client *client);
+static int detach_inform(struct i2c_client *client);
+
+int writereg(struct i2c_client *client, unsigned char reg, unsigned char data)
+{
+	int ret;
+	unsigned char msg[] = { 0x1f, 0x00 };
+
+	printk("<1>writing reg 0x%02x, data 0x%02x\n", reg, data);
+
+	msg[0] = reg;
+	msg[1] = data;
+	ret = i2c_master_send(client, msg, 2);
+	if (ret != 2)
+		printk("writereg error\n");
+	return ret;
+}
+
+static int writeregs(struct i2c_client *client, const unsigned char *regs)
+{
+	unsigned char reg, data;
+
+	while (*regs != 0x00) {
+		reg = *(regs++);
+		data = *(regs++);
+		if (writereg(client, reg, data) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+static struct i2c_adapter ivtv_i2c_adapter_template = {
+	.name = "ivtv i2c driver",
+	.id = I2C_HW_B_BT848,	/*algo-bit is OR'd with this */
+	.algo = NULL,		/*set by i2c-algo-bit */
+	.algo_data = NULL,	/*filled from template */
+	.client_register = attach_inform,
+	.client_unregister = detach_inform,
+/* i2c-2.8.0 and later */
+	.owner = THIS_MODULE,
+	.class = I2C_ADAP_CLASS_TV_ANALOG,
+};
+
+static struct i2c_algo_bit_data ivtv_i2c_algo_template = {
+	NULL,			/*?? */
+	ivtv_setsda,		/*setsda function */
+	ivtv_setscl,		/*" */
+	ivtv_getsda,		/*" */
+	ivtv_getscl,		/*" */
+	5,			/*udelay or mdelay */
+	5,			/*whatever above isn't */
+	200			/*timeout */
+};
+
+void ivtv_setscl(void *data, int state)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	if (state)
+		itv->i2c_state |= 0x01;
+	else
+		itv->i2c_state &= ~0x01;
+
+	/* write them out */
+	/* write bits are inverted */
+	writel(~itv->i2c_state, (itv->reg_mem + IVTV_REG_I2C_SETSCL_OFFSET));
+}
+
+void ivtv_setsda(void *data, int state)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+
+	if (state)
+		itv->i2c_state |= 0x01;
+	else
+		itv->i2c_state &= ~0x01;
+
+	/* write them out */
+	/* write bits are inverted */
+	writel(~itv->i2c_state, (itv->reg_mem + IVTV_REG_I2C_SETSDA_OFFSET));
+}
+
+int ivtv_getscl(void *data)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+	return readb(itv->reg_mem + IVTV_REG_I2C_GETSCL_OFFSET);
+}
+
+int ivtv_getsda(void *data)
+{
+	struct ivtv *itv = (struct ivtv *)data;
+	return readb(itv->reg_mem + IVTV_REG_I2C_GETSDA_OFFSET);
+}
+
+static struct i2c_client ivtv_i2c_client_template = {
+	.name = "ivtv internal use only",
+	.id = -1,
+};
+
+static int attach_inform(struct i2c_client *client)
+{
+	struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+	int i;
+
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c client attach\n");
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		if (itv->i2c_clients[i] == NULL) {
+			itv->i2c_clients[i] = client;
+			break;
+		}
+	}
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c attach [client=%s,%s]\n",
+		   client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+
+	return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+	struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+	int i;
+
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c client detach\n");
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		if (itv->i2c_clients[i] == client) {
+			itv->i2c_clients[i] = NULL;
+			break;
+		}
+	}
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c detach [client=%s,%s]\n",
+		   client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+
+	return 0;
+}
+
+void ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd,
+			  void *arg)
+{
+	int i;
+
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "call_i2c_client\n");
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		if (NULL == itv->i2c_clients[i])
+			continue;
+		if (NULL == itv->i2c_clients[i]->driver->command)
+			continue;
+		if (addr == itv->i2c_clients[i]->addr) {
+			itv->i2c_clients[i]->driver->command(itv->
+							     i2c_clients[i],
+							     cmd, arg);
+			return;
+		}
+	}
+	IVTV_DEBUG(IVTV_DEBUG_ERR, "i2c client addr: 0x%02x not found!\n",
+		   addr);
+}
+
+int ivtv_i2c_direct(struct ivtv *itv, int addr, const unsigned char *regs)
+{
+	int i, ret = 0;
+
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c_direct\n");
+	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+		if (NULL == itv->i2c_clients[i])
+			continue;
+		if (addr == itv->i2c_clients[i]->addr) {
+			ret = writeregs(itv->i2c_clients[i], regs);
+			break;
+		}
+	}
+
+	if (ret) {
+		IVTV_DEBUG(IVTV_DEBUG_ERR, "error %d writing reg\n", ret);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv)
+{
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c init\n");
+	memcpy(&itv->i2c_adap, &ivtv_i2c_adapter_template,
+	       sizeof(struct i2c_adapter));
+	memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
+	       sizeof(struct i2c_algo_bit_data));
+	memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+	       sizeof(struct i2c_client));
+
+	sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name),
+		" #%d", itv->num);
+	itv->i2c_algo.data = itv;
+	i2c_set_adapdata(&itv->i2c_adap, itv);
+	itv->i2c_adap.algo_data = &itv->i2c_algo;
+	itv->i2c_client.adapter = &itv->i2c_adap;
+
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "setting scl and sda to 1\n");
+	ivtv_setscl(itv, 1);
+	ivtv_setsda(itv, 1);
+
+	itv->i2c_rc = i2c_bit_add_bus(&itv->i2c_adap);
+	return itv->i2c_rc;
+}
+
+void __devexit exit_ivtv_i2c(struct ivtv *itv)
+{
+	IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c exit\n");
+
+	i2c_bit_del_bus(&itv->i2c_adap);
+}
diff -upN /dev/null current/drivers/media/video/ivtv.h
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/ivtv.h	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,846 @@
+#ifndef IVTV_H
+#define IVTV_H
+
+/* Header for ivtv project:
+ * Driver for the iTVC15 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.sourceforge.net/projects/ivtv/
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/video_decoder.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/system.h>
+#include "msp3400.h"
+/* If you don't want to patch to v4l2, grab a copy of
+ * videodev2.h and put it in the same dir as this file */ 
+#ifndef HAVE_V4L2
+ #define HAVE_V4L2 1 
+ #include "videodev2.h"
+#endif
+#include <linux/i2c-algo-bit.h>
+#include <linux/byteorder/swab.h>
+#include <media/tuner.h>
+
+#define IVTV_ENCODER_OFFSET	0x00000000
+#define IVTV_ENCODER_SIZE	0x01000000
+
+#define IVTV_DECODER_OFFSET	0x01000000
+#define IVTV_DECODER_SIZE	0x01000000
+
+#define IVTV_ENCDEC_SIZE	(IVTV_ENCODER_SIZE + IVTV_DECODER_SIZE)
+
+#define IVTV_REG_OFFSET 	0x02000000
+#define IVTV_REG_SIZE		0x00010000
+
+#define IVTV_IOREMAP_SIZE	(IVTV_ENCDEC_SIZE + IVTV_REG_SIZE)
+
+#define IVTV_IOREMAP_ERROR	"ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h " \
+				"or disabling CONFIG_HIMEM4G into the kernel would help"
+
+/* General */
+#define IVTV_DRIVER_NAME "ivtv"
+#define IVTV_DRIVER_VERSION_MAJOR 0
+#define IVTV_DRIVER_VERSION_MINOR 1
+#define IVTV_DRIVER_VERSION_PATCHLEVEL 9
+#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
+#define IVTV_MAX_CARDS 9
+#define IVTV_DEFAULT_NUM_CARDS 1
+#define IVTV_MAX_YUV_BUFFERS 500
+#define IVTV_MIN_YUV_BUFFERS 40
+#define IVTV_DEFAULT_YUV_BUFFERS 60
+#define IVTV_MAX_MPG_BUFFERS 100
+#define IVTV_MIN_MPG_BUFFERS 15
+#define IVTV_DEFAULT_MPG_BUFFERS IVTV_MAX_MPG_BUFFERS
+#define IVTV_MAX_DEC_YUV_BUFFERS 500
+#define IVTV_MIN_DEC_YUV_BUFFERS 17
+#define IVTV_DEFAULT_DEC_YUV_BUFFERS 0
+#define IVTV_MAX_DEC_MPG_BUFFERS 100
+#define IVTV_MIN_DEC_MPG_BUFFERS 5
+#define IVTV_DEFAULT_DEC_MPG_BUFFERS 8
+#define IVTV_MAX_VBI_BUFFERS 100
+#define IVTV_MIN_VBI_BUFFERS 3
+#define IVTV_DEFAULT_VBI_BUFFERS 10
+#define IVTV_MIN_DEC_MPG_QLEN 0
+#define IVTV_DEFAULT_DEC_MPG_QLEN 2
+#define IVTV_MIN_DEC_YUV_QLEN 0
+#define IVTV_DEFAULT_DEC_YUV_QLEN 0
+#define IVTV_IOCTL_SET_DEBUG_LEVEL _IOWR('@', 98, int *)
+#define IVTV_IOCTL_GET_DEBUG_LEVEL _IOR('@', 99, int *)
+
+#define IVTV_PCI_ID_250_V1 0x4001 /* subsystem id */
+#define IVTV_PCI_ID_250_V2 0x4009
+#define IVTV_PCI_ID_250_V3 0x4801 /* treat like 250_V1 */
+#define IVTV_PCI_ID_250_V4 0x4803 /* treat like 250_V2 */
+#define IVTV_PCI_ID_350_V1 0x4000
+#define IVTV_PCI_ID_350_V2 0x4800 /* treat like 350_V1 */
+#define IVTV_250_V1 0 /* wintv pvr 250, encoder and decoder */
+#define IVTV_250_V2 1 /* pvr 250, encoder only */
+#define IVTV_350_V1 2 /* encoder, decoder, tv-out */
+#define IVTV_250_V1_STREAMS 3
+#define IVTV_250_V2_STREAMS 3
+#define IVTV_350_V1_STREAMS 5
+#define IVTV_V4L2_DEC_OFFSET 16 /* offset from 0 to register dec. v4l2 minors on */
+#define IVTV_V4L2_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */
+
+#define IVTV_ENC_STREAM_TYPE_MPG 0
+#define IVTV_ENC_STREAM_TYPE_YUV 1
+#define IVTV_ENC_STREAM_TYPE_VBI 2
+#define IVTV_DEC_STREAM_TYPE_MPG 3
+#define IVTV_DEC_STREAM_TYPE_YUV 4
+
+#define IVTV_ENC_MEM_START 0x00000000
+#define IVTV_DEC_MEM_START 0x01000000
+#define PCI_VENDOR_ID_ICOMP  0x4444
+#define PCI_DEVICE_ID_IVTV15 0x0803
+#define PCI_DEVICE_ID_IVTV16 0x0016
+#define IVTV_DEVNAME "ivtv: iTVC15/16 mpg2 encoder chip"
+#define IVTV_MBOX_MAX_BOXES 20
+#define IVTV_MBOX_API_BOXES 6
+#define IVTV_MBOX_DMA_START 6
+#define IVTV_MBOX_DMA_END 8
+#define IVTV_MBOX_MAX_DATA 16
+#define IVTV_MBOX_DMA 9
+#define IVTV_MBOX_FIELD_DISPLAYED 8
+#define IVTV_MBOX_SIZE 80
+#define IVTV_SAA7115_I2C_ADDR 0x21
+#define IVTV_TUNER_I2C_ADDR 0x61
+#define IVTV_MSP3400_I2C_ADDR 0x40
+#define IVTV_DMA_BUF_SIZE 34560 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */
+#define IVTV_DMA_DEC_BUF_SIZE 32768 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */
+//#define IVTV_DMA_BUF_SIZE 65536 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */
+//#define IVTV_DMA_DEC_BUF_SIZE 65536 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */
+
+#define IVTV_DMA_MAX_XFER 0x00080000 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */
+#define IVTV_DEC_MIN_BUF  0x00050000 /* want this many bytes+ in decoder buffer */
+#define IVTV_SLEEP_WAIT (HZ/10) /*100 ms*/
+#define IVTV_MAX_DATA_SLEEP 30
+#define DEC_DMA_TIMEOUT (15*HZ/100) /* used to be 100/15 */
+
+#define IVTV_DMA_ERR_LIST 0x00000008
+#define IVTV_DMA_ERR_WRITE 0x00000004
+#define IVTV_DMA_ERR_READ 0x00000002
+#define IVTV_DMA_SUCCESS 0x00000001
+#define IVTV_DMA_READ_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ)
+#define IVTV_DMA_WRITE_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE)
+#define IVTV_DMA_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ)
+
+/* video related */
+#define IVTV_MAX_INPUTS 9
+
+/*ioctl's*/
+#define IVTV_CTL_PRINTBOXES 0x00000001
+#define IVTV_CTL_CLEANUP 0x00000002
+#define IVTV_CTL_INIT_VIDCAP 0x00000003
+
+/* Registers */
+#define IVTV_REG_DMASTATUS (0x0004 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_IRQSTATUS (0x0040 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_IRQMASK (0x0048 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_VDM (0x2800 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_AO (0x2D00 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_BYTEFLUSH (0x2D24 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/)
+
+/* IRQ Masks */
+#define IVTV_IRQ_MASK_DEFAULT 0x00000404 /*stuff to ignore*/
+#define IVTV_IRQ_MASK_CAPTURE 0xFC000400 /*inverse mask, we want the high bits!*/
+#define IVTV_IRQ_MASK_DECODE 0x00FC0400
+
+#define IVTV_IRQ_ENC_START_CAP		(0x1 << 31)
+#define IVTV_IRQ_ENC_EOS		(0x1 << 30)
+#define IVTV_IRQ_ENC_VBI_CAP		(0x1 << 29)
+#define IVTV_IRQ_ENC_VIM_RST		(0x1 << 28)
+#define IVTV_IRQ_ENC_DMA_COMPLETE	(0x1 << 27)
+
+#define IVTV_IRQ_DEC_COPY_PROTECT	(0x1 << 25)
+#define IVTV_IRQ_DEC_AUD_MODE_CHG	(0x1 << 24)
+#define IVTV_IRQ_DEC_DATA_REQ		(0x1 << 22)
+#define IVTV_IRQ_DEC_IFRAME_DONE	(0x1 << 21)
+#define IVTV_IRQ_DEC_DMA_COMPLETE	(0x1 << 20)
+#define IVTV_IRQ_DEC_VBI_RE_INSERT	(0x1 << 19)
+#define IVTV_IRQ_DEC_DMA_ERR		(0x1 << 18)
+#define IVTV_IRQ_DEC_VSYNC		(0x1 << 10)
+
+#define IVTV_IRQ_DEBUG_KLUGE ( IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_ENC_VIM_RST | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DEC_COPY_PROTECT | IVTV_IRQ_DEC_AUD_MODE_CHG | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_IFRAME_DONE | IVTV_IRQ_DEC_DMA_COMPLETE | IVTV_IRQ_DEC_VBI_RE_INSERT | IVTV_IRQ_DEC_DMA_ERR )
+
+/* commands */
+#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
+#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
+#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB
+#define IVTV_CMD_VDM_STOP 0x00000000
+#define IVTV_CMD_AO_STOP 0x00000005
+#define IVTV_CMD_APU_PING 0x00000000
+#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE
+#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+#define IVTV_CMD_SPU_STOP 0x00000001
+#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A
+#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640
+#define IVTV_SDRAM_SLEEPTIME (60 * HZ / 100)	/* 600ms */
+
+/*Used for locating the firmware mailboxes*/
+#define IVTV_FIRM_ENC_FILENAME "/lib/modules/ivtv-fw-enc.bin" 
+#define IVTV_FIRM_DEC_FILENAME "/lib/modules/ivtv-fw-dec.bin"
+#define IVTV_FIRM_IMAGE_SIZE 256*1024
+#define IVTV_FIRM_SEARCH_ENCODER_START IVTV_ENCODER_OFFSET
+#define IVTV_FIRM_SEARCH_DECODER_START IVTV_DECODER_OFFSET
+#define IVTV_FIRM_SEARCH_ENCODER_END (IVTV_ENCODER_OFFSET + IVTV_ENCODER_SIZE - 1)
+#define IVTV_FIRM_SEARCH_DECODER_END (IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE - 1)
+#define IVTV_FIRM_SEARCH_STEP 0x00000100
+ 
+/* Firmware mailbox flags*/
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE 0x00000002
+#define IVTV_MBOX_IN_USE 0x00000001
+#define IVTV_MBOX_FREE 0x00000000
+
+/*Firmware API commands*/
+#define IVTV_API_ENC_GETVER 0x000000C4
+#define IVTV_API_DEC_GETVER 0x00000011
+#define IVTV_API_ENC_HALT_FW 0x000000C3
+#define IVTV_API_DEC_HALT_FW 0x0000000E
+#define IVTV_API_DEC_START_PLAYBACK 0x00000001
+#define IVTV_API_DEC_STOP_PLAYBACK 0x00000002
+#define IVTV_API_DEC_PLAYBACK_SPEED 0x00000003
+#define IVTV_API_DEC_STEP_VIDEO 0x00000005
+#define IVTV_API_DEC_PAUSE_PLAYBACK 0x0000000d
+#define IVTV_API_DEC_DMA_BLOCKSIZE 0x00000008
+#define IVTV_API_DEC_DMA_FROM_HOST 0x00000000b
+#define IVTV_API_DEC_DISP_STANDARD 0x00000010
+#define IVTV_API_DEC_STREAM_INPUT 0x00000014
+#define IVTV_API_DEC_TIMING_INFO 0x00000015
+#define IVTV_API_DEC_SELECT_AUDIO 0x00000016
+#define IVTV_API_DEC_EVENT_NOTIFICATION 0x00000017
+#define IVTV_API_DEC_DISPLAY_BUFFERS 0x00000018
+#define IVTV_API_DEC_DECODE_SOURCE 0x0000001a
+#define IVTV_API_DEC_AUDIO_OUTPUT 0x0000001b
+#define IVTV_API_DEC_SET_AV_DELAY 0x0000001c
+#define IVTV_API_DEC_BUFFER 0x0000001e
+#define IVTV_API_DEC_DMA_STATUS 0x0000000a
+#define IVTV_API_DEC_XFER_INFO 0x00000009
+#define IVTV_API_STD_TIMEOUT 0x00010000 /*units??*/
+#define IVTV_API_ASSIGN_DMA_BLOCKLEN 0x000000c9
+#define IVTV_API_ASSIGN_PGM_INDEX_INFO 0x000000c7
+#define IVTV_API_ASSIGN_STREAM_TYPE 0x000000b9
+#define IVTV_API_ASSIGN_OUTPUT_PORT 0x000000bb
+#define IVTV_API_ASSIGN_FRAMERATE 0x0000008f
+#define IVTV_API_ASSIGN_FRAME_SIZE 0x00000091
+#define IVTV_API_ASSIGN_ASPECT_RATIO 0x00000099
+#define IVTV_API_ASSIGN_BITRATES 0x00000095
+#define IVTV_API_ASSIGN_GOP_PROPERTIES 0x00000097
+#define IVTV_API_ASSIGN_3_2_PULLDOWN 0x000000b1
+#define IVTV_API_ASSIGN_GOP_CLOSURE 0x000000c5
+#define IVTV_API_ASSIGN_AUDIO_PROPERTIES 0x000000bd
+#define IVTV_API_ASSIGN_DNR_FILTER_MODE 0x0000009b
+#define IVTV_API_ASSIGN_DNR_FILTER_PROPS 0x0000009d
+#define IVTV_API_ASSIGN_CORING_LEVELS 0x0000009f
+#define IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE 0x000000a1
+#define IVTV_API_ASSIGN_FRAME_DROP_RATE 0x000000d0
+#define IVTV_API_ASSIGN_PLACEHOLDER 0x000000d8
+#define IVTV_API_INITIALIZE_INPUT 0x000000cd
+#define IVTV_API_ASSIGN_NUM_VSYNC_LINES 0x000000d6
+#define IVTV_API_BEGIN_CAPTURE 0x00000081
+#define IVTV_API_PAUSE_ENCODER 0x000000d2
+#define IVTV_API_EVENT_NOTIFICATION 0x000000d5
+#define IVTV_API_END_CAPTURE 0x00000082
+#define IVTV_API_SCHED_DMA_TO_HOST 0x000000cc
+#define IVTV_API_FB_GET_FRAMEBUFFER 0x00000041
+#define IVTV_API_FB_GET_PIXEL_FORMAT 0x00000042
+#define IVTV_API_FB_SET_PIXEL_FORMAT 0x00000043
+#define IVTV_API_FB_GET_STATE 0x00000044
+#define IVTV_API_FB_SET_STATE 0x00000045
+#define IVTV_API_FB_GET_OSD_COORDS 0x00000046
+#define IVTV_API_FB_SET_OSD_COORDS 0x00000047
+#define IVTV_API_FB_GET_SCREEN_COORDS 0x00000048
+#define IVTV_API_FB_SET_SCREEN_COORDS 0x00000049
+#define IVTV_API_FB_GET_GLOBAL_ALPHA 0x0000004a
+#define IVTV_API_FB_SET_GLOBAL_ALPHA 0x0000004b
+#define IVTV_API_FB_SET_BLEND_COORDS 0x0000004c
+// 0x4d unknown
+// 0x4e unknown
+#define IVTV_API_FB_GET_FLICKER_STATE 0x0000004f
+#define IVTV_API_FB_SET_FLICKER_STATE 0x00000050
+// 0x51 unknown
+#define IVTV_API_FB_BLT_COPY 0x00000052
+#define IVTV_API_FB_BLT_FILL 0x00000053
+#define IVTV_API_FB_BLT_TEXT 0x00000054
+// 0x55 unknown
+#define IVTV_API_FB_SET_FRAMEBUFFER_WINDOW 0x00000056
+// 0x57 - 0x5f unknown
+#define IVTV_API_FB_SET_CHROMA_KEY 0x00000060
+#define IVTV_API_FB_GET_ALPHA_CONTENT_INDEX 0x00000061
+#define IVTV_API_FB_SET_ALPHA_CONTENT_INDEX 0x00000062
+
+/* i2c stuff */
+#define I2C_CLIENTS_MAX 16
+#define I2C_TIMING (0x7<<4)
+#define IVTV_REG_I2C_SETSCL_OFFSET (0x7000 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_I2C_SETSDA_OFFSET (0x7004 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_I2C_GETSCL_OFFSET (0x7008 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_I2C_GETSDA_OFFSET (0x700c /*| IVTV_REG_OFFSET*/)
+
+/* debugging */
+#define IVTV_DEBUG_ERR   (1 << 0)
+#define IVTV_DEBUG_INFO  (1 << 1)
+#define IVTV_DEBUG_API   (1 << 2)
+#define IVTV_DEBUG_DMA   (1 << 3)
+#define IVTV_DEBUG_IOCTL (1 << 4)
+#define IVTV_DEBUG_I2C   (1 << 5)
+#define IVTV_DEBUG_IRQ   (1 << 6)
+#define IVTV_DEBUG(x,args...) if((x)&ivtv_debug) printk("ivtv: " args);
+#define IVTV_DEBUG_FB(x,args...) if((x)&ivtv_debug) printk("ivtv-fb: " args);
+
+/* Temp saa7115 hack FIXME */
+#define DECODER_SET_SIZE    76598
+#define	DECODER_GET_PICTURE 76599
+
+/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
+#define MPEG_FRAME_TYPE_IFRAME 1
+#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
+#define MPEG_FRAME_TYPE_ALL 7
+
+/* External API stuff */
+#define IVTV_IOC_FWAPI		0xFFEE7701 /*just some values i picked for now*/
+#define IVTV_IOC_ZCOUNT		0xFFEE7702
+#define IVTV_IOC_G_CODEC	0xFFEE7703
+#define IVTV_IOC_S_CODEC	0xFFEE7704
+
+/* allow direct access to the saa7115 registers for testing */
+#define SAA7115_GET_REG         0xFFEE7705
+#define SAA7115_SET_REG         0xFFEE7706
+
+
+#define DECODER_SET_AUDIO       0xFFEE7707
+#define DECODER_AUDIO_32_KHZ	0
+#define DECODER_AUDIO_441_KHZ	1
+#define DECODER_AUDIO_48_KHZ	2
+
+#define IVTV_IOC_PLAY     	0xFFEE7781
+#define IVTV_IOC_PAUSE     	0xFFEE7782
+#define IVTV_IOC_FRAMESYNC	0xFFEE7783
+#define IVTV_IOC_GET_TIMING	0xFFEE7784
+#define IVTV_IOC_S_SLOW_FAST	0xFFEE7785
+#define IVTV_IOC_S_START_DECODE 0xFFEE7786
+#define IVTV_IOC_S_STOP_DECODE  0xFFEE7787
+#define IVTV_IOC_S_OSD          0xFFEE7788
+#define IVTV_IOC_GET_FB		0xFFEE7789
+
+#define IVTV_IOC_START_DECODE	_IOW('@', 29, struct ivtv_cfg_start_decode)
+#define IVTV_IOC_STOP_DECODE	_IOW('@', 30, struct ivtv_cfg_stop_decode)
+#define IVTV_IOC_G_SPEED	_IOR('@', 31, struct ivtv_speed)
+#define IVTV_IOC_S_SPEED	_IOW('@', 32, struct ivtv_speed)
+#define IVTV_IOC_DEC_STEP	_IOW('@', 33, int)
+#define IVTV_IOC_DEC_FLUSH	_IOW('@', 34, int)
+
+/* Framebuffer external API */
+/* NOTE: These must *exactly* match the structures and constants in driver/ivtv.h */
+
+struct ivtvfb_ioctl_state_info {
+  unsigned long status;
+  unsigned long alpha;
+};
+
+struct ivtvfb_ioctl_blt_copy_args {
+  int x, y, width, height, source_offset, source_stride;
+};
+
+struct ivtvfb_ioctl_dma_host_to_ivtv_args {
+	void* source;
+	unsigned long dest_offset;
+	int count;
+};
+
+struct ivtvfb_ioctl_get_frame_buffer {
+	void* mem;
+	int   bytes;
+	int   sizex;
+	int   sizey;
+};
+
+struct ivtv_osd_coords {
+  unsigned long offset;
+  unsigned long max_offset;
+  int pixel_stride;
+  int lines;
+  int x;
+  int y;
+};
+
+struct rectangle {
+  int x0;
+  int y0;
+  int x1;
+  int y1;
+};
+
+#define IVTVFB_IOCTL_GET_STATE          _IOR('@', 1, struct ivtvfb_ioctl_state_info)
+#define IVTVFB_IOCTL_SET_STATE          _IOW('@', 2, struct ivtvfb_ioctl_state_info)
+#define IVTVFB_IOCTL_PREP_FRAME         _IOW('@', 3, struct ivtvfb_ioctl_dma_host_to_ivtv_args)
+#define IVTVFB_IOCTL_BLT_COPY           _IOW('@', 4, struct ivtvfb_ioctl_blt_copy_args)
+#define IVTVFB_IOCTL_GET_ACTIVE_BUFFER  _IOR('@', 5, struct ivtv_osd_coords)
+#define IVTVFB_IOCTL_SET_ACTIVE_BUFFER  _IOW('@', 6, struct ivtv_osd_coords)
+#define IVTVFB_IOCTL_GET_FRAME_BUFFER   _IOR('@', 7, struct ivtvfb_ioctl_get_frame_buffer)
+
+#define IVTVFB_STATUS_ENABLED           (1 << 0)
+#define IVTVFB_STATUS_GLOBAL_ALPHA      (1 << 1)
+#define IVTVFB_STATUS_LOCAL_ALPHA       (1 << 2)
+#define IVTVFB_STATUS_FLICKER_REDUCTION (1 << 3)
+
+/* Stream types */
+#define IVTV_STREAM_PS		0
+#define IVTV_STREAM_TS		1
+#define IVTV_STREAM_MPEG1	2
+#define IVTV_STREAM_PES_AV	3
+#define IVTV_STREAM_PES_V	5
+#define IVTV_STREAM_PES_A	7
+#define IVTV_STREAM_DVD		10
+#define IVTV_STREAM_VCD		11
+#define IVTV_STREAM_SVCD	12
+#define IVTV_STREAM_DVD_S1	13
+#define IVTV_STREAM_DVD_S2	14
+
+/* Custom v4l controls */
+#ifndef V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_PRIVATE_BASE			0x08000000
+#endif
+
+#define V4L2_CID_IVTV_FREQ	(V4L2_CID_PRIVATE_BASE)
+#define V4L2_CID_IVTV_ENC	(V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_IVTV_BITRATE	(V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_IVTV_MONO	(V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_IVTV_JOINT	(V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_IVTV_EMPHASIS	(V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_IVTV_CRC	(V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_IVTV_COPYRIGHT	(V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_IVTV_GEN	(V4L2_CID_PRIVATE_BASE + 8)
+
+#define IVTV_V4L2_AUDIO_MENUCOUNT 9 /* # of v4l controls */
+
+#define IVTV_DEC_PRIVATE_BASE	(V4L2_CID_PRIVATE_BASE + IVTV_V4L2_AUDIO_MENUCOUNT)
+
+#define V4L2_CID_IVTV_DEC_SMOOTH_FF	(IVTV_DEC_PRIVATE_BASE + 0)
+#define V4L2_CID_IVTV_DEC_FR_MASK	(IVTV_DEC_PRIVATE_BASE + 1)
+#define V4L2_CID_IVTV_DEC_SP_MUTE	(IVTV_DEC_PRIVATE_BASE + 2)
+#define V4L2_CID_IVTV_DEC_FR_FIELD	(IVTV_DEC_PRIVATE_BASE + 3)
+#define V4L2_CID_IVTV_DEC_AUD_SKIP	(IVTV_DEC_PRIVATE_BASE + 4)
+#define V4L2_CID_IVTV_DEC_NUM_BUFFERS   (IVTV_DEC_PRIVATE_BASE + 5)
+#define V4L2_CID_IVTV_DEC_PREBUFFER     (IVTV_DEC_PRIVATE_BASE + 6)
+
+#define IVTV_V4L2_DEC_MENUCOUNT 7
+
+#ifdef SAA7115_REGTEST
+/* allow direct access to the saa7115 registers for testing */
+#define SAA7115_GET_REG         0xFFEE7705
+#define SAA7115_SET_REG         0xFFEE7706
+
+struct saa7115_reg_t {
+	u8 reg;
+	u8 val;
+};
+#endif
+struct saa7114 {
+	int norm;
+	int input;
+	int enable;
+	int bright;
+	int contrast;
+	int hue;
+	int sat;
+	int playback;
+};
+
+
+struct ivtv_cfg_start_decode {
+	u32	gop_offset;		/*Frames in GOP to skip before starting */
+	u32	muted_audio_frames;	/* #of audio frames to mute */
+};
+
+struct ivtv_cfg_stop_decode {
+	int	hide_last; /* 1 = show black after stop, 0 = show last frame */
+	u64	pts_stop; /* PTS to stop at */
+};
+
+struct ivtv_speed {
+	int scale; 		/* 1-?? (50 for now) */
+	int smooth;		/* Smooth mode when in slow/fast mode */
+	int speed; 		/* 0 = slow, 1 = fast */
+	int direction; 		/* 0 = forward, 1 = reverse (not supportd */
+	int fr_mask;		/* 0 = I, 1 = I,P, 2 = I,P,B */
+	int b_per_gop;		/* frames per GOP (reverse only) */
+	int aud_mute;		/* Mute audio while in slow/fast mode */
+	int fr_field;		/* 1 = show every field, 0 = show every frame */
+	int mute;		/* # of audio frames to mute on playback resume */
+};
+
+struct ivtv_slow_fast {
+	int speed; /* 0 = slow, 1 = fast */
+	int scale; /* 1-?? (50 for now) */
+};
+
+struct ivtv_ioctl_fwapi {
+        u32 cmd;
+        u32 result;
+        int args;
+        u32 data[IVTV_MBOX_MAX_DATA];
+};
+
+struct ivtv_ioctl_framesync {
+  u32 frame;
+  u64 pts;
+  u64 scr;
+};
+
+struct ivtv_audio_meta {
+	        struct v4l2_queryctrl   *ctrl;
+	        struct v4l2_querymenu   *menu;
+	        u32                     *table;
+	        u32                     mask;
+	        s32                     setting;
+};
+
+/* For use with IVTV_IOC_G_CODEC and IVTV_IOC_S_CODEC */
+struct ivtv_ioctl_codec {
+	u32 aspect;
+	u32 audio_bitmap;
+	u32 bframes;
+	u32 bitrate_mode;
+	u32 bitrate;
+	u32 bitrate_peak;
+	u32 dnr_mode;
+	u32 dnr_spatial;
+	u32 dnr_temporal;
+	u32 dnr_type;
+	u32 framerate;
+	u32 framespergop;
+	u32 gop_closure;
+	u32 pulldown;
+	u32 stream_type;
+};
+
+extern int ivtv_debug;
+extern int ivtv_pal;
+
+/* Scatter-Gather array element, used in DMA transfers */
+struct ivtv_SG_element {
+	u32 src;
+	u32 dst;
+	u32 size;
+};
+
+/* ivtv-specific mailbox template */
+struct ivtv_mailbox {
+	unsigned long flags;
+	u32 cmd;
+	u32 retval;
+	u32 timeout;
+	u32 data[IVTV_MBOX_MAX_DATA];
+};
+
+struct ivtv_state {
+	unsigned long freq; /* Current tuned frequency */
+	int input; /* Current digitizer input */
+	u32 flags;/* tuner, audio */
+	u16 type; /* tv or camera */
+	u16 norm; /* Current video standard */
+	/* more to come! */
+};
+
+struct ivtv_buffer {
+	size_t			readpos;
+	dma_addr_t		dma_handle;
+	struct v4l2_buffer	buffer;
+        struct list_head        list;
+	unsigned long		ts;
+};
+
+struct ivtv_buffer_list {
+	struct video_device	*vdev; /* to get itv from */
+	int			elements;
+        struct list_head        list;
+};
+
+struct ivtv_options {
+	int yuv_fixup;		/* Should we re-work YUV to a standard format? */
+	int yuv_buffers;	/* How many yuv buffers to allocate? */
+	int mpg_buffers;	/* how many mpg buffers to allocate? */
+	int vbi_buffers;	/* how many vbi buffers to allocate? */
+	int dec_mpg_buffers;	/* how many decoder mpg buffers to allocate? */
+	int dec_yuv_buffers;	/* How many decoder yuv buffers to allocate? */
+	int dec_mpg_qlen;	/* how many decoder mpg buffers to queue? */
+	int dec_yuv_qlen;	/* how many decoder yuv buffers to queue? */
+	int num_devices;	/* how many cards to detect? */
+};
+
+struct ivtv_dec_options {
+	int hide_last_frame;	/* 0 = display last frame on stop_decode
+				 * 1 = display black */
+	u32 pts_low;		/* low bits PTS to stop playback at */
+	u32 pts_hi;		/* hi bits  PTS to stop playback at */
+	int gop_offset;		/* on start-playback, skip this
+				 *  # of frames in the GOP */
+	int mute_frames;	/* # of audio frames to mute on playback start */
+	int decbuffers;		/* 0 = 6 buffers, 1 = 9 buffers */
+	int prebuffer;		/* 0 = no prebuffer, 1 = enabled, see docs */
+	struct ivtv_speed	speed;
+};
+
+/* per-stream, s_flags */
+#define IVTV_F_S_DMAP		0
+#define IVTV_F_S_OVERFLOW	1
+#define IVTV_F_S_CAP		2
+#define IVTV_F_S_UNINIT		3
+
+/* per-ivtv, i_flags */
+#define IVTV_F_I_BUSY		0
+#define IVTV_F_I_NEEDS_DATA	1
+#define IVTV_F_I_EOS		2
+#define IVTV_F_I_OSD_DMA	3
+
+struct ivtv_v4l2_stream {
+	int			buf_size; /* size of buffers this stream */
+	long			id;
+	long			seq;
+	int			ubytes; /* bytes written back to user this frame */
+	unsigned long		s_flags;
+	int			v4l_reg_type;
+	wait_queue_head_t	waitq;
+	struct video_device	*v4l2dev;
+	struct v4l2_format	format;
+
+	// FIXME need to make sure no read() if streaming
+	struct ivtv_buffer_list free_q; /* unused buffers */
+	struct ivtv_buffer_list full_q; /* filled buffers */
+	struct ivtv_buffer_list dma_q;  /* awaiting dma to fill them */
+					/* only updated in interrupt time! */
+
+	int			controlcount; /* Number of elements in controls */
+	struct v4l2_control	*controls;
+};
+
+struct ivtv_v4l2_table {
+	int	count;
+	int	active;
+	union {
+		struct v4l2_input	*input;
+		struct v4l2_output	*output;
+		struct v4l2_audio	*audio;
+		struct v4l2_tuner	*tuner;
+		struct v4l2_control	*control;
+		struct v4l2_standard	*std;
+	} table;
+};
+		
+struct ivtv_v4l2 {
+	u32			capabilities;
+	struct ivtv_v4l2_table	input;
+	int			audio_output;
+	struct ivtv_v4l2_table	output;
+	struct ivtv_v4l2_table	audio;
+	struct ivtv_v4l2_table	tuner;
+	struct ivtv_v4l2_table	standard;
+	struct v4l2_capability	capability;
+	struct v4l2_frequency   freq;
+	int			streamcount; /* Number of elements in streams */
+	struct ivtv_v4l2_stream	*streams;
+
+        /* codec settings */
+        struct ivtv_ioctl_codec codec;
+	struct ivtv_audio_meta audio_meta[IVTV_V4L2_AUDIO_MENUCOUNT];
+
+	/* FIXME probably should get rid of this */
+	wait_queue_head_t	waitq;
+};
+
+struct ivtv_open_id {
+        int                     open_id;
+        int                     type;
+        struct ivtv             *itv;
+        struct list_head        list;
+};
+
+struct ivtvfb_user_dma_to_device {
+  	int page_count;
+	struct ivtv_SG_element* sglist;
+	struct page **map;
+	dma_addr_t sg_dma_handle;
+};
+
+/* Stuct to hold info about ivtv cards */
+struct ivtv {
+	int card_type; /* pvr 250 rev1, 250 rev2, 350 are options so far */
+	struct pci_dev *dev;
+	struct ivtv_options	options;
+	struct ivtv_dec_options	dec_options;
+	int num;                /* invalidate during init! */
+	int first_read;		/* used to clean up stream */
+	unsigned long i_flags;
+	atomic_t capturing;
+	atomic_t decoding;
+	struct semaphore sem_lock ____cacheline_aligned_in_smp;
+	spinlock_t lock ____cacheline_aligned_in_smp;
+
+	long	open_id; /* incremented each time an open occurs
+			    used as unique ID */
+
+	/* FIXME should use part of v4l2_performace instead */
+	unsigned long trans_id; 
+
+	struct tasklet_struct dma_sched_tq;
+
+	u32 enc_fw_ver, dec_fw_ver, base_addr; /*is base_addr needed? */
+	u32			irqmask;
+
+       	struct ivtv_mailbox *enc_mbox, *dec_mbox;
+	struct semaphore enc_msem ____cacheline_aligned_in_smp;
+	struct semaphore dec_msem ____cacheline_aligned_in_smp;
+
+	unsigned char card_rev, *io_mem, *reg_mem;
+
+	wait_queue_head_t cap_w, vsync_w;
+
+	/*FIXME perhaps move these to the v4l2_stream struct */
+	struct ivtv_SG_element *SGarray, *DSGarray;
+	dma_addr_t SG_handle, DSG_handle;
+
+	/* Decoder */
+	struct ivtv_ioctl_framesync dec_timestamp;
+	wait_queue_head_t dec_master_w;
+	struct timer_list dec_timeout;
+
+	/* Framebuffer DMA support */
+	struct ivtvfb_user_dma_to_device* user_dma_to_device_state;
+	int	fb_id;
+
+	/* i2c */
+	struct i2c_adapter         i2c_adap;
+	struct i2c_algo_bit_data   i2c_algo;
+	struct i2c_client          i2c_client;
+	int                        i2c_state, i2c_rc, i2c_command;
+	struct i2c_client         *i2c_clients[I2C_CLIENTS_MAX];
+
+	/* v4l2 and User settings*/
+	struct ivtv_state	state;
+	struct ivtv_v4l2	v4l2;
+	struct list_head	client_list;
+};
+
+/* Globals */
+extern struct ivtv ivtv_cards[];
+extern int ivtv_cards_active;
+extern int dec_yuv_buffers;
+extern int dec_mpg_buffers;
+extern int yuv_buffers;
+extern int mpg_buffers;
+extern int vbi_buffers;
+extern spinlock_t ivtv_lock;
+
+/*==============Prototypes==================*/
+/* FIXME some of these proably need fine-tuning 
+ *  to avoid warnings
+ */
+
+void ivtv_setscl(void *data, int state);
+void ivtv_setsda(void *data, int state);
+int ivtv_getscl(void *data);
+int ivtv_getsda(void *data);
+
+void ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
+int ivtv_i2c_direct(struct ivtv *itv, int addr, const unsigned char *regs);
+
+void ivtv_inc(struct i2c_adapter *adapter);
+void ivtv_dec(struct i2c_adapter *adapter);
+
+/* init + register i2c algo-bit adapter */
+int __devinit init_ivtv_i2c(struct ivtv *itv);
+void __devexit exit_ivtv_i2c(struct ivtv *itv);
+
+/* end i2c stuff */
+
+/* Initialization stuff */
+int  ivtv_firmware_copy(struct ivtv *itv);
+
+/* Unload stuff */
+void ivtv_v4l2_cleanup(struct ivtv *itv);
+int  ivtv_stop_firmware(struct ivtv *itv);
+void ivtv_zero_usage_count(void);
+void ivtv_flush_queues(struct ivtv_open_id *id);
+
+/* API Related */
+int ivtv_find_firmware_mailbox(struct ivtv *itv);
+int ivtv_get_free_mailbox(struct ivtv_mailbox *mbox);
+int ivtv_api_call(struct ivtv_mailbox *mbox, u32 cmd,  struct semaphore *sem,
+	       	int elements, u32 data[]);
+int ivtv_api_getresult_nosleep(struct ivtv_mailbox *mbox, u32 *result, u32 data[]);
+int ivtv_api_getresult(struct ivtv_mailbox *mbox, struct semaphore *sem,
+		u32 *result, u32 data[]);
+int ivtv_api(struct ivtv_mailbox *mbox, struct semaphore *sem, int cmd,
+		u32 *result, int args, u32 data[]);
+extern int __ivtv_api(struct ivtv_mailbox *mbox, int cmd,
+		u32 *result, int args, u32 data[]);
+int ivtv_v4l2_setup(struct ivtv *itv);
+
+/* Capture related */
+int ivtv_stop_decode(struct ivtv_open_id *id);
+int ivtv_stop_all_captures(struct ivtv *itv);
+int ivtv_stop_capture(struct ivtv_open_id *id);
+long ivtv_read(struct ivtv_open_id *id, char *ubuf, size_t count, int block);
+int ivtv_get_timing_info(struct ivtv *itv, struct ivtv_ioctl_framesync *info);
+ssize_t ivtv_write(struct ivtv_open_id *id, const char *buf, size_t count,
+                   int block);
+unsigned int ivtv_poll(struct file *filp, poll_table *wait);
+unsigned int ivtv_dec_poll(struct file *filp, poll_table *wait);
+	/* makes a queue complete with 'length' items */
+	/* NOTE: This returns the # of buffers allocated */
+extern int ivtv_init_queue(struct ivtv *itv,struct ivtv_buffer_list *queue,
+			   int length, enum v4l2_buf_type type);
+	/* moves all items in queue 'src' to queue 'dst' */
+extern int ivtv_move_queue(struct ivtv *itv, struct ivtv_buffer_list *src,
+			   struct ivtv_buffer_list *dst);
+
+/* Hardware/IRQ */
+extern void ivtv_set_irq_mask(struct ivtv *itv, unsigned long mask);
+extern void ivtv_clear_irq_mask(struct ivtv *itv, unsigned long mask);
+extern void ivtv_sleep_timeout(int timeout);
+
+/* Testing/Debugging */
+extern int ivtv_close(struct ivtv_open_id *id);
+
+/* debug stuff, to get the locking right */
+#ifndef WARN_ON
+#define WARN_ON(condition) do { \
+	if (unlikely((condition)!=0)) { \
+		printk("Badness in %s at %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \
+		dump_stack(); \
+	} \
+} while (0)
+#endif
+
+#define IVTV_ASSERT(x)	WARN_ON(!(x))
+
+static inline int ivtv_sem_count(struct semaphore *sem)
+{
+	return atomic_read(&sem->count);
+}
+
+#endif
diff -upN reference/drivers/media/video/msp3400.c current/drivers/media/video/msp3400.c
--- reference/drivers/media/video/msp3400.c	2004-02-18 14:56:58.000000000 -0800
+++ current/drivers/media/video/msp3400.c	2004-03-30 21:42:12.000000000 -0800
@@ -58,6 +58,7 @@ static int debug    = 0;    /* debug out
 static int once     = 0;    /* no continous stereo monitoring */
 static int amsound  = 0;    /* hard-wire AM sound at 6.5 Hz (france),
 			      the autoscan seems work well only with FM... */
+static int standard = 1;   /* Override auto detect of audio standard, if needed. */
 static int simple   = -1;   /* use short programming (>= msp3410 only) */
 static int dolby    = 0;
 
@@ -113,6 +114,7 @@ struct msp3400c {
 MODULE_PARM(once,"i");
 MODULE_PARM(debug,"i");
 MODULE_PARM(simple,"i");
+MODULE_PARM(standard,"i");
 MODULE_PARM(amsound,"i");
 MODULE_PARM(dolby,"i");
 
@@ -384,7 +386,7 @@ static void msp3400c_setvolume(struct i2
 
 	if (!muted) {
 		vol     = (left > right) ? left : right;
-		val     = (vol * 0x73 / 65535) << 8;
+		val     = (vol * 0x7F / 65535) << 8;
 	}
 	if (vol > 0) {
 		balance = ((right-left) * 127) / vol;
@@ -395,8 +397,11 @@ static void msp3400c_setvolume(struct i2
 		muted ? "on" : "off", left, right, val>>8, balance);
 	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */
 	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones  */
-	/* scart - on/off only */
-	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0);
+	// scart - on/off only - AEW why? undone NOTE values below
+	// 40000 are mostly useless, 59343 is a good default (0x73)
+	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007,
+			muted ? 0x1 : (val | 0x1));
+
 	msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8);
 }
 
@@ -473,6 +478,20 @@ static void msp3400c_setmode(struct i2c_
 	}
 }
 
+// given a bitmask of VIDEO_SOUND_XXX returns the "best" in the bitmask
+static int best_video_sound(int mode) {
+	int ret_cap = VIDEO_SOUND_MONO;
+	if (mode & VIDEO_SOUND_STEREO) {
+		ret_cap = VIDEO_SOUND_STEREO;
+	} else if (mode & VIDEO_SOUND_LANG1) {
+		ret_cap = VIDEO_SOUND_LANG1;
+	} else if (mode & VIDEO_SOUND_LANG2) {
+		ret_cap = VIDEO_SOUND_LANG2;
+	}
+	return ret_cap;
+}
+
+
 /* turn on/off nicam + stereo */
 static void msp3400c_setstereo(struct i2c_client *client, int mode)
 {
@@ -547,7 +566,7 @@ static void msp3400c_setstereo(struct i2
 	}
 
 	/* switch audio */
-	switch (mode) {
+	switch (best_video_sound(mode)) {
 	case VIDEO_SOUND_STEREO:
 		src = 0x0020 | nicam;
 #if 0 
@@ -1056,7 +1075,7 @@ static int msp3410d_thread(void *data)
 		switch (msp->norm) {
 		case VIDEO_MODE_PAL:
 			mode = 0x1003;
-			std  = 1;
+			std  = standard;
 			break;
 		case VIDEO_MODE_NTSC:  /* BTSC */
 			mode = 0x2003;
@@ -1064,15 +1083,19 @@ static int msp3410d_thread(void *data)
 			break;
 		case VIDEO_MODE_SECAM: 
 			mode = 0x0003;
-			std  = 1;
+			std  = standard;
 			break;
 		case VIDEO_MODE_RADIO:
 			mode = 0x0003;
 			std  = 0x0040;
 			break;
+		case VIDEO_MODE_AUTO:
+			mode = 0x2003;
+			std  = standard;
+			break;
 		default:
 			mode = 0x0003;
-			std  = 1;
+			std  = standard;
 			break;
 		}
 		msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode);
@@ -1189,8 +1212,11 @@ static int msp3410d_thread(void *data)
 			msp->nicam_on = 0;
 			msp->watch_stereo = 1;
 			break;
-		}
-		
+		}    
+		// AEW a true reset has probably messed with our ACB register
+		// we need to restore this.
+		msp3400c_write(client, I2C_MSP3400C_DFP, 0x0013, msp->acb);
+
 		/* unmute + restore dfp registers */
 		msp3400c_setbass(client, msp->bass);
 		msp3400c_settreble(client, msp->treble);
@@ -1257,12 +1283,12 @@ static int msp_attach(struct i2c_adapter
 	}
 	
 	memset(msp,0,sizeof(struct msp3400c));
-	msp->left   = 65535;
-	msp->right  = 65535;
+	msp->left   = 59343;
+	msp->right  = 59343;
 	msp->bass   = 32768;
 	msp->treble = 32768;
 	msp->input  = -1;
-	msp->muted  = 1;
+	/* msp->muted  = 1; */
 	for (i = 0; i < DFP_COUNT; i++)
 		msp->dfp_regs[i] = -1;
 
@@ -1480,7 +1506,7 @@ static int msp_command(struct i2c_client
 		struct video_audio *va = arg;
 
 		dprintk(KERN_DEBUG "msp34xx: VIDIOCGAUDIO\n");
-		va->flags |= VIDEO_AUDIO_VOLUME |
+		va->flags = VIDEO_AUDIO_VOLUME |
 			VIDEO_AUDIO_BASS |
 			VIDEO_AUDIO_TREBLE |
 			VIDEO_AUDIO_MUTABLE;
@@ -1532,6 +1558,7 @@ static int msp_command(struct i2c_client
 		
 		dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN\n");
 		msp->norm = vc->norm;
+		msp_wake_thread(client);
 		break;
 	}
 	case VIDIOCSFREQ:
@@ -1541,14 +1568,22 @@ static int msp_command(struct i2c_client
 		msp_wake_thread(client);
 		break;
 	}
+	case MSP_SET_MATRIX:
+	{
+		struct msp_matrix *mspm = arg;
 
-	default:
-		/* nothing */
+		dprintk(KERN_DEBUG "msp34xx: MSP_SET_MATRIX\n");
+		msp3400c_set_scart(client, mspm->input, mspm->output);
 		break;
 	}
-	return 0;
-}
 
+	default: 
+		/* nothing */
+		break;  
+	}        
+	return 0;
+}         
+          
 /* ----------------------------------------------------------------------- */
 
 static int msp3400_init_module(void)
diff -upN reference/drivers/media/video/msp3400.h current/drivers/media/video/msp3400.h
--- reference/drivers/media/video/msp3400.h	2002-12-09 18:46:22.000000000 -0800
+++ current/drivers/media/video/msp3400.h	2004-03-30 21:42:12.000000000 -0800
@@ -8,7 +8,15 @@ struct msp_dfpreg {
     int value;
 };
 
+struct msp_matrix {
+    int input;
+    int output;
+};
+
 #define MSP_SET_DFPREG     _IOW('m',15,struct msp_dfpreg)
 #define MSP_GET_DFPREG     _IOW('m',16,struct msp_dfpreg)
 
+/* ioctl for MSP_SET_MATRIX will have to be registered */
+#define MSP_SET_MATRIX     _IOW('m',17,struct msp_matrix)
+
 #endif /* MSP3400_H */
diff -upN /dev/null current/drivers/media/video/saa7115.c
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/saa7115.c	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,1130 @@
+/* 
+ * saa7114 - Philips SAA7114H video decoder driver version 0.0.1
+ *
+ * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Based on saa7111 driver by Dave Perks
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *    - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * Changes by Kevin Thayer <nufan_wfk at yahoo.com>
+ *    - changed to saa7115. (2/17/2003)
+ *
+ * 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/version.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/init.h>
+
+#include <linux/slab.h>
+
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include <linux/videodev.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+
+MODULE_DESCRIPTION("Philips SAA7115 video decoder driver");
+MODULE_AUTHOR("Kevin Thayer");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#ifndef I2C_DRIVERID_SAA7114
+#warning Using temporary hack for missing I2C driver-ID for saa7114
+#define I2C_DRIVERID_SAA7114 I2C_DRIVERID_EXP1
+#endif
+
+#include <linux/video_decoder.h>
+
+static int debug = 1;
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/* FIXME need to get this properly allocated 
+ *   also defined in ivtv.h, so change it there too */
+#define DECODER_SET_SIZE    76598
+#define	DECODER_GET_PICTURE 76599
+
+/* Need to be able to set the audio bitrates */
+#define DECODER_SET_AUDIO	0xFFEE7707
+#define DECODER_AUDIO_32_KHZ	0
+#define DECODER_AUDIO_441_KHZ	1
+#define DECODER_AUDIO_48_KHZ	2
+
+#ifdef SAA7115_REGTEST
+/* allow direct access to the saa7115 registers for testing */
+#define SAA7115_GET_REG		0xFFEE7705
+#define SAA7115_SET_REG		0xFFEE7706
+
+struct saa7115_reg_t {
+	u8 reg;
+	u8 val;
+};
+#endif
+
+#define dprintk(num, format, args...) \
+	do { \
+		if (debug >= num) \
+			printk(format, ##args); \
+	} while (0)
+
+/* ----------------------------------------------------------------------- */
+
+static u8 readreg(struct i2c_client *client, unsigned char reg)
+{
+	struct i2c_adapter *adap = client->adapter;
+	unsigned char mm1[] = { 0x1e };
+	unsigned char mm2[] = { 0x00 };
+	struct i2c_msg msgs[2];
+
+	msgs[0].flags = 0;
+	msgs[1].flags = I2C_M_RD;
+	msgs[0].addr = msgs[1].addr = client->addr;
+	mm1[0] = reg;
+	msgs[0].len = 1;
+	msgs[1].len = 1;
+	msgs[0].buf = mm1;
+	msgs[1].buf = mm2;
+	i2c_transfer(adap, msgs, 2);
+
+	return mm2[0];
+}
+
+struct saa7114 {
+	int norm;
+	int input;
+	int enable;
+	int bright;
+	int contrast;
+	int hue;
+	int sat;
+	int playback;
+	int audio;
+};
+
+#define   I2C_SAA7114        0x42
+#define   I2C_SAA7114A       0x40
+
+#define   I2C_DELAY   10
+
+//#define SAA_7114_NTSC_HSYNC_START       (-3)
+//#define SAA_7114_NTSC_HSYNC_STOP        (-18)
+
+#define SAA_7114_NTSC_HSYNC_START  (-17)
+#define SAA_7114_NTSC_HSYNC_STOP   (-32)
+
+//#define SAA_7114_NTSC_HOFFSET           (5)
+#define SAA_7114_NTSC_HOFFSET		(6)
+#define SAA_7114_NTSC_VOFFSET           (10)
+#define SAA_7114_NTSC_WIDTH             (720)
+#define SAA_7114_NTSC_HEIGHT            (480)	/* was 250 */
+
+#define SAA_7114_SECAM_HSYNC_START      (-17)
+#define SAA_7114_SECAM_HSYNC_STOP       (-32)
+
+#define SAA_7114_SECAM_HOFFSET          (2)
+#define SAA_7114_SECAM_VOFFSET          (10)
+#define SAA_7114_SECAM_WIDTH            (720)
+#define SAA_7114_SECAM_HEIGHT           (300)
+
+#define SAA_7114_PAL_HSYNC_START        (-17)
+#define SAA_7114_PAL_HSYNC_STOP         (-32)
+
+#define SAA_7114_PAL_HOFFSET            (2)
+#define SAA_7114_PAL_VOFFSET            (10)
+#define SAA_7114_PAL_WIDTH              (720)
+#define SAA_7114_PAL_HEIGHT             (300)
+
+#define SAA_7114_VERTICAL_CHROMA_OFFSET         0	//0x50504040
+#define SAA_7114_VERTICAL_LUMA_OFFSET           0
+
+#define REG_ADDR(x) (((x) << 1) + 1)
+#define LOBYTE(x) ((unsigned char)((x) & 0xff))
+#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff))
+#define LOWORD(x) ((unsigned short int)((x) & 0xffff))
+#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff))
+
+/* ----------------------------------------------------------------------- */
+
+static inline int saa7114_write(struct i2c_client *client, u8 reg, u8 value)
+{
+//      struct saa7114 *decoder = i2c_get_clientdata(client);
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int writeregs(struct i2c_client *client, const unsigned char *regs)
+{
+	unsigned char reg, data;
+
+	while (*regs != 0x00) {
+		reg = *(regs++);
+		data = *(regs++);
+		if (saa7114_write(client, reg, data) < 0)
+			return -1;
+	}
+	return 0;
+}
+
+static inline int saa7114_read(struct i2c_client *client, u8 reg)
+{
+	return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const unsigned char init_saa7115_auto_input[] = {
+	0x01, 0x08,		//(was 0x48)   // 0x08: white peak control enabled, 0x48: white peak control disabled
+	0x03, 0x2C,		//was 0x20     // 0x20: automatic gain control, 0x2c: user programmable gain
+	0x04, 0x90,		// analog gain set to 0
+	0x05, 0x90,		// analog gain set to 0
+	0x06, 0xEB,		// horiz sync begin = -21
+	0x07, 0xE0,		// horiz sync stop = -17
+
+	// done in misc
+//      0x09, 0x40, //80 for svideo  // 0x40: use luminance comb filter, 0x80: don't use ...
+
+	0x0A, 0x80,		//i2c dump ends up at 96 (was 80) // decoder brightness, 0x80 is itu standard
+	0x0B, 0x44,		// decoder contrast, 0x44 is itu standard
+	0x0C, 0x40,		// decoder saturation, 0x40 is itu standard
+	0x0D, 0x00,		//i2c dump ends up at 04 (was 00) // chrominance hue control
+	0x0F, 0x24,		//i2c dump says 0x30 (was 0x2A) // chrominance gain control , should be 0x00 for agc, otherwise 0x80+0x24: 0xA4
+	0x10, 0x06,
+	0x11, 0x00,
+	0x12, 0x9D,		//i2c dump says 0x9D (was 0x00)
+	0x13, 0x80,		//" 0x80 (was 0x00)
+	0x14, 0x01,		//" 0x01 (was 0x01)
+	0x15, 0x04,		//" 0x00 (was 0x11) //should also be moved to NTSC/PAL VGATE start
+	/* moved to NTSC/PAL sections
+	   0x16, 0x11, //" 0x11 (was 0xFE) // VGATE stop
+	 */
+	0x17, 0x98,		//" 0x98 (was 0xD8) //may set to 98 // VGATE MSB and other values
+	0x18, 0x40,		// raw data gain 0x00 = nominal
+	0x19, 0x80,		// raw data offset 0x80 = 0 LSB
+	0x1A, 0x77,		// color killer level control 0x77 = recommended
+	0x1B, 0x42,		// misc chroma control 0x42 = recommended
+	0x1C, 0xA9,		// combfilter control 0xA9 = recommended
+	0x1D, 0x01,		// combfilter control 0x01 = recommended
+	0x88, 0xD0,		//reset device // set programmed, reset
+	0x88, 0xF0,		//Set device programmed, all in operational mode // set programmed, active ?programmed should be 0?
+	0x00, 0x00		// ? not necessary, version readback register
+};
+
+/* ============== SAA7715 VIDEO templates =============  */
+
+static const unsigned char cfg_saa7115_reset_scaler[] = {
+	0x87, 0x00,		//Disable I-port output
+	0x88, 0x0B,		//reset scaler (was 0xD0) // ?should be 0xD0
+	0x88, 0xF0,		//activate scaler
+	0x87, 0x01,		//Enable I-port output  // what about high bits? how is ICLK used?
+	0x00, 0x00
+};
+static const unsigned char cfg_saa7115_NTSC_fullres_x[] = {
+	0xCC, 0xD0,		//hsize low (output) //hor output window size = 0x2d0 = 720
+	0xCD, 0x02,		//hsize hi (output)
+
+	0xD0, 0x01,		// down scale = 1
+	0xD1, 0x00,		// prescale accumulation length = 1
+	0xD2, 0x00,		// dc gain and fir prefilter control
+	0xD4, 0x80,		//Lum Brightness // nominal value = 0x80
+	0xD5, 0x40,		//Lum contrast // nominal value = 0x40
+	0xD6, 0x40,		//Chroma satur. // nominal value = 0x80
+	0xD8, 0x00,		// hor lum scaling 0x0400 = 1
+	0xD9, 0x04,
+	0xDA, 0x00,		//H-phase offset Luma = 0
+	0xDC, 0x00,		// hor chrom scaling 0x0200. must be hor lum scaling /2
+	0xDD, 0x02,		//H-scaling incr chroma 
+	0xDE, 0x00,		//H-phase offset chroma // must be offset luma /2
+
+	0x00, 0x00
+};
+static const unsigned char cfg_saa7115_NTSC_fullres_y[] = {
+	0xCE, 0xFD,		//vsize low (output) was FD // ver output window size = 253 ??240
+	0xCF, 0x00,		//vsize hi (output)
+
+	0xE0, 0x00,		//V-scaling incr luma low 0x0400 = 1
+	0xE1, 0x04,		//" hi
+	0xE2, 0x00,		//V-scaling incr chroma low // must be same as luma
+	0xE3, 0x04,		//" hi
+	0xE4, 0x01,		//V-scaling mode control // no mirroring, higher order accumulation
+	0xE8, 0x00,		//V-phase offset chroma 00 //?only regs E8 and EC necessary?
+	0xE9, 0x00,		//V-phase offset chroma 01
+	0xEA, 0x00,		//V-phase offset chroma 10
+	0xEB, 0x00,		//V-phase offset chroma 11
+	0xEC, 0x00,		//V-phase offset luma 00
+	0xED, 0x00,		//V-phase offset luma 01
+	0xEE, 0x00,		//V-phase offset luma 10
+
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_NTSC_video[] = {
+	0x80, 0x00,		//reset tasks
+	0x88, 0x0B,		//reset scaler (was 0xD0)
+
+	0x16, 0x11,		//" 0x11 (was 0xFE) //VGATE pulse stop
+
+	0x08, 0x68,		//i2c dump says 0x68 (was 0xB0) NTSC ONLY  // 0xBO: auto detection, 0x68 = NTSC
+	0x0E, 0x07,		//i2c dump says 0x0d (was 0x07) // lots of different stuff... video autodetection is on
+
+	0xC0, 0x00,		//Task Handling Control (was 0x00)
+	0xC1, 0x08,		//X-port formats/config
+	0xC2, 0x00,		//Input Ref. signal Def.
+	0xC3, 0x80,		//I-port config (was 0x80)
+	0xC4, 0x02,		//hoffset low (input) // 0x0002 is minimum
+	0xC5, 0x00,		//hoffset hi (input)
+	0xC6, 0xD0,		//hsize low (input) // 0x02d0 = 720
+	0xC7, 0x02,		//hsize hi (input)
+	0xC8, 0x14,		//voff low was 0x14, changing to 0x0E (14) // 0x0014 = 20
+	0xC9, 0x00,		//voff hi
+	0xCA, 0xFD,		//vsize low (input) was FD // 0x00fd = 253
+	0xCB, 0x00,		//vsize hi (input)
+
+	0xF0, 0xAD,		//Set PLL Register. NTSC 525 lines per frame, 27 MHz
+	0xF1, 0x05,		//low bit with 0xF0, (was 0x05)
+	0xF5, 0xAD,		//Set pulse generator register
+	0xF6, 0x01,
+
+	0x87, 0x00,		//Disable I-port output
+	0x88, 0x0B,		//reset scaler (was 0xD0)
+	0x80, 0x20,		//Activate only task "B", continuous mode (was 0xA0)
+	0x88, 0xF0,		//activate scaler
+	0x87, 0x01,		//Enable I-port output
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_PAL_fullres_x[] = {
+	0xCC, 0xD0,		//hsize low (output) //720 same as NTSC
+	0xCD, 0x02,		//hsize hi (output)
+
+	0xD0, 0x01,
+	0xD1, 0x00,
+	0xD2, 0x00,
+	0xD4, 0x80,		//Lum Brightness
+	0xD5, 0x40,		//Lum contrast
+	0xD6, 0x40,		//Chroma satur.
+	0xD8, 0x00,
+	0xD9, 0x04,
+	0xDA, 0x00,		//H-phase offset Luma
+	0xDC, 0x00,
+	0xDD, 0x02,		//H-scaling incr chroma
+	0xDE, 0x00,		//H-phase offset chroma
+
+	0x00, 0x00
+};
+static const unsigned char cfg_saa7115_PAL_fullres_y[] = {
+	0xCE, 0x20,		//vsize low (output) // 0x0120 = 288
+	0xCF, 0x01,		//vsize hi (output)
+
+	0xE0, 0x00,		//V-scaling incr luma low
+	0xE1, 0x04,		//" hi
+	0xE2, 0x00,		//V-scaling incr chroma low
+	0xE3, 0x04,		//" hi
+	0xE4, 0x01,		//V-scaling mode control
+	0xE8, 0x00,		//V-phase offset chroma 00
+	0xE9, 0x00,		//V-phase offset chroma 01
+	0xEA, 0x00,		//V-phase offset chroma 10
+	0xEB, 0x00,		//V-phase offset chroma 11
+	0xEC, 0x00,		//V-phase offset luma 00
+	0xED, 0x00,		//V-phase offset luma 01
+	0xEE, 0x00,		//V-phase offset luma 10
+	0xEF, 0x00,		//V-phase offset luma 11
+
+	0x00, 0x00
+};
+
+/* FIXME need to input proper height/width */
+static const unsigned char cfg_saa7115_PAL_video[] = {
+	0x80, 0x00,		//reset tasks
+	0x88, 0x0B,		//reset scaler (was 0xD0)
+
+	0x16, 0x15,		//" 0x11 (was 0xFE)
+
+	0x08, 0x28,		//i2c dump says 0x28 (was 0xB0) PAL ONLY // 0x28 = PAL
+	0x0E, 0x07,		//i2c dump says 0x0d (was 0x07)
+
+	0xC0, 0x00,		//Task Handling Control (was 0x00)
+	0xC1, 0x08,		//X-port formats/config
+	0xC2, 0x00,		//Input Ref. signal Def.
+	0xC3, 0x80,		//I-port config (was 0x80)
+	0xC4, 0x00,		//hoffset low (input)
+	0xC5, 0x00,		//hoffset hi (input)
+	0xC6, 0xD0,		//hsize low (input) // 0x02D0 = 720
+	0xC7, 0x02,		//hsize hi (input)
+	0xC8, 0x14,		//voffset low (input) low was 0x14, changing to 0x0E (14)
+	0xC9, 0x00,		//voffset hi (input)
+	0xCA, 0x20,		//vsize low (input) // 288
+	0xCB, 0x01,		//vsize hi (input)
+
+	0xF0, 0xB0,		//Set PLL Register. PAL 625 lines per frame, 27 MHz
+	0xF1, 0x05,		//low bit with 0xF0, (was 0x05)
+	0xF5, 0xB0,		//Set pulse generator register
+	0xF6, 0x01,
+
+	0x87, 0x00,		//Disable I-port output
+	0x88, 0x0B,		//reset scaler (was 0xD0)
+	0x80, 0xA0,		//Activate only task "B", continuous mode (was 0xA0)
+	0x88, 0xF0,		//activate scaler
+	0x87, 0x01,		//Enable I-port output
+	0x00, 0x00
+};
+
+/* ============== SAA7715 VIDEO templates (end) =======  */
+
+static const unsigned char init_saa7115_misc[] = {
+	0x38, 0x03,		// audio stuff
+	0x39, 0x10,
+	0x3A, 0x00,
+
+//      0x80, 0x00, // set below
+	0x81, 0x01,		//reg 0x15,0x16 define blanking window
+	0x82, 0x00,
+	0x83, 0x01,		//was 0x01 // I port settings
+	0x84, 0x20,
+	0x85, 0x21,
+	0x86, 0xC5,
+	0x87, 0x01,
+//      0x88, 0xD0, // unnecessary
+
+//      0xF0, 0xAD, //this goes in PAL/NTSC video
+//      0xF1, 0x05,
+	0xF2, 0x50,		// crystal clock = 24.576 MHz, target = 27MHz
+	0xF3, 0x46,
+	0xF4, 0x00,
+//      0xF5, 0xAD, //this goes in PAL/NTSC video
+//      0xF6, 0x01,
+	0xF7, 0x4B,		// not the recommended settings!
+	0xF8, 0x00,
+	0xF9, 0x4B,
+	0xFA, 0x00,
+	0xFB, 0x4B,
+//      0xFC, 0x00, // unused
+//      0xFD, 0x00,
+//      0xFE, 0x00,
+	0xFF, 0x88,		// PLL2 lock detection settings: 71 lines 50% phase error
+
+//      0x88, 0xF0, // unnecessary
+
+//      0x0D, 0x04, // already set in auto_input
+//      0x0C, 0x40,
+//      0x0A, 0x96,
+//      0x0B, 0x41,
+//      0x98, 0x05, // belongs to task A; unnecessary
+/* Turn off VBI */
+	0x40, 0x00,
+	0x41, 0xFF,
+	0x42, 0xFF,
+	0x43, 0xFF,
+	0x44, 0xFF,
+	0x45, 0xFF,
+	0x46, 0xFF,
+	0x47, 0xFF,
+	0x48, 0xFF,
+	0x49, 0xFF,
+	0x4A, 0xFF,
+	0x4B, 0xFF,
+	0x4C, 0xFF,
+	0x4D, 0xFF,
+	0x4E, 0xFF,
+	0x4F, 0xFF,
+	0x50, 0xFF,
+	0x51, 0xFF,
+	0x52, 0xFF,
+	0x53, 0xFF,
+	0x54, 0xFF,
+	0x55, 0xFF,
+	0x56, 0xFF,
+	0x57, 0xFF,
+	0x58, 0x00,
+	0x59, 0x47,
+	0x5A, 0x06,
+	0x5B, 0x88,
+	0x5D, 0xBF,
+	0x5E, 0x35,
+
+	0x02, 0x84,		//input tuner -> input 4, amplifier active
+	0x09, 0x53,		//chrom trap for tuner // special tuner stuff?
+
+	0x80, 0x20,		//was 0x30 // 0x20 clock from PLL2, 0x30 clock from ICLK
+	0x88, 0xD0,
+	0x88, 0xF0,
+	0x00, 0x00
+};
+
+/* ============== SAA7715 AUDIO settings =============  */
+static const unsigned char cfg_saa7115_48_audio[] = {
+	0x34, 0xCE,		// 48khz
+	0x35, 0xFB,		// "
+	0x36, 0x30,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_441_audio[] = {
+	0x34, 0xF2,		// 44.1khz
+	0x35, 0x00,		// "
+	0x36, 0x2D,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_32_audio[] = {
+	0x34, 0xDF,		// 32.0khz
+	0x35, 0xA7,		// "
+	0x36, 0x20,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_NTSC_48_audio[] = {
+	0x30, 0xCD,		// 48.0khz NTSC
+	0x31, 0x20,		// "
+	0x32, 0x03,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_PAL_48_audio[] = {
+	0x30, 0x00,		// 48.0khz PAL
+	0x31, 0xC0,		// "
+	0x32, 0x03,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_NTSC_441_audio[] = {
+	0x30, 0xBC,		// 44.1khz NTSC
+	0x31, 0xDF,		// "
+	0x32, 0x02,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_PAL_441_audio[] = {
+	0x30, 0x00,		// 44.1khz PAL
+	0x31, 0x72,		// "
+	0x32, 0x03,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_NTSC_32_audio[] = {
+	0x30, 0xDE,		// 32.0khz NTSC
+	0x31, 0x15,		// "
+	0x32, 0x02,		// "
+	0x00, 0x00
+};
+
+static const unsigned char cfg_saa7115_PAL_32_audio[] = {
+	0x30, 0x00,		// 32.0khz PAL
+	0x31, 0x80,		// "
+	0x32, 0x02,		// "
+	0x00, 0x00
+};
+
+/* ============ SAA7715 AUDIO settings (end) ============= */
+
+static int
+saa7114_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	struct saa7114 *decoder = i2c_get_clientdata(client);
+
+	switch (cmd) {
+
+	case 0:
+		//dprintk(1, KERN_INFO "%s: writing init\n", client->name);
+		//saa7114_write_block(client, init, sizeof(init));
+		break;
+#ifdef SAA7115_REGTEST
+		/* ioctls to allow direct access to the saa7115 registers for testing */
+	case SAA7115_GET_REG:
+		{
+			struct saa7115_reg_t *saa7115_reg =
+			    (struct saa7115_reg_t *)arg;
+
+			saa7115_reg->val =
+			    saa7114_read(client, saa7115_reg->reg);
+			break;
+		}
+	case SAA7115_SET_REG:
+		{
+			struct saa7115_reg_t *saa7115_reg =
+			    (struct saa7115_reg_t *)arg;
+
+			saa7114_write(client, saa7115_reg->reg,
+				      saa7115_reg->val);
+			break;
+		}
+#endif
+	case DECODER_SET_SIZE:
+		{
+			/* Used video_window because it has height/width and is
+			 * already defined */
+			struct video_window *wind = arg;
+			int HPSC, HFSC;
+			int VSCY, Vsrc;
+
+			dprintk(1, KERN_INFO "%s: decoder set size\n",
+				client->name);
+
+			/* FIXME need better bounds checking here */
+			if ((wind->width < 1) || (wind->width > 1440))
+				return -EINVAL;
+			if ((wind->height < 1) || (wind->height > 960))
+				return -EINVAL;
+
+			/* probably have a valid size, let's set it */
+/* Set output width/height */
+			/* width */
+			saa7114_write(client, 0xCC, (u8) (wind->width & 0xFF));
+			saa7114_write(client, 0xCD,
+				      (u8) ((wind->width >> 8) & 0xFF));
+			/* height */
+			saa7114_write(client, 0xCE, (u8) (wind->height & 0xFF));
+			saa7114_write(client, 0xCF,
+				      (u8) ((wind->height >> 8) & 0xFF));
+
+/* Scaling settings */
+			/* Hprescaler is floor(inres/outres) */
+			/* FIXME hardcoding input res */
+			if (wind->width != 720) {
+				HPSC = (int)(720 / wind->width);
+				HFSC =
+				    (int)((1024 * 720) / (HPSC * wind->width));
+
+				printk("Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC,
+				       HFSC);
+				/* FIXME hardcodes to "Task B" 
+				 * write H prescaler integer */
+				saa7114_write(client, 0xD0, (u8) (HPSC & 0x3F));
+
+				/* write H fine-scaling (luminance) */
+				saa7114_write(client, 0xD8, (u8) (HFSC & 0xFF));
+				saa7114_write(client, 0xD9,
+					      (u8) ((HFSC >> 8) & 0xFF));
+				/* write H fine-scaling (chrominance)
+				 * must be lum/2, so i'll just bitshift :) */
+				saa7114_write(client, 0xDC,
+					      (u8) ((HFSC >> 1) & 0xFF));
+				saa7114_write(client, 0xDD,
+					      (u8) ((HFSC >> 9) & 0xFF));
+			} else {
+				if (decoder->norm != VIDEO_MODE_NTSC) {
+					printk("Setting full PAL width\n");
+					writeregs(client,
+						  cfg_saa7115_PAL_fullres_x);
+				} else {
+					printk("Setting full NTSC width\n");
+					writeregs(client,
+						  cfg_saa7115_NTSC_fullres_x);
+				}
+			}
+
+			Vsrc = 480;
+			if (decoder->norm != VIDEO_MODE_NTSC)
+				Vsrc = 576;
+
+			if (wind->height != Vsrc) {
+				VSCY = (int)((1024 * Vsrc) / wind->height);
+				printk("Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+				/* write V fine-scaling (luminance) */
+				saa7114_write(client, 0xE0, (u8) (VSCY & 0xFF));
+				saa7114_write(client, 0xE1,
+					      (u8) ((VSCY >> 8) & 0xFF));
+				/* write V fine-scaling (chrominance) */
+				saa7114_write(client, 0xE2, (u8) (VSCY & 0xFF));
+				saa7114_write(client, 0xE3,
+					      (u8) ((VSCY >> 8) & 0xFF));
+			} else {
+				if (decoder->norm != VIDEO_MODE_NTSC) {
+					printk("Setting full PAL height\n");
+					writeregs(client,
+						  cfg_saa7115_PAL_fullres_y);
+				} else {
+					printk("Setting full NTSC height\n");
+					writeregs(client,
+						  cfg_saa7115_NTSC_fullres_y);
+				}
+			}
+
+			writeregs(client, cfg_saa7115_reset_scaler);
+			break;
+		}
+	case DECODER_DUMP:
+		{
+			int i;
+
+			dprintk(1, KERN_INFO "%s: decoder dump\n",
+				client->name);
+
+			for (i = 0; i < 32; i += 16) {
+				int j;
+
+				printk(KERN_DEBUG "%s: %03x", client->name, i);
+				for (j = 0; j < 16; ++j) {
+					printk(" %02x",
+					       saa7114_read(client, i + j));
+				}
+				printk("\n");
+			}
+		}
+		break;
+
+	case DECODER_GET_CAPABILITIES:
+		{
+			struct video_decoder_capability *cap = arg;
+
+			dprintk(1, KERN_DEBUG "%s: decoder get capabilities\n",
+				client->name);
+
+			cap->flags = VIDEO_DECODER_PAL |
+			    VIDEO_DECODER_NTSC |
+			    VIDEO_DECODER_SECAM |
+			    VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR;
+			cap->inputs = 8;
+			cap->outputs = 1;
+		}
+		break;
+
+	case DECODER_SET_AUDIO:
+		{
+			int *iarg = arg;
+			dprintk(1, KERN_DEBUG "%s set audio: 0x%02x\n",
+				client->name, *iarg);
+			switch (*iarg) {
+			case DECODER_AUDIO_32_KHZ:
+				writeregs(client, cfg_saa7115_32_audio);
+				if (decoder->norm == VIDEO_MODE_NTSC) {
+					writeregs(client,
+						  cfg_saa7115_NTSC_32_audio);
+				} else {
+					writeregs(client,
+						  cfg_saa7115_PAL_32_audio);
+				}
+				break;
+			case DECODER_AUDIO_441_KHZ:
+				writeregs(client, cfg_saa7115_441_audio);
+				if (decoder->norm == VIDEO_MODE_NTSC) {
+					writeregs(client,
+						  cfg_saa7115_NTSC_441_audio);
+				} else {
+					writeregs(client,
+						  cfg_saa7115_PAL_441_audio);
+				}
+				break;
+			case DECODER_AUDIO_48_KHZ:
+				writeregs(client, cfg_saa7115_48_audio);
+				if (decoder->norm == VIDEO_MODE_NTSC) {
+					writeregs(client,
+						  cfg_saa7115_NTSC_48_audio);
+				} else {
+					writeregs(client,
+						  cfg_saa7115_PAL_48_audio);
+				}
+				break;
+			default:
+				printk(KERN_DEBUG
+				       "%s invalid audio setting 0x%02x\n",
+				       client->name, *iarg);
+			}
+
+			/*FIXME digitizer reset needed? 
+			 *   if so, uncomment this line */
+			//writeregs(client, cfg_saa7115_reset_scaler);
+
+			decoder->audio = *iarg;
+
+		}
+		break;
+	case DECODER_GET_STATUS:
+		{
+			int *iarg = arg;
+			int status;
+			int res;
+
+			status = saa7114_read(client, 0x1f);
+
+			dprintk(1, KERN_DEBUG "%s status: 0x%02x\n",
+				client->name, status);
+			res = 0;
+			if ((status & (1 << 6)) == 0) {
+				res |= DECODER_STATUS_GOOD;
+			}
+			switch (decoder->norm) {
+			case VIDEO_MODE_NTSC:
+				res |= DECODER_STATUS_NTSC;
+				break;
+			case VIDEO_MODE_PAL:
+				res |= DECODER_STATUS_PAL;
+				break;
+			case VIDEO_MODE_SECAM:
+				res |= DECODER_STATUS_SECAM;
+				break;
+			default:
+			case VIDEO_MODE_AUTO:
+				if ((status & (1 << 5)) != 0) {
+					res |= DECODER_STATUS_NTSC;
+				} else {
+					res |= DECODER_STATUS_PAL;
+				}
+				break;
+			}
+			if ((status & (1 << 0)) != 0) {
+				res |= DECODER_STATUS_COLOR;
+			}
+			*iarg = res;
+		}
+		break;
+
+	case DECODER_SET_NORM:
+		{
+			u16 *iarg = arg;
+
+			dprintk(1, KERN_DEBUG "%s: decoder set norm ",
+				client->name);
+
+			switch (*iarg) {
+
+			case VIDEO_MODE_NTSC:
+				dprintk(1, "NTSC\n");
+				writeregs(client, cfg_saa7115_NTSC_video);
+				break;
+
+			case VIDEO_MODE_PAL:
+				dprintk(1, "PAL\n");
+				writeregs(client, cfg_saa7115_PAL_video);
+				break;
+
+			case VIDEO_MODE_SECAM:
+				dprintk(1, "SECAM\n");
+				writeregs(client, cfg_saa7115_PAL_video);
+				break;
+
+			default:
+				dprintk(1, " Unknown video mode!!!\n");
+				return -EINVAL;
+
+			}
+
+			decoder->norm = *iarg;
+
+			/* switch audio mode too! */
+			saa7114_command(client, DECODER_SET_AUDIO,
+					&decoder->audio);
+
+		}
+		break;
+
+	case DECODER_SET_INPUT:
+		{
+			int *iarg = arg;
+
+			dprintk(1, KERN_DEBUG "%s: decoder set input (%d)\n",
+				client->name, *iarg);
+			/* inputs from 0-9 are available */
+			if (*iarg < 0 || *iarg > 9) {
+				return -EINVAL;
+			}
+
+			if (decoder->input != *iarg) {
+				dprintk(1,
+					KERN_DEBUG "%s: now setting %s input\n",
+					client->name,
+					*iarg >= 6 ? "S-Video" : "Composite");
+				decoder->input = *iarg;
+
+				/* select mode */
+				saa7114_write(client,
+					      0x02,
+					      (saa7114_read(client, 0x02) &
+					       0xf0) | decoder->input);
+
+				/* bypass chrominance trap for modes 6..9 */
+				saa7114_write(client, 0x09,
+					      (saa7114_read(client, 0x09) &
+					       0x7f) | (decoder->input <
+							6 ? 0x0 : 0x80));
+			}
+		}
+		break;
+
+	case DECODER_SET_OUTPUT:
+		{
+			int *iarg = arg;
+
+			dprintk(1, KERN_DEBUG "%s: decoder set output\n",
+				client->name);
+
+			/* not much choice of outputs */
+			if (*iarg != 0) {
+				return -EINVAL;
+			}
+		}
+		break;
+
+	case DECODER_ENABLE_OUTPUT:
+		{
+			int *iarg = arg;
+			int enable = (*iarg != 0);
+
+			dprintk(1, KERN_DEBUG "%s: decoder %s output\n",
+				client->name, enable ? "enable" : "disable");
+
+			decoder->playback = !enable;
+
+			if (decoder->enable != enable) {
+				decoder->enable = enable;
+
+				if (decoder->enable) {
+					saa7114_write(client, 0x87, 0x01);
+				} else {
+					saa7114_write(client, 0x87, 0x00);
+				}
+			}
+		}
+		break;
+
+	case DECODER_GET_PICTURE:
+		{
+			struct saa7114 *pic = arg;
+
+			pic->bright = decoder->bright;
+			pic->contrast = decoder->contrast;
+			pic->sat = decoder->sat;
+			pic->hue = decoder->hue;
+		}
+		break;
+
+	case DECODER_SET_PICTURE:
+		{
+			struct saa7114 *pic = arg;
+
+			dprintk(1,
+				KERN_DEBUG
+				"%s: decoder set picture bright=%d contrast=%d saturation=%d hue=%d\n",
+				client->name, pic->bright, pic->contrast,
+				pic->sat, pic->hue);
+
+			if (decoder->bright != pic->bright) {
+				/* We want 0 to 255 */
+				if (pic->bright < 0 || pic->bright > 255) {
+					dprintk(0,
+						KERN_ERR
+						"%s: invalid brightness setting %d",
+						client->name, pic->bright);
+					return -EINVAL;
+				}
+				decoder->bright = pic->bright;
+				saa7114_write(client, 0x0a, decoder->bright);
+			}
+			if (decoder->contrast != pic->contrast) {
+				/* We want 0 to 127 */
+				if (pic->contrast < 0 || pic->contrast > 127) {
+					dprintk(0,
+						KERN_ERR
+						"%s: invalid contrast setting %d",
+						client->name, pic->contrast);
+					return -EINVAL;
+				}
+				decoder->contrast = pic->contrast;
+				saa7114_write(client, 0x0b, decoder->contrast);
+			}
+			if (decoder->sat != pic->sat) {
+				/* We want 0 to 127 */
+				if (pic->sat < 0 || pic->sat > 127) {
+					dprintk(0,
+						KERN_ERR
+						"%s: invalid saturation setting %d",
+						client->name, pic->sat);
+					return -EINVAL;
+				}
+				decoder->sat = pic->sat;
+				saa7114_write(client, 0x0c, decoder->sat);
+			}
+			if (decoder->hue != pic->hue) {
+				/* We want -128 to 127 */
+				if (pic->hue < -128 || pic->hue > 127) {
+					dprintk(0,
+						KERN_ERR
+						"%s: invalid hue setting %d",
+						client->name, pic->hue);
+					return -EINVAL;
+				}
+				decoder->hue = pic->hue;
+				saa7114_write(client, 0x0d, decoder->hue);
+			}
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] =
+    { I2C_SAA7114 >> 1, I2C_SAA7114A >> 1, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+static int saa7114_i2c_id = 0;
+struct i2c_driver i2c_driver_saa7114;
+
+static int
+saa7114_detect_client(struct i2c_adapter *adapter, int address, int kind)
+{
+//      int i, err[30];
+//      short int hoff = SAA_7114_NTSC_HOFFSET;
+//      short int voff = SAA_7114_NTSC_VOFFSET;
+//      short int w = SAA_7114_NTSC_WIDTH;
+//      short int h = SAA_7114_NTSC_HEIGHT;
+	struct i2c_client *client;
+	struct saa7114 *decoder;
+
+	dprintk(1,
+		KERN_INFO
+		"saa7114.c: detecting saa7114 client on address 0x%x\n",
+		address << 1);
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == 0)
+		return -ENOMEM;
+	memset(client, 0, sizeof(struct i2c_client));
+	client->addr = address;
+	client->adapter = adapter;
+	client->driver = &i2c_driver_saa7114;
+	client->flags = I2C_CLIENT_ALLOW_USE;
+	client->id = saa7114_i2c_id++;
+	snprintf(client->name, sizeof(client->name) - 1, "saa7115[%d]",
+		 client->id);
+
+	decoder = kmalloc(sizeof(struct saa7114), GFP_KERNEL);
+	i2c_set_clientdata(client, decoder);
+	if (decoder == NULL) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	memset(decoder, 0, sizeof(struct saa7114));
+	decoder->norm = VIDEO_MODE_NTSC;
+	decoder->input = -1;
+	decoder->enable = 1;
+	decoder->bright = 128;
+	decoder->contrast = 64;
+	decoder->hue = 0;
+	decoder->sat = 64;
+	decoder->playback = 0;	// initially capture mode used
+	decoder->audio = DECODER_AUDIO_48_KHZ;
+
+	dprintk(1, KERN_INFO "saa7115.c: writing init values\n");
+
+	/* init to NTSC/48khz */
+	writeregs(client, init_saa7115_auto_input);
+	writeregs(client, init_saa7115_misc);
+	writeregs(client, cfg_saa7115_NTSC_fullres_x);
+	writeregs(client, cfg_saa7115_NTSC_fullres_y);
+	writeregs(client, cfg_saa7115_NTSC_video);
+	writeregs(client, cfg_saa7115_48_audio);
+	writeregs(client, cfg_saa7115_NTSC_48_audio);
+	writeregs(client, cfg_saa7115_reset_scaler);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(2 * HZ);
+
+	printk("status: (1E) 0x%02x, (1F) 0x%02x\n",
+	       readreg(client, 0x1e), readreg(client, 0x1f));
+
+	i2c_attach_client(client);
+
+	return 0;
+}
+
+static int saa7114_attach_adapter(struct i2c_adapter *adapter)
+{
+	dprintk(1,
+		KERN_INFO
+		"saa7114.c: starting probe for adapter %s (0x%x)\n",
+		adapter->name, adapter->id);
+	return i2c_probe(adapter, &addr_data, &saa7114_detect_client);
+}
+
+static int saa7114_detach_client(struct i2c_client *client)
+{
+	struct saa7114 *decoder = i2c_get_clientdata(client);
+	int err;
+
+	err = i2c_detach_client(client);
+	if (err) {
+		return err;
+	}
+
+	kfree(decoder);
+	kfree(client);
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+struct i2c_driver i2c_driver_saa7114 = {
+	.name = "saa7115",
+
+	.id = I2C_DRIVERID_SAA7114,
+	.flags = I2C_DF_NOTIFY,
+
+	.attach_adapter = saa7114_attach_adapter,
+	.detach_client = saa7114_detach_client,
+	.command = saa7114_command,
+	.owner = THIS_MODULE,
+};
+
+static int __init saa7114_init(void)
+{
+	return i2c_add_driver(&i2c_driver_saa7114);
+}
+
+static void __exit saa7114_exit(void)
+{
+	i2c_del_driver(&i2c_driver_saa7114);
+}
+
+module_init(saa7114_init);
+module_exit(saa7114_exit);
diff -upN /dev/null current/drivers/media/video/saa7127.c
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/saa7127.c	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,854 @@
+/*
+ * saa7127 - Philips SAA7127 video encoder driver version 0.2
+ *
+ * Copyright (C) 2003 Roy Bulter <rbulter@hetnet.nl>
+ *
+ * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter
+ *
+ * Copyright (C) 2000-2001 Gillem <htoa@gmx.net>
+ * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de>
+ *
+ * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo
+ *
+ * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org>
+ *
+ * This driver is designed for the Hauppauge 250/350 Linux driver
+ * designed by the Ivytv Project (ivtv.sf.net) 
+ *  
+ * Copyright (C) 2003 Kevin Thayer <nufan_wfk@yahoo.com>
+ *
+ * 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.
+ */
+
+/*
+ * Revision History
+ *
+ *
+ * Revision : 0.1 (09-05-2003)
+ * Change   : First Version
+ *
+ * Revision : 0.2 (21-05-2003)
+ * Change   : solved compiler error on line 785(800) 
+ *            reg61h variable was not set in saa7127_set_norm function
+ *
+ * Revision : 0.3 (21-07-2003) Matt T. Yourst <yourst@yourst.com>
+ * Change   : Update configuration tables to make NTSC appear correctly;
+ *            Enable alternative outputs (s-video, composite, RGB, etc.)
+ */
+
+#include <linux/version.h>
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/init.h>
+
+#include <linux/slab.h>
+
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/videodev.h>
+#include <linux/video_encoder.h>
+
+#include "saa7127.h"
+
+/*
+ **********************************************************************
+ *  Debug Macro's
+ *
+ *
+ **********************************************************************
+ */
+
+#define CURRENT_MASK   3
+#define NO_MASK        0
+#define INFO_MASK      1	/*  0b0000000000001 */
+#define ERROR_MASK     2	/*  0b0000000000010 */
+#define ENTER_MASK     4	/*  0b0000000000100 */
+#define RETURN_MASK    8	/*  0b0000000001000 */
+#define TRACE1_MASK    16	/*  0b0000000010000 */
+#define TRACE2_MASK    32	/*  0b0000000100000 */
+#define TRACE3_MASK    64	/*  0b0000001000000 */
+#define TRACE4_MASK    128	/*  0b0000010000000 */
+#define TRACE5_MASK    256	/*  0b0000100000000 */
+#define TRACE6_MASK    512	/*  0b0001000000000 */
+#define TRACE7_MASK    1024	/*  0b0010000000000 */
+#define TRACE8_MASK    2048	/*  0b0100000000000 */
+#define TRACE9_MASK    4096	/*  0b1000000000000 */
+
+static int debug_mask = CURRENT_MASK;
+static int test_image = 0;
+static int pal = 0;
+static int enable_output = 0;
+static int output_select = SAA7127_OUTPUT_TYPE_SVIDEO;
+
+#define INFO(format, args...)\
+      if ((debug_mask&INFO_MASK) == INFO_MASK)\
+      {\
+        printk("[%s: INFO]: ", __FILE__);\
+        printk(format, ##args);\
+        printk("\n");\
+      }\
+
+#define ERROR(format, args...)\
+      if ((debug_mask&ERROR_MASK) == ERROR_MASK)\
+      {\
+        printk("[%s: %d: ERROR]: ", __FILE__,__LINE__);\
+        printk(format, ##args);\
+        printk("\n");\
+      }\
+
+
+#ifdef DEBUG
+
+#define ENTER\
+  if ((debug_mask&ENTER_MASK) == ENTER_MASK)\
+  {\
+    printk("[%s : %s: %d: ENTER]: ",__FILE__,__FUNCTION__,__LINE__);\
+    printk("\n");\
+  }\
+
+#define RETURN(value)\
+      if ((debug_mask&RETURN_MASK) == RETURN_MASK)\
+      {\
+        printk("[%s : %s : %d: RETURN]: ", __FILE__,__FUNCTION__,__LINE__);\
+        printk("value: %x",value);\
+        printk("\n");\
+      }\
+      return(value);\
+
+static int get_trace_mask(int num)
+{
+	switch (num) {
+	case 1:
+		return TRACE1_MASK;
+		break;
+	case 2:
+		return TRACE2_MASK;
+		break;
+	case 3:
+		return TRACE3_MASK;
+		break;
+	case 4:
+		return TRACE4_MASK;
+		break;
+	case 5:
+		return TRACE5_MASK;
+		break;
+	case 6:
+		return TRACE6_MASK;
+		break;
+	case 7:
+		return TRACE7_MASK;
+		break;
+	case 8:
+		return TRACE8_MASK;
+		break;
+	case 9:
+		return TRACE9_MASK;
+		break;
+	default:
+		return NO_MASK;
+	}
+}
+
+#define TRACE(num, format, args...) \
+      if ((debug_mask&get_trace_mask(num)) == get_trace_mask(num)) \
+      {\
+        printk("[%s: %d: TRACE%d] ", __FILE__, __LINE__,num);\
+        printk(format, ##args);\
+        printk("\n");\
+      }\
+
+#else
+
+#define ENTER
+#define RETURN(value) return(value);
+#define TRACE(num, format, args...)
+
+#endif				/* DEBUG */
+
+/*
+ **********************************************************************
+ *  
+ *  Array's with configuration parameters for the SAA7127
+ *
+ **********************************************************************
+ */
+
+struct i2c_reg_value {
+	unsigned char reg;
+	unsigned char value;
+};
+
+struct i2c_reg_value saa7127_init_config_common[] = {
+	{SAA7127_REG_WIDESCREEN_CONFIG, 0x0d},
+	{SAA7127_REG_WIDESCREEN_ENABLE, 0x00},
+	{SAA7127_REG_COPYGEN_0, 0x77},
+	{SAA7127_REG_COPYGEN_1, 0x41},
+	{SAA7127_REG_COPYGEN_2, 0x00},	// (Macrovision enable/disable)
+	{SAA7127_REG_OUTPUT_PORT_CONTROL, 0x9e},
+	{SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00},
+	{SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00},
+	{SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80},	// (for color bars)
+	{SAA7127_REG_LINE_21_ODD_0, 0x77},
+	{SAA7127_REG_LINE_21_ODD_1, 0x41},
+	{SAA7127_REG_LINE_21_EVEN_0, 0x88},
+	{SAA7127_REG_LINE_21_EVEN_1, 0x41},
+	{SAA7127_REG_RCV_PORT_CONTROL, 0x12},
+	{SAA7127_REG_VTRIG, 0xf9},
+	{SAA7127_REG_HTRIG_HI, 0x00},
+	{SAA7127_REG_RCV2_OUTPUT_START, 0x41},
+	{SAA7127_REG_RCV2_OUTPUT_END, 0xc3},
+	{SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00},
+	{SAA7127_REG_TTX_REQUEST_H_START, 0x3e},
+	{SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8},
+	{SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03},
+	{SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15},
+	{SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16},
+	{SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15},
+	{SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16},
+	{SAA7127_REG_FIRST_ACTIVE, 0x1a},
+	{SAA7127_REG_LAST_ACTIVE, 0x01},
+	{SAA7127_REG_MSB_VERTICAL, 0xc0},
+	{SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00},
+	{SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00},
+	{0, 0}
+};
+
+#define SAA7127_NTSC_DAC_CONTROL 0x05
+struct i2c_reg_value saa7127_init_config_ntsc[] = {
+	{SAA7127_REG_BURST_START, 0x19},
+	{SAA7127_REG_BURST_END, 0x1d},
+	{SAA7127_REG_CHROMA_PHASE, 0x27},
+	{SAA7127_REG_GAINU, 0x88},
+	{SAA7127_REG_GAINV, 0xc0},
+	{SAA7127_REG_BLACK_LEVEL, 0x3f},
+	{SAA7127_REG_BLANKING_LEVEL, 0x36},
+	{SAA7127_REG_VBI_BLANKING, 0x36},
+	{SAA7127_REG_DAC_CONTROL, 0x05},
+	{SAA7127_REG_BURST_AMP, 0x4a},
+	{SAA7127_REG_SUBC3, 0x1f},
+	{SAA7127_REG_SUBC2, 0x7c},
+	{SAA7127_REG_SUBC1, 0xf0},
+	{SAA7127_REG_SUBC0, 0x21},
+	{SAA7127_REG_MULTI, 0x90},
+	{SAA7127_REG_CLOSED_CAPTION, 0x14},
+	{0, 0}
+};
+
+#define SAA7127_PAL_DAC_CONTROL 0x02
+struct i2c_reg_value saa7127_init_config_pal[] = {
+	{SAA7127_REG_BURST_START, 0x21},
+	{SAA7127_REG_BURST_END, 0x1d},
+	{SAA7127_REG_CHROMA_PHASE, 0x3f},
+	{SAA7127_REG_GAINU, 0x7d},
+	{SAA7127_REG_GAINV, 0xaf},
+	{SAA7127_REG_BLACK_LEVEL, 0x23},
+	{SAA7127_REG_BLANKING_LEVEL, 0x35},
+	{SAA7127_REG_VBI_BLANKING, 0x35},
+	{SAA7127_REG_DAC_CONTROL, 0x02},
+	{SAA7127_REG_BURST_AMP, 0x2f},
+	{SAA7127_REG_SUBC3, 0xcb},
+	{SAA7127_REG_SUBC2, 0x8a},
+	{SAA7127_REG_SUBC1, 0x09},
+	{SAA7127_REG_SUBC0, 0x2a},
+	{SAA7127_REG_MULTI, 0xa0},
+	{SAA7127_REG_CLOSED_CAPTION, 0x00},
+	{0, 0}
+};
+
+/*
+ **********************************************************************
+ *  
+ *  Encoder Struct, holds the configuration state of the encoder
+ *
+ **********************************************************************
+ */
+
+struct saa7127 {
+	enum SAA7127_video_norm norm;
+	enum SAA7127_input_type input_type;
+	enum SAA7127_output_type output_type;
+	enum SAA7127_enable_type enable;
+	enum SAA7127_wss_enable_type wss_enable;
+	enum SAA7127_wss_mode_type wss_mode;
+	u8 reg_2d;
+	u8 reg_3a;
+	u8 reg_61;
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_read(struct i2c_client *client, u8 reg)
+{
+	ENTER;
+	RETURN(i2c_smbus_read_byte_data(client, reg));
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_writereg(struct i2c_client *client, u8 reg, u8 val)
+{
+	ENTER;
+	if (i2c_smbus_write_byte_data(client, reg, val) < 0) {
+		ERROR("I2C Write Problem");
+		return (-1);
+	}
+	TRACE(4, "I2C Write to reg: %x, data: %x", reg, val);
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_write_inittab(struct i2c_client *client,
+				 const struct i2c_reg_value *regs)
+{
+	ENTER;
+
+	while (regs->reg != 0) {
+		if (i2c_smbus_write_byte_data(client, regs->reg, regs->value) <
+		    0) {
+			ERROR("I2C Write Problem");
+			RETURN(-1);
+		}
+		TRACE(4, "I2C Write to reg: %x, data: %x", regs->reg,
+		      regs->value);
+		regs++;
+	}
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_wss(struct i2c_client *client)
+{
+	struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client);
+
+	ENTER;
+	switch (encoder->wss_enable) {
+	case SAA7127_WSS_DISABLE:
+		TRACE(3, "Disable Wide Screen Signal");
+		saa7127_writereg(client, 0x27, 0x00);
+		break;
+	case SAA7127_WSS_ENABLE:
+		TRACE(3, "Enable Wide Screen Signal");
+		saa7127_writereg(client, 0x27, 0x80);
+		break;
+	default:
+		return (-EINVAL);
+	}
+	return (0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_wss_mode(struct i2c_client *client)
+{
+	struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client);
+
+	ENTER;
+
+	switch (encoder->wss_mode) {
+	case SAA7127_WSS_MODE_4_3_FULL_FORMAT:
+		TRACE(3, "Widescreen Mode 4:3 Full Format");
+		saa7127_writereg(client, 0x26, 0x08);
+		break;
+	case SAA7127_WSS_MODE_BOX_14_9_C:
+		TRACE(3, "Widescreen Mode Box 14:9 Center");
+		saa7127_writereg(client, 0x26, 0x01);
+		break;
+	case SAA7127_WSS_MODE_BOX_14_9_TOP:
+		TRACE(3, "Widescreen Mode Box 14:9 Top");
+		saa7127_writereg(client, 0x26, 0x02);
+		break;
+	case SAA7127_WSS_MODE_BOX_16_9_C:
+		TRACE(3, "Widescreen Mode Box 16:9 Center");
+		saa7127_writereg(client, 0x26, 0x0b);
+		break;
+	case SAA7127_WSS_MODE_BOX_16_9_TOP:
+		TRACE(3, "Widescreen Mode Box 16:9 Top");
+		saa7127_writereg(client, 0x26, 0x04);
+		break;
+	case SAA7127_WSS_MODE_SMALL_BOX_16_9_C:
+		TRACE(3, "Widescreen Mode Small Box 16:9 Center");
+		saa7127_writereg(client, 0x26, 0x0d);
+		break;
+	case SAA7127_WSS_MODE_4_3_14_9_FULL_FORMAT:
+		TRACE(3, "Widescreen Mode 14:9 Full Format");
+		saa7127_writereg(client, 0x26, 0x0e);
+		break;
+	case SAA7127_WSS_MODE_16_9_ANAMORPHIC:
+		TRACE(3, "Widescreen Mode 16:9 Full Format");
+		saa7127_writereg(client, 0x26, 0x07);
+		break;
+	default:
+		RETURN(-EINVAL);
+	}
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_enable(struct i2c_client *client)
+{
+	struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client);
+
+	ENTER;
+
+	switch (encoder->enable) {
+	case SAA7127_DISABLE:
+		TRACE(3, "Disable Video Output");
+		saa7127_writereg(client, 0x2d, (encoder->reg_2d & 0xf0));
+		saa7127_writereg(client, 0x61, (encoder->reg_61 | 0xc0));
+		break;
+	case SAA7127_ENABLE:
+		TRACE(3, "Enable Video Output");
+		saa7127_writereg(client, 0x2d, encoder->reg_2d);
+		saa7127_writereg(client, 0x61, encoder->reg_61);
+		break;
+	default:
+		RETURN(-EINVAL);
+	}
+
+#if 0
+	int j;
+	for (j = 0; j < 128 / 16; j++) {
+		TRACE(3,
+		      "saa7127 registers 0x%02x-0x%02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		      j * 16, j * 16 + 15, saa7127_read(client, j * 16 + 0),
+		      saa7127_read(client, j * 16 + 1), saa7127_read(client,
+								     j * 16 +
+								     2),
+		      saa7127_read(client, j * 16 + 3), saa7127_read(client,
+								     j * 16 +
+								     4),
+		      saa7127_read(client, j * 16 + 5), saa7127_read(client,
+								     j * 16 +
+								     6),
+		      saa7127_read(client, j * 16 + 7), saa7127_read(client,
+								     j * 16 +
+								     8),
+		      saa7127_read(client, j * 16 + 9), saa7127_read(client,
+								     j * 16 +
+								     10),
+		      saa7127_read(client, j * 16 + 11), saa7127_read(client,
+								      j * 16 +
+								      12),
+		      saa7127_read(client, j * 16 + 13), saa7127_read(client,
+								      j * 16 +
+								      14),
+		      saa7127_read(client, j * 16 + 15));
+	}
+#endif
+
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_norm(struct i2c_client *client)
+{
+	struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client);
+	const struct i2c_reg_value *inittab;
+
+	ENTER;
+
+	switch (encoder->norm) {
+	case SAA7127_VIDEO_NORM_NTSC:
+		TRACE(3, "Selecting NTSC video Standard");
+		inittab = saa7127_init_config_ntsc;
+		encoder->reg_61 = SAA7127_NTSC_DAC_CONTROL;
+		break;
+	case SAA7127_VIDEO_NORM_PAL:
+		TRACE(3, "Selecting PAL video Standard");
+		inittab = saa7127_init_config_pal;
+		encoder->reg_61 = SAA7127_PAL_DAC_CONTROL;
+		break;
+	default:
+		RETURN(-EINVAL);
+	}
+
+	/* Write Table */
+	saa7127_write_inittab(client, inittab);
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_output_type(struct i2c_client *client)
+{
+	struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client);
+
+	ENTER;
+
+	encoder->reg_3a = 0x13;	// by default swithch YUV to RGB-matrix on
+
+	switch (encoder->output_type) {
+	case SAA7127_OUTPUT_TYPE_RGB:
+		TRACE(3, "Selecting RGB Output Type");
+		encoder->reg_2d = 0x0f;	// RGB + CVBS (for sync)
+		break;
+	case SAA7127_OUTPUT_TYPE_COMPOSITE:
+		TRACE(3, "Selecting Composite Output Type");
+		encoder->reg_2d = 0x08;	// 00001000 CVBS only, RGB DAC's off (high impedance mode) !!!
+		break;
+	case SAA7127_OUTPUT_TYPE_SVIDEO:
+		TRACE(3, "Selecting S-Video Output Type");
+		encoder->reg_2d = 0xff;	// 11111111  croma -> R, luma -> CVBS + G + B
+		break;
+	case SAA7127_OUTPUT_TYPE_YUV_V:
+		TRACE(3, "Selecting YUV V Output Type");
+		encoder->reg_2d = 0x4f;	// reg 2D = 01001111, all DAC's on, RGB + VBS
+		encoder->reg_3a = 0x0b;	// reg 3A = 00001011, bypass RGB-matrix
+		break;
+	case SAA7127_OUTPUT_TYPE_YUV_C:
+		TRACE(3, "Selecting YUV C Output Type");
+		encoder->reg_2d = 0x0f;	// reg 2D = 00001111, all DAC's on, RGB + CVBS
+		encoder->reg_3a = 0x0b;	// reg 3A = 00001011, bypass RGB-matrix
+		break;
+	default:
+		RETURN(-EINVAL);
+	}
+
+	/* Configure Encoder */
+
+	saa7127_writereg(client, 0x2d, encoder->reg_2d);
+	saa7127_writereg(client, 0x3a,
+			 (encoder->input_type ==
+			  SAA7127_INPUT_TYPE_TEST_IMAGE) ? 0x80 : encoder->
+			 reg_3a);
+
+	RETURN(0);
+}
+
+static int saa7127_set_input_type(struct i2c_client *client)
+{
+	struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client);
+
+	ENTER;
+
+	switch (encoder->input_type) {
+	case SAA7127_INPUT_TYPE_NORMAL:	/* avia */
+		TRACE(3, "Selecting Normal Encoder Input");
+		saa7127_writereg(client, 0x3a, encoder->reg_3a);
+		break;
+	case SAA7127_INPUT_TYPE_TEST_IMAGE:	/* color bar */
+		TRACE(3, "Selecting Colour Bar generator");
+		saa7127_writereg(client, 0x3a, 0x80);
+		break;
+	default:
+		RETURN(-EINVAL);
+	}
+
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_command(struct i2c_client *client,
+			   unsigned int cmd, void *parg)
+{
+	struct saa7127 *encoder = i2c_get_clientdata(client);
+	unsigned long arg = (unsigned long)parg;
+	struct video_encoder_capability *cap = parg;
+	ENTER;
+	printk("saa7127_command: entered with cmd = %d\n", cmd);
+
+	switch (cmd) {
+	case ENCODER_GET_CAPABILITIES:
+		TRACE(3, "Asking Encoder Capabilities");
+		cap->flags = VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC;
+		cap->inputs = 1;
+		cap->outputs = 1;
+		break;
+
+	case ENCODER_SET_NORM:
+		TRACE(3, "Setting Encoder Video Standard");
+		switch (arg) {
+		case VIDEO_MODE_NTSC:
+			TRACE(3, "Set NTSC Video Mode");
+			encoder->norm = SAA7127_VIDEO_NORM_NTSC;
+			break;
+		case VIDEO_MODE_PAL:
+			TRACE(3, "Set PAL Video Mode");
+			encoder->norm = SAA7127_VIDEO_NORM_PAL;
+			break;
+		default:
+			return (-EINVAL);
+		}
+		saa7127_set_norm(client);
+		break;
+
+	case ENCODER_SET_INPUT:
+		TRACE(3, "Setting Encoder Input");
+		switch (arg) {
+		case SAA7127_INPUT_NORMAL:	/* encoder input selected */
+			TRACE(3, "Select Normal input");
+			encoder->input_type = SAA7127_INPUT_TYPE_NORMAL;
+			break;
+		case SAA7127_INPUT_TESTIMAGE:	/* Internal colourbars selected */
+			TRACE(3, "Select ColourBar Generator");
+			encoder->input_type = SAA7127_INPUT_TYPE_TEST_IMAGE;
+			break;
+		default:
+			RETURN(-EINVAL);
+		}
+		saa7127_set_input_type(client);
+		break;
+
+	case ENCODER_SET_OUTPUT:
+		TRACE(3, "Setting Encoder Output");
+		encoder->output_type = arg;
+		saa7127_set_output_type(client);
+		break;
+
+	case ENCODER_ENABLE_OUTPUT:
+		TRACE(3, "Turn on/off Output");
+		switch (arg) {
+		case SAA7127_VIDEO_ENABLE:
+			TRACE(3, "Turn on Video Output");
+			encoder->enable = SAA7127_ENABLE;
+			break;
+		case SAA7127_VIDEO_DISABLE:
+			TRACE(3, "Turn off Video Output");
+			encoder->enable = SAA7127_DISABLE;
+			break;
+		default:
+			RETURN(-EINVAL);
+		}
+		saa7127_set_enable(client);
+		break;
+
+	default:
+		RETURN(-EINVAL);
+	}
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static unsigned short normal_i2c[] =
+    { I2C_SAA7127_ADRESS >> 1, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+static int saa7127_i2c_id = 0;
+struct i2c_driver i2c_driver_saa7127;
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_detect_client(struct i2c_adapter *adapter,
+				 int address, int kind)
+{
+	struct i2c_client *client;
+	struct saa7127 *encoder;
+	int read_result = 0;
+
+	ENTER;
+
+	TRACE(1, "detecting saa7127 client on address 0x%x", address << 1);
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return (0);
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == 0)
+		return (-ENOMEM);
+
+	memset(client, 0, sizeof(struct i2c_client));
+	client->addr = address;
+	client->adapter = adapter;
+	client->driver = &i2c_driver_saa7127;
+	client->flags = I2C_CLIENT_ALLOW_USE;
+	client->id = saa7127_i2c_id++;
+	snprintf(client->name, sizeof(client->name) - 1, "saa7127[%d]",
+		 client->id);
+
+	encoder = kmalloc(sizeof(struct saa7127), GFP_KERNEL);
+
+	if (encoder == NULL) {
+		kfree(client);
+		return (-ENOMEM);
+	}
+
+	i2c_set_clientdata(client, encoder);
+	memset(encoder, 0, sizeof(struct saa7127));
+
+	/* Initialize default values */
+	encoder->output_type = output_select;
+	encoder->wss_enable = SAA7127_WSS_DISABLE;
+	encoder->wss_mode = SAA7127_WSS_MODE_4_3_FULL_FORMAT;
+
+	/* Look if the pal module parameter is set */
+
+	if (pal == 1) {
+		/* Select PAL Video Standard */
+		encoder->norm = SAA7127_VIDEO_NORM_PAL;
+	} else {
+		/* Select NTSC Video Standard, default */
+		encoder->norm = SAA7127_VIDEO_NORM_NTSC;
+	}
+
+	/* Look if the Encoder needs to be enabled */
+
+	if (enable_output == 1) {
+		encoder->enable = SAA7127_ENABLE;
+	} else {
+		/* for default disable output */
+		/* Because the MPEG DECODER is not initialised */
+		encoder->enable = SAA7127_DISABLE;
+	}
+
+	/* The Encoder is does have internal Colourbar generator */
+	/* This can be used for debugging, configuration values for the encoder */
+
+	if (test_image == 1) {
+		/* Select ColourBar Generator */
+		encoder->input_type = SAA7127_INPUT_TYPE_TEST_IMAGE;
+	} else {
+		/* Select normal input */
+		encoder->input_type = SAA7127_INPUT_TYPE_NORMAL;
+	}
+
+	TRACE(2, "writing init values");
+
+	/* Configure Encoder */
+
+	printk("saa7127: Configuring encoder...");
+	saa7127_write_inittab(client, saa7127_init_config_common);
+	saa7127_set_norm(client);
+	saa7127_set_output_type(client);
+	saa7127_set_wss(client);
+	saa7127_set_wss_mode(client);
+	saa7127_set_input_type(client);
+	saa7127_set_enable(client);
+
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(2 * HZ);
+
+	read_result = saa7127_read(client, 0x00);
+
+	TRACE(4, "Read status register (00h) : 0x%02x ", read_result);
+
+	i2c_attach_client(client);
+
+	RETURN(0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_attach_adapter(struct i2c_adapter *adapter)
+{
+
+	TRACE(2, "starting probe for adapter %s (0x%x)", adapter->name,
+	      adapter->id);
+	return (i2c_probe(adapter, &addr_data, &saa7127_detect_client));
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_detach_client(struct i2c_client *client)
+{
+	struct saa7127 *encoder = i2c_get_clientdata(client);
+	int err;
+
+	/* Turn off TV output */
+
+	encoder->enable = SAA7127_DISABLE;
+	saa7127_set_enable(client);
+
+	err = i2c_detach_client(client);
+
+	if (err) {
+		return (err);
+	}
+
+	kfree(encoder);
+	kfree(client);
+	return (0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct i2c_driver i2c_driver_saa7127 = {
+	.name = "saa7127",
+	.id = I2C_DRIVERID_SAA7127,
+	.flags = I2C_DF_NOTIFY,
+	.attach_adapter = saa7127_attach_adapter,
+	.detach_client = saa7127_detach_client,
+	.command = saa7127_command,
+	.owner = THIS_MODULE,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int __init saa7127_init(void)
+{
+	INFO("SAA7127 video encoder driver loaded");
+	TRACE(1, "Driver version: V %s", SAA7127_DRIVER_VERSION);
+	return (i2c_add_driver(&i2c_driver_saa7127));
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void __exit saa7127_exit(void)
+{
+
+	INFO("SAA7127 video encoder driver unloaded");
+	i2c_del_driver(&i2c_driver_saa7127);
+}
+
+/* ----------------------------------------------------------------------- */
+
+module_init(saa7127_init);
+module_exit(saa7127_exit);
+
+MODULE_DESCRIPTION("Philips SAA7127 video encoder driver");
+MODULE_AUTHOR("Roy Bulter");
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug_mask, "i");
+MODULE_PARM(test_image, "i");
+MODULE_PARM(pal, "i");
+MODULE_PARM(enable_output, "i");
+MODULE_PARM(output_select, "i");
+MODULE_PARM_DESC(debug_mask, "debug_mask (0-8192) ");
+MODULE_PARM_DESC(test_image, "test_image (0-1) ");
+MODULE_PARM_DESC(pal, "pal (0-1) ");
+MODULE_PARM_DESC(enable_output, "enable_output (0-1) ");
+MODULE_PARM_DESC(output_select,
+		 "output_select (0 = composite, 1 = s-video, 2 = rgb, 3 = YUVc, 4 = YUVv)");
diff -upN /dev/null current/drivers/media/video/saa7127.h
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/saa7127.h	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,154 @@
+#ifndef _SAA7127_H
+#define _SAA7127_H
+
+/*
+ **********************************************************************
+ *
+ * Define's
+ *
+ *
+ **********************************************************************
+ */
+
+
+#define SAA7127_DRIVER_VERSION "0.3"
+
+
+#ifndef I2C_DRIVERID_SAA7127
+  #warning Using temporary hack for missing I2C driver-ID for saa7127
+  #define I2C_DRIVERID_SAA7127 I2C_DRIVERID_EXP2
+#endif
+
+
+#define I2C_SAA7127_ADRESS  0x88
+
+#define SAA7127_VIDEO_ENABLE        0x01
+#define SAA7127_VIDEO_DISABLE       0x00
+
+#define SAA7127_INPUT_TESTIMAGE     0x01
+#define SAA7127_INPUT_NORMAL        0x00
+
+/*
+ * SAA7127 registers
+ */
+
+#define SAA7127_REG_STATUS                           0x00
+/* (registers 0x01-0x25 unused.) */
+#define SAA7127_REG_WIDESCREEN_CONFIG                0x26
+#define SAA7127_REG_WIDESCREEN_ENABLE                0x27
+#define SAA7127_REG_BURST_START                      0x28
+#define SAA7127_REG_BURST_END                        0x29
+#define SAA7127_REG_COPYGEN_0                        0x2a
+#define SAA7127_REG_COPYGEN_1                        0x2b
+#define SAA7127_REG_COPYGEN_2                        0x2c
+#define SAA7127_REG_OUTPUT_PORT_CONTROL              0x2d
+/* (registers 0x2e-0x37 unused.) */
+#define SAA7127_REG_GAIN_LUMINANCE_RGB               0x38
+#define SAA7127_REG_GAIN_COLORDIFF_RGB               0x39
+#define SAA7127_REG_INPUT_PORT_CONTROL_1             0x3A
+/* (registers 0x3b-0x53 undefined) */
+#define SAA7127_REG_CHROMA_PHASE                     0x5A
+#define SAA7127_REG_GAINU                            0x5B
+#define SAA7127_REG_GAINV                            0x5C
+#define SAA7127_REG_BLACK_LEVEL                      0x5D
+#define SAA7127_REG_BLANKING_LEVEL                   0x5E
+#define SAA7127_REG_VBI_BLANKING                     0x5F
+/* (register 0x60 unused) */
+#define SAA7127_REG_DAC_CONTROL                      0x61
+#define SAA7127_REG_BURST_AMP                        0x62
+#define SAA7127_REG_SUBC3                            0x63
+#define SAA7127_REG_SUBC2                            0x64
+#define SAA7127_REG_SUBC1                            0x65
+#define SAA7127_REG_SUBC0                            0x66
+#define SAA7127_REG_LINE_21_ODD_0                    0x67
+#define SAA7127_REG_LINE_21_ODD_1                    0x68
+#define SAA7127_REG_LINE_21_EVEN_0                   0x69
+#define SAA7127_REG_LINE_21_EVEN_1                   0x6A
+#define SAA7127_REG_RCV_PORT_CONTROL                 0x6B
+#define SAA7127_REG_VTRIG                            0x6C
+#define SAA7127_REG_HTRIG_HI                         0x6D
+#define SAA7127_REG_MULTI                            0x6E
+#define SAA7127_REG_CLOSED_CAPTION                   0x6F
+#define SAA7127_REG_RCV2_OUTPUT_START                0x70
+#define SAA7127_REG_RCV2_OUTPUT_END                  0x71
+#define SAA7127_REG_RCV2_OUTPUT_MSBS                 0x72
+#define SAA7127_REG_TTX_REQUEST_H_START              0x73
+#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH       0x74
+#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT        0x75
+#define SAA7127_REG_TTX_ODD_REQ_VERT_START           0x76
+#define SAA7127_REG_TTX_ODD_REQ_VERT_END             0x77
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_START          0x78
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_END            0x79
+#define SAA7127_REG_FIRST_ACTIVE                     0x7A
+#define SAA7127_REG_LAST_ACTIVE                      0x7B
+#define SAA7127_REG_MSB_VERTICAL                     0x7C
+/* (register 0x7d unused) */
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_0            0x7E
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_1            0x7F
+
+
+
+/*
+ **********************************************************************
+ *  
+ *  Enumurations
+ *
+ **********************************************************************
+ */
+
+
+/* Enumeration for the Video Standard */
+
+
+enum SAA7127_video_norm       {
+                                SAA7127_VIDEO_NORM_NTSC,
+                                SAA7127_VIDEO_NORM_PAL
+                              };
+
+
+/* Enumeration for the Supported input types */
+
+enum SAA7127_input_type      {
+                                SAA7127_INPUT_TYPE_NORMAL,
+                                SAA7127_INPUT_TYPE_TEST_IMAGE
+                             };
+
+
+/* Enumeration for the Supported Output signal types */
+
+enum SAA7127_output_type      {
+                                SAA7127_OUTPUT_TYPE_COMPOSITE,
+                                SAA7127_OUTPUT_TYPE_SVIDEO,
+                                SAA7127_OUTPUT_TYPE_RGB,
+                                SAA7127_OUTPUT_TYPE_YUV_C,
+                                SAA7127_OUTPUT_TYPE_YUV_V
+                              };
+
+/* Enumeration for the enable/disabeling the output signal */
+
+enum SAA7127_enable_type      {
+                                SAA7127_DISABLE,
+                                SAA7127_ENABLE
+                              };
+/* Enumeration for the turning on/off the Wide screen signal for Wide screen TV */
+
+enum SAA7127_wss_enable_type  {
+                                SAA7127_WSS_DISABLE,
+                                SAA7127_WSS_ENABLE
+                              };
+
+/* Enumeration for the selecting the different Wide screen mode */
+
+enum SAA7127_wss_mode_type    {
+                                SAA7127_WSS_MODE_4_3_FULL_FORMAT,           /* full format 4:3 */
+                                SAA7127_WSS_MODE_BOX_14_9_C,           /* box 14:9 c */
+                                SAA7127_WSS_MODE_BOX_14_9_TOP,              /* box 14:9 top */
+                                SAA7127_WSS_MODE_BOX_16_9_C,           /* box 16:9 c */
+                                SAA7127_WSS_MODE_BOX_16_9_TOP,              /* box 16:9 top */
+                                SAA7127_WSS_MODE_SMALL_BOX_16_9_C,     /* box > 16:9 c */
+                                SAA7127_WSS_MODE_4_3_14_9_FULL_FORMAT,      /* full format 4:3 with 14:9 c letterbox content */
+                                SAA7127_WSS_MODE_16_9_ANAMORPHIC            /* full format 16:9 (anamorphic) */
+                              };
+
+
+#endif // _SAA7127_H
diff -upN /dev/null current/drivers/media/video/tveeprom.c
--- /dev/null	2004-02-24 15:23:11.000000000 -0800
+++ current/drivers/media/video/tveeprom.c	2004-03-30 21:42:12.000000000 -0800
@@ -0,0 +1,546 @@
+/* 
+ * tveeprom - eeprom decoder for tvcard configuration eeproms
+ *
+ * Data and decoding routines shamelessly borrowed from bttv-cards.c
+ * eeprom access routine shamelessly borrowed from bttv-if.c
+ * which are:
+
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+                           & Marcus Metzler (mocm@thp.uni-koeln.de)
+    (c) 1999-2001 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+ * Adjustments to fit a more general model and all bugs:
+ 
+ 	Copyright (C) 2003 John Klar <linpvr at projectplasma.com>
+
+ * 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/version.h>
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/init.h>
+
+#include <linux/slab.h>
+
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+
+#include <linux/kmod.h>
+#include <linux/init.h>
+
+#include <linux/videodev.h>
+
+#include <linux/version.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("i2c eeprom decoder driver");
+MODULE_AUTHOR("John Klar");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#include <media/tuner.h>
+
+#ifndef I2C_DRIVERID_TVEEPROM
+#warning Using temporary hack for missing I2C driver-ID for tveeprom
+#define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2
+#endif
+
+static int debug = 1;
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+static int verbose = 0;
+MODULE_PARM(verbose, "i");
+MODULE_PARM_DESC(verbose, "Verbose level (0-1)");
+
+#define STRM(array,i) (i < sizeof(array)/sizeof(char*) ? array[i] : "unknown")
+
+#define dprintk(num, format, args...) \
+	do { \
+		if (debug >= num) \
+			printk(format, ##args); \
+	} while (0)
+
+/* ----------------------------------------------------------------------- */
+
+static unsigned char eeprom_buf[256];
+
+struct tveeprom {
+	u32 has_radio;
+
+	u32 tuner_type;
+	u32 tuner_formats;
+
+	u32 digitizer;
+	u32 digitizer_formats;
+
+	u32 audio_processor;
+	/* a_p_fmts? */
+
+	u32 model;
+	u32 revision;
+	u32 serial_number;
+	char rev_str[5];
+};
+
+#define   I2C_TVEEPROM        0xA0
+#define   I2C_TVEEPROMA       0xA0
+
+#define   I2C_DELAY   10
+
+#define REG_ADDR(x) (((x) << 1) + 1)
+#define LOBYTE(x) ((unsigned char)((x) & 0xff))
+#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff))
+#define LOWORD(x) ((unsigned short int)((x) & 0xffff))
+#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff))
+
+/* ----------------------------------------------------------------------- */
+/* some hauppauge specific stuff                                           */
+
+static struct HAUPPAUGE_TUNER_FMT {
+	int id;
+	char *name;
+} hauppauge_tuner_fmt[] __devinitdata = {
+	{
+	0x00000000, "unknown1"}, {
+	0x00000000, "unknown2"}, {
+	0x00000007, "PAL(B/G)"}, {
+	0x00001000, "NTSC(M)"}, {
+	0x00000010, "PAL(I)"}, {
+	0x00400000, "SECAM(L/L´)"}, {
+	0x00000e00, "PAL(D/K)"}, {
+0x03000000, "ATSC Digital"},};
+
+static struct HAUPPAUGE_TUNER {
+	int id;
+	char *name;
+} hauppauge_tuner[] __devinitdata = {
+	{
+	TUNER_ABSENT, ""}, {
+	TUNER_ABSENT, "External"}, {
+	TUNER_ABSENT, "Unspecified"}, {
+	TUNER_PHILIPS_PAL, "Philips FI1216"}, {
+	TUNER_PHILIPS_SECAM, "Philips FI1216MF"}, {
+	TUNER_PHILIPS_NTSC, "Philips FI1236"}, {
+	TUNER_PHILIPS_PAL_I, "Philips FI1246"}, {
+	TUNER_PHILIPS_PAL_DK, "Philips FI1256"}, {
+	TUNER_PHILIPS_PAL, "Philips FI1216 MK2"}, {
+	TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2"}, {
+	TUNER_PHILIPS_NTSC, "Philips FI1236 MK2"}, {
+	TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2"}, {
+	TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2"}, {
+	TUNER_TEMIC_NTSC, "Temic 4032FY5"}, {
+	TUNER_TEMIC_PAL, "Temic 4002FH5"}, {
+	TUNER_TEMIC_PAL_I, "Temic 4062FY5"}, {
+	TUNER_PHILIPS_PAL, "Philips FR1216 MK2"}, {
+	TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2"}, {
+	TUNER_PHILIPS_NTSC, "Philips FR1236 MK2"}, {
+	TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2"}, {
+	TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2"}, {
+	TUNER_PHILIPS_PAL, "Philips FM1216"}, {
+	TUNER_PHILIPS_SECAM, "Philips FM1216MF"}, {
+	TUNER_PHILIPS_NTSC, "Philips FM1236"}, {
+	TUNER_PHILIPS_PAL_I, "Philips FM1246"}, {
+	TUNER_PHILIPS_PAL_DK, "Philips FM1256"}, {
+	TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5"}, {
+	TUNER_ABSENT, "Samsung TCPN9082D"}, {
+	TUNER_ABSENT, "Samsung TCPM9092P"}, {
+	TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5"}, {
+	TUNER_ABSENT, "Samsung TCPN9085D"}, {
+	TUNER_ABSENT, "Samsung TCPB9085P"}, {
+	TUNER_ABSENT, "Samsung TCPL9091P"}, {
+	TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5"}, {
+	TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME"}, {
+	TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5"}, {
+	TUNER_ABSENT, "Philips TD1536"}, {
+	TUNER_ABSENT, "Philips TD1536D"}, {
+	TUNER_PHILIPS_NTSC, "Philips FMR1236"},	/* mono radio */
+	{
+	TUNER_ABSENT, "Philips FI1256MP"}, {
+	TUNER_ABSENT, "Samsung TCPQ9091P"}, {
+	TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5"}, {
+	TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5"}, {
+	TUNER_TEMIC_4046FM5, "Temic 4046FM5"}, {
+	TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5"}, {
+	TUNER_ABSENT, "Philips TD1536D_FH_44"}, {
+	TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, {
+	TUNER_LG_PAL_FM, "LG TP18PSB01D"}, {
+	TUNER_LG_PAL, "LG TP18PSB11D"}, {
+	TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, {
+	TUNER_LG_PAL_I, "LG TAPC-I701D"}
+};
+
+static char *sndtype[] = {
+	"None", "TEA6300", "TEA6320", "TDA9850", "MSP3400C", "MSP3410D",
+	"MSP3415", "MSP3430", "MSP3438", "CS5331", "MSP3435", "MSP3440",
+	"MSP3445", "MSP3411", "MSP3416", "MSP3425",
+
+	"Type 0x10", "Type 0x11", "Type 0x12", "Type 0x13",
+	"Type 0x14", "Type 0x15", "Type 0x16", "Type 0x17",
+	"Type 0x18", "MSP4418", "Type 0x1a", "MSP4448",
+	"Type 0x1c", "Type 0x1d", "Type 0x1e", "Type 0x1f",
+};
+
+static void __devinit hauppauge_eeprom(struct tveeprom *tvee,
+				       unsigned char *eeprom_data)
+{
+
+	/* ----------------------------------------------
+	 ** The hauppauge eeprom format is tagged
+	 **
+	 ** if packet[0] == 0x84, then packet[0..1] == length
+	 ** else length = packet[0] & 3f;
+	 ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum
+	 **
+	 ** In our (ivtv) case we're interested in the following:
+	 ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuners)
+	 ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into hauppauge_fmts)
+	 ** radio:      tag [00].{last} or [0e].00  (bitmask.  bit2=FM)
+	 ** audio proc: tag [02].01 or [05].00 (lower nibble indexes lut?)
+
+	 ** Fun info:
+	 ** model:      tag [00].07-08 or [06].00-01
+	 ** revision:   tag [00].09-0b or [06].04-06
+	 ** serial#:    tag [01].05-07 or [04].04-06
+
+	 ** # of inputs/outputs ???
+	 */
+
+	int i, j, len, done, tag, tuner = 0, t_format = 0;
+	char *t_name = NULL, *t_fmt_name = NULL;
+
+	tvee->revision = done = len = 0;
+	for (i = 0; !done && i < 256; i += len) {
+
+		dprintk(2,
+			KERN_INFO
+			"tvee: processing pos=%02x (%02x,%02x)\n",
+			i, eeprom_data[i], eeprom_data[i + 1]);
+
+		if (eeprom_data[i] == 0x84) {
+			len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
+			i += 3;
+		} else if ((eeprom_data[i] & 0xf0) == 0x70) {
+			if ((eeprom_data[i] & 0x08)) {
+				/* verify checksum! */
+				done = 1;
+				break;
+			}
+			len = eeprom_data[i] & 0x07;
+			++i;
+		} else {
+			printk(KERN_WARNING
+			       "Encountered bad packet header [%02x].  "
+			       "Corrupt or not a Hauppauge eeprom.\n",
+			       eeprom_data[i]);
+			return;
+		}
+
+		dprintk(1, KERN_INFO "%3d [%02x] ", len, eeprom_data[i]);
+		for (j = 1; j < len; j++) {
+			dprintk(1, "%02x ", eeprom_data[i + j]);
+		}
+		dprintk(1, "\n");
+
+		/* process by tag */
+		tag = eeprom_data[i];
+		switch (tag) {
+		case 0x00:
+			tuner = eeprom_data[i + 6];
+			t_format = eeprom_data[i + 5];
+			tvee->has_radio = eeprom_data[i + len - 1];
+			tvee->model =
+			    eeprom_data[i + 8] + (eeprom_data[i + 9] << 8);
+			tvee->revision = eeprom_data[i + 10] +
+			    (eeprom_data[i + 11] << 8) +
+			    (eeprom_data[i + 12] << 16);
+			break;
+		case 0x01:
+			tvee->serial_number =
+			    eeprom_data[i + 6] +
+			    (eeprom_data[i + 7] << 8) +
+			    (eeprom_data[i + 8] << 16);
+			break;
+		case 0x02:
+			tvee->audio_processor = eeprom_data[i + 2] & 0x0f;
+			break;
+		case 0x04:
+			tvee->serial_number =
+			    eeprom_data[i + 5] +
+			    (eeprom_data[i + 6] << 8) +
+			    (eeprom_data[i + 7] << 16);
+			break;
+		case 0x05:
+			tvee->audio_processor = eeprom_data[i + 1] & 0x0f;
+			break;
+		case 0x06:
+			tvee->model =
+			    eeprom_data[i + 1] + (eeprom_data[i + 2] << 8);
+			tvee->revision = eeprom_data[i + 5] +
+			    (eeprom_data[i + 6] << 8) +
+			    (eeprom_data[i + 7] << 16);
+			break;
+		case 0x0a:
+			tuner = eeprom_data[i + 2];
+			t_format = eeprom_data[i + 1];
+			break;
+		case 0x0e:
+			tvee->has_radio = eeprom_data[i + 1];
+			break;
+		default:
+			printk(KERN_WARNING
+			       "Not sure what to do with tag [%02x]\n", tag);
+			/* dump the rest of the packet? */
+		}
+
+	}
+
+	if (!done) {
+		printk(KERN_WARNING "Ran out of data!\n");
+		return;
+	}
+
+	if (tvee->revision != 0) {
+		tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f);
+		tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f);
+		tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f);
+		tvee->rev_str[3] = 32 + (tvee->revision & 0x3f);
+		tvee->rev_str[4] = 0;
+	}
+
+	if (tuner < sizeof(hauppauge_tuner) / sizeof(struct HAUPPAUGE_TUNER)) {
+		tvee->tuner_type = hauppauge_tuner[tuner].id;
+		t_name = hauppauge_tuner[tuner].name;
+	} else {
+		t_name = "<unknown>";
+	}
+
+	tvee->tuner_formats = 0;
+	t_fmt_name = "<none>";
+	for (i = 0; i < 8; i++) {
+		if ((t_format & (1 << i))) {
+			tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
+			/* yuck */
+			t_fmt_name = hauppauge_tuner_fmt[i].name;
+		}
+	}
+
+#if 0
+	if (t_format <
+	    sizeof(hauppauge_tuner_fmt) / sizeof(struct HAUPPAUGE_TUNER_FMT)) {
+		tvee->tuner_formats = hauppauge_tuner_fmt[t_format].id;
+		t_fmt_name = hauppauge_tuner_fmt[t_format].name;
+	} else {
+		t_fmt_name = "<unknown>";
+	}
+#endif
+
+	printk(KERN_INFO "tvee: Hauppauge: model=%d, rev=%s, serial#=%d\n",
+	       tvee->model, tvee->rev_str, tvee->serial_number);
+	printk(KERN_INFO "tvee: tuner=%s (idx=%d, type=%d)\n",
+	       t_name, tuner, tvee->tuner_type);
+	printk(KERN_INFO "tvee: tuner fmt=%s (eeprom=0x%02x, v4l2=0x%08x)\n",
+	       t_fmt_name, t_format, tvee->tuner_formats);
+
+	printk(KERN_INFO "tvee: audio_processor=%s (type=%d)\n",
+	       STRM(sndtype, tvee->audio_processor), tvee->audio_processor);
+
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* write I2C */
+int tvee_I2CWrite(struct i2c_client *client,
+		  unsigned char b1, unsigned char b2, int both)
+{
+	unsigned char buffer[2];
+	int bytes = both ? 2 : 1;
+
+	buffer[0] = b1;
+	buffer[1] = b2;
+	if (bytes != i2c_master_send(client, buffer, bytes))
+		return -1;
+	return 0;
+}
+
+void __devinit tvee_readee(struct i2c_client *client, unsigned char *eedata)
+{
+	int i;
+
+	if (tvee_I2CWrite(client, 0, -1, 0) < 0) {
+		printk(KERN_WARNING "tvee: readee error\n");
+		return;
+	}
+
+	for (i = 0; i < 256; i += 16) {
+		if (16 != i2c_master_recv(client, eedata + i, 16)) {
+			printk(KERN_WARNING "tvee: readee error\n");
+			break;
+		}
+	}
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+tveeprom_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+
+	struct tveeprom *eeprom = i2c_get_clientdata(client);
+	u32 *eeprom_props = arg;
+
+	switch (cmd) {
+
+	case 0:
+		eeprom_props[0] = eeprom->tuner_type;
+		eeprom_props[1] = eeprom->tuner_formats;
+		eeprom_props[2] = eeprom->model;
+		eeprom_props[3] = eeprom->revision;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] =
+    { I2C_TVEEPROM >> 1, I2C_TVEEPROMA >> 1, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+static int tveeprom_i2c_id = 0;
+struct i2c_driver i2c_driver_tveeprom;
+
+static int
+tveeprom_detect_client(struct i2c_adapter *adapter, int address, int kind)
+{
+	struct i2c_client *client;
+	struct tveeprom *eeprom;
+
+	dprintk(1,
+		KERN_INFO
+		"tveeprom.c: detecting tveeprom client on address 0x%x\n",
+		address << 1);
+
+	/* Check if the adapter supports the needed features */
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == 0)
+		return -ENOMEM;
+	memset(client, 0, sizeof(struct i2c_client));
+	client->addr = address;
+	client->adapter = adapter;
+	client->driver = &i2c_driver_tveeprom;
+	client->flags = I2C_CLIENT_ALLOW_USE;
+	client->id = tveeprom_i2c_id++;
+	snprintf(client->name, sizeof(client->name) - 1, "tveeprom[%d]",
+		 client->id);
+
+	eeprom = kmalloc(sizeof(struct tveeprom), GFP_KERNEL);
+	if (eeprom == NULL) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(client, eeprom);
+	memset(eeprom, 0, sizeof(struct tveeprom));
+	eeprom->tuner_type = -1;
+	eeprom->has_radio = 0;
+	eeprom->model = 0;
+
+	tvee_readee(client, eeprom_buf);
+	hauppauge_eeprom(eeprom, eeprom_buf);
+
+	return 0;
+}
+
+static int tveeprom_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (adapter->id != (I2C_ALGO_BIT | I2C_HW_B_BT848))
+		return 0;
+	dprintk(1,
+		KERN_INFO
+		"tveeprom.c: starting probe for adapter %s (0x%x)\n",
+		adapter->name, adapter->id);
+	return i2c_probe(adapter, &addr_data, tveeprom_detect_client);
+}
+
+static int tveeprom_detach_client(struct i2c_client *client)
+{
+	struct tveeprom *eeprom = i2c_get_clientdata(client);
+	int err;
+
+	err = i2c_detach_client(client);
+	if (err) {
+		return err;
+	}
+
+	kfree(eeprom);
+	kfree(client);
+	return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* i2c implementation */
+struct i2c_driver i2c_driver_tveeprom = {
+	.name = "tveeprom",
+
+	.id = I2C_DRIVERID_TVEEPROM,
+	.flags = I2C_DF_NOTIFY,
+
+	.attach_adapter = tveeprom_attach_adapter,
+	.detach_client = tveeprom_detach_client,
+	.command = tveeprom_command,
+	.owner = THIS_MODULE,
+};
+
+static int __init tveeprom_init(void)
+{
+	return i2c_add_driver(&i2c_driver_tveeprom);
+}
+
+static void __exit tveeprom_exit(void)
+{
+	i2c_del_driver(&i2c_driver_tveeprom);
+}
+
+module_init(tveeprom_init);
+module_exit(tveeprom_exit);