Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb

* 'master' of master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (132 commits)
  V4L/DVB 4949b: Fix container_of pointer retreival
  V4L/DVB (4949a): Fix INIT_WORK
  V4L/DVB (4949): Cxusb: codingstyle cleanups
  V4L/DVB (4948): Cxusb: Convert tuner functions to use dvb_pll_attach
  V4L/DVB (4947): Cx88: trivial cleanups
  V4L/DVB (4946): Cx88: Move cx88_dvb_bus_ctrl out of the card-specific area
  V4L/DVB (4945): Cx88: consolidate cx22702_config structs
  V4L/DVB (4944): Cx88: Convert DViCO FusionHDTV Hybrid to use dvb_pll_attach
  V4L/DVB (4943): Cx88: cleanup dvb_pll_attach for lgdt3302 tuners
  V4L/DVB (4953): Usbvision minor fixes
  V4L/DVB (4951): Add version.h, since it is required for VIDIOC_QUERYCAP
  V4L/DVB (4940): Or51211: Changed SNR and signal strength calculations
  V4L/DVB (4939): Or51132: Changed SNR and signal strength reporting
  V4L/DVB (4938): Cx88: Convert lgdt3302 tuning function to use dvb_pll_attach
  V4L/DVB (4941): Remove LINUX_VERSION_CODE and fix identations
  V4L/DVB (4942): Whitespace cleanups
  V4L/DVB (4937): Usbvision cleanup and code reorganization
  V4L/DVB (4936): Make MT4049FM5 tuner to set FM Gain to Normal
  V4L/DVB (4935): Added the capability of selecting fm gain by tuner
  V4L/DVB (4934): Usbvision radio requires GainNormal at e register
  ...
diff --git a/Documentation/dvb/cards.txt b/Documentation/dvb/cards.txt
index ca58e33..cc09187 100644
--- a/Documentation/dvb/cards.txt
+++ b/Documentation/dvb/cards.txt
@@ -22,10 +22,10 @@
    - ves1x93		: Alps BSRV2 (ves1893 demodulator) and dbox2 (ves1993)
    - cx24110		: Conexant HM1221/HM1811 (cx24110 or cx24106 demod, cx24108 PLL)
    - grundig_29504-491	: Grundig 29504-491 (Philips TDA8083 demodulator), tsa5522 PLL
-   - mt312		: Zarlink mt312 or Mitel vp310 demodulator, sl1935 or tsa5059 PLL
+   - mt312		: Zarlink mt312 or Mitel vp310 demodulator, sl1935 or tsa5059 PLLi, Technisat Sky2Pc with bios Rev. 2.3
    - stv0299		: Alps BSRU6 (tsa5059 PLL), LG TDQB-S00x (tsa5059 PLL),
 			  LG TDQF-S001F (sl1935 PLL), Philips SU1278 (tua6100 PLL),
-			  Philips SU1278SH (tsa5059 PLL), Samsung TBMU24112IMB
+			  Philips SU1278SH (tsa5059 PLL), Samsung TBMU24112IMB, Technisat Sky2Pc with bios Rev. 2.6
   DVB-C:
    - ves1820		: various (ves1820 demodulator, sp5659c or spXXXX PLL)
    - at76c651		: Atmel AT76c651(B) with DAT7021 PLL
diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
index 8755b3e..62e32b4 100644
--- a/Documentation/video4linux/CARDLIST.cx88
+++ b/Documentation/video4linux/CARDLIST.cx88
@@ -43,7 +43,7 @@
  42 -> digitalnow DNTV Live! DVB-T Pro                     [1822:0025,1822:0019]
  43 -> KWorld/VStream XPert DVB-T with cx22702             [17de:08a1,12ab:2300]
  44 -> DViCO FusionHDTV DVB-T Dual Digital                 [18ac:db50,18ac:db54]
- 45 -> KWorld HardwareMpegTV XPert                         [17de:0840]
+ 45 -> KWorld HardwareMpegTV XPert                         [17de:0840,1421:0305]
  46 -> DViCO FusionHDTV DVB-T Hybrid                       [18ac:db40,18ac:db44]
  47 -> pcHDTV HD5500 HDTV                                  [7063:5500]
  48 -> Kworld MCE 200 Deluxe                               [17de:0841]
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index 53ce6a3..f6201cc 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -76,7 +76,7 @@
  75 -> AVerMedia AVerTVHD MCE A180              [1461:1044]
  76 -> SKNet MonsterTV Mobile                   [1131:4ee9]
  77 -> Pinnacle PCTV 40i/50i/110i (saa7133)     [11bd:002e]
- 78 -> ASUSTeK P7131 Dual                       [1043:4862]
+ 78 -> ASUSTeK P7131 Dual                       [1043:4862,1043:4876]
  79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)
  80 -> ASUS Digimatrix TV                       [1043:0210]
  81 -> Philips Tiger reference design           [1131:2018]
@@ -99,3 +99,8 @@
  98 -> Proteus Pro 2309                         [0919:2003]
  99 -> AVerMedia TV Hybrid A16AR                [1461:2c00]
 100 -> Asus Europa2 OEM                         [1043:4860]
+101 -> Pinnacle PCTV 310i                       [11bd:002f]
+102 -> Avermedia AVerTV Studio 507              [1461:9715]
+103 -> Compro Videomate DVB-T200A
+104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid     [0070:6701]
+105 -> Terratec Cinergy HT PCMCIA               [153b:1172]
diff --git a/Documentation/video4linux/cafe_ccic b/Documentation/video4linux/cafe_ccic
new file mode 100644
index 0000000..8882102
--- /dev/null
+++ b/Documentation/video4linux/cafe_ccic
@@ -0,0 +1,54 @@
+"cafe_ccic" is a driver for the Marvell 88ALP01 "cafe" CMOS camera
+controller.  This is the controller found in first-generation OLPC systems,
+and this driver was written with support from the OLPC project.
+
+Current status: the core driver works.  It can generate data in YUV422,
+RGB565, and RGB444 formats.  (Anybody looking at the code will see RGB32 as
+well, but that is a debugging aid which will be removed shortly).  VGA and
+QVGA modes work; CIF is there but the colors remain funky.  Only the OV7670
+sensor is known to work with this controller at this time.
+
+To try it out: either of these commands will work:
+
+     mplayer tv:// -tv driver=v4l2:width=640:height=480 -nosound
+     mplayer tv:// -tv driver=v4l2:width=640:height=480:outfmt=bgr16 -nosound
+
+The "xawtv" utility also works; gqcam does not, for unknown reasons.
+
+There are a few load-time options, most of which can be changed after
+loading via sysfs as well:
+
+ - alloc_bufs_at_load:  Normally, the driver will not allocate any DMA
+   buffers until the time comes to transfer data.  If this option is set,
+   then worst-case-sized buffers will be allocated at module load time.
+   This option nails down the memory for the life of the module, but
+   perhaps decreases the chances of an allocation failure later on.
+
+ - dma_buf_size: The size of DMA buffers to allocate.  Note that this
+   option is only consulted for load-time allocation; when buffers are
+   allocated at run time, they will be sized appropriately for the current
+   camera settings.
+
+ - n_dma_bufs: The controller can cycle through either two or three DMA
+   buffers.  Normally, the driver tries to use three buffers; on faster
+   systems, however, it will work well with only two.
+
+ - min_buffers: The minimum number of streaming I/O buffers that the driver
+   will consent to work with.  Default is one, but, on slower systems,
+   better behavior with mplayer can be achieved by setting to a higher
+   value (like six).
+
+ - max_buffers: The maximum number of streaming I/O buffers; default is
+   ten.  That number was carefully picked out of a hat and should not be
+   assumed to actually mean much of anything.
+
+ - flip: If this boolean parameter is set, the sensor will be instructed to
+   invert the video image.  Whether it makes sense is determined by how
+   your particular camera is mounted.
+
+Work is ongoing with this driver, stay tuned.
+
+jon
+
+Jonathan Corbet
+corbet@lwn.net
diff --git a/Documentation/video4linux/zr36120.txt b/Documentation/video4linux/zr36120.txt
deleted file mode 100644
index 1a1c2d0..0000000
--- a/Documentation/video4linux/zr36120.txt
+++ /dev/null
@@ -1,162 +0,0 @@
-Driver for Trust Computer Products Framegrabber, version 0.6.1
------- --- ----- -------- -------- ------------  ------- - - -
-
-- ZORAN ------------------------------------------------------
- Author: Pauline Middelink <middelin@polyware.nl>
-   Date: 18 September 1999
-Version: 0.6.1
-
-- Description ------------------------------------------------
-
-Video4Linux compatible driver for an unknown brand framegrabber
-(Sold in the Netherlands by TRUST Computer Products) and various
-other zoran zr36120 based framegrabbers.
-
-The card contains a ZR36120 Multimedia PCI Interface and a Philips
-SAA7110 Onechip Frontend videodecoder. There is also an DSP of
-which I have forgotten the number, since i will never get that thing
-to work without specs from the vendor itself.
-
-The SAA711x are capable of processing 6 different video inputs,
-CVBS1..6 and Y1+C1, Y2+C2, Y3+C3. All in 50/60Hz, NTSC, PAL or
-SECAM and delivering a YUV datastream.  On my card the input
-'CVBS-0' corresponds to channel CVBS2 and 'S-Video' to Y2+C2.
-
-I have some reports of other cards working with the mentioned
-chip sets. For a list of other working cards please have a look
-at the cards named in the tvcards struct in the beginning of
-zr36120.c
-
-After some testing, I discovered that the carddesigner messed up
-on the I2C interface. The Zoran chip includes 2 lines SDA and SCL
-which (s)he connected reversely. So we have to clock on the SDA
-and r/w data on the SCL pin. Life is fun... Each cardtype now has
-a bit which signifies if you have a card with the same deficiency.
-
-Oh, for the completeness of this story I must mention that my
-card delivers the VSYNC pulse of the SAA chip to GIRQ1, not
-GIRQ0 as some other cards have. This is also incorporated in
-the driver be clearing/setting the 'useirq1' bit in the tvcard
-description.
-
-Another problems of continuous capturing data with a Zoran chip
-is something nasty inside the chip. It effectively halves the
-fps we ought to get... Here is the scenario: capturing frames
-to memory is done in the so-called snapshot mode. In this mode
-the Zoran stops after capturing a frame worth of data and wait
-till the application set GRAB bit to indicate readiness for the
-next frame. After detecting a set bit, the chip neatly waits
-till the start of a frame, captures it and it goes back to off.
-Smart ppl will notice the problem here. Its the waiting on the
-_next_ frame each time we set the GRAB bit... Oh well, 12,5 fps
-is still plenty fast for me.
--- update 28/7/1999 --
-Don't believe a word I just said... Proof is the output
-of `streamer -t 300 -r 25 -f avi15 -o /dev/null`
-	++--+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+-
-	syncer: done
-	writer: done
-(note the /dev/null is prudent here, my system is not able to
- grab /and/ write 25 fps to a file... gifts welcome :) )
-The technical reasoning follows: The zoran completed the last
-frame, the VSYNC goes low, and GRAB is cleared. The interrupt
-routine starts to work since its VSYNC driven, and again
-activates the GRAB bit. A few ms later the VSYNC (re-)rises and
-the zoran starts to work on a new and freshly broadcasted frame....
-
-For pointers I used the specs of both chips. Below are the URLs:
-  http://www.zoran.com/ftp/download/devices/pci/ZR36120/36120data.pdf
-  http://www-us.semiconductor.philips.com/acrobat/datasheets/SAA_7110_A_1.pdf
-Some alternatives for the Philips SAA 7110 datasheet are:
-  http://www.datasheetcatalog.com/datasheets_pdf/S/A/A/7/SAA7110.shtml
-  http://www.datasheetarchive.com/search.php?search=SAA7110&sType=part
-
-The documentation has very little on absolute numbers or timings
-needed for the various modes/resolutions, but there are other
-programs you can borrow those from.
-
------- Install --------------------------------------------
-Read the file called TODO.  Note its long list of limitations.
-
-Build a kernel with VIDEO4LINUX enabled. Activate the
-BT848 driver; we need this because we have need for the
-other modules (i2c and videodev) it enables.
-
-To install this software, extract it into a suitable directory.
-Examine the makefile and change anything you don't like.  Type "make".
-
-After making the modules check if you have the much needed
-/dev/video devices. If not, execute the following 4 lines:
-	mknod /dev/video c 81 0
-	mknod /dev/video1 c 81 1
-	mknod /dev/video2 c 81 2
-	mknod /dev/video3 c 81 3
-	mknod /dev/video4 c 81 4
-
-After making/checking the devices do:
-	modprobe i2c
-	modprobe videodev
-	modprobe saa7110	(optional)
-	modprobe saa7111	(optional)
-	modprobe tuner		(optional)
-	insmod zoran cardtype=<n>
-
-<n> is the cardtype of the card you have. The cardnumber can
-be found in the source of zr36120. Look for tvcards. If your
-card is not there, please try if any other card gives some
-response, and mail me if you got a working tvcard addition.
-
-PS. <TVCard editors behold!)
-    Don't forget to set video_input to the number of inputs
-    you defined in the video_mux part of the tvcard definition.
-    It's a common error to add a channel but not incrementing
-    video_input and getting angry with me/v4l/linux/linus :(
-
-You are now ready to test the framegrabber with your favorite
-video4linux compatible tool
-
------- Application ----------------------------------------
-
-This device works with all Video4Linux compatible applications,
-given the limitations in the TODO file.
-
------- API ------------------------------------------------
-
-This uses the V4L interface as of kernel release 2.1.116, and in
-fact has not been tested on any lower version.  There are a couple
-of minor differences due to the fact that the amount of data returned
-with each frame varies, and no doubt there are discrepancies due to my
-misunderstanding of the API.  I intend to convert this driver to the
-new V4L2 API when it has stabilized more.
-
------- Current state --------------------------------------
-
-The driver is capable of overlaying a video image in screen, and
-even capable of grabbing frames. It uses the BIGPHYSAREA patch
-to allocate lots of large memory blocks when tis patch is
-found in the kernel, but it doesn't need it.
-The consequence is that, when loading the driver as a module,
-the module may tell you it's out of memory, but 'free' says
-otherwise. The reason is simple; the modules wants its memory
-contiguous, not fragmented, and after a long uptime there
-probably isn't a fragment of memory large enough...
-
-The driver uses a double buffering scheme, which should really
-be an n-way buffer, depending on the size of allocated framebuffer
-and the requested grab-size/format.
-This current version also fixes a dead-lock situation during irq
-time, which really, really froze my system... :)
-
-Good luck.
-  Pauline
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 9f7e1fe..87410db 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -67,6 +67,7 @@
 
 config VIDEO_TUNER
 	tristate
+	depends on I2C
 
 config VIDEO_BUF
 	tristate
@@ -82,6 +83,7 @@
 
 config VIDEO_TVEEPROM
 	tristate
+	depends on I2C
 
 config USB_DABUSB
 	tristate "DABUSB driver"
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index db75344..f51e02fe 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -1552,3 +1552,58 @@
 };
 
 EXPORT_SYMBOL_GPL(ir_codes_norwood);
+
+/* From reading the following remotes:
+ * Zenith Universal 7 / TV Mode 807 / VCR Mode 837
+ * Hauppauge (from NOVA-CI-s box product)
+ * This is a "middle of the road" approach, differences are noted
+ */
+IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = {
+	[ 0x00 ] = KEY_0,
+	[ 0x01 ] = KEY_1,
+	[ 0x02 ] = KEY_2,
+	[ 0x03 ] = KEY_3,
+	[ 0x04 ] = KEY_4,
+	[ 0x05 ] = KEY_5,
+	[ 0x06 ] = KEY_6,
+	[ 0x07 ] = KEY_7,
+	[ 0x08 ] = KEY_8,
+	[ 0x09 ] = KEY_9,
+	[ 0x0a ] = KEY_ENTER,
+	[ 0x0b ] = KEY_RED,
+	[ 0x0c ] = KEY_POWER,             /* RADIO on Hauppauge */
+	[ 0x0d ] = KEY_MUTE,
+	[ 0x0f ] = KEY_A,                 /* TV on Hauppauge */
+	[ 0x10 ] = KEY_VOLUMEUP,
+	[ 0x11 ] = KEY_VOLUMEDOWN,
+	[ 0x14 ] = KEY_B,
+	[ 0x1c ] = KEY_UP,
+	[ 0x1d ] = KEY_DOWN,
+	[ 0x1e ] = KEY_OPTION,            /* RESERVED on Hauppauge */
+	[ 0x1f ] = KEY_BREAK,
+	[ 0x20 ] = KEY_CHANNELUP,
+	[ 0x21 ] = KEY_CHANNELDOWN,
+	[ 0x22 ] = KEY_PREVIOUS,          /* Prev. Ch on Zenith, SOURCE on Hauppauge */
+	[ 0x24 ] = KEY_RESTART,
+	[ 0x25 ] = KEY_OK,
+	[ 0x26 ] = KEY_CYCLEWINDOWS,      /* MINIMIZE on Hauppauge */
+	[ 0x28 ] = KEY_ENTER,             /* VCR mode on Zenith */
+	[ 0x29 ] = KEY_PAUSE,
+	[ 0x2b ] = KEY_RIGHT,
+	[ 0x2c ] = KEY_LEFT,
+	[ 0x2e ] = KEY_MENU,              /* FULL SCREEN on Hauppauge */
+	[ 0x30 ] = KEY_SLOW,
+	[ 0x31 ] = KEY_PREVIOUS,          /* VCR mode on Zenith */
+	[ 0x32 ] = KEY_REWIND,
+	[ 0x34 ] = KEY_FASTFORWARD,
+	[ 0x35 ] = KEY_PLAY,
+	[ 0x36 ] = KEY_STOP,
+	[ 0x37 ] = KEY_RECORD,
+	[ 0x38 ] = KEY_TUNER,             /* TV/VCR on Zenith */
+	[ 0x3a ] = KEY_C,
+	[ 0x3c ] = KEY_EXIT,
+	[ 0x3d ] = KEY_POWER2,
+	[ 0x3e ] = KEY_TUNER,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old);
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
index 5297a36..8c85efc 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146_i2c.c
@@ -189,13 +189,21 @@
 		saa7146_write(dev, I2C_TRANSFER, *dword);
 
 		dev->i2c_op = 1;
+		SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
 		SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
 		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
 
-		wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0);
-		if (signal_pending (current)) {
-			/* a signal arrived */
-			return -ERESTARTSYS;
+		timeout = HZ/100 + 1; /* 10ms */
+		timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout);
+		if (timeout == -ERESTARTSYS || dev->i2c_op) {
+			SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+			SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
+			if (timeout == -ERESTARTSYS)
+				/* a signal arrived */
+				return -ERESTARTSYS;
+
+			printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for end of xfer\n");
+			return -EIO;
 		}
 		status = saa7146_read(dev, I2C_STATUS);
 	} else {
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index a0dcd59..7987595 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -9,6 +9,7 @@
 	select DVB_STV0297 if !DVB_FE_CUSTOMISE
 	select DVB_BCM3510 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	help
 	  Support for the digital TV receiver chip made by B2C2 Inc. included in
 	  Technisats PCI cards and USB boxes.
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
index b8ba878..c2b35e3 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -14,7 +14,7 @@
 #include "stv0297.h"
 #include "mt312.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "dvb-pll.h"
 
 /* lnb control */
@@ -303,12 +303,6 @@
 	return request_firmware(fw, name, fc->dev);
 }
 
-static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params)
-{
-	struct flexcop_device *fc = fe->dvb->priv;
-	return lg_h06xf_pll_set(fe, &fc->i2c_adap, params);
-}
-
 static struct lgdt330x_config air2pc_atsc_hd5000_config = {
 	.demod_address       = 0x59,
 	.demod_chip          = LGDT3303,
@@ -533,7 +527,7 @@
 	/* try the air atsc 3nd generation (lgdt3303) */
 	if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
 		fc->dev_type          = FC_AIR_ATSC3;
-		fc->fe->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
+		dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap);
 		info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address);
 	} else
 	/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
index ae2ff5d..dd66b60 100644
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -1,13 +1,13 @@
 config DVB_BT8XX
 	tristate "BT8xx based PCI cards"
 	depends on DVB_CORE && PCI && I2C && VIDEO_BT848
-	select DVB_PLL
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_SP887X if !DVB_FE_CUSTOMISE
 	select DVB_NXT6000 if !DVB_FE_CUSTOMISE
 	select DVB_CX24110 if !DVB_FE_CUSTOMISE
 	select DVB_OR51211 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	select FW_LOADER
 	help
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index 14e69a7..80a85cb 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -34,7 +34,6 @@
 #include "dvb_frontend.h"
 #include "dvb-bt8xx.h"
 #include "bt878.h"
-#include "dvb-pll.h"
 
 static int debug;
 
@@ -568,12 +567,6 @@
 	.demod_init = digitv_alps_tded4_demod_init,
 };
 
-static int tdvs_tua6034_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
-{
-	struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
-	return lg_h06xf_pll_set(fe, card->i2c_adapter, params);
-}
-
 static struct lgdt330x_config tdvs_tua6034_config = {
 	.demod_address    = 0x0e,
 	.demod_chip       = LGDT3303,
@@ -616,7 +609,7 @@
 		lgdt330x_reset(card);
 		card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
 		if (card->fe != NULL) {
-			card->fe->ops.tuner_ops.set_params = tdvs_tua6034_tuner_set_params;
+			dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter);
 			dprintk ("dvb_bt8xx: lgdt330x detected\n");
 		}
 		break;
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
index 4745a90..e75f417 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
@@ -37,7 +37,7 @@
 #include "cx24110.h"
 #include "or51211.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "zl10353.h"
 
 struct dvb_bt8xx_card {
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
index 9123147..d64b96c 100644
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -746,6 +746,7 @@
 				dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event);
 				input_report_key(cinergyt2->rc_input_dev,
 						 cinergyt2->rc_input_event, 0);
+				input_sync(cinergyt2->rc_input_dev);
 				cinergyt2->rc_input_event = KEY_MAX;
 			}
 			cinergyt2->rc_last_code = ~0;
@@ -783,6 +784,7 @@
 			dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event);
 			input_report_key(cinergyt2->rc_input_dev,
 					 cinergyt2->rc_input_event, 1);
+			input_sync(cinergyt2->rc_input_dev);
 			cinergyt2->rc_last_code = rc_events[n].value;
 		}
 	}
@@ -798,8 +800,9 @@
 {
 	struct input_dev *input_dev;
 	int i;
+	int err;
 
-	cinergyt2->rc_input_dev = input_dev = input_allocate_device();
+	input_dev = input_allocate_device();
 	if (!input_dev)
 		return -ENOMEM;
 
@@ -817,7 +820,13 @@
 	input_dev->keycodesize = 0;
 	input_dev->keycodemax = 0;
 
-	input_register_device(cinergyt2->rc_input_dev);
+	err = input_register_device(input_dev);
+	if (err) {
+		input_free_device(input_dev);
+		return err;
+	}
+
+	cinergyt2->rc_input_dev = input_dev;
 	schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
 
 	return 0;
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index a263b3f..ad52143 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -69,6 +69,8 @@
 config DVB_USB_DIB0700
 	tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
 	depends on DVB_USB
+	select DVB_DIB7000P
+	select DVB_DIB7000M
 	select DVB_DIB3000MC
 	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	help
@@ -96,6 +98,7 @@
 	depends on DVB_USB
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	help
@@ -157,6 +160,17 @@
 	help
 	  Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
 
+config DVB_USB_TTUSB2
+	tristate "Pinnacle 400e DVB-S USB2.0 support"
+	depends on DVB_USB
+	select DVB_TDA10086 if !DVB_FE_CUSTOMISE
+	select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+	select DVB_TDA826X if !DVB_FE_CUSTOMISE
+	help
+	  Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The
+	  firmware protocol used by this module is similar to the one used by the
+	  old ttusb-driver - that's why the module is called dvb-usb-ttusb2.ko.
+
 config DVB_USB_DTT200U
 	tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
 	depends on DVB_USB
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index e239107..154d593 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -36,6 +36,9 @@
 dvb-usb-cxusb-objs = cxusb.o
 obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o
 
+dvb-usb-ttusb2-objs = ttusb2.o
+obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o
+
 dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o
 obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
 
diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c
index 2ed3eb6..a6c5f19 100644
--- a/drivers/media/dvb/dvb-usb/a800.c
+++ b/drivers/media/dvb/dvb-usb/a800.c
@@ -116,24 +116,24 @@
 		{
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 32,
-	.streaming_ctrl   = dibusb2_0_streaming_ctrl,
-	.pid_filter       = dibusb_pid_filter,
-	.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
+			.streaming_ctrl   = dibusb2_0_streaming_ctrl,
+			.pid_filter       = dibusb_pid_filter,
+			.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
 
-	.frontend_attach  = dibusb_dib3000mc_frontend_attach,
-	.tuner_attach     = dibusb_dib3000mc_tuner_attach,
+			.frontend_attach  = dibusb_dib3000mc_frontend_attach,
+			.tuner_attach     = dibusb_dib3000mc_tuner_attach,
 
-	/* parameter for the MPEG2-data transfer */
-			.stream = {
-				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x06,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+			/* parameter for the MPEG2-data transfer */
+					.stream = {
+						.type = USB_BULK,
+				.count = 7,
+				.endpoint = 0x06,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 
 			.size_of_priv     = sizeof(struct dibusb_state),
 		},
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 43f3906..15d12fc 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -14,12 +14,12 @@
  * TODO: Use the cx25840-driver for the analogue part
  *
  * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
- * Copyright (C) 2005 Michael Krufky (mkrufky@m1k.net)
+ * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org)
  * Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au)
  *
- *	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, version 2.
+ *   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, version 2.
  *
  * see Documentation/dvb/README.dvb-usb for more information
  */
@@ -27,29 +27,29 @@
 
 #include "cx22702.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "mt352.h"
 #include "mt352_priv.h"
 #include "zl10353.h"
 
 /* debug */
 int dvb_usb_cxusb_debug;
-module_param_named(debug,dvb_usb_cxusb_debug, int, 0644);
+module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
 
 static int cxusb_ctrl_msg(struct dvb_usb_device *d,
-		u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+			  u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
 {
 	int wo = (rbuf == NULL || rlen == 0); /* write-only */
 	u8 sndbuf[1+wlen];
-	memset(sndbuf,0,1+wlen);
+	memset(sndbuf, 0, 1+wlen);
 
 	sndbuf[0] = cmd;
-	memcpy(&sndbuf[1],wbuf,wlen);
+	memcpy(&sndbuf[1], wbuf, wlen);
 	if (wo)
-		dvb_usb_generic_write(d,sndbuf,1+wlen);
+		dvb_usb_generic_write(d, sndbuf, 1+wlen);
 	else
-		dvb_usb_generic_rw(d,sndbuf,1+wlen,rbuf,rlen,0);
+		dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
 
 	return 0;
 }
@@ -58,14 +58,14 @@
 static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
 {
 	struct cxusb_state *st = d->priv;
-	u8 o[2],i;
+	u8 o[2], i;
 
 	if (st->gpio_write_state[GPIO_TUNER] == onoff)
 		return;
 
 	o[0] = GPIO_TUNER;
 	o[1] = onoff;
-	cxusb_ctrl_msg(d,CMD_GPIO_WRITE,o,2,&i,1);
+	cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
 
 	if (i != 0x01)
 		deb_info("gpio_write failed.\n");
@@ -74,7 +74,8 @@
 }
 
 /* I2C */
-static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
+static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+			  int num)
 {
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	int i;
@@ -89,12 +90,12 @@
 
 		if (d->udev->descriptor.idVendor == USB_VID_MEDION)
 			switch (msg[i].addr) {
-				case 0x63:
-					cxusb_gpio_tuner(d,0);
-					break;
-				default:
-					cxusb_gpio_tuner(d,1);
-					break;
+			case 0x63:
+				cxusb_gpio_tuner(d, 0);
+				break;
+			default:
+				cxusb_gpio_tuner(d, 1);
+				break;
 			}
 
 		/* read request */
@@ -103,26 +104,27 @@
 			obuf[0] = msg[i].len;
 			obuf[1] = msg[i+1].len;
 			obuf[2] = msg[i].addr;
-			memcpy(&obuf[3],msg[i].buf,msg[i].len);
+			memcpy(&obuf[3], msg[i].buf, msg[i].len);
 
 			if (cxusb_ctrl_msg(d, CMD_I2C_READ,
-						obuf, 3+msg[i].len,
-						ibuf, 1+msg[i+1].len) < 0)
+					   obuf, 3+msg[i].len,
+					   ibuf, 1+msg[i+1].len) < 0)
 				break;
 
 			if (ibuf[0] != 0x08)
 				deb_i2c("i2c read may have failed\n");
 
-			memcpy(msg[i+1].buf,&ibuf[1],msg[i+1].len);
+			memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
 
 			i++;
 		} else { /* write */
 			u8 obuf[2+msg[i].len], ibuf;
 			obuf[0] = msg[i].addr;
 			obuf[1] = msg[i].len;
-			memcpy(&obuf[2],msg[i].buf,msg[i].len);
+			memcpy(&obuf[2], msg[i].buf, msg[i].len);
 
-			if (cxusb_ctrl_msg(d,CMD_I2C_WRITE, obuf, 2+msg[i].len, &ibuf,1) < 0)
+			if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf,
+					   2+msg[i].len, &ibuf,1) < 0)
 				break;
 			if (ibuf != 0x08)
 				deb_i2c("i2c write may have failed\n");
@@ -324,16 +326,8 @@
 	return 0;
 }
 
-static int cxusb_lgh064f_tuner_set_params(struct dvb_frontend *fe,
-					  struct dvb_frontend_parameters *fep)
-{
-	struct dvb_usb_adapter *adap = fe->dvb->priv;
-	return lg_h06xf_pll_set(fe, &adap->dev->i2c_adap, fep);
-}
-
 static struct cx22702_config cxusb_cx22702_config = {
 	.demod_address = 0x63,
-
 	.output_mode = CX22702_PARALLEL_OUTPUT,
 };
 
@@ -374,31 +368,27 @@
 
 static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	adap->pll_addr = 0x61;
-	adap->pll_desc = &dvb_pll_thomson_dtt7579;
-	adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+	dvb_attach(dvb_pll_attach, adap->fe, 0x61,
+		   NULL, &dvb_pll_thomson_dtt7579);
 	return 0;
 }
 
 static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	adap->pll_addr = 0x61;
-	adap->pll_desc = &dvb_pll_lg_z201;
-	adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+	dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, &dvb_pll_lg_z201);
 	return 0;
 }
 
 static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	adap->pll_addr = 0x60;
-	adap->pll_desc = &dvb_pll_thomson_dtt7579;
-	adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+	dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+		   NULL, &dvb_pll_thomson_dtt7579);
 	return 0;
 }
 
-static int cxusb_lgdt3303_tuner_attach(struct dvb_usb_adapter *adap)
+static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
 {
-	adap->fe->ops.tuner_ops.set_params = cxusb_lgh064f_tuner_set_params;
+	dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap);
 	return 0;
 }
 
@@ -410,7 +400,8 @@
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1);
 
-	if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, &adap->dev->i2c_adap)) != NULL)
+	if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
+				   &adap->dev->i2c_adap)) != NULL)
 		return 0;
 
 	return -EIO;
@@ -423,7 +414,8 @@
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 
-	if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, &adap->dev->i2c_adap)) != NULL)
+	if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config,
+				   &adap->dev->i2c_adap)) != NULL)
 		return 0;
 
 	return -EIO;
@@ -437,7 +429,8 @@
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 
-	if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config, &adap->dev->i2c_adap)) != NULL)
+	if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
+				   &adap->dev->i2c_adap)) != NULL)
 		return 0;
 
 	return -EIO;
@@ -450,8 +443,11 @@
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 
-	if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, &adap->dev->i2c_adap)) != NULL) ||
-		((adap->fe = dvb_attach(zl10353_attach, &cxusb_zl10353_dee1601_config, &adap->dev->i2c_adap)) != NULL))
+	if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
+				    &adap->dev->i2c_adap)) != NULL) ||
+		((adap->fe = dvb_attach(zl10353_attach,
+					&cxusb_zl10353_dee1601_config,
+					&adap->dev->i2c_adap)) != NULL))
 		return 0;
 
 	return -EIO;
@@ -463,7 +459,8 @@
  */
 
 #define BLUEBIRD_01_ID_OFFSET 6638
-static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, const struct firmware *fw)
+static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
+						  const struct firmware *fw)
 {
 	if (fw->size < BLUEBIRD_01_ID_OFFSET + 4)
 		return -EINVAL;
@@ -471,10 +468,12 @@
 	if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) &&
 	    fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) {
 
-		fw->data[BLUEBIRD_01_ID_OFFSET + 2] = udev->descriptor.idProduct + 1;
-		fw->data[BLUEBIRD_01_ID_OFFSET + 3] = udev->descriptor.idProduct >> 8;
+		fw->data[BLUEBIRD_01_ID_OFFSET + 2] =
+			udev->descriptor.idProduct + 1;
+		fw->data[BLUEBIRD_01_ID_OFFSET + 3] =
+			udev->descriptor.idProduct >> 8;
 
-		return usb_cypress_load_firmware(udev,fw,CYPRESS_FX2);
+		return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
 	}
 
 	return -EINVAL;
@@ -488,7 +487,7 @@
 static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
 
 static int cxusb_probe(struct usb_interface *intf,
-		const struct usb_device_id *id)
+		       const struct usb_device_id *id)
 {
 	if (dvb_usb_device_init(intf,&cxusb_medion_properties,THIS_MODULE,NULL) == 0 ||
 		dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
@@ -502,20 +501,20 @@
 }
 
 static struct usb_device_id cxusb_table [] = {
-		{ USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
-		{}		/* Terminating entry */
+	{ USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
+	{}		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, cxusb_table);
 
@@ -529,20 +528,20 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.streaming_ctrl   = cxusb_streaming_ctrl,
-	.frontend_attach  = cxusb_cx22702_frontend_attach,
-	.tuner_attach     = cxusb_fmd1216me_tuner_attach,
-	/* parameter for the MPEG2-data transfer */
-			.stream = {
-				.type = USB_BULK,
-		.count = 5,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+			.streaming_ctrl   = cxusb_streaming_ctrl,
+			.frontend_attach  = cxusb_cx22702_frontend_attach,
+			.tuner_attach     = cxusb_fmd1216me_tuner_attach,
+			/* parameter for the MPEG2-data transfer */
+					.stream = {
+						.type = USB_BULK,
+				.count = 5,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 
 		},
 	},
@@ -575,21 +574,21 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.streaming_ctrl   = cxusb_streaming_ctrl,
-	.frontend_attach  = cxusb_lgdt3303_frontend_attach,
-	.tuner_attach     = cxusb_lgdt3303_tuner_attach,
+			.streaming_ctrl   = cxusb_streaming_ctrl,
+			.frontend_attach  = cxusb_lgdt3303_frontend_attach,
+			.tuner_attach     = cxusb_lgh064f_tuner_attach,
 
-	/* parameter for the MPEG2-data transfer */
-			.stream = {
-				.type = USB_BULK,
-		.count = 5,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+			/* parameter for the MPEG2-data transfer */
+					.stream = {
+						.type = USB_BULK,
+				.count = 5,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 		},
 	},
 
@@ -627,20 +626,20 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.streaming_ctrl   = cxusb_streaming_ctrl,
-	.frontend_attach  = cxusb_dee1601_frontend_attach,
-	.tuner_attach     = cxusb_dee1601_tuner_attach,
-	/* parameter for the MPEG2-data transfer */
+			.streaming_ctrl   = cxusb_streaming_ctrl,
+			.frontend_attach  = cxusb_dee1601_frontend_attach,
+			.tuner_attach     = cxusb_dee1601_tuner_attach,
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 5,
-		.endpoint = 0x04,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+				.count = 5,
+				.endpoint = 0x04,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 		},
 	},
 
@@ -686,21 +685,21 @@
 	.num_adapters = 2,
 	.adapter = {
 		{
-	.streaming_ctrl   = cxusb_streaming_ctrl,
-	.frontend_attach  = cxusb_mt352_frontend_attach,
-	.tuner_attach     = cxusb_lgz201_tuner_attach,
+			.streaming_ctrl   = cxusb_streaming_ctrl,
+			.frontend_attach  = cxusb_mt352_frontend_attach,
+			.tuner_attach     = cxusb_lgz201_tuner_attach,
 
-	/* parameter for the MPEG2-data transfer */
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 5,
-		.endpoint = 0x04,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+				.count = 5,
+				.endpoint = 0x04,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 		},
 	},
 	.power_ctrl       = cxusb_bluebird_power_ctrl,
@@ -736,21 +735,21 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.streaming_ctrl   = cxusb_streaming_ctrl,
-	.frontend_attach  = cxusb_mt352_frontend_attach,
-	.tuner_attach     = cxusb_dtt7579_tuner_attach,
+			.streaming_ctrl   = cxusb_streaming_ctrl,
+			.frontend_attach  = cxusb_mt352_frontend_attach,
+			.tuner_attach     = cxusb_dtt7579_tuner_attach,
 
-	/* parameter for the MPEG2-data transfer */
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 5,
-		.endpoint = 0x04,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+				.count = 5,
+				.endpoint = 0x04,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 		},
 	},
 	.power_ctrl       = cxusb_bluebird_power_ctrl,
@@ -776,7 +775,7 @@
 static struct usb_driver cxusb_driver = {
 	.name		= "dvb_usb_cxusb",
 	.probe		= cxusb_probe,
-	.disconnect = dvb_usb_device_exit,
+	.disconnect     = dvb_usb_device_exit,
 	.id_table	= cxusb_table,
 };
 
@@ -802,7 +801,7 @@
 module_exit (cxusb_module_exit);
 
 MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
-MODULE_AUTHOR("Michael Krufky <mkrufky@m1k.net>");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
 MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
 MODULE_VERSION("1.0-alpha");
diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h
index ac84347..cda3ade 100644
--- a/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/drivers/media/dvb/dvb-usb/dib0700.h
@@ -24,18 +24,23 @@
 #define REQUEST_I2C_WRITE    0x3
 #define REQUEST_POLL_RC      0x4
 #define REQUEST_JUMPRAM      0x8
+#define REQUEST_SET_CLOCK    0xB
 #define REQUEST_SET_GPIO     0xC
 #define REQUEST_ENABLE_VIDEO 0xF
 	// 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog)
 	// 2 Byte: MPEG2 mode:  4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
 	// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines)    4LSB(     "                "           )
+#define REQUEST_GET_VERSION  0x15
 
 struct dib0700_state {
 	u8 channel_state;
 	u16 mt2060_if1[2];
+
+	u8 is_dib7000pc;
 };
 
 extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val);
+extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3);
 extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw);
 extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
 extern struct i2c_algorithm dib0700_i2c_algo;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index dca6c69..6a4d150 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -135,14 +135,46 @@
 int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
 			struct dvb_usb_device_description **desc, int *cold)
 {
-	u8 buf[3] = { REQUEST_SET_GPIO, 4, (GPIO_IN << 7) | (0 << 6) }; // GPIO4 is save - used for I2C
-	*cold = usb_control_msg(udev, usb_sndctrlpipe(udev,0),
-		buf[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, buf, 3, USB_CTRL_GET_TIMEOUT) != 3;
+	u8 b[16];
+	s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev,0),
+		REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT);
+
+	deb_info("FW GET_VERSION length: %d\n",ret);
+
+	*cold = ret <= 0;
 
 	deb_info("cold: %d\n", *cold);
 	return 0;
 }
 
+static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
+	u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv,
+	u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
+{
+	u8 b[10];
+	b[0] = REQUEST_SET_CLOCK;
+	b[1] = (en_pll << 7) | (pll_src << 6) | (pll_range << 5) | (clock_gpio3 << 4);
+	b[2] = (pll_prediv >> 8)  & 0xff; // MSB
+	b[3] =  pll_prediv        & 0xff; // LSB
+	b[4] = (pll_loopdiv >> 8) & 0xff; // MSB
+	b[5] =  pll_loopdiv       & 0xff; // LSB
+	b[6] = (free_div >> 8)    & 0xff; // MSB
+	b[7] =  free_div          & 0xff; // LSB
+	b[8] = (dsuScaler >> 8)   & 0xff; // MSB
+	b[9] =  dsuScaler         & 0xff; // LSB
+
+	return dib0700_ctrl_wr(d, b, 10);
+}
+
+int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
+{
+	switch (clk_MHz) {
+		case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break;
+		default: return -EINVAL;
+	}
+	return 0;
+}
+
 static int dib0700_jumpram(struct usb_device *udev, u32 address)
 {
 	int ret, actlen;
@@ -197,7 +229,7 @@
 		/* start the firmware */
 		if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
 			info("firmware started successfully.");
-			msleep(100);
+			msleep(500);
 		}
 	} else
 		ret = -EIO;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index e473bfe..2208757 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -9,6 +9,8 @@
 #include "dib0700.h"
 
 #include "dib3000mc.h"
+#include "dib7000m.h"
+#include "dib7000p.h"
 #include "mt2060.h"
 
 static int force_lna_activation;
@@ -95,37 +97,189 @@
 }
 
 /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
-/*
-static struct mt2060_config stk7000p_mt2060_config = {
-	0x60
+static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = {
+	BAND_UHF | BAND_VHF,       // band_caps
+
+	/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+	 * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
+	(0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
+
+	712,  // inv_gain
+	41,  // time_stabiliz
+
+	0,  // alpha_level
+	118,  // thlock
+
+	0,     // wbd_inv
+	4095,  // wbd_ref
+	0,     // wbd_sel
+	0,     // wbd_alpha
+
+	42598,  // agc1_max
+	17694,  // agc1_min
+	45875,  // agc2_max
+	2621,  // agc2_min
+	0,  // agc1_pt1
+	76,  // agc1_pt2
+	139,  // agc1_pt3
+	52,  // agc1_slope1
+	59,  // agc1_slope2
+	107,  // agc2_pt1
+	172,  // agc2_pt2
+	57,  // agc2_slope1
+	70,  // agc2_slope2
+
+	21,  // alpha_mant
+	25,  // alpha_exp
+	28,  // beta_mant
+	48,  // beta_exp
+
+	1,  // perform_agc_softsplit
+	{  0,     // split_min
+	   107,   // split_max
+	   51800, // global_split_min
+	   24700  // global_split_max
+	},
 };
-*/
+
+static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = {
+	BAND_UHF | BAND_VHF,
+
+	/* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+	 * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */
+	(0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup
+
+	712, // inv_gain
+	41,  // time_stabiliz
+
+	0,   // alpha_level
+	118, // thlock
+
+	0,    // wbd_inv
+	4095, // wbd_ref
+	0,    // wbd_sel
+	0,    // wbd_alpha
+
+	42598, // agc1_max
+	16384, // agc1_min
+	42598, // agc2_max
+	    0, // agc2_min
+
+	  0,   // agc1_pt1
+	137,   // agc1_pt2
+	255,   // agc1_pt3
+
+	  0,   // agc1_slope1
+	255,   // agc1_slope2
+
+	0,     // agc2_pt1
+	0,     // agc2_pt2
+
+	 0,    // agc2_slope1
+	41,    // agc2_slope2
+
+	15, // alpha_mant
+	25, // alpha_exp
+
+	28, // beta_mant
+	48, // beta_exp
+
+	0, // perform_agc_softsplit
+};
+
+static struct dibx000_bandwidth_config stk7700p_pll_config = {
+	60000, 30000, // internal, sampling
+	1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
+	0, 0, 1, 1, 0, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo
+	(3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k
+	60258167, // ifreq
+	20452225, // timf
+};
+
+static struct dib7000m_config stk7700p_dib7000m_config = {
+	.dvbt_mode = 1,
+	.output_mpeg2_in_188_bytes = 1,
+	.quartz_direct = 1,
+
+	.agc_config_count = 1,
+	.agc = &stk7700p_7000m_mt2060_agc_config,
+	.bw  = &stk7700p_pll_config,
+
+	.gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
+	.gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
+	.gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
+};
+
+static struct dib7000p_config stk7700p_dib7000p_config = {
+	.output_mpeg2_in_188_bytes = 1,
+
+	.agc = &stk7700p_7000p_mt2060_agc_config,
+	.bw  = &stk7700p_pll_config,
+
+	.gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS,
+	.gpio_val = DIB7000M_GPIO_DEFAULT_VALUES,
+	.gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS,
+};
 
 static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap)
 {
+	struct dib0700_state *st = adap->dev->priv;
 	/* unless there is no real power management in DVB - we leave the device on GPIO6 */
-	dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10);
-	dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10);
-	dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10);
-	dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10);
 
-//	adap->fe = dib7000m_attach(&adap->dev->i2c_adap, &stk7700p_dib7000m_config, 18);
-	return 0;
+	dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+	dib0700_set_gpio(adap->dev, GPIO6,  GPIO_OUT, 0); msleep(50);
+
+	dib0700_set_gpio(adap->dev, GPIO6,  GPIO_OUT, 1); msleep(10);
+	dib0700_set_gpio(adap->dev, GPIO9,  GPIO_OUT, 1);
+
+	dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10);
+	dib0700_ctrl_clock(adap->dev, 72, 1);
+	dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(100);
+
+	dib0700_set_gpio(adap->dev,  GPIO0, GPIO_OUT, 1);
+
+	st->mt2060_if1[0] = 1220;
+
+	if (dib7000pc_detection(&adap->dev->i2c_adap)) {
+		adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config);
+		st->is_dib7000pc = 1;
+	} else
+		adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config);
+
+	return adap->fe == NULL ? -ENODEV : 0;
 }
 
+static struct mt2060_config stk7700p_mt2060_config = {
+	0x60
+};
+
 static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap)
 {
-//	tun_i2c = dib7000m_get_tuner_i2c_master(adap->fe, 1);
-//	return mt2060_attach(adap->fe, tun_i2c, &stk3000p_mt2060_config, if1);
-	return 0;
+	struct dib0700_state *st = adap->dev->priv;
+	struct i2c_adapter *tun_i2c;
+
+	if (st->is_dib7000pc)
+		tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+	else
+		tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
+
+	return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config,
+		st->mt2060_if1[0]) == NULL ? -ENODEV : 0;
 }
 
 struct usb_device_id dib0700_usb_id_table[] = {
 		{ USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_STK7700P) },
+		{ USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_STK7700P_PC) },
+
 		{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500) },
 		{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) },
 		{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) },
 		{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) },
+		{ USB_DEVICE(USB_VID_COMPRO,    USB_PID_COMPRO_VIDEOMATE_U500) },
+		{ USB_DEVICE(USB_VID_UNIWILL,   USB_PID_UNIWILL_STK7700P) },
+		{ USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV_DONGLE_STK7700P) },
+		{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) },
+		{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) },
 		{ }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -167,20 +321,32 @@
 			},
 		},
 
-		.num_device_descs = 3,
+		.num_device_descs = 6,
 		.devices = {
 			{   "DiBcom STK7700P reference design",
-				{ &dib0700_usb_id_table[0], NULL },
+				{ &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] },
 				{ NULL },
 			},
 			{   "Hauppauge Nova-T Stick",
-				{ &dib0700_usb_id_table[3], NULL },
+				{ &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL },
 				{ NULL },
 			},
 			{   "AVerMedia AVerTV DVB-T Volar",
-				{ &dib0700_usb_id_table[4], NULL },
+				{ &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] },
 				{ NULL },
 			},
+			{   "Compro Videomate U500",
+				{ &dib0700_usb_id_table[6], NULL },
+				{ NULL },
+			},
+			{   "Uniwill STK7700P based (Hama and others)",
+				{ &dib0700_usb_id_table[7], NULL },
+				{ NULL },
+			},
+			{   "Leadtek Winfast DTV Dongle (STK7700P based)",
+				{ &dib0700_usb_id_table[8], NULL },
+				{ NULL },
+			}
 		}
 	}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
 
@@ -202,7 +368,7 @@
 		.num_device_descs = 1,
 		.devices = {
 			{   "Hauppauge Nova-T 500 Dual DVB-T",
-				{ &dib0700_usb_id_table[1], &dib0700_usb_id_table[2], NULL },
+				{ &dib0700_usb_id_table[2], &dib0700_usb_id_table[3], NULL },
 				{ NULL },
 			},
 		}
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c
index 4fe363e..7a6ae8f 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c
@@ -163,23 +163,23 @@
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 16,
 
-	.streaming_ctrl   = dibusb_streaming_ctrl,
-	.pid_filter       = dibusb_pid_filter,
-	.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
-	.frontend_attach  = dibusb_dib3000mb_frontend_attach,
-	.tuner_attach     = dibusb_tuner_probe_and_attach,
+			.streaming_ctrl   = dibusb_streaming_ctrl,
+			.pid_filter       = dibusb_pid_filter,
+			.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
+			.frontend_attach  = dibusb_dib3000mb_frontend_attach,
+			.tuner_attach     = dibusb_tuner_probe_and_attach,
 
-	/* parameter for the MPEG2-data transfer */
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 	},
@@ -248,23 +248,23 @@
 			.caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER,
 			.pid_filter_count = 16,
 
-	.streaming_ctrl   = dibusb_streaming_ctrl,
-	.pid_filter       = dibusb_pid_filter,
-	.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
-	.frontend_attach  = dibusb_dib3000mb_frontend_attach,
-	.tuner_attach     = dibusb_tuner_probe_and_attach,
+			.streaming_ctrl   = dibusb_streaming_ctrl,
+			.pid_filter       = dibusb_pid_filter,
+			.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
+			.frontend_attach  = dibusb_dib3000mb_frontend_attach,
+			.tuner_attach     = dibusb_tuner_probe_and_attach,
 
-	/* parameter for the MPEG2-data transfer */
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 			.size_of_priv     = sizeof(struct dibusb_state),
 		},
 	},
@@ -312,22 +312,23 @@
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 16,
 
-	.streaming_ctrl   = dibusb2_0_streaming_ctrl,
-	.pid_filter       = dibusb_pid_filter,
-	.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
-	.frontend_attach  = dibusb_dib3000mb_frontend_attach,
-	.tuner_attach     = dibusb_thomson_tuner_attach,
-	/* parameter for the MPEG2-data transfer */
+			.streaming_ctrl   = dibusb2_0_streaming_ctrl,
+			.pid_filter       = dibusb_pid_filter,
+			.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
+			.frontend_attach  = dibusb_dib3000mb_frontend_attach,
+			.tuner_attach     = dibusb_thomson_tuner_attach,
+
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x06,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x06,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 	},
@@ -369,22 +370,22 @@
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 16,
 
-	.streaming_ctrl   = dibusb2_0_streaming_ctrl,
-	.pid_filter       = dibusb_pid_filter,
-	.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
-	.frontend_attach  = dibusb_dib3000mb_frontend_attach,
-	.tuner_attach     = dibusb_tuner_probe_and_attach,
-	/* parameter for the MPEG2-data transfer */
+			.streaming_ctrl   = dibusb2_0_streaming_ctrl,
+			.pid_filter       = dibusb_pid_filter,
+			.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
+			.frontend_attach  = dibusb_dib3000mb_frontend_attach,
+			.tuner_attach     = dibusb_tuner_probe_and_attach,
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x06,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x06,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 	},
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c
index a0fd37e..e7ea3e7 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c
@@ -54,23 +54,23 @@
 		{
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 32,
-	.streaming_ctrl   = dibusb2_0_streaming_ctrl,
-	.pid_filter       = dibusb_pid_filter,
-	.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
-	.frontend_attach  = dibusb_dib3000mc_frontend_attach,
-	.tuner_attach     = dibusb_dib3000mc_tuner_attach,
+			.streaming_ctrl   = dibusb2_0_streaming_ctrl,
+			.pid_filter       = dibusb_pid_filter,
+			.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
+			.frontend_attach  = dibusb_dib3000mc_frontend_attach,
+			.tuner_attach     = dibusb_dib3000mc_tuner_attach,
 
 	/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x06,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x06,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 	},
diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c
index 8fb3437..4a198d4 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/dvb/dvb-usb/digitv.c
@@ -274,20 +274,20 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.frontend_attach  = digitv_frontend_attach,
-	.tuner_attach     = digitv_tuner_attach,
+			.frontend_attach  = digitv_frontend_attach,
+			.tuner_attach     = digitv_tuner_attach,
 
-	/* parameter for the MPEG2-data transfer */
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 		}
 	},
 	.identify_state   = digitv_identify_state,
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c
index fa43a41..7dbe143 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.c
+++ b/drivers/media/dvb/dvb-usb/dtt200u.c
@@ -268,20 +268,20 @@
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
 			.pid_filter_count = 15,
 
-	.streaming_ctrl  = dtt200u_streaming_ctrl,
-	.pid_filter      = dtt200u_pid_filter,
-	.frontend_attach = dtt200u_frontend_attach,
-	/* parameter for the MPEG2-data transfer */
+			.streaming_ctrl  = dtt200u_streaming_ctrl,
+			.pid_filter      = dtt200u_pid_filter,
+			.frontend_attach = dtt200u_frontend_attach,
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 		}
 	},
 	.power_ctrl      = dtt200u_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 4d6b069..299382d 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -33,6 +33,7 @@
 #define USB_VID_VISIONPLUS			0x13d3
 #define USB_VID_TWINHAN				0x1822
 #define USB_VID_ULTIMA_ELECTRONIC		0x05d8
+#define USB_VID_UNIWILL				0x1584
 #define USB_VID_WIDEVIEW			0x14aa
 
 /* Product IDs */
@@ -46,6 +47,7 @@
 #define USB_PID_COMPRO_DVBU2000_WARM			0xd001
 #define USB_PID_COMPRO_DVBU2000_UNK_COLD		0x010c
 #define USB_PID_COMPRO_DVBU2000_UNK_WARM		0x010d
+#define USB_PID_COMPRO_VIDEOMATE_U500			0x1e78
 #define USB_PID_DIBCOM_HOOK_DEFAULT			0x0064
 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM		0x0065
 #define USB_PID_DIBCOM_MOD3000_COLD			0x0bb8
@@ -53,7 +55,9 @@
 #define USB_PID_DIBCOM_MOD3001_COLD			0x0bc6
 #define USB_PID_DIBCOM_MOD3001_WARM			0x0bc7
 #define USB_PID_DIBCOM_STK7700P				0x1e14
+#define USB_PID_DIBCOM_STK7700P_PC			0x1e78
 #define USB_PID_DIBCOM_ANCHOR_2135_COLD			0x2131
+#define USB_PID_UNIWILL_STK7700P			0x6003
 #define USB_PID_GRANDTEC_DVBT_USB_COLD			0x0fa0
 #define USB_PID_GRANDTEC_DVBT_USB_WARM			0x0fa1
 #define USB_PID_KWORLD_VSTREAM_COLD			0x17de
@@ -97,7 +101,9 @@
 #define USB_PID_HAUPPAUGE_NOVA_T_500			0x9941
 #define USB_PID_HAUPPAUGE_NOVA_T_500_2			0x9950
 #define USB_PID_HAUPPAUGE_NOVA_T_STICK			0x7050
-#define USB_PID_AVERMEDIA_VOLAR				0x1234
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2		0x7060
+#define USB_PID_AVERMEDIA_VOLAR				0xa807
+#define USB_PID_AVERMEDIA_VOLAR_2			0xb808
 #define USB_PID_NEBULA_DIGITV				0x0201
 #define USB_PID_DVICO_BLUEBIRD_LGDT			0xd820
 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD		0xd500
@@ -110,8 +116,8 @@
 #define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM		0xdb51
 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD		0xdb58
 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM		0xdb59
-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD	0xdb54
-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM	0xdb55
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD		0xdb54
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM		0xdb55
 #define USB_PID_MEDION_MD95700				0x0932
 #define USB_PID_KYE_DVB_T_COLD				0x701e
 #define USB_PID_KYE_DVB_T_WARM				0x701f
@@ -125,7 +131,9 @@
 #define USB_PID_GRANDTEC_DVBT_USB2_WARM			0x0bc7
 #define USB_PID_WINFAST_DTV_DONGLE_COLD			0x6025
 #define USB_PID_WINFAST_DTV_DONGLE_WARM			0x6026
+#define USB_PID_WINFAST_DTV_DONGLE_STK7700P		0x6f00
 #define USB_PID_GENPIX_8PSK_COLD			0x0200
 #define USB_PID_GENPIX_8PSK_WARM			0x0201
 
+
 #endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
index 794e447..19ff597 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
@@ -90,7 +90,9 @@
 
 int dvb_usb_remote_init(struct dvb_usb_device *d)
 {
+	struct input_dev *input_dev;
 	int i;
+	int err;
 
 	if (d->props.rc_key_map == NULL ||
 		d->props.rc_query == NULL ||
@@ -100,23 +102,24 @@
 	usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
 	strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
 
-	d->rc_input_dev = input_allocate_device();
-	if (!d->rc_input_dev)
+	input_dev = input_allocate_device();
+	if (!input_dev)
 		return -ENOMEM;
 
-	d->rc_input_dev->evbit[0] = BIT(EV_KEY);
-	d->rc_input_dev->keycodesize = sizeof(unsigned char);
-	d->rc_input_dev->keycodemax = KEY_MAX;
-	d->rc_input_dev->name = "IR-receiver inside an USB DVB receiver";
-	d->rc_input_dev->phys = d->rc_phys;
-	usb_to_input_id(d->udev, &d->rc_input_dev->id);
-	d->rc_input_dev->cdev.dev = &d->udev->dev;
+	input_dev->evbit[0] = BIT(EV_KEY);
+	input_dev->keycodesize = sizeof(unsigned char);
+	input_dev->keycodemax = KEY_MAX;
+	input_dev->name = "IR-receiver inside an USB DVB receiver";
+	input_dev->phys = d->rc_phys;
+	usb_to_input_id(d->udev, &input_dev->id);
+	input_dev->cdev.dev = &d->udev->dev;
 
 	/* set the bits for the keys */
 	deb_rc("key map size: %d\n", d->props.rc_key_map_size);
 	for (i = 0; i < d->props.rc_key_map_size; i++) {
-		deb_rc("setting bit for event %d item %d\n",d->props.rc_key_map[i].event, i);
-		set_bit(d->props.rc_key_map[i].event, d->rc_input_dev->keybit);
+		deb_rc("setting bit for event %d item %d\n",
+			d->props.rc_key_map[i].event, i);
+		set_bit(d->props.rc_key_map[i].event, input_dev->keybit);
 	}
 
 	/* Start the remote-control polling. */
@@ -124,10 +127,16 @@
 		d->props.rc_interval = 100; /* default */
 
 	/* setting these two values to non-zero, we have to manage key repeats */
-	d->rc_input_dev->rep[REP_PERIOD] = d->props.rc_interval;
-	d->rc_input_dev->rep[REP_DELAY]  = d->props.rc_interval + 150;
+	input_dev->rep[REP_PERIOD] = d->props.rc_interval;
+	input_dev->rep[REP_DELAY]  = d->props.rc_interval + 150;
 
-	input_register_device(d->rc_input_dev);
+	err = input_register_device(input_dev);
+	if (err) {
+		input_free_device(input_dev);
+		return err;
+	}
+
+	d->rc_input_dev = input_dev;
 
 	INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control);
 
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index 7375eb2..518d67f 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -194,19 +194,19 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.streaming_ctrl   = gp8psk_streaming_ctrl,
-	.frontend_attach  = gp8psk_frontend_attach,
-	/* parameter for the MPEG2-data transfer */
+			.streaming_ctrl   = gp8psk_streaming_ctrl,
+			.frontend_attach  = gp8psk_frontend_attach,
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x82,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 		}
 	},
 	.power_ctrl       = gp8psk_power_ctrl,
diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
index a58874c..d48622e 100644
--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
@@ -163,23 +163,23 @@
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 32,
 
-	.streaming_ctrl   = dibusb2_0_streaming_ctrl,
-	.pid_filter       = dibusb_pid_filter,
-	.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
-	.frontend_attach  = dibusb_dib3000mc_frontend_attach,
-	.tuner_attach     = dibusb_dib3000mc_tuner_attach,
+			.streaming_ctrl   = dibusb2_0_streaming_ctrl,
+			.pid_filter       = dibusb_pid_filter,
+			.pid_filter_ctrl  = dibusb_pid_filter_ctrl,
+			.frontend_attach  = dibusb_dib3000mc_frontend_attach,
+			.tuner_attach     = dibusb_dib3000mc_tuner_attach,
 
-	/* parameter for the MPEG2-data transfer */
-			.stream = {
-				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x06,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+			/* parameter for the MPEG2-data transfer */
+					.stream = {
+						.type = USB_BULK,
+				.count = 7,
+				.endpoint = 0x06,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c
new file mode 100644
index 0000000..95d2997
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/ttusb2.c
@@ -0,0 +1,270 @@
+/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
+ * (e.g. Pinnacle 400e DVB-S USB2.0).
+ *
+ * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes.
+ *
+ * TDA8263 + TDA10086
+ *
+ * I2C addresses:
+ * 0x08 - LNBP21PD   - LNB power supply
+ * 0x0e - TDA10086   - Demodulator
+ * 0x50 - FX2 eeprom
+ * 0x60 - TDA8263    - Tuner
+ * 0x78 ???
+ *
+ * Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org>
+ *
+ *	This program is free software; you can redistribute it and/or modify it
+ *	under the terms of the GNU General Public License as published by the Free
+ *	Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#define DVB_USB_LOG_PREFIX "ttusb2"
+#include "dvb-usb.h"
+
+#include "ttusb2.h"
+
+#include "tda826x.h"
+#include "tda10086.h"
+#include "lnbp21.h"
+
+/* debug */
+static int dvb_usb_ttusb2_debug;
+#define deb_info(args...)   dprintk(dvb_usb_ttusb2_debug,0x01,args)
+module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS);
+
+struct ttusb2_state {
+	u8 id;
+};
+
+static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
+		u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+	struct ttusb2_state *st = d->priv;
+	u8 s[wlen+4],r[64] = { 0 };
+	int ret = 0;
+
+	memset(s,0,wlen+4);
+
+	s[0] = 0xaa;
+	s[1] = ++st->id;
+	s[2] = cmd;
+	s[3] = wlen;
+	memcpy(&s[4],wbuf,wlen);
+
+	ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0);
+
+	if (ret  != 0 ||
+		r[0] != 0x55 ||
+		r[1] != s[1] ||
+		r[2] != cmd ||
+		(rlen > 0 && r[3] != rlen)) {
+		warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]);
+		return -EIO;
+	}
+
+	if (rlen > 0)
+		memcpy(rbuf, &r[4], rlen);
+
+	return 0;
+}
+
+static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	static u8 obuf[60], ibuf[60];
+	int i,read;
+
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	if (num > 2)
+		warn("more than 2 i2c messages at a time is not handled yet. TODO.");
+
+	for (i = 0; i < num; i++) {
+		read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+
+		obuf[0] = (msg[i].addr << 1) | read;
+		obuf[1] = msg[i].len;
+
+		/* read request */
+		if (read)
+			obuf[2] = msg[i+1].len;
+		else
+			obuf[2] = 0;
+
+		memcpy(&obuf[3],msg[i].buf,msg[i].len);
+
+		if (ttusb2_msg(d, CMD_I2C_XFER, obuf, msg[i].len+3, ibuf, obuf[2] + 3) < 0) {
+			err("i2c transfer failed.");
+			break;
+		}
+
+		if (read) {
+			memcpy(msg[i+1].buf,&ibuf[3],msg[i+1].len);
+			i++;
+		}
+	}
+
+	mutex_unlock(&d->i2c_mutex);
+	return i;
+}
+
+static u32 ttusb2_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm ttusb2_i2c_algo = {
+	.master_xfer   = ttusb2_i2c_xfer,
+	.functionality = ttusb2_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static int ttusb2_identify_state (struct usb_device *udev, struct
+		dvb_usb_device_properties *props, struct dvb_usb_device_description **desc,
+		int *cold)
+{
+	*cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0;
+	return 0;
+}
+
+static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+	u8 b = onoff;
+	ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0);
+	return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0);
+}
+
+
+static struct tda10086_config tda10086_config = {
+	.demod_address = 0x0e,
+	.invert = 0,
+};
+
+static int ttusb2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	if (usb_set_interface(adap->dev->udev,0,3) < 0)
+		err("set interface to alts=3 failed");
+
+	if ((adap->fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
+		deb_info("TDA10086 attach failed\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int ttusb2_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	if (dvb_attach(tda826x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
+		deb_info("TDA8263 attach failed\n");
+		return -ENODEV;
+	}
+
+	if (dvb_attach(lnbp21_attach, adap->fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
+		deb_info("LNBP21 attach failed\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties ttusb2_properties;
+
+static int ttusb2_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	return dvb_usb_device_init(intf,&ttusb2_properties,THIS_MODULE,NULL);
+}
+
+static struct usb_device_id ttusb2_table [] = {
+		{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
+		{}		/* Terminating entry */
+};
+MODULE_DEVICE_TABLE (usb, ttusb2_table);
+
+static struct dvb_usb_device_properties ttusb2_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+	.usb_ctrl = CYPRESS_FX2,
+	.firmware = "dvb-usb-pctv-400e-01.fw",
+
+	.size_of_priv = sizeof(struct ttusb2_state),
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.streaming_ctrl   = NULL, // ttusb2_streaming_ctrl,
+
+			.frontend_attach  = ttusb2_frontend_attach,
+			.tuner_attach     = ttusb2_tuner_attach,
+
+			/* parameter for the MPEG2-data transfer */
+			.stream = {
+				.type = USB_ISOC,
+				.count = 5,
+				.endpoint = 0x02,
+				.u = {
+					.isoc = {
+						.framesperurb = 4,
+						.framesize = 940,
+						.interval = 1,
+					}
+				}
+			}
+		}
+	},
+
+	.power_ctrl       = ttusb2_power_ctrl,
+	.identify_state   = ttusb2_identify_state,
+
+	.i2c_algo         = &ttusb2_i2c_algo,
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+
+	.num_device_descs = 1,
+	.devices = {
+		{   "Pinnacle 400e DVB-S USB2.0",
+			{ &ttusb2_table[0], NULL },
+			{ NULL },
+		},
+	}
+};
+
+static struct usb_driver ttusb2_driver = {
+	.name		= "dvb_usb_ttusb2",
+	.probe		= ttusb2_probe,
+	.disconnect = dvb_usb_device_exit,
+	.id_table	= ttusb2_table,
+};
+
+/* module stuff */
+static int __init ttusb2_module_init(void)
+{
+	int result;
+	if ((result = usb_register(&ttusb2_driver))) {
+		err("usb_register failed. Error number %d",result);
+		return result;
+	}
+
+	return 0;
+}
+
+static void __exit ttusb2_module_exit(void)
+{
+	/* deregister this driver from the USB subsystem */
+	usb_deregister(&ttusb2_driver);
+}
+
+module_init (ttusb2_module_init);
+module_exit (ttusb2_module_exit);
+
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.h b/drivers/media/dvb/dvb-usb/ttusb2.h
new file mode 100644
index 0000000..52a63af
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/ttusb2.h
@@ -0,0 +1,70 @@
+/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
+ * (e.g. Pinnacle 400e DVB-S USB2.0).
+ *
+ * Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.de>
+ *
+ *	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, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#ifndef _DVB_USB_TTUSB2_H_
+#define _DVB_USB_TTUSB2_H_
+
+/* TTUSB protocol
+ *
+ * always to messages (out/in)
+ * out message:
+ * 0xaa <id> <cmdbyte> <datalen> <data...>
+ *
+ * in message (complete block is always 0x40 bytes long)
+ * 0x55 <id> <cmdbyte> <datalen> <data...>
+ *
+ * id is incremented for each transaction
+ */
+
+#define CMD_DSP_DOWNLOAD    0x13
+/* out data: <byte>[28]
+ * last block must be empty */
+
+#define CMD_DSP_BOOT        0x14
+/* out data: nothing */
+
+#define CMD_POWER           0x15
+/* out data: <on=1/off=0> */
+
+#define CMD_LNB             0x16
+/* out data: <power=1> <18V=0,13V=1> <tone> <??=1> <??=1> */
+
+#define CMD_GET_VERSION     0x17
+/* in  data: <version_byte>[5] */
+
+#define CMD_DISEQC          0x18
+/* out data: <master=0xff/burst=??> <cmdlen> <cmdbytes>[cmdlen] */
+
+#define CMD_PID_ENABLE      0x22
+/* out data: <index> <type: ts=1/sec=2> <pid msb> <pid lsb> */
+
+#define CMD_PID_DISABLE     0x23
+/* out data: <index> */
+
+#define CMD_FILTER_ENABLE   0x24
+/* out data: <index> <pid_idx> <filter>[12] <mask>[12] */
+
+#define CMD_FILTER_DISABLE  0x25
+/* out data: <index> */
+
+#define CMD_GET_DSP_VERSION 0x26
+/* in  data: <version_byte>[28] */
+
+#define CMD_I2C_XFER        0x31
+/* out data: <addr << 1> <sndlen> <rcvlen> <data>[sndlen]
+ * in  data: <addr << 1> <sndlen> <rcvlen> <data>[rcvlen] */
+
+#define CMD_I2C_BITRATE     0x32
+/* out data: <default=0> */
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c
index f9941ea..f77b48f 100644
--- a/drivers/media/dvb/dvb-usb/umt-010.c
+++ b/drivers/media/dvb/dvb-usb/umt-010.c
@@ -99,21 +99,21 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.streaming_ctrl   = dibusb2_0_streaming_ctrl,
-	.frontend_attach  = umt_mt352_frontend_attach,
-	.tuner_attach     = umt_tuner_attach,
+			.streaming_ctrl   = dibusb2_0_streaming_ctrl,
+			.frontend_attach  = umt_mt352_frontend_attach,
+			.tuner_attach     = umt_tuner_attach,
 
-	/* parameter for the MPEG2-data transfer */
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 20,
-		.endpoint = 0x06,
-		.u = {
-			.bulk = {
-				.buffersize = 512,
-			}
-		}
-	},
+				.count = 20,
+				.endpoint = 0x06,
+				.u = {
+					.bulk = {
+						.buffersize = 512,
+					}
+				}
+			},
 
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c
index 02bd61a..16533b3 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/drivers/media/dvb/dvb-usb/vp702x.c
@@ -275,22 +275,22 @@
 			.caps             = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
 
 			.streaming_ctrl   = vp702x_streaming_ctrl,
-	.frontend_attach  = vp702x_frontend_attach,
+			.frontend_attach  = vp702x_frontend_attach,
 
-	/* parameter for the MPEG2-data transfer */
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
 				.count = 10,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 			.size_of_priv     = sizeof(struct vp702x_state),
 		}
-		},
+	},
 	.read_mac_address = vp702x_read_mac_addr,
 
 	.rc_key_map       = vp702x_rc_keys,
diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c
index b4cf002..69a46b3 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/drivers/media/dvb/dvb-usb/vp7045.c
@@ -125,7 +125,25 @@
 	{ 0x00, 0x00, KEY_TAB }, /* Tab */
 	{ 0x00, 0x48, KEY_INFO }, /* Preview */
 	{ 0x00, 0x04, KEY_LIST }, /* RecordList */
-	{ 0x00, 0x0f, KEY_TEXT } /* Teletext */
+	{ 0x00, 0x0f, KEY_TEXT }, /* Teletext */
+	{ 0x00, 0x41, KEY_PREVIOUSSONG },
+	{ 0x00, 0x42, KEY_NEXTSONG },
+	{ 0x00, 0x4b, KEY_UP },
+	{ 0x00, 0x51, KEY_DOWN },
+	{ 0x00, 0x4e, KEY_LEFT },
+	{ 0x00, 0x52, KEY_RIGHT },
+	{ 0x00, 0x4f, KEY_ENTER },
+	{ 0x00, 0x13, KEY_CANCEL },
+	{ 0x00, 0x4a, KEY_CLEAR },
+	{ 0x00, 0x54, KEY_PRINT }, /* Capture */
+	{ 0x00, 0x43, KEY_SUBTITLE }, /* Subtitle/CC */
+	{ 0x00, 0x08, KEY_VIDEO }, /* A/V */
+	{ 0x00, 0x07, KEY_SLEEP }, /* Hibernate */
+	{ 0x00, 0x45, KEY_ZOOM }, /* Zoom+ */
+	{ 0x00, 0x18, KEY_RED},
+	{ 0x00, 0x53, KEY_GREEN},
+	{ 0x00, 0x5e, KEY_YELLOW},
+	{ 0x00, 0x5f, KEY_BLUE}
 };
 
 static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
@@ -217,18 +235,18 @@
 	.num_adapters = 1,
 	.adapter = {
 		{
-	.frontend_attach  = vp7045_frontend_attach,
-	/* parameter for the MPEG2-data transfer */
+			.frontend_attach  = vp7045_frontend_attach,
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 		}
 	},
 	.power_ctrl       = vp7045_power_ctrl,
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index aebb8d6f..af314bb 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -172,6 +172,22 @@
 	  A DVB-T tuner module. Designed for mobile usage. Say Y when you want
 	  to support this frontend.
 
+config DVB_DIB7000M
+	tristate "DiBcom 7000MA/MB/PA/PB/MC"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+	  to support this frontend.
+
+config DVB_DIB7000P
+	tristate "DiBcom 7000PC"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+	  to support this frontend.
+
 comment "DVB-C (cable) frontends"
 	depends on DVB_CORE
 
@@ -281,6 +297,14 @@
 	help
 	  A driver for the silicon IF tuner MT2060 from Microtune.
 
+config DVB_TUNER_LGH06XF
+	tristate "LG TDVS-H06xF ATSC tuner"
+	depends on DVB_CORE && I2C
+	select DVB_PLL
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A driver for the LG TDVS-H06xF ATSC tuner family.
+
 comment "Miscellaneous devices"
 	depends on DVB_CORE
 
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index dce9cf0..3fa6e5d 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -13,6 +13,8 @@
 obj-$(CONFIG_DVB_L64781) += l64781.o
 obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
 obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o
 obj-$(CONFIG_DVB_MT312) += mt312.o
 obj-$(CONFIG_DVB_VES1820) += ves1820.o
 obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
@@ -37,3 +39,4 @@
 obj-$(CONFIG_DVB_TDA826X) += tda826x.o
 obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
 obj-$(CONFIG_DVB_TUA6100) += tua6100.o
+obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
index 3561a77..5da6617 100644
--- a/drivers/media/dvb/frontends/dib3000mc.c
+++ b/drivers/media/dvb/frontends/dib3000mc.c
@@ -511,16 +511,11 @@
 
 
 	/* a channel for autosearch */
-	reg = 0;
-	if (chan->nfft == -1 && chan->guard == -1) reg = 7;
-	if (chan->nfft == -1 && chan->guard != -1) reg = 2;
-	if (chan->nfft != -1 && chan->guard == -1) reg = 3;
-
 	fchan.nfft = 1; fchan.guard = 0; fchan.nqam = 2;
 	fchan.vit_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2;
 	fchan.vit_hrch = 0; fchan.vit_select_hp = 1;
 
-	dib3000mc_set_channel_cfg(state, &fchan, reg);
+	dib3000mc_set_channel_cfg(state, &fchan, 7);
 
 	reg = dib3000mc_read_word(state, 0);
 	dib3000mc_write_word(state, 0, reg | (1 << 8));
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c
new file mode 100644
index 0000000..f5d40aa
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000m.c
@@ -0,0 +1,1191 @@
+/*
+ * Linux-DVB Driver for DiBcom's DiB7000M and
+ *              first generation DiB7000P-demodulator-family.
+ *
+ * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/)
+ *
+ * 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, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "dib7000m.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M:"); printk(args); } } while (0)
+
+struct dib7000m_state {
+	struct dvb_frontend demod;
+    struct dib7000m_config cfg;
+
+	u8 i2c_addr;
+	struct i2c_adapter   *i2c_adap;
+
+	struct dibx000_i2c_master i2c_master;
+
+/* offset is 1 in case of the 7000MC */
+	u8 reg_offs;
+
+	u16 wbd_ref;
+
+	u8 current_band;
+	fe_bandwidth_t current_bandwidth;
+	struct dibx000_agc_config *current_agc;
+	u32 timf;
+
+	u16 revision;
+};
+
+enum dib7000m_power_mode {
+	DIB7000M_POWER_ALL = 0,
+
+	DIB7000M_POWER_NO,
+	DIB7000M_POWER_INTERF_ANALOG_AGC,
+	DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD,
+	DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD,
+	DIB7000M_POWER_INTERFACE_ONLY,
+};
+
+static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
+{
+	u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
+	u8 rb[2];
+	struct i2c_msg msg[2] = {
+		{ .addr = state->i2c_addr >> 1, .flags = 0,        .buf = wb, .len = 2 },
+		{ .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+	};
+
+	if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+		dprintk("i2c read error on %d\n",reg);
+
+	return (rb[0] << 8) | rb[1];
+}
+
+static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
+{
+	u8 b[4] = {
+		(reg >> 8) & 0xff, reg & 0xff,
+		(val >> 8) & 0xff, val & 0xff,
+	};
+	struct i2c_msg msg = {
+		.addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+	};
+	return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode)
+{
+	int    ret = 0;
+	u16 outreg, fifo_threshold, smo_mode,
+		sram = 0x0005; /* by default SRAM output is disabled */
+
+	outreg = 0;
+	fifo_threshold = 1792;
+	smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1);
+
+	dprintk("-I-  Setting output mode for demod %p to %d\n",
+			&state->demod, mode);
+
+	switch (mode) {
+		case OUTMODE_MPEG2_PAR_GATED_CLK:   // STBs with parallel gated clock
+			outreg = (1 << 10);  /* 0x0400 */
+			break;
+		case OUTMODE_MPEG2_PAR_CONT_CLK:    // STBs with parallel continues clock
+			outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+			break;
+		case OUTMODE_MPEG2_SERIAL:          // STBs with serial input
+			outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */
+			break;
+		case OUTMODE_DIVERSITY:
+			if (state->cfg.hostbus_diversity)
+				outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+			else
+				sram   |= 0x0c00;
+			break;
+		case OUTMODE_MPEG2_FIFO:            // e.g. USB feeding
+			smo_mode |= (3 << 1);
+			fifo_threshold = 512;
+			outreg = (1 << 10) | (5 << 6);
+			break;
+		case OUTMODE_HIGH_Z:  // disable
+			outreg = 0;
+			break;
+		default:
+			dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+			break;
+	}
+
+	if (state->cfg.output_mpeg2_in_188_bytes)
+		smo_mode |= (1 << 5) ;
+
+	ret |= dib7000m_write_word(state,  294 + state->reg_offs, smo_mode);
+	ret |= dib7000m_write_word(state,  295 + state->reg_offs, fifo_threshold); /* synchronous fread */
+	ret |= dib7000m_write_word(state, 1795, outreg);
+	ret |= dib7000m_write_word(state, 1805, sram);
+
+	return ret;
+}
+
+static int dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode)
+{
+	/* by default everything is going to be powered off */
+	u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906  = 0x3fff;
+
+	/* now, depending on the requested mode, we power on */
+	switch (mode) {
+		/* power up everything in the demod */
+		case DIB7000M_POWER_ALL:
+			reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000;
+			break;
+
+		/* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */
+		case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */
+			reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2));
+			break;
+
+		case DIB7000M_POWER_INTERF_ANALOG_AGC:
+			reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10));
+			reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2));
+			reg_906 &= ~((1 << 0));
+			break;
+
+		case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD:
+			reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000;
+			break;
+
+		case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD:
+			reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000;
+			break;
+		case DIB7000M_POWER_NO:
+			break;
+	}
+
+	/* always power down unused parts */
+	if (!state->cfg.mobile_mode)
+		reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);
+
+	/* P_sdio_select_clk = 0 on MC */
+	if (state->revision != 0x4000)
+		reg_906 <<= 1;
+
+	dib7000m_write_word(state,  903,  reg_903);
+	dib7000m_write_word(state,  904,  reg_904);
+	dib7000m_write_word(state,  905,  reg_905);
+	dib7000m_write_word(state,  906,  reg_906);
+
+	return 0;
+}
+
+static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no)
+{
+	int ret = 0;
+	u16 reg_913 = dib7000m_read_word(state, 913),
+	       reg_914 = dib7000m_read_word(state, 914);
+
+	switch (no) {
+		case DIBX000_SLOW_ADC_ON:
+			reg_914 |= (1 << 1) | (1 << 0);
+			ret |= dib7000m_write_word(state, 914, reg_914);
+			reg_914 &= ~(1 << 1);
+			break;
+
+		case DIBX000_SLOW_ADC_OFF:
+			reg_914 |=  (1 << 1) | (1 << 0);
+			break;
+
+		case DIBX000_ADC_ON:
+			if (state->revision == 0x4000) { // workaround for PA/MA
+				// power-up ADC
+				dib7000m_write_word(state, 913, 0);
+				dib7000m_write_word(state, 914, reg_914 & 0x3);
+				// power-down bandgag
+				dib7000m_write_word(state, 913, (1 << 15));
+				dib7000m_write_word(state, 914, reg_914 & 0x3);
+			}
+
+			reg_913 &= 0x0fff;
+			reg_914 &= 0x0003;
+			break;
+
+		case DIBX000_ADC_OFF: // leave the VBG voltage on
+			reg_913 |= (1 << 14) | (1 << 13) | (1 << 12);
+			reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
+			break;
+
+		case DIBX000_VBG_ENABLE:
+			reg_913 &= ~(1 << 15);
+			break;
+
+		case DIBX000_VBG_DISABLE:
+			reg_913 |= (1 << 15);
+			break;
+
+		default:
+			break;
+	}
+
+//	dprintk("-D-  913: %x, 914: %x\n", reg_913, reg_914);
+
+	ret |= dib7000m_write_word(state, 913, reg_913);
+	ret |= dib7000m_write_word(state, 914, reg_914);
+
+	return ret;
+}
+
+static int dib7000m_set_bandwidth(struct dvb_frontend *demod, u8 bw_idx)
+{
+	struct dib7000m_state *state = demod->demodulator_priv;
+	u32 timf;
+
+	// store the current bandwidth for later use
+	state->current_bandwidth = bw_idx;
+
+	if (state->timf == 0) {
+		dprintk("-D-  Using default timf\n");
+		timf = state->cfg.bw->timf;
+	} else {
+		dprintk("-D-  Using updated timf\n");
+		timf = state->timf;
+	}
+
+	timf = timf * (BW_INDEX_TO_KHZ(bw_idx) / 100) / 80;
+
+	dib7000m_write_word(state, 23, (timf >> 16) & 0xffff);
+	dib7000m_write_word(state, 24, (timf      ) & 0xffff);
+
+	return 0;
+}
+
+static int dib7000m_sad_calib(struct dib7000m_state *state)
+{
+
+/* internal */
+//	dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+	dib7000m_write_word(state, 929, (0 << 1) | (0 << 0));
+	dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096
+
+	/* do the calibration */
+	dib7000m_write_word(state, 929, (1 << 0));
+	dib7000m_write_word(state, 929, (0 << 0));
+
+	msleep(1);
+
+	return 0;
+}
+
+static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw)
+{
+	dib7000m_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff);
+	dib7000m_write_word(state, 19,  (bw->internal*1000)        & 0xffff);
+	dib7000m_write_word(state, 21,  (bw->ifreq          >> 16) & 0xffff);
+	dib7000m_write_word(state, 22,   bw->ifreq                 & 0xffff);
+
+	dib7000m_write_word(state, 928, bw->sad_cfg);
+}
+
+static void dib7000m_reset_pll(struct dib7000m_state *state)
+{
+	const struct dibx000_bandwidth_config *bw = state->cfg.bw;
+	u16 reg_907,reg_910;
+
+	/* default */
+	reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) |
+		(bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) |
+		(bw->enable_refdiv << 1) | (0 << 0);
+	reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset;
+
+	// for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value)
+	// this is only working only for 30 MHz crystals
+	if (!state->cfg.quartz_direct) {
+		reg_910 |= (1 << 5);  // forcing the predivider to 1
+
+		// if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2)
+		if(state->cfg.input_clk_is_div_2)
+			reg_907 |= (16 << 9);
+		else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary
+			reg_907 |= (8 << 9);
+	} else {
+		reg_907 |= (bw->pll_ratio & 0x3f) << 9;
+		reg_910 |= (bw->pll_prediv << 5);
+	}
+
+	dib7000m_write_word(state, 910, reg_910); // pll cfg
+	dib7000m_write_word(state, 907, reg_907); // clk cfg0
+	dib7000m_write_word(state, 908, 0x0006);  // clk_cfg1
+
+	dib7000m_reset_pll_common(state, bw);
+}
+
+static void dib7000mc_reset_pll(struct dib7000m_state *state)
+{
+	const struct dibx000_bandwidth_config *bw = state->cfg.bw;
+
+	// clk_cfg0
+	dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0));
+
+	// clk_cfg1
+	//dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) |
+	dib7000m_write_word(state, 908, (0 << 14) | (3 << 12) |(0 << 11) |
+			(bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) |
+			(bw->pll_bypass << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0));
+
+	// smpl_cfg
+	dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7));
+
+	dib7000m_reset_pll_common(state, bw);
+}
+
+static int dib7000m_reset_gpio(struct dib7000m_state *st)
+{
+	/* reset the GPIOs */
+	dprintk("-D-  gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",
+		st->cfg.gpio_dir, st->cfg.gpio_val,st->cfg.gpio_pwm_pos);
+
+	dib7000m_write_word(st, 773, st->cfg.gpio_dir);
+	dib7000m_write_word(st, 774, st->cfg.gpio_val);
+
+	/* TODO 782 is P_gpio_od */
+
+	dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos);
+
+	dib7000m_write_word(st, 780, st->cfg.pwm_freq_div);
+	return 0;
+}
+
+static int dib7000m_demod_reset(struct dib7000m_state *state)
+{
+	dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);
+
+	/* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */
+	dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE);
+
+	/* restart all parts */
+	dib7000m_write_word(state,  898, 0xffff);
+	dib7000m_write_word(state,  899, 0xffff);
+	dib7000m_write_word(state,  900, 0xff0f);
+	dib7000m_write_word(state,  901, 0xfffc);
+
+	dib7000m_write_word(state,  898, 0);
+	dib7000m_write_word(state,  899, 0);
+	dib7000m_write_word(state,  900, 0);
+	dib7000m_write_word(state,  901, 0);
+
+	if (state->revision == 0x4000)
+		dib7000m_reset_pll(state);
+	else
+		dib7000mc_reset_pll(state);
+
+	if (dib7000m_reset_gpio(state) != 0)
+		dprintk("-E-  GPIO reset was not successful.\n");
+
+	if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+		dprintk("-E-  OUTPUT_MODE could not be resetted.\n");
+
+	/* unforce divstr regardless whether i2c enumeration was done or not */
+	dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) );
+
+	dib7000m_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+
+	dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+	dib7000m_sad_calib(state);
+	dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF);
+
+	dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY);
+
+	return 0;
+}
+
+static void dib7000m_restart_agc(struct dib7000m_state *state)
+{
+	// P_restart_iqc & P_restart_agc
+	dib7000m_write_word(state, 898, 0x0c00);
+	dib7000m_write_word(state, 898, 0x0000);
+}
+
+static int dib7000m_agc_soft_split(struct dib7000m_state *state)
+{
+	u16 agc,split_offset;
+
+	if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0)
+		return 0;
+
+	// n_agc_global
+	agc = dib7000m_read_word(state, 390);
+
+	if (agc > state->current_agc->split.min_thres)
+		split_offset = state->current_agc->split.min;
+	else if (agc < state->current_agc->split.max_thres)
+		split_offset = state->current_agc->split.max;
+	else
+		split_offset = state->current_agc->split.max *
+			(agc - state->current_agc->split.min_thres) /
+			(state->current_agc->split.max_thres - state->current_agc->split.min_thres);
+
+	dprintk("AGC split_offset: %d\n",split_offset);
+
+	// P_agc_force_split and P_agc_split_offset
+	return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset);
+}
+
+static int dib7000m_update_lna(struct dib7000m_state *state)
+{
+	int i;
+	u16 dyn_gain;
+
+	// when there is no LNA to program return immediatly
+	if (state->cfg.update_lna == NULL)
+		return 0;
+
+	msleep(60);
+	for (i = 0; i < 20; i++) {
+		// read dyn_gain here (because it is demod-dependent and not tuner)
+		dyn_gain = dib7000m_read_word(state, 390);
+
+		dprintk("agc global: %d\n", dyn_gain);
+
+		if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed
+			dib7000m_restart_agc(state);
+			msleep(60);
+		} else
+			break;
+	}
+	return 0;
+}
+
+static void dib7000m_set_agc_config(struct dib7000m_state *state, u8 band)
+{
+	struct dibx000_agc_config *agc = NULL;
+	int i;
+	if (state->current_band == band)
+		return;
+	state->current_band = band;
+
+	for (i = 0; i < state->cfg.agc_config_count; i++)
+		if (state->cfg.agc[i].band_caps & band) {
+			agc = &state->cfg.agc[i];
+			break;
+		}
+
+	if (agc == NULL) {
+		dprintk("-E-  No valid AGC configuration found for band 0x%02x\n",band);
+		return;
+	}
+
+	state->current_agc = agc;
+
+	/* AGC */
+	dib7000m_write_word(state, 72 ,  agc->setup);
+	dib7000m_write_word(state, 73 ,  agc->inv_gain);
+	dib7000m_write_word(state, 74 ,  agc->time_stabiliz);
+	dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock);
+
+	// Demod AGC loop configuration
+	dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp);
+	dib7000m_write_word(state, 99, (agc->beta_mant  << 6) | agc->beta_exp);
+
+	dprintk("-D-  WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
+		state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
+
+	/* AGC continued */
+	if (state->wbd_ref != 0)
+		dib7000m_write_word(state, 102, state->wbd_ref);
+	else // use default
+		dib7000m_write_word(state, 102, agc->wbd_ref);
+
+	dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) );
+	dib7000m_write_word(state, 104,  agc->agc1_max);
+	dib7000m_write_word(state, 105,  agc->agc1_min);
+	dib7000m_write_word(state, 106,  agc->agc2_max);
+	dib7000m_write_word(state, 107,  agc->agc2_min);
+	dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 );
+	dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+	dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+	dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+	if (state->revision > 0x4000) { // settings for the MC
+		dib7000m_write_word(state, 71,   agc->agc1_pt3);
+//		dprintk("-D-  929: %x %d %d\n",
+//			(dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel);
+		dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2));
+	} else {
+		// wrong default values
+		u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 };
+		for (i = 0; i < 9; i++)
+			dib7000m_write_word(state, 88 + i, b[i]);
+	}
+}
+
+static void dib7000m_update_timf_freq(struct dib7000m_state *state)
+{
+	u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437);
+	state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100);
+	dib7000m_write_word(state, 23, (u16) (timf >> 16));
+	dib7000m_write_word(state, 24, (u16) (timf & 0xffff));
+	dprintk("-D-  Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf);
+}
+
+static void dib7000m_set_channel(struct dib7000m_state *state, struct dibx000_ofdm_channel *ch, u8 seq)
+{
+	u16 value, est[4];
+
+	dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->RF_kHz));
+
+	/* nfft, guard, qam, alpha */
+	dib7000m_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha));
+	dib7000m_write_word(state, 5, (seq << 4));
+
+	/* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */
+	value = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1);
+	if (ch->vit_hrch == 0 || ch->vit_select_hp == 1)
+		value |= (ch->vit_code_rate_hp << 1);
+	else
+		value |= (ch->vit_code_rate_lp << 1);
+	dib7000m_write_word(state, 267 + state->reg_offs, value);
+
+	/* offset loop parameters */
+
+	/* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */
+	dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80);
+
+	/* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+	dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+	/* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */
+	dib7000m_write_word(state, 32, (0 << 4) | 0x3);
+
+	/* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */
+	dib7000m_write_word(state, 33, (0 << 4) | 0x5);
+
+	/* P_dvsy_sync_wait */
+	switch (ch->nfft) {
+		case 1: value = 256; break;
+		case 2: value = 128; break;
+		case 0:
+		default: value = 64; break;
+	}
+	value *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin
+	value <<= 4;
+
+	/* deactive the possibility of diversity reception if extended interleave - not for 7000MC */
+	/* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
+	if (ch->intlv_native || state->revision > 0x4000)
+		value |= (1 << 2) | (2 << 0);
+	else
+		value |= 0;
+	dib7000m_write_word(state, 266 + state->reg_offs, value);
+
+	/* channel estimation fine configuration */
+	switch (ch->nqam) {
+		case 2:
+			est[0] = 0x0148;       /* P_adp_regul_cnt 0.04 */
+			est[1] = 0xfff0;       /* P_adp_noise_cnt -0.002 */
+			est[2] = 0x00a4;       /* P_adp_regul_ext 0.02 */
+			est[3] = 0xfff8;       /* P_adp_noise_ext -0.001 */
+			break;
+		case 1:
+			est[0] = 0x023d;       /* P_adp_regul_cnt 0.07 */
+			est[1] = 0xffdf;       /* P_adp_noise_cnt -0.004 */
+			est[2] = 0x00a4;       /* P_adp_regul_ext 0.02 */
+			est[3] = 0xfff0;       /* P_adp_noise_ext -0.002 */
+			break;
+		default:
+			est[0] = 0x099a;       /* P_adp_regul_cnt 0.3 */
+			est[1] = 0xffae;       /* P_adp_noise_cnt -0.01 */
+			est[2] = 0x0333;       /* P_adp_regul_ext 0.1 */
+			est[3] = 0xfff8;       /* P_adp_noise_ext -0.002 */
+			break;
+	}
+	for (value = 0; value < 4; value++)
+		dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]);
+
+	// set power-up level: interf+analog+AGC
+	dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC);
+	dib7000m_set_adc_state(state, DIBX000_ADC_ON);
+
+	msleep(7);
+
+	//AGC initialization
+	if (state->cfg.agc_control)
+		state->cfg.agc_control(&state->demod, 1);
+
+	dib7000m_restart_agc(state);
+
+	// wait AGC rough lock time
+	msleep(5);
+
+	dib7000m_update_lna(state);
+	dib7000m_agc_soft_split(state);
+
+	// wait AGC accurate lock time
+	msleep(7);
+
+	if (state->cfg.agc_control)
+		state->cfg.agc_control(&state->demod, 0);
+
+	// set power-up level: autosearch
+	dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD);
+}
+
+static int dib7000m_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+	struct dib7000m_state *state = demod->demodulator_priv;
+	struct dibx000_ofdm_channel auto_ch;
+	int ret = 0;
+	u32 value;
+
+	INIT_OFDM_CHANNEL(&auto_ch);
+	auto_ch.RF_kHz           = ch->RF_kHz;
+	auto_ch.Bw               = ch->Bw;
+	auto_ch.nqam             = 2;
+	auto_ch.guard            = 0;
+	auto_ch.nfft             = 1;
+	auto_ch.vit_alpha        = 1;
+	auto_ch.vit_select_hp    = 1;
+	auto_ch.vit_code_rate_hp = 2;
+	auto_ch.vit_code_rate_lp = 3;
+	auto_ch.vit_hrch         = 0;
+	auto_ch.intlv_native     = 1;
+
+	dib7000m_set_channel(state, &auto_ch, 7);
+
+	// always use the setting for 8MHz here lock_time for 7,6 MHz are longer
+	value = 30 * state->cfg.bw->internal;
+	ret |= dib7000m_write_word(state, 6,  (u16) ((value >> 16) & 0xffff)); // lock0 wait time
+	ret |= dib7000m_write_word(state, 7,  (u16)  (value        & 0xffff)); // lock0 wait time
+	value = 100 * state->cfg.bw->internal;
+	ret |= dib7000m_write_word(state, 8,  (u16) ((value >> 16) & 0xffff)); // lock1 wait time
+	ret |= dib7000m_write_word(state, 9,  (u16)  (value        & 0xffff)); // lock1 wait time
+	value = 500 * state->cfg.bw->internal;
+	ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time
+	ret |= dib7000m_write_word(state, 11, (u16)  (value        & 0xffff)); // lock2 wait time
+
+	// start search
+	value = dib7000m_read_word(state, 0);
+	ret |= dib7000m_write_word(state, 0, value | (1 << 9));
+
+	/* clear n_irq_pending */
+	if (state->revision == 0x4000)
+		dib7000m_write_word(state, 1793, 0);
+	else
+		dib7000m_read_word(state, 537);
+
+	ret |= dib7000m_write_word(state, 0, (u16) value);
+
+	return ret;
+}
+
+static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg)
+{
+	u16 irq_pending = dib7000m_read_word(state, reg);
+
+	if (irq_pending & 0x1) { // failed
+		dprintk("#\n");
+		return 1;
+	}
+
+	if (irq_pending & 0x2) { // succeeded
+		dprintk("!\n");
+		return 2;
+	}
+	return 0; // still pending
+}
+
+static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod)
+{
+	struct dib7000m_state *state = demod->demodulator_priv;
+	if (state->revision == 0x4000)
+		return dib7000m_autosearch_irq(state, 1793);
+	else
+		return dib7000m_autosearch_irq(state, 537);
+}
+
+static int dib7000m_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+	struct dib7000m_state *state = demod->demodulator_priv;
+	int ret = 0;
+	u16 value;
+
+	// we are already tuned - just resuming from suspend
+	if (ch != NULL)
+		dib7000m_set_channel(state, ch, 0);
+	else
+		return -EINVAL;
+
+	// restart demod
+	ret |= dib7000m_write_word(state, 898, 0x4000);
+	ret |= dib7000m_write_word(state, 898, 0x0000);
+	msleep(45);
+
+	ret |= dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD);
+	/* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+	ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+	// never achieved a lock with that bandwidth so far - wait for timfreq to update
+	if (state->timf == 0)
+		msleep(200);
+
+	//dump_reg(state);
+	/* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */
+	value = (6 << 8) | 0x80;
+	switch (ch->nfft) {
+		case 0: value |= (7 << 12); break;
+		case 1: value |= (9 << 12); break;
+		case 2: value |= (8 << 12); break;
+	}
+	ret |= dib7000m_write_word(state, 26, value);
+
+	/* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */
+	value = (0 << 4);
+	switch (ch->nfft) {
+		case 0: value |= 0x6; break;
+		case 1: value |= 0x8; break;
+		case 2: value |= 0x7; break;
+	}
+	ret |= dib7000m_write_word(state, 32, value);
+
+	/* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */
+	value = (0 << 4);
+	switch (ch->nfft) {
+		case 0: value |= 0x6; break;
+		case 1: value |= 0x8; break;
+		case 2: value |= 0x7; break;
+	}
+	ret |= dib7000m_write_word(state, 33,  value);
+
+	// we achieved a lock - it's time to update the osc freq
+	if ((dib7000m_read_word(state, 535) >> 6)  & 0x1)
+		dib7000m_update_timf_freq(state);
+
+	return ret;
+}
+
+static int dib7000m_init(struct dvb_frontend *demod)
+{
+	struct dib7000m_state *state = demod->demodulator_priv;
+	int ret = 0;
+	u8 o = state->reg_offs;
+
+	dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);
+
+	if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0)
+		dprintk("-E-  could not start Slow ADC\n");
+
+	if (state->cfg.dvbt_mode)
+		dib7000m_write_word(state, 1796, 0x0); // select DVB-T output
+
+	if (state->cfg.mobile_mode)
+		ret |= dib7000m_write_word(state, 261 + o, 2);
+	else
+		ret |= dib7000m_write_word(state, 224 + o, 1);
+
+	ret |= dib7000m_write_word(state, 173 + o, 0);
+	ret |= dib7000m_write_word(state, 174 + o, 0);
+	ret |= dib7000m_write_word(state, 175 + o, 0);
+	ret |= dib7000m_write_word(state, 176 + o, 0);
+	ret |= dib7000m_write_word(state, 177 + o, 0);
+	ret |= dib7000m_write_word(state, 178 + o, 0);
+	ret |= dib7000m_write_word(state, 179 + o, 0);
+	ret |= dib7000m_write_word(state, 180 + o, 0);
+
+	// P_corm_thres Lock algorithms configuration
+	ret |= dib7000m_write_word(state, 26, 0x6680);
+
+	// P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on
+	ret |= dib7000m_write_word(state, 170 + o, 0x0410);
+	// P_fft_nb_to_cut
+	ret |= dib7000m_write_word(state, 182 + o, 8192);
+	// P_pha3_thres
+	ret |= dib7000m_write_word(state, 195 + o, 0x0ccd);
+	// P_cti_use_cpe, P_cti_use_prog
+	ret |= dib7000m_write_word(state, 196 + o,     0);
+	// P_cspu_regul, P_cspu_win_cut
+	ret |= dib7000m_write_word(state, 205 + o, 0x200f);
+	// P_adp_regul_cnt
+	ret |= dib7000m_write_word(state, 214 + o, 0x023d);
+	// P_adp_noise_cnt
+	ret |= dib7000m_write_word(state, 215 + o, 0x00a4);
+	// P_adp_regul_ext
+	ret |= dib7000m_write_word(state, 216 + o, 0x00a4);
+	// P_adp_noise_ext
+	ret |= dib7000m_write_word(state, 217 + o, 0x7ff0);
+	// P_adp_fil
+	ret |= dib7000m_write_word(state, 218 + o, 0x3ccc);
+
+	// P_2d_byp_ti_num
+	ret |= dib7000m_write_word(state, 226 + o, 0);
+
+	// P_fec_*
+	ret |= dib7000m_write_word(state, 281 + o, 0x0010);
+	// P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+	ret |= dib7000m_write_word(state, 294 + o,0x0062);
+
+	// P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ...
+	if(state->cfg.tuner_is_baseband)
+		ret |= dib7000m_write_word(state, 36, 0x0755);
+	else
+		ret |= dib7000m_write_word(state, 36, 0x1f55);
+
+	// auto search configuration
+	ret |= dib7000m_write_word(state, 2,  0x0004);
+	ret |= dib7000m_write_word(state, 3,  0x1000);
+	ret |= dib7000m_write_word(state, 4,  0x0814);
+	ret |= dib7000m_write_word(state, 6,  0x001b);
+	ret |= dib7000m_write_word(state, 7,  0x7740);
+	ret |= dib7000m_write_word(state, 8,  0x005b);
+	ret |= dib7000m_write_word(state, 9,  0x8d80);
+	ret |= dib7000m_write_word(state, 10, 0x01c9);
+	ret |= dib7000m_write_word(state, 11, 0xc380);
+	ret |= dib7000m_write_word(state, 12, 0x0000);
+	ret |= dib7000m_write_word(state, 13, 0x0080);
+	ret |= dib7000m_write_word(state, 14, 0x0000);
+	ret |= dib7000m_write_word(state, 15, 0x0090);
+	ret |= dib7000m_write_word(state, 16, 0x0001);
+	ret |= dib7000m_write_word(state, 17, 0xd4c0);
+	ret |= dib7000m_write_word(state, 263 + o,0x0001);
+
+	// P_divclksel=3 P_divbitsel=1
+	if (state->revision == 0x4000)
+		dib7000m_write_word(state, 909, (3 << 10) | (1 << 6));
+	else
+		dib7000m_write_word(state, 909, (3 << 4) | 1);
+
+	// Tuner IO bank: max drive (14mA)
+	ret |= dib7000m_write_word(state, 912 ,0x2c8a);
+
+	ret |= dib7000m_write_word(state, 1817, 1);
+
+	return ret;
+}
+
+static int dib7000m_sleep(struct dvb_frontend *demod)
+{
+	struct dib7000m_state *st = demod->demodulator_priv;
+	dib7000m_set_output_mode(st, OUTMODE_HIGH_Z);
+	return dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY) |
+		dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) |
+		dib7000m_set_adc_state(st, DIBX000_ADC_OFF);
+}
+
+static int dib7000m_identify(struct dib7000m_state *state)
+{
+	u16 value;
+	if ((value = dib7000m_read_word(state, 896)) != 0x01b3) {
+		dprintk("-E-  DiB7000M: wrong Vendor ID (read=0x%x)\n",value);
+		return -EREMOTEIO;
+	}
+
+	state->revision = dib7000m_read_word(state, 897);
+	if (state->revision != 0x4000 &&
+		state->revision != 0x4001 &&
+		state->revision != 0x4002) {
+		dprintk("-E-  DiB7000M: wrong Device ID (%x)\n",value);
+		return -EREMOTEIO;
+	}
+
+	/* protect this driver to be used with 7000PC */
+	if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) {
+		dprintk("-E-  DiB7000M: this driver does not work with DiB7000PC\n");
+		return -EREMOTEIO;
+	}
+
+	switch (state->revision) {
+		case 0x4000: dprintk("-I-  found DiB7000MA/PA/MB/PB\n"); break;
+		case 0x4001: state->reg_offs = 1; dprintk("-I-  found DiB7000HC\n"); break;
+		case 0x4002: state->reg_offs = 1; dprintk("-I-  found DiB7000MC\n"); break;
+	}
+
+	return 0;
+}
+
+
+static int dib7000m_get_frontend(struct dvb_frontend* fe,
+				struct dvb_frontend_parameters *fep)
+{
+	struct dib7000m_state *state = fe->demodulator_priv;
+	u16 tps = dib7000m_read_word(state,480);
+
+	fep->inversion = INVERSION_AUTO;
+
+	fep->u.ofdm.bandwidth = state->current_bandwidth;
+
+	switch ((tps >> 8) & 0x3) {
+		case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+		case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+		/* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
+	}
+
+	switch (tps & 0x3) {
+		case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+		case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+		case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+		case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+	}
+
+	switch ((tps >> 14) & 0x3) {
+		case 0: fep->u.ofdm.constellation = QPSK; break;
+		case 1: fep->u.ofdm.constellation = QAM_16; break;
+		case 2:
+		default: fep->u.ofdm.constellation = QAM_64; break;
+	}
+
+	/* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+	/* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */
+
+	fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+	switch ((tps >> 5) & 0x7) {
+		case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+		case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+		case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+		case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+		case 7:
+		default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+
+	}
+
+	switch ((tps >> 2) & 0x7) {
+		case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+		case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+		case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+		case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+		case 7:
+		default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+	}
+
+	/* native interleaver: (dib7000m_read_word(state, 481) >>  5) & 0x1 */
+
+	return 0;
+}
+
+static int dib7000m_set_frontend(struct dvb_frontend* fe,
+				struct dvb_frontend_parameters *fep)
+{
+	struct dib7000m_state *state = fe->demodulator_priv;
+	struct dibx000_ofdm_channel ch;
+
+	INIT_OFDM_CHANNEL(&ch);
+	FEP2DIB(fep,&ch);
+
+	state->current_bandwidth = fep->u.ofdm.bandwidth;
+	dib7000m_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe, fep);
+
+	if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+		fep->u.ofdm.guard_interval    == GUARD_INTERVAL_AUTO ||
+		fep->u.ofdm.constellation     == QAM_AUTO ||
+		fep->u.ofdm.code_rate_HP      == FEC_AUTO) {
+		int i = 800, found;
+
+		dib7000m_autosearch_start(fe, &ch);
+		do {
+			msleep(1);
+			found = dib7000m_autosearch_is_irq(fe);
+		} while (found == 0 && i--);
+
+		dprintk("autosearch returns: %d\n",found);
+		if (found == 0 || found == 1)
+			return 0; // no channel found
+
+		dib7000m_get_frontend(fe, fep);
+		FEP2DIB(fep, &ch);
+	}
+
+	/* make this a config parameter */
+	dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+	return dib7000m_tune(fe, &ch);
+}
+
+static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+	struct dib7000m_state *state = fe->demodulator_priv;
+	u16 lock = dib7000m_read_word(state, 535);
+
+	*stat = 0;
+
+	if (lock & 0x8000)
+		*stat |= FE_HAS_SIGNAL;
+	if (lock & 0x3000)
+		*stat |= FE_HAS_CARRIER;
+	if (lock & 0x0100)
+		*stat |= FE_HAS_VITERBI;
+	if (lock & 0x0010)
+		*stat |= FE_HAS_SYNC;
+	if (lock & 0x0008)
+		*stat |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct dib7000m_state *state = fe->demodulator_priv;
+	*ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527);
+	return 0;
+}
+
+static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+	struct dib7000m_state *state = fe->demodulator_priv;
+	*unc = dib7000m_read_word(state, 534);
+	return 0;
+}
+
+static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct dib7000m_state *state = fe->demodulator_priv;
+	u16 val = dib7000m_read_word(state, 390);
+	*strength = 65535 - val;
+	return 0;
+}
+
+static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+	*snr = 0x0000;
+	return 0;
+}
+
+static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+	tune->min_delay_ms = 1000;
+	return 0;
+}
+
+static void dib7000m_release(struct dvb_frontend *demod)
+{
+	struct dib7000m_state *st = demod->demodulator_priv;
+	dibx000_exit_i2c_master(&st->i2c_master);
+	kfree(st);
+}
+
+struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+{
+	struct dib7000m_state *st = demod->demodulator_priv;
+	return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+EXPORT_SYMBOL(dib7000m_get_i2c_master);
+
+int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[])
+{
+	struct dib7000m_state st = { .i2c_adap = i2c };
+	int k = 0;
+	u8 new_addr = 0;
+
+	for (k = no_of_demods-1; k >= 0; k--) {
+		st.cfg = cfg[k];
+
+		/* designated i2c address */
+		new_addr          = (0x40 + k) << 1;
+		st.i2c_addr = new_addr;
+		if (dib7000m_identify(&st) != 0) {
+			st.i2c_addr = default_addr;
+			if (dib7000m_identify(&st) != 0) {
+				dprintk("DiB7000M #%d: not identified\n", k);
+				return -EIO;
+			}
+		}
+
+		/* start diversity to pull_down div_str - just for i2c-enumeration */
+		dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY);
+
+		dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output
+
+		/* set new i2c address and force divstart */
+		dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2);
+
+		dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
+	}
+
+	for (k = 0; k < no_of_demods; k++) {
+		st.cfg = cfg[k];
+		st.i2c_addr = (0x40 + k) << 1;
+
+		// unforce divstr
+		dib7000m_write_word(&st,1794, st.i2c_addr << 2);
+
+		/* deactivate div - it was just for i2c-enumeration */
+		dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(dib7000m_i2c_enumeration);
+
+static struct dvb_frontend_ops dib7000m_ops;
+struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg)
+{
+	struct dvb_frontend *demod;
+	struct dib7000m_state *st;
+	st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL);
+	if (st == NULL)
+		return NULL;
+
+	memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config));
+	st->i2c_adap = i2c_adap;
+	st->i2c_addr = i2c_addr;
+
+	demod                   = &st->demod;
+	demod->demodulator_priv = st;
+	memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops));
+
+	if (dib7000m_identify(st) != 0)
+		goto error;
+
+	if (st->revision == 0x4000)
+		dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr);
+	else
+		dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr);
+
+	dib7000m_demod_reset(st);
+
+	return demod;
+
+error:
+	kfree(st);
+	return NULL;
+}
+EXPORT_SYMBOL(dib7000m_attach);
+
+static struct dvb_frontend_ops dib7000m_ops = {
+	.info = {
+		.name = "DiBcom 7000MA/MB/PA/PB/MC",
+		.type = FE_OFDM,
+		.frequency_min      = 44250000,
+		.frequency_max      = 867250000,
+		.frequency_stepsize = 62500,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_RECOVER |
+			FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release              = dib7000m_release,
+
+	.init                 = dib7000m_init,
+	.sleep                = dib7000m_sleep,
+
+	.set_frontend         = dib7000m_set_frontend,
+	.get_tune_settings    = dib7000m_fe_get_tune_settings,
+	.get_frontend         = dib7000m_get_frontend,
+
+	.read_status          = dib7000m_read_status,
+	.read_ber             = dib7000m_read_ber,
+	.read_signal_strength = dib7000m_read_signal_strength,
+	.read_snr             = dib7000m_read_snr,
+	.read_ucblocks        = dib7000m_read_unc_blocks,
+};
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h
new file mode 100644
index 0000000..597e9cc
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000m.h
@@ -0,0 +1,51 @@
+#ifndef DIB7000M_H
+#define DIB7000M_H
+
+#include "dibx000_common.h"
+
+struct dib7000m_config {
+	u8 dvbt_mode;
+	u8 output_mpeg2_in_188_bytes;
+	u8 hostbus_diversity;
+	u8 tuner_is_baseband;
+	u8 mobile_mode;
+	int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+	u8 agc_config_count;
+	struct dibx000_agc_config *agc;
+
+	struct dibx000_bandwidth_config *bw;
+
+#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff
+	u16 gpio_dir;
+#define DIB7000M_GPIO_DEFAULT_VALUES     0x0000
+	u16 gpio_val;
+#define DIB7000M_GPIO_PWM_POS0(v)        ((v & 0xf) << 12)
+#define DIB7000M_GPIO_PWM_POS1(v)        ((v & 0xf) << 8 )
+#define DIB7000M_GPIO_PWM_POS2(v)        ((v & 0xf) << 4 )
+#define DIB7000M_GPIO_PWM_POS3(v)         (v & 0xf)
+#define DIB7000M_GPIO_DEFAULT_PWM_POS    0xffff
+	u16 gpio_pwm_pos;
+
+	u16 pwm_freq_div;
+
+	u8 quartz_direct;
+
+	u8 input_clk_is_div_2;
+
+	int (*agc_control) (struct dvb_frontend *, u8 before);
+};
+
+#define DEFAULT_DIB7000M_I2C_ADDRESS 18
+
+extern struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg);
+extern struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
+
+/* TODO
+extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val);
+extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod);
+extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff);
+extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod);
+*/
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
new file mode 100644
index 0000000..0349a4b
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -0,0 +1,1019 @@
+/*
+ * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC).
+ *
+ * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/)
+ *
+ * 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, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "dib7000p.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P:"); printk(args); } } while (0)
+
+struct dib7000p_state {
+	struct dvb_frontend demod;
+    struct dib7000p_config cfg;
+
+	u8 i2c_addr;
+	struct i2c_adapter   *i2c_adap;
+
+	struct dibx000_i2c_master i2c_master;
+
+	u16 wbd_ref;
+
+	u8 current_band;
+	fe_bandwidth_t current_bandwidth;
+	struct dibx000_agc_config *current_agc;
+	u32 timf;
+
+	u16 gpio_dir;
+	u16 gpio_val;
+};
+
+enum dib7000p_power_mode {
+	DIB7000P_POWER_ALL = 0,
+	DIB7000P_POWER_INTERFACE_ONLY,
+};
+
+static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
+{
+	u8 wb[2] = { reg >> 8, reg & 0xff };
+	u8 rb[2];
+	struct i2c_msg msg[2] = {
+		{ .addr = state->i2c_addr >> 1, .flags = 0,        .buf = wb, .len = 2 },
+		{ .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+	};
+
+	if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+		dprintk("i2c read error on %d\n",reg);
+
+	return (rb[0] << 8) | rb[1];
+}
+
+static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
+{
+	u8 b[4] = {
+		(reg >> 8) & 0xff, reg & 0xff,
+		(val >> 8) & 0xff, val & 0xff,
+	};
+	struct i2c_msg msg = {
+		.addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+	};
+	return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
+{
+	int    ret = 0;
+	u16 outreg, fifo_threshold, smo_mode;
+
+	outreg = 0;
+	fifo_threshold = 1792;
+	smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1);
+
+	dprintk("-I-  Setting output mode for demod %p to %d\n",
+			&state->demod, mode);
+
+	switch (mode) {
+		case OUTMODE_MPEG2_PAR_GATED_CLK:   // STBs with parallel gated clock
+			outreg = (1 << 10);  /* 0x0400 */
+			break;
+		case OUTMODE_MPEG2_PAR_CONT_CLK:    // STBs with parallel continues clock
+			outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+			break;
+		case OUTMODE_MPEG2_SERIAL:          // STBs with serial input
+			outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */
+			break;
+		case OUTMODE_DIVERSITY:
+			if (state->cfg.hostbus_diversity)
+				outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+			else
+				outreg = (1 << 11);
+			break;
+		case OUTMODE_MPEG2_FIFO:            // e.g. USB feeding
+			smo_mode |= (3 << 1);
+			fifo_threshold = 512;
+			outreg = (1 << 10) | (5 << 6);
+			break;
+		case OUTMODE_HIGH_Z:  // disable
+			outreg = 0;
+			break;
+		default:
+			dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+			break;
+	}
+
+	if (state->cfg.output_mpeg2_in_188_bytes)
+		smo_mode |= (1 << 5) ;
+
+	ret |= dib7000p_write_word(state,  235, smo_mode);
+	ret |= dib7000p_write_word(state,  236, fifo_threshold); /* synchronous fread */
+	ret |= dib7000p_write_word(state, 1286, outreg);         /* P_Div_active */
+
+	return ret;
+}
+
+static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode)
+{
+	/* by default everything is powered off */
+	u16 reg_774 = 0xffff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899  = 0x0003,
+		reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff);
+
+	/* now, depending on the requested mode, we power on */
+	switch (mode) {
+		/* power up everything in the demod */
+		case DIB7000P_POWER_ALL:
+			reg_774 = 0x0000; reg_775 = 0x0000; reg_776 = 0x0; reg_899 = 0x0; reg_1280 &= 0x01ff;
+			break;
+		/* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */
+		case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */
+			reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10));
+			break;
+/* TODO following stuff is just converted from the dib7000-driver - check when is used what */
+	}
+
+	dib7000p_write_word(state,  774,  reg_774);
+	dib7000p_write_word(state,  775,  reg_775);
+	dib7000p_write_word(state,  776,  reg_776);
+	dib7000p_write_word(state,  899,  reg_899);
+	dib7000p_write_word(state, 1280, reg_1280);
+
+	return 0;
+}
+
+static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no)
+{
+	u16 reg_908 = dib7000p_read_word(state, 908),
+	       reg_909 = dib7000p_read_word(state, 909);
+
+	switch (no) {
+		case DIBX000_SLOW_ADC_ON:
+			reg_909 |= (1 << 1) | (1 << 0);
+			dib7000p_write_word(state, 909, reg_909);
+			reg_909 &= ~(1 << 1);
+			break;
+
+		case DIBX000_SLOW_ADC_OFF:
+			reg_909 |=  (1 << 1) | (1 << 0);
+			break;
+
+		case DIBX000_ADC_ON:
+			reg_908 &= 0x0fff;
+			reg_909 &= 0x0003;
+			break;
+
+		case DIBX000_ADC_OFF: // leave the VBG voltage on
+			reg_908 |= (1 << 14) | (1 << 13) | (1 << 12);
+			reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
+			break;
+
+		case DIBX000_VBG_ENABLE:
+			reg_908 &= ~(1 << 15);
+			break;
+
+		case DIBX000_VBG_DISABLE:
+			reg_908 |= (1 << 15);
+			break;
+
+		default:
+			break;
+	}
+
+//	dprintk("908: %x, 909: %x\n", reg_908, reg_909);
+
+	dib7000p_write_word(state, 908, reg_908);
+	dib7000p_write_word(state, 909, reg_909);
+}
+
+static int dib7000p_set_bandwidth(struct dvb_frontend *demod, u8 BW_Idx)
+{
+	struct dib7000p_state *state = demod->demodulator_priv;
+	u32 timf;
+
+	// store the current bandwidth for later use
+	state->current_bandwidth = BW_Idx;
+
+	if (state->timf == 0) {
+		dprintk("-D-  Using default timf\n");
+		timf = state->cfg.bw->timf;
+	} else {
+		dprintk("-D-  Using updated timf\n");
+		timf = state->timf;
+	}
+
+	timf = timf * (BW_INDEX_TO_KHZ(BW_Idx) / 100) / 80;
+
+	dprintk("timf: %d\n",timf);
+
+	dib7000p_write_word(state, 23, (timf >> 16) & 0xffff);
+	dib7000p_write_word(state, 24, (timf      ) & 0xffff);
+
+	return 0;
+}
+
+static int dib7000p_sad_calib(struct dib7000p_state *state)
+{
+/* internal */
+//	dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+	dib7000p_write_word(state, 73, (0 << 1) | (0 << 0));
+	dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096
+
+	/* do the calibration */
+	dib7000p_write_word(state, 73, (1 << 0));
+	dib7000p_write_word(state, 73, (0 << 0));
+
+	msleep(1);
+
+	return 0;
+}
+
+static void dib7000p_reset_pll(struct dib7000p_state *state)
+{
+	struct dibx000_bandwidth_config *bw = &state->cfg.bw[0];
+
+	dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset);
+	dib7000p_write_word(state, 900, ((bw->pll_ratio & 0x3f) << 9) | (bw->pll_bypass << 15) | (bw->modulo << 7) | (bw->ADClkSrc << 6) |
+		(bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0));
+
+	dib7000p_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff);
+	dib7000p_write_word(state, 19,  (bw->internal*1000       ) & 0xffff);
+	dib7000p_write_word(state, 21,  (bw->ifreq          >> 16) & 0xffff);
+	dib7000p_write_word(state, 22,  (bw->ifreq               ) & 0xffff);
+
+	dib7000p_write_word(state, 72, bw->sad_cfg);
+}
+
+static int dib7000p_reset_gpio(struct dib7000p_state *st)
+{
+	/* reset the GPIOs */
+	dprintk("-D-  gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",st->gpio_dir, st->gpio_val,st->cfg.gpio_pwm_pos);
+
+	dib7000p_write_word(st, 1029, st->gpio_dir);
+	dib7000p_write_word(st, 1030, st->gpio_val);
+
+	/* TODO 1031 is P_gpio_od */
+
+	dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos);
+
+	dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div);
+	return 0;
+}
+
+static int dib7000p_demod_reset(struct dib7000p_state *state)
+{
+	dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+
+	dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE);
+
+	/* restart all parts */
+	dib7000p_write_word(state,  770, 0xffff);
+	dib7000p_write_word(state,  771, 0xffff);
+	dib7000p_write_word(state,  772, 0x001f);
+	dib7000p_write_word(state,  898, 0x0003);
+	/* except i2c, sdio, gpio - control interfaces */
+	dib7000p_write_word(state, 1280, 0x01fc - ((1 << 7) | (1 << 6) | (1 << 5)) );
+
+	dib7000p_write_word(state,  770, 0);
+	dib7000p_write_word(state,  771, 0);
+	dib7000p_write_word(state,  772, 0);
+	dib7000p_write_word(state,  898, 0);
+	dib7000p_write_word(state, 1280, 0);
+
+	/* default */
+	dib7000p_reset_pll(state);
+
+	if (dib7000p_reset_gpio(state) != 0)
+		dprintk("-E-  GPIO reset was not successful.\n");
+
+	if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+		dprintk("-E-  OUTPUT_MODE could not be resetted.\n");
+
+	/* unforce divstr regardless whether i2c enumeration was done or not */
+	dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1) );
+
+	dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
+
+	return 0;
+}
+
+static void dib7000p_restart_agc(struct dib7000p_state *state)
+{
+	// P_restart_iqc & P_restart_agc
+	dib7000p_write_word(state, 770, 0x0c00);
+	dib7000p_write_word(state, 770, 0x0000);
+}
+
+static void dib7000p_update_lna(struct dib7000p_state *state)
+{
+	int i;
+	u16 dyn_gain;
+
+	// when there is no LNA to program return immediatly
+	if (state->cfg.update_lna == NULL)
+		return;
+
+	for (i = 0; i < 5; i++) {
+		// read dyn_gain here (because it is demod-dependent and not tuner)
+		dyn_gain = dib7000p_read_word(state, 394);
+
+		if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed
+			dib7000p_restart_agc(state);
+			msleep(5);
+		} else
+			break;
+	}
+}
+
+static void dib7000p_pll_clk_cfg(struct dib7000p_state *state)
+{
+	u16 tmp = 0;
+	tmp = dib7000p_read_word(state, 903);
+	dib7000p_write_word(state, 903, (tmp | 0x1));   //pwr-up pll
+	tmp = dib7000p_read_word(state, 900);
+	dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6));     //use High freq clock
+}
+
+static void dib7000p_update_timf_freq(struct dib7000p_state *state)
+{
+	u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428);
+	state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100);
+	dib7000p_write_word(state, 23, (u16) (timf >> 16));
+	dib7000p_write_word(state, 24, (u16) (timf & 0xffff));
+	dprintk("-D-  Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf);
+}
+
+static void dib7000p_set_channel(struct dib7000p_state *state, struct dibx000_ofdm_channel *ch, u8 seq)
+{
+	u16 tmp, est[4]; // reg_26, reg_32, reg_33, reg_187, reg_188, reg_189, reg_190, reg_207, reg_208;
+
+	/* nfft, guard, qam, alpha */
+	dib7000p_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha));
+	dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */
+
+	/* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */
+	tmp = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1);
+	if (ch->vit_hrch == 0 || ch->vit_select_hp == 1)
+		tmp |= (ch->vit_code_rate_hp << 1);
+	else
+		tmp |= (ch->vit_code_rate_lp << 1);
+	dib7000p_write_word(state, 208, tmp);
+
+	/* P_dvsy_sync_wait */
+	switch (ch->nfft) {
+		case 1: tmp = 256; break;
+		case 2: tmp = 128; break;
+		case 0:
+		default: tmp = 64; break;
+	}
+	tmp *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin
+	tmp <<= 4;
+
+	/* deactive the possibility of diversity reception if extended interleave */
+	/* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
+	if (ch->intlv_native || ch->nfft == 1)
+		tmp |= (1 << 2) | (2 << 0);
+	dib7000p_write_word(state, 207, tmp);
+
+	dib7000p_write_word(state, 26, 0x6680);   // timf(6xxx)
+	dib7000p_write_word(state, 29, 0x1273);   // isi inh1273 on1073
+	dib7000p_write_word(state, 32, 0x0003);   // pha_off_max(xxx3)
+	dib7000p_write_word(state, 33, 0x0005);   // sfreq(xxx5)
+
+	/* channel estimation fine configuration */
+	switch (ch->nqam) {
+		case 2:
+			est[0] = 0x0148;       /* P_adp_regul_cnt 0.04 */
+			est[1] = 0xfff0;       /* P_adp_noise_cnt -0.002 */
+			est[2] = 0x00a4;       /* P_adp_regul_ext 0.02 */
+			est[3] = 0xfff8;       /* P_adp_noise_ext -0.001 */
+			break;
+		case 1:
+			est[0] = 0x023d;       /* P_adp_regul_cnt 0.07 */
+			est[1] = 0xffdf;       /* P_adp_noise_cnt -0.004 */
+			est[2] = 0x00a4;       /* P_adp_regul_ext 0.02 */
+			est[3] = 0xfff0;       /* P_adp_noise_ext -0.002 */
+			break;
+		default:
+			est[0] = 0x099a;       /* P_adp_regul_cnt 0.3 */
+			est[1] = 0xffae;       /* P_adp_noise_cnt -0.01 */
+			est[2] = 0x0333;       /* P_adp_regul_ext 0.1 */
+			est[3] = 0xfff8;       /* P_adp_noise_ext -0.002 */
+			break;
+	}
+	for (tmp = 0; tmp < 4; tmp++)
+		dib7000p_write_word(state, 187 + tmp, est[tmp]);
+
+	// set power-up level: interf+analog+AGC
+	dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+	dib7000p_set_adc_state(state, DIBX000_ADC_ON);
+	dib7000p_pll_clk_cfg(state);
+	msleep(7);
+
+	// AGC initialization
+	if (state->cfg.agc_control)
+		state->cfg.agc_control(&state->demod, 1);
+
+	dib7000p_restart_agc(state);
+
+	// wait AGC rough lock time
+	msleep(5);
+
+	dib7000p_update_lna(state);
+
+	// wait AGC accurate lock time
+	msleep(7);
+	if (state->cfg.agc_control)
+		state->cfg.agc_control(&state->demod, 0);
+}
+
+static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+	struct dib7000p_state *state = demod->demodulator_priv;
+	struct dibx000_ofdm_channel auto_ch;
+	u32 value;
+
+	INIT_OFDM_CHANNEL(&auto_ch);
+	auto_ch.RF_kHz           = ch->RF_kHz;
+	auto_ch.Bw               = ch->Bw;
+	auto_ch.nqam             = 2;
+	auto_ch.guard            = 0;
+	auto_ch.nfft             = 1;
+	auto_ch.vit_alpha        = 1;
+	auto_ch.vit_select_hp    = 1;
+	auto_ch.vit_code_rate_hp = 2;
+	auto_ch.vit_code_rate_lp = 3;
+	auto_ch.vit_hrch         = 0;
+	auto_ch.intlv_native     = 1;
+
+	dib7000p_set_channel(state, &auto_ch, 7);
+
+	// always use the setting for 8MHz here lock_time for 7,6 MHz are longer
+	value = 30 * state->cfg.bw->internal;
+	dib7000p_write_word(state, 6,  (u16) ((value >> 16) & 0xffff)); // lock0 wait time
+	dib7000p_write_word(state, 7,  (u16)  (value        & 0xffff)); // lock0 wait time
+	value = 100 * state->cfg.bw->internal;
+	dib7000p_write_word(state, 8,  (u16) ((value >> 16) & 0xffff)); // lock1 wait time
+	dib7000p_write_word(state, 9,  (u16)  (value        & 0xffff)); // lock1 wait time
+	value = 500 * state->cfg.bw->internal;
+	dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time
+	dib7000p_write_word(state, 11, (u16)  (value        & 0xffff)); // lock2 wait time
+
+	value = dib7000p_read_word(state, 0);
+	dib7000p_write_word(state, 0, (1 << 9) | value);
+	dib7000p_read_word(state, 1284);
+	dib7000p_write_word(state, 0, (u16) value);
+
+	return 0;
+}
+
+static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod)
+{
+	struct dib7000p_state *state = demod->demodulator_priv;
+	u16 irq_pending = dib7000p_read_word(state, 1284);
+
+	if (irq_pending & 0x1) // failed
+		return 1;
+
+	if (irq_pending & 0x2) // succeeded
+		return 2;
+
+	return 0; // still pending
+}
+
+static int dib7000p_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+	struct dib7000p_state *state = demod->demodulator_priv;
+	u16 tmp = 0;
+
+	if (ch != NULL)
+		dib7000p_set_channel(state, ch, 0);
+	else
+		return -EINVAL;
+
+	// restart demod
+	dib7000p_write_word(state, 770, 0x4000);
+	dib7000p_write_word(state, 770, 0x0000);
+	msleep(45);
+
+	/* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+	dib7000p_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+	// never achieved a lock with that bandwidth so far - wait for osc-freq to update
+	if (state->timf == 0)
+		msleep(200);
+
+	/* offset loop parameters */
+
+	/* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */
+	tmp = (6 << 8) | 0x80;
+	switch (ch->nfft) {
+		case 0: tmp |= (7 << 12); break;
+		case 1: tmp |= (9 << 12); break;
+		case 2: tmp |= (8 << 12); break;
+	}
+	dib7000p_write_word(state, 26, tmp);  /* timf_a(6xxx) */
+
+	/* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */
+	tmp = (0 << 4);
+	switch (ch->nfft) {
+		case 0: tmp |= 0x6; break;
+		case 1: tmp |= 0x8; break;
+		case 2: tmp |= 0x7; break;
+	}
+	dib7000p_write_word(state, 32,  tmp);
+
+	/* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */
+	tmp = (0 << 4);
+	switch (ch->nfft) {
+		case 0: tmp |= 0x6; break;
+		case 1: tmp |= 0x8; break;
+		case 2: tmp |= 0x7; break;
+	}
+	dib7000p_write_word(state, 33,  tmp);
+
+	tmp = dib7000p_read_word(state,509);
+	if (!((tmp >> 6) & 0x1)) {
+		/* restart the fec */
+		tmp = dib7000p_read_word(state,771);
+		dib7000p_write_word(state, 771, tmp | (1 << 1));
+		dib7000p_write_word(state, 771, tmp);
+		msleep(10);
+		tmp = dib7000p_read_word(state,509);
+	}
+
+	// we achieved a lock - it's time to update the osc freq
+	if ((tmp >> 6) & 0x1)
+		dib7000p_update_timf_freq(state);
+
+	return 0;
+}
+
+static int dib7000p_init(struct dvb_frontend *demod)
+{
+	struct dibx000_agc_config *agc;
+	struct dib7000p_state *state = demod->demodulator_priv;
+	int ret = 0;
+
+	// Demodulator default configuration
+	agc = state->cfg.agc;
+
+	dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+	dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+
+	/* AGC */
+	ret |= dib7000p_write_word(state, 75 ,  agc->setup );
+	ret |= dib7000p_write_word(state, 76 ,  agc->inv_gain );
+	ret |= dib7000p_write_word(state, 77 ,  agc->time_stabiliz );
+	ret |= dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock);
+
+	// Demod AGC loop configuration
+	ret |= dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp);
+	ret |= dib7000p_write_word(state, 102, (agc->beta_mant << 6)  | agc->beta_exp);
+
+	/* AGC continued */
+	dprintk("-D-  WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
+		state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
+
+	if (state->wbd_ref != 0)
+		ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref);
+	else
+		ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref);
+
+	ret |= dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) );
+
+	ret |= dib7000p_write_word(state, 107,  agc->agc1_max);
+	ret |= dib7000p_write_word(state, 108,  agc->agc1_min);
+	ret |= dib7000p_write_word(state, 109,  agc->agc2_max);
+	ret |= dib7000p_write_word(state, 110,  agc->agc2_min);
+	ret |= dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2 );
+	ret |= dib7000p_write_word(state, 112,  agc->agc1_pt3);
+	ret |= dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+	ret |= dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+	ret |= dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+	/* disable power smoothing */
+	ret |= dib7000p_write_word(state, 145, 0);
+	ret |= dib7000p_write_word(state, 146, 0);
+	ret |= dib7000p_write_word(state, 147, 0);
+	ret |= dib7000p_write_word(state, 148, 0);
+	ret |= dib7000p_write_word(state, 149, 0);
+	ret |= dib7000p_write_word(state, 150, 0);
+	ret |= dib7000p_write_word(state, 151, 0);
+	ret |= dib7000p_write_word(state, 152, 0);
+
+	// P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26
+	ret |= dib7000p_write_word(state, 26 ,0x6680);
+
+	// P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16
+	ret |= dib7000p_write_word(state, 142,0x0410);
+	// P_fft_freq_dir=1, P_fft_nb_to_cut=0
+	ret |= dib7000p_write_word(state, 154,1 << 13);
+	// P_pha3_thres, default 0x3000
+	ret |= dib7000p_write_word(state, 168,0x0ccd);
+	// P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010
+	//ret |= dib7000p_write_word(state, 169,0x0010);
+	// P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005
+	ret |= dib7000p_write_word(state, 183,0x200f);
+	// P_adp_regul_cnt=573, default: 410
+	ret |= dib7000p_write_word(state, 187,0x023d);
+	// P_adp_noise_cnt=
+	ret |= dib7000p_write_word(state, 188,0x00a4);
+	// P_adp_regul_ext
+	ret |= dib7000p_write_word(state, 189,0x00a4);
+	// P_adp_noise_ext
+	ret |= dib7000p_write_word(state, 190,0x7ff0);
+	// P_adp_fil
+	ret |= dib7000p_write_word(state, 191,0x3ccc);
+
+	ret |= dib7000p_write_word(state, 222,0x0010);
+	// P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+	ret |= dib7000p_write_word(state, 235,0x0062);
+
+	// P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ...
+	if(state->cfg.tuner_is_baseband)
+		ret |= dib7000p_write_word(state, 36,0x0755);
+	else
+		ret |= dib7000p_write_word(state, 36,0x1f55);
+
+	// auto search configuration
+	ret |= dib7000p_write_word(state, 2  ,0x0004);
+	ret |= dib7000p_write_word(state, 3  ,0x1000);
+
+	/* Equal Lock */
+	ret |= dib7000p_write_word(state, 4   ,0x0814);
+
+	ret |= dib7000p_write_word(state, 6  ,0x001b);
+	ret |= dib7000p_write_word(state, 7  ,0x7740);
+	ret |= dib7000p_write_word(state, 8  ,0x005b);
+	ret |= dib7000p_write_word(state, 9  ,0x8d80);
+	ret |= dib7000p_write_word(state, 10 ,0x01c9);
+	ret |= dib7000p_write_word(state, 11 ,0xc380);
+	ret |= dib7000p_write_word(state, 12 ,0x0000);
+	ret |= dib7000p_write_word(state, 13 ,0x0080);
+	ret |= dib7000p_write_word(state, 14 ,0x0000);
+	ret |= dib7000p_write_word(state, 15 ,0x0090);
+	ret |= dib7000p_write_word(state, 16 ,0x0001);
+	ret |= dib7000p_write_word(state, 17 ,0xd4c0);
+
+	// P_clk_cfg1
+	ret |= dib7000p_write_word(state, 901, 0x0006);
+
+	// P_divclksel=3 P_divbitsel=1
+	ret |= dib7000p_write_word(state, 902, (3 << 10) | (1 << 6));
+
+	// Tuner IO bank: max drive (14mA) + divout pads max drive
+	ret |= dib7000p_write_word(state, 905, 0x2c8e);
+
+	ret |= dib7000p_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+	dib7000p_sad_calib(state);
+
+	return ret;
+}
+
+static int dib7000p_sleep(struct dvb_frontend *demod)
+{
+	struct dib7000p_state *state = demod->demodulator_priv;
+	return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
+}
+
+static int dib7000p_identify(struct dib7000p_state *st)
+{
+	u16 value;
+	dprintk("-I-  DiB7000PC: checking demod on I2C address: %d (%x)\n",
+		st->i2c_addr, st->i2c_addr);
+
+	if ((value = dib7000p_read_word(st, 768)) != 0x01b3) {
+		dprintk("-E-  DiB7000PC: wrong Vendor ID (read=0x%x)\n",value);
+		return -EREMOTEIO;
+	}
+
+	if ((value = dib7000p_read_word(st, 769)) != 0x4000) {
+		dprintk("-E-  DiB7000PC: wrong Device ID (%x)\n",value);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+
+static int dib7000p_get_frontend(struct dvb_frontend* fe,
+				struct dvb_frontend_parameters *fep)
+{
+	struct dib7000p_state *state = fe->demodulator_priv;
+	u16 tps = dib7000p_read_word(state,463);
+
+	fep->inversion = INVERSION_AUTO;
+
+	fep->u.ofdm.bandwidth = state->current_bandwidth;
+
+	switch ((tps >> 8) & 0x3) {
+		case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+		case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+		/* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
+	}
+
+	switch (tps & 0x3) {
+		case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+		case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+		case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+		case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+	}
+
+	switch ((tps >> 14) & 0x3) {
+		case 0: fep->u.ofdm.constellation = QPSK; break;
+		case 1: fep->u.ofdm.constellation = QAM_16; break;
+		case 2:
+		default: fep->u.ofdm.constellation = QAM_64; break;
+	}
+
+	/* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+	/* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */
+
+	fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+	switch ((tps >> 5) & 0x7) {
+		case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+		case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+		case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+		case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+		case 7:
+		default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+
+	}
+
+	switch ((tps >> 2) & 0x7) {
+		case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+		case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+		case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+		case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+		case 7:
+		default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+	}
+
+	/* native interleaver: (dib7000p_read_word(state, 464) >>  5) & 0x1 */
+
+	return 0;
+}
+
+static int dib7000p_set_frontend(struct dvb_frontend* fe,
+				struct dvb_frontend_parameters *fep)
+{
+	struct dib7000p_state *state = fe->demodulator_priv;
+	struct dibx000_ofdm_channel ch;
+
+	INIT_OFDM_CHANNEL(&ch);
+	FEP2DIB(fep,&ch);
+
+	state->current_bandwidth = fep->u.ofdm.bandwidth;
+	dib7000p_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe, fep);
+
+	if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+		fep->u.ofdm.guard_interval    == GUARD_INTERVAL_AUTO ||
+		fep->u.ofdm.constellation     == QAM_AUTO ||
+		fep->u.ofdm.code_rate_HP      == FEC_AUTO) {
+		int i = 800, found;
+
+		dib7000p_autosearch_start(fe, &ch);
+		do {
+			msleep(1);
+			found = dib7000p_autosearch_is_irq(fe);
+		} while (found == 0 && i--);
+
+		dprintk("autosearch returns: %d\n",found);
+		if (found == 0 || found == 1)
+			return 0; // no channel found
+
+		dib7000p_get_frontend(fe, fep);
+		FEP2DIB(fep, &ch);
+	}
+
+	/* make this a config parameter */
+	dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+	return dib7000p_tune(fe, &ch);
+}
+
+static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+	struct dib7000p_state *state = fe->demodulator_priv;
+	u16 lock = dib7000p_read_word(state, 509);
+
+	*stat = 0;
+
+	if (lock & 0x8000)
+		*stat |= FE_HAS_SIGNAL;
+	if (lock & 0x3000)
+		*stat |= FE_HAS_CARRIER;
+	if (lock & 0x0100)
+		*stat |= FE_HAS_VITERBI;
+	if (lock & 0x0010)
+		*stat |= FE_HAS_SYNC;
+	if (lock & 0x0008)
+		*stat |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int dib7000p_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct dib7000p_state *state = fe->demodulator_priv;
+	*ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501);
+	return 0;
+}
+
+static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+	struct dib7000p_state *state = fe->demodulator_priv;
+	*unc = dib7000p_read_word(state, 506);
+	return 0;
+}
+
+static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct dib7000p_state *state = fe->demodulator_priv;
+	u16 val = dib7000p_read_word(state, 394);
+	*strength = 65535 - val;
+	return 0;
+}
+
+static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+	*snr = 0x0000;
+	return 0;
+}
+
+static int dib7000p_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+	tune->min_delay_ms = 1000;
+	return 0;
+}
+
+static void dib7000p_release(struct dvb_frontend *demod)
+{
+	struct dib7000p_state *st = demod->demodulator_priv;
+	dibx000_exit_i2c_master(&st->i2c_master);
+	kfree(st);
+}
+
+int dib7000pc_detection(struct i2c_adapter *i2c_adap)
+{
+	u8 tx[2], rx[2];
+	struct i2c_msg msg[2] = {
+		{ .addr = 18 >> 1, .flags = 0,        .buf = tx, .len = 2 },
+		{ .addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2 },
+	};
+
+	tx[0] = 0x03;
+	tx[1] = 0x00;
+
+	if (i2c_transfer(i2c_adap, msg, 2) == 2)
+		if (rx[0] == 0x01 && rx[1] == 0xb3) {
+			dprintk("-D-  DiB7000PC detected\n");
+			return 1;
+		}
+
+	msg[0].addr = msg[1].addr = 0x40;
+
+	if (i2c_transfer(i2c_adap, msg, 2) == 2)
+		if (rx[0] == 0x01 && rx[1] == 0xb3) {
+			dprintk("-D-  DiB7000PC detected\n");
+			return 1;
+		}
+
+	dprintk("-D-  DiB7000PC not detected\n");
+	return 0;
+}
+EXPORT_SYMBOL(dib7000pc_detection);
+
+struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+{
+	struct dib7000p_state *st = demod->demodulator_priv;
+	return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+EXPORT_SYMBOL(dib7000p_get_i2c_master);
+
+int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
+{
+	struct dib7000p_state st = { .i2c_adap = i2c };
+	int k = 0;
+	u8 new_addr = 0;
+
+	for (k = no_of_demods-1; k >= 0; k--) {
+		st.cfg = cfg[k];
+
+		/* designated i2c address */
+		new_addr          = (0x40 + k) << 1;
+		st.i2c_addr = new_addr;
+		if (dib7000p_identify(&st) != 0) {
+			st.i2c_addr = default_addr;
+			if (dib7000p_identify(&st) != 0) {
+				dprintk("DiB7000P #%d: not identified\n", k);
+				return -EIO;
+			}
+		}
+
+		/* start diversity to pull_down div_str - just for i2c-enumeration */
+		dib7000p_set_output_mode(&st, OUTMODE_DIVERSITY);
+
+		/* set new i2c address and force divstart */
+		dib7000p_write_word(&st, 1285, (new_addr << 2) | 0x2);
+
+		dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
+	}
+
+	for (k = 0; k < no_of_demods; k++) {
+		st.cfg = cfg[k];
+		st.i2c_addr = (0x40 + k) << 1;
+
+		// unforce divstr
+		dib7000p_write_word(&st, 1285, st.i2c_addr << 2);
+
+		/* deactivate div - it was just for i2c-enumeration */
+		dib7000p_set_output_mode(&st, OUTMODE_HIGH_Z);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(dib7000p_i2c_enumeration);
+
+static struct dvb_frontend_ops dib7000p_ops;
+struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
+{
+	struct dvb_frontend *demod;
+	struct dib7000p_state *st;
+	st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL);
+	if (st == NULL)
+		return NULL;
+
+	memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config));
+	st->i2c_adap = i2c_adap;
+	st->i2c_addr = i2c_addr;
+	st->gpio_val = cfg->gpio_val;
+	st->gpio_dir = cfg->gpio_dir;
+
+	demod                   = &st->demod;
+	demod->demodulator_priv = st;
+	memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
+
+	if (dib7000p_identify(st) != 0)
+		goto error;
+
+	dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
+
+	dib7000p_demod_reset(st);
+
+	return demod;
+
+error:
+	kfree(st);
+	return NULL;
+}
+EXPORT_SYMBOL(dib7000p_attach);
+
+static struct dvb_frontend_ops dib7000p_ops = {
+	.info = {
+		.name = "DiBcom 7000PC",
+		.type = FE_OFDM,
+		.frequency_min      = 44250000,
+		.frequency_max      = 867250000,
+		.frequency_stepsize = 62500,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_RECOVER |
+			FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release              = dib7000p_release,
+
+	.init                 = dib7000p_init,
+	.sleep                = dib7000p_sleep,
+
+	.set_frontend         = dib7000p_set_frontend,
+	.get_tune_settings    = dib7000p_fe_get_tune_settings,
+	.get_frontend         = dib7000p_get_frontend,
+
+	.read_status          = dib7000p_read_status,
+	.read_ber             = dib7000p_read_ber,
+	.read_signal_strength = dib7000p_read_signal_strength,
+	.read_snr             = dib7000p_read_snr,
+	.read_ucblocks        = dib7000p_read_unc_blocks,
+};
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
new file mode 100644
index 0000000..79465cf
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -0,0 +1,46 @@
+#ifndef DIB7000P_H
+#define DIB7000P_H
+
+#include "dibx000_common.h"
+
+struct dib7000p_config {
+	u8 output_mpeg2_in_188_bytes;
+	u8 hostbus_diversity;
+	u8 tuner_is_baseband;
+	int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+	struct dibx000_agc_config *agc;
+	struct dibx000_bandwidth_config *bw;
+
+#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff
+	u16 gpio_dir;
+#define DIB7000P_GPIO_DEFAULT_VALUES     0x0000
+	u16 gpio_val;
+#define DIB7000P_GPIO_PWM_POS0(v)        ((v & 0xf) << 12)
+#define DIB7000P_GPIO_PWM_POS1(v)        ((v & 0xf) << 8 )
+#define DIB7000P_GPIO_PWM_POS2(v)        ((v & 0xf) << 4 )
+#define DIB7000P_GPIO_PWM_POS3(v)         (v & 0xf)
+#define DIB7000P_GPIO_DEFAULT_PWM_POS    0xffff
+	u16 gpio_pwm_pos;
+
+	u16 pwm_freq_div;
+
+	u8 quartz_direct;
+
+	int (*agc_control) (struct dvb_frontend *, u8 before);
+};
+
+#define DEFAULT_DIB7000P_I2C_ADDRESS 18
+
+extern struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg);
+extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
+extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
+
+/* TODO
+extern INT dib7000p_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val);
+extern INT dib7000p_enable_vbg_voltage(struct dibDemod *demod);
+extern void dib7000p_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff);
+extern USHORT dib7000p_get_current_agc_global(struct dibDemod *demod);
+*/
+
+#endif
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
index bb0c65f..a1df604 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -32,6 +32,13 @@
 #define BAND_LBAND 0x01
 #define BAND_UHF   0x02
 #define BAND_VHF   0x04
+#define BAND_SBAND 0x08
+#define BAND_FM	   0x10
+
+#define BAND_OF_FREQUENCY(freq_kHz) ( (freq_kHz) <= 115000 ? BAND_FM : \
+									(freq_kHz) <= 250000 ? BAND_VHF : \
+									(freq_kHz) <= 863000 ? BAND_UHF : \
+									(freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND )
 
 struct dibx000_agc_config {
 	/* defines the capabilities of this AGC-setting - using the BAND_-defines*/
@@ -129,6 +136,7 @@
 
 /* I hope I can get rid of the following kludge in the near future */
 struct dibx000_ofdm_channel {
+	u32 RF_kHz;
 	u8  Bw;
 	s16 nfft;
 	s16 guard;
@@ -138,9 +146,11 @@
 	s16 vit_alpha;
 	s16 vit_code_rate_hp;
 	s16 vit_code_rate_lp;
+	u8  intlv_native;
 };
 
 #define FEP2DIB(fep,ch) \
+	(ch)->RF_kHz           = (fep)->frequency / 1000; \
 	(ch)->Bw               = (fep)->u.ofdm.bandwidth; \
 	(ch)->nfft             = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \
 	(ch)->guard            = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \
@@ -149,7 +159,8 @@
 	(ch)->vit_select_hp    = 1; \
 	(ch)->vit_alpha        = 1; \
 	(ch)->vit_code_rate_hp = (fep)->u.ofdm.code_rate_HP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_HP; \
-	(ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP;
+	(ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; \
+	(ch)->intlv_native     = 1;
 
 #define INIT_OFDM_CHANNEL(ch) do {\
 	(ch)->Bw               = 0;  \
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index b7e7108..62de760 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -472,14 +472,14 @@
 		printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
 		       desc->name, div, buf[0], buf[1], buf[2], buf[3]);
 
-	return 0;
+	// calculate the frequency we set it to
+	return (div * desc->entries[i].stepsize) - desc->entries[i].offset;
 }
 EXPORT_SYMBOL(dvb_pll_configure);
 
 static int dvb_pll_release(struct dvb_frontend *fe)
 {
-	if (fe->tuner_priv)
-		kfree(fe->tuner_priv);
+	kfree(fe->tuner_priv);
 	fe->tuner_priv = NULL;
 	return 0;
 }
@@ -489,7 +489,8 @@
 	struct dvb_pll_priv *priv = fe->tuner_priv;
 	u8 buf[4];
 	struct i2c_msg msg =
-		{ .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) };
+		{ .addr = priv->pll_i2c_address, .flags = 0,
+		  .buf = buf, .len = sizeof(buf) };
 	int i;
 	int result;
 
@@ -517,16 +518,16 @@
 	return 0;
 }
 
-static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static int dvb_pll_set_params(struct dvb_frontend *fe,
+			      struct dvb_frontend_parameters *params)
 {
 	struct dvb_pll_priv *priv = fe->tuner_priv;
 	u8 buf[4];
 	struct i2c_msg msg =
-		{ .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) };
+		{ .addr = priv->pll_i2c_address, .flags = 0,
+		  .buf = buf, .len = sizeof(buf) };
 	int result;
-	u32 div;
-	int i;
-	u32 bandwidth = 0;
+	u32 bandwidth = 0, frequency = 0;
 
 	if (priv->i2c == NULL)
 		return -EINVAL;
@@ -536,8 +537,11 @@
 		bandwidth = params->u.ofdm.bandwidth;
 	}
 
-	if ((result = dvb_pll_configure(priv->pll_desc, buf, params->frequency, bandwidth)) != 0)
+	if ((result = dvb_pll_configure(priv->pll_desc, buf,
+					params->frequency, bandwidth)) < 0)
 		return result;
+	else
+		frequency = result;
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
@@ -545,26 +549,19 @@
 		return result;
 	}
 
-	// calculate the frequency we set it to
-	for (i = 0; i < priv->pll_desc->count; i++) {
-		if (params->frequency > priv->pll_desc->entries[i].limit)
-			continue;
-		break;
-	}
-	div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize;
-	priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset;
+	priv->frequency = frequency;
 	priv->bandwidth = bandwidth;
 
 	return 0;
 }
 
-static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len)
+static int dvb_pll_calc_regs(struct dvb_frontend *fe,
+			     struct dvb_frontend_parameters *params,
+			     u8 *buf, int buf_len)
 {
 	struct dvb_pll_priv *priv = fe->tuner_priv;
 	int result;
-	u32 div;
-	int i;
-	u32 bandwidth = 0;
+	u32 bandwidth = 0, frequency = 0;
 
 	if (buf_len < 5)
 		return -EINVAL;
@@ -574,18 +571,15 @@
 		bandwidth = params->u.ofdm.bandwidth;
 	}
 
-	if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params->frequency, bandwidth)) != 0)
+	if ((result = dvb_pll_configure(priv->pll_desc, buf+1,
+					params->frequency, bandwidth)) < 0)
 		return result;
+	else
+		frequency = result;
+
 	buf[0] = priv->pll_i2c_address;
 
-	// calculate the frequency we set it to
-	for (i = 0; i < priv->pll_desc->count; i++) {
-		if (params->frequency > priv->pll_desc->entries[i].limit)
-			continue;
-		break;
-	}
-	div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize;
-	priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset;
+	priv->frequency = frequency;
 	priv->bandwidth = bandwidth;
 
 	return 5;
@@ -614,10 +608,13 @@
 	.get_bandwidth = dvb_pll_get_bandwidth,
 };
 
-struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc)
+struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
+				    struct i2c_adapter *i2c,
+				    struct dvb_pll_desc *desc)
 {
 	u8 b1 [] = { 0 };
-	struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 };
+	struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD,
+			       .buf = b1, .len = 1 };
 	struct dvb_pll_priv *priv = NULL;
 	int ret;
 
@@ -640,7 +637,9 @@
 	priv->i2c = i2c;
 	priv->pll_desc = desc;
 
-	memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops));
+	memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
+	       sizeof(struct dvb_tuner_ops));
+
 	strncpy(fe->ops.tuner_ops.info.name, desc->name, 128);
 	fe->ops.tuner_ops.info.frequency_min = desc->min;
 	fe->ops.tuner_ops.info.frequency_min = desc->max;
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index ed5ac5a..681186a 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -48,7 +48,7 @@
 extern struct dvb_pll_desc dvb_pll_thomson_fe6600;
 
 extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
-		      u32 freq, int bandwidth);
+			     u32 freq, int bandwidth);
 
 /**
  * Attach a dvb-pll to the supplied frontend structure.
@@ -59,6 +59,9 @@
  * @param desc dvb_pll_desc to use.
  * @return Frontend pointer on success, NULL on failure
  */
-extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc);
+extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+					   int pll_addr,
+					   struct i2c_adapter *i2c,
+					   struct dvb_pll_desc *desc);
 
 #endif
diff --git a/drivers/media/dvb/frontends/lg_h06xf.h b/drivers/media/dvb/frontends/lg_h06xf.h
deleted file mode 100644
index 754d51d..0000000
--- a/drivers/media/dvb/frontends/lg_h06xf.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  lg_h06xf.h - ATSC Tuner support for LG TDVS-H06xF
- *
- *  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.
- */
-
-#ifndef _LG_H06XF_H_
-#define _LG_H06XF_H_
-#include "dvb-pll.h"
-
-static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap,
-		     struct dvb_frontend_parameters* params)
-{
-	u8 buf[4];
-	struct i2c_msg msg = { .addr = 0x61, .flags = 0,
-			       .buf = buf, .len = sizeof(buf) };
-	int err;
-
-	dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0);
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) {
-		printk(KERN_WARNING "lg_h06xf: %s error "
-			"(addr %02x <- %02x, err = %i)\n",
-			__FUNCTION__, buf[0], buf[1], err);
-		if (err < 0)
-			return err;
-		else
-			return -EREMOTEIO;
-	}
-
-	/* Set the Auxiliary Byte. */
-	buf[0] = buf[2];
-	buf[0] &= ~0x20;
-	buf[0] |= 0x18;
-	buf[1] = 0x50;
-	msg.len = 2;
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) {
-		printk(KERN_WARNING "lg_h06xf: %s error "
-			"(addr %02x <- %02x, err = %i)\n",
-			__FUNCTION__, buf[0], buf[1], err);
-		if (err < 0)
-			return err;
-		else
-			return -EREMOTEIO;
-	}
-
-	return 0;
-}
-#endif
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index 9a35470..68aad0f 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -31,9 +31,6 @@
  *   Air2PC/AirStar 2 ATSC 3rd generation (HD5000)
  *   pcHDTV HD5500
  *
- * TODO:
- * signal strength always returns 0.
- *
  */
 
 #include <linux/kernel.h>
@@ -46,9 +43,13 @@
 #include <asm/byteorder.h>
 
 #include "dvb_frontend.h"
+#include "dvb_math.h"
 #include "lgdt330x_priv.h"
 #include "lgdt330x.h"
 
+/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */
+/* #define USE_EQMSE */
+
 static int debug = 0;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
@@ -68,6 +69,7 @@
 
 	/* Demodulator private data */
 	fe_modulation_t current_modulation;
+	u32 snr; /* Result of last SNR calculation */
 
 	/* Tuner private data */
 	u32 current_frequency;
@@ -302,10 +304,10 @@
 	static u8 lgdt3303_8vsb_44_data[] = {
 		0x04, 0x00,
 		0x0d, 0x40,
-	0x0e, 0x87,
-	0x0f, 0x8e,
-	0x10, 0x01,
-	0x47, 0x8b };
+		0x0e, 0x87,
+		0x0f, 0x8e,
+		0x10, 0x01,
+		0x47, 0x8b };
 
 	/*
 	 * Array of byte pairs <address, value>
@@ -435,9 +437,6 @@
 		/* Test signal does not exist flag */
 		/* as well as the AGC lock flag.   */
 		*status |= FE_HAS_SIGNAL;
-	} else {
-		/* Without a signal all other status bits are meaningless */
-		return 0;
 	}
 
 	/*
@@ -500,9 +499,6 @@
 		/* Test input signal does not exist flag */
 		/* as well as the AGC lock flag.   */
 		*status |= FE_HAS_SIGNAL;
-	} else {
-		/* Without a signal all other status bits are meaningless */
-		return 0;
 	}
 
 	/* Carrier Recovery Lock Status Register */
@@ -543,151 +539,150 @@
 	return 0;
 }
 
-static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+/* Calculate SNR estimation (scaled by 2^24)
+
+   8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM
+   equations from LGDT3303 datasheet.  VSB is the same between the '02
+   and '03, so maybe QAM is too?  Perhaps someone with a newer datasheet
+   that has QAM information could verify?
+
+   For 8-VSB: (two ways, take your pick)
+   LGDT3302:
+     SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE)
+   LGDT3303:
+     SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE)
+   LGDT3302 & LGDT3303:
+     SNR_PT = 10 * log10(25 * 32^2 / PT_MSE)  (we use this one)
+   For 64-QAM:
+     SNR    = 10 * log10( 688128   / MSEQAM)
+   For 256-QAM:
+     SNR    = 10 * log10( 696320   / MSEQAM)
+
+   We re-write the snr equation as:
+     SNR * 2^24 = 10*(c - intlog10(MSE))
+   Where for 256-QAM, c = log10(696320) * 2^24, and so on. */
+
+static u32 calculate_snr(u32 mse, u32 c)
 {
-	/* not directly available. */
-	*strength = 0;
-	return 0;
+	if (mse == 0) /* No signal */
+		return 0;
+
+	mse = intlog10(mse);
+	if (mse > c) {
+		/* Negative SNR, which is possible, but realisticly the
+		demod will lose lock before the signal gets this bad.  The
+		API only allows for unsigned values, so just return 0 */
+		return 0;
+	}
+	return 10*(c - mse);
 }
 
 static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr)
 {
-#ifdef SNR_IN_DB
-	/*
-	 * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise)
-	 * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker
-	 * respectively. The following tables are built on these formulas.
-	 * The usual definition is SNR = 20 log10(signal/noise)
-	 * If the specification is wrong the value retuned is 1/2 the actual SNR in db.
-	 *
-	 * This table is a an ordered list of noise values computed by the
-	 * formula from the spec sheet such that the index into the table
-	 * starting at 43 or 45 is the SNR value in db. There are duplicate noise
-	 * value entries at the beginning because the SNR varies more than
-	 * 1 db for a change of 1 digit in noise at very small values of noise.
-	 *
-	 * Examples from SNR_EQ table:
-	 * noise SNR
-	 *   0    43
-	 *   1    42
-	 *   2    39
-	 *   3    37
-	 *   4    36
-	 *   5    35
-	 *   6    34
-	 *   7    33
-	 *   8    33
-	 *   9    32
-	 *   10   32
-	 *   11   31
-	 *   12   31
-	 *   13   30
-	 */
-
-	static const u32 SNR_EQ[] =
-		{ 1,     2,      2,      2, 3,      3,      4,     4,     5,     7,
-		  9,     11,     13,     17, 21,     26,     33,    41,    52,    65,
-		  81,    102,    129,    162, 204,    257,    323,   406,   511,   644,
-		  810,   1020,   1284,   1616, 2035,   2561,   3224,  4059,  5110,  6433,
-		  8098,  10195,  12835,  16158, 20341,  25608,  32238, 40585, 51094, 64323,
-		  80978, 101945, 128341, 161571, 203406, 256073, 0x40000
-		};
-
-	static const u32 SNR_PH[] =
-		{ 1,     2,      2,      2,      3,      3,     4,     5,     6,     8,
-		  10,    12,     15,     19,     23,     29, 37,    46,    58,    73,
-		  91,    115,    144,    182,    229,    288, 362,   456,   574,   722,
-		  909,   1144,   1440,   1813,   2282,   2873, 3617,  4553,  5732,  7216,
-		  9084,  11436,  14396,  18124,  22817,  28724,  36161, 45524, 57312, 72151,
-		  90833, 114351, 143960, 181235, 228161, 0x080000
-		};
-
-	static u8 buf[5];/* read data buffer */
-	static u32 noise;   /* noise value */
-	static u32 snr_db;  /* index into SNR_EQ[] */
 	struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+	u8 buf[5];	/* read data buffer */
+	u32 noise;	/* noise value */
+	u32 c;		/* per-modulation SNR calculation constant */
 
-	/* read both equalizer and phase tracker noise data */
-	i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
-	if (state->current_modulation == VSB_8) {
-		/* Equalizer Mean-Square Error Register for VSB */
+	switch(state->current_modulation) {
+	case VSB_8:
+		i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+		/* Use Equalizer Mean-Square Error Register */
+		/* SNR for ranges from -15.61 to +41.58 */
 		noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
-
-		/*
-		 * Look up noise value in table.
-		 * A better search algorithm could be used...
-		 * watch out there are duplicate entries.
-		 */
-		for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) {
-			if (noise < SNR_EQ[snr_db]) {
-				*snr = 43 - snr_db;
-				break;
-			}
-		}
-	} else {
-		/* Phase Tracker Mean-Square Error Register for QAM */
-		noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
-
-		/* Look up noise value in table. */
-		for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) {
-			if (noise < SNR_PH[snr_db]) {
-				*snr = 45 - snr_db;
-				break;
-			}
-		}
-	}
+		c = 69765745; /* log10(25*24^2)*2^24 */
 #else
-	/* Return the raw noise value */
-	static u8 buf[5];/* read data buffer */
-	static u32 noise;   /* noise value */
-	struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
-
-	/* read both equalizer and pase tracker noise data */
-	i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
-	if (state->current_modulation == VSB_8) {
-		/* Phase Tracker Mean-Square Error Register for VSB */
+		/* Use Phase Tracker Mean-Square Error Register */
+		/* SNR for ranges from -13.11 to +44.08 */
 		noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
-	} else {
-
-		/* Carrier Recovery Mean-Square Error for QAM */
-		i2c_read_demod_bytes(state, 0x1a, buf, 2);
+		c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+		break;
+	case QAM_64:
+	case QAM_256:
+		i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
 		noise = ((buf[0] & 3) << 8) | buf[1];
+		c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+		/* log10(688128)*2^24 and log10(696320)*2^24 */
+		break;
+	default:
+		printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+		       __FUNCTION__);
+		return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
 	}
 
-	/* Small values for noise mean signal is better so invert noise */
-	*snr = ~noise;
-#endif
+	state->snr = calculate_snr(noise, c);
+	*snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
 
-	dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+	dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+		state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
 
 	return 0;
 }
 
 static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr)
 {
-	/* Return the raw noise value */
-	static u8 buf[5];/* read data buffer */
-	static u32 noise;   /* noise value */
 	struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+	u8 buf[5];	/* read data buffer */
+	u32 noise;	/* noise value */
+	u32 c;		/* per-modulation SNR calculation constant */
 
-	if (state->current_modulation == VSB_8) {
-
-		i2c_read_demod_bytes(state, 0x6e, buf, 5);
-		/* Phase Tracker Mean-Square Error Register for VSB */
+	switch(state->current_modulation) {
+	case VSB_8:
+		i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+		/* Use Equalizer Mean-Square Error Register */
+		/* SNR for ranges from -16.12 to +44.08 */
+		noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2];
+		c = 73957994; /* log10(25*32^2)*2^24 */
+#else
+		/* Use Phase Tracker Mean-Square Error Register */
+		/* SNR for ranges from -13.11 to +44.08 */
 		noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4];
-	} else {
-
-		/* Carrier Recovery Mean-Square Error for QAM */
-		i2c_read_demod_bytes(state, 0x1a, buf, 2);
+		c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+		break;
+	case QAM_64:
+	case QAM_256:
+		i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
 		noise = (buf[0] << 8) | buf[1];
+		c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+		/* log10(688128)*2^24 and log10(696320)*2^24 */
+		break;
+	default:
+		printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+		       __FUNCTION__);
+		return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
 	}
 
-	/* Small values for noise mean signal is better so invert noise */
-	*snr = ~noise;
+	state->snr = calculate_snr(noise, c);
+	*snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
 
-	dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+	dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+		state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+	return 0;
+}
+
+static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	/* Calculate Strength from SNR up to 35dB */
+	/* Even though the SNR can go higher than 35dB, there is some comfort */
+	/* factor in having a range of strong signals that can show at 100%   */
+	struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+	u16 snr;
+	int ret;
+
+	ret = fe->ops.read_snr(fe, &snr);
+	if (ret != 0)
+		return ret;
+	/* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+	/* scale the range 0 - 35*2^24 into 0 - 65535 */
+	if (state->snr >= 8960 * 0x10000)
+		*strength = 0xffff;
+	else
+		*strength = state->snr / 8960;
 
 	return 0;
 }
diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb/frontends/lgdt330x_priv.h
index 59b7c5b..38c7669 100644
--- a/drivers/media/dvb/frontends/lgdt330x_priv.h
+++ b/drivers/media/dvb/frontends/lgdt330x_priv.h
@@ -51,14 +51,19 @@
 	AGC_RFIF_ACC2= 0x3b,
 	AGC_STATUS= 0x3f,
 	SYNC_STATUS_VSB= 0x43,
-	EQPH_ERR0= 0x47,
-	EQ_ERR1= 0x48,
-	EQ_ERR2= 0x49,
-	PH_ERR1= 0x4a,
-	PH_ERR2= 0x4b,
 	DEMUX_CONTROL= 0x66,
+	LGDT3302_EQPH_ERR0= 0x47,
+	LGDT3302_EQ_ERR1= 0x48,
+	LGDT3302_EQ_ERR2= 0x49,
+	LGDT3302_PH_ERR1= 0x4a,
+	LGDT3302_PH_ERR2= 0x4b,
 	LGDT3302_PACKET_ERR_COUNTER1= 0x6a,
 	LGDT3302_PACKET_ERR_COUNTER2= 0x6b,
+	LGDT3303_EQPH_ERR0= 0x6e,
+	LGDT3303_EQ_ERR1= 0x6f,
+	LGDT3303_EQ_ERR2= 0x70,
+	LGDT3303_PH_ERR1= 0x71,
+	LGDT3303_PH_ERR2= 0x72,
 	LGDT3303_PACKET_ERR_COUNTER1= 0x8b,
 	LGDT3303_PACKET_ERR_COUNTER2= 0x8c,
 };
diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c
new file mode 100644
index 0000000..2202d0c
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgh06xf.c
@@ -0,0 +1,134 @@
+/*
+ *  lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF
+ *
+ *  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 "dvb-pll.h"
+#include "lgh06xf.h"
+
+#define LG_H06XF_PLL_I2C_ADDR 0x61
+
+struct lgh06xf_priv {
+	struct i2c_adapter *i2c;
+	u32 frequency;
+};
+
+static int lgh06xf_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int lgh06xf_set_params(struct dvb_frontend* fe,
+			      struct dvb_frontend_parameters* params)
+{
+	struct lgh06xf_priv *priv = fe->tuner_priv;
+	u8 buf[4];
+	struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0,
+			       .buf = buf, .len = sizeof(buf) };
+	u32 frequency;
+	int result;
+
+	if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf,
+					params->frequency, 0)) < 0)
+		return result;
+	else
+		frequency = result;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+		printk(KERN_WARNING "lgh06xf: %s error "
+		       "(addr %02x <- %02x, result = %i)\n",
+		       __FUNCTION__, buf[0], buf[1], result);
+		if (result < 0)
+			return result;
+		else
+			return -EREMOTEIO;
+	}
+
+	/* Set the Auxiliary Byte. */
+	buf[0] = buf[2];
+	buf[0] &= ~0x20;
+	buf[0] |= 0x18;
+	buf[1] = 0x50;
+	msg.len = 2;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+		printk(KERN_WARNING "lgh06xf: %s error "
+		       "(addr %02x <- %02x, result = %i)\n",
+		       __FUNCTION__, buf[0], buf[1], result);
+		if (result < 0)
+			return result;
+		else
+			return -EREMOTEIO;
+	}
+
+	priv->frequency = frequency;
+
+	return 0;
+}
+
+static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct lgh06xf_priv *priv = fe->tuner_priv;
+	*frequency = priv->frequency;
+	return 0;
+}
+
+static struct dvb_tuner_ops lgh06xf_tuner_ops = {
+	.release       = lgh06xf_release,
+	.set_params    = lgh06xf_set_params,
+	.get_frequency = lgh06xf_get_frequency,
+};
+
+struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe,
+				    struct i2c_adapter *i2c)
+{
+	struct lgh06xf_priv *priv = NULL;
+
+	priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->i2c = i2c;
+
+	memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops,
+	       sizeof(struct dvb_tuner_ops));
+
+	strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name,
+		sizeof(fe->ops.tuner_ops.info.name));
+
+	fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min;
+	fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max;
+
+	fe->tuner_priv = priv;
+	return fe;
+}
+
+EXPORT_SYMBOL(lgh06xf_attach);
+
+MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support");
+MODULE_AUTHOR("Michael Krufky");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h
new file mode 100644
index 0000000..510b4be
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgh06xf.h
@@ -0,0 +1,35 @@
+/*
+ *  lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF
+ *
+ *  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.
+ */
+
+#ifndef _LGH06XF_H_
+#define _LGH06XF_H_
+#include "dvb_frontend.h"
+
+#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE))
+extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
+					    struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
+						  struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_TUNER_LGH06XF */
+
+#endif /* _LGH06XF_H_ */
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
index d20ab30..5a3a6e5 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -40,6 +40,7 @@
 #include <linux/slab.h>
 #include <asm/byteorder.h>
 
+#include "dvb_math.h"
 #include "dvb_frontend.h"
 #include "dvb-pll.h"
 #include "or51132.h"
@@ -62,6 +63,7 @@
 
 	/* Demodulator private data */
 	fe_modulation_t current_modulation;
+	u32 snr; /* Result of last SNR calculation */
 
 	/* Tuner private data */
 	u32 current_frequency;
@@ -465,124 +467,128 @@
 	return 0;
 }
 
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
-     0,  352,  602,  795,  954, 1088, 1204, 1306, 1397, 1480,
-  1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
-  2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
-  2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
-  2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
-  2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
-  2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
-  3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
-  3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
-  3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
-  3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
-  3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
-  3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
-  3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
-  3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
-  3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
-  3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
-  3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
-  3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
-  3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
+/* Calculate SNR estimation (scaled by 2^24)
 
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+   8-VSB SNR and QAM equations from Oren datasheets
 
-static unsigned int i20Log10(unsigned short val)
+   For 8-VSB:
+     SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K
+
+     Where K = 0 if NTSC rejection filter is OFF; and
+	   K = 3 if NTSC rejection filter is ON
+
+   For QAM64:
+     SNR[dB] = 10 * log10(897152044.8282 / MSE^2 )
+
+   For QAM256:
+     SNR[dB] = 10 * log10(907832426.314266  / MSE^2 )
+
+   We re-write the snr equation as:
+     SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+   Where for QAM256, c = log10(907832426.314266) * 2^24
+   and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */
+
+static u32 calculate_snr(u32 mse, u32 c)
 {
-	unsigned int rntval = 100;
-	unsigned int tmp = val;
-	unsigned int exp = 1;
+	if (mse == 0) /* No signal */
+		return 0;
 
-	while(tmp > 100) {tmp /= 100; exp++;}
-
-	val = (2 * val)/denom[exp];
-	if (exp > 1) rntval = 2000*exp;
-
-	rntval += i100x20log10[val];
-	return rntval;
-}
-
-static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
-{
-	struct or51132_state* state = fe->demodulator_priv;
-	unsigned char rec_buf[2];
-	unsigned char snd_buf[2];
-	u8 rcvr_stat;
-	u16 snr_equ;
-	u32 signal_strength;
-	int usK;
-
-	snd_buf[0]=0x04;
-	snd_buf[1]=0x02; /* SNR after Equalizer */
-	msleep(30); /* 30ms */
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: read_status write error\n");
-		return -1;
+	mse = 2*intlog10(mse);
+	if (mse > c) {
+		/* Negative SNR, which is possible, but realisticly the
+		demod will lose lock before the signal gets this bad.  The
+		API only allows for unsigned values, so just return 0 */
+		return 0;
 	}
-	msleep(30); /* 30ms */
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: read_status read error\n");
-		return -1;
-	}
-	snr_equ = rec_buf[0] | (rec_buf[1] << 8);
-	dprintk("read_signal_strength snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
-
-	/* Receiver Status */
-	snd_buf[0]=0x04;
-	snd_buf[1]=0x00;
-	msleep(30); /* 30ms */
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: read_signal_strength read_status write error\n");
-		return -1;
-	}
-	msleep(30); /* 30ms */
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: read_signal_strength read_status read error\n");
-		return -1;
-	}
-	dprintk("read_signal_strength read_status %x %x\n",rec_buf[0],rec_buf[1]);
-	rcvr_stat = rec_buf[1];
-	usK = (rcvr_stat & 0x10) ? 3 : 0;
-
-	/* The value reported back from the frontend will be FFFF=100% 0000=0% */
-	signal_strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000;
-	if (signal_strength > 0xffff)
-		*strength = 0xffff;
-	else
-		*strength = signal_strength;
-	dprintk("read_signal_strength %i\n",*strength);
-
-	return 0;
+	return 10*(c - mse);
 }
 
 static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
 {
 	struct or51132_state* state = fe->demodulator_priv;
-	unsigned char rec_buf[2];
-	unsigned char snd_buf[2];
-	u16 snr_equ;
+	u8 rec_buf[2];
+	u8 snd_buf[2];
+	u32 noise;
+	u32 c;
+	u32 usK;
 
+	/* Register is same for VSB or QAM firmware */
 	snd_buf[0]=0x04;
 	snd_buf[1]=0x02; /* SNR after Equalizer */
 	msleep(30); /* 30ms */
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: read_snr write error\n");
-		return -1;
+		printk(KERN_WARNING "or51132: snr write error\n");
+		return -EREMOTEIO;
 	}
 	msleep(30); /* 30ms */
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: read_snr dvr read error\n");
-		return -1;
+		printk(KERN_WARNING "or51132: snr read error\n");
+		return -EREMOTEIO;
 	}
-	snr_equ = rec_buf[0] | (rec_buf[1] << 8);
-	dprintk("read_snr snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+	noise = rec_buf[0] | (rec_buf[1] << 8);
+	dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise);
 
-	*snr = 0xFFFF - snr_equ;
-	dprintk("read_snr %i\n",*snr);
+	/* Read status, contains modulation type for QAM_AUTO and
+	   NTSC filter for VSB */
+	snd_buf[0]=0x04;
+	snd_buf[1]=0x00; /* Status register */
+	msleep(30); /* 30ms */
+	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
+		printk(KERN_WARNING "or51132: status write error\n");
+		return -EREMOTEIO;
+	}
+	msleep(30); /* 30ms */
+	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+		printk(KERN_WARNING "or51132: status read error\n");
+		return -EREMOTEIO;
+	}
+
+	usK = 0;
+	switch (rec_buf[0]) {
+	case 0x06:
+		usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0;
+		/* Fall through to QAM64 case */
+	case 0x43:
+		c = 150204167;
+		break;
+	case 0x45:
+		c = 150290396;
+		break;
+	default:
+		printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]);
+		return -EREMOTEIO;
+	}
+	dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__,
+		rec_buf[0], rec_buf[1]&0x10?"n":"ff");
+
+	/* Calculate SNR using noise, c, and NTSC rejection correction */
+	state->snr = calculate_snr(noise, c) - usK;
+	*snr = (state->snr) >> 16;
+
+	dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+		state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
+
+	return 0;
+}
+
+static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	/* Calculate Strength from SNR up to 35dB */
+	/* Even though the SNR can go higher than 35dB, there is some comfort */
+	/* factor in having a range of strong signals that can show at 100%   */
+	struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	u16 snr;
+	int ret;
+
+	ret = fe->ops.read_snr(fe, &snr);
+	if (ret != 0)
+		return ret;
+	/* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+	/* scale the range 0 - 35*2^24 into 0 - 65535 */
+	if (state->snr >= 8960 * 0x10000)
+		*strength = 0xffff;
+	else
+		*strength = state->snr / 8960;
 
 	return 0;
 }
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
index 2bf124b..048d7cf 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -39,6 +39,7 @@
 #include <linux/slab.h>
 #include <asm/byteorder.h>
 
+#include "dvb_math.h"
 #include "dvb_frontend.h"
 #include "or51211.h"
 
@@ -63,6 +64,7 @@
 
 	/* Demodulator private data */
 	u8 initialized:1;
+	u32 snr; /* Result of last SNR claculation */
 
 	/* Tuner private data */
 	u32 current_frequency;
@@ -292,107 +294,81 @@
 	return 0;
 }
 
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
-		0,  352,  602,  795,  954, 1088, 1204, 1306, 1397, 1480,
-	 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
-	 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
-	 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
-	 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
-	 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
-	 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
-	 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
-	 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
-	 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
-	 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
-	 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
-	 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
-	 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
-	 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
-	 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
-	 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
-	 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
-	 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
-	 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
+/* Calculate SNR estimation (scaled by 2^24)
 
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+   8-VSB SNR equation from Oren datasheets
 
-static unsigned int i20Log10(unsigned short val)
+   For 8-VSB:
+     SNR[dB] = 10 * log10(219037.9454 / MSE^2 )
+
+   We re-write the snr equation as:
+     SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+   Where for 8-VSB, c = log10(219037.9454) * 2^24 */
+
+static u32 calculate_snr(u32 mse, u32 c)
 {
-	unsigned int rntval = 100;
-	unsigned int tmp = val;
-	unsigned int exp = 1;
+	if (mse == 0) /* No signal */
+		return 0;
 
-	while(tmp > 100) {tmp /= 100; exp++;}
-
-	val = (2 * val)/denom[exp];
-	if (exp > 1) rntval = 2000*exp;
-
-	rntval += i100x20log10[val];
-	return rntval;
-}
-
-static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
-{
-	struct or51211_state* state = fe->demodulator_priv;
-	u8 rec_buf[2];
-	u8 snd_buf[4];
-	u8 snr_equ;
-	u32 signal_strength;
-
-	/* SNR after Equalizer */
-	snd_buf[0] = 0x04;
-	snd_buf[1] = 0x00;
-	snd_buf[2] = 0x04;
-	snd_buf[3] = 0x00;
-
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
-		printk(KERN_WARNING "or51211: read_status write error\n");
-		return -1;
+	mse = 2*intlog10(mse);
+	if (mse > c) {
+		/* Negative SNR, which is possible, but realisticly the
+		demod will lose lock before the signal gets this bad.  The
+		API only allows for unsigned values, so just return 0 */
+		return 0;
 	}
-	msleep(3);
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51211: read_status read error\n");
-		return -1;
-	}
-	snr_equ = rec_buf[0] & 0xff;
-
-	/* The value reported back from the frontend will be FFFF=100% 0000=0% */
-	signal_strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000;
-	if (signal_strength > 0xffff)
-		*strength = 0xffff;
-	else
-		*strength = signal_strength;
-	dprintk("read_signal_strength %i\n",*strength);
-
-	return 0;
+	return 10*(c - mse);
 }
 
 static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
 {
 	struct or51211_state* state = fe->demodulator_priv;
 	u8 rec_buf[2];
-	u8 snd_buf[4];
+	u8 snd_buf[3];
 
 	/* SNR after Equalizer */
 	snd_buf[0] = 0x04;
 	snd_buf[1] = 0x00;
 	snd_buf[2] = 0x04;
-	snd_buf[3] = 0x00;
 
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
-		printk(KERN_WARNING "or51211: read_status write error\n");
+		printk(KERN_WARNING "%s: error writing snr reg\n",
+		       __FUNCTION__);
 		return -1;
 	}
-	msleep(3);
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51211: read_status read error\n");
+		printk(KERN_WARNING "%s: read_status read error\n",
+		       __FUNCTION__);
 		return -1;
 	}
-	*snr = rec_buf[0] & 0xff;
 
-	dprintk("read_snr %i\n",*snr);
+	state->snr = calculate_snr(rec_buf[0], 89599047);
+	*snr = (state->snr) >> 16;
+
+	dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __FUNCTION__, rec_buf[0],
+		state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
+
+	return 0;
+}
+
+static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	/* Calculate Strength from SNR up to 35dB */
+	/* Even though the SNR can go higher than 35dB, there is some comfort */
+	/* factor in having a range of strong signals that can show at 100%   */
+	struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv;
+	u16 snr;
+	int ret;
+
+	ret = fe->ops.read_snr(fe, &snr);
+	if (ret != 0)
+		return ret;
+	/* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+	/* scale the range 0 - 35*2^24 into 0 - 65535 */
+	if (state->snr >= 8960 * 0x10000)
+		*strength = 0xffff;
+	else
+		*strength = state->snr / 8960;
 
 	return 0;
 }
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index 11e0dca..00e4bcd 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -648,18 +648,24 @@
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities
 		break;
-	case TDA10046_AGC_TDA827X:
+	case TDA10046_AGC_TDA827X_GP11:
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02);   // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
 		tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities
 		break;
-	case TDA10046_AGC_TDA827X_GPL:
+	case TDA10046_AGC_TDA827X_GP00:
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02);   // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
 		tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
 		break;
+	case TDA10046_AGC_TDA827X_GP01:
+		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02);   // AGC setup
+		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
+		tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
+		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities
+		break;
 	}
 	tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
 	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
index 605ad2d..ec502d7 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -35,8 +35,9 @@
 	TDA10046_AGC_DEFAULT,		/* original configuration */
 	TDA10046_AGC_IFO_AUTO_NEG,	/* IF AGC only, automatic, negtive */
 	TDA10046_AGC_IFO_AUTO_POS,	/* IF AGC only, automatic, positive */
-	TDA10046_AGC_TDA827X,		/* IF AGC only, special setup for tda827x */
-	TDA10046_AGC_TDA827X_GPL,	/* same as above, but GPIOs 0 */
+	TDA10046_AGC_TDA827X_GP11,	/* IF AGC only, special setup for tda827x */
+	TDA10046_AGC_TDA827X_GP00,	/* same as above, but GPIOs 0 */
+	TDA10046_AGC_TDA827X_GP01,	/* same as above, but GPIO3=0 GPIO1=1*/
 };
 
 enum tda10046_if {
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c
index 3aa45eb..67415c9 100644
--- a/drivers/media/dvb/frontends/tda8083.c
+++ b/drivers/media/dvb/frontends/tda8083.c
@@ -262,12 +262,29 @@
 	if (sync & 0x10)
 		*status |= FE_HAS_SYNC;
 
+	if (sync & 0x20) /* frontend can not lock */
+		*status |= FE_TIMEDOUT;
+
 	if ((sync & 0x1f) == 0x1f)
 		*status |= FE_HAS_LOCK;
 
 	return 0;
 }
 
+static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda8083_state* state = fe->demodulator_priv;
+	int ret;
+	u8 buf[3];
+
+	if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf))))
+		return ret;
+
+	*ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2];
+
+	return 0;
+}
+
 static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 {
 	struct tda8083_state* state = fe->demodulator_priv;
@@ -288,6 +305,17 @@
 	return 0;
 }
 
+static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda8083_state* state = fe->demodulator_priv;
+
+	*ucblocks = tda8083_readreg(state, 0x0f);
+	if (*ucblocks == 0xff)
+		*ucblocks = 0xffffffff;
+
+	return 0;
+}
+
 static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 {
 	struct tda8083_state* state = fe->demodulator_priv;
@@ -440,6 +468,8 @@
 	.read_status = tda8083_read_status,
 	.read_signal_strength = tda8083_read_signal_strength,
 	.read_snr = tda8083_read_snr,
+	.read_ber = tda8083_read_ber,
+	.read_ucblocks = tda8083_read_ucblocks,
 
 	.diseqc_send_master_cmd = tda8083_send_diseqc_msg,
 	.diseqc_send_burst = tda8083_diseqc_send_burst,
diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c
index 34815b0..79f971d 100644
--- a/drivers/media/dvb/frontends/tda826x.c
+++ b/drivers/media/dvb/frontends/tda826x.c
@@ -42,8 +42,7 @@
 
 static int tda826x_release(struct dvb_frontend *fe)
 {
-	if (fe->tuner_priv)
-		kfree(fe->tuner_priv);
+	kfree(fe->tuner_priv);
 	fe->tuner_priv = NULL;
 	return 0;
 }
@@ -133,18 +132,21 @@
 {
 	struct tda826x_priv *priv = NULL;
 	u8 b1 [] = { 0, 0 };
-	struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 };
+	struct i2c_msg msg[2] = {
+		{ .addr = addr, .flags = 0,        .buf = NULL, .len = 0 },
+		{ .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 }
+	};
 	int ret;
 
 	dprintk("%s:\n", __FUNCTION__);
 
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
-	ret = i2c_transfer (i2c, &msg, 1);
+	ret = i2c_transfer (i2c, msg, 2);
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
-	if (ret != 1)
+	if (ret != 2)
 		return NULL;
 	if (!(b1[1] & 0x80))
 		return NULL;
diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c
index 8855439..6ba0029 100644
--- a/drivers/media/dvb/frontends/tua6100.c
+++ b/drivers/media/dvb/frontends/tua6100.c
@@ -43,8 +43,7 @@
 
 static int tua6100_release(struct dvb_frontend *fe)
 {
-	if (fe->tuner_priv)
-		kfree(fe->tuner_priv);
+	kfree(fe->tuner_priv);
 	fe->tuner_priv = NULL;
 	return 0;
 }
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 95531a6..eec7ccf 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -92,6 +92,7 @@
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_TDA1004X if !DVB_FE_CUSTOMISE
 	select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+	select VIDEO_IR
 	help
 	  Support for simple SAA7146 based DVB cards
 	  (so called Budget- or Nova-PCI cards) without onboard
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index bba23bc..366c137 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -2828,7 +2828,7 @@
 
 static struct saa7146_extension av7110_extension = {
 	.name		= "dvb",
-	.flags		= SAA7146_I2C_SHORT_DELAY,
+	.flags		= SAA7146_USE_I2C_IRQ,
 
 	.module		= THIS_MODULE,
 	.pci_tbl	= &pci_tbl[0],
diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c
index d54bbcd..e4544ea 100644
--- a/drivers/media/dvb/ttpci/av7110_ir.c
+++ b/drivers/media/dvb/ttpci/av7110_ir.c
@@ -48,7 +48,8 @@
 	if (!data || !test_bit(data, input_dev->key))
 		return;
 
-	input_event(input_dev, EV_KEY, data, !!0);
+	input_report_key(input_dev, data, 0);
+	input_sync(input_dev);
 }
 
 
@@ -115,14 +116,17 @@
 		del_timer(&keyup_timer);
 		if (keyup_timer.data != keycode || new_toggle != old_toggle) {
 			delay_timer_finished = 0;
-			input_event(input_dev, EV_KEY, keyup_timer.data, !!0);
-			input_event(input_dev, EV_KEY, keycode, !0);
-		} else
-			if (delay_timer_finished)
-				input_event(input_dev, EV_KEY, keycode, 2);
+			input_event(input_dev, EV_KEY, keyup_timer.data, 0);
+			input_event(input_dev, EV_KEY, keycode, 1);
+			input_sync(input_dev);
+		} else if (delay_timer_finished) {
+			input_event(input_dev, EV_KEY, keycode, 2);
+			input_sync(input_dev);
+		}
 	} else {
 		delay_timer_finished = 0;
-		input_event(input_dev, EV_KEY, keycode, !0);
+		input_event(input_dev, EV_KEY, keycode, 1);
+		input_sync(input_dev);
 	}
 
 	keyup_timer.expires = jiffies + UP_TIMEOUT;
@@ -211,6 +215,7 @@
 int __devinit av7110_ir_init(struct av7110 *av7110)
 {
 	static struct proc_dir_entry *e;
+	int err;
 
 	if (av_cnt >= sizeof av_list/sizeof av_list[0])
 		return -ENOSPC;
@@ -231,7 +236,11 @@
 		set_bit(EV_KEY, input_dev->evbit);
 		set_bit(EV_REP, input_dev->evbit);
 		input_register_keys();
-		input_register_device(input_dev);
+		err = input_register_device(input_dev);
+		if (err) {
+			input_free_device(input_dev);
+			return err;
+		}
 		input_dev->timer.function = input_repeat_key;
 
 		e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 2235ff8..89ab4b5 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -360,7 +360,7 @@
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
 
 	/* Enable DEBI pins */
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+	saa7146_write(saa, MC1, MASK_27 | MASK_11);
 
 	/* register CI interface */
 	budget_av->ca.owner = THIS_MODULE;
@@ -386,7 +386,7 @@
 	return 0;
 
 error:
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 	return result;
 }
 
@@ -403,7 +403,7 @@
 	dvb_ca_en50221_release(&budget_av->ca);
 
 	/* disable DEBI pins */
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 }
 
 
@@ -655,6 +655,10 @@
 	.demod_address = 0x0c,
 };
 
+static struct tda10021_config philips_cu1216_config_altaddress = {
+	.demod_address = 0x0d,
+};
+
 
 
 
@@ -831,7 +835,7 @@
 		return -EINVAL;
 
 	rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf,
-			params->frequency, 0);
+			     params->frequency, 0);
 	if(rc < 0) return rc;
 
 	if (fe->ops.i2c_gate_ctrl)
@@ -914,6 +918,7 @@
 #define SUBID_DVBS_TV_STAR_CI	0x0016
 #define SUBID_DVBS_EASYWATCH_1  0x001a
 #define SUBID_DVBS_EASYWATCH	0x001e
+#define SUBID_DVBC_EASYWATCH	0x002a
 #define SUBID_DVBC_KNC1		0x0020
 #define SUBID_DVBC_KNC1_PLUS	0x0021
 #define SUBID_DVBC_CINERGY1200	0x1156
@@ -947,11 +952,15 @@
 	/* Enable / PowerON Frontend */
 	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
 
+	/* Wait for PowerON */
+	msleep(100);
+
 	/* additional setup necessary for the PLUS cards */
 	switch (saa->pci->subsystem_device) {
 		case SUBID_DVBS_KNC1_PLUS:
 		case SUBID_DVBC_KNC1_PLUS:
 		case SUBID_DVBT_KNC1_PLUS:
+		case SUBID_DVBC_EASYWATCH:
 			saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
 			break;
 	}
@@ -1006,10 +1015,15 @@
 	case SUBID_DVBC_KNC1:
 	case SUBID_DVBC_KNC1_PLUS:
 	case SUBID_DVBC_CINERGY1200:
+	case SUBID_DVBC_EASYWATCH:
 		budget_av->reinitialise_demod = 1;
 		fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
 				     &budget_av->budget.i2c_adap,
 				     read_pwm(budget_av));
+		if (fe == NULL)
+			fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress,
+					     &budget_av->budget.i2c_adap,
+					     read_pwm(budget_av));
 		if (fe) {
 			budget_av->tda10021_poclkp = 1;
 			budget_av->tda10021_set_frontend = fe->ops.set_frontend;
@@ -1242,6 +1256,7 @@
 MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
 MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
 MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP);
 MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
 MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
 MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
@@ -1260,6 +1275,7 @@
 	MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
 	MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
 	MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
+	MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a),
 	MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
 	MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
 	MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
@@ -1277,7 +1293,7 @@
 
 static struct saa7146_extension budget_extension = {
 	.name = "budget_av",
-	.flags = SAA7146_I2C_SHORT_DELAY,
+	.flags = SAA7146_USE_I2C_IRQ,
 
 	.pci_tbl = pci_tbl,
 
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index cd5ec48..f2066b4 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -37,6 +37,7 @@
 #include <linux/interrupt.h>
 #include <linux/input.h>
 #include <linux/spinlock.h>
+#include <media/ir-common.h>
 
 #include "dvb_ca_en50221.h"
 #include "stv0299.h"
@@ -72,162 +73,218 @@
 #define SLOTSTATUS_READY	8
 #define SLOTSTATUS_OCCUPIED	(SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
 
+/* Milliseconds during which key presses are regarded as key repeat and during
+ * which the debounce logic is active
+ */
+#define IR_REPEAT_TIMEOUT	350
+
+/* RC5 device wildcard */
+#define IR_DEVICE_ANY		255
+
+/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two),
+ * this setting allows the superflous sequences to be ignored
+ */
+static int debounce = 0;
+module_param(debounce, int, 0644);
+MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)");
+
+static int rc5_device = -1;
+module_param(rc5_device, int, 0644);
+MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)");
+
+static int ir_debug = 0;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+struct budget_ci_ir {
+	struct input_dev *dev;
+	struct tasklet_struct msp430_irq_tasklet;
+	char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
+	char phys[32];
+	struct ir_input_state state;
+	int rc5_device;
+};
+
 struct budget_ci {
 	struct budget budget;
-	struct input_dev *input_dev;
-	struct tasklet_struct msp430_irq_tasklet;
 	struct tasklet_struct ciintf_irq_tasklet;
 	int slot_status;
 	int ci_irq;
 	struct dvb_ca_en50221 ca;
-	char ir_dev_name[50];
+	struct budget_ci_ir ir;
 	u8 tuner_pll_address; /* used for philips_tdm1316l configs */
 };
 
-/* from reading the following remotes:
-   Zenith Universal 7 / TV Mode 807 / VCR Mode 837
-   Hauppauge (from NOVA-CI-s box product)
-   i've taken a "middle of the road" approach and note the differences
-*/
-static u16 key_map[64] = {
-	/* 0x0X */
-	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8,
-	KEY_9,
-	KEY_ENTER,
-	KEY_RED,
-	KEY_POWER,		/* RADIO on Hauppauge */
-	KEY_MUTE,
-	0,
-	KEY_A,			/* TV on Hauppauge */
-	/* 0x1X */
-	KEY_VOLUMEUP, KEY_VOLUMEDOWN,
-	0, 0,
-	KEY_B,
-	0, 0, 0, 0, 0, 0, 0,
-	KEY_UP, KEY_DOWN,
-	KEY_OPTION,		/* RESERVED on Hauppauge */
-	KEY_BREAK,
-	/* 0x2X */
-	KEY_CHANNELUP, KEY_CHANNELDOWN,
-	KEY_PREVIOUS,		/* Prev. Ch on Zenith, SOURCE on Hauppauge */
-	0, KEY_RESTART, KEY_OK,
-	KEY_CYCLEWINDOWS,	/* MINIMIZE on Hauppauge */
-	0,
-	KEY_ENTER,		/* VCR mode on Zenith */
-	KEY_PAUSE,
-	0,
-	KEY_RIGHT, KEY_LEFT,
-	0,
-	KEY_MENU,		/* FULL SCREEN on Hauppauge */
-	0,
-	/* 0x3X */
-	KEY_SLOW,
-	KEY_PREVIOUS,		/* VCR mode on Zenith */
-	KEY_REWIND,
-	0,
-	KEY_FASTFORWARD,
-	KEY_PLAY, KEY_STOP,
-	KEY_RECORD,
-	KEY_TUNER,		/* TV/VCR on Zenith */
-	0,
-	KEY_C,
-	0,
-	KEY_EXIT,
-	KEY_POWER2,
-	KEY_TUNER,		/* VCR mode on Zenith */
-	0,
-};
-
-static void msp430_ir_debounce(unsigned long data)
+static void msp430_ir_keyup(unsigned long data)
 {
-	struct input_dev *dev = (struct input_dev *) data;
-
-	if (dev->rep[0] == 0 || dev->rep[0] == ~0) {
-		input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
-		return;
-	}
-
-	dev->rep[0] = 0;
-	dev->timer.expires = jiffies + HZ * 350 / 1000;
-	add_timer(&dev->timer);
-	input_event(dev, EV_KEY, key_map[dev->repeat_key], 2);	/* REPEAT */
+	struct budget_ci_ir *ir = (struct budget_ci_ir *) data;
+	ir_input_nokey(ir->dev, &ir->state);
 }
 
 static void msp430_ir_interrupt(unsigned long data)
 {
 	struct budget_ci *budget_ci = (struct budget_ci *) data;
-	struct input_dev *dev = budget_ci->input_dev;
-	unsigned int code =
-		ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+	struct input_dev *dev = budget_ci->ir.dev;
+	static int bounces = 0;
+	int device;
+	int toggle;
+	static int prev_toggle = -1;
+	static u32 ir_key;
+	u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
 
-	if (code & 0x40) {
-		code &= 0x3f;
+	/*
+	 * The msp430 chip can generate two different bytes, command and device
+	 *
+	 * type1: X1CCCCCC, C = command bits (0 - 63)
+	 * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit
+	 *
+	 * More than one command byte may be generated before the device byte
+	 * Only when we have both, a correct keypress is generated
+	 */
 
-		if (timer_pending(&dev->timer)) {
-			if (code == dev->repeat_key) {
-				++dev->rep[0];
-				return;
-			}
-			del_timer(&dev->timer);
-			input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
-		}
+	/* Is this a RC5 command byte? */
+	if (command & 0x40) {
+		if (ir_debug)
+			printk("budget_ci: received command byte 0x%02x\n", command);
+		ir_key = command & 0x3f;
+		return;
+	}
 
-		if (!key_map[code]) {
-			printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code);
-			return;
-		}
+	/* It's a RC5 device byte */
+	if (ir_debug)
+		printk("budget_ci: received device byte 0x%02x\n", command);
+	device = command & 0x1f;
+	toggle = command & 0x20;
 
-		/* initialize debounce and repeat */
-		dev->repeat_key = code;
-		/* Zenith remote _always_ sends 2 sequences */
-		dev->rep[0] = ~0;
-		/* 350 milliseconds */
-		dev->timer.expires = jiffies + HZ * 350 / 1000;
-		/* MAKE */
-		input_event(dev, EV_KEY, key_map[code], !0);
-		add_timer(&dev->timer);
+	if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device)
+		return;
+
+	/* Ignore repeated key sequences if requested */
+	if (toggle == prev_toggle && ir_key == dev->repeat_key &&
+	    bounces > 0 && timer_pending(&dev->timer)) {
+		if (ir_debug)
+			printk("budget_ci: debounce logic ignored IR command\n");
+		bounces--;
+		return;
+	}
+	prev_toggle = toggle;
+
+	/* Are we still waiting for a keyup event? */
+	if (del_timer(&dev->timer))
+		ir_input_nokey(dev, &budget_ci->ir.state);
+
+	/* Generate keypress */
+	if (ir_debug)
+		printk("budget_ci: generating keypress 0x%02x\n", ir_key);
+	ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8)));
+
+	/* Do we want to delay the keyup event? */
+	if (debounce) {
+		bounces = debounce;
+		mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT));
+	} else {
+		ir_input_nokey(dev, &budget_ci->ir.state);
 	}
 }
 
 static int msp430_ir_init(struct budget_ci *budget_ci)
 {
 	struct saa7146_dev *saa = budget_ci->budget.dev;
-	struct input_dev *input_dev;
-	int i;
+	struct input_dev *input_dev = budget_ci->ir.dev;
+	int error;
 
-	budget_ci->input_dev = input_dev = input_allocate_device();
-	if (!input_dev)
-		return -ENOMEM;
+	budget_ci->ir.dev = input_dev = input_allocate_device();
+	if (!input_dev) {
+		printk(KERN_ERR "budget_ci: IR interface initialisation failed\n");
+		error = -ENOMEM;
+		goto out1;
+	}
 
-	sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name);
+	snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name),
+		 "Budget-CI dvb ir receiver %s", saa->name);
+	snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys),
+		 "pci-%s/ir0", pci_name(saa->pci));
 
-	input_dev->name = budget_ci->ir_dev_name;
+	input_dev->name = budget_ci->ir.name;
 
-	set_bit(EV_KEY, input_dev->evbit);
-	for (i = 0; i < ARRAY_SIZE(key_map); i++)
-		if (key_map[i])
-			set_bit(key_map[i], input_dev->keybit);
+	input_dev->phys = budget_ci->ir.phys;
+	input_dev->id.bustype = BUS_PCI;
+	input_dev->id.version = 1;
+	if (saa->pci->subsystem_vendor) {
+		input_dev->id.vendor = saa->pci->subsystem_vendor;
+		input_dev->id.product = saa->pci->subsystem_device;
+	} else {
+		input_dev->id.vendor = saa->pci->vendor;
+		input_dev->id.product = saa->pci->device;
+	}
+	input_dev->cdev.dev = &saa->pci->dev;
 
-	input_register_device(budget_ci->input_dev);
+	/* Select keymap and address */
+	switch (budget_ci->budget.dev->pci->subsystem_device) {
+	case 0x100c:
+	case 0x100f:
+	case 0x1010:
+	case 0x1011:
+	case 0x1012:
+	case 0x1017:
+		/* The hauppauge keymap is a superset of these remotes */
+		ir_input_init(input_dev, &budget_ci->ir.state,
+			      IR_TYPE_RC5, ir_codes_hauppauge_new);
 
-	input_dev->timer.function = msp430_ir_debounce;
+		if (rc5_device < 0)
+			budget_ci->ir.rc5_device = 0x1f;
+		else
+			budget_ci->ir.rc5_device = rc5_device;
+		break;
+	default:
+		/* unknown remote */
+		ir_input_init(input_dev, &budget_ci->ir.state,
+			      IR_TYPE_RC5, ir_codes_budget_ci_old);
 
-	saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06);
+		if (rc5_device < 0)
+			budget_ci->ir.rc5_device = IR_DEVICE_ANY;
+		else
+			budget_ci->ir.rc5_device = rc5_device;
+		break;
+	}
+
+	/* initialise the key-up debounce timeout handler */
+	input_dev->timer.function = msp430_ir_keyup;
+	input_dev->timer.data = (unsigned long) &budget_ci->ir;
+
+	error = input_register_device(input_dev);
+	if (error) {
+		printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
+		goto out2;
+	}
+
+	tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt,
+		     (unsigned long) budget_ci);
+
+	SAA7146_IER_ENABLE(saa, MASK_06);
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
 
 	return 0;
+
+out2:
+	input_free_device(input_dev);
+out1:
+	return error;
 }
 
 static void msp430_ir_deinit(struct budget_ci *budget_ci)
 {
 	struct saa7146_dev *saa = budget_ci->budget.dev;
-	struct input_dev *dev = budget_ci->input_dev;
+	struct input_dev *dev = budget_ci->ir.dev;
 
-	saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06);
+	SAA7146_IER_DISABLE(saa, MASK_06);
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+	tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
 
-	if (del_timer(&dev->timer))
-		input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+	if (del_timer(&dev->timer)) {
+		ir_input_nokey(dev, &budget_ci->ir.state);
+		input_sync(dev);
+	}
 
 	input_unregister_device(dev);
 }
@@ -428,7 +485,7 @@
 	memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
 
 	// enable DEBI pins
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+	saa7146_write(saa, MC1, MASK_27 | MASK_11);
 
 	// test if it is there
 	ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
@@ -480,7 +537,7 @@
 		} else {
 			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
 		}
-		saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
+		SAA7146_IER_ENABLE(saa, MASK_03);
 	}
 
 	// enable interface
@@ -502,7 +559,7 @@
 	return 0;
 
 error:
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 	return result;
 }
 
@@ -512,7 +569,7 @@
 
 	// disable CI interrupts
 	if (budget_ci->ci_irq) {
-		saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
+		SAA7146_IER_DISABLE(saa, MASK_03);
 		saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
 		tasklet_kill(&budget_ci->ciintf_irq_tasklet);
 	}
@@ -530,7 +587,7 @@
 	dvb_ca_en50221_release(&budget_ci->ca);
 
 	// disable DEBI pins
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 }
 
 static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
@@ -540,7 +597,7 @@
 	dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
 
 	if (*isr & MASK_06)
-		tasklet_schedule(&budget_ci->msp430_irq_tasklet);
+		tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet);
 
 	if (*isr & MASK_10)
 		ttpci_budget_irq10_handler(dev, isr);
@@ -835,7 +892,7 @@
 		band = 1;
 	} else if (tuner_frequency < 200000000) {
 		cp = 6;
-		band = 1;
+		band = 2;
 	} else if (tuner_frequency < 290000000) {
 		cp = 3;
 		band = 2;
@@ -1083,24 +1140,23 @@
 	struct budget_ci *budget_ci;
 	int err;
 
-	if (!(budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL)))
-		return -ENOMEM;
+	budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL);
+	if (!budget_ci) {
+		err = -ENOMEM;
+		goto out1;
+	}
 
 	dprintk(2, "budget_ci: %p\n", budget_ci);
 
-	budget_ci->budget.ci_present = 0;
-
 	dev->ext_priv = budget_ci;
 
-	if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) {
-		kfree(budget_ci);
-		return err;
-	}
+	err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE);
+	if (err)
+		goto out2;
 
-	tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt,
-		     (unsigned long) budget_ci);
-
-	msp430_ir_init(budget_ci);
+	err = msp430_ir_init(budget_ci);
+	if (err)
+		goto out3;
 
 	ciintf_init(budget_ci);
 
@@ -1110,6 +1166,13 @@
 	ttpci_budget_init_hooks(&budget_ci->budget);
 
 	return 0;
+
+out3:
+	ttpci_budget_deinit(&budget_ci->budget);
+out2:
+	kfree(budget_ci);
+out1:
+	return err;
 }
 
 static int budget_ci_detach(struct saa7146_dev *dev)
@@ -1120,16 +1183,13 @@
 
 	if (budget_ci->budget.ci_present)
 		ciintf_deinit(budget_ci);
+	msp430_ir_deinit(budget_ci);
 	if (budget_ci->budget.dvb_frontend) {
 		dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
 		dvb_frontend_detach(budget_ci->budget.dvb_frontend);
 	}
 	err = ttpci_budget_deinit(&budget_ci->budget);
 
-	tasklet_kill(&budget_ci->msp430_irq_tasklet);
-
-	msp430_ir_deinit(budget_ci);
-
 	// disable frontend and CI interface
 	saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
 
@@ -1162,7 +1222,7 @@
 
 static struct saa7146_extension budget_extension = {
 	.name = "budget_ci dvb",
-	.flags = SAA7146_I2C_SHORT_DELAY,
+	.flags = SAA7146_USE_I2C_IRQ,
 
 	.module = THIS_MODULE,
 	.pci_tbl = &pci_tbl[0],
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
index 56f1c80..9268a82 100644
--- a/drivers/media/dvb/ttpci/budget.c
+++ b/drivers/media/dvb/ttpci/budget.c
@@ -555,7 +555,7 @@
 
 static struct saa7146_extension budget_extension = {
 	.name		= "budget dvb",
-	.flags		= SAA7146_I2C_SHORT_DELAY,
+	.flags		= SAA7146_USE_I2C_IRQ,
 
 	.module		= THIS_MODULE,
 	.pci_tbl	= pci_tbl,
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index 10b121a..bd6e7ba 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -238,6 +238,7 @@
 		 * for now lets report each signal as a key down and up*/
 		dprintk("%s:rc signal:%d\n", __FUNCTION__, buffer[4]);
 		input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1);
+		input_sync(dec->rc_input_dev);
 		input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0);
 		input_sync(dec->rc_input_dev);
 	}
@@ -1187,11 +1188,12 @@
 	struct input_dev *input_dev;
 	u8 b[] = { 0x00, 0x01 };
 	int i;
+	int err;
 
 	usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys));
 	strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys));
 
-	dec->rc_input_dev = input_dev = input_allocate_device();
+	input_dev = input_allocate_device();
 	if (!input_dev)
 		return -ENOMEM;
 
@@ -1205,8 +1207,13 @@
 	for (i = 0; i < ARRAY_SIZE(rc_keys); i++)
 		  set_bit(rc_keys[i], input_dev->keybit);
 
-	input_register_device(input_dev);
+	err = input_register_device(input_dev);
+	if (err) {
+		input_free_device(input_dev);
+		return err;
+	}
 
+	dec->rc_input_dev = input_dev;
 	if (usb_submit_urb(dec->irq_urb, GFP_KERNEL))
 		printk("%s: usb_submit_urb failed\n",__FUNCTION__);
 	/* enable irq pipe */
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index b8fde5c..29a11c1 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -184,6 +184,14 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called ks0127.
 
+config VIDEO_OV7670
+	tristate "OmniVision OV7670 sensor support"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV7670 VGA camera.  It currently only works with the M88ALP01
+	  controller.
+
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L1 && I2C
@@ -567,18 +575,6 @@
 	help
 	  Support for the AverMedia 6 Eyes video surveillance card.
 
-config VIDEO_ZR36120
-	tristate "Zoran ZR36120/36125 Video For Linux"
-	depends on PCI && I2C && VIDEO_V4L1 && BROKEN
-	help
-	  Support for ZR36120/ZR36125 based frame grabber/overlay boards.
-	  This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV,
-	  and Buster boards. Please read the material in
-	  <file:Documentation/video4linux/zr36120.txt> for more information.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called zr36120.
-
 config VIDEO_MEYE
 	tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
 	depends on PCI && SONYPI && VIDEO_V4L1
@@ -670,6 +666,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called arv.
 
+config VIDEO_CAFE_CCIC
+	tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
+	depends on I2C && VIDEO_V4L2
+	select VIDEO_OV7670
+	---help---
+	  This is a video4linux2 driver for the Marvell 88ALP01 integrated
+	  CMOS camera controller.  This is the controller found on first-
+	  generation OLPC systems.
+
 #
 # USB Multimedia device configuration
 #
@@ -681,6 +686,8 @@
 
 source "drivers/media/video/em28xx/Kconfig"
 
+source "drivers/media/video/usbvision/Kconfig"
+
 source "drivers/media/video/usbvideo/Kconfig"
 
 source "drivers/media/video/et61x251/Kconfig"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index af57abc..9b1f3f0 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -2,7 +2,6 @@
 # Makefile for the video capture/playback device drivers.
 #
 
-zoran-objs      :=	zr36120.o zr36120_i2c.o zr36120_mem.o
 zr36067-objs	:=	zoran_procfs.o zoran_device.o \
 			zoran_driver.o zoran_card.o
 tuner-objs	:=	tuner-core.o tuner-types.o tuner-simple.o \
@@ -23,7 +22,6 @@
 obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
 obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
 
-obj-$(CONFIG_VIDEO_ZR36120) += zoran.o
 obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
 obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
 obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
@@ -64,6 +62,7 @@
 obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
 obj-$(CONFIG_VIDEO_CX88) += cx88/
 obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
 obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
 obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
 obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
@@ -92,6 +91,9 @@
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
 
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o
+obj-$(CONFIG_VIDEO_OV7670) 	+= ov7670.o
+
 obj-$(CONFIG_USB_DABUSB)        += dabusb.o
 obj-$(CONFIG_USB_OV511)         += ov511.o
 obj-$(CONFIG_USB_SE401)         += se401.o
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 6e1ddad..3c8e474 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -1793,7 +1793,7 @@
 		memset(i,0,sizeof(*i));
 		i->index    = n;
 		i->type     = V4L2_INPUT_TYPE_CAMERA;
-		i->audioset = 0;
+		i->audioset = 1;
 		if (i->index == bttv_tvcards[btv->c.type].tuner) {
 			sprintf(i->name, "Television");
 			i->type  = V4L2_INPUT_TYPE_TUNER;
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c
index 933d6db..cbc012f 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/video/bt8xx/bttv-input.c
@@ -259,24 +259,59 @@
 
 /* ---------------------------------------------------------------------- */
 
+static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir)
+{
+	if (ir->polling) {
+		init_timer(&ir->timer);
+		ir->timer.function = bttv_input_timer;
+		ir->timer.data     = (unsigned long)btv;
+		ir->timer.expires  = jiffies + HZ;
+		add_timer(&ir->timer);
+	} else if (ir->rc5_gpio) {
+		/* set timer_end for code completion */
+		init_timer(&ir->timer_end);
+		ir->timer_end.function = bttv_rc5_timer_end;
+		ir->timer_end.data = (unsigned long)ir;
+
+		init_timer(&ir->timer_keyup);
+		ir->timer_keyup.function = bttv_rc5_timer_keyup;
+		ir->timer_keyup.data = (unsigned long)ir;
+	}
+}
+
+static void bttv_ir_stop(struct bttv *btv)
+{
+	if (btv->remote->polling) {
+		del_timer_sync(&btv->remote->timer);
+		flush_scheduled_work();
+	}
+
+	if (btv->remote->rc5_gpio) {
+		u32 gpio;
+
+		del_timer_sync(&btv->remote->timer_end);
+		flush_scheduled_work();
+
+		gpio = bttv_gpio_read(&btv->c);
+		bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
+	}
+}
+
 int bttv_input_init(struct bttv *btv)
 {
 	struct bttv_ir *ir;
 	IR_KEYTAB_TYPE *ir_codes = NULL;
 	struct input_dev *input_dev;
 	int ir_type = IR_TYPE_OTHER;
+	int err = -ENOMEM;
 
 	if (!btv->has_remote)
 		return -ENODEV;
 
 	ir = kzalloc(sizeof(*ir),GFP_KERNEL);
 	input_dev = input_allocate_device();
-	if (!ir || !input_dev) {
-		kfree(ir);
-		input_free_device(input_dev);
-		return -ENOMEM;
-	}
-	memset(ir,0,sizeof(*ir));
+	if (!ir || !input_dev)
+		goto err_out_free;
 
 	/* detect & configure */
 	switch (btv->c.type) {
@@ -348,10 +383,9 @@
 		break;
 	}
 	if (NULL == ir_codes) {
-		dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n",btv->c.type);
-		kfree(ir);
-		input_free_device(input_dev);
-		return -ENODEV;
+		dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type);
+		err = -ENODEV;
+		goto err_out_free;
 	}
 
 	if (ir->rc5_gpio) {
@@ -389,32 +423,26 @@
 	input_dev->cdev.dev = &btv->c.pci->dev;
 
 	btv->remote = ir;
-	if (ir->polling) {
-		init_timer(&ir->timer);
-		ir->timer.function = bttv_input_timer;
-		ir->timer.data     = (unsigned long)btv;
-		ir->timer.expires  = jiffies + HZ;
-		add_timer(&ir->timer);
-	} else if (ir->rc5_gpio) {
-		/* set timer_end for code completion */
-		init_timer(&ir->timer_end);
-		ir->timer_end.function = bttv_rc5_timer_end;
-		ir->timer_end.data = (unsigned long)ir;
-
-		init_timer(&ir->timer_keyup);
-		ir->timer_keyup.function = bttv_rc5_timer_keyup;
-		ir->timer_keyup.data = (unsigned long)ir;
-	}
+	bttv_ir_start(btv, ir);
 
 	/* all done */
-	input_register_device(btv->remote->dev);
-	printk(DEVNAME ": %s detected at %s\n",ir->name,ir->phys);
+	err = input_register_device(btv->remote->dev);
+	if (err)
+		goto err_out_stop;
 
 	/* the remote isn't as bouncy as a keyboard */
 	ir->dev->rep[REP_DELAY] = repeat_delay;
 	ir->dev->rep[REP_PERIOD] = repeat_period;
 
 	return 0;
+
+ err_out_stop:
+	bttv_ir_stop(btv);
+	btv->remote = NULL;
+ err_out_free:
+	input_free_device(input_dev);
+	kfree(ir);
+	return err;
 }
 
 void bttv_input_fini(struct bttv *btv)
@@ -422,22 +450,7 @@
 	if (btv->remote == NULL)
 		return;
 
-	if (btv->remote->polling) {
-		del_timer_sync(&btv->remote->timer);
-		flush_scheduled_work();
-	}
-
-
-	if (btv->remote->rc5_gpio) {
-		u32 gpio;
-
-		del_timer_sync(&btv->remote->timer_end);
-		flush_scheduled_work();
-
-		gpio = bttv_gpio_read(&btv->c);
-		bttv_gpio_write(&btv->c, gpio & ~(1 << 4));
-	}
-
+	bttv_ir_stop(btv);
 	input_unregister_device(btv->remote->dev);
 	kfree(btv->remote);
 	btv->remote = NULL;
diff --git a/drivers/media/video/cafe_ccic-regs.h b/drivers/media/video/cafe_ccic-regs.h
new file mode 100644
index 0000000..b2c22a0
--- /dev/null
+++ b/drivers/media/video/cafe_ccic-regs.h
@@ -0,0 +1,160 @@
+/*
+ * Register definitions for the m88alp01 camera interface.  Offsets in bytes
+ * as given in the spec.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc.
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#define REG_Y0BAR	0x00
+#define REG_Y1BAR	0x04
+#define REG_Y2BAR	0x08
+/* ... */
+
+#define REG_IMGPITCH	0x24	/* Image pitch register */
+#define   IMGP_YP_SHFT	  2		/* Y pitch params */
+#define   IMGP_YP_MASK	  0x00003ffc	/* Y pitch field */
+#define	  IMGP_UVP_SHFT	  18		/* UV pitch (planar) */
+#define   IMGP_UVP_MASK   0x3ffc0000
+#define REG_IRQSTATRAW	0x28	/* RAW IRQ Status */
+#define   IRQ_EOF0	  0x00000001	/* End of frame 0 */
+#define   IRQ_EOF1	  0x00000002	/* End of frame 1 */
+#define   IRQ_EOF2	  0x00000004	/* End of frame 2 */
+#define   IRQ_SOF0	  0x00000008	/* Start of frame 0 */
+#define   IRQ_SOF1	  0x00000010	/* Start of frame 1 */
+#define   IRQ_SOF2	  0x00000020	/* Start of frame 2 */
+#define   IRQ_OVERFLOW	  0x00000040	/* FIFO overflow */
+#define   IRQ_TWSIW	  0x00010000	/* TWSI (smbus) write */
+#define   IRQ_TWSIR	  0x00020000	/* TWSI read */
+#define   IRQ_TWSIE	  0x00040000	/* TWSI error */
+#define   TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE)
+#define   FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2)
+#define   ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW)
+#define REG_IRQMASK	0x2c	/* IRQ mask - same bits as IRQSTAT */
+#define REG_IRQSTAT	0x30	/* IRQ status / clear */
+
+#define REG_IMGSIZE	0x34	/* Image size */
+#define  IMGSZ_V_MASK	  0x1fff0000
+#define  IMGSZ_V_SHIFT	  16
+#define	 IMGSZ_H_MASK	  0x00003fff
+#define REG_IMGOFFSET	0x38	/* IMage offset */
+
+#define REG_CTRL0	0x3c	/* Control 0 */
+#define   C0_ENABLE	  0x00000001	/* Makes the whole thing go */
+
+/* Mask for all the format bits */
+#define   C0_DF_MASK	  0x00fffffc    /* Bits 2-23 */
+
+/* RGB ordering */
+#define   C0_RGB4_RGBX	  0x00000000
+#define	  C0_RGB4_XRGB	  0x00000004
+#define	  C0_RGB4_BGRX	  0x00000008
+#define   C0_RGB4_XBGR	  0x0000000c
+#define   C0_RGB5_RGGB	  0x00000000
+#define	  C0_RGB5_GRBG	  0x00000004
+#define	  C0_RGB5_GBRG	  0x00000008
+#define   C0_RGB5_BGGR	  0x0000000c
+
+/* Spec has two fields for DIN and DOUT, but they must match, so
+   combine them here. */
+#define   C0_DF_YUV	  0x00000000    /* Data is YUV	    */
+#define   C0_DF_RGB	  0x000000a0	/* ... RGB		    */
+#define   C0_DF_BAYER     0x00000140	/* ... Bayer                */
+/* 8-8-8 must be missing from the below - ask */
+#define   C0_RGBF_565	  0x00000000
+#define   C0_RGBF_444	  0x00000800
+#define   C0_RGB_BGR	  0x00001000	/* Blue comes first */
+#define   C0_YUV_PLANAR	  0x00000000	/* YUV 422 planar format */
+#define   C0_YUV_PACKED	  0x00008000	/* YUV 422 packed	*/
+#define   C0_YUV_420PL	  0x0000a000	/* YUV 420 planar	*/
+/* Think that 420 packed must be 111 - ask */
+#define	  C0_YUVE_YUYV	  0x00000000	/* Y1CbY0Cr 		*/
+#define	  C0_YUVE_YVYU	  0x00010000	/* Y1CrY0Cb 		*/
+#define	  C0_YUVE_VYUY	  0x00020000	/* CrY1CbY0 		*/
+#define	  C0_YUVE_UYVY	  0x00030000	/* CbY1CrY0 		*/
+#define   C0_YUVE_XYUV	  0x00000000    /* 420: .YUV		*/
+#define	  C0_YUVE_XYVU	  0x00010000	/* 420: .YVU 		*/
+#define	  C0_YUVE_XUVY	  0x00020000	/* 420: .UVY 		*/
+#define	  C0_YUVE_XVUY	  0x00030000	/* 420: .VUY 		*/
+/* Bayer bits 18,19 if needed */
+#define   C0_HPOL_LOW	  0x01000000	/* HSYNC polarity active low */
+#define   C0_VPOL_LOW	  0x02000000	/* VSYNC polarity active low */
+#define   C0_VCLK_LOW	  0x04000000	/* VCLK on falling edge */
+#define   C0_DOWNSCALE	  0x08000000	/* Enable downscaler */
+#define	  C0_SIFM_MASK	  0xc0000000	/* SIF mode bits */
+#define   C0_SIF_HVSYNC	  0x00000000	/* Use H/VSYNC */
+#define   CO_SOF_NOSYNC	  0x40000000	/* Use inband active signaling */
+
+
+#define REG_CTRL1	0x40	/* Control 1 */
+#define   C1_444ALPHA	  0x00f00000	/* Alpha field in RGB444 */
+#define   C1_ALPHA_SHFT	  20
+#define   C1_DMAB32	  0x00000000	/* 32-byte DMA burst */
+#define   C1_DMAB16	  0x02000000	/* 16-byte DMA burst */
+#define	  C1_DMAB64	  0x04000000	/* 64-byte DMA burst */
+#define	  C1_DMAB_MASK	  0x06000000
+#define   C1_TWOBUFS	  0x08000000	/* Use only two DMA buffers */
+#define   C1_PWRDWN	  0x10000000	/* Power down */
+
+#define REG_CLKCTRL	0x88	/* Clock control */
+#define   CLK_DIV_MASK	  0x0000ffff	/* Upper bits RW "reserved" */
+
+#define REG_GPR		0xb4	/* General purpose register.  This
+				   controls inputs to the power and reset
+				   pins on the OV7670 used with OLPC;
+				   other deployments could differ.  */
+#define   GPR_C1EN	  0x00000020	/* Pad 1 (power down) enable */
+#define   GPR_C0EN	  0x00000010	/* Pad 0 (reset) enable */
+#define	  GPR_C1	  0x00000002	/* Control 1 value */
+/*
+ * Control 0 is wired to reset on OLPC machines.  For ov7x sensors,
+ * it is active low, for 0v6x, instead, it's active high.  What
+ * fun.
+ */
+#define   GPR_C0	  0x00000001	/* Control 0 value */
+
+#define REG_TWSIC0	0xb8	/* TWSI (smbus) control 0 */
+#define   TWSIC0_EN       0x00000001	/* TWSI enable */
+#define   TWSIC0_MODE	  0x00000002	/* 1 = 16-bit, 0 = 8-bit */
+#define   TWSIC0_SID	  0x000003fc	/* Slave ID */
+#define   TWSIC0_SID_SHIFT 2
+#define   TWSIC0_CLKDIV   0x0007fc00	/* Clock divider */
+#define   TWSIC0_MASKACK  0x00400000	/* Mask ack from sensor */
+#define   TWSIC0_OVMAGIC  0x00800000	/* Make it work on OV sensors */
+
+#define REG_TWSIC1	0xbc	/* TWSI control 1 */
+#define   TWSIC1_DATA	  0x0000ffff	/* Data to/from camchip */
+#define   TWSIC1_ADDR	  0x00ff0000	/* Address (register) */
+#define   TWSIC1_ADDR_SHIFT 16
+#define   TWSIC1_READ	  0x01000000	/* Set for read op */
+#define   TWSIC1_WSTAT	  0x02000000	/* Write status */
+#define   TWSIC1_RVALID	  0x04000000	/* Read data valid */
+#define   TWSIC1_ERROR	  0x08000000	/* Something screwed up */
+
+
+#define REG_UBAR	0xc4	/* Upper base address register */
+
+/*
+ * Here's the weird global control registers which are said to live
+ * way up here.
+ */
+#define REG_GL_CSR     0x3004  /* Control/status register */
+#define   GCSR_SRS	 0x00000001	/* SW Reset set */
+#define   GCSR_SRC  	 0x00000002	/* SW Reset clear */
+#define	  GCSR_MRS	 0x00000004	/* Master reset set */
+#define	  GCSR_MRC	 0x00000008	/* HW Reset clear */
+#define   GCSR_CCIC_EN   0x00004000    /* CCIC Clock enable */
+#define REG_GL_IMASK   0x300c  /* Interrupt mask register */
+#define   GIMSK_CCIC_EN          0x00000004    /* CCIC Interrupt enable */
+
+#define REG_LEN                REG_GL_IMASK + 4
+
+
+/*
+ * Useful stuff that probably belongs somewhere global.
+ */
+#define VGA_WIDTH	640
+#define VGA_HEIGHT	480
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
new file mode 100644
index 0000000..e347c7e
--- /dev/null
+++ b/drivers/media/video/cafe_ccic.c
@@ -0,0 +1,2228 @@
+/*
+ * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe"
+ * multifunction chip.  Currently works with the Omnivision OV7670
+ * sensor.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc.
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/jiffies.h>
+#include <linux/vmalloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "cafe_ccic-regs.h"
+
+#define CAFE_VERSION 0x000001
+
+
+/*
+ * Parameters.
+ */
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Video");
+
+/*
+ * Internal DMA buffer management.  Since the controller cannot do S/G I/O,
+ * we must have physically contiguous buffers to bring frames into.
+ * These parameters control how many buffers we use, whether we
+ * allocate them at load time (better chance of success, but nails down
+ * memory) or when somebody tries to use the camera (riskier), and,
+ * for load-time allocation, how big they should be.
+ *
+ * The controller can cycle through three buffers.  We could use
+ * more by flipping pointers around, but it probably makes little
+ * sense.
+ */
+
+#define MAX_DMA_BUFS 3
+static int alloc_bufs_at_load = 0;
+module_param(alloc_bufs_at_load, bool, 0444);
+MODULE_PARM_DESC(alloc_bufs_at_load,
+		"Non-zero value causes DMA buffers to be allocated at module "
+		"load time.  This increases the chances of successfully getting "
+		"those buffers, but at the cost of nailing down the memory from "
+		"the outset.");
+
+static int n_dma_bufs = 3;
+module_param(n_dma_bufs, uint, 0644);
+MODULE_PARM_DESC(n_dma_bufs,
+		"The number of DMA buffers to allocate.  Can be either two "
+		"(saves memory, makes timing tighter) or three.");
+
+static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2;  /* Worst case */
+module_param(dma_buf_size, uint, 0444);
+MODULE_PARM_DESC(dma_buf_size,
+		"The size of the allocated DMA buffers.  If actual operating "
+		"parameters require larger buffers, an attempt to reallocate "
+		"will be made.");
+
+static int min_buffers = 1;
+module_param(min_buffers, uint, 0644);
+MODULE_PARM_DESC(min_buffers,
+		"The minimum number of streaming I/O buffers we are willing "
+		"to work with.");
+
+static int max_buffers = 10;
+module_param(max_buffers, uint, 0644);
+MODULE_PARM_DESC(max_buffers,
+		"The maximum number of streaming I/O buffers an application "
+		"will be allowed to allocate.  These buffers are big and live "
+		"in vmalloc space.");
+
+static int flip = 0;
+module_param(flip, bool, 0444);
+MODULE_PARM_DESC(flip,
+		"If set, the sensor will be instructed to flip the image "
+		"vertically.");
+
+
+enum cafe_state {
+	S_NOTREADY,	/* Not yet initialized */
+	S_IDLE,		/* Just hanging around */
+	S_FLAKED,	/* Some sort of problem */
+	S_SINGLEREAD,	/* In read() */
+	S_SPECREAD,   	/* Speculative read (for future read()) */
+	S_STREAMING	/* Streaming data */
+};
+
+/*
+ * Tracking of streaming I/O buffers.
+ */
+struct cafe_sio_buffer {
+	struct list_head list;
+	struct v4l2_buffer v4lbuf;
+	char *buffer;   /* Where it lives in kernel space */
+	int mapcount;
+	struct cafe_camera *cam;
+};
+
+/*
+ * A description of one of our devices.
+ * Locking: controlled by s_mutex.  Certain fields, however, require
+ * 	    the dev_lock spinlock; they are marked as such by comments.
+ *	    dev_lock is also required for access to device registers.
+ */
+struct cafe_camera
+{
+	enum cafe_state state;
+	unsigned long flags;   		/* Buffer status, mainly (dev_lock) */
+	int users;			/* How many open FDs */
+	struct file *owner;		/* Who has data access (v4l2) */
+
+	/*
+	 * Subsystem structures.
+	 */
+	struct pci_dev *pdev;
+	struct video_device v4ldev;
+	struct i2c_adapter i2c_adapter;
+	struct i2c_client *sensor;
+
+	unsigned char __iomem *regs;
+	struct list_head dev_list;	/* link to other devices */
+
+	/* DMA buffers */
+	unsigned int nbufs;		/* How many are alloc'd */
+	int next_buf;			/* Next to consume (dev_lock) */
+	unsigned int dma_buf_size;  	/* allocated size */
+	void *dma_bufs[MAX_DMA_BUFS];	/* Internal buffer addresses */
+	dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
+	unsigned int specframes;	/* Unconsumed spec frames (dev_lock) */
+	unsigned int sequence;		/* Frame sequence number */
+	unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual buffers */
+
+	/* Streaming buffers */
+	unsigned int n_sbufs;		/* How many we have */
+	struct cafe_sio_buffer *sb_bufs; /* The array of housekeeping structs */
+	struct list_head sb_avail;	/* Available for data (we own) (dev_lock) */
+	struct list_head sb_full;	/* With data (user space owns) (dev_lock) */
+	struct tasklet_struct s_tasklet;
+
+	/* Current operating parameters */
+	enum v4l2_chip_ident sensor_type;		/* Currently ov7670 only */
+	struct v4l2_pix_format pix_format;
+
+	/* Locks */
+	struct mutex s_mutex; /* Access to this structure */
+	spinlock_t dev_lock;  /* Access to device */
+
+	/* Misc */
+	wait_queue_head_t smbus_wait;	/* Waiting on i2c events */
+	wait_queue_head_t iowait;	/* Waiting on frame data */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	struct dentry *dfs_regs;
+	struct dentry *dfs_cam_regs;
+#endif
+};
+
+/*
+ * Status flags.  Always manipulated with bit operations.
+ */
+#define CF_BUF0_VALID	 0	/* Buffers valid - first three */
+#define CF_BUF1_VALID	 1
+#define CF_BUF2_VALID	 2
+#define CF_DMA_ACTIVE	 3	/* A frame is incoming */
+#define CF_CONFIG_NEEDED 4	/* Must configure hardware */
+
+
+
+/*
+ * Start over with DMA buffers - dev_lock needed.
+ */
+static void cafe_reset_buffers(struct cafe_camera *cam)
+{
+	int i;
+
+	cam->next_buf = -1;
+	for (i = 0; i < cam->nbufs; i++)
+		clear_bit(i, &cam->flags);
+	cam->specframes = 0;
+}
+
+static inline int cafe_needs_config(struct cafe_camera *cam)
+{
+	return test_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+static void cafe_set_config_needed(struct cafe_camera *cam, int needed)
+{
+	if (needed)
+		set_bit(CF_CONFIG_NEEDED, &cam->flags);
+	else
+		clear_bit(CF_CONFIG_NEEDED, &cam->flags);
+}
+
+
+
+
+/*
+ * Debugging and related.
+ */
+#define cam_err(cam, fmt, arg...) \
+	dev_err(&(cam)->pdev->dev, fmt, ##arg);
+#define cam_warn(cam, fmt, arg...) \
+	dev_warn(&(cam)->pdev->dev, fmt, ##arg);
+#define cam_dbg(cam, fmt, arg...) \
+	dev_dbg(&(cam)->pdev->dev, fmt, ##arg);
+
+
+/* ---------------------------------------------------------------------*/
+/*
+ * We keep a simple list of known devices to search at open time.
+ */
+static LIST_HEAD(cafe_dev_list);
+static DEFINE_MUTEX(cafe_dev_list_lock);
+
+static void cafe_add_dev(struct cafe_camera *cam)
+{
+	mutex_lock(&cafe_dev_list_lock);
+	list_add_tail(&cam->dev_list, &cafe_dev_list);
+	mutex_unlock(&cafe_dev_list_lock);
+}
+
+static void cafe_remove_dev(struct cafe_camera *cam)
+{
+	mutex_lock(&cafe_dev_list_lock);
+	list_del(&cam->dev_list);
+	mutex_unlock(&cafe_dev_list_lock);
+}
+
+static struct cafe_camera *cafe_find_dev(int minor)
+{
+	struct cafe_camera *cam;
+
+	mutex_lock(&cafe_dev_list_lock);
+	list_for_each_entry(cam, &cafe_dev_list, dev_list) {
+		if (cam->v4ldev.minor == minor)
+			goto done;
+	}
+	cam = NULL;
+  done:
+	mutex_unlock(&cafe_dev_list_lock);
+	return cam;
+}
+
+
+static struct cafe_camera *cafe_find_by_pdev(struct pci_dev *pdev)
+{
+	struct cafe_camera *cam;
+
+	mutex_lock(&cafe_dev_list_lock);
+	list_for_each_entry(cam, &cafe_dev_list, dev_list) {
+		if (cam->pdev == pdev)
+			goto done;
+	}
+	cam = NULL;
+  done:
+	mutex_unlock(&cafe_dev_list_lock);
+	return cam;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/*
+ * Device register I/O
+ */
+static inline void cafe_reg_write(struct cafe_camera *cam, unsigned int reg,
+		unsigned int val)
+{
+	iowrite32(val, cam->regs + reg);
+}
+
+static inline unsigned int cafe_reg_read(struct cafe_camera *cam,
+		unsigned int reg)
+{
+	return ioread32(cam->regs + reg);
+}
+
+
+static inline void cafe_reg_write_mask(struct cafe_camera *cam, unsigned int reg,
+		unsigned int val, unsigned int mask)
+{
+	unsigned int v = cafe_reg_read(cam, reg);
+
+	v = (v & ~mask) | (val & mask);
+	cafe_reg_write(cam, reg, v);
+}
+
+static inline void cafe_reg_clear_bit(struct cafe_camera *cam,
+		unsigned int reg, unsigned int val)
+{
+	cafe_reg_write_mask(cam, reg, 0, val);
+}
+
+static inline void cafe_reg_set_bit(struct cafe_camera *cam,
+		unsigned int reg, unsigned int val)
+{
+	cafe_reg_write_mask(cam, reg, val, val);
+}
+
+
+
+/* -------------------------------------------------------------------- */
+/*
+ * The I2C/SMBUS interface to the camera itself starts here.  The
+ * controller handles SMBUS itself, presenting a relatively simple register
+ * interface; all we have to do is to tell it where to route the data.
+ */
+#define CAFE_SMBUS_TIMEOUT (HZ)  /* generous */
+
+static int cafe_smbus_write_done(struct cafe_camera *cam)
+{
+	unsigned long flags;
+	int c1;
+
+	/*
+	 * We must delay after the interrupt, or the controller gets confused
+	 * and never does give us good status.  Fortunately, we don't do this
+	 * often.
+	 */
+	udelay(20);
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	c1 = cafe_reg_read(cam, REG_TWSIC1);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT;
+}
+
+static int cafe_smbus_write_data(struct cafe_camera *cam,
+		u16 addr, u8 command, u8 value)
+{
+	unsigned int rval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+	rval |= TWSIC0_OVMAGIC;  /* Make OV sensors work */
+	/*
+	 * Marvell sez set clkdiv to all 1's for now.
+	 */
+	rval |= TWSIC0_CLKDIV;
+	cafe_reg_write(cam, REG_TWSIC0, rval);
+	(void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
+	rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+	cafe_reg_write(cam, REG_TWSIC1, rval);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	msleep(2); /* Required or things flake */
+
+	wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam),
+			CAFE_SMBUS_TIMEOUT);
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	rval = cafe_reg_read(cam, REG_TWSIC1);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+	if (rval & TWSIC1_WSTAT) {
+		cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr,
+				command, value);
+		return -EIO;
+	}
+	if (rval & TWSIC1_ERROR) {
+		cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr,
+				command, value);
+		return -EIO;
+	}
+	return 0;
+}
+
+
+
+static int cafe_smbus_read_done(struct cafe_camera *cam)
+{
+	unsigned long flags;
+	int c1;
+
+	/*
+	 * We must delay after the interrupt, or the controller gets confused
+	 * and never does give us good status.  Fortunately, we don't do this
+	 * often.
+	 */
+	udelay(20);
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	c1 = cafe_reg_read(cam, REG_TWSIC1);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	return c1 & (TWSIC1_RVALID|TWSIC1_ERROR);
+}
+
+
+
+static int cafe_smbus_read_data(struct cafe_camera *cam,
+		u16 addr, u8 command, u8 *value)
+{
+	unsigned int rval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID);
+	rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */
+	/*
+	 * Marvel sez set clkdiv to all 1's for now.
+	 */
+	rval |= TWSIC0_CLKDIV;
+	cafe_reg_write(cam, REG_TWSIC0, rval);
+	(void) cafe_reg_read(cam, REG_TWSIC1); /* force write */
+	rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR);
+	cafe_reg_write(cam, REG_TWSIC1, rval);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+	wait_event_timeout(cam->smbus_wait,
+			cafe_smbus_read_done(cam), CAFE_SMBUS_TIMEOUT);
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	rval = cafe_reg_read(cam, REG_TWSIC1);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+	if (rval & TWSIC1_ERROR) {
+		cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command);
+		return -EIO;
+	}
+	if (! (rval & TWSIC1_RVALID)) {
+		cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr,
+				command);
+		return -EIO;
+	}
+	*value = rval & 0xff;
+	return 0;
+}
+
+/*
+ * Perform a transfer over SMBUS.  This thing is called under
+ * the i2c bus lock, so we shouldn't race with ourselves...
+ */
+static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+		unsigned short flags, char rw, u8 command,
+		int size, union i2c_smbus_data *data)
+{
+	struct cafe_camera *cam = i2c_get_adapdata(adapter);
+	int ret = -EINVAL;
+
+	/*
+	 * Refuse to talk to anything but OV cam chips.  We should
+	 * never even see an attempt to do so, but one never knows.
+	 */
+	if (cam->sensor && addr != cam->sensor->addr) {
+		cam_err(cam, "funky smbus addr %d\n", addr);
+		return -EINVAL;
+	}
+	/*
+	 * This interface would appear to only do byte data ops.  OK
+	 * it can do word too, but the cam chip has no use for that.
+	 */
+	if (size != I2C_SMBUS_BYTE_DATA) {
+		cam_err(cam, "funky xfer size %d\n", size);
+		return -EINVAL;
+	}
+
+	if (rw == I2C_SMBUS_WRITE)
+		ret = cafe_smbus_write_data(cam, addr, command, data->byte);
+	else if (rw == I2C_SMBUS_READ)
+		ret = cafe_smbus_read_data(cam, addr, command, &data->byte);
+	return ret;
+}
+
+
+static void cafe_smbus_enable_irq(struct cafe_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cafe_reg_set_bit(cam, REG_IRQMASK, TWSIIRQS);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+static u32 cafe_smbus_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_READ_BYTE_DATA  |
+	       I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+static struct i2c_algorithm cafe_smbus_algo = {
+	.smbus_xfer = cafe_smbus_xfer,
+	.functionality = cafe_smbus_func
+};
+
+/* Somebody is on the bus */
+static int cafe_cam_init(struct cafe_camera *cam);
+static void cafe_ctlr_stop_dma(struct cafe_camera *cam);
+static void cafe_ctlr_power_down(struct cafe_camera *cam);
+
+static int cafe_smbus_attach(struct i2c_client *client)
+{
+	struct cafe_camera *cam = i2c_get_adapdata(client->adapter);
+
+	/*
+	 * Don't talk to chips we don't recognize.
+	 */
+	if (client->driver->id == I2C_DRIVERID_OV7670) {
+		cam->sensor = client;
+		return cafe_cam_init(cam);
+	}
+	return -EINVAL;
+}
+
+static int cafe_smbus_detach(struct i2c_client *client)
+{
+	struct cafe_camera *cam = i2c_get_adapdata(client->adapter);
+
+	if (cam->sensor == client) {
+		cafe_ctlr_stop_dma(cam);
+		cafe_ctlr_power_down(cam);
+		cam_err(cam, "lost the sensor!\n");
+		cam->sensor = NULL;  /* Bummer, no camera */
+		cam->state = S_NOTREADY;
+	}
+	return 0;
+}
+
+static int cafe_smbus_setup(struct cafe_camera *cam)
+{
+	struct i2c_adapter *adap = &cam->i2c_adapter;
+	int ret;
+
+	cafe_smbus_enable_irq(cam);
+	adap->id = I2C_HW_SMBUS_CAFE;
+	adap->class = I2C_CLASS_CAM_DIGITAL;
+	adap->owner = THIS_MODULE;
+	adap->client_register = cafe_smbus_attach;
+	adap->client_unregister = cafe_smbus_detach;
+	adap->algo = &cafe_smbus_algo;
+	strcpy(adap->name, "cafe_ccic");
+	i2c_set_adapdata(adap, cam);
+	ret = i2c_add_adapter(adap);
+	if (ret)
+		printk(KERN_ERR "Unable to register cafe i2c adapter\n");
+	return ret;
+}
+
+static void cafe_smbus_shutdown(struct cafe_camera *cam)
+{
+	i2c_del_adapter(&cam->i2c_adapter);
+}
+
+
+/* ------------------------------------------------------------------- */
+/*
+ * Deal with the controller.
+ */
+
+/*
+ * Do everything we think we need to have the interface operating
+ * according to the desired format.
+ */
+static void cafe_ctlr_dma(struct cafe_camera *cam)
+{
+	/*
+	 * Store the first two Y buffers (we aren't supporting
+	 * planar formats for now, so no UV bufs).  Then either
+	 * set the third if it exists, or tell the controller
+	 * to just use two.
+	 */
+	cafe_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]);
+	cafe_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]);
+	if (cam->nbufs > 2) {
+		cafe_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]);
+		cafe_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS);
+	}
+	else
+		cafe_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS);
+	cafe_reg_write(cam, REG_UBAR, 0); /* 32 bits only for now */
+}
+
+static void cafe_ctlr_image(struct cafe_camera *cam)
+{
+	int imgsz;
+	struct v4l2_pix_format *fmt = &cam->pix_format;
+
+	imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
+		(fmt->bytesperline & IMGSZ_H_MASK);
+	cafe_reg_write(cam, REG_IMGSIZE, imgsz);
+	cafe_reg_write(cam, REG_IMGOFFSET, 0);
+	/* YPITCH just drops the last two bits */
+	cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
+			IMGP_YP_MASK);
+	/*
+	 * Tell the controller about the image format we are using.
+	 */
+	switch (cam->pix_format.pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+	    cafe_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
+			    C0_DF_MASK);
+	    break;
+
+	case V4L2_PIX_FMT_RGB444:
+	    cafe_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
+			    C0_DF_MASK);
+		/* Alpha value? */
+	    break;
+
+	case V4L2_PIX_FMT_RGB565:
+	    cafe_reg_write_mask(cam, REG_CTRL0,
+			    C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
+			    C0_DF_MASK);
+	    break;
+
+	default:
+	    cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
+	    break;
+	}
+	/*
+	 * Make sure it knows we want to use hsync/vsync.
+	 */
+	cafe_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
+			C0_SIFM_MASK);
+}
+
+
+/*
+ * Configure the controller for operation; caller holds the
+ * device mutex.
+ */
+static int cafe_ctlr_configure(struct cafe_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cafe_ctlr_dma(cam);
+	cafe_ctlr_image(cam);
+	cafe_set_config_needed(cam, 0);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	return 0;
+}
+
+static void cafe_ctlr_irq_enable(struct cafe_camera *cam)
+{
+	/*
+	 * Clear any pending interrupts, since we do not
+	 * expect to have I/O active prior to enabling.
+	 */
+	cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS);
+	cafe_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+static void cafe_ctlr_irq_disable(struct cafe_camera *cam)
+{
+	cafe_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS);
+}
+
+/*
+ * Make the controller start grabbing images.  Everything must
+ * be set up before doing this.
+ */
+static void cafe_ctlr_start(struct cafe_camera *cam)
+{
+	/* set_bit performs a read, so no other barrier should be
+	   needed here */
+	cafe_reg_set_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+static void cafe_ctlr_stop(struct cafe_camera *cam)
+{
+	cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+}
+
+static void cafe_ctlr_init(struct cafe_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	/*
+	 * Added magic to bring up the hardware on the B-Test board
+	 */
+	cafe_reg_write(cam, 0x3038, 0x8);
+	cafe_reg_write(cam, 0x315c, 0x80008);
+	/*
+	 * Go through the dance needed to wake the device up.
+	 * Note that these registers are global and shared
+	 * with the NAND and SD devices.  Interaction between the
+	 * three still needs to be examined.
+	 */
+	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */
+	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC);
+	cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS);
+	mdelay(5);	/* FIXME revisit this */
+	cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC);
+	cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN);
+	/*
+	 * Make sure it's not powered down.
+	 */
+	cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+	/*
+	 * Turn off the enable bit.  It sure should be off anyway,
+	 * but it's good to be sure.
+	 */
+	cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
+	/*
+	 * Mask all interrupts.
+	 */
+	cafe_reg_write(cam, REG_IRQMASK, 0);
+	/*
+	 * Clock the sensor appropriately.  Controller clock should
+	 * be 48MHz, sensor "typical" value is half that.
+	 */
+	cafe_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+/*
+ * Stop the controller, and don't return until we're really sure that no
+ * further DMA is going on.
+ */
+static void cafe_ctlr_stop_dma(struct cafe_camera *cam)
+{
+	unsigned long flags;
+
+	/*
+	 * Theory: stop the camera controller (whether it is operating
+	 * or not).  Delay briefly just in case we race with the SOF
+	 * interrupt, then wait until no DMA is active.
+	 */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cafe_ctlr_stop(cam);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	mdelay(1);
+	wait_event_timeout(cam->iowait,
+			!test_bit(CF_DMA_ACTIVE, &cam->flags), HZ);
+	if (test_bit(CF_DMA_ACTIVE, &cam->flags))
+		cam_err(cam, "Timeout waiting for DMA to end\n");
+		/* This would be bad news - what now? */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cam->state = S_IDLE;
+	cafe_ctlr_irq_disable(cam);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/*
+ * Power up and down.
+ */
+static void cafe_ctlr_power_up(struct cafe_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
+	/*
+	 * Put the sensor into operational mode (assumes OLPC-style
+	 * wiring).  Control 0 is reset - set to 1 to operate.
+	 * Control 1 is power down, set to 0 to operate.
+	 */
+	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
+	mdelay(1); /* Marvell says 1ms will do it */
+	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+	mdelay(1); /* Enough? */
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+static void cafe_ctlr_power_down(struct cafe_camera *cam)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1);
+	cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+/* -------------------------------------------------------------------- */
+/*
+ * Communications with the sensor.
+ */
+
+static int __cafe_cam_cmd(struct cafe_camera *cam, int cmd, void *arg)
+{
+	struct i2c_client *sc = cam->sensor;
+	int ret;
+
+	if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL)
+		return -EINVAL;
+	ret = sc->driver->command(sc, cmd, arg);
+	if (ret == -EPERM) /* Unsupported command */
+		return 0;
+	return ret;
+}
+
+static int __cafe_cam_reset(struct cafe_camera *cam)
+{
+	int zero = 0;
+	return __cafe_cam_cmd(cam, VIDIOC_INT_RESET, &zero);
+}
+
+/*
+ * We have found the sensor on the i2c.  Let's try to have a
+ * conversation.
+ */
+static int cafe_cam_init(struct cafe_camera *cam)
+{
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	if (cam->state != S_NOTREADY)
+		cam_warn(cam, "Cam init with device in funky state %d",
+				cam->state);
+	ret = __cafe_cam_reset(cam);
+	if (ret)
+		goto out;
+	ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type);
+	if (ret)
+		goto out;
+//	if (cam->sensor->addr != OV7xx0_SID) {
+	if (cam->sensor_type != V4L2_IDENT_OV7670) {
+		cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr);
+		ret = -EINVAL;
+		goto out;
+	}
+/* Get/set parameters? */
+	ret = 0;
+	cam->state = S_IDLE;
+  out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+/*
+ * Configure the sensor to match the parameters we have.  Caller should
+ * hold s_mutex
+ */
+static int cafe_cam_set_flip(struct cafe_camera *cam)
+{
+	struct v4l2_control ctrl;
+
+	memset(&ctrl, 0, sizeof(ctrl));
+	ctrl.id = V4L2_CID_VFLIP;
+	ctrl.value = flip;
+	return __cafe_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl);
+}
+
+
+static int cafe_cam_configure(struct cafe_camera *cam)
+{
+	struct v4l2_format fmt;
+	int ret, zero = 0;
+
+	if (cam->state != S_IDLE)
+		return -EINVAL;
+	fmt.fmt.pix = cam->pix_format;
+	ret = __cafe_cam_cmd(cam, VIDIOC_INT_INIT, &zero);
+	if (ret == 0)
+		ret = __cafe_cam_cmd(cam, VIDIOC_S_FMT, &fmt);
+	/*
+	 * OV7670 does weird things if flip is set *before* format...
+	 */
+	ret += cafe_cam_set_flip(cam);
+	return ret;
+}
+
+/* -------------------------------------------------------------------- */
+/*
+ * DMA buffer management.  These functions need s_mutex held.
+ */
+
+/* FIXME: this is inefficient as hell, since dma_alloc_coherent just
+ * does a get_free_pages() call, and we waste a good chunk of an orderN
+ * allocation.  Should try to allocate the whole set in one chunk.
+ */
+static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime)
+{
+	int i;
+
+	cafe_set_config_needed(cam, 1);
+	if (loadtime)
+		cam->dma_buf_size = dma_buf_size;
+	else
+		cam->dma_buf_size = cam->pix_format.sizeimage;
+	if (n_dma_bufs > 3)
+		n_dma_bufs = 3;
+
+	cam->nbufs = 0;
+	for (i = 0; i < n_dma_bufs; i++) {
+		cam->dma_bufs[i] = dma_alloc_coherent(&cam->pdev->dev,
+				cam->dma_buf_size, cam->dma_handles + i,
+				GFP_KERNEL);
+		if (cam->dma_bufs[i] == NULL) {
+			cam_warn(cam, "Failed to allocate DMA buffer\n");
+			break;
+		}
+		/* For debug, remove eventually */
+		memset(cam->dma_bufs[i], 0xcc, cam->dma_buf_size);
+		(cam->nbufs)++;
+	}
+
+	switch (cam->nbufs) {
+	case 1:
+	    dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
+			    cam->dma_bufs[0], cam->dma_handles[0]);
+	    cam->nbufs = 0;
+	case 0:
+	    cam_err(cam, "Insufficient DMA buffers, cannot operate\n");
+	    return -ENOMEM;
+
+	case 2:
+	    if (n_dma_bufs > 2)
+		    cam_warn(cam, "Will limp along with only 2 buffers\n");
+	    break;
+	}
+	return 0;
+}
+
+static void cafe_free_dma_bufs(struct cafe_camera *cam)
+{
+	int i;
+
+	for (i = 0; i < cam->nbufs; i++) {
+		dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size,
+				cam->dma_bufs[i], cam->dma_handles[i]);
+		cam->dma_bufs[i] = NULL;
+	}
+	cam->nbufs = 0;
+}
+
+
+
+
+
+/* ----------------------------------------------------------------------- */
+/*
+ * Here starts the V4L2 interface code.
+ */
+
+/*
+ * Read an image from the device.
+ */
+static ssize_t cafe_deliver_buffer(struct cafe_camera *cam,
+		char __user *buffer, size_t len, loff_t *pos)
+{
+	int bufno;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	if (cam->next_buf < 0) {
+		cam_err(cam, "deliver_buffer: No next buffer\n");
+		spin_unlock_irqrestore(&cam->dev_lock, flags);
+		return -EIO;
+	}
+	bufno = cam->next_buf;
+	clear_bit(bufno, &cam->flags);
+	if (++(cam->next_buf) >= cam->nbufs)
+		cam->next_buf = 0;
+	if (! test_bit(cam->next_buf, &cam->flags))
+		cam->next_buf = -1;
+	cam->specframes = 0;
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+
+	if (len > cam->pix_format.sizeimage)
+		len = cam->pix_format.sizeimage;
+	if (copy_to_user(buffer, cam->dma_bufs[bufno], len))
+		return -EFAULT;
+	(*pos) += len;
+	return len;
+}
+
+/*
+ * Get everything ready, and start grabbing frames.
+ */
+static int cafe_read_setup(struct cafe_camera *cam, enum cafe_state state)
+{
+	int ret;
+	unsigned long flags;
+
+	/*
+	 * Configuration.  If we still don't have DMA buffers,
+	 * make one last, desperate attempt.
+	 */
+	if (cam->nbufs == 0)
+		if (cafe_alloc_dma_bufs(cam, 0))
+			return -ENOMEM;
+
+	if (cafe_needs_config(cam)) {
+		cafe_cam_configure(cam);
+		ret = cafe_ctlr_configure(cam);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * Turn it loose.
+	 */
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	cafe_reset_buffers(cam);
+	cafe_ctlr_irq_enable(cam);
+	cam->state = state;
+	cafe_ctlr_start(cam);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	return 0;
+}
+
+
+static ssize_t cafe_v4l_read(struct file *filp,
+		char __user *buffer, size_t len, loff_t *pos)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret;
+
+	/*
+	 * Perhaps we're in speculative read mode and already
+	 * have data?
+	 */
+	mutex_lock(&cam->s_mutex);
+	if (cam->state == S_SPECREAD) {
+		if (cam->next_buf >= 0) {
+			ret = cafe_deliver_buffer(cam, buffer, len, pos);
+			if (ret != 0)
+				goto out_unlock;
+		}
+	} else if (cam->state == S_FLAKED || cam->state == S_NOTREADY) {
+		ret = -EIO;
+		goto out_unlock;
+	} else if (cam->state != S_IDLE) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	/*
+	 * v4l2: multiple processes can open the device, but only
+	 * one gets to grab data from it.
+	 */
+	if (cam->owner && cam->owner != filp) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+	cam->owner = filp;
+
+	/*
+	 * Do setup if need be.
+	 */
+	if (cam->state != S_SPECREAD) {
+		ret = cafe_read_setup(cam, S_SINGLEREAD);
+		if (ret)
+			goto out_unlock;
+	}
+	/*
+	 * Wait for something to happen.  This should probably
+	 * be interruptible (FIXME).
+	 */
+	wait_event_timeout(cam->iowait, cam->next_buf >= 0, HZ);
+	if (cam->next_buf < 0) {
+		cam_err(cam, "read() operation timed out\n");
+		cafe_ctlr_stop_dma(cam);
+		ret = -EIO;
+		goto out_unlock;
+	}
+	/*
+	 * Give them their data and we should be done.
+	 */
+	ret = cafe_deliver_buffer(cam, buffer, len, pos);
+
+  out_unlock:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+
+
+
+
+
+
+/*
+ * Streaming I/O support.
+ */
+
+
+
+static int cafe_vidioc_streamon(struct file *filp, void *priv,
+		enum v4l2_buf_type type)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret = -EINVAL;
+
+	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		goto out;
+	mutex_lock(&cam->s_mutex);
+	if (cam->state != S_IDLE || cam->n_sbufs == 0)
+		goto out_unlock;
+
+	cam->sequence = 0;
+	ret = cafe_read_setup(cam, S_STREAMING);
+
+  out_unlock:
+	mutex_unlock(&cam->s_mutex);
+  out:
+	return ret;
+}
+
+
+static int cafe_vidioc_streamoff(struct file *filp, void *priv,
+		enum v4l2_buf_type type)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret = -EINVAL;
+
+	if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		goto out;
+	mutex_lock(&cam->s_mutex);
+	if (cam->state != S_STREAMING)
+		goto out_unlock;
+
+	cafe_ctlr_stop_dma(cam);
+	ret = 0;
+
+  out_unlock:
+	mutex_unlock(&cam->s_mutex);
+  out:
+	return ret;
+}
+
+
+
+static int cafe_setup_siobuf(struct cafe_camera *cam, int index)
+{
+	struct cafe_sio_buffer *buf = cam->sb_bufs + index;
+
+	INIT_LIST_HEAD(&buf->list);
+	buf->v4lbuf.length = PAGE_ALIGN(cam->pix_format.sizeimage);
+	buf->buffer = vmalloc_user(buf->v4lbuf.length);
+	if (buf->buffer == NULL)
+		return -ENOMEM;
+	buf->mapcount = 0;
+	buf->cam = cam;
+
+	buf->v4lbuf.index = index;
+	buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	buf->v4lbuf.field = V4L2_FIELD_NONE;
+	buf->v4lbuf.memory = V4L2_MEMORY_MMAP;
+	/*
+	 * Offset: must be 32-bit even on a 64-bit system.  video-buf
+	 * just uses the length times the index, but the spec warns
+	 * against doing just that - vma merging problems.  So we
+	 * leave a gap between each pair of buffers.
+	 */
+	buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length;
+	return 0;
+}
+
+static int cafe_free_sio_buffers(struct cafe_camera *cam)
+{
+	int i;
+
+	/*
+	 * If any buffers are mapped, we cannot free them at all.
+	 */
+	for (i = 0; i < cam->n_sbufs; i++)
+		if (cam->sb_bufs[i].mapcount > 0)
+			return -EBUSY;
+	/*
+	 * OK, let's do it.
+	 */
+	for (i = 0; i < cam->n_sbufs; i++)
+		vfree(cam->sb_bufs[i].buffer);
+	cam->n_sbufs = 0;
+	kfree(cam->sb_bufs);
+	cam->sb_bufs = NULL;
+	INIT_LIST_HEAD(&cam->sb_avail);
+	INIT_LIST_HEAD(&cam->sb_full);
+	return 0;
+}
+
+
+
+static int cafe_vidioc_reqbufs(struct file *filp, void *priv,
+		struct v4l2_requestbuffers *req)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret;
+
+	/*
+	 * Make sure it's something we can do.  User pointers could be
+	 * implemented without great pain, but that's not been done yet.
+	 */
+	if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (req->memory != V4L2_MEMORY_MMAP)
+		return -EINVAL;
+	/*
+	 * If they ask for zero buffers, they really want us to stop streaming
+	 * (if it's happening) and free everything.  Should we check owner?
+	 */
+	mutex_lock(&cam->s_mutex);
+	if (req->count == 0) {
+		if (cam->state == S_STREAMING)
+			cafe_ctlr_stop_dma(cam);
+		ret = cafe_free_sio_buffers (cam);
+		goto out;
+	}
+	/*
+	 * Device needs to be idle and working.  We *could* try to do the
+	 * right thing in S_SPECREAD by shutting things down, but it
+	 * probably doesn't matter.
+	 */
+	if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) {
+		ret = -EBUSY;
+		goto out;
+	}
+	cam->owner = filp;
+
+	if (req->count < min_buffers)
+		req->count = min_buffers;
+	else if (req->count > max_buffers)
+		req->count = max_buffers;
+	if (cam->n_sbufs > 0) {
+		ret = cafe_free_sio_buffers(cam);
+		if (ret)
+			goto out;
+	}
+
+	cam->sb_bufs = kzalloc(req->count*sizeof(struct cafe_sio_buffer),
+			GFP_KERNEL);
+	if (cam->sb_bufs == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	for (cam->n_sbufs = 0; cam->n_sbufs < req->count; (cam->n_sbufs++)) {
+		ret = cafe_setup_siobuf(cam, cam->n_sbufs);
+		if (ret)
+			break;
+	}
+
+	if (cam->n_sbufs == 0)  /* no luck at all - ret already set */
+		kfree(cam->sb_bufs);
+	else
+		ret = 0;
+	req->count = cam->n_sbufs;  /* In case of partial success */
+
+  out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int cafe_vidioc_querybuf(struct file *filp, void *priv,
+		struct v4l2_buffer *buf)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret = -EINVAL;
+
+	mutex_lock(&cam->s_mutex);
+	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		goto out;
+	if (buf->index < 0 || buf->index >= cam->n_sbufs)
+		goto out;
+	*buf = cam->sb_bufs[buf->index].v4lbuf;
+	ret = 0;
+  out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+static int cafe_vidioc_qbuf(struct file *filp, void *priv,
+		struct v4l2_buffer *buf)
+{
+	struct cafe_camera *cam = filp->private_data;
+	struct cafe_sio_buffer *sbuf;
+	int ret = -EINVAL;
+	unsigned long flags;
+
+	mutex_lock(&cam->s_mutex);
+	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		goto out;
+	if (buf->index < 0 || buf->index >= cam->n_sbufs)
+		goto out;
+	sbuf = cam->sb_bufs + buf->index;
+	if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) {
+		ret = 0; /* Already queued?? */
+		goto out;
+	}
+	if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) {
+		/* Spec doesn't say anything, seems appropriate tho */
+		ret = -EBUSY;
+		goto out;
+	}
+	sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED;
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	list_add(&sbuf->list, &cam->sb_avail);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+	ret = 0;
+  out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+static int cafe_vidioc_dqbuf(struct file *filp, void *priv,
+		struct v4l2_buffer *buf)
+{
+	struct cafe_camera *cam = filp->private_data;
+	struct cafe_sio_buffer *sbuf;
+	int ret = -EINVAL;
+	unsigned long flags;
+
+	mutex_lock(&cam->s_mutex);
+	if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		goto out_unlock;
+	if (cam->state != S_STREAMING)
+		goto out_unlock;
+	if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) {
+		ret = -EAGAIN;
+		goto out_unlock;
+	}
+
+	while (list_empty(&cam->sb_full) && cam->state == S_STREAMING) {
+		mutex_unlock(&cam->s_mutex);
+		if (wait_event_interruptible(cam->iowait,
+						!list_empty(&cam->sb_full))) {
+			ret = -ERESTARTSYS;
+			goto out;
+		}
+		mutex_lock(&cam->s_mutex);
+	}
+
+	if (cam->state != S_STREAMING)
+		ret = -EINTR;
+	else {
+		spin_lock_irqsave(&cam->dev_lock, flags);
+		/* Should probably recheck !list_empty() here */
+		sbuf = list_entry(cam->sb_full.next,
+				struct cafe_sio_buffer, list);
+		list_del_init(&sbuf->list);
+		spin_unlock_irqrestore(&cam->dev_lock, flags);
+		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE;
+		*buf = sbuf->v4lbuf;
+		ret = 0;
+	}
+
+  out_unlock:
+	mutex_unlock(&cam->s_mutex);
+  out:
+	return ret;
+}
+
+
+
+static void cafe_v4l_vm_open(struct vm_area_struct *vma)
+{
+	struct cafe_sio_buffer *sbuf = vma->vm_private_data;
+	/*
+	 * Locking: done under mmap_sem, so we don't need to
+	 * go back to the camera lock here.
+	 */
+	sbuf->mapcount++;
+}
+
+
+static void cafe_v4l_vm_close(struct vm_area_struct *vma)
+{
+	struct cafe_sio_buffer *sbuf = vma->vm_private_data;
+
+	mutex_lock(&sbuf->cam->s_mutex);
+	sbuf->mapcount--;
+	/* Docs say we should stop I/O too... */
+	if (sbuf->mapcount == 0)
+		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED;
+	mutex_unlock(&sbuf->cam->s_mutex);
+}
+
+static struct vm_operations_struct cafe_v4l_vm_ops = {
+	.open = cafe_v4l_vm_open,
+	.close = cafe_v4l_vm_close
+};
+
+
+static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct cafe_camera *cam = filp->private_data;
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	int ret = -EINVAL;
+	int i;
+	struct cafe_sio_buffer *sbuf = NULL;
+
+	if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED))
+		return -EINVAL;
+	/*
+	 * Find the buffer they are looking for.
+	 */
+	mutex_lock(&cam->s_mutex);
+	for (i = 0; i < cam->n_sbufs; i++)
+		if (cam->sb_bufs[i].v4lbuf.m.offset == offset) {
+			sbuf = cam->sb_bufs + i;
+			break;
+		}
+	if (sbuf == NULL)
+		goto out;
+
+	ret = remap_vmalloc_range(vma, sbuf->buffer, 0);
+	if (ret)
+		goto out;
+	vma->vm_flags |= VM_DONTEXPAND;
+	vma->vm_private_data = sbuf;
+	vma->vm_ops = &cafe_v4l_vm_ops;
+	sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED;
+	cafe_v4l_vm_open(vma);
+	ret = 0;
+  out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+
+static int cafe_v4l_open(struct inode *inode, struct file *filp)
+{
+	struct cafe_camera *cam;
+
+	cam = cafe_find_dev(iminor(inode));
+	if (cam == NULL)
+		return -ENODEV;
+	filp->private_data = cam;
+
+	mutex_lock(&cam->s_mutex);
+	if (cam->users == 0) {
+		cafe_ctlr_power_up(cam);
+		__cafe_cam_reset(cam);
+		cafe_set_config_needed(cam, 1);
+	/* FIXME make sure this is complete */
+	}
+	(cam->users)++;
+	mutex_unlock(&cam->s_mutex);
+	return 0;
+}
+
+
+static int cafe_v4l_release(struct inode *inode, struct file *filp)
+{
+	struct cafe_camera *cam = filp->private_data;
+
+	mutex_lock(&cam->s_mutex);
+	(cam->users)--;
+	if (filp == cam->owner) {
+		cafe_ctlr_stop_dma(cam);
+		cafe_free_sio_buffers(cam);
+		cam->owner = NULL;
+	}
+	if (cam->users == 0) {
+		cafe_ctlr_power_down(cam);
+		if (! alloc_bufs_at_load)
+			cafe_free_dma_bufs(cam);
+	}
+	mutex_unlock(&cam->s_mutex);
+	return 0;
+}
+
+
+
+static unsigned int cafe_v4l_poll(struct file *filp,
+		struct poll_table_struct *pt)
+{
+	struct cafe_camera *cam = filp->private_data;
+
+	poll_wait(filp, &cam->iowait, pt);
+	if (cam->next_buf >= 0)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+
+
+static int cafe_vidioc_queryctrl(struct file *filp, void *priv,
+		struct v4l2_queryctrl *qc)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_QUERYCTRL, qc);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int cafe_vidioc_g_ctrl(struct file *filp, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_G_CTRL, ctrl);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int cafe_vidioc_s_ctrl(struct file *filp, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct cafe_camera *cam = filp->private_data;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_S_CTRL, ctrl);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+
+
+
+static int cafe_vidioc_querycap(struct file *file, void *priv,
+		struct v4l2_capability *cap)
+{
+	strcpy(cap->driver, "cafe_ccic");
+	strcpy(cap->card, "cafe_ccic");
+	cap->version = CAFE_VERSION;
+	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+	return 0;
+}
+
+
+/*
+ * The default format we use until somebody says otherwise.
+ */
+static struct v4l2_pix_format cafe_def_pix_format = {
+	.width		= VGA_WIDTH,
+	.height		= VGA_HEIGHT,
+	.pixelformat	= V4L2_PIX_FMT_YUYV,
+	.field		= V4L2_FIELD_NONE,
+	.bytesperline	= VGA_WIDTH*2,
+	.sizeimage	= VGA_WIDTH*VGA_HEIGHT*2,
+};
+
+static int cafe_vidioc_enum_fmt_cap(struct file *filp,
+		void *priv, struct v4l2_fmtdesc *fmt)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+
+static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
+		struct v4l2_format *fmt)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_TRY_FMT, fmt);
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
+		struct v4l2_format *fmt)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	/*
+	 * Can't do anything if the device is not idle
+	 * Also can't if there are streaming buffers in place.
+	 */
+	if (cam->state != S_IDLE || cam->n_sbufs > 0)
+		return -EBUSY;
+	/*
+	 * See if the formatting works in principle.
+	 */
+	ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt);
+	if (ret)
+		return ret;
+	/*
+	 * Now we start to change things for real, so let's do it
+	 * under lock.
+	 */
+	mutex_lock(&cam->s_mutex);
+	cam->pix_format = fmt->fmt.pix;
+	/*
+	 * Make sure we have appropriate DMA buffers.
+	 */
+	ret = -ENOMEM;
+	if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage)
+		cafe_free_dma_bufs(cam);
+	if (cam->nbufs == 0) {
+		if (cafe_alloc_dma_bufs(cam, 0))
+			goto out;
+	}
+	/*
+	 * It looks like this might work, so let's program the sensor.
+	 */
+	ret = cafe_cam_configure(cam);
+	if (! ret)
+		ret = cafe_ctlr_configure(cam);
+  out:
+	mutex_unlock(&cam->s_mutex);
+	return ret;
+}
+
+/*
+ * Return our stored notion of how the camera is/should be configured.
+ * The V4l2 spec wants us to be smarter, and actually get this from
+ * the camera (and not mess with it at open time).  Someday.
+ */
+static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv,
+		struct v4l2_format *f)
+{
+	struct cafe_camera *cam = priv;
+
+	f->fmt.pix = cam->pix_format;
+	return 0;
+}
+
+/*
+ * We only have one input - the sensor - so minimize the nonsense here.
+ */
+static int cafe_vidioc_enum_input(struct file *filp, void *priv,
+		struct v4l2_input *input)
+{
+	if (input->index != 0)
+		return -EINVAL;
+
+	input->type = V4L2_INPUT_TYPE_CAMERA;
+	input->std = V4L2_STD_ALL; /* Not sure what should go here */
+	strcpy(input->name, "Camera");
+	return 0;
+}
+
+static int cafe_vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+	*i = 0;
+	return 0;
+}
+
+static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+	return 0;
+}
+
+/* from vivi.c */
+static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a)
+{
+	return 0;
+}
+
+/*
+ * G/S_PARM.  Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
+ */
+static int cafe_vidioc_g_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+static int cafe_vidioc_s_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+
+static void cafe_v4l_dev_release(struct video_device *vd)
+{
+	struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev);
+
+	kfree(cam);
+}
+
+
+/*
+ * This template device holds all of those v4l2 methods; we
+ * clone it for specific real devices.
+ */
+
+static struct file_operations cafe_v4l_fops = {
+	.owner = THIS_MODULE,
+	.open = cafe_v4l_open,
+	.release = cafe_v4l_release,
+	.read = cafe_v4l_read,
+	.poll = cafe_v4l_poll,
+	.mmap = cafe_v4l_mmap,
+	.ioctl = video_ioctl2,
+	.llseek = no_llseek,
+};
+
+static struct video_device cafe_v4l_template = {
+	.name = "cafe",
+	.type = VFL_TYPE_GRABBER,
+	.type2 = VID_TYPE_CAPTURE,
+	.minor = -1, /* Get one dynamically */
+	.tvnorms = V4L2_STD_NTSC_M,
+	.current_norm = V4L2_STD_NTSC_M,  /* make mplayer happy */
+
+	.fops = &cafe_v4l_fops,
+	.release = cafe_v4l_dev_release,
+
+	.vidioc_querycap 	= cafe_vidioc_querycap,
+	.vidioc_enum_fmt_cap	= cafe_vidioc_enum_fmt_cap,
+	.vidioc_try_fmt_cap	= cafe_vidioc_try_fmt_cap,
+	.vidioc_s_fmt_cap	= cafe_vidioc_s_fmt_cap,
+	.vidioc_g_fmt_cap	= cafe_vidioc_g_fmt_cap,
+	.vidioc_enum_input	= cafe_vidioc_enum_input,
+	.vidioc_g_input		= cafe_vidioc_g_input,
+	.vidioc_s_input		= cafe_vidioc_s_input,
+	.vidioc_s_std		= cafe_vidioc_s_std,
+	.vidioc_reqbufs		= cafe_vidioc_reqbufs,
+	.vidioc_querybuf	= cafe_vidioc_querybuf,
+	.vidioc_qbuf		= cafe_vidioc_qbuf,
+	.vidioc_dqbuf		= cafe_vidioc_dqbuf,
+	.vidioc_streamon	= cafe_vidioc_streamon,
+	.vidioc_streamoff	= cafe_vidioc_streamoff,
+	.vidioc_queryctrl	= cafe_vidioc_queryctrl,
+	.vidioc_g_ctrl		= cafe_vidioc_g_ctrl,
+	.vidioc_s_ctrl		= cafe_vidioc_s_ctrl,
+	.vidioc_g_parm		= cafe_vidioc_g_parm,
+	.vidioc_s_parm		= cafe_vidioc_s_parm,
+};
+
+
+
+
+
+
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Interrupt handler stuff
+ */
+
+
+
+static void cafe_frame_tasklet(unsigned long data)
+{
+	struct cafe_camera *cam = (struct cafe_camera *) data;
+	int i;
+	unsigned long flags;
+	struct cafe_sio_buffer *sbuf;
+
+	spin_lock_irqsave(&cam->dev_lock, flags);
+	for (i = 0; i < cam->nbufs; i++) {
+		int bufno = cam->next_buf;
+		if (bufno < 0) {  /* "will never happen" */
+			cam_err(cam, "No valid bufs in tasklet!\n");
+			break;
+		}
+		if (++(cam->next_buf) >= cam->nbufs)
+			cam->next_buf = 0;
+		if (! test_bit(bufno, &cam->flags))
+			continue;
+		if (list_empty(&cam->sb_avail))
+			break;  /* Leave it valid, hope for better later */
+		clear_bit(bufno, &cam->flags);
+		/*
+		 * We could perhaps drop the spinlock during this
+		 * big copy.  Something to consider.
+		 */
+		sbuf = list_entry(cam->sb_avail.next,
+				struct cafe_sio_buffer, list);
+		memcpy(sbuf->buffer, cam->dma_bufs[bufno],
+				cam->pix_format.sizeimage);
+		sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage;
+		sbuf->v4lbuf.sequence = cam->buf_seq[bufno];
+		sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED;
+		sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE;
+		list_move_tail(&sbuf->list, &cam->sb_full);
+	}
+	if (! list_empty(&cam->sb_full))
+		wake_up(&cam->iowait);
+	spin_unlock_irqrestore(&cam->dev_lock, flags);
+}
+
+
+
+static void cafe_frame_complete(struct cafe_camera *cam, int frame)
+{
+	/*
+	 * Basic frame housekeeping.
+	 */
+	if (test_bit(frame, &cam->flags) && printk_ratelimit())
+		cam_err(cam, "Frame overrun on %d, frames lost\n", frame);
+	set_bit(frame, &cam->flags);
+	clear_bit(CF_DMA_ACTIVE, &cam->flags);
+	if (cam->next_buf < 0)
+		cam->next_buf = frame;
+	cam->buf_seq[frame] = ++(cam->sequence);
+
+	switch (cam->state) {
+	/*
+	 * If in single read mode, try going speculative.
+	 */
+	    case S_SINGLEREAD:
+		cam->state = S_SPECREAD;
+		cam->specframes = 0;
+		wake_up(&cam->iowait);
+		break;
+
+	/*
+	 * If we are already doing speculative reads, and nobody is
+	 * reading them, just stop.
+	 */
+	    case S_SPECREAD:
+		if (++(cam->specframes) >= cam->nbufs) {
+			cafe_ctlr_stop(cam);
+			cafe_ctlr_irq_disable(cam);
+			cam->state = S_IDLE;
+		}
+		wake_up(&cam->iowait);
+		break;
+	/*
+	 * For the streaming case, we defer the real work to the
+	 * camera tasklet.
+	 *
+	 * FIXME: if the application is not consuming the buffers,
+	 * we should eventually put things on hold and restart in
+	 * vidioc_dqbuf().
+	 */
+	    case S_STREAMING:
+		tasklet_schedule(&cam->s_tasklet);
+		break;
+
+	    default:
+		cam_err(cam, "Frame interrupt in non-operational state\n");
+		break;
+	}
+}
+
+
+
+
+static void cafe_frame_irq(struct cafe_camera *cam, unsigned int irqs)
+{
+	unsigned int frame;
+
+	cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */
+	/*
+	 * Handle any frame completions.  There really should
+	 * not be more than one of these, or we have fallen
+	 * far behind.
+	 */
+	for (frame = 0; frame < cam->nbufs; frame++)
+		if (irqs & (IRQ_EOF0 << frame))
+			cafe_frame_complete(cam, frame);
+	/*
+	 * If a frame starts, note that we have DMA active.  This
+	 * code assumes that we won't get multiple frame interrupts
+	 * at once; may want to rethink that.
+	 */
+	if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2))
+		set_bit(CF_DMA_ACTIVE, &cam->flags);
+}
+
+
+
+static irqreturn_t cafe_irq(int irq, void *data)
+{
+	struct cafe_camera *cam = data;
+	unsigned int irqs;
+
+	spin_lock(&cam->dev_lock);
+	irqs = cafe_reg_read(cam, REG_IRQSTAT);
+	if ((irqs & ALLIRQS) == 0) {
+		spin_unlock(&cam->dev_lock);
+		return IRQ_NONE;
+	}
+	if (irqs & FRAMEIRQS)
+		cafe_frame_irq(cam, irqs);
+	if (irqs & TWSIIRQS) {
+		cafe_reg_write(cam, REG_IRQSTAT, TWSIIRQS);
+		wake_up(&cam->smbus_wait);
+	}
+	spin_unlock(&cam->dev_lock);
+	return IRQ_HANDLED;
+}
+
+
+/* -------------------------------------------------------------------------- */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * Debugfs stuff.
+ */
+
+static char cafe_debug_buf[1024];
+static struct dentry *cafe_dfs_root;
+
+static void cafe_dfs_setup(void)
+{
+	cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL);
+	if (IS_ERR(cafe_dfs_root)) {
+		cafe_dfs_root = NULL;  /* Never mind */
+		printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n");
+	}
+}
+
+static void cafe_dfs_shutdown(void)
+{
+	if (cafe_dfs_root)
+		debugfs_remove(cafe_dfs_root);
+}
+
+static int cafe_dfs_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t cafe_dfs_read_regs(struct file *file,
+		char __user *buf, size_t count, loff_t *ppos)
+{
+	struct cafe_camera *cam = file->private_data;
+	char *s = cafe_debug_buf;
+	int offset;
+
+	for (offset = 0; offset < 0x44; offset += 4)
+		s += sprintf(s, "%02x: %08x\n", offset,
+				cafe_reg_read(cam, offset));
+	for (offset = 0x88; offset <= 0x90; offset += 4)
+		s += sprintf(s, "%02x: %08x\n", offset,
+				cafe_reg_read(cam, offset));
+	for (offset = 0xb4; offset <= 0xbc; offset += 4)
+		s += sprintf(s, "%02x: %08x\n", offset,
+				cafe_reg_read(cam, offset));
+	for (offset = 0x3000; offset <= 0x300c; offset += 4)
+		s += sprintf(s, "%04x: %08x\n", offset,
+				cafe_reg_read(cam, offset));
+	return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf,
+			s - cafe_debug_buf);
+}
+
+static struct file_operations cafe_dfs_reg_ops = {
+	.owner = THIS_MODULE,
+	.read = cafe_dfs_read_regs,
+	.open = cafe_dfs_open
+};
+
+static ssize_t cafe_dfs_read_cam(struct file *file,
+		char __user *buf, size_t count, loff_t *ppos)
+{
+	struct cafe_camera *cam = file->private_data;
+	char *s = cafe_debug_buf;
+	int offset;
+
+	if (! cam->sensor)
+		return -EINVAL;
+	for (offset = 0x0; offset < 0x8a; offset++)
+	{
+		u8 v;
+
+		cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v);
+		s += sprintf(s, "%02x: %02x\n", offset, v);
+	}
+	return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf,
+			s - cafe_debug_buf);
+}
+
+static struct file_operations cafe_dfs_cam_ops = {
+	.owner = THIS_MODULE,
+	.read = cafe_dfs_read_cam,
+	.open = cafe_dfs_open
+};
+
+
+
+static void cafe_dfs_cam_setup(struct cafe_camera *cam)
+{
+	char fname[40];
+
+	if (!cafe_dfs_root)
+		return;
+	sprintf(fname, "regs-%d", cam->v4ldev.minor);
+	cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root,
+			cam, &cafe_dfs_reg_ops);
+	sprintf(fname, "cam-%d", cam->v4ldev.minor);
+	cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root,
+			cam, &cafe_dfs_cam_ops);
+}
+
+
+static void cafe_dfs_cam_shutdown(struct cafe_camera *cam)
+{
+	if (! IS_ERR(cam->dfs_regs))
+		debugfs_remove(cam->dfs_regs);
+	if (! IS_ERR(cam->dfs_cam_regs))
+		debugfs_remove(cam->dfs_cam_regs);
+}
+
+#else
+
+#define cafe_dfs_setup()
+#define cafe_dfs_shutdown()
+#define cafe_dfs_cam_setup(cam)
+#define cafe_dfs_cam_shutdown(cam)
+#endif    /* CONFIG_VIDEO_ADV_DEBUG */
+
+
+
+
+/* ------------------------------------------------------------------------*/
+/*
+ * PCI interface stuff.
+ */
+
+static int cafe_pci_probe(struct pci_dev *pdev,
+		const struct pci_device_id *id)
+{
+	int ret;
+	u16 classword;
+	struct cafe_camera *cam;
+	/*
+	 * Make sure we have a camera here - we'll get calls for
+	 * the other cafe devices as well.
+	 */
+	pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword);
+	if (classword != PCI_CLASS_MULTIMEDIA_VIDEO)
+		return -ENODEV;
+	/*
+	 * Start putting together one of our big camera structures.
+	 */
+	ret = -ENOMEM;
+	cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
+	if (cam == NULL)
+		goto out;
+	mutex_init(&cam->s_mutex);
+	mutex_lock(&cam->s_mutex);
+	spin_lock_init(&cam->dev_lock);
+	cam->state = S_NOTREADY;
+	cafe_set_config_needed(cam, 1);
+	init_waitqueue_head(&cam->smbus_wait);
+	init_waitqueue_head(&cam->iowait);
+	cam->pdev = pdev;
+	cam->pix_format = cafe_def_pix_format;
+	INIT_LIST_HEAD(&cam->dev_list);
+	INIT_LIST_HEAD(&cam->sb_avail);
+	INIT_LIST_HEAD(&cam->sb_full);
+	tasklet_init(&cam->s_tasklet, cafe_frame_tasklet, (unsigned long) cam);
+	/*
+	 * Get set up on the PCI bus.
+	 */
+	ret = pci_enable_device(pdev);
+	if (ret)
+		goto out_free;
+	pci_set_master(pdev);
+
+	ret = -EIO;
+	cam->regs = pci_iomap(pdev, 0, 0);
+	if (! cam->regs) {
+		printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
+		goto out_free;
+	}
+	ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
+	if (ret)
+		goto out_iounmap;
+	cafe_ctlr_init(cam);
+	cafe_ctlr_power_up(cam);
+	/*
+	 * Set up I2C/SMBUS communications
+	 */
+	mutex_unlock(&cam->s_mutex);  /* attach can deadlock */
+	ret = cafe_smbus_setup(cam);
+	if (ret)
+		goto out_freeirq;
+	/*
+	 * Get the v4l2 setup done.
+	 */
+	mutex_lock(&cam->s_mutex);
+	cam->v4ldev = cafe_v4l_template;
+	cam->v4ldev.debug = 0;
+//	cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
+	ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1);
+	if (ret)
+		goto out_smbus;
+	/*
+	 * If so requested, try to get our DMA buffers now.
+	 */
+	if (alloc_bufs_at_load) {
+		if (cafe_alloc_dma_bufs(cam, 1))
+			cam_warn(cam, "Unable to alloc DMA buffers at load"
+					" will try again later.");
+	}
+
+	cafe_dfs_cam_setup(cam);
+	mutex_unlock(&cam->s_mutex);
+	cafe_add_dev(cam);
+	return 0;
+
+  out_smbus:
+	cafe_smbus_shutdown(cam);
+  out_freeirq:
+	cafe_ctlr_power_down(cam);
+	free_irq(pdev->irq, cam);
+  out_iounmap:
+	pci_iounmap(pdev, cam->regs);
+  out_free:
+	kfree(cam);
+  out:
+	return ret;
+}
+
+
+/*
+ * Shut down an initialized device
+ */
+static void cafe_shutdown(struct cafe_camera *cam)
+{
+/* FIXME: Make sure we take care of everything here */
+	cafe_dfs_cam_shutdown(cam);
+	if (cam->n_sbufs > 0)
+		/* What if they are still mapped?  Shouldn't be, but... */
+		cafe_free_sio_buffers(cam);
+	cafe_remove_dev(cam);
+	cafe_ctlr_stop_dma(cam);
+	cafe_ctlr_power_down(cam);
+	cafe_smbus_shutdown(cam);
+	cafe_free_dma_bufs(cam);
+	free_irq(cam->pdev->irq, cam);
+	pci_iounmap(cam->pdev, cam->regs);
+	video_unregister_device(&cam->v4ldev);
+	/* kfree(cam); done in v4l_release () */
+}
+
+
+static void cafe_pci_remove(struct pci_dev *pdev)
+{
+	struct cafe_camera *cam = cafe_find_by_pdev(pdev);
+
+	if (cam == NULL) {
+		cam_warn(cam, "pci_remove on unknown pdev %p\n", pdev);
+		return;
+	}
+	mutex_lock(&cam->s_mutex);
+	if (cam->users > 0)
+		cam_warn(cam, "Removing a device with users!\n");
+	cafe_shutdown(cam);
+/* No unlock - it no longer exists */
+}
+
+
+
+
+static struct pci_device_id cafe_ids[] = {
+	{ PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */
+	{ PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */
+	{ PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, cafe_ids);
+
+static struct pci_driver cafe_pci_driver = {
+	.name = "cafe1000-ccic",
+	.id_table = cafe_ids,
+	.probe = cafe_pci_probe,
+	.remove = cafe_pci_remove,
+};
+
+
+
+
+static int __init cafe_init(void)
+{
+	int ret;
+
+	printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
+			CAFE_VERSION);
+	cafe_dfs_setup();
+	ret = pci_register_driver(&cafe_pci_driver);
+	if (ret) {
+		printk(KERN_ERR "Unable to register cafe_ccic driver\n");
+		goto out;
+	}
+	request_module("ov7670");  /* FIXME want something more general */
+	ret = 0;
+
+  out:
+	return ret;
+}
+
+
+static void __exit cafe_exit(void)
+{
+	pci_unregister_driver(&cafe_pci_driver);
+	cafe_dfs_shutdown();
+}
+
+module_init(cafe_init);
+module_exit(cafe_exit);
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 0f9d969..b2a66ba 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -53,6 +53,7 @@
 	select DVB_OR51132 if !DVB_FE_CUSTOMISE
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	select DVB_NXT200X if !DVB_FE_CUSTOMISE
 	select DVB_CX24123 if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 4673832..0cf0360 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -50,7 +50,6 @@
 #define dprintk(level,fmt, arg...)	if (debug >= level) \
 	printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
 
-static LIST_HEAD(cx8802_devlist);
 
 /* ------------------------------------------------------------------ */
 
@@ -882,7 +881,7 @@
 				  BLACKBIRD_MPEG_CAPTURE,
 				  BLACKBIRD_RAW_BITS_NONE);
 
-		cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
+		cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook);
 
 		blackbird_initialize_codec(dev);
 		cx88_set_scale(dev->core, dev->width, dev->height,
@@ -914,11 +913,15 @@
 	}
 
 	default:
-		return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
+		return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook);
 	}
 	return 0;
 }
 
+int (*cx88_ioctl_hook)(struct inode *inode, struct file *file,
+			unsigned int cmd, void *arg);
+unsigned int (*cx88_ioctl_translator)(unsigned int cmd);
+
 static unsigned int mpeg_translate_ioctl(unsigned int cmd)
 {
 	return cmd;
@@ -927,33 +930,49 @@
 static int mpeg_ioctl(struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
-	cmd = mpeg_translate_ioctl( cmd );
-	return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl);
+	cmd = cx88_ioctl_translator( cmd );
+	return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook);
 }
 
 static int mpeg_open(struct inode *inode, struct file *file)
 {
 	int minor = iminor(inode);
-	struct cx8802_dev *h,*dev = NULL;
+	struct cx8802_dev *dev = NULL;
 	struct cx8802_fh *fh;
-	struct list_head *list;
+	struct cx8802_driver *drv = NULL;
+	int err;
 
-	list_for_each(list,&cx8802_devlist) {
-		h = list_entry(list, struct cx8802_dev, devlist);
-		if (h->mpeg_dev->minor == minor)
-			dev = h;
-	}
-	if (NULL == dev)
+       dev = cx8802_get_device(inode);
+
+	dprintk( 1, "%s\n", __FUNCTION__);
+
+	if (dev == NULL)
 		return -ENODEV;
 
-	if (blackbird_initialize_codec(dev) < 0)
+	/* Make sure we can acquire the hardware */
+	drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+	if (drv) {
+		err = drv->request_acquire(drv);
+		if(err != 0) {
+			dprintk(1,"%s: Unable to acquire hardware, %d\n", __FUNCTION__, err);
+			return err;
+		}
+	}
+
+	if (blackbird_initialize_codec(dev) < 0) {
+		if (drv)
+			drv->request_release(drv);
 		return -EINVAL;
+	}
 	dprintk(1,"open minor=%d\n",minor);
 
 	/* allocate + initialize per filehandle data */
 	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
-	if (NULL == fh)
+	if (NULL == fh) {
+		if (drv)
+			drv->request_release(drv);
 		return -ENOMEM;
+	}
 	file->private_data = fh;
 	fh->dev      = dev;
 
@@ -974,6 +993,8 @@
 static int mpeg_release(struct inode *inode, struct file *file)
 {
 	struct cx8802_fh  *fh  = file->private_data;
+	struct cx8802_dev *dev = NULL;
+	struct cx8802_driver *drv = NULL;
 
 	/* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */
 	blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
@@ -992,6 +1013,16 @@
 	videobuf_mmap_free(&fh->mpegq);
 	file->private_data = NULL;
 	kfree(fh);
+
+	/* Make sure we release the hardware */
+	dev = cx8802_get_device(inode);
+	if (dev == NULL)
+		return -ENODEV;
+
+	drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+	if (drv)
+		drv->request_release(drv);
+
 	return 0;
 }
 
@@ -1043,6 +1074,44 @@
 
 /* ------------------------------------------------------------------ */
 
+/* The CX8802 MPEG API will call this when we can use the hardware */
+static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	int err = 0;
+
+	switch (core->board) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* By default, core setup will leave the cx22702 out of reset, on the bus.
+		 * We left the hardware on power up with the cx22702 active.
+		 * We're being given access to re-arrange the GPIOs.
+		 * Take the bus off the cx22702 and put the cx23416 on it.
+		 */
+		cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */
+		cx_set(MO_GP0_IO,   0x00000004); /* Disable the cx22702 */
+		break;
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
+/* The CX8802 MPEG API will call this when we need to release the hardware */
+static int cx8802_blackbird_advise_release(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	int err = 0;
+
+	switch (core->board) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* Exit leaving the cx23416 on the bus */
+		break;
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
 static void blackbird_unregister_video(struct cx8802_dev *dev)
 {
 	if (dev->mpeg_dev) {
@@ -1073,28 +1142,23 @@
 
 /* ----------------------------------------------------------- */
 
-static int __devinit blackbird_probe(struct pci_dev *pci_dev,
-				     const struct pci_device_id *pci_id)
+static int cx8802_blackbird_probe(struct cx8802_driver *drv)
 {
-	struct cx8802_dev *dev;
-	struct cx88_core  *core;
+	struct cx88_core *core = drv->core;
+	struct cx8802_dev *dev = core->dvbdev;
 	int err;
 
-	/* general setup */
-	core = cx88_core_get(pci_dev);
-	if (NULL == core)
-		return -EINVAL;
+	dprintk( 1, "%s\n", __FUNCTION__);
+	dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+		core->board,
+		core->name,
+		core->pci_bus,
+		core->pci_slot);
 
 	err = -ENODEV;
 	if (!(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD))
 		goto fail_core;
 
-	err = -ENOMEM;
-	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
-	if (NULL == dev)
-		goto fail_core;
-	dev->pci = pci_dev;
-	dev->core = core;
 	dev->width = 720;
 	dev->height = 576;
 	cx2341x_fill_defaults(&dev->params);
@@ -1106,64 +1170,36 @@
 		dev->height = 576;
 	}
 
-	err = cx8802_init_common(dev);
-	if (0 != err)
-		goto fail_free;
-
 	/* blackbird stuff */
 	printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
 	       core->name);
 	host_setup(dev->core);
 
-	list_add_tail(&dev->devlist,&cx8802_devlist);
 	blackbird_register_video(dev);
 
 	/* initial device configuration: needed ? */
 
 	return 0;
 
- fail_free:
-	kfree(dev);
  fail_core:
-	cx88_core_put(core,pci_dev);
 	return err;
 }
 
-static void __devexit blackbird_remove(struct pci_dev *pci_dev)
+static int cx8802_blackbird_remove(struct cx8802_driver *drv)
 {
-	struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
-
 	/* blackbird */
-	blackbird_unregister_video(dev);
-	list_del(&dev->devlist);
+	blackbird_unregister_video(drv->core->dvbdev);
 
-	/* common */
-	cx8802_fini_common(dev);
-	cx88_core_put(dev->core,dev->pci);
-	kfree(dev);
+	return 0;
 }
 
-static struct pci_device_id cx8802_pci_tbl[] = {
-	{
-		.vendor       = 0x14f1,
-		.device       = 0x8802,
-		.subvendor    = PCI_ANY_ID,
-		.subdevice    = PCI_ANY_ID,
-	},{
-		/* --- end of list --- */
-	}
-};
-MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
-
-static struct pci_driver blackbird_pci_driver = {
-	.name     = "cx88-blackbird",
-	.id_table = cx8802_pci_tbl,
-	.probe    = blackbird_probe,
-	.remove   = __devexit_p(blackbird_remove),
-#ifdef CONFIG_PM
-	.suspend  = cx8802_suspend_common,
-	.resume   = cx8802_resume_common,
-#endif
+static struct cx8802_driver cx8802_blackbird_driver = {
+	.type_id	= CX88_MPEG_BLACKBIRD,
+	.hw_access	= CX8802_DRVCTL_SHARED,
+	.probe		= cx8802_blackbird_probe,
+	.remove		= cx8802_blackbird_remove,
+	.advise_acquire	= cx8802_blackbird_advise_acquire,
+	.advise_release	= cx8802_blackbird_advise_release,
 };
 
 static int blackbird_init(void)
@@ -1176,17 +1212,22 @@
 	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
 	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
 #endif
-	return pci_register_driver(&blackbird_pci_driver);
+	cx88_ioctl_hook = mpeg_do_ioctl;
+	cx88_ioctl_translator = mpeg_translate_ioctl;
+	return cx8802_register_driver(&cx8802_blackbird_driver);
 }
 
 static void blackbird_fini(void)
 {
-	pci_unregister_driver(&blackbird_pci_driver);
+	cx8802_unregister_driver(&cx8802_blackbird_driver);
 }
 
 module_init(blackbird_init);
 module_exit(blackbird_fini);
 
+EXPORT_SYMBOL(cx88_ioctl_hook);
+EXPORT_SYMBOL(cx88_ioctl_translator);
+
 /* ----------------------------------------------------------- */
 /*
  * Local variables:
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index f764a57..c791708 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -281,18 +281,22 @@
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
 			.gpio0  = 0x0000bde2,
+			.extadc = 1,
 		},{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
 			.gpio0  = 0x0000bde6,
+			.extadc = 1,
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.gpio0  = 0x0000bde6,
+			.extadc = 1,
 		}},
 		.radio = {
 			.type   = CX88_RADIO,
 			.gpio0  = 0x0000bd62,
+			.extadc = 1,
 		},
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
@@ -353,6 +357,7 @@
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.gpio0  = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
+			.extadc = 1,
 		}},
 		.radio = {
 			.type   = CX88_RADIO,
@@ -523,6 +528,7 @@
 		.input          = {{
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
+			.extadc = 1,
 		}},
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
@@ -646,18 +652,22 @@
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
 			.gpio0  = 0x00009d80,
+			.extadc = 1,
 		},{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
 			.gpio0  = 0x00009d76,
+			.extadc = 1,
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.gpio0  = 0x00009d76,
+			.extadc = 1,
 		}},
 		.radio = {
 			.type   = CX88_RADIO,
 			.gpio0  = 0x00009d00,
+			.extadc = 1,
 		},
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
@@ -786,25 +796,29 @@
 		.tuner_addr     = ADDR_UNSET,
 		.radio_addr     = ADDR_UNSET,
 		.tda9887_conf   = TDA9887_PRESENT,
-		.mpeg           = CX88_MPEG_BLACKBIRD,
 		.input          = {{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 0,
 			.gpio0  = 0x0000cd73,
+			.extadc = 1,
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 1,
 			.gpio0  = 0x0000cd73,
+			.extadc = 1,
 		},{
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 3,
 			.gpio0  = 0x0000cdb3,
+			.extadc = 1,
 		}},
 		.radio = {
 			.type   = CX88_RADIO,
 			.vmux   = 2,
 			.gpio0  = 0x0000cdf3,
+			.extadc = 1,
 		},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
 	[CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
 		 /* Alexander Wold <awold@bigfoot.com> */
@@ -1050,7 +1064,6 @@
 		.mpeg           = CX88_MPEG_DVB,
 	},
 	[CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
-		/* FIXME: Audio not working for s-video / composite inputs. */
 		.name           = "KWorld HardwareMpegTV XPert",
 		.tuner_type     = TUNER_PHILIPS_TDA8290,
 		.radio_type     = UNSET,
@@ -1065,10 +1078,12 @@
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
 			.gpio0  = 0x3de6,
+			.extadc = 1,
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.gpio0  = 0x3de6,
+			.extadc = 1,
 		}},
 		.radio = {
 			.type   = CX88_RADIO,
@@ -1252,35 +1267,35 @@
 			.gpio0  = 0x070b,
 		}},
 	},
-       [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
-	       .name           = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
-	       .tuner_type     = TUNER_LG_PAL_NEW_TAPC,
-	       .radio_type     = UNSET,
-	       .tuner_addr     = ADDR_UNSET,
-	       .radio_addr     = ADDR_UNSET,
-	       .input          = {{
-		       .type   = CX88_VMUX_TELEVISION,
-		       .vmux   = 0,
-		       .gpio0  = 0x003fffff,
-		       .gpio1  = 0x00e00000,
-		       .gpio2  = 0x003fffff,
-		       .gpio3  = 0x02000000,
-	       },{
-		       .type   = CX88_VMUX_COMPOSITE1,
-		       .vmux   = 1,
-		       .gpio0  = 0x003fffff,
-		       .gpio1  = 0x00e00000,
-		       .gpio2  = 0x003fffff,
-		       .gpio3  = 0x02000000,
+	[CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
+		.name           = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
+		.tuner_type     = TUNER_LG_PAL_NEW_TAPC,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0x003fffff,
+			.gpio1  = 0x00e00000,
+			.gpio2  = 0x003fffff,
+			.gpio3  = 0x02000000,
 		},{
-		       .type   = CX88_VMUX_SVIDEO,
-		       .vmux   = 2,
-		       .gpio0  = 0x003fffff,
-		       .gpio1  = 0x00e00000,
-		       .gpio2  = 0x003fffff,
-		       .gpio3  = 0x02000000,
-	       }},
-       },
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0x003fffff,
+			.gpio1  = 0x00e00000,
+			.gpio2  = 0x003fffff,
+			.gpio3  = 0x02000000,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0x003fffff,
+			.gpio1  = 0x00e00000,
+			.gpio2  = 0x003fffff,
+			.gpio3  = 0x02000000,
+		}},
+	},
 	[CX88_BOARD_HAUPPAUGE_HVR1300] = {
 		.name		= "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
 		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
@@ -1293,17 +1308,20 @@
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
 			.gpio0	= 0xe780,
+			.extadc = 1,
 		},{
 			.type	= CX88_VMUX_COMPOSITE1,
 			.vmux	= 1,
 			.gpio0	= 0xe780,
+			.extadc = 1,
 		},{
 			.type	= CX88_VMUX_SVIDEO,
 			.vmux	= 2,
 			.gpio0	= 0xe780,
+			.extadc = 1,
 		}},
 		/* fixme: Add radio support */
-		.mpeg           = CX88_MPEG_DVB,
+		.mpeg           = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
 	},
 };
 const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
@@ -1513,6 +1531,10 @@
 	},{
 		.subvendor = 0x17de,
 		.subdevice = 0x0840,
+	       .card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+       },{
+	       .subvendor = 0x1421,
+	       .subdevice = 0x0305,
 		.card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
 	},{
 		.subvendor = 0x18ac,
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index 0ef13e7..8b20335 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -42,7 +42,7 @@
 #include "cx22702.h"
 #include "or51132.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "nxt200x.h"
 #include "cx24123.h"
 #include "isl6421.h"
@@ -57,7 +57,7 @@
 MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
 
 #define dprintk(level,fmt, arg...)	if (debug >= level) \
-	printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg)
+	printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg)
 
 /* ------------------------------------------------------------------ */
 
@@ -74,8 +74,8 @@
 	return 0;
 }
 
-static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-			   enum v4l2_field field)
+static int dvb_buf_prepare(struct videobuf_queue *q,
+			   struct videobuf_buffer *vb, enum v4l2_field field)
 {
 	struct cx8802_dev *dev = q->priv_data;
 	return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
@@ -87,7 +87,8 @@
 	cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
 }
 
-static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void dvb_buf_release(struct videobuf_queue *q,
+			    struct videobuf_buffer *vb)
 {
 	cx88_free_buffer(q, (struct cx88_buffer*)vb);
 }
@@ -100,6 +101,26 @@
 };
 
 /* ------------------------------------------------------------------ */
+
+static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx8802_driver *drv = NULL;
+	int ret = 0;
+
+	drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
+	if (drv) {
+		if (acquire)
+			ret = drv->request_acquire(drv);
+		else
+			ret = drv->request_release(drv);
+	}
+
+	return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
 static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
 {
 	static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x39 };
@@ -268,35 +289,6 @@
 };
 #endif
 
-static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe,
-					 struct dvb_frontend_parameters *params)
-{
-	u8 pllbuf[4];
-	struct cx8802_dev *dev= fe->dvb->priv;
-	struct i2c_msg msg =
-		{ .addr = dev->core->pll_addr, .flags = 0,
-		  .buf = pllbuf, .len = 4 };
-	int err;
-
-	dvb_pll_configure(dev->core->pll_desc, pllbuf,
-			  params->frequency,
-			  params->u.ofdm.bandwidth);
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) {
-		printk(KERN_WARNING "cx88-dvb: %s error "
-		       "(addr %02x <- %02x, err = %i)\n",
-		       __FUNCTION__, pllbuf[0], pllbuf[1], err);
-		if (err < 0)
-			return err;
-		else
-			return -EREMOTEIO;
-	}
-
-	return 0;
-}
-
 static struct zl10353_config dvico_fusionhdtv_hybrid = {
 	.demod_address = 0x0f,
 	.no_tuner      = 1,
@@ -311,28 +303,12 @@
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 };
 
-static struct cx22702_config hauppauge_novat_config = {
-	.demod_address = 0x43,
-	.output_mode   = CX22702_SERIAL_OUTPUT,
-};
-
-static struct cx22702_config hauppauge_hvr1100_config = {
+static struct cx22702_config hauppauge_hvr_config = {
 	.demod_address = 0x63,
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 };
 
-static struct cx22702_config hauppauge_hvr1300_config = {
-	.demod_address = 0x63,
-	.output_mode   = CX22702_SERIAL_OUTPUT,
-};
-
-static struct cx22702_config hauppauge_hvr3000_config = {
-	.demod_address = 0x63,
-	.output_mode = CX22702_SERIAL_OUTPUT,
-};
-
-static int or51132_set_ts_param(struct dvb_frontend* fe,
-				int is_punctured)
+static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured)
 {
 	struct cx8802_dev *dev= fe->dvb->priv;
 	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
@@ -344,50 +320,6 @@
 	.set_ts_params = or51132_set_ts_param,
 };
 
-static int lgdt3302_tuner_set_params(struct dvb_frontend* fe,
-				     struct dvb_frontend_parameters* params)
-{
-	/* FIXME make this routine use the tuner-simple code.
-	 * It could probably be shared with a number of ATSC
-	 * frontends. Many share the same tuner with analog TV. */
-
-	struct cx8802_dev *dev= fe->dvb->priv;
-	struct cx88_core *core = dev->core;
-	u8 buf[4];
-	struct i2c_msg msg =
-		{ .addr = dev->core->pll_addr, .flags = 0, .buf = buf, .len = 4 };
-	int err;
-
-	dvb_pll_configure(core->pll_desc, buf, params->frequency, 0);
-	dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n",
-		__FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]);
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) {
-		printk(KERN_WARNING "cx88-dvb: %s error "
-		       "(addr %02x <- %02x, err = %i)\n",
-		       __FUNCTION__, buf[0], buf[1], err);
-		if (err < 0)
-			return err;
-		else
-			return -EREMOTEIO;
-	}
-	return 0;
-}
-
-static int lgdt3303_tuner_set_params(struct dvb_frontend* fe,
-				     struct dvb_frontend_parameters* params)
-{
-	struct cx8802_dev *dev= fe->dvb->priv;
-	struct cx88_core *core = dev->core;
-
-	/* Put the analog decoder in standby to keep it quiet */
-	cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
-
-	return lg_h06xf_pll_set(fe, &core->i2c_adap, params);
-}
-
 static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index)
 {
 	struct cx8802_dev *dev= fe->dvb->priv;
@@ -432,8 +364,7 @@
 	.set_ts_params = lgdt330x_set_ts_param,
 };
 
-static int nxt200x_set_ts_param(struct dvb_frontend* fe,
-				int is_punctured)
+static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
 {
 	struct cx8802_dev *dev= fe->dvb->priv;
 	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
@@ -469,11 +400,10 @@
 	struct cx8802_dev *dev= fe->dvb->priv;
 	struct cx88_core *core = dev->core;
 
-	if (voltage == SEC_VOLTAGE_OFF) {
+	if (voltage == SEC_VOLTAGE_OFF)
 		cx_write(MO_GP0_IO, 0x000006fb);
-	} else {
+	else
 		cx_write(MO_GP0_IO, 0x000006f9);
-	}
 
 	if (core->prev_set_voltage)
 		return core->prev_set_voltage(fe, voltage);
@@ -522,7 +452,7 @@
 	switch (dev->core->board) {
 	case CX88_BOARD_HAUPPAUGE_DVB_T1:
 		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_novat_config,
+					       &connexant_refboard_config,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
@@ -547,32 +477,11 @@
 	case CX88_BOARD_HAUPPAUGE_HVR1100:
 	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
 		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_hvr1100_config,
+					       &hauppauge_hvr_config,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
-				   &dev->core->i2c_adap,
-				   &dvb_pll_fmd1216me);
-		}
-		break;
-	case CX88_BOARD_HAUPPAUGE_HVR1300:
-		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_hvr1300_config,
-					       &dev->core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
-				   &dev->core->i2c_adap,
-				   &dvb_pll_fmd1216me);
-		}
-		break;
-	case CX88_BOARD_HAUPPAUGE_HVR3000:
-		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_hvr3000_config,
-					       &dev->core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
-				   &dev->core->i2c_adap,
-				   &dvb_pll_fmd1216me);
+				   &dev->core->i2c_adap, &dvb_pll_fmd1216me);
 		}
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
@@ -647,18 +556,17 @@
 #endif
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
-		dev->core->pll_addr = 0x61;
-		dev->core->pll_desc = &dvb_pll_thomson_fe6600;
 		dev->dvb.frontend = dvb_attach(zl10353_attach,
 					       &dvico_fusionhdtv_hybrid,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = dvico_hybrid_tuner_set_params;
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_thomson_fe6600);
 		}
 		break;
 	case CX88_BOARD_PCHDTV_HD3000:
-		dev->dvb.frontend = dvb_attach(or51132_attach,
-					       &pchdtv_hd3000,
+		dev->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
@@ -679,13 +587,13 @@
 
 		/* Select RF connector callback */
 		fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
-		dev->core->pll_addr = 0x61;
-		dev->core->pll_desc = &dvb_pll_microtune_4042;
 		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &fusionhdtv_3_gold,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_microtune_4042);
 		}
 		}
 		break;
@@ -699,13 +607,13 @@
 		mdelay(100);
 		cx_set(MO_GP0_IO, 9);
 		mdelay(200);
-		dev->core->pll_addr = 0x61;
-		dev->core->pll_desc = &dvb_pll_thomson_dtt761x;
 		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &fusionhdtv_3_gold,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_thomson_dtt761x);
 		}
 		}
 		break;
@@ -723,7 +631,8 @@
 					       &fusionhdtv_5_gold,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
+			dvb_attach(lgh06xf_attach, dev->dvb.frontend,
+				   &dev->core->i2c_adap);
 		}
 		}
 		break;
@@ -741,7 +650,8 @@
 					       &pchdtv_hd5500,
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
+			dvb_attach(lgh06xf_attach, dev->dvb.frontend,
+				   &dev->core->i2c_adap);
 		}
 		}
 		break;
@@ -782,6 +692,24 @@
 			dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
 		}
 		break;
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		dev->dvb.frontend = dvb_attach(cx22702_attach,
+					       &hauppauge_hvr_config,
+					       &dev->core->i2c_adap);
+		if (dev->dvb.frontend != NULL) {
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap, &dvb_pll_fmd1216me);
+		}
+		break;
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+		dev->dvb.frontend = dvb_attach(cx22702_attach,
+					       &hauppauge_hvr_config,
+					       &dev->core->i2c_adap);
+		if (dev->dvb.frontend != NULL) {
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap, &dvb_pll_fmd1216me);
+		}
+		break;
 	default:
 		printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
 		       dev->core->name);
@@ -796,6 +724,8 @@
 		dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min;
 		dev->dvb.frontend->ops.info.frequency_max = dev->core->pll_desc->max;
 	}
+	/* Ensure all frontends negotiate bus access */
+	dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
 
 	/* Put the analog decoder in standby to keep it quiet */
 	cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
@@ -806,37 +736,67 @@
 
 /* ----------------------------------------------------------- */
 
-static int __devinit dvb_probe(struct pci_dev *pci_dev,
-			       const struct pci_device_id *pci_id)
+/* CX8802 MPEG -> mini driver - We have been given the hardware */
+static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
 {
-	struct cx8802_dev *dev;
-	struct cx88_core  *core;
+	struct cx88_core *core = drv->core;
+	int err = 0;
+	dprintk( 1, "%s\n", __FUNCTION__);
+
+	switch (core->board) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* We arrive here with either the cx23416 or the cx22702
+		 * on the bus. Take the bus from the cx23416 and enable the
+		 * cx22702 demod
+		 */
+		cx_set(MO_GP0_IO,   0x00000080); /* cx22702 out of reset and enable */
+		cx_clear(MO_GP0_IO, 0x00000004);
+		udelay(1000);
+		break;
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
+/* CX8802 MPEG -> mini driver - We no longer have the hardware */
+static int cx8802_dvb_advise_release(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	int err = 0;
+	dprintk( 1, "%s\n", __FUNCTION__);
+
+	switch (core->board) {
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		/* Do Nothing, leave the cx22702 on the bus. */
+		break;
+	default:
+		err = -ENODEV;
+	}
+	return err;
+}
+
+static int cx8802_dvb_probe(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+	struct cx8802_dev *dev = drv->core->dvbdev;
 	int err;
 
-	/* general setup */
-	core = cx88_core_get(pci_dev);
-	if (NULL == core)
-		return -EINVAL;
+	dprintk( 1, "%s\n", __FUNCTION__);
+	dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+		core->board,
+		core->name,
+		core->pci_bus,
+		core->pci_slot);
 
 	err = -ENODEV;
 	if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB))
 		goto fail_core;
 
-	err = -ENOMEM;
-	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
-	if (NULL == dev)
-		goto fail_core;
-	dev->pci = pci_dev;
-	dev->core = core;
-
-	err = cx8802_init_common(dev);
-	if (0 != err)
-		goto fail_free;
-
 #ifdef HAVE_VP3054_I2C
 	err = vp3054_i2c_probe(dev);
 	if (0 != err)
-		goto fail_free;
+		goto fail_core;
 #endif
 
 	/* dvb stuff */
@@ -848,28 +808,16 @@
 			    sizeof(struct cx88_buffer),
 			    dev);
 	err = dvb_register(dev);
-	if (0 != err)
-		goto fail_fini;
+	if (err != 0)
+		printk("%s dvb_register failed err = %d\n", __FUNCTION__, err);
 
-	/* Maintain a reference to cx88-video can query the 8802 device. */
-	core->dvbdev = dev;
-	return 0;
-
- fail_fini:
-	cx8802_fini_common(dev);
- fail_free:
-	kfree(dev);
  fail_core:
-	cx88_core_put(core,pci_dev);
 	return err;
 }
 
-static void __devexit dvb_remove(struct pci_dev *pci_dev)
+static int cx8802_dvb_remove(struct cx8802_driver *drv)
 {
-	struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
-
-	/* Destroy any 8802 reference. */
-	dev->core->dvbdev = NULL;
+	struct cx8802_dev *dev = drv->core->dvbdev;
 
 	/* dvb */
 	videobuf_dvb_unregister(&dev->dvb);
@@ -878,33 +826,16 @@
 	vp3054_i2c_remove(dev);
 #endif
 
-	/* common */
-	cx8802_fini_common(dev);
-	cx88_core_put(dev->core,dev->pci);
-	kfree(dev);
+	return 0;
 }
 
-static struct pci_device_id cx8802_pci_tbl[] = {
-	{
-		.vendor       = 0x14f1,
-		.device       = 0x8802,
-		.subvendor    = PCI_ANY_ID,
-		.subdevice    = PCI_ANY_ID,
-	},{
-		/* --- end of list --- */
-	}
-};
-MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
-
-static struct pci_driver dvb_pci_driver = {
-	.name     = "cx88-dvb",
-	.id_table = cx8802_pci_tbl,
-	.probe    = dvb_probe,
-	.remove   = __devexit_p(dvb_remove),
-#ifdef CONFIG_PM
-	.suspend  = cx8802_suspend_common,
-	.resume   = cx8802_resume_common,
-#endif
+static struct cx8802_driver cx8802_dvb_driver = {
+	.type_id        = CX88_MPEG_DVB,
+	.hw_access      = CX8802_DRVCTL_SHARED,
+	.probe          = cx8802_dvb_probe,
+	.remove         = cx8802_dvb_remove,
+	.advise_acquire = cx8802_dvb_advise_acquire,
+	.advise_release = cx8802_dvb_advise_release,
 };
 
 static int dvb_init(void)
@@ -917,12 +848,12 @@
 	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
 	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
 #endif
-	return pci_register_driver(&dvb_pci_driver);
+	return cx8802_register_driver(&cx8802_dvb_driver);
 }
 
 static void dvb_fini(void)
 {
-	pci_unregister_driver(&dvb_pci_driver);
+	cx8802_unregister_driver(&cx8802_dvb_driver);
 }
 
 module_init(dvb_init);
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index e60a0a5..8136673 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -155,6 +155,35 @@
 	mod_timer(&ir->timer, timeout);
 }
 
+static void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir)
+{
+	if (ir->polling) {
+		INIT_WORK(&ir->work, cx88_ir_work);
+		init_timer(&ir->timer);
+		ir->timer.function = ir_timer;
+		ir->timer.data = (unsigned long)ir;
+		schedule_work(&ir->work);
+	}
+	if (ir->sampling) {
+		core->pci_irqmask |= (1 << 18);	/* IR_SMP_INT */
+		cx_write(MO_DDS_IO, 0xa80a80);	/* 4 kHz sample rate */
+		cx_write(MO_DDSCFG_IO, 0x5);	/* enable */
+	}
+}
+
+static void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir)
+{
+	if (ir->sampling) {
+		cx_write(MO_DDSCFG_IO, 0x0);
+		core->pci_irqmask &= ~(1 << 18);
+	}
+
+	if (ir->polling) {
+		del_timer_sync(&ir->timer);
+		flush_scheduled_work();
+	}
+}
+
 /* ---------------------------------------------------------------------- */
 
 int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
@@ -163,14 +192,12 @@
 	struct input_dev *input_dev;
 	IR_KEYTAB_TYPE *ir_codes = NULL;
 	int ir_type = IR_TYPE_OTHER;
+	int err = -ENOMEM;
 
 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
 	input_dev = input_allocate_device();
-	if (!ir || !input_dev) {
-		kfree(ir);
-		input_free_device(input_dev);
-		return -ENOMEM;
-	}
+	if (!ir || !input_dev)
+		goto err_out_free;
 
 	ir->input = input_dev;
 
@@ -280,9 +307,8 @@
 	}
 
 	if (NULL == ir_codes) {
-		kfree(ir);
-		input_free_device(input_dev);
-		return -ENODEV;
+		err = -ENODEV;
+		goto err_out_free;
 	}
 
 	/* init input device */
@@ -307,23 +333,22 @@
 	ir->core = core;
 	core->ir = ir;
 
-	if (ir->polling) {
-		INIT_WORK(&ir->work, cx88_ir_work);
-		init_timer(&ir->timer);
-		ir->timer.function = ir_timer;
-		ir->timer.data = (unsigned long)ir;
-		schedule_work(&ir->work);
-	}
-	if (ir->sampling) {
-		core->pci_irqmask |= (1 << 18);	/* IR_SMP_INT */
-		cx_write(MO_DDS_IO, 0xa80a80);	/* 4 kHz sample rate */
-		cx_write(MO_DDSCFG_IO, 0x5);	/* enable */
-	}
+	cx88_ir_start(core, ir);
 
 	/* all done */
-	input_register_device(ir->input);
+	err = input_register_device(ir->input);
+	if (err)
+		goto err_out_stop;
 
 	return 0;
+
+ err_out_stop:
+	cx88_ir_stop(core, ir);
+	core->ir = NULL;
+ err_out_free:
+	input_free_device(input_dev);
+	kfree(ir);
+	return err;
 }
 
 int cx88_ir_fini(struct cx88_core *core)
@@ -334,15 +359,7 @@
 	if (NULL == ir)
 		return 0;
 
-	if (ir->sampling) {
-		cx_write(MO_DDSCFG_IO, 0x0);
-		core->pci_irqmask &= ~(1 << 18);
-	}
-	if (ir->polling) {
-		del_timer(&ir->timer);
-		flush_scheduled_work();
-	}
-
+	cx88_ir_stop(core, ir);
 	input_unregister_device(ir->input);
 	kfree(ir);
 
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index 6b23a4e..1fe1a83 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -44,8 +44,12 @@
 MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
 
 #define dprintk(level,fmt, arg...)	if (debug >= level) \
-	printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg)
+	printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg)
 
+#define mpeg_dbg(level,fmt, arg...)	if (debug >= level) \
+	printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg)
+
+static LIST_HEAD(cx8802_devlist);
 /* ------------------------------------------------------------------ */
 
 static int cx8802_start_dma(struct cx8802_dev    *dev,
@@ -65,17 +69,13 @@
 
 	/* FIXME: this needs a review.
 	 * also: move to cx88-blackbird + cx88-dvb source files? */
-	if (cx88_boards[core->board].mpeg == (CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) ) {
-		/* Report a warning until the mini driver patch is applied,
-		 * else the following conditions will set the dma registers incorrectly.
-		 * This will be removed in the next major patch and changes to the conditions
-		 * will be made.
-		 */
-		printk(KERN_INFO "%s() board->(CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) is invalid\n", __FUNCTION__);
-		return -EINVAL;
-	}
 
-	if (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) {
+	dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id);
+
+	if ( (core->active_type_id == CX88_MPEG_DVB) &&
+		(cx88_boards[core->board].mpeg & CX88_MPEG_DVB) ) {
+
+		dprintk( 1, "cx8802_start_dma doing .dvb\n");
 		/* negedge driven & software reset */
 		cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
 		udelay(100);
@@ -93,15 +93,17 @@
 			cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
 			udelay(100);
 			break;
+		case CX88_BOARD_HAUPPAUGE_HVR1300:
+			break;
 		default:
 			cx_write(TS_SOP_STAT, 0x00);
 			break;
 		}
 		cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
 		udelay(100);
-	}
-
-	if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) {
+	} else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) &&
+		(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) ) {
+		dprintk( 1, "cx8802_start_dma doing .blackbird\n");
 		cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
 
 		cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
@@ -112,6 +114,10 @@
 
 		cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
 		udelay(100);
+	} else {
+		printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __FUNCTION__,
+			cx88_boards[core->board].mpeg );
+		return -EINVAL;
 	}
 
 	/* reset counter */
@@ -542,8 +548,315 @@
 	return 0;
 }
 
-/* ----------------------------------------------------------- */
+struct cx8802_dev * cx8802_get_device(struct inode *inode)
+{
+	int minor = iminor(inode);
+	struct cx8802_dev *h = NULL;
+	struct list_head *list;
 
+	list_for_each(list,&cx8802_devlist) {
+		h = list_entry(list, struct cx8802_dev, devlist);
+		if (h->mpeg_dev->minor == minor)
+			return h;
+	}
+
+	return NULL;
+}
+
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype)
+{
+	struct cx8802_dev *h = NULL;
+	struct cx8802_driver *d = NULL;
+	struct list_head *list;
+	struct list_head *list2;
+
+	list_for_each(list,&cx8802_devlist) {
+		h = list_entry(list, struct cx8802_dev, devlist);
+		if (h != dev)
+			continue;
+
+		list_for_each(list2, &h->drvlist.devlist) {
+			d = list_entry(list2, struct cx8802_driver, devlist);
+
+			/* only unregister the correct driver type */
+			if (d->type_id == btype) {
+				return d;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/* Driver asked for hardware access. */
+int cx8802_request_acquire(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+
+	/* Fail a request for hardware if the device is busy. */
+	if (core->active_type_id != CX88_BOARD_NONE)
+		return -EBUSY;
+
+	if (drv->advise_acquire)
+	{
+		core->active_type_id = drv->type_id;
+		drv->advise_acquire(drv);
+
+		mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO));
+	}
+
+	return 0;
+}
+
+/* Driver asked to release hardware. */
+int cx8802_request_release(struct cx8802_driver *drv)
+{
+	struct cx88_core *core = drv->core;
+
+	if (drv->advise_release)
+	{
+		drv->advise_release(drv);
+		core->active_type_id = CX88_BOARD_NONE;
+		mpeg_dbg(1,"%s() Post release GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO));
+	}
+
+	return 0;
+}
+
+static int cx8802_check_driver(struct cx8802_driver *drv)
+{
+	if (drv == NULL)
+		return -ENODEV;
+
+	if ((drv->type_id != CX88_MPEG_DVB) &&
+		(drv->type_id != CX88_MPEG_BLACKBIRD))
+		return -EINVAL;
+
+	if ((drv->hw_access != CX8802_DRVCTL_SHARED) &&
+		(drv->hw_access != CX8802_DRVCTL_EXCLUSIVE))
+		return -EINVAL;
+
+	if ((drv->probe == NULL) ||
+		(drv->remove == NULL) ||
+		(drv->advise_acquire == NULL) ||
+		(drv->advise_release == NULL))
+		return -EINVAL;
+
+	return 0;
+}
+
+int cx8802_register_driver(struct cx8802_driver *drv)
+{
+	struct cx8802_dev *h;
+	struct cx8802_driver *driver;
+	struct list_head *list;
+	int err = 0, i = 0;
+
+	printk(KERN_INFO "%s() ->registering driver type=%s access=%s\n", __FUNCTION__ ,
+		drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird",
+		drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive");
+
+	if ((err = cx8802_check_driver(drv)) != 0) {
+		printk(KERN_INFO "%s() cx8802_driver is invalid\n", __FUNCTION__ );
+		return err;
+	}
+
+	list_for_each(list,&cx8802_devlist) {
+		h = list_entry(list, struct cx8802_dev, devlist);
+
+		printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n",
+			h->core->name,h->pci->subsystem_vendor,
+			h->pci->subsystem_device,cx88_boards[h->core->board].name,
+			h->core->board);
+
+		/* Bring up a new struct for each driver instance */
+		driver = kzalloc(sizeof(*drv),GFP_KERNEL);
+		if (driver == NULL)
+			return -ENOMEM;
+
+		/* Snapshot of the driver registration data */
+		drv->core = h->core;
+		drv->suspend = cx8802_suspend_common;
+		drv->resume = cx8802_resume_common;
+		drv->request_acquire = cx8802_request_acquire;
+		drv->request_release = cx8802_request_release;
+		memcpy(driver, drv, sizeof(*driver));
+
+		err = drv->probe(driver);
+		if (err == 0) {
+			i++;
+			mutex_lock(&drv->core->lock);
+			list_add_tail(&driver->devlist,&h->drvlist.devlist);
+			mutex_unlock(&drv->core->lock);
+		} else {
+			printk(KERN_ERR "%s() ->probe failed err = %d\n", __FUNCTION__, err);
+		}
+
+	}
+	if (i == 0)
+		err = -ENODEV;
+	else
+		err = 0;
+
+	return err;
+}
+
+int cx8802_unregister_driver(struct cx8802_driver *drv)
+{
+	struct cx8802_dev *h;
+	struct cx8802_driver *d;
+	struct list_head *list;
+	struct list_head *list2, *q;
+	int err = 0, i = 0;
+
+	printk(KERN_INFO "%s() ->unregistering driver type=%s\n", __FUNCTION__ ,
+		drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird");
+
+	list_for_each(list,&cx8802_devlist) {
+		i++;
+		h = list_entry(list, struct cx8802_dev, devlist);
+
+		printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n",
+			h->core->name,h->pci->subsystem_vendor,
+			h->pci->subsystem_device,cx88_boards[h->core->board].name,
+			h->core->board);
+
+		list_for_each_safe(list2, q, &h->drvlist.devlist) {
+			d = list_entry(list2, struct cx8802_driver, devlist);
+
+			/* only unregister the correct driver type */
+			if (d->type_id != drv->type_id)
+				continue;
+
+			err = d->remove(d);
+			if (err == 0) {
+				mutex_lock(&drv->core->lock);
+				list_del(list2);
+				mutex_unlock(&drv->core->lock);
+			} else
+				printk(KERN_ERR "%s() ->remove failed err = %d\n", __FUNCTION__, err);
+
+		}
+
+	}
+
+	return err;
+}
+
+/* ----------------------------------------------------------- */
+static int __devinit cx8802_probe(struct pci_dev *pci_dev,
+			       const struct pci_device_id *pci_id)
+{
+	struct cx8802_dev *dev;
+	struct cx88_core  *core;
+	int err;
+
+	/* general setup */
+	core = cx88_core_get(pci_dev);
+	if (NULL == core)
+		return -EINVAL;
+
+	printk("%s/2: cx2388x 8802 Driver Manager\n", core->name);
+
+	err = -ENODEV;
+	if (!cx88_boards[core->board].mpeg)
+		goto fail_core;
+
+	err = -ENOMEM;
+	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+	if (NULL == dev)
+		goto fail_core;
+	dev->pci = pci_dev;
+	dev->core = core;
+
+	err = cx8802_init_common(dev);
+	if (err != 0)
+		goto fail_free;
+
+	INIT_LIST_HEAD(&dev->drvlist.devlist);
+	list_add_tail(&dev->devlist,&cx8802_devlist);
+
+	/* Maintain a reference so cx88-video can query the 8802 device. */
+	core->dvbdev = dev;
+	return 0;
+
+ fail_free:
+	kfree(dev);
+ fail_core:
+	cx88_core_put(core,pci_dev);
+	return err;
+}
+
+static void __devexit cx8802_remove(struct pci_dev *pci_dev)
+{
+	struct cx8802_dev *dev;
+	struct cx8802_driver *h;
+	struct list_head *list;
+
+	dev = pci_get_drvdata(pci_dev);
+
+	dprintk( 1, "%s\n", __FUNCTION__);
+
+	list_for_each(list,&dev->drvlist.devlist) {
+		h = list_entry(list, struct cx8802_driver, devlist);
+		dprintk( 1, " ->driver\n");
+		if (h->remove == NULL) {
+			printk(KERN_ERR "%s .. skipping driver, no probe function\n", __FUNCTION__);
+			continue;
+		}
+		printk(KERN_INFO "%s .. Removing driver type %d\n", __FUNCTION__, h->type_id);
+		cx8802_unregister_driver(h);
+		list_del(&dev->drvlist.devlist);
+	}
+
+	/* Destroy any 8802 reference. */
+	dev->core->dvbdev = NULL;
+
+	/* common */
+	cx8802_fini_common(dev);
+	cx88_core_put(dev->core,dev->pci);
+	kfree(dev);
+}
+
+static struct pci_device_id cx8802_pci_tbl[] = {
+	{
+		.vendor       = 0x14f1,
+		.device       = 0x8802,
+		.subvendor    = PCI_ANY_ID,
+		.subdevice    = PCI_ANY_ID,
+	},{
+		/* --- end of list --- */
+	}
+};
+MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl);
+
+static struct pci_driver cx8802_pci_driver = {
+	.name     = "cx88-mpeg driver manager",
+	.id_table = cx8802_pci_tbl,
+	.probe    = cx8802_probe,
+	.remove   = __devexit_p(cx8802_remove),
+};
+
+static int cx8802_init(void)
+{
+	printk(KERN_INFO "cx2388x cx88-mpeg Driver Manager version %d.%d.%d loaded\n",
+	       (CX88_VERSION_CODE >> 16) & 0xff,
+	       (CX88_VERSION_CODE >>  8) & 0xff,
+	       CX88_VERSION_CODE & 0xff);
+#ifdef SNAPSHOT
+	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
+	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
+#endif
+	return pci_register_driver(&cx8802_pci_driver);
+}
+
+static void cx8802_fini(void)
+{
+	pci_unregister_driver(&cx8802_pci_driver);
+}
+
+module_init(cx8802_init);
+module_exit(cx8802_fini);
 EXPORT_SYMBOL(cx8802_buf_prepare);
 EXPORT_SYMBOL(cx8802_buf_queue);
 EXPORT_SYMBOL(cx8802_cancel_buffers);
@@ -551,9 +864,10 @@
 EXPORT_SYMBOL(cx8802_init_common);
 EXPORT_SYMBOL(cx8802_fini_common);
 
-EXPORT_SYMBOL(cx8802_suspend_common);
-EXPORT_SYMBOL(cx8802_resume_common);
-
+EXPORT_SYMBOL(cx8802_register_driver);
+EXPORT_SYMBOL(cx8802_unregister_driver);
+EXPORT_SYMBOL(cx8802_get_device);
+EXPORT_SYMBOL(cx8802_get_driver);
 /* ----------------------------------------------------------- */
 /*
  * Local variables:
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index 58ba9f7..3482e01 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -143,19 +143,6 @@
 	cx88_start_audio_dma(core);
 
 	if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) {
-		/* sets sound input from external adc */
-		switch (core->board) {
-		case CX88_BOARD_HAUPPAUGE_ROSLYN:
-		case CX88_BOARD_KWORLD_MCE200_DELUXE:
-		case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT:
-		case CX88_BOARD_PIXELVIEW_PLAYTV_P7000:
-		case CX88_BOARD_ASUS_PVR_416:
-			cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
-			break;
-		default:
-			cx_set(AUD_CTL, EN_I2SIN_ENABLE);
-		}
-
 		cx_write(AUD_I2SINPUTCNTL, 4);
 		cx_write(AUD_BAUDRATE, 1);
 		/* 'pass-thru mode': this enables the i2s output to the mpeg encoder */
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 90e298d..8613378 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -454,6 +454,14 @@
 		cx_clear(MO_FILTER_ODD,   0x00002020);
 		break;
 	}
+
+	if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) {
+		/* sets sound input from external adc */
+		if (INPUT(input)->extadc)
+			cx_set(AUD_CTL, EN_I2SIN_ENABLE);
+		else
+			cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
+	}
 	return 0;
 }
 
@@ -1490,6 +1498,30 @@
 		mutex_unlock(&core->lock);
 		return 0;
 	}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	/* ioctls to allow direct acces to the cx2388x registers */
+	case VIDIOC_INT_G_REGISTER:
+	{
+		struct v4l2_register *reg = arg;
+
+		if (reg->i2c_id != 0)
+			return -EINVAL;
+		/* cx2388x has a 24-bit register space */
+		reg->val = cx_read(reg->reg&0xffffff);
+		return 0;
+	}
+	case VIDIOC_INT_S_REGISTER:
+	{
+		struct v4l2_register *reg = arg;
+
+		if (reg->i2c_id != 0)
+			return -EINVAL;
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		cx_write(reg->reg&0xffffff, reg->val);
+		return 0;
+	}
+#endif
 
 	default:
 		return v4l_compat_translate_ioctl(inode,file,cmd,arg,
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index 3bc91aa..7054e94 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -74,6 +74,11 @@
 	CX88_MPEG_BLACKBIRD
 };
 
+enum cx8802_board_access {
+	CX8802_DRVCTL_SHARED    = 1,
+	CX8802_DRVCTL_EXCLUSIVE = 2,
+};
+
 /* ----------------------------------------------------------- */
 /* tv norms                                                    */
 
@@ -220,6 +225,7 @@
 	enum cx88_itype type;
 	unsigned int    vmux;
 	u32             gpio0, gpio1, gpio2, gpio3;
+	unsigned int    extadc:1;
 };
 
 struct cx88_board {
@@ -330,6 +336,7 @@
 
 	/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
 	struct cx8802_dev          *dvbdev;
+	enum cx88_board_type       active_type_id;
 };
 
 struct cx8800_dev;
@@ -405,6 +412,31 @@
 	int                        disabled;
 };
 
+struct cx8802_driver {
+	struct cx88_core *core;
+	struct list_head devlist;
+
+	/* Type of driver and access required */
+	enum cx88_board_type type_id;
+	enum cx8802_board_access hw_access;
+
+	/* MPEG 8802 internal only */
+	int (*suspend)(struct pci_dev *pci_dev, pm_message_t state);
+	int (*resume)(struct pci_dev *pci_dev);
+
+	/* MPEG 8802 -> mini driver - Driver probe and configuration */
+	int (*probe)(struct cx8802_driver *drv);
+	int (*remove)(struct cx8802_driver *drv);
+
+	/* MPEG 8802 -> mini driver - Access for hardware control */
+	int (*advise_acquire)(struct cx8802_driver *drv);
+	int (*advise_release)(struct cx8802_driver *drv);
+
+	/* MPEG 8802 <- mini driver - Access for hardware control */
+	int (*request_acquire)(struct cx8802_driver *drv);
+	int (*request_release)(struct cx8802_driver *drv);
+};
+
 struct cx8802_dev {
 	struct cx88_core           *core;
 	spinlock_t                 slock;
@@ -439,6 +471,9 @@
 
 	/* mpeg params */
 	struct cx2341x_mpeg_params params;
+
+	/* List of attached drivers */
+	struct cx8802_driver       drvlist;
 };
 
 /* ----------------------------------------------------------- */
@@ -571,6 +606,11 @@
 void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
 int cx88_audio_thread(void *data);
 
+int cx8802_register_driver(struct cx8802_driver *drv);
+int cx8802_unregister_driver(struct cx8802_driver *drv);
+struct cx8802_dev * cx8802_get_device(struct inode *inode);
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
+
 /* ----------------------------------------------------------- */
 /* cx88-input.c                                                */
 
@@ -600,6 +640,13 @@
 extern const u32 cx88_user_ctrls[];
 extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl);
 
+/* ----------------------------------------------------------- */
+/* cx88-blackbird.c                                            */
+/* used by cx88-ivtv ioctl emulation layer                     */
+extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file,
+			      unsigned int cmd, void *arg);
+extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd);
+
 /*
  * Local variables:
  * c-basic-offset: 8
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index ab87e7b..59edf58 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -305,15 +305,14 @@
 	int ir_type;
 	struct IR_i2c *ir;
 	struct input_dev *input_dev;
+	int err;
 
 	ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!ir || !input_dev) {
-		input_free_device(input_dev);
-		kfree(ir);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto err_out_free;
 	}
-	memset(ir,0,sizeof(*ir));
 
 	ir->c = client_template;
 	ir->input = input_dev;
@@ -355,32 +354,34 @@
 		break;
 	case 0x7a:
 	case 0x47:
+	case 0x71:
 		/* Handled by saa7134-input */
 		name        = "SAA713x remote";
 		ir_type     = IR_TYPE_OTHER;
 		break;
 	default:
 		/* shouldn't happen */
-		printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n",addr);
-		kfree(ir);
-		return -1;
+		printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n", addr);
+		err = -ENODEV;
+		goto err_out_free;
 	}
 
 	/* Sets name */
 	snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name);
-	ir->ir_codes=ir_codes;
+	ir->ir_codes = ir_codes;
 
 	/* register i2c device
 	 * At device register, IR codes may be changed to be
 	 * board dependent.
 	 */
-	i2c_attach_client(&ir->c);
+	err = i2c_attach_client(&ir->c);
+	if (err)
+		goto err_out_free;
 
 	/* If IR not supported or disabled, unregisters driver */
 	if (ir->get_key == NULL) {
-		i2c_detach_client(&ir->c);
-		kfree(ir);
-		return -1;
+		err = -ENODEV;
+		goto err_out_detach;
 	}
 
 	/* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */
@@ -389,15 +390,17 @@
 		 ir->c.dev.bus_id);
 
 	/* init + register input device */
-	ir_input_init(input_dev,&ir->ir,ir_type,ir->ir_codes);
+	ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
 	input_dev->id.bustype = BUS_I2C;
 	input_dev->name       = ir->c.name;
 	input_dev->phys       = ir->phys;
 
-	/* register event device */
-	input_register_device(ir->input);
+	err = input_register_device(ir->input);
+	if (err)
+		goto err_out_detach;
+
 	printk(DEVNAME ": %s detected at %s [%s]\n",
-	       ir->input->name,ir->input->phys,adap->name);
+	       ir->input->name, ir->input->phys, adap->name);
 
 	/* start polling via eventd */
 	INIT_WORK(&ir->work, ir_work);
@@ -407,6 +410,13 @@
 	schedule_work(&ir->work);
 
 	return 0;
+
+ err_out_detach:
+	i2c_detach_client(&ir->c);
+ err_out_free:
+	input_free_device(input_dev);
+	kfree(ir);
+	return err;
 }
 
 static int ir_detach(struct i2c_client *client)
@@ -414,7 +424,7 @@
 	struct IR_i2c *ir = i2c_get_clientdata(client);
 
 	/* kill outstanding polls */
-	del_timer(&ir->timer);
+	del_timer_sync(&ir->timer);
 	flush_scheduled_work();
 
 	/* unregister devices */
@@ -439,7 +449,7 @@
 	*/
 
 	static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1};
-	static const int probe_saa7134[] = { 0x7a, 0x47, -1 };
+	static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, -1 };
 	static const int probe_em28XX[] = { 0x30, 0x47, -1 };
 	const int *probe = NULL;
 	struct i2c_client c;
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index b0aea40..152cc6b 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -160,10 +160,6 @@
 		printk("mxb: saa7111 i2c module not available.\n");
 		return -ENODEV;
 	}
-	if ((result = request_module("tuner")) < 0) {
-		printk("mxb: tuner i2c module not available.\n");
-		return -ENODEV;
-	}
 	if ((result = request_module("tea6420")) < 0) {
 		printk("mxb: tea6420 i2c module not available.\n");
 		return -ENODEV;
@@ -176,6 +172,10 @@
 		printk("mxb: tda9840 i2c module not available.\n");
 		return -ENODEV;
 	}
+	if ((result = request_module("tuner")) < 0) {
+		printk("mxb: tuner i2c module not available.\n");
+		return -ENODEV;
+	}
 
 	mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
 	if( NULL == mxb ) {
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
new file mode 100644
index 0000000..89dd18c3
--- /dev/null
+++ b/drivers/media/video/ov7670.c
@@ -0,0 +1,1333 @@
+/*
+ * A V4L2 driver for OmniVision OV7670 cameras.
+ *
+ * Copyright 2006 One Laptop Per Child Association, Inc.  Written
+ * by Jonathan Corbet with substantial inspiration from Mark
+ * McClelland's ovcamchip code.
+ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev.h>
+#include <media/v4l2-common.h>
+#include <linux/i2c.h>
+
+
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net.");
+MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
+MODULE_LICENSE("GPL");
+
+/*
+ * Basic window sizes.  These probably belong somewhere more globally
+ * useful.
+ */
+#define VGA_WIDTH	640
+#define VGA_HEIGHT	480
+#define QVGA_WIDTH	320
+#define QVGA_HEIGHT	240
+#define CIF_WIDTH	352
+#define CIF_HEIGHT	288
+#define QCIF_WIDTH	176
+#define	QCIF_HEIGHT	144
+
+/*
+ * Our nominal (default) frame rate.
+ */
+#define OV7670_FRAME_RATE 30
+
+/*
+ * The 7670 sits on i2c with ID 0x42
+ */
+#define OV7670_I2C_ADDR 0x42
+
+/* Registers */
+#define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE	0x01	/* blue gain */
+#define REG_RED		0x02	/* red gain */
+#define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1	0x04	/* Control 1 */
+#define  COM1_CCIR656	  0x40  /* CCIR656 enable */
+#define REG_BAVE	0x05	/* U/B Average level */
+#define REG_GbAVE	0x06	/* Y/Gb Average level */
+#define REG_AECHH	0x07	/* AEC MS 5 bits */
+#define REG_RAVE	0x08	/* V/R Average level */
+#define REG_COM2	0x09	/* Control 2 */
+#define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
+#define REG_PID		0x0a	/* Product ID MSB */
+#define REG_VER		0x0b	/* Product ID LSB */
+#define REG_COM3	0x0c	/* Control 3 */
+#define  COM3_SWAP	  0x40	  /* Byte swap */
+#define  COM3_SCALEEN	  0x08	  /* Enable scaling */
+#define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
+#define REG_COM4	0x0d	/* Control 4 */
+#define REG_COM5	0x0e	/* All "reserved" */
+#define REG_COM6	0x0f	/* Control 6 */
+#define REG_AECH	0x10	/* More bits of AEC value */
+#define REG_CLKRC	0x11	/* Clocl control */
+#define   CLK_EXT	  0x40	  /* Use external clock directly */
+#define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
+#define REG_COM7	0x12	/* Control 7 */
+#define   COM7_RESET	  0x80	  /* Register reset */
+#define   COM7_FMT_MASK	  0x38
+#define   COM7_FMT_VGA	  0x00
+#define	  COM7_FMT_CIF	  0x20	  /* CIF format */
+#define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
+#define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
+#define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
+#define	  COM7_YUV	  0x00	  /* YUV */
+#define	  COM7_BAYER	  0x01	  /* Bayer format */
+#define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
+#define REG_COM8	0x13	/* Control 8 */
+#define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
+#define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
+#define   COM8_BFILT	  0x20	  /* Band filter enable */
+#define   COM8_AGC	  0x04	  /* Auto gain enable */
+#define   COM8_AWB	  0x02	  /* White balance enable */
+#define   COM8_AEC	  0x01	  /* Auto exposure enable */
+#define REG_COM9	0x14	/* Control 9  - gain ceiling */
+#define REG_COM10	0x15	/* Control 10 */
+#define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
+#define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
+#define   COM10_HREF_REV  0x08	  /* Reverse HREF */
+#define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
+#define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
+#define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
+#define REG_HSTART	0x17	/* Horiz start high bits */
+#define REG_HSTOP	0x18	/* Horiz stop high bits */
+#define REG_VSTART	0x19	/* Vert start high bits */
+#define REG_VSTOP	0x1a	/* Vert stop high bits */
+#define REG_PSHFT	0x1b	/* Pixel delay after HREF */
+#define REG_MIDH	0x1c	/* Manuf. ID high */
+#define REG_MIDL	0x1d	/* Manuf. ID low */
+#define REG_MVFP	0x1e	/* Mirror / vflip */
+#define   MVFP_MIRROR	  0x20	  /* Mirror image */
+#define   MVFP_FLIP	  0x10	  /* Vertical flip */
+
+#define REG_AEW		0x24	/* AGC upper limit */
+#define REG_AEB		0x25	/* AGC lower limit */
+#define REG_VPT		0x26	/* AGC/AEC fast mode op region */
+#define REG_HSYST	0x30	/* HSYNC rising edge delay */
+#define REG_HSYEN	0x31	/* HSYNC falling edge delay */
+#define REG_HREF	0x32	/* HREF pieces */
+#define REG_TSLB	0x3a	/* lots of stuff */
+#define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
+#define REG_COM11	0x3b	/* Control 11 */
+#define   COM11_NIGHT	  0x80	  /* NIght mode enable */
+#define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
+#define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
+#define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
+#define   COM11_EXP	  0x02
+#define REG_COM12	0x3c	/* Control 12 */
+#define   COM12_HREF	  0x80	  /* HREF always */
+#define REG_COM13	0x3d	/* Control 13 */
+#define   COM13_GAMMA	  0x80	  /* Gamma enable */
+#define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
+#define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
+#define REG_COM14	0x3e	/* Control 14 */
+#define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
+#define REG_EDGE	0x3f	/* Edge enhancement factor */
+#define REG_COM15	0x40	/* Control 15 */
+#define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
+#define	  COM15_R01FE	  0x80	  /*            01 to FE */
+#define   COM15_R00FF	  0xc0	  /*            00 to FF */
+#define   COM15_RGB565	  0x10	  /* RGB565 output */
+#define   COM15_RGB555	  0x30	  /* RGB555 output */
+#define REG_COM16	0x41	/* Control 16 */
+#define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
+#define REG_COM17	0x42	/* Control 17 */
+#define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
+#define   COM17_CBAR	  0x08	  /* DSP Color bar */
+
+/*
+ * This matrix defines how the colors are generated, must be
+ * tweaked to adjust hue and saturation.
+ *
+ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
+ *
+ * They are nine-bit signed quantities, with the sign bit
+ * stored in 0x58.  Sign for v-red is bit 0, and up from there.
+ */
+#define	REG_CMATRIX_BASE 0x4f
+#define   CMATRIX_LEN 6
+#define REG_CMATRIX_SIGN 0x58
+
+
+#define REG_BRIGHT	0x55	/* Brightness */
+#define REG_CONTRAS	0x56	/* Contrast control */
+
+#define REG_GFIX	0x69	/* Fix gain control */
+
+#define REG_RGB444	0x8c	/* RGB 444 control */
+#define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
+#define   R444_RGBX	  0x01	  /* Empty nibble at end */
+
+#define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
+#define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
+
+#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
+#define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
+#define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
+#define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
+#define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
+#define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
+#define REG_BD60MAX	0xab	/* 60hz banding step limit */
+
+
+/*
+ * Information we maintain about a known sensor.
+ */
+struct ov7670_format_struct;  /* coming later */
+struct ov7670_info {
+	struct ov7670_format_struct *fmt;  /* Current format */
+	unsigned char sat;		/* Saturation value */
+	int hue;			/* Hue value */
+};
+
+
+
+
+/*
+ * The default register settings, as obtained from OmniVision.  There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
+ *
+ * These settings give VGA YUYV.
+ */
+
+struct regval_list {
+	unsigned char reg_num;
+	unsigned char value;
+};
+
+static struct regval_list ov7670_default_regs[] = {
+	{ REG_COM7, COM7_RESET },
+/*
+ * Clock scale: 3 = 15fps
+ *              2 = 20fps
+ *              1 = 30fps
+ */
+	{ REG_CLKRC, 0x1 },	/* OV: clock scale (30 fps) */
+	{ REG_TSLB,  0x04 },	/* OV */
+	{ REG_COM7, 0 },	/* VGA */
+	/*
+	 * Set the hardware window.  These values from OV don't entirely
+	 * make sense - hstop is less than hstart.  But they work...
+	 */
+	{ REG_HSTART, 0x13 },	{ REG_HSTOP, 0x01 },
+	{ REG_HREF, 0xb6 },	{ REG_VSTART, 0x02 },
+	{ REG_VSTOP, 0x7a },	{ REG_VREF, 0x0a },
+
+	{ REG_COM3, 0 },	{ REG_COM14, 0 },
+	/* Mystery scaling numbers */
+	{ 0x70, 0x3a },		{ 0x71, 0x35 },
+	{ 0x72, 0x11 },		{ 0x73, 0xf0 },
+	{ 0xa2, 0x02 },		{ REG_COM10, 0x0 },
+
+	/* Gamma curve values */
+	{ 0x7a, 0x20 },		{ 0x7b, 0x10 },
+	{ 0x7c, 0x1e },		{ 0x7d, 0x35 },
+	{ 0x7e, 0x5a },		{ 0x7f, 0x69 },
+	{ 0x80, 0x76 },		{ 0x81, 0x80 },
+	{ 0x82, 0x88 },		{ 0x83, 0x8f },
+	{ 0x84, 0x96 },		{ 0x85, 0xa3 },
+	{ 0x86, 0xaf },		{ 0x87, 0xc4 },
+	{ 0x88, 0xd7 },		{ 0x89, 0xe8 },
+
+	/* AGC and AEC parameters.  Note we start by disabling those features,
+	   then turn them only after tweaking the values. */
+	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
+	{ REG_GAIN, 0 },	{ REG_AECH, 0 },
+	{ REG_COM4, 0x40 }, /* magic reserved bit */
+	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+	{ REG_BD50MAX, 0x05 },	{ REG_BD60MAX, 0x07 },
+	{ REG_AEW, 0x95 },	{ REG_AEB, 0x33 },
+	{ REG_VPT, 0xe3 },	{ REG_HAECC1, 0x78 },
+	{ REG_HAECC2, 0x68 },	{ 0xa1, 0x03 }, /* magic */
+	{ REG_HAECC3, 0xd8 },	{ REG_HAECC4, 0xd8 },
+	{ REG_HAECC5, 0xf0 },	{ REG_HAECC6, 0x90 },
+	{ REG_HAECC7, 0x94 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
+
+	/* Almost all of these are magic "reserved" values.  */
+	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
+	{ 0x16, 0x02 },		{ REG_MVFP, 0x07|MVFP_MIRROR },
+	{ 0x21, 0x02 },		{ 0x22, 0x91 },
+	{ 0x29, 0x07 },		{ 0x33, 0x0b },
+	{ 0x35, 0x0b },		{ 0x37, 0x1d },
+	{ 0x38, 0x71 },		{ 0x39, 0x2a },
+	{ REG_COM12, 0x78 },	{ 0x4d, 0x40 },
+	{ 0x4e, 0x20 },		{ REG_GFIX, 0 },
+	{ 0x6b, 0x4a },		{ 0x74, 0x10 },
+	{ 0x8d, 0x4f },		{ 0x8e, 0 },
+	{ 0x8f, 0 },		{ 0x90, 0 },
+	{ 0x91, 0 },		{ 0x96, 0 },
+	{ 0x9a, 0 },		{ 0xb0, 0x84 },
+	{ 0xb1, 0x0c },		{ 0xb2, 0x0e },
+	{ 0xb3, 0x82 },		{ 0xb8, 0x0a },
+
+	/* More reserved magic, some of which tweaks white balance */
+	{ 0x43, 0x0a },		{ 0x44, 0xf0 },
+	{ 0x45, 0x34 },		{ 0x46, 0x58 },
+	{ 0x47, 0x28 },		{ 0x48, 0x3a },
+	{ 0x59, 0x88 },		{ 0x5a, 0x88 },
+	{ 0x5b, 0x44 },		{ 0x5c, 0x67 },
+	{ 0x5d, 0x49 },		{ 0x5e, 0x0e },
+	{ 0x6c, 0x0a },		{ 0x6d, 0x55 },
+	{ 0x6e, 0x11 },		{ 0x6f, 0x9f }, /* "9e for advance AWB" */
+	{ 0x6a, 0x40 },		{ REG_BLUE, 0x40 },
+	{ REG_RED, 0x60 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
+
+	/* Matrix coefficients */
+	{ 0x4f, 0x80 },		{ 0x50, 0x80 },
+	{ 0x51, 0 },		{ 0x52, 0x22 },
+	{ 0x53, 0x5e },		{ 0x54, 0x80 },
+	{ 0x58, 0x9e },
+
+	{ REG_COM16, COM16_AWBGAIN },	{ REG_EDGE, 0 },
+	{ 0x75, 0x05 },		{ 0x76, 0xe1 },
+	{ 0x4c, 0 },		{ 0x77, 0x01 },
+	{ REG_COM13, 0xc3 },	{ 0x4b, 0x09 },
+	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
+	{ 0x56, 0x40 },
+
+	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP|COM11_HZAUTO },
+	{ 0xa4, 0x88 },		{ 0x96, 0 },
+	{ 0x97, 0x30 },		{ 0x98, 0x20 },
+	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
+	{ 0x9b, 0x29 },		{ 0x9c, 0x03 },
+	{ 0x9d, 0x4c },		{ 0x9e, 0x3f },
+	{ 0x78, 0x04 },
+
+	/* Extra-weird stuff.  Some sort of multiplexor register */
+	{ 0x79, 0x01 },		{ 0xc8, 0xf0 },
+	{ 0x79, 0x0f },		{ 0xc8, 0x00 },
+	{ 0x79, 0x10 },		{ 0xc8, 0x7e },
+	{ 0x79, 0x0a },		{ 0xc8, 0x80 },
+	{ 0x79, 0x0b },		{ 0xc8, 0x01 },
+	{ 0x79, 0x0c },		{ 0xc8, 0x0f },
+	{ 0x79, 0x0d },		{ 0xc8, 0x20 },
+	{ 0x79, 0x09 },		{ 0xc8, 0x80 },
+	{ 0x79, 0x02 },		{ 0xc8, 0xc0 },
+	{ 0x79, 0x03 },		{ 0xc8, 0x40 },
+	{ 0x79, 0x05 },		{ 0xc8, 0x30 },
+	{ 0x79, 0x26 },
+
+	{ 0xff, 0xff },	/* END MARKER */
+};
+
+
+/*
+ * Here we'll try to encapsulate the changes for just the output
+ * video format.
+ *
+ * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
+ *
+ * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
+ */
+
+
+static struct regval_list ov7670_fmt_yuv422[] = {
+	{ REG_COM7, 0x0 },  /* Selects YUV mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0 },
+	{ REG_COM15, COM15_R00FF },
+	{ REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0x80 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0x80 }, 	/* "matrix coefficient 2" */
+	{ 0x51, 0    },		/* vb */
+	{ 0x52, 0x22 }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0x5e }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0x80 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb565[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0x0 },
+	{ REG_COM15, COM15_RGB565 },
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x51, 0    },		/* vb */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb444[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
+	{ REG_COM1, 0x40 },	/* Magic reserved bit */
+	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x51, 0    },		/* vb */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
+	{ 0xff, 0xff },
+};
+
+
+
+
+/*
+ * Low-level register I/O.
+ */
+
+static int ov7670_read(struct i2c_client *c, unsigned char reg,
+		unsigned char *value)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(c, reg);
+	if (ret >= 0)
+		*value = (unsigned char) ret;
+	return ret;
+}
+
+
+static int ov7670_write(struct i2c_client *c, unsigned char reg,
+		unsigned char value)
+{
+	return i2c_smbus_write_byte_data(c, reg, value);
+}
+
+
+/*
+ * Write a list of register settings; ff/ff stops the process.
+ */
+static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals)
+{
+	while (vals->reg_num != 0xff || vals->value != 0xff) {
+		int ret = ov7670_write(c, vals->reg_num, vals->value);
+		if (ret < 0)
+			return ret;
+		vals++;
+	}
+	return 0;
+}
+
+
+/*
+ * Stuff that knows about the sensor.
+ */
+static void ov7670_reset(struct i2c_client *client)
+{
+	ov7670_write(client, REG_COM7, COM7_RESET);
+	msleep(1);
+}
+
+
+static int ov7670_init(struct i2c_client *client)
+{
+	return ov7670_write_array(client, ov7670_default_regs);
+}
+
+
+
+static int ov7670_detect(struct i2c_client *client)
+{
+	unsigned char v;
+	int ret;
+
+	ret = ov7670_init(client);
+	if (ret < 0)
+		return ret;
+	ret = ov7670_read(client, REG_MIDH, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x7f) /* OV manuf. id. */
+		return -ENODEV;
+	ret = ov7670_read(client, REG_MIDL, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0xa2)
+		return -ENODEV;
+	/*
+	 * OK, we know we have an OmniVision chip...but which one?
+	 */
+	ret = ov7670_read(client, REG_PID, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x76)  /* PID + VER = 0x76 / 0x73 */
+		return -ENODEV;
+	ret = ov7670_read(client, REG_VER, &v);
+	if (ret < 0)
+		return ret;
+	if (v != 0x73)  /* PID + VER = 0x76 / 0x73 */
+		return -ENODEV;
+	return 0;
+}
+
+
+/*
+ * Store information about the video data format.  The color matrix
+ * is deeply tied into the format, so keep the relevant values here.
+ * The magic matrix nubmers come from OmniVision.
+ */
+static struct ov7670_format_struct {
+	__u8 *desc;
+	__u32 pixelformat;
+	struct regval_list *regs;
+	int cmatrix[CMATRIX_LEN];
+} ov7670_formats[] = {
+	{
+		.desc		= "YUYV 4:2:2",
+		.pixelformat	= V4L2_PIX_FMT_YUYV,
+		.regs 		= ov7670_fmt_yuv422,
+		.cmatrix	= { 128, -128, 0, -34, -94, 128 },
+	},
+	{
+		.desc		= "RGB 444",
+		.pixelformat	= V4L2_PIX_FMT_RGB444,
+		.regs		= ov7670_fmt_rgb444,
+		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
+	},
+	{
+		.desc		= "RGB 565",
+		.pixelformat	= V4L2_PIX_FMT_RGB565,
+		.regs		= ov7670_fmt_rgb565,
+		.cmatrix	= { 179, -179, 0, -61, -176, 228 },
+	},
+};
+#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0]))
+
+/*
+ * All formats we support are 2 bytes/pixel.
+ */
+#define BYTES_PER_PIXEL 2
+
+/*
+ * Then there is the issue of window sizes.  Try to capture the info here.
+ */
+
+/*
+ * QCIF mode is done (by OV) in a very strange way - it actually looks like
+ * VGA with weird scaling options - they do *not* use the canned QCIF mode
+ * which is allegedly provided by the sensor.  So here's the weird register
+ * settings.
+ */
+static struct regval_list ov7670_qcif_regs[] = {
+	{ REG_COM3, COM3_SCALEEN|COM3_DCWEN },
+	{ REG_COM3, COM3_DCWEN },
+	{ REG_COM14, COM14_DCWEN | 0x01},
+	{ 0x73, 0xf1 },
+	{ 0xa2, 0x52 },
+	{ 0x7b, 0x1c },
+	{ 0x7c, 0x28 },
+	{ 0x7d, 0x3c },
+	{ 0x7f, 0x69 },
+	{ REG_COM9, 0x38 },
+	{ 0xa1, 0x0b },
+	{ 0x74, 0x19 },
+	{ 0x9a, 0x80 },
+	{ 0x43, 0x14 },
+	{ REG_COM13, 0xc0 },
+	{ 0xff, 0xff },
+};
+
+static struct ov7670_win_size {
+	int	width;
+	int	height;
+	unsigned char com7_bit;
+	int	hstart;		/* Start/stop values for the camera.  Note */
+	int	hstop;		/* that they do not always make complete */
+	int	vstart;		/* sense to humans, but evidently the sensor */
+	int	vstop;		/* will do the right thing... */
+	struct regval_list *regs; /* Regs to tweak */
+/* h/vref stuff */
+} ov7670_win_sizes[] = {
+	/* VGA */
+	{
+		.width		= VGA_WIDTH,
+		.height		= VGA_HEIGHT,
+		.com7_bit	= COM7_FMT_VGA,
+		.hstart		= 158,		/* These values from */
+		.hstop		=  14,		/* Omnivision */
+		.vstart		=  10,
+		.vstop		= 490,
+		.regs 		= NULL,
+	},
+	/* CIF */
+	{
+		.width		= CIF_WIDTH,
+		.height		= CIF_HEIGHT,
+		.com7_bit	= COM7_FMT_CIF,
+		.hstart		= 170,		/* Empirically determined */
+		.hstop		=  90,
+		.vstart		=  14,
+		.vstop		= 494,
+		.regs 		= NULL,
+	},
+	/* QVGA */
+	{
+		.width		= QVGA_WIDTH,
+		.height		= QVGA_HEIGHT,
+		.com7_bit	= COM7_FMT_QVGA,
+		.hstart		= 164,		/* Empirically determined */
+		.hstop		=  20,
+		.vstart		=  14,
+		.vstop		= 494,
+		.regs 		= NULL,
+	},
+	/* QCIF */
+	{
+		.width		= QCIF_WIDTH,
+		.height		= QCIF_HEIGHT,
+		.com7_bit	= COM7_FMT_VGA, /* see comment above */
+		.hstart		= 456,		/* Empirically determined */
+		.hstop		=  24,
+		.vstart		=  14,
+		.vstop		= 494,
+		.regs 		= ov7670_qcif_regs,
+	},
+};
+
+#define N_WIN_SIZES (sizeof(ov7670_win_sizes)/sizeof(ov7670_win_sizes[0]))
+
+
+/*
+ * Store a set of start/stop values into the camera.
+ */
+static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop,
+		int vstart, int vstop)
+{
+	int ret;
+	unsigned char v;
+/*
+ * Horizontal: 11 bits, top 8 live in hstart and hstop.  Bottom 3 of
+ * hstart are in href[2:0], bottom 3 of hstop in href[5:3].  There is
+ * a mystery "edge offset" value in the top two bits of href.
+ */
+	ret =  ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff);
+	ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff);
+	ret += ov7670_read(client, REG_HREF, &v);
+	v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
+	msleep(10);
+	ret += ov7670_write(client, REG_HREF, v);
+/*
+ * Vertical: similar arrangement, but only 10 bits.
+ */
+	ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff);
+	ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff);
+	ret += ov7670_read(client, REG_VREF, &v);
+	v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
+	msleep(10);
+	ret += ov7670_write(client, REG_VREF, v);
+	return ret;
+}
+
+
+static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
+{
+	struct ov7670_format_struct *ofmt;
+
+	if (fmt->index >= N_OV7670_FMTS)
+		return -EINVAL;
+
+	ofmt = ov7670_formats + fmt->index;
+	fmt->flags = 0;
+	strcpy(fmt->description, ofmt->desc);
+	fmt->pixelformat = ofmt->pixelformat;
+	return 0;
+}
+
+
+static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
+		struct ov7670_format_struct **ret_fmt,
+		struct ov7670_win_size **ret_wsize)
+{
+	int index;
+	struct ov7670_win_size *wsize;
+	struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+	for (index = 0; index < N_OV7670_FMTS; index++)
+		if (ov7670_formats[index].pixelformat == pix->pixelformat)
+			break;
+	if (index >= N_OV7670_FMTS)
+		return -EINVAL;
+	if (ret_fmt != NULL)
+		*ret_fmt = ov7670_formats + index;
+	/*
+	 * Fields: the OV devices claim to be progressive.
+	 */
+	if (pix->field == V4L2_FIELD_ANY)
+		pix->field = V4L2_FIELD_NONE;
+	else if (pix->field != V4L2_FIELD_NONE)
+		return -EINVAL;
+	/*
+	 * Round requested image size down to the nearest
+	 * we support, but not below the smallest.
+	 */
+	for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES;
+	     wsize++)
+		if (pix->width >= wsize->width && pix->height >= wsize->height)
+			break;
+	if (wsize >= ov7670_win_sizes + N_WIN_SIZES)
+		wsize--;   /* Take the smallest one */
+	if (ret_wsize != NULL)
+		*ret_wsize = wsize;
+	/*
+	 * Note the size we'll actually handle.
+	 */
+	pix->width = wsize->width;
+	pix->height = wsize->height;
+	pix->bytesperline = pix->width*BYTES_PER_PIXEL;
+	pix->sizeimage = pix->height*pix->bytesperline;
+	return 0;
+}
+
+/*
+ * Set a format.
+ */
+static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+{
+	int ret;
+	struct ov7670_format_struct *ovfmt;
+	struct ov7670_win_size *wsize;
+	struct ov7670_info *info = i2c_get_clientdata(c);
+	unsigned char com7;
+
+	ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize);
+	if (ret)
+		return ret;
+	/*
+	 * COM7 is a pain in the ass, it doesn't like to be read then
+	 * quickly written afterward.  But we have everything we need
+	 * to set it absolutely here, as long as the format-specific
+	 * register sets list it first.
+	 */
+	com7 = ovfmt->regs[0].value;
+	com7 |= wsize->com7_bit;
+	ov7670_write(c, REG_COM7, com7);
+	/*
+	 * Now write the rest of the array.  Also store start/stops
+	 */
+	ov7670_write_array(c, ovfmt->regs + 1);
+	ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart,
+			wsize->vstop);
+	ret = 0;
+	if (wsize->regs)
+		ret = ov7670_write_array(c, wsize->regs);
+	info->fmt = ovfmt;
+	return 0;
+}
+
+/*
+ * Implement G/S_PARM.  There is a "high quality" mode we could try
+ * to do someday; for now, we just do the frame rate tweak.
+ */
+static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	unsigned char clkrc;
+	int ret;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	ret = ov7670_read(c, REG_CLKRC, &clkrc);
+	if (ret < 0)
+		return ret;
+	memset(cp, 0, sizeof(struct v4l2_captureparm));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = 1;
+	cp->timeperframe.denominator = OV7670_FRAME_RATE;
+	if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1)
+		cp->timeperframe.denominator /= (clkrc & CLK_SCALE);
+	return 0;
+}
+
+static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	unsigned char clkrc;
+	int ret, div;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+	/*
+	 * CLKRC has a reserved bit, so let's preserve it.
+	 */
+	ret = ov7670_read(c, REG_CLKRC, &clkrc);
+	if (ret < 0)
+		return ret;
+	if (tpf->numerator == 0 || tpf->denominator == 0)
+		div = 1;  /* Reset to full rate */
+	else
+		div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator;
+	if (div == 0)
+		div = 1;
+	else if (div > CLK_SCALE)
+		div = CLK_SCALE;
+	clkrc = (clkrc & 0x80) | div;
+	tpf->numerator = 1;
+	tpf->denominator = OV7670_FRAME_RATE/div;
+	return ov7670_write(c, REG_CLKRC, clkrc);
+}
+
+
+
+/*
+ * Code for dealing with controls.
+ */
+
+
+
+
+
+static int ov7670_store_cmatrix(struct i2c_client *client,
+		int matrix[CMATRIX_LEN])
+{
+	int i, ret;
+	unsigned char signbits;
+
+	/*
+	 * Weird crap seems to exist in the upper part of
+	 * the sign bits register, so let's preserve it.
+	 */
+	ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits);
+	signbits &= 0xc0;
+
+	for (i = 0; i < CMATRIX_LEN; i++) {
+		unsigned char raw;
+
+		if (matrix[i] < 0) {
+			signbits |= (1 << i);
+			if (matrix[i] < -255)
+				raw = 0xff;
+			else
+				raw = (-1 * matrix[i]) & 0xff;
+		}
+		else {
+			if (matrix[i] > 255)
+				raw = 0xff;
+			else
+				raw = matrix[i] & 0xff;
+		}
+		ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw);
+	}
+	ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits);
+	return ret;
+}
+
+
+/*
+ * Hue also requires messing with the color matrix.  It also requires
+ * trig functions, which tend not to be well supported in the kernel.
+ * So here is a simple table of sine values, 0-90 degrees, in steps
+ * of five degrees.  Values are multiplied by 1000.
+ *
+ * The following naive approximate trig functions require an argument
+ * carefully limited to -180 <= theta <= 180.
+ */
+#define SIN_STEP 5
+static const int ov7670_sin_table[] = {
+	   0,	 87,   173,   258,   342,   422,
+	 499,	573,   642,   707,   766,   819,
+	 866,	906,   939,   965,   984,   996,
+	1000
+};
+
+static int ov7670_sine(int theta)
+{
+	int chs = 1;
+	int sine;
+
+	if (theta < 0) {
+		theta = -theta;
+		chs = -1;
+	}
+	if (theta <= 90)
+		sine = ov7670_sin_table[theta/SIN_STEP];
+	else {
+		theta -= 90;
+		sine = 1000 - ov7670_sin_table[theta/SIN_STEP];
+	}
+	return sine*chs;
+}
+
+static int ov7670_cosine(int theta)
+{
+	theta = 90 - theta;
+	if (theta > 180)
+		theta -= 360;
+	else if (theta < -180)
+		theta += 360;
+	return ov7670_sine(theta);
+}
+
+
+
+
+static void ov7670_calc_cmatrix(struct ov7670_info *info,
+		int matrix[CMATRIX_LEN])
+{
+	int i;
+	/*
+	 * Apply the current saturation setting first.
+	 */
+	for (i = 0; i < CMATRIX_LEN; i++)
+		matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7;
+	/*
+	 * Then, if need be, rotate the hue value.
+	 */
+	if (info->hue != 0) {
+		int sinth, costh, tmpmatrix[CMATRIX_LEN];
+
+		memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int));
+		sinth = ov7670_sine(info->hue);
+		costh = ov7670_cosine(info->hue);
+
+		matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000;
+		matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000;
+		matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000;
+		matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000;
+		matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000;
+		matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000;
+	}
+}
+
+
+
+static int ov7670_t_sat(struct i2c_client *client, int value)
+{
+	struct ov7670_info *info = i2c_get_clientdata(client);
+	int matrix[CMATRIX_LEN];
+	int ret;
+
+	info->sat = value;
+	ov7670_calc_cmatrix(info, matrix);
+	ret = ov7670_store_cmatrix(client, matrix);
+	return ret;
+}
+
+static int ov7670_q_sat(struct i2c_client *client, __s32 *value)
+{
+	struct ov7670_info *info = i2c_get_clientdata(client);
+
+	*value = info->sat;
+	return 0;
+}
+
+static int ov7670_t_hue(struct i2c_client *client, int value)
+{
+	struct ov7670_info *info = i2c_get_clientdata(client);
+	int matrix[CMATRIX_LEN];
+	int ret;
+
+	if (value < -180 || value > 180)
+		return -EINVAL;
+	info->hue = value;
+	ov7670_calc_cmatrix(info, matrix);
+	ret = ov7670_store_cmatrix(client, matrix);
+	return ret;
+}
+
+
+static int ov7670_q_hue(struct i2c_client *client, __s32 *value)
+{
+	struct ov7670_info *info = i2c_get_clientdata(client);
+
+	*value = info->hue;
+	return 0;
+}
+
+
+/*
+ * Some weird registers seem to store values in a sign/magnitude format!
+ */
+static unsigned char ov7670_sm_to_abs(unsigned char v)
+{
+	if ((v & 0x80) == 0)
+		return v + 128;
+	else
+		return 128 - (v & 0x7f);
+}
+
+
+static unsigned char ov7670_abs_to_sm(unsigned char v)
+{
+	if (v > 127)
+		return v & 0x7f;
+	else
+		return (128 - v) | 0x80;
+}
+
+static int ov7670_t_brightness(struct i2c_client *client, int value)
+{
+	unsigned char com8, v;
+	int ret;
+
+	ov7670_read(client, REG_COM8, &com8);
+	com8 &= ~COM8_AEC;
+	ov7670_write(client, REG_COM8, com8);
+	v = ov7670_abs_to_sm(value);
+	ret = ov7670_write(client, REG_BRIGHT, v);
+	return ret;
+}
+
+static int ov7670_q_brightness(struct i2c_client *client, __s32 *value)
+{
+	unsigned char v;
+	int ret = ov7670_read(client, REG_BRIGHT, &v);
+
+	*value = ov7670_sm_to_abs(v);
+	return ret;
+}
+
+static int ov7670_t_contrast(struct i2c_client *client, int value)
+{
+	return ov7670_write(client, REG_CONTRAS, (unsigned char) value);
+}
+
+static int ov7670_q_contrast(struct i2c_client *client, __s32 *value)
+{
+	unsigned char v;
+	int ret = ov7670_read(client, REG_CONTRAS, &v);
+
+	*value = v;
+	return ret;
+}
+
+static int ov7670_q_hflip(struct i2c_client *client, __s32 *value)
+{
+	int ret;
+	unsigned char v;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	*value = (v & MVFP_MIRROR) == MVFP_MIRROR;
+	return ret;
+}
+
+
+static int ov7670_t_hflip(struct i2c_client *client, int value)
+{
+	unsigned char v;
+	int ret;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	if (value)
+		v |= MVFP_MIRROR;
+	else
+		v &= ~MVFP_MIRROR;
+	msleep(10);  /* FIXME */
+	ret += ov7670_write(client, REG_MVFP, v);
+	return ret;
+}
+
+
+
+static int ov7670_q_vflip(struct i2c_client *client, __s32 *value)
+{
+	int ret;
+	unsigned char v;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	*value = (v & MVFP_FLIP) == MVFP_FLIP;
+	return ret;
+}
+
+
+static int ov7670_t_vflip(struct i2c_client *client, int value)
+{
+	unsigned char v;
+	int ret;
+
+	ret = ov7670_read(client, REG_MVFP, &v);
+	if (value)
+		v |= MVFP_FLIP;
+	else
+		v &= ~MVFP_FLIP;
+	msleep(10);  /* FIXME */
+	ret += ov7670_write(client, REG_MVFP, v);
+	return ret;
+}
+
+
+static struct ov7670_control {
+	struct v4l2_queryctrl qc;
+	int (*query)(struct i2c_client *c, __s32 *value);
+	int (*tweak)(struct i2c_client *c, int value);
+} ov7670_controls[] =
+{
+	{
+		.qc = {
+			.id = V4L2_CID_BRIGHTNESS,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Brightness",
+			.minimum = 0,
+			.maximum = 255,
+			.step = 1,
+			.default_value = 0x80,
+			.flags = V4L2_CTRL_FLAG_SLIDER
+		},
+		.tweak = ov7670_t_brightness,
+		.query = ov7670_q_brightness,
+	},
+	{
+		.qc = {
+			.id = V4L2_CID_CONTRAST,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Contrast",
+			.minimum = 0,
+			.maximum = 127,
+			.step = 1,
+			.default_value = 0x40,   /* XXX ov7670 spec */
+			.flags = V4L2_CTRL_FLAG_SLIDER
+		},
+		.tweak = ov7670_t_contrast,
+		.query = ov7670_q_contrast,
+	},
+	{
+		.qc = {
+			.id = V4L2_CID_SATURATION,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "Saturation",
+			.minimum = 0,
+			.maximum = 256,
+			.step = 1,
+			.default_value = 0x80,
+			.flags = V4L2_CTRL_FLAG_SLIDER
+		},
+		.tweak = ov7670_t_sat,
+		.query = ov7670_q_sat,
+	},
+	{
+		.qc = {
+			.id = V4L2_CID_HUE,
+			.type = V4L2_CTRL_TYPE_INTEGER,
+			.name = "HUE",
+			.minimum = -180,
+			.maximum = 180,
+			.step = 5,
+			.default_value = 0,
+			.flags = V4L2_CTRL_FLAG_SLIDER
+		},
+		.tweak = ov7670_t_hue,
+		.query = ov7670_q_hue,
+	},
+	{
+		.qc = {
+			.id = V4L2_CID_VFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Vertical flip",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 0,
+		},
+		.tweak = ov7670_t_vflip,
+		.query = ov7670_q_vflip,
+	},
+	{
+		.qc = {
+			.id = V4L2_CID_HFLIP,
+			.type = V4L2_CTRL_TYPE_BOOLEAN,
+			.name = "Horizontal mirror",
+			.minimum = 0,
+			.maximum = 1,
+			.step = 1,
+			.default_value = 0,
+		},
+		.tweak = ov7670_t_hflip,
+		.query = ov7670_q_hflip,
+	},
+};
+#define N_CONTROLS (sizeof(ov7670_controls)/sizeof(ov7670_controls[0]))
+
+static struct ov7670_control *ov7670_find_control(__u32 id)
+{
+	int i;
+
+	for (i = 0; i < N_CONTROLS; i++)
+		if (ov7670_controls[i].qc.id == id)
+			return ov7670_controls + i;
+	return NULL;
+}
+
+
+static int ov7670_queryctrl(struct i2c_client *client,
+		struct v4l2_queryctrl *qc)
+{
+	struct ov7670_control *ctrl = ov7670_find_control(qc->id);
+
+	if (ctrl == NULL)
+		return -EINVAL;
+	*qc = ctrl->qc;
+	return 0;
+}
+
+static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+	int ret;
+
+	if (octrl == NULL)
+		return -EINVAL;
+	ret = octrl->query(client, &ctrl->value);
+	if (ret >= 0)
+		return 0;
+	return ret;
+}
+
+static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+{
+	struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+	int ret;
+
+	if (octrl == NULL)
+		return -EINVAL;
+	ret =  octrl->tweak(client, ctrl->value);
+	if (ret >= 0)
+		return 0;
+	return ret;
+}
+
+
+
+
+
+
+/*
+ * Basic i2c stuff.
+ */
+static struct i2c_driver ov7670_driver;
+
+static int ov7670_attach(struct i2c_adapter *adapter)
+{
+	int ret;
+	struct i2c_client *client;
+	struct ov7670_info *info;
+
+	/*
+	 * For now: only deal with adapters we recognize.
+	 */
+	if (adapter->id != I2C_HW_SMBUS_CAFE)
+		return -ENODEV;
+
+	client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL);
+	if (! client)
+		return -ENOMEM;
+	client->adapter = adapter;
+	client->addr = OV7670_I2C_ADDR;
+	client->driver = &ov7670_driver,
+	strcpy(client->name, "OV7670");
+	/*
+	 * Set up our info structure.
+	 */
+	info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL);
+	if (! info) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+	info->fmt = &ov7670_formats[0];
+	info->sat = 128;	/* Review this */
+	i2c_set_clientdata(client, info);
+
+	/*
+	 * Make sure it's an ov7670
+	 */
+	ret = ov7670_detect(client);
+	if (ret)
+		goto out_free_info;
+	i2c_attach_client(client);
+	return 0;
+
+  out_free_info:
+	kfree(info);
+  out_free:
+	kfree(client);
+	return ret;
+}
+
+
+static int ov7670_detach(struct i2c_client *client)
+{
+	i2c_detach_client(client);
+	kfree(i2c_get_clientdata(client));
+	kfree(client);
+	return 0;
+}
+
+
+static int ov7670_command(struct i2c_client *client, unsigned int cmd,
+		void *arg)
+{
+	switch (cmd) {
+	case VIDIOC_INT_G_CHIP_IDENT:
+		* (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670;
+		return 0;
+
+	case VIDIOC_INT_RESET:
+		ov7670_reset(client);
+		return 0;
+
+	case VIDIOC_INT_INIT:
+		return ov7670_init(client);
+
+	case VIDIOC_ENUM_FMT:
+		return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg);
+	case VIDIOC_TRY_FMT:
+		return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL);
+	case VIDIOC_S_FMT:
+		return ov7670_s_fmt(client, (struct v4l2_format *) arg);
+	case VIDIOC_QUERYCTRL:
+		return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg);
+	case VIDIOC_S_CTRL:
+		return ov7670_s_ctrl(client, (struct v4l2_control *) arg);
+	case VIDIOC_G_CTRL:
+		return ov7670_g_ctrl(client, (struct v4l2_control *) arg);
+	case VIDIOC_S_PARM:
+		return ov7670_s_parm(client, (struct v4l2_streamparm *) arg);
+	case VIDIOC_G_PARM:
+		return ov7670_g_parm(client, (struct v4l2_streamparm *) arg);
+	}
+	return -EINVAL;
+}
+
+
+
+static struct i2c_driver ov7670_driver = {
+	.driver = {
+		.name = "ov7670",
+	},
+	.id 		= I2C_DRIVERID_OV7670,
+	.class 		= I2C_CLASS_CAM_DIGITAL,
+	.attach_adapter = ov7670_attach,
+	.detach_client	= ov7670_detach,
+	.command	= ov7670_command,
+};
+
+
+/*
+ * Module initialization
+ */
+static int __init ov7670_mod_init(void)
+{
+	printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n");
+	return i2c_add_driver(&ov7670_driver);
+}
+
+static void __exit ov7670_mod_exit(void)
+{
+	i2c_del_driver(&ov7670_driver);
+}
+
+module_init(ov7670_mod_init);
+module_exit(ov7670_mod_exit);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index c80c26b..848fb233 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -260,6 +260,22 @@
 				  sizeof(decoder_ops[0]))) - 1;
 	hdw->decoder_ctrl = &ctxt->ctrl;
 	cp->handler = &ctxt->handler;
+	{
+		/*
+		  Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit
+		  of nuttiness for cx25840 causes that module to
+		  correctly set up its video scaling.  This is really
+		  a problem in the cx25840 module itself, but we work
+		  around it here.  The problem has not been seen in
+		  ivtv because there VBI is supported and set up.  We
+		  don't do VBI here (at least not yet) and thus we
+		  never attempted to even set it up.
+		 */
+		struct v4l2_format fmt;
+		memset(&fmt,0,sizeof(fmt));
+		fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+		pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_FMT,&fmt);
+	}
 	pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up",
 		   cp->client->addr);
 	return !0;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 1f78733..d200496 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -356,28 +356,6 @@
 	return 0;
 }
 
-static int ctrl_hres_max_get(struct pvr2_ctrl *cptr,int *vp)
-{
-	/* If we're dealing with a 24xxx device, force the horizontal
-	   maximum to be 720 no matter what, since we can't get the device
-	   to work properly with any other value.  Otherwise just return
-	   the normal value. */
-	*vp = cptr->info->def.type_int.max_value;
-	if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720;
-	return 0;
-}
-
-static int ctrl_hres_min_get(struct pvr2_ctrl *cptr,int *vp)
-{
-	/* If we're dealing with a 24xxx device, force the horizontal
-	   minimum to be 720 no matter what, since we can't get the device
-	   to work properly with any other value.  Otherwise just return
-	   the normal value. */
-	*vp = cptr->info->def.type_int.min_value;
-	if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720;
-	return 0;
-}
-
 static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
 {
 	/* Actual maximum depends on the video standard in effect. */
@@ -758,10 +736,6 @@
 		.default_value = 720,
 		DEFREF(res_hor),
 		DEFINT(19,720),
-		/* Hook in check for clamp on horizontal resolution in
-		   order to avoid unsolved problem involving cx25840. */
-		.get_max_value = ctrl_hres_max_get,
-		.get_min_value = ctrl_hres_min_get,
 	},{
 		.desc = "Vertical capture resolution",
 		.name = "resolution_ver",
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index 3b9012f8e3..f9bb41d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -185,6 +185,79 @@
 	}
 }
 
+
+/* This is a special entry point for cases of I2C transaction attempts to
+   the IR receiver.  The implementation here simulates the IR receiver by
+   issuing a command to the FX2 firmware and using that response to return
+   what the real I2C receiver would have returned.  We use this for 24xxx
+   devices, where the IR receiver chip has been removed and replaced with
+   FX2 related logic. */
+static int i2c_24xxx_ir(struct pvr2_hdw *hdw,
+			u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+	u8 dat[4];
+	unsigned int stat;
+
+	if (!(rlen || wlen)) {
+		/* This is a probe attempt.  Just let it succeed. */
+		return 0;
+	}
+
+	/* We don't understand this kind of transaction */
+	if ((wlen != 0) || (rlen == 0)) return -EIO;
+
+	if (rlen < 3) {
+		/* Mike Isely <isely@pobox.com> Appears to be a probe
+		   attempt from lirc.  Just fill in zeroes and return.  If
+		   we try instead to do the full transaction here, then bad
+		   things seem to happen within the lirc driver module
+		   (version 0.8.0-7 sources from Debian, when run under
+		   vanilla 2.6.17.6 kernel) - and I don't have the patience
+		   to chase it down. */
+		if (rlen > 0) rdata[0] = 0;
+		if (rlen > 1) rdata[1] = 0;
+		return 0;
+	}
+
+	/* Issue a command to the FX2 to read the IR receiver. */
+	LOCK_TAKE(hdw->ctl_lock); do {
+		hdw->cmd_buffer[0] = 0xec;
+		stat = pvr2_send_request(hdw,
+					 hdw->cmd_buffer,1,
+					 hdw->cmd_buffer,4);
+		dat[0] = hdw->cmd_buffer[0];
+		dat[1] = hdw->cmd_buffer[1];
+		dat[2] = hdw->cmd_buffer[2];
+		dat[3] = hdw->cmd_buffer[3];
+	} while (0); LOCK_GIVE(hdw->ctl_lock);
+
+	/* Give up if that operation failed. */
+	if (stat != 0) return stat;
+
+	/* Mangle the results into something that looks like the real IR
+	   receiver. */
+	rdata[2] = 0xc1;
+	if (dat[0] != 1) {
+		/* No code received. */
+		rdata[0] = 0;
+		rdata[1] = 0;
+	} else {
+		u16 val;
+		/* Mash the FX2 firmware-provided IR code into something
+		   that the normal i2c chip-level driver expects. */
+		val = dat[1];
+		val <<= 8;
+		val |= dat[2];
+		val >>= 1;
+		val &= ~0x0003;
+		val |= 0x8000;
+		rdata[0] = (val >> 8) & 0xffu;
+		rdata[1] = val & 0xffu;
+	}
+
+	return 0;
+}
+
 /* This is a special entry point that is entered if an I2C operation is
    attempted to a wm8775 chip on model 24xxx hardware.  Autodetect of this
    part doesn't work, but we know it is really there.  So let's look for
@@ -887,17 +960,17 @@
 {
 	unsigned int idx;
 
-	// The default action for all possible I2C addresses is just to do
-	// the transfer normally.
+	/* The default action for all possible I2C addresses is just to do
+	   the transfer normally. */
 	for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) {
 		hdw->i2c_func[idx] = pvr2_i2c_basic_op;
 	}
 
-	// If however we're dealing with new hardware, insert some hacks in
-	// the I2C transfer stack to let things work better.
+	/* However, deal with various special cases for 24xxx hardware. */
 	if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
 		hdw->i2c_func[0x1b] = i2c_hack_wm8775;
 		hdw->i2c_func[0x44] = i2c_hack_cx25840;
+		hdw->i2c_func[0x18] = i2c_24xxx_ir;
 	}
 
 	// Configure the adapter and set up everything else related to it.
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index f28398d..c2374ed 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -851,7 +851,7 @@
 
 	/* On 60Hz, it is using a higher Vertical Output Size */
 	if (!is_50hz)
-		res+=(VRES_60HZ-480)>>1;
+		res += (VRES_60HZ - 480) >> 1;
 
 		/* height */
 	saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
@@ -907,7 +907,7 @@
 
 	/* Activates task "B" */
 	saa711x_write(client, R_80_GLOBAL_CNTL_1,
-				saa711x_read(client,R_80_GLOBAL_CNTL_1)|0x20);
+				saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
 
 	return 0;
 }
@@ -932,11 +932,11 @@
 	if (std & V4L2_STD_525_60) {
 		v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
 		saa711x_writeregs(client, saa7115_cfg_60hz_video);
-		saa711x_set_size(client,720,480);
+		saa711x_set_size(client, 720, 480);
 	} else {
 		v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
 		saa711x_writeregs(client, saa7115_cfg_50hz_video);
-		saa711x_set_size(client,720,576);
+		saa711x_set_size(client, 720, 576);
 	}
 
 	/* Register 0E - Bits D6-D4 on NO-AUTO mode
@@ -1464,13 +1464,13 @@
 	client->driver = &i2c_driver_saa711x;
 	snprintf(client->name, sizeof(client->name) - 1, "saa7115");
 
-	for (i=0;i<0x0f;i++) {
+	for (i = 0; i < 0x0f; i++) {
 		saa711x_write(client, 0, i);
-		name[i] = (saa711x_read(client, 0) &0x0f) +'0';
-		if (name[i]>'9')
-			name[i]+='a'-'9'-1;
+		name[i] = (saa711x_read(client, 0) & 0x0f) + '0';
+		if (name[i] > '9')
+			name[i] += 'a' - '9' - 1;
 	}
-	name[i]='\0';
+	name[i] = '\0';
 
 	saa711x_write(client, 0, 5);
 	chip_id = saa711x_read(client, 0) & 0x0f;
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index 4abf5c0..ffb0f64 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -1,10 +1,6 @@
 /*
  *   SAA713x ALSA support for V4L
  *
- *
- *   Caveats:
- *        - Volume doesn't work (it's always at max)
- *
  *   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, version 2
@@ -614,13 +610,18 @@
 	snd_card_saa7134_pcm_t *pcm;
 	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
 	struct saa7134_dev *dev = saa7134->dev;
-	int err;
+	int amux, err;
 
 	mutex_lock(&dev->dmasound.lock);
 
 	dev->dmasound.read_count  = 0;
 	dev->dmasound.read_offset = 0;
 
+	amux = dev->input->amux;
+	if ((amux < 1) || (amux > 3))
+		amux = 1;
+	dev->dmasound.input  =  amux - 1;
+
 	mutex_unlock(&dev->dmasound.lock);
 
 	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
@@ -713,6 +714,8 @@
 				  struct snd_ctl_elem_value * ucontrol)
 {
 	snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+	struct saa7134_dev *dev = chip->dev;
+
 	int change, addr = kcontrol->private_value;
 	int left, right;
 
@@ -727,10 +730,52 @@
 	if (right > 20)
 		right = 20;
 	spin_lock_irq(&chip->mixer_lock);
-	change = chip->mixer_volume[addr][0] != left ||
-		 chip->mixer_volume[addr][1] != right;
-	chip->mixer_volume[addr][0] = left;
-	chip->mixer_volume[addr][1] = right;
+	change = 0;
+	if (chip->mixer_volume[addr][0] != left) {
+		change = 1;
+		right = left;
+	}
+	if (chip->mixer_volume[addr][1] != right) {
+		change = 1;
+		left = right;
+	}
+	if (change) {
+		switch (dev->pci->device) {
+			case PCI_DEVICE_ID_PHILIPS_SAA7134:
+				switch (addr) {
+					case MIXER_ADDR_TVTUNER:
+						left = 20;
+						break;
+					case MIXER_ADDR_LINE1:
+						saa_andorb(SAA7134_ANALOG_IO_SELECT,  0x10,
+							   (left > 10) ? 0x00 : 0x10);
+						break;
+					case MIXER_ADDR_LINE2:
+						saa_andorb(SAA7134_ANALOG_IO_SELECT,  0x20,
+							   (left > 10) ? 0x00 : 0x20);
+						break;
+				}
+				break;
+			case PCI_DEVICE_ID_PHILIPS_SAA7133:
+			case PCI_DEVICE_ID_PHILIPS_SAA7135:
+				switch (addr) {
+					case MIXER_ADDR_TVTUNER:
+						left = 20;
+						break;
+					case MIXER_ADDR_LINE1:
+						saa_andorb(0x0594,  0x10,
+							   (left > 10) ? 0x00 : 0x10);
+						break;
+					case MIXER_ADDR_LINE2:
+						saa_andorb(0x0594,  0x20,
+							   (left > 10) ? 0x00 : 0x20);
+						break;
+				}
+				break;
+		}
+		chip->mixer_volume[addr][0] = left;
+		chip->mixer_volume[addr][1] = right;
+	}
 	spin_unlock_irq(&chip->mixer_lock);
 	return change;
 }
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 51f0cfd..4dead84 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -2462,14 +2462,17 @@
 			.vmux = 1,
 			.amux = TV,
 			.tv   = 1,
+			.gpio = 0x0000000,
 		},{
 			.name = name_comp1,
 			.vmux = 3,
 			.amux = LINE2,
+			.gpio = 0x0200000,
 		},{
 			.name = name_svideo,
 			.vmux = 8,
 			.amux = LINE2,
+			.gpio = 0x0200000,
 		}},
 		.radio = {
 			.name = name_radio,
@@ -3022,6 +3025,158 @@
 			.amux   = LINE1,
 		},
 	},
+	[SAA7134_BOARD_PINNACLE_PCTV_310i] = {
+		.name           = "Pinnacle PCTV 310i",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.gpiomask       = 0x000200000,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 4,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name = name_comp1,
+			.vmux = 1,
+			.amux = LINE2,
+		},{
+			.name = name_comp2,
+			.vmux = 0,
+			.amux = LINE2,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux   = TV,
+			.gpio   = 0x0200000,
+		},
+	},
+	[SAA7134_BOARD_AVERMEDIA_STUDIO_507] = {
+		/* Mikhail Fedotov <mo_fedotov@mail.ru> */
+		.name           = "Avermedia AVerTV Studio 507",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_FM1256_IH3,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		.gpiomask       = 0x03,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+			.gpio = 0x00,
+		},{
+			.name = name_comp1,
+			.vmux = 0,
+			.amux = LINE2,
+			.gpio = 0x00,
+		},{
+			.name = name_comp2,
+			.vmux = 3,
+			.amux = LINE2,
+			.gpio = 0x00,
+		},{
+			.name = name_svideo,
+			.vmux = 8,
+			.amux = LINE2,
+			.gpio = 0x00,
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux = LINE2,
+			.gpio = 0x01,
+		},
+		.mute  = {
+			.name = name_mute,
+			.amux = LINE1,
+			.gpio = 0x00,
+		},
+	},
+	[SAA7134_BOARD_VIDEOMATE_DVBT_200A] = {
+		/* Francis Barber <fedora@barber-family.id.au> */
+		.name           = "Compro Videomate DVB-T200A",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_ABSENT,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 3,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 1,
+			.amux   = LINE2,
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2,
+		}},
+	},
+	[SAA7134_BOARD_HAUPPAUGE_HVR1110] = {
+		/* Thomas Genty <tomlohave@gmail.com> */
+		.name           = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE2, /* FIXME: audio doesn't work on svideo/composite */
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2, /* FIXME: audio doesn't work on svideo/composite */
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux   = TV,
+		},
+	},
+	[SAA7134_BOARD_CINERGY_HT_PCMCIA] = {
+		.name           = "Terratec Cinergy HT PCMCIA",
+		.audio_clock    = 0x00187de7,
+		.tuner_type     = TUNER_PHILIPS_TDA8290,
+		.radio_type     = UNSET,
+		.tuner_addr	= ADDR_UNSET,
+		.radio_addr	= ADDR_UNSET,
+		.mpeg           = SAA7134_MPEG_DVB,
+		.inputs = {{
+			.name   = name_tv,
+			.vmux   = 1,
+			.amux   = TV,
+			.tv     = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 0,
+			.amux   = LINE1,
+		},{
+			.name   = name_svideo,
+			.vmux   = 6,
+			.amux   = LINE1,
+		}},
+	},
 };
 
 const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -3631,6 +3786,36 @@
 		.subdevice    = 0x4860,
 		.driver_data  = SAA7134_BOARD_ASUS_EUROPA2_HYBRID,
 	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x11bd,
+		.subdevice    = 0x002f,
+		.driver_data  = SAA7134_BOARD_PINNACLE_PCTV_310i,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1461, /* Avermedia Technologies Inc */
+		.subdevice    = 0x9715,
+		.driver_data  = SAA7134_BOARD_AVERMEDIA_STUDIO_507,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x1043,
+		.subdevice    = 0x4876,
+		.driver_data  = SAA7134_BOARD_ASUSTeK_P7131_DUAL,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x0070,
+		.subdevice    = 0x6701,
+		.driver_data  = SAA7134_BOARD_HAUPPAUGE_HVR1110,
+	},{
+		.vendor       = PCI_VENDOR_ID_PHILIPS,
+		.device       = PCI_DEVICE_ID_PHILIPS_SAA7133,
+		.subvendor    = 0x153b,
+		.subdevice    = 0x1172,
+		.driver_data  = SAA7134_BOARD_CINERGY_HT_PCMCIA,
+	},{
 		/* --- boards without eeprom + subsystem ID --- */
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
 		.device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -3717,6 +3902,7 @@
 	case SAA7134_BOARD_AVERMEDIA_305:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
 	case SAA7134_BOARD_AVERMEDIA_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
 	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
 	case SAA7134_BOARD_AVERMEDIA_777:
 /*      case SAA7134_BOARD_SABRENT_SBTTVFM:  */ /* not finished yet */
@@ -3725,6 +3911,7 @@
 	case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
 	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
 	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
 	case SAA7134_BOARD_MANLI_MTV001:
 	case SAA7134_BOARD_MANLI_MTV002:
 	case SAA7134_BOARD_BEHOLD_409FM:
@@ -3793,7 +3980,9 @@
 		break;
 	/* i2c remotes */
 	case SAA7134_BOARD_PINNACLE_PCTV_110i:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
 	case SAA7134_BOARD_UPMOST_PURPLE_TV:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
 		dev->has_remote = SAA7134_REMOTE_I2C;
 		break;
 	case SAA7134_BOARD_AVERMEDIA_A169_B:
@@ -3924,9 +4113,11 @@
 		}
 		break;
 	case SAA7134_BOARD_PHILIPS_TIGER:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
 	case SAA7134_BOARD_TEVION_DVBT_220RF:
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
 	case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
 		/* this is a hybrid board, initialize to analog mode
 		 * and configure firmware eeprom address
 		 */
@@ -3952,6 +4143,14 @@
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
 		}
 		break;
+	case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+		/* make the tda10046 find its eeprom */
+		{
+		u8 data[] = { 0x3c, 0x33, 0x60};
+		struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+		i2c_transfer(&dev->i2c_adap, &msg, 1);
+		}
+		break;
 	case SAA7134_BOARD_KWORLD_ATSC110:
 		{
 			/* enable tuner */
@@ -3964,6 +4163,29 @@
 					       dev->name, i);
 		}
 		break;
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+		/* The T200 and the T200A share the same pci id.  Consequently,
+		 * we are going to query eeprom to try to find out which one we
+		 * are actually looking at. */
+
+		/* Don't do this if the board was specifically selected with an
+		 * insmod option or if we have the default configuration T200*/
+		if(!dev->autodetected || (dev->eedata[0x41] == 0xd0))
+			break;
+		if(dev->eedata[0x41] == 0x02) {
+			/* Reconfigure board  as T200A */
+			dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A;
+			dev->tuner_type   = saa7134_boards[dev->board].tuner_type;
+			dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+			printk(KERN_INFO "%s: Reconfigured board as %s\n",
+				dev->name, saa7134_boards[dev->board].name);
+		} else {
+			printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n",
+				dev->name, dev->eedata[0x41]);
+			break;
+		}
+		break;
 	}
 	return 0;
 }
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index 5c9e63d..ed038ff 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -889,15 +889,16 @@
 		must_configure_manually();
 		dev->board = SAA7134_BOARD_UNKNOWN;
 	}
+	dev->autodetected = card[dev->nr] != dev->board;
 	dev->tuner_type   = saa7134_boards[dev->board].tuner_type;
 	dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
 	if (UNSET != tuner[dev->nr])
 		dev->tuner_type = tuner[dev->nr];
-	printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
-	       dev->name,pci_dev->subsystem_vendor,
-	       pci_dev->subsystem_device,saa7134_boards[dev->board].name,
-	       dev->board, card[dev->nr] == dev->board ?
-	       "insmod option" : "autodetected");
+		printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+		dev->name,pci_dev->subsystem_vendor,
+		pci_dev->subsystem_device,saa7134_boards[dev->board].name,
+		dev->board, dev->autodetected ?
+		"autodetected" : "insmod option");
 
 	/* get mmio */
 	if (!request_mem_region(pci_resource_start(pci_dev,0),
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 6b61d9b..fa83398 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -50,6 +50,10 @@
 module_param(antenna_pwr, int, 0444);
 MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
 
+static int use_frontent = 0;
+module_param(use_frontent, int, 0644);
+MODULE_PARM_DESC(use_frontent,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
+
 /* ------------------------------------------------------------------ */
 static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
 {
@@ -293,7 +297,7 @@
 	return philips_tda6651_pll_set(0x60, fe, params);
 }
 
-static int philips_tu1216_request_firmware(struct dvb_frontend *fe,
+static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
 					   const struct firmware **fw, char *name)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
@@ -308,7 +312,7 @@
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.if_freq       = TDA10046_FREQ_3617,
-	.request_firmware = philips_tu1216_request_firmware,
+	.request_firmware = philips_tda1004x_request_firmware,
 };
 
 /* ------------------------------------------------------------------ */
@@ -331,12 +335,12 @@
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.if_freq       = TDA10046_FREQ_3617,
-	.request_firmware = philips_tu1216_request_firmware,
+	.request_firmware = philips_tda1004x_request_firmware,
 };
 
 /* ------------------------------------------------------------------ */
 
-static int philips_europa_tuner_init(struct dvb_frontend *fe)
+static int philips_td1316_tuner_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
 	static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
@@ -347,18 +351,8 @@
 		fe->ops.i2c_gate_ctrl(fe, 1);
 	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
 		return -EIO;
-	msleep(1);
-
-	/* switch the board to dvb mode */
-	init_msg.addr = 0x43;
-	init_msg.len  = 0x02;
-	msg[0] = 0x00;
-	msg[1] = 0x40;
 	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
-		return -EIO;
-
+		fe->ops.i2c_gate_ctrl(fe, 0);
 	return 0;
 }
 
@@ -367,6 +361,22 @@
 	return philips_tda6651_pll_set(0x61, fe, params);
 }
 
+static int philips_europa_tuner_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	static u8 msg[] = { 0x00, 0x40};
+	struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+
+	if (philips_td1316_tuner_init(fe))
+		return -EIO;
+	msleep(1);
+	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
 static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
@@ -671,7 +681,7 @@
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 };
@@ -812,6 +822,27 @@
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 	i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+	return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	static u8 tda8290_close[] = { 0x21, 0xc0};
+	static u8 tda8290_open[]  = { 0x21, 0x80};
+	struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
+	if (enable) {
+		tda8290_msg.buf = tda8290_close;
+	} else {
+		tda8290_msg.buf = tda8290_open;
+	}
+	if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1)
+		return -EIO;
+	msleep(20);
 	return 0;
 }
 
@@ -820,24 +851,11 @@
 static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
 {
 	int ret;
-	struct saa7134_dev *dev = fe->dvb->priv;
-	static u8 tda8290_close[] = { 0x21, 0xc0};
-	static u8 tda8290_open[]  = { 0x21, 0x80};
-	struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
 
-	/* close tda8290 i2c bridge */
-	tda8290_msg.buf = tda8290_close;
-	ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
-	if (ret != 1)
-		return -EIO;
-	msleep(20);
 	ret = philips_tda827xa_pll_set(0x61, fe, params);
 	if (ret != 0)
 		return ret;
-	/* open tda8290 i2c bridge */
-	tda8290_msg.buf = tda8290_open;
-	i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
-	return ret;
+	return 0;
 }
 
 static int philips_tiger_tuner_init(struct dvb_frontend *fe)
@@ -867,13 +885,80 @@
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.if_freq       = TDA10046_FREQ_045,
+	.request_firmware = NULL,
+};
+/* ------------------------------------------------------------------ */
+
+static int cinergy_ht_tuner_init(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	static u8 data[] = { 0x3c, 0x33, 0x62};
+	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+
+	if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe)
+{
+	struct saa7134_dev *dev = fe->dvb->priv;
+	static u8 data[] = { 0x3c, 0x33, 0x60};
+	struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+
+	i2c_transfer(&dev->i2c_adap, &msg, 1);
+	philips_tda827xa_tuner_sleep( 0x61, fe);
+	return 0;
+}
+
+static struct tda1004x_config cinergy_ht_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X_GP01,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 };
 
 /* ------------------------------------------------------------------ */
 
+static struct tda1004x_config pinnacle_pctv_310i_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.if_freq       = TDA10046_FREQ_045,
+	.request_firmware = philips_tda1004x_request_firmware,
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config hauppauge_hvr_1110_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.if_freq       = TDA10046_FREQ_045,
+	.request_firmware = philips_tda1004x_request_firmware,
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config asus_p7131_dual_config = {
+	.demod_address = 0x08,
+	.invert        = 1,
+	.invert_oclk   = 0,
+	.xtal_freq     = TDA10046_XTAL_16M,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
+	.if_freq       = TDA10046_FREQ_045,
+	.request_firmware = philips_tda1004x_request_firmware,
+};
+
 static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe)
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
@@ -921,7 +1006,7 @@
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GPL,
+	.agc_config    = TDA10046_AGC_TDA827X_GP00,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 };
@@ -958,7 +1043,7 @@
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GPL,
+	.agc_config    = TDA10046_AGC_TDA827X_GP00,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 };
@@ -983,7 +1068,7 @@
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 };
@@ -1028,7 +1113,7 @@
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 };
@@ -1168,6 +1253,29 @@
 					       &philips_tiger_config,
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
+			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+			dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
+			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
+			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
+		}
+		break;
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
+		dev->dvb.frontend = dvb_attach(tda10046_attach,
+					       &pinnacle_pctv_310i_config,
+					       &dev->i2c_adap);
+		if (dev->dvb.frontend) {
+			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+			dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
+			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
+			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
+		}
+		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+		dev->dvb.frontend = dvb_attach(tda10046_attach,
+					       &hauppauge_hvr_1110_config,
+					       &dev->i2c_adap);
+		if (dev->dvb.frontend) {
+			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
 			dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
 			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
@@ -1175,9 +1283,10 @@
 		break;
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
 		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &philips_tiger_config,
+					       &asus_p7131_dual_config,
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
+			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
 			dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init;
 			dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep;
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
@@ -1194,12 +1303,27 @@
 		}
 		break;
 	case SAA7134_BOARD_FLYDVB_TRIO:
-		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &lifeview_trio_config,
-					       &dev->i2c_adap);
-		if (dev->dvb.frontend) {
-			dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep;
-			dev->dvb.frontend->ops.tuner_ops.set_params = lifeview_trio_tuner_set_params;
+		if(! use_frontent) {	//terrestrial
+			dev->dvb.frontend = dvb_attach(tda10046_attach,
+						       &lifeview_trio_config,
+						       &dev->i2c_adap);
+			if (dev->dvb.frontend) {
+				dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep;
+				dev->dvb.frontend->ops.tuner_ops.set_params =
+								lifeview_trio_tuner_set_params;
+			}
+		} else {  	      //satellite
+			dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
+			if (dev->dvb.frontend) {
+				if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63,
+									&dev->i2c_adap, 0) == NULL) {
+					printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__);
+				}
+				if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap,
+										0x08, 0, 0) == NULL) {
+					printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__);
+				}
+			}
 		}
 		break;
 	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
@@ -1281,7 +1405,27 @@
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params;
 		}
 		break;
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+		dev->dvb.frontend = dvb_attach(tda10046_attach,
+				&philips_europa_config,
+				&dev->i2c_adap);
+		if (dev->dvb.frontend) {
+			dev->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init;
+			dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+		}
+		break;
+	case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+		dev->dvb.frontend = dvb_attach(tda10046_attach,
+					       &cinergy_ht_config,
+					       &dev->i2c_adap);
+		if (dev->dvb.frontend) {
+			dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+			dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init;
+			dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep;
+			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
 
+		}
+		break;
 	default:
 		printk("%s: Huh? unknown DVB card?\n",dev->name);
 		break;
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index 6162550..6f9fe86 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -341,6 +341,7 @@
 	switch (client->addr) {
 		case 0x7a:
 		case 0x47:
+		case 0x71:
 		{
 			struct IR_i2c *ir = i2c_get_clientdata(client);
 			d1printk("%s i2c IR detected (%s).\n",
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index dee8355..60b38de 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -112,6 +112,27 @@
 	return 1;
 }
 
+static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	unsigned char buf[5], cod4, code3, code4;
+
+	/* poll IR chip */
+	if (5 != i2c_master_recv(&ir->c,buf,5))
+		return -EIO;
+
+	cod4	= buf[4];
+	code4	= (cod4 >> 2);
+	code3	= buf[3];
+	if (code3 == 0)
+		/* no key pressed */
+		return 0;
+
+	/* return key */
+	*ir_key = code4;
+	*ir_raw = code4;
+	return 1;
+}
+
 void saa7134_input_irq(struct saa7134_dev *dev)
 {
 	struct saa7134_ir *ir = dev->remote;
@@ -131,6 +152,23 @@
 	mod_timer(&ir->timer, timeout);
 }
 
+static void saa7134_ir_start(struct saa7134_dev *dev, struct saa7134_ir *ir)
+{
+	if (ir->polling) {
+		init_timer(&ir->timer);
+		ir->timer.function = saa7134_input_timer;
+		ir->timer.data     = (unsigned long)dev;
+		ir->timer.expires  = jiffies + HZ;
+		add_timer(&ir->timer);
+	}
+}
+
+static void saa7134_ir_stop(struct saa7134_dev *dev)
+{
+	if (dev->remote->polling)
+		del_timer_sync(&dev->remote->timer);
+}
+
 int saa7134_input_init1(struct saa7134_dev *dev)
 {
 	struct saa7134_ir *ir;
@@ -141,6 +179,7 @@
 	u32 mask_keyup   = 0;
 	int polling      = 0;
 	int ir_type      = IR_TYPE_OTHER;
+	int err;
 
 	if (dev->has_remote != SAA7134_REMOTE_GPIO)
 		return -ENODEV;
@@ -184,6 +223,7 @@
 	case SAA7134_BOARD_AVERMEDIA_307:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
 	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
 		ir_codes     = ir_codes_avermedia;
 		mask_keycode = 0x0007C8;
@@ -266,9 +306,8 @@
 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	if (!ir || !input_dev) {
-		kfree(ir);
-		input_free_device(input_dev);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto err_out_free;
 	}
 
 	ir->dev = input_dev;
@@ -299,18 +338,22 @@
 	}
 	input_dev->cdev.dev = &dev->pci->dev;
 
-	/* all done */
 	dev->remote = ir;
-	if (ir->polling) {
-		init_timer(&ir->timer);
-		ir->timer.function = saa7134_input_timer;
-		ir->timer.data     = (unsigned long)dev;
-		ir->timer.expires  = jiffies + HZ;
-		add_timer(&ir->timer);
-	}
+	saa7134_ir_start(dev, ir);
 
-	input_register_device(ir->dev);
+	err = input_register_device(ir->dev);
+	if (err)
+		goto err_out_stop;
+
 	return 0;
+
+ err_out_stop:
+	saa7134_ir_stop(dev);
+	dev->remote = NULL;
+ err_out_free:
+	input_free_device(input_dev);
+	kfree(ir);
+	return err;
 }
 
 void saa7134_input_fini(struct saa7134_dev *dev)
@@ -318,8 +361,7 @@
 	if (NULL == dev->remote)
 		return;
 
-	if (dev->remote->polling)
-		del_timer_sync(&dev->remote->timer);
+	saa7134_ir_stop(dev);
 	input_unregister_device(dev->remote->dev);
 	kfree(dev->remote);
 	dev->remote = NULL;
@@ -335,6 +377,7 @@
 
 	switch (dev->board) {
 	case SAA7134_BOARD_PINNACLE_PCTV_110i:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
 		snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV");
 		if (pinnacle_remote == 0) {
 			ir->get_key   = get_key_pinnacle_color;
@@ -349,6 +392,11 @@
 		ir->get_key   = get_key_purpletv;
 		ir->ir_codes  = ir_codes_purpletv;
 		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+		snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110");
+		ir->get_key   = get_key_hvr1110;
+		ir->ir_codes  = ir_codes_hauppauge_new;
+		break;
 	default:
 		dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board);
 		break;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 7cf96b4..e88ad7b 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -227,6 +227,11 @@
 #define SAA7134_BOARD_PROTEUS_2309 98
 #define SAA7134_BOARD_AVERMEDIA_A16AR   99
 #define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100
+#define SAA7134_BOARD_PINNACLE_PCTV_310i  101
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200A  103
+#define SAA7134_BOARD_HAUPPAUGE_HVR1110    104
+#define SAA7134_BOARD_CINERGY_HT_PCMCIA    105
 
 #define SAA7134_MAXBOARDS 8
 #define SAA7134_INPUT_MAX 8
@@ -446,6 +451,9 @@
 	struct v4l2_prio_state     prio;
 #endif
 
+	/* insmod option/autodetected */
+	int                        autodetected;
+
 	/* various device info */
 	unsigned int               resources;
 	struct video_device        *video_dev;
diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c
index 6d1ef1e..a1ec3ac 100644
--- a/drivers/media/video/stv680.c
+++ b/drivers/media/video/stv680.c
@@ -687,7 +687,7 @@
 		stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
 		if (stv680->sbuf[i].data == NULL) {
 			PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
-			return -1;
+			goto nomem_err;
 		}
 	}
 
@@ -698,7 +698,7 @@
 		stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
 		if (stv680->scratch[i].data == NULL) {
 			PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
-			return -1;
+			goto nomem_err;
 		}
 		stv680->scratch[i].state = BUFFER_UNUSED;
 	}
@@ -706,7 +706,7 @@
 	for (i = 0; i < STV680_NUMSBUF; i++) {
 		urb = usb_alloc_urb (0, GFP_KERNEL);
 		if (!urb)
-			return -ENOMEM;
+			goto nomem_err;
 
 		/* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
 		usb_fill_bulk_urb (urb, stv680->udev,
@@ -721,6 +721,21 @@
 
 	stv680->framecount = 0;
 	return 0;
+
+ nomem_err:
+	for (i = 0; i < STV680_NUMSCRATCH; i++) {
+		kfree(stv680->scratch[i].data);
+		stv680->scratch[i].data = NULL;
+	}
+	for (i = 0; i < STV680_NUMSBUF; i++) {
+		usb_kill_urb(stv680->urb[i]);
+		usb_free_urb(stv680->urb[i]);
+		stv680->urb[i] = NULL;
+		kfree(stv680->sbuf[i].data);
+		stv680->sbuf[i].data = NULL;
+	}
+	return -ENOMEM;
+
 }
 
 static int stv680_stop_stream (struct usb_stv *stv680)
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c
index 87ffb0e..fde576f 100644
--- a/drivers/media/video/tda9887.c
+++ b/drivers/media/video/tda9887.c
@@ -482,6 +482,12 @@
 		buf[1] &= ~cQSS;
 	if (t->tda9887_config & TDA9887_GATING_18)
 		buf[3] &= ~cGating_36;
+
+	if (t->tda9887_config & TDA9887_GAIN_NORMAL) {
+		radio_stereo.e &= ~cTunerGainLow;
+		radio_mono.e &= ~cTunerGainLow;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 40590ba..705daaa 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -443,6 +443,10 @@
 			printk("%02x ",buffer[i]);
 		printk("\n");
 	}
+	/* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
+	if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
+		return -ENODEV;
+
 	/* autodetection code based on the i2c addr */
 	if (!no_autodetect) {
 		switch (addr) {
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c
index 63db4e9..1b9b074 100644
--- a/drivers/media/video/tuner-simple.c
+++ b/drivers/media/video/tuner-simple.c
@@ -108,6 +108,7 @@
 		case TUNER_PHILIPS_FM1216ME_MK3:
 		case TUNER_PHILIPS_FM1236_MK3:
 		case TUNER_PHILIPS_FM1256_IH3:
+		case TUNER_LG_NTSC_TAPE:
 			stereo = ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
 			break;
 		default:
@@ -421,6 +422,7 @@
 	case TUNER_PHILIPS_FM1216ME_MK3:
 	case TUNER_PHILIPS_FM1236_MK3:
 	case TUNER_PHILIPS_FMD1216ME_MK3:
+	case TUNER_LG_NTSC_TAPE:
 		buffer[3] = 0x19;
 		break;
 	case TUNER_TNF_5335MF:
@@ -465,6 +467,8 @@
 			config |= TDA9887_INTERCARRIER;
 /*		if (params->port1_set_for_fm_mono)
 			config &= ~TDA9887_PORT1_ACTIVE;*/
+		if (params->fm_gain_normal)
+			config |= TDA9887_GAIN_NORMAL;
 		i2c_clients_command(c->adapter, TDA9887_SET_CONFIG, &config);
 	}
 	if (4 != (rc = i2c_master_send(c,buffer,4)))
diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c
index 7816823..74c3e6f 100644
--- a/drivers/media/video/tuner-types.c
+++ b/drivers/media/video/tuner-types.c
@@ -651,6 +651,7 @@
 		.has_tda9887 = 1,
 		.port1_invert_for_secam_lc = 1,
 		.default_pll_gating_18 = 1,
+		.fm_gain_normal=1,
 	},
 };
 
@@ -672,16 +673,6 @@
 	},
 };
 
-/* ------------ TUNER_LG_NTSC_TAPE - LGINNOTEK NTSC ------------ */
-
-static struct tuner_params tuner_lg_ntsc_tape_params[] = {
-	{
-		.type   = TUNER_PARAM_TYPE_NTSC,
-		.ranges = tuner_fm1236_mk3_ntsc_ranges,
-		.count  = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
-	},
-};
-
 /* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */
 
 static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = {
@@ -1331,8 +1322,8 @@
 	},
 	[TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */
 		.name   = "LG NTSC (TAPE series)",
-		.params = tuner_lg_ntsc_tape_params,
-		.count  = ARRAY_SIZE(tuner_lg_ntsc_tape_params),
+		.params = tuner_fm1236_mk3_params,
+		.count  = ARRAY_SIZE(tuner_fm1236_mk3_params),
 	},
 	[TUNER_TNF_8831BGFF] = { /* Philips PAL */
 		.name   = "Tenna TNF 8831 BGFF)",
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index 6b9ef73..2624e3f 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -430,7 +430,7 @@
 			tvee->has_radio = eeprom_data[i+len-1];
 			/* old style tag, don't know how to detect
 			IR presence, mark as unknown. */
-			tvee->has_ir = 2;
+			tvee->has_ir = -1;
 			tvee->model =
 				eeprom_data[i+8] +
 				(eeprom_data[i+9] << 8);
@@ -653,13 +653,14 @@
 			STRM(decoderIC, tvee->decoder_processor),
 			tvee->decoder_processor);
 	}
-	if (tvee->has_ir == 2)
+	if (tvee->has_ir == -1)
 		tveeprom_info("has %sradio\n",
 				tvee->has_radio ? "" : "no ");
 	else
-		tveeprom_info("has %sradio, has %sIR remote\n",
+		tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n",
 				tvee->has_radio ? "" : "no ",
-				tvee->has_ir ? "" : "no ");
+				(tvee->has_ir & 1) ? "" : "no ",
+				(tvee->has_ir & 2) ? "" : "no ");
 }
 EXPORT_SYMBOL(tveeprom_hauppauge_analog);
 
diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c
index bbf2bee..ec0ff22 100644
--- a/drivers/media/video/usbvideo/quickcam_messenger.c
+++ b/drivers/media/video/usbvideo/quickcam_messenger.c
@@ -86,6 +86,7 @@
 static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
 {
 	struct input_dev *input_dev;
+	int error;
 
 	usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
 	strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
@@ -106,7 +107,13 @@
 
 	input_dev->private = cam;
 
-	input_register_device(cam->input);
+	error = input_register_device(cam->input);
+	if (error) {
+		warn("Failed to register camera's input device, err: %d\n",
+		     error);
+		input_free_device(cam->input);
+		cam->input = NULL;
+	}
 }
 
 static void qcm_unregister_input(struct qcm *cam)
diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig
new file mode 100644
index 0000000..fc24ef0
--- /dev/null
+++ b/drivers/media/video/usbvision/Kconfig
@@ -0,0 +1,12 @@
+config VIDEO_USBVISION
+	tristate "USB video devices based on Nogatech NT1003/1004/1005"
+	depends on I2C && VIDEO_V4L2
+	select VIDEO_TUNER
+	select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+	---help---
+	  There are more than 50 different USB video devices based on
+	  NT1003/1004/1005 USB Bridges. This driver enables using those
+	  devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbvision.
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
new file mode 100644
index 0000000..9ac92a8
--- /dev/null
+++ b/drivers/media/video/usbvision/Makefile
@@ -0,0 +1,5 @@
+usbvision-objs  := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o
+
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c
new file mode 100644
index 0000000..134eb98
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-cards.c
@@ -0,0 +1,157 @@
+/*
+ * USBVISION.H
+ *  usbvision header file
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * 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/list.h>
+#include <linux/i2c.h>
+#include <media/v4l2-dev.h>
+#include <media/tuner.h>
+#include "usbvision.h"
+
+/* Supported Devices: A table for usbvision.c*/
+struct usbvision_device_data_st  usbvision_device_data[] = {
+	{0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"},
+	{0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1, -1, -1, -1, "Xanboo"},
+	{0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL,   1, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Belkin USBView II"},
+	{0x0571, 0x0002,  0, CODEC_SAA7111, 2, V4L2_STD_PAL,   0, 0, 1, 0, 0,                          -1, -1, -1, -1,  7, "echoFX InterView Lite"},
+	{0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"},
+	{0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC,  0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "D-Link V100"},
+	{0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1, -1, -1, -1, "X10 USB Camera"},
+	{0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL,   1, 0, 1, 0, 0,                          -1, -1, -1,  3,  7, "Osprey 50"},
+	{0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC,	 0, 0, 1, 0, 0,				 -1, -1,  0,  3,  7, "Hauppauge USB-Live Model 600"},
+	{0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 	 2, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"},
+	{0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"},
+	{0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"},
+	{0x0573, 0x4450,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "PixelView PlayTv-USB PRO (PAL) FM"},
+	{0x0573, 0x4550,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "ZTV ZT-721 2.4GHz USB A/V Receiver"},
+	{0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"},
+	{0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
+	{0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"},
+	{0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "},
+	{0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"},
+	{0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
+	{0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"},
+	{0x0573, 0x4d2a,  0, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_MICROTUNE_4049FM5,    -1, -1,  0,  3,  7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"},
+	{0x0573, 0x4d2b,  0, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_MICROTUNE_4049FM5,    -1, -1,  0,  3,  7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"},
+	{0x0573, 0x4d2c,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1,  0,  3,  7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"},
+	{0x0573, 0x4d20,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"},
+	{0x0573, 0x4d21,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB II (PAL)"},
+	{0x0573, 0x4d22,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB II (PAL) MODEL 566"},
+	{0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1,  0,  3,  7, "Hauppauge WinTv-USB (SECAM) 4D23"},
+	{0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1,  0,  3,  7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"},
+	{0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1,  0,  3,  7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"},
+	{0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL,       -1, -1,  0,  3,  7, "Hauppauge WinTv-USB Model 40204 Rev B281"},
+	{0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL,       -1, -1,  0,  3,  7, "Hauppauge WinTv-USB Model 40204 Rev B283"},
+	{0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB Model 40205 Rev B298"},
+	{0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1,  0,  3,  7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"},
+	{0x0573, 0x4d31,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB III (PAL) FM Model 568"},
+	{0x0573, 0x4d32,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,  0,  3,  7, "Hauppauge WinTv-USB III (PAL) FM Model 573"},
+	{0x0573, 0x4d35,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_MICROTUNE_4049FM5,    -1, -1,  0,  3,  7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"},
+	{0x0573, 0x4d37,  0, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1,  0,  3,  7, "Hauppauge WinTV USB device Model 40219 Rev E189"},
+	{0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1,  5,  5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"},
+	{0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL,   0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Digital Video Creator I"},
+	{0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC,  0, 0, 1, 0, 0,   			 -1, -1, 82, 20,  7, "Global Village GV-007 (NTSC)"},
+	{0x07d0, 0x0003,  0, CODEC_SAA7113, 2, V4L2_STD_NTSC,  0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"},
+	{0x07d0, 0x0004,  0, CODEC_SAA7113, 2, V4L2_STD_PAL,   0, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"},
+	{0x07d0, 0x0005,  0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0,				 -1, -1,  0,  3,  7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"},
+	{0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I,  -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"},
+	{0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM,        -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"},
+	{0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_PHILIPS_PAL,          -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"},
+	{0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL,   1, 0, 1, 1, TUNER_PHILIPS_PAL,          -1, -1, -1, -1, -1, "Miro PCTV USB"},
+	{0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"},
+	{0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL,    -1, -1,  0,  3,  7, "Pinnacle Studio PCTV USB (PAL) FM"},
+	{0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC,  1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC,   -1, -1,  0,  3,  7, "Pinnacle Studio PCTV USB (NTSC) FM"},
+	{0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL,    -1, -1,  0,  3,  7, "Pinnacle Studio PCTV USB (PAL) FM"},
+	{0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC,  1, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Pinnacle Studio Linx Video input cable (NTSC)"},
+	{0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL,   1, 0, 1, 0, 0,                          -1, -1,  0,  3,  7, "Pinnacle Studio Linx Video input cable (PAL)"},
+	{0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL,   1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL,    -1, -1,  0,  3,  7, "Pinnacle PCTV Bungee USB (PAL) FM"},
+	{0x2400, 0x4200, -1, CODEC_SAA7111, 3, VIDEO_MODE_NTSC,  1, 0, 1, 1, TUNER_PHILIPS_NTSC_M,       -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"},
+	{}  /* Terminating entry */
+};
+
+/* Supported Devices */
+
+struct usb_device_id usbvision_table [] = {
+	{ USB_DEVICE(0xFFF0, 0xFFF0) },  /* Custom Dummy USBVision Device */
+	{ USB_DEVICE(0x0A6F, 0x0400) },  /* Xanboo */
+	{ USB_DEVICE(0x050d, 0x0208) },  /* Belkin USBView II */
+	{ USB_DEVICE(0x0571, 0x0002) },  /* echoFX InterView Lite */
+	{ USB_DEVICE(0x0573, 0x0003) },  /* USBGear USBG-V1 */
+	{ USB_DEVICE(0x0573, 0x0400) },  /* D-Link V100 */
+	{ USB_DEVICE(0x0573, 0x2000) },  /* X10 USB Camera */
+	{ USB_DEVICE(0x0573, 0x2d00) },  /* Osprey 50 */
+	{ USB_DEVICE(0x0573, 0x2d01) },  /* Hauppauge USB-Live Model 600 */
+	{ USB_DEVICE(0x0573, 0x2101) },  /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */
+	{ USB_DEVICE(0x0573, 0x4100) },  /* Nogatech USB-TV FM (NTSC) */
+	{ USB_DEVICE(0x0573, 0x4110) },  /* PNY USB-TV (NTSC) FM */
+	{ USB_DEVICE(0x0573, 0x4450) },  /* PixelView PlayTv-USB PRO (PAL) FM */
+	{ USB_DEVICE(0x0573, 0x4550) },  /* ZTV ZT-721 2.4GHz USB A/V Receiver */
+	{ USB_DEVICE(0x0573, 0x4d00) },  /* Hauppauge WinTv-USB USA */
+	{ USB_DEVICE(0x0573, 0x4d01) },  /* Hauppauge WinTv-USB */
+	{ USB_DEVICE(0x0573, 0x4d02) },  /* Hauppauge WinTv-USB UK */
+	{ USB_DEVICE(0x0573, 0x4d03) },  /* Hauppauge WinTv-USB France */
+	{ USB_DEVICE(0x0573, 0x4d10) },  /* Hauppauge WinTv-USB with FM USA radio */
+	{ USB_DEVICE(0x0573, 0x4d11) },  /* Hauppauge WinTv-USB (PAL) with FM radio */
+	{ USB_DEVICE(0x0573, 0x4d12) },  /* Hauppauge WinTv-USB UK with FM Radio */
+	{ USB_DEVICE(0x0573, 0x4d2a) },  /* Hauppague WinTv USB Model 602 40201 Rev B285 */
+	{ USB_DEVICE(0x0573, 0x4d2b) },  /* Hauppague WinTv USB Model 602 40201 Rev B282 */
+	{ USB_DEVICE(0x0573, 0x4d2c) },  /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/
+	{ USB_DEVICE(0x0573, 0x4d20) },  /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */
+	{ USB_DEVICE(0x0573, 0x4d21) },  /* Hauppauge WinTv-USB II (PAL) with FM radio*/
+	{ USB_DEVICE(0x0573, 0x4d22) },  /* Hauppauge WinTv-USB II (PAL) Model 566 */
+	{ USB_DEVICE(0x0573, 0x4d23) },  /* Hauppauge WinTv-USB France 4D23*/
+	{ USB_DEVICE(0x0573, 0x4d25) },  /* Hauppauge WinTv-USB Model 40209 rev B234 */
+	{ USB_DEVICE(0x0573, 0x4d26) },  /* Hauppauge WinTv-USB Model 40209 Rev B243 */
+	{ USB_DEVICE(0x0573, 0x4d27) },  /* Hauppauge WinTv-USB Model 40204 Rev B281 */
+	{ USB_DEVICE(0x0573, 0x4d28) },  /* Hauppauge WinTv-USB Model 40204 Rev B283 */
+	{ USB_DEVICE(0x0573, 0x4d29) },  /* Hauppauge WinTv-USB Model 40205 Rev B298 */
+	{ USB_DEVICE(0x0573, 0x4d30) },  /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */
+	{ USB_DEVICE(0x0573, 0x4d31) },  /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */
+	{ USB_DEVICE(0x0573, 0x4d32) },  /* Hauppauge WinTv-USB III (PAL) FM Model 573 */
+	{ USB_DEVICE(0x0573, 0x4d35) },  /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */
+	{ USB_DEVICE(0x0573, 0x4d37) },  /* Hauppauge WinTv-USB Model 40219 Rev E189 */
+	{ USB_DEVICE(0x0768, 0x0006) },  /* Camtel Technology USB TV Genie Pro FM Model TVB330 */
+	{ USB_DEVICE(0x07d0, 0x0001) },  /* Digital Video Creator I */
+	{ USB_DEVICE(0x07d0, 0x0002) },  /* Global Village GV-007 (NTSC) */
+	{ USB_DEVICE(0x07d0, 0x0003) },  /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */
+	{ USB_DEVICE(0x07d0, 0x0004) },  /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */
+	{ USB_DEVICE(0x07d0, 0x0005) },  /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */
+	{ USB_DEVICE(0x2304, 0x010d) },  /* Pinnacle Studio PCTV USB (PAL) */
+	{ USB_DEVICE(0x2304, 0x0109) },  /* Pinnacle Studio PCTV USB (SECAM) */
+	{ USB_DEVICE(0x2304, 0x0110) },  /* Pinnacle Studio PCTV USB (PAL) */
+	{ USB_DEVICE(0x2304, 0x0111) },  /* Miro PCTV USB */
+	{ USB_DEVICE(0x2304, 0x0112) },  /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
+	{ USB_DEVICE(0x2304, 0x0210) },  /* Pinnacle Studio PCTV USB (PAL) with FM radio */
+	{ USB_DEVICE(0x2304, 0x0212) },  /* Pinnacle Studio PCTV USB (NTSC) with FM radio */
+	{ USB_DEVICE(0x2304, 0x0214) },  /* Pinnacle Studio PCTV USB (PAL) with FM radio */
+	{ USB_DEVICE(0x2304, 0x0300) },  /* Pinnacle Studio Linx Video input cable (NTSC) */
+	{ USB_DEVICE(0x2304, 0x0301) },  /* Pinnacle Studio Linx Video input cable (PAL) */
+	{ USB_DEVICE(0x2304, 0x0419) },  /* Pinnacle PCTV Bungee USB (PAL) FM */
+
+	{ USB_DEVICE(0x2400, 0x4200) },  /* Hauppauge WinTv-USB2 Model 42012 */
+
+	{ }  /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usbvision_table);
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
new file mode 100644
index 0000000..797b97b
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -0,0 +1,2554 @@
+/*
+ * usbvision-core.c - driver for NT100x USB video capture devices
+ *
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *                         Dwaine Garden <dwainegarden@rogers.com>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/highmem.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/videodev2.h>
+#include <linux/video_decoder.h>
+#include <linux/i2c.h>
+
+#include <media/saa7115.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/audiochip.h>
+
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "usbvision.h"
+
+static unsigned int core_debug = 0;
+module_param(core_debug,int,0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int force_testpattern = 0;
+module_param(force_testpattern,int,0644);
+MODULE_PARM_DESC(force_testpattern,"enable test pattern display [core]");
+
+static int adjustCompression = 1;			// Set the compression to be adaptive
+module_param(adjustCompression, int, 0444);
+MODULE_PARM_DESC(adjustCompression, " Set the ADPCM compression for the device.  Default: 1 (On)");
+
+static int SwitchSVideoInput = 0;			// To help people with Black and White output with using s-video input.  Some cables and input device are wired differently.
+module_param(SwitchSVideoInput, int, 0444);
+MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input.  Some cables and input device are wired differently. Default: 0 (Off)");
+
+#define	ENABLE_HEXDUMP	0	/* Enable if you need it */
+
+
+#ifdef USBVISION_DEBUG
+	#define PDEBUG(level, fmt, args...) \
+		if (core_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
+#else
+	#define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+#define DBG_HEADER	1<<0
+#define DBG_IRQ		1<<1
+#define DBG_ISOC	1<<2
+#define DBG_PARSE	1<<3
+#define DBG_SCRATCH	1<<4
+#define DBG_FUNC	1<<5
+
+static const int max_imgwidth = MAX_FRAME_WIDTH;
+static const int max_imgheight = MAX_FRAME_HEIGHT;
+static const int min_imgwidth = MIN_FRAME_WIDTH;
+static const int min_imgheight = MIN_FRAME_HEIGHT;
+
+/* The value of 'scratch_buf_size' affects quality of the picture
+ * in many ways. Shorter buffers may cause loss of data when client
+ * is too slow. Larger buffers are memory-consuming and take longer
+ * to work with. This setting can be adjusted, but the default value
+ * should be OK for most desktop users.
+ */
+#define DEFAULT_SCRATCH_BUF_SIZE	(0x20000)		// 128kB memory scratch buffer
+static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE;
+
+// Function prototypes
+static int usbvision_request_intra (struct usb_usbvision *usbvision);
+static int usbvision_unrequest_intra (struct usb_usbvision *usbvision);
+static int usbvision_adjust_compression (struct usb_usbvision *usbvision);
+static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision);
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+/*
+ * Here we want the physical address of the memory.
+ * This is used when initializing the contents of the area.
+ */
+
+void *usbvision_rvmalloc(unsigned long size)
+{
+	void *mem;
+	unsigned long adr;
+
+	size = PAGE_ALIGN(size);
+	mem = vmalloc_32(size);
+	if (!mem)
+		return NULL;
+
+	memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		SetPageReserved(vmalloc_to_page((void *)adr));
+		adr += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	return mem;
+}
+
+void usbvision_rvfree(void *mem, unsigned long size)
+{
+	unsigned long adr;
+
+	if (!mem)
+		return;
+
+	size = PAGE_ALIGN(size);
+
+	adr = (unsigned long) mem;
+	while ((long) size > 0) {
+		ClearPageReserved(vmalloc_to_page((void *)adr));
+		adr += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	vfree(mem);
+}
+
+
+
+#if ENABLE_HEXDUMP
+static void usbvision_hexdump(const unsigned char *data, int len)
+{
+	char tmp[80];
+	int i, k;
+
+	for (i = k = 0; len > 0; i++, len--) {
+		if (i > 0 && (i % 16 == 0)) {
+			printk("%s\n", tmp);
+			k = 0;
+		}
+		k += sprintf(&tmp[k], "%02x ", data[i]);
+	}
+	if (k > 0)
+		printk("%s\n", tmp);
+}
+#endif
+
+/********************************
+ * scratch ring buffer handling
+ ********************************/
+int scratch_len(struct usb_usbvision *usbvision)    /*This returns the amount of data actually in the buffer */
+{
+	int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr;
+	if (len < 0) {
+		len += scratch_buf_size;
+	}
+	PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len);
+
+	return len;
+}
+
+
+/* This returns the free space left in the buffer */
+int scratch_free(struct usb_usbvision *usbvision)
+{
+	int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr;
+	if (free <= 0) {
+		free += scratch_buf_size;
+	}
+	if (free) {
+		free -= 1;							/* at least one byte in the buffer must */
+										/* left blank, otherwise there is no chance to differ between full and empty */
+	}
+	PDEBUG(DBG_SCRATCH, "return %d\n", free);
+
+	return free;
+}
+
+
+/* This puts data into the buffer */
+int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, int len)
+{
+	int len_part;
+
+	if (usbvision->scratch_write_ptr + len < scratch_buf_size) {
+		memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len);
+		usbvision->scratch_write_ptr += len;
+	}
+	else {
+		len_part = scratch_buf_size - usbvision->scratch_write_ptr;
+		memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part);
+		if (len == len_part) {
+			usbvision->scratch_write_ptr = 0;			/* just set write_ptr to zero */
+		}
+		else {
+			memcpy(usbvision->scratch, data + len_part, len - len_part);
+			usbvision->scratch_write_ptr = len - len_part;
+		}
+	}
+
+	PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr);
+
+	return len;
+}
+
+/* This marks the write_ptr as position of new frame header */
+void scratch_mark_header(struct usb_usbvision *usbvision)
+{
+	PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr);
+
+	usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] =
+				usbvision->scratch_write_ptr;
+	usbvision->scratch_headermarker_write_ptr += 1;
+	usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER;
+}
+
+/* This gets data from the buffer at the given "ptr" position */
+int scratch_get_extra(struct usb_usbvision *usbvision, unsigned char *data, int *ptr, int len)
+{
+	int len_part;
+	if (*ptr + len < scratch_buf_size) {
+		memcpy(data, usbvision->scratch + *ptr, len);
+		*ptr += len;
+	}
+	else {
+		len_part = scratch_buf_size - *ptr;
+		memcpy(data, usbvision->scratch + *ptr, len_part);
+		if (len == len_part) {
+			*ptr = 0;							/* just set the y_ptr to zero */
+		}
+		else {
+			memcpy(data + len_part, usbvision->scratch, len - len_part);
+			*ptr = len - len_part;
+		}
+	}
+
+	PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr);
+
+	return len;
+}
+
+
+/* This sets the scratch extra read pointer */
+void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr, int len)
+{
+	*ptr = (usbvision->scratch_read_ptr + len)%scratch_buf_size;
+
+	PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
+}
+
+
+/*This increments the scratch extra read pointer */
+void scratch_inc_extra_ptr(int *ptr, int len)
+{
+	*ptr = (*ptr + len) % scratch_buf_size;
+
+	PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr);
+}
+
+
+/* This gets data from the buffer */
+int scratch_get(struct usb_usbvision *usbvision, unsigned char *data, int len)
+{
+	int len_part;
+	if (usbvision->scratch_read_ptr + len < scratch_buf_size) {
+		memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len);
+		usbvision->scratch_read_ptr += len;
+	}
+	else {
+		len_part = scratch_buf_size - usbvision->scratch_read_ptr;
+		memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part);
+		if (len == len_part) {
+			usbvision->scratch_read_ptr = 0;				/* just set the read_ptr to zero */
+		}
+		else {
+			memcpy(data + len_part, usbvision->scratch, len - len_part);
+			usbvision->scratch_read_ptr = len - len_part;
+		}
+	}
+
+	PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr);
+
+	return len;
+}
+
+
+/* This sets read pointer to next header and returns it */
+int scratch_get_header(struct usb_usbvision *usbvision,struct usbvision_frame_header *header)
+{
+	int errCode = 0;
+
+	PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr);
+
+	while (usbvision->scratch_headermarker_write_ptr -
+		usbvision->scratch_headermarker_read_ptr != 0) {
+		usbvision->scratch_read_ptr =
+			usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr];
+		usbvision->scratch_headermarker_read_ptr += 1;
+		usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER;
+		scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH);
+		if ((header->magic_1 == USBVISION_MAGIC_1)
+			 && (header->magic_2 == USBVISION_MAGIC_2)
+			 && (header->headerLength == USBVISION_HEADER_LENGTH)) {
+			errCode = USBVISION_HEADER_LENGTH;
+			header->frameWidth  = header->frameWidthLo  + (header->frameWidthHi << 8);
+			header->frameHeight = header->frameHeightLo + (header->frameHeightHi << 8);
+			break;
+		}
+	}
+
+	return errCode;
+}
+
+
+/*This removes len bytes of old data from the buffer */
+void scratch_rm_old(struct usb_usbvision *usbvision, int len)
+{
+
+	usbvision->scratch_read_ptr += len;
+	usbvision->scratch_read_ptr %= scratch_buf_size;
+	PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr);
+}
+
+
+/*This resets the buffer - kills all data in it too */
+void scratch_reset(struct usb_usbvision *usbvision)
+{
+	PDEBUG(DBG_SCRATCH, "\n");
+
+	usbvision->scratch_read_ptr = 0;
+	usbvision->scratch_write_ptr = 0;
+	usbvision->scratch_headermarker_read_ptr = 0;
+	usbvision->scratch_headermarker_write_ptr = 0;
+	usbvision->isocstate = IsocState_NoFrame;
+}
+
+int usbvision_scratch_alloc(struct usb_usbvision *usbvision)
+{
+	usbvision->scratch = vmalloc(scratch_buf_size);
+	scratch_reset(usbvision);
+	if(usbvision->scratch == NULL) {
+		err("%s: unable to allocate %d bytes for scratch",
+		    __FUNCTION__, scratch_buf_size);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+void usbvision_scratch_free(struct usb_usbvision *usbvision)
+{
+	if (usbvision->scratch != NULL) {
+		vfree(usbvision->scratch);
+		usbvision->scratch = NULL;
+	}
+}
+
+/*
+ * usbvision_testpattern()
+ *
+ * Procedure forms a test pattern (yellow grid on blue background).
+ *
+ * Parameters:
+ * fullframe:   if TRUE then entire frame is filled, otherwise the procedure
+ *		continues from the current scanline.
+ * pmode	0: fill the frame with solid blue color (like on VCR or TV)
+ *		1: Draw a colored grid
+ *
+ */
+void usbvision_testpattern(struct usb_usbvision *usbvision, int fullframe,
+			int pmode)
+{
+	static const char proc[] = "usbvision_testpattern";
+	struct usbvision_frame *frame;
+	unsigned char *f;
+	int num_cell = 0;
+	int scan_length = 0;
+	static int num_pass = 0;
+
+	if (usbvision == NULL) {
+		printk(KERN_ERR "%s: usbvision == NULL\n", proc);
+		return;
+	}
+	if (usbvision->curFrame == NULL) {
+		printk(KERN_ERR "%s: usbvision->curFrame is NULL.\n", proc);
+		return;
+	}
+
+	/* Grab the current frame */
+	frame = usbvision->curFrame;
+
+	/* Optionally start at the beginning */
+	if (fullframe) {
+		frame->curline = 0;
+		frame->scanlength = 0;
+	}
+
+	/* Form every scan line */
+	for (; frame->curline < frame->frmheight; frame->curline++) {
+		int i;
+
+		f = frame->data + (usbvision->curwidth * 3 * frame->curline);
+		for (i = 0; i < usbvision->curwidth; i++) {
+			unsigned char cb = 0x80;
+			unsigned char cg = 0;
+			unsigned char cr = 0;
+
+			if (pmode == 1) {
+				if (frame->curline % 32 == 0)
+					cb = 0, cg = cr = 0xFF;
+				else if (i % 32 == 0) {
+					if (frame->curline % 32 == 1)
+						num_cell++;
+					cb = 0, cg = cr = 0xFF;
+				} else {
+					cb =
+					    ((num_cell * 7) +
+					     num_pass) & 0xFF;
+					cg =
+					    ((num_cell * 5) +
+					     num_pass * 2) & 0xFF;
+					cr =
+					    ((num_cell * 3) +
+					     num_pass * 3) & 0xFF;
+				}
+			} else {
+				/* Just the blue screen */
+			}
+
+			*f++ = cb;
+			*f++ = cg;
+			*f++ = cr;
+			scan_length += 3;
+		}
+	}
+
+	frame->grabstate = FrameState_Done;
+	frame->scanlength += scan_length;
+	++num_pass;
+
+}
+
+/*
+ * usbvision_decompress_alloc()
+ *
+ * allocates intermediate buffer for decompression
+ */
+int usbvision_decompress_alloc(struct usb_usbvision *usbvision)
+{
+	int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2;
+	usbvision->IntraFrameBuffer = vmalloc(IFB_size);
+	if (usbvision->IntraFrameBuffer == NULL) {
+		err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/*
+ * usbvision_decompress_free()
+ *
+ * frees intermediate buffer for decompression
+ */
+void usbvision_decompress_free(struct usb_usbvision *usbvision)
+{
+	if (usbvision->IntraFrameBuffer != NULL) {
+		vfree(usbvision->IntraFrameBuffer);
+		usbvision->IntraFrameBuffer = NULL;
+	}
+}
+
+/************************************************************
+ * Here comes the data parsing stuff that is run as interrupt
+ ************************************************************/
+/*
+ * usbvision_find_header()
+ *
+ * Locate one of supported header markers in the scratch buffer.
+ */
+static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision)
+{
+	struct usbvision_frame *frame;
+	int foundHeader = 0;
+
+	frame = usbvision->curFrame;
+
+	while (scratch_get_header(usbvision, &frame->isocHeader) == USBVISION_HEADER_LENGTH) {
+		// found header in scratch
+		PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u",
+				frame->isocHeader.magic_2,
+				frame->isocHeader.magic_1,
+				frame->isocHeader.headerLength,
+				frame->isocHeader.frameNum,
+				frame->isocHeader.framePhase,
+				frame->isocHeader.frameLatency,
+				frame->isocHeader.dataFormat,
+				frame->isocHeader.formatParam,
+				frame->isocHeader.frameWidth,
+				frame->isocHeader.frameHeight);
+
+		if (usbvision->requestIntra) {
+			if (frame->isocHeader.formatParam & 0x80) {
+				foundHeader = 1;
+				usbvision->lastIsocFrameNum = -1; // do not check for lost frames this time
+				usbvision_unrequest_intra(usbvision);
+				break;
+			}
+		}
+		else {
+			foundHeader = 1;
+			break;
+		}
+	}
+
+	if (foundHeader) {
+		frame->frmwidth = frame->isocHeader.frameWidth * usbvision->stretch_width;
+		frame->frmheight = frame->isocHeader.frameHeight * usbvision->stretch_height;
+		frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth)>> 3;
+	}
+	else { // no header found
+		PDEBUG(DBG_HEADER, "skipping scratch data, no header");
+		scratch_reset(usbvision);
+		return ParseState_EndParse;
+	}
+
+	// found header
+	if (frame->isocHeader.dataFormat==ISOC_MODE_COMPRESS) {
+		//check isocHeader.frameNum for lost frames
+		if (usbvision->lastIsocFrameNum >= 0) {
+			if (((usbvision->lastIsocFrameNum + 1) % 32) != frame->isocHeader.frameNum) {
+				// unexpected frame drop: need to request new intra frame
+				PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isocHeader.frameNum);
+				usbvision_request_intra(usbvision);
+				return ParseState_NextFrame;
+			}
+		}
+		usbvision->lastIsocFrameNum = frame->isocHeader.frameNum;
+	}
+	usbvision->header_count++;
+	frame->scanstate = ScanState_Lines;
+	frame->curline = 0;
+
+	if (force_testpattern) {
+		usbvision_testpattern(usbvision, 1, 1);
+		return ParseState_NextFrame;
+	}
+	return ParseState_Continue;
+}
+
+static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision,
+					   long *pcopylen)
+{
+	volatile struct usbvision_frame *frame;
+	unsigned char *f;
+	int len;
+	int i;
+	unsigned char yuyv[4]={180, 128, 10, 128}; // YUV components
+	unsigned char rv, gv, bv;	// RGB components
+	int clipmask_index, bytes_per_pixel;
+	int stretch_bytes, clipmask_add;
+
+	frame  = usbvision->curFrame;
+	f = frame->data + (frame->v4l2_linesize * frame->curline);
+
+	/* Make sure there's enough data for the entire line */
+	len = (frame->isocHeader.frameWidth * 2)+5;
+	if (scratch_len(usbvision) < len) {
+		PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len);
+		return ParseState_Out;
+	}
+
+	if ((frame->curline + 1) >= frame->frmheight) {
+		return ParseState_NextFrame;
+	}
+
+	bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+	stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
+	clipmask_index = frame->curline * MAX_FRAME_WIDTH;
+	clipmask_add = usbvision->stretch_width;
+
+	for (i = 0; i < frame->frmwidth; i+=(2 * usbvision->stretch_width)) {
+
+		scratch_get(usbvision, &yuyv[0], 4);
+
+		if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+			*f++ = yuyv[0]; // Y
+			*f++ = yuyv[3]; // U
+		}
+		else {
+
+			YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv);
+			switch (frame->v4l2_format.format) {
+				case V4L2_PIX_FMT_RGB565:
+					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
+					*f++ = (0x07 & (gv >> 5)) | (0xF8 &  rv);
+					break;
+				case V4L2_PIX_FMT_RGB24:
+					*f++ = bv;
+					*f++ = gv;
+					*f++ = rv;
+					break;
+				case V4L2_PIX_FMT_RGB32:
+					*f++ = bv;
+					*f++ = gv;
+					*f++ = rv;
+					f++;
+					break;
+				case V4L2_PIX_FMT_RGB555:
+					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
+					*f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
+					break;
+			}
+		}
+		clipmask_index += clipmask_add;
+		f += stretch_bytes;
+
+		if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+			*f++ = yuyv[2]; // Y
+			*f++ = yuyv[1]; // V
+		}
+		else {
+
+			YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv);
+			switch (frame->v4l2_format.format) {
+				case V4L2_PIX_FMT_RGB565:
+					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
+					*f++ = (0x07 & (gv >> 5)) | (0xF8 &  rv);
+					break;
+				case V4L2_PIX_FMT_RGB24:
+					*f++ = bv;
+					*f++ = gv;
+					*f++ = rv;
+					break;
+				case V4L2_PIX_FMT_RGB32:
+					*f++ = bv;
+					*f++ = gv;
+					*f++ = rv;
+					f++;
+					break;
+				case V4L2_PIX_FMT_RGB555:
+					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
+					*f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
+					break;
+			}
+		}
+		clipmask_index += clipmask_add;
+		f += stretch_bytes;
+	}
+
+	frame->curline += usbvision->stretch_height;
+	*pcopylen += frame->v4l2_linesize * usbvision->stretch_height;
+
+	if (frame->curline >= frame->frmheight) {
+		return ParseState_NextFrame;
+	}
+	else {
+		return ParseState_Continue;
+	}
+}
+
+/* The decompression routine  */
+static int usbvision_decompress(struct usb_usbvision *usbvision,unsigned char *Compressed,
+								unsigned char *Decompressed, int *StartPos,
+								int *BlockTypeStartPos, int Len)
+{
+	int RestPixel, Idx, MaxPos, Pos, ExtraPos, BlockLen, BlockTypePos, BlockTypeLen;
+	unsigned char BlockByte, BlockCode, BlockType, BlockTypeByte, Integrator;
+
+	Integrator = 0;
+	Pos = *StartPos;
+	BlockTypePos = *BlockTypeStartPos;
+	MaxPos = 396; //Pos + Len;
+	ExtraPos = Pos;
+	BlockLen = 0;
+	BlockByte = 0;
+	BlockCode = 0;
+	BlockType = 0;
+	BlockTypeByte = 0;
+	BlockTypeLen = 0;
+	RestPixel = Len;
+
+	for (Idx = 0; Idx < Len; Idx++) {
+
+		if (BlockLen == 0) {
+			if (BlockTypeLen==0) {
+				BlockTypeByte = Compressed[BlockTypePos];
+				BlockTypePos++;
+				BlockTypeLen = 4;
+			}
+			BlockType = (BlockTypeByte & 0xC0) >> 6;
+
+			//statistic:
+			usbvision->ComprBlockTypes[BlockType]++;
+
+			Pos = ExtraPos;
+			if (BlockType == 0) {
+				if(RestPixel >= 24) {
+					Idx += 23;
+					RestPixel -= 24;
+					Integrator = Decompressed[Idx];
+				} else {
+					Idx += RestPixel - 1;
+					RestPixel = 0;
+				}
+			} else {
+				BlockCode = Compressed[Pos];
+				Pos++;
+				if (RestPixel >= 24) {
+					BlockLen  = 24;
+				} else {
+					BlockLen = RestPixel;
+				}
+				RestPixel -= BlockLen;
+				ExtraPos = Pos + (BlockLen / 4);
+			}
+			BlockTypeByte <<= 2;
+			BlockTypeLen -= 1;
+		}
+		if (BlockLen > 0) {
+			if ((BlockLen%4) == 0) {
+				BlockByte = Compressed[Pos];
+				Pos++;
+			}
+			if (BlockType == 1) { //inter Block
+				Integrator = Decompressed[Idx];
+			}
+			switch (BlockByte & 0xC0) {
+				case 0x03<<6:
+					Integrator += Compressed[ExtraPos];
+					ExtraPos++;
+					break;
+				case 0x02<<6:
+					Integrator += BlockCode;
+					break;
+				case 0x00:
+					Integrator -= BlockCode;
+					break;
+			}
+			Decompressed[Idx] = Integrator;
+			BlockByte <<= 2;
+			BlockLen -= 1;
+		}
+	}
+	*StartPos = ExtraPos;
+	*BlockTypeStartPos = BlockTypePos;
+	return Idx;
+}
+
+
+/*
+ * usbvision_parse_compress()
+ *
+ * Parse compressed frame from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ */
+static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision,
+					   long *pcopylen)
+{
+#define USBVISION_STRIP_MAGIC		0x5A
+#define USBVISION_STRIP_LEN_MAX		400
+#define USBVISION_STRIP_HEADER_LEN	3
+
+	struct usbvision_frame *frame;
+	unsigned char *f,*u = NULL ,*v = NULL;
+	unsigned char StripData[USBVISION_STRIP_LEN_MAX];
+	unsigned char StripHeader[USBVISION_STRIP_HEADER_LEN];
+	int Idx, IdxEnd, StripLen, StripPtr, StartBlockPos, BlockPos, BlockTypePos;
+	int clipmask_index, bytes_per_pixel, rc;
+	int imageSize;
+	unsigned char rv, gv, bv;
+	static unsigned char *Y, *U, *V;
+
+	frame  = usbvision->curFrame;
+	imageSize = frame->frmwidth * frame->frmheight;
+	if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) ||
+	     (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) {       // this is a planar format
+		//... v4l2_linesize not used here.
+		f = frame->data + (frame->width * frame->curline);
+	} else
+		f = frame->data + (frame->v4l2_linesize * frame->curline);
+
+	if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers
+		// get base of u and b planes add halfoffset
+
+		u = frame->data
+			+ imageSize
+			+ (frame->frmwidth >>1) * frame->curline ;
+		v = u + (imageSize >>1 );
+
+	} else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){
+
+		v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ;
+		u = v + (imageSize >>2) ;
+	}
+
+	if (frame->curline == 0) {
+		usbvision_adjust_compression(usbvision);
+	}
+
+	if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) {
+		return ParseState_Out;
+	}
+
+	//get strip header without changing the scratch_read_ptr
+	scratch_set_extra_ptr(usbvision, &StripPtr, 0);
+	scratch_get_extra(usbvision, &StripHeader[0], &StripPtr,
+				USBVISION_STRIP_HEADER_LEN);
+
+	if (StripHeader[0] != USBVISION_STRIP_MAGIC) {
+		// wrong strip magic
+		usbvision->stripMagicErrors++;
+		return ParseState_NextFrame;
+	}
+
+	if (frame->curline != (int)StripHeader[2]) {
+		//line number missmatch error
+		usbvision->stripLineNumberErrors++;
+	}
+
+	StripLen = 2 * (unsigned int)StripHeader[1];
+	if (StripLen > USBVISION_STRIP_LEN_MAX) {
+		// strip overrun
+		// I think this never happens
+		usbvision_request_intra(usbvision);
+	}
+
+	if (scratch_len(usbvision) < StripLen) {
+		//there is not enough data for the strip
+		return ParseState_Out;
+	}
+
+	if (usbvision->IntraFrameBuffer) {
+		Y = usbvision->IntraFrameBuffer + frame->frmwidth * frame->curline;
+		U = usbvision->IntraFrameBuffer + imageSize + (frame->frmwidth / 2) * (frame->curline / 2);
+		V = usbvision->IntraFrameBuffer + imageSize / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2);
+	}
+	else {
+		return ParseState_NextFrame;
+	}
+
+	bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+	clipmask_index = frame->curline * MAX_FRAME_WIDTH;
+
+	scratch_get(usbvision, StripData, StripLen);
+
+	IdxEnd = frame->frmwidth;
+	BlockTypePos = USBVISION_STRIP_HEADER_LEN;
+	StartBlockPos = BlockTypePos + (IdxEnd - 1) / 96 + (IdxEnd / 2 - 1) / 96 + 2;
+	BlockPos = StartBlockPos;
+
+	usbvision->BlockPos = BlockPos;
+
+	if ((rc = usbvision_decompress(usbvision, StripData, Y, &BlockPos, &BlockTypePos, IdxEnd)) != IdxEnd) {
+		//return ParseState_Continue;
+	}
+	if (StripLen > usbvision->maxStripLen) {
+		usbvision->maxStripLen = StripLen;
+	}
+
+	if (frame->curline%2) {
+		if ((rc = usbvision_decompress(usbvision, StripData, V, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
+		//return ParseState_Continue;
+		}
+	}
+	else {
+		if ((rc = usbvision_decompress(usbvision, StripData, U, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) {
+			//return ParseState_Continue;
+		}
+	}
+
+	if (BlockPos > usbvision->comprBlockPos) {
+		usbvision->comprBlockPos = BlockPos;
+	}
+	if (BlockPos > StripLen) {
+		usbvision->stripLenErrors++;
+	}
+
+	for (Idx = 0; Idx < IdxEnd; Idx++) {
+		if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+			*f++ = Y[Idx];
+			*f++ = Idx & 0x01 ? U[Idx/2] : V[Idx/2];
+		}
+		else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) {
+			*f++ = Y[Idx];
+			if ( Idx & 0x01)
+				*u++ = U[Idx>>1] ;
+			else
+				*v++ = V[Idx>>1];
+		}
+		else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) {
+			*f++ = Y [Idx];
+			if ( !((  Idx & 0x01  ) | (  frame->curline & 0x01  )) ){
+
+/* 				 only need do this for 1 in 4 pixels */
+/* 				 intraframe buffer is YUV420 format */
+
+				*u++ = U[Idx >>1];
+				*v++ = V[Idx >>1];
+			}
+
+		}
+		else {
+			YUV_TO_RGB_BY_THE_BOOK(Y[Idx], U[Idx/2], V[Idx/2], rv, gv, bv);
+			switch (frame->v4l2_format.format) {
+				case V4L2_PIX_FMT_GREY:
+					*f++ = Y[Idx];
+					break;
+				case V4L2_PIX_FMT_RGB555:
+					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2));
+					*f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1));
+					break;
+				case V4L2_PIX_FMT_RGB565:
+					*f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3));
+					*f++ = (0x07 & (gv >> 5)) | (0xF8 &  rv);
+					break;
+				case V4L2_PIX_FMT_RGB24:
+					*f++ = bv;
+					*f++ = gv;
+					*f++ = rv;
+					break;
+				case V4L2_PIX_FMT_RGB32:
+					*f++ = bv;
+					*f++ = gv;
+					*f++ = rv;
+					f++;
+					break;
+			}
+		}
+		clipmask_index++;
+	}
+	/* Deal with non-integer no. of bytes for YUV420P */
+	if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420 )
+		*pcopylen += frame->v4l2_linesize;
+	else
+		*pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1;
+
+	frame->curline += 1;
+
+	if (frame->curline >= frame->frmheight) {
+		return ParseState_NextFrame;
+	}
+	else {
+		return ParseState_Continue;
+	}
+
+}
+
+
+/*
+ * usbvision_parse_lines_420()
+ *
+ * Parse two lines from the scratch buffer, put
+ * decoded RGB value into the current frame buffer and add the written
+ * number of bytes (RGB) to the *pcopylen.
+ *
+ */
+static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision,
+					   long *pcopylen)
+{
+	struct usbvision_frame *frame;
+	unsigned char *f_even = NULL, *f_odd = NULL;
+	unsigned int pixel_per_line, block;
+	int pixel, block_split;
+	int y_ptr, u_ptr, v_ptr, y_odd_offset;
+	const int   y_block_size = 128;
+	const int  uv_block_size = 64;
+	const int sub_block_size = 32;
+	const int y_step[] = { 0, 0, 0, 2 },  y_step_size = 4;
+	const int uv_step[]= { 0, 0, 0, 4 }, uv_step_size = 4;
+	unsigned char y[2], u, v;	/* YUV components */
+	int y_, u_, v_, vb, uvg, ur;
+	int r_, g_, b_;			/* RGB components */
+	unsigned char g;
+	int clipmask_even_index, clipmask_odd_index, bytes_per_pixel;
+	int clipmask_add, stretch_bytes;
+
+	frame  = usbvision->curFrame;
+	f_even = frame->data + (frame->v4l2_linesize * frame->curline);
+	f_odd  = f_even + frame->v4l2_linesize * usbvision->stretch_height;
+
+	/* Make sure there's enough data for the entire line */
+	/* In this mode usbvision transfer 3 bytes for every 2 pixels */
+	/* I need two lines to decode the color */
+	bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
+	stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel;
+	clipmask_even_index = frame->curline * MAX_FRAME_WIDTH;
+	clipmask_odd_index  = clipmask_even_index + MAX_FRAME_WIDTH;
+	clipmask_add = usbvision->stretch_width;
+	pixel_per_line = frame->isocHeader.frameWidth;
+
+	if (scratch_len(usbvision) < (int)pixel_per_line * 3) {
+		//printk(KERN_DEBUG "out of data, need %d\n", len);
+		return ParseState_Out;
+	}
+
+	if ((frame->curline + 1) >= frame->frmheight) {
+		return ParseState_NextFrame;
+	}
+
+	block_split = (pixel_per_line%y_block_size) ? 1 : 0;	//are some blocks splitted into different lines?
+
+	y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size)
+			+ block_split * uv_block_size;
+
+	scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset);
+	scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size);
+	scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset
+			+ (4 - block_split) * sub_block_size);
+
+	for (block = 0; block < (pixel_per_line / sub_block_size);
+	     block++) {
+
+
+		for (pixel = 0; pixel < sub_block_size; pixel +=2) {
+			scratch_get(usbvision, &y[0], 2);
+			scratch_get_extra(usbvision, &u, &u_ptr, 1);
+			scratch_get_extra(usbvision, &v, &v_ptr, 1);
+
+			//I don't use the YUV_TO_RGB macro for better performance
+			v_ = v - 128;
+			u_ = u - 128;
+			vb =              132252 * v_;
+			uvg= -53281 * u_ - 25625 * v_;
+			ur = 104595 * u_;
+
+			if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+				*f_even++ = y[0];
+				*f_even++ = v;
+			}
+			else {
+				y_ = 76284 * (y[0] - 16);
+
+				b_ = (y_ + vb) >> 16;
+				g_ = (y_ + uvg)>> 16;
+				r_ = (y_ + ur) >> 16;
+
+				switch (frame->v4l2_format.format) {
+					case V4L2_PIX_FMT_RGB565:
+						g = LIMIT_RGB(g_);
+						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+						*f_even++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
+						break;
+					case V4L2_PIX_FMT_RGB24:
+						*f_even++ = LIMIT_RGB(b_);
+						*f_even++ = LIMIT_RGB(g_);
+						*f_even++ = LIMIT_RGB(r_);
+						break;
+					case V4L2_PIX_FMT_RGB32:
+						*f_even++ = LIMIT_RGB(b_);
+						*f_even++ = LIMIT_RGB(g_);
+						*f_even++ = LIMIT_RGB(r_);
+						f_even++;
+						break;
+					case V4L2_PIX_FMT_RGB555:
+						g = LIMIT_RGB(g_);
+						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+						*f_even++ = (0x03 & (          g   >> 6)) |
+							    (0x7C & (LIMIT_RGB(r_) >> 1));
+						break;
+				}
+			}
+			clipmask_even_index += clipmask_add;
+			f_even += stretch_bytes;
+
+			if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+				*f_even++ = y[1];
+				*f_even++ = u;
+			}
+			else {
+				y_ = 76284 * (y[1] - 16);
+
+				b_ = (y_ + vb) >> 16;
+				g_ = (y_ + uvg)>> 16;
+				r_ = (y_ + ur) >> 16;
+
+				switch (frame->v4l2_format.format) {
+					case V4L2_PIX_FMT_RGB565:
+						g = LIMIT_RGB(g_);
+						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+						*f_even++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
+						break;
+					case V4L2_PIX_FMT_RGB24:
+						*f_even++ = LIMIT_RGB(b_);
+						*f_even++ = LIMIT_RGB(g_);
+						*f_even++ = LIMIT_RGB(r_);
+						break;
+					case V4L2_PIX_FMT_RGB32:
+						*f_even++ = LIMIT_RGB(b_);
+						*f_even++ = LIMIT_RGB(g_);
+						*f_even++ = LIMIT_RGB(r_);
+						f_even++;
+						break;
+					case V4L2_PIX_FMT_RGB555:
+						g = LIMIT_RGB(g_);
+						*f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+						*f_even++ = (0x03 & (          g   >> 6)) |
+							    (0x7C & (LIMIT_RGB(r_) >> 1));
+						break;
+				}
+			}
+			clipmask_even_index += clipmask_add;
+			f_even += stretch_bytes;
+
+			scratch_get_extra(usbvision, &y[0], &y_ptr, 2);
+
+			if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+				*f_odd++ = y[0];
+				*f_odd++ = v;
+			}
+			else {
+				y_ = 76284 * (y[0] - 16);
+
+				b_ = (y_ + vb) >> 16;
+				g_ = (y_ + uvg)>> 16;
+				r_ = (y_ + ur) >> 16;
+
+				switch (frame->v4l2_format.format) {
+					case V4L2_PIX_FMT_RGB565:
+						g = LIMIT_RGB(g_);
+						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+						*f_odd++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
+						break;
+					case V4L2_PIX_FMT_RGB24:
+						*f_odd++ = LIMIT_RGB(b_);
+						*f_odd++ = LIMIT_RGB(g_);
+						*f_odd++ = LIMIT_RGB(r_);
+						break;
+					case V4L2_PIX_FMT_RGB32:
+						*f_odd++ = LIMIT_RGB(b_);
+						*f_odd++ = LIMIT_RGB(g_);
+						*f_odd++ = LIMIT_RGB(r_);
+						f_odd++;
+						break;
+					case V4L2_PIX_FMT_RGB555:
+						g = LIMIT_RGB(g_);
+						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+						*f_odd++ = (0x03 & (          g   >> 6)) |
+							   (0x7C & (LIMIT_RGB(r_) >> 1));
+						break;
+				}
+			}
+			clipmask_odd_index += clipmask_add;
+			f_odd += stretch_bytes;
+
+			if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) {
+				*f_odd++ = y[1];
+				*f_odd++ = u;
+			}
+			else {
+				y_ = 76284 * (y[1] - 16);
+
+				b_ = (y_ + vb) >> 16;
+				g_ = (y_ + uvg)>> 16;
+				r_ = (y_ + ur) >> 16;
+
+				switch (frame->v4l2_format.format) {
+					case V4L2_PIX_FMT_RGB565:
+						g = LIMIT_RGB(g_);
+						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3));
+						*f_odd++ = (0x07 & (          g   >> 5)) | (0xF8 & LIMIT_RGB(r_));
+						break;
+					case V4L2_PIX_FMT_RGB24:
+						*f_odd++ = LIMIT_RGB(b_);
+						*f_odd++ = LIMIT_RGB(g_);
+						*f_odd++ = LIMIT_RGB(r_);
+						break;
+					case V4L2_PIX_FMT_RGB32:
+						*f_odd++ = LIMIT_RGB(b_);
+						*f_odd++ = LIMIT_RGB(g_);
+						*f_odd++ = LIMIT_RGB(r_);
+						f_odd++;
+						break;
+					case V4L2_PIX_FMT_RGB555:
+						g = LIMIT_RGB(g_);
+						*f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2));
+						*f_odd++ = (0x03 & (          g   >> 6)) |
+							   (0x7C & (LIMIT_RGB(r_) >> 1));
+						break;
+				}
+			}
+			clipmask_odd_index += clipmask_add;
+			f_odd += stretch_bytes;
+		}
+
+		scratch_rm_old(usbvision,y_step[block % y_step_size] * sub_block_size);
+		scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size]
+				* sub_block_size);
+		scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size]
+				* sub_block_size);
+		scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size]
+				* sub_block_size);
+	}
+
+	scratch_rm_old(usbvision, pixel_per_line * 3 / 2
+			+ block_split * sub_block_size);
+
+	frame->curline += 2 * usbvision->stretch_height;
+	*pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height;
+
+	if (frame->curline >= frame->frmheight)
+		return ParseState_NextFrame;
+	else
+		return ParseState_Continue;
+}
+
+/*
+ * usbvision_parse_data()
+ *
+ * Generic routine to parse the scratch buffer. It employs either
+ * usbvision_find_header() or usbvision_parse_lines() to do most
+ * of work.
+ *
+ */
+static void usbvision_parse_data(struct usb_usbvision *usbvision)
+{
+	struct usbvision_frame *frame;
+	enum ParseState newstate;
+	long copylen = 0;
+	unsigned long lock_flags;
+
+	frame = usbvision->curFrame;
+
+	PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision));
+
+	while (1) {
+
+		newstate = ParseState_Out;
+		if (scratch_len(usbvision)) {
+			if (frame->scanstate == ScanState_Scanning) {
+				newstate = usbvision_find_header(usbvision);
+			}
+			else if (frame->scanstate == ScanState_Lines) {
+				if (usbvision->isocMode == ISOC_MODE_YUV420) {
+					newstate = usbvision_parse_lines_420(usbvision, &copylen);
+				}
+				else if (usbvision->isocMode == ISOC_MODE_YUV422) {
+					newstate = usbvision_parse_lines_422(usbvision, &copylen);
+				}
+				else if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
+					newstate = usbvision_parse_compress(usbvision, &copylen);
+				}
+
+			}
+		}
+		if (newstate == ParseState_Continue) {
+			continue;
+		}
+		else if ((newstate == ParseState_NextFrame) || (newstate == ParseState_Out)) {
+			break;
+		}
+		else {
+			return;	/* ParseState_EndParse */
+		}
+	}
+
+	if (newstate == ParseState_NextFrame) {
+		frame->grabstate = FrameState_Done;
+		do_gettimeofday(&(frame->timestamp));
+		frame->sequence = usbvision->frame_num;
+
+		spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+		list_move_tail(&(frame->frame), &usbvision->outqueue);
+		usbvision->curFrame = NULL;
+		spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+		usbvision->frame_num++;
+
+		/* This will cause the process to request another frame. */
+		if (waitqueue_active(&usbvision->wait_frame)) {
+			PDEBUG(DBG_PARSE, "Wake up !");
+			wake_up_interruptible(&usbvision->wait_frame);
+		}
+	}
+	else
+		frame->grabstate = FrameState_Grabbing;
+
+
+	/* Update the frame's uncompressed length. */
+	frame->scanlength += copylen;
+}
+
+
+/*
+ * Make all of the blocks of data contiguous
+ */
+static int usbvision_compress_isochronous(struct usb_usbvision *usbvision,
+					  struct urb *urb)
+{
+	unsigned char *packet_data;
+	int i, totlen = 0;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		int packet_len = urb->iso_frame_desc[i].actual_length;
+		int packet_stat = urb->iso_frame_desc[i].status;
+
+		packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		/* Detect and ignore errored packets */
+		if (packet_stat) {	// packet_stat != 0 ?????????????
+			PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat);
+			usbvision->isocErrCount++;
+			continue;
+		}
+
+		/* Detect and ignore empty packets */
+		if (packet_len < 0) {
+			PDEBUG(DBG_ISOC, "error packet [%d]", i);
+			usbvision->isocSkipCount++;
+			continue;
+		}
+		else if (packet_len == 0) {	/* Frame end ????? */
+			PDEBUG(DBG_ISOC, "null packet [%d]", i);
+			usbvision->isocstate=IsocState_NoFrame;
+			usbvision->isocSkipCount++;
+			continue;
+		}
+		else if (packet_len > usbvision->isocPacketSize) {
+			PDEBUG(DBG_ISOC, "packet[%d] > isocPacketSize", i);
+			usbvision->isocSkipCount++;
+			continue;
+		}
+
+		PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len);
+
+		if (usbvision->isocstate==IsocState_NoFrame) { //new frame begins
+			usbvision->isocstate=IsocState_InFrame;
+			scratch_mark_header(usbvision);
+			usbvision_measure_bandwidth(usbvision);
+			PDEBUG(DBG_ISOC, "packet with header");
+		}
+
+		/*
+		 * If usbvision continues to feed us with data but there is no
+		 * consumption (if, for example, V4L client fell asleep) we
+		 * may overflow the buffer. We have to move old data over to
+		 * free room for new data. This is bad for old data. If we
+		 * just drop new data then it's bad for new data... choose
+		 * your favorite evil here.
+		 */
+		if (scratch_free(usbvision) < packet_len) {
+
+			usbvision->scratch_ovf_count++;
+			PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d",
+			       scratch_len(usbvision), packet_len);
+			scratch_rm_old(usbvision, packet_len - scratch_free(usbvision));
+		}
+
+		/* Now we know that there is enough room in scratch buffer */
+		scratch_put(usbvision, packet_data, packet_len);
+		totlen += packet_len;
+		usbvision->isocDataCount += packet_len;
+		usbvision->isocPacketCount++;
+	}
+#if ENABLE_HEXDUMP
+	if (totlen > 0) {
+		static int foo = 0;
+		if (foo < 1) {
+			printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen);
+			usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen);
+			++foo;
+		}
+	}
+#endif
+ return totlen;
+}
+
+static void usbvision_isocIrq(struct urb *urb)
+{
+	int errCode = 0;
+	int len;
+	struct usb_usbvision *usbvision = urb->context;
+	int i;
+	unsigned long startTime = jiffies;
+	struct usbvision_frame **f;
+
+	/* We don't want to do anything if we are about to be removed! */
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return;
+
+	f = &usbvision->curFrame;
+
+	/* Manage streaming interruption */
+	if (usbvision->streaming == Stream_Interrupt) {
+		usbvision->streaming = Stream_Idle;
+		if ((*f)) {
+			(*f)->grabstate = FrameState_Ready;
+			(*f)->scanstate = ScanState_Scanning;
+		}
+		PDEBUG(DBG_IRQ, "stream interrupted");
+		wake_up_interruptible(&usbvision->wait_stream);
+	}
+
+	/* Copy the data received into our scratch buffer */
+	len = usbvision_compress_isochronous(usbvision, urb);
+
+	usbvision->isocUrbCount++;
+	usbvision->urb_length = len;
+
+	if (usbvision->streaming == Stream_On) {
+
+		/* If we collected enough data let's parse! */
+		if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH) {	/* 12 == header_length */
+			/*If we don't have a frame we're current working on, complain */
+			if(!list_empty(&(usbvision->inqueue))) {
+				if (!(*f)) {
+					(*f) = list_entry(usbvision->inqueue.next,struct usbvision_frame, frame);
+				}
+				usbvision_parse_data(usbvision);
+			}
+			else {
+				PDEBUG(DBG_IRQ, "received data, but no one needs it");
+				scratch_reset(usbvision);
+			}
+		}
+	}
+	else {
+		PDEBUG(DBG_IRQ, "received data, but no one needs it");
+		scratch_reset(usbvision);
+	}
+
+	usbvision->timeInIrq += jiffies - startTime;
+
+	for (i = 0; i < USBVISION_URB_FRAMES; i++) {
+		urb->iso_frame_desc[i].status = 0;
+		urb->iso_frame_desc[i].actual_length = 0;
+	}
+
+	urb->status = 0;
+	urb->dev = usbvision->dev;
+	errCode = usb_submit_urb (urb, GFP_ATOMIC);
+
+	/* Disable this warning.  By design of the driver. */
+	//	if(errCode) {
+	//		err("%s: usb_submit_urb failed: error %d", __FUNCTION__, errCode);
+	//	}
+
+	return;
+}
+
+/*************************************/
+/* Low level usbvision access functions */
+/*************************************/
+
+/*
+ * usbvision_read_reg()
+ *
+ * return  < 0 -> Error
+ *        >= 0 -> Data
+ */
+
+int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg)
+{
+	int errCode = 0;
+	unsigned char buffer[1];
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return -1;
+
+	errCode = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1),
+				USBVISION_OP_CODE,
+				USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+				0, (__u16) reg, buffer, 1, HZ);
+
+	if (errCode < 0) {
+		err("%s: failed: error %d", __FUNCTION__, errCode);
+		return errCode;
+	}
+	return buffer[0];
+}
+
+/*
+ * usbvision_write_reg()
+ *
+ * return 1 -> Reg written
+ *        0 -> usbvision is not yet ready
+ *       -1 -> Something went wrong
+ */
+
+int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
+			    unsigned char value)
+{
+	int errCode = 0;
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return 0;
+
+	errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+				USBVISION_OP_CODE,
+				USB_DIR_OUT | USB_TYPE_VENDOR |
+				USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ);
+
+	if (errCode < 0) {
+		err("%s: failed: error %d", __FUNCTION__, errCode);
+	}
+	return errCode;
+}
+
+
+static void usbvision_ctrlUrb_complete(struct urb *urb)
+{
+	struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context;
+
+	PDEBUG(DBG_IRQ, "");
+	usbvision->ctrlUrbBusy = 0;
+	if (waitqueue_active(&usbvision->ctrlUrb_wq)) {
+		wake_up_interruptible(&usbvision->ctrlUrb_wq);
+	}
+}
+
+
+static int usbvision_write_reg_irq(struct usb_usbvision *usbvision,int address,
+									unsigned char *data, int len)
+{
+	int errCode = 0;
+
+	PDEBUG(DBG_IRQ, "");
+	if (len > 8) {
+		return -EFAULT;
+	}
+//	down(&usbvision->ctrlUrbLock);
+	if (usbvision->ctrlUrbBusy) {
+//		up(&usbvision->ctrlUrbLock);
+		return -EBUSY;
+	}
+	usbvision->ctrlUrbBusy = 1;
+//	up(&usbvision->ctrlUrbLock);
+
+	usbvision->ctrlUrbSetup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+	usbvision->ctrlUrbSetup.bRequest     = USBVISION_OP_CODE;
+	usbvision->ctrlUrbSetup.wValue       = 0;
+	usbvision->ctrlUrbSetup.wIndex       = cpu_to_le16(address);
+	usbvision->ctrlUrbSetup.wLength      = cpu_to_le16(len);
+	usb_fill_control_urb (usbvision->ctrlUrb, usbvision->dev,
+							usb_sndctrlpipe(usbvision->dev, 1),
+							(unsigned char *)&usbvision->ctrlUrbSetup,
+							(void *)usbvision->ctrlUrbBuffer, len,
+							usbvision_ctrlUrb_complete,
+							(void *)usbvision);
+
+	memcpy(usbvision->ctrlUrbBuffer, data, len);
+
+	errCode = usb_submit_urb(usbvision->ctrlUrb, GFP_ATOMIC);
+	if (errCode < 0) {
+		// error in usb_submit_urb()
+		usbvision->ctrlUrbBusy = 0;
+	}
+	PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, errCode);
+	return errCode;
+}
+
+
+static int usbvision_init_compression(struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+
+	usbvision->lastIsocFrameNum = -1;
+	usbvision->isocDataCount = 0;
+	usbvision->isocPacketCount = 0;
+	usbvision->isocSkipCount = 0;
+	usbvision->comprLevel = 50;
+	usbvision->lastComprLevel = -1;
+	usbvision->isocUrbCount = 0;
+	usbvision->requestIntra = 1;
+	usbvision->isocMeasureBandwidthCount = 0;
+
+	return errCode;
+}
+
+/* this function measures the used bandwidth since last call
+ * return:    0 : no error
+ * sets usedBandwidth to 1-100 : 1-100% of full bandwidth resp. to isocPacketSize
+ */
+static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+
+	if (usbvision->isocMeasureBandwidthCount < 2) { // this gives an average bandwidth of 3 frames
+		usbvision->isocMeasureBandwidthCount++;
+		return errCode;
+	}
+	if ((usbvision->isocPacketSize > 0) && (usbvision->isocPacketCount > 0)) {
+		usbvision->usedBandwidth = usbvision->isocDataCount /
+					(usbvision->isocPacketCount + usbvision->isocSkipCount) *
+					100 / usbvision->isocPacketSize;
+	}
+	usbvision->isocMeasureBandwidthCount = 0;
+	usbvision->isocDataCount = 0;
+	usbvision->isocPacketCount = 0;
+	usbvision->isocSkipCount = 0;
+	return errCode;
+}
+
+static int usbvision_adjust_compression (struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+	unsigned char buffer[6];
+
+	PDEBUG(DBG_IRQ, "");
+	if ((adjustCompression) && (usbvision->usedBandwidth > 0)) {
+		usbvision->comprLevel += (usbvision->usedBandwidth - 90) / 2;
+		RESTRICT_TO_RANGE(usbvision->comprLevel, 0, 100);
+		if (usbvision->comprLevel != usbvision->lastComprLevel) {
+			int distorsion;
+			if (usbvision->bridgeType == BRIDGE_NT1004 || usbvision->bridgeType == BRIDGE_NT1005) {
+				buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100);	// PCM Threshold 1
+				buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100);	// PCM Threshold 2
+				distorsion = 7 + 248 * usbvision->comprLevel / 100;
+				buffer[2] = (unsigned char)(distorsion & 0xFF);				// Average distorsion Threshold (inter)
+				buffer[3] = (unsigned char)(distorsion & 0xFF);				// Average distorsion Threshold (intra)
+				distorsion = 1 + 42 * usbvision->comprLevel / 100;
+				buffer[4] = (unsigned char)(distorsion & 0xFF);				// Maximum distorsion Threshold (inter)
+				buffer[5] = (unsigned char)(distorsion & 0xFF);				// Maximum distorsion Threshold (intra)
+			}
+			else { //BRIDGE_NT1003
+				buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100);	// PCM threshold 1
+				buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100);	// PCM threshold 2
+				distorsion = 2 + 253 * usbvision->comprLevel / 100;
+				buffer[2] = (unsigned char)(distorsion & 0xFF);				// distorsion threshold bit0-7
+				buffer[3] = 0; 	//(unsigned char)((distorsion >> 8) & 0x0F);		// distorsion threshold bit 8-11
+				distorsion = 0 + 43 * usbvision->comprLevel / 100;
+				buffer[4] = (unsigned char)(distorsion & 0xFF);				// maximum distorsion bit0-7
+				buffer[5] = 0; //(unsigned char)((distorsion >> 8) & 0x01);		// maximum distorsion bit 8
+			}
+			errCode = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6);
+			if (errCode == 0){
+				PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0],
+								buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
+				usbvision->lastComprLevel = usbvision->comprLevel;
+			}
+		}
+	}
+	return errCode;
+}
+
+static int usbvision_request_intra (struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+	unsigned char buffer[1];
+
+	PDEBUG(DBG_IRQ, "");
+	usbvision->requestIntra = 1;
+	buffer[0] = 1;
+	usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
+	return errCode;
+}
+
+static int usbvision_unrequest_intra (struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+	unsigned char buffer[1];
+
+	PDEBUG(DBG_IRQ, "");
+	usbvision->requestIntra = 0;
+	buffer[0] = 0;
+	usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1);
+	return errCode;
+}
+
+/*******************************
+ * usbvision utility functions
+ *******************************/
+
+int usbvision_power_off(struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+
+	PDEBUG(DBG_FUNC, "");
+
+	errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
+	if (errCode == 1) {
+		usbvision->power = 0;
+	}
+	PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode!=1)?"ERROR":"power is off", errCode);
+	return errCode;
+}
+
+/*
+ * usbvision_set_video_format()
+ *
+ */
+static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format)
+{
+	static const char proc[] = "usbvision_set_video_format";
+	int rc;
+	unsigned char value[2];
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return 0;
+
+	PDEBUG(DBG_FUNC, "isocMode %#02x", format);
+
+	if ((format != ISOC_MODE_YUV422)
+	    && (format != ISOC_MODE_YUV420)
+	    && (format != ISOC_MODE_COMPRESS)) {
+		printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420",
+		       format);
+		format = ISOC_MODE_YUV420;
+	}
+	value[0] = 0x0A;  //TODO: See the effect of the filter
+	value[1] = format;
+	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+			     USBVISION_OP_CODE,
+			     USB_DIR_OUT | USB_TYPE_VENDOR |
+			     USB_RECIP_ENDPOINT, 0,
+			     (__u16) USBVISION_FILT_CONT, value, 2, HZ);
+
+	if (rc < 0) {
+		printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - "
+		       "reconnect or reload driver.\n", proc, rc);
+	}
+	usbvision->isocMode = format;
+	return rc;
+}
+
+/*
+ * usbvision_set_output()
+ *
+ */
+
+int usbvision_set_output(struct usb_usbvision *usbvision, int width,
+			 int height)
+{
+	int errCode = 0;
+	int UsbWidth, UsbHeight;
+	unsigned int frameRate=0, frameDrop=0;
+	unsigned char value[4];
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision)) {
+		return 0;
+	}
+
+	if (width > MAX_USB_WIDTH) {
+		UsbWidth = width / 2;
+		usbvision->stretch_width = 2;
+	}
+	else {
+		UsbWidth = width;
+		usbvision->stretch_width = 1;
+	}
+
+	if (height > MAX_USB_HEIGHT) {
+		UsbHeight = height / 2;
+		usbvision->stretch_height = 2;
+	}
+	else {
+		UsbHeight = height;
+		usbvision->stretch_height = 1;
+	}
+
+	RESTRICT_TO_RANGE(UsbWidth, MIN_FRAME_WIDTH, MAX_USB_WIDTH);
+	UsbWidth &= ~(MIN_FRAME_WIDTH-1);
+	RESTRICT_TO_RANGE(UsbHeight, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT);
+	UsbHeight &= ~(1);
+
+	PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d",
+						UsbWidth, UsbHeight, width, height,
+						usbvision->stretch_width, usbvision->stretch_height);
+
+	/* I'll not rewrite the same values */
+	if ((UsbWidth != usbvision->curwidth) || (UsbHeight != usbvision->curheight)) {
+		value[0] = UsbWidth & 0xff;		//LSB
+		value[1] = (UsbWidth >> 8) & 0x03;	//MSB
+		value[2] = UsbHeight & 0xff;		//LSB
+		value[3] = (UsbHeight >> 8) & 0x03;	//MSB
+
+		errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+			     USBVISION_OP_CODE,
+			     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+				 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ);
+
+		if (errCode < 0) {
+			err("%s failed: error %d", __FUNCTION__, errCode);
+			return errCode;
+		}
+		usbvision->curwidth = usbvision->stretch_width * UsbWidth;
+		usbvision->curheight = usbvision->stretch_height * UsbHeight;
+	}
+
+	if (usbvision->isocMode == ISOC_MODE_YUV422) {
+		frameRate = (usbvision->isocPacketSize * 1000) / (UsbWidth * UsbHeight * 2);
+	}
+	else if (usbvision->isocMode == ISOC_MODE_YUV420) {
+		frameRate = (usbvision->isocPacketSize * 1000) / ((UsbWidth * UsbHeight * 12) / 8);
+	}
+	else {
+		frameRate = FRAMERATE_MAX;
+	}
+
+	if (usbvision->tvnorm->id & V4L2_STD_625_50) {
+		frameDrop = frameRate * 32 / 25 - 1;
+	}
+	else if (usbvision->tvnorm->id & V4L2_STD_525_60) {
+		frameDrop = frameRate * 32 / 30 - 1;
+	}
+
+	RESTRICT_TO_RANGE(frameDrop, FRAMERATE_MIN, FRAMERATE_MAX);
+
+	PDEBUG(DBG_FUNC, "frameRate %d fps, frameDrop %d", frameRate, frameDrop);
+
+	frameDrop = FRAMERATE_MAX; 	// We can allow the maximum here, because dropping is controlled
+
+	/* frameDrop = 7; => framePhase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ...
+		=> frameSkip = 4;
+		=> frameRate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25;
+
+	   frameDrop = 9; => framePhase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ...
+	    => frameSkip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ...
+		=> frameRate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125;
+	*/
+	errCode = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frameDrop);
+	return errCode;
+}
+
+
+/*
+ * usbvision_frames_alloc
+ * allocate the maximum frames this driver can manage
+ */
+int usbvision_frames_alloc(struct usb_usbvision *usbvision)
+{
+	int i;
+
+	/* Allocate memory for the frame buffers */
+	usbvision->max_frame_size = MAX_FRAME_SIZE;
+	usbvision->fbuf_size = USBVISION_NUMFRAMES * usbvision->max_frame_size;
+	usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size);
+
+	if(usbvision->fbuf == NULL) {
+		err("%s: unable to allocate %d bytes for fbuf ",
+		    __FUNCTION__, usbvision->fbuf_size);
+		return -ENOMEM;
+	}
+	spin_lock_init(&usbvision->queue_lock);
+	init_waitqueue_head(&usbvision->wait_frame);
+	init_waitqueue_head(&usbvision->wait_stream);
+
+	/* Allocate all buffers */
+	for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+		usbvision->frame[i].index = i;
+		usbvision->frame[i].grabstate = FrameState_Unused;
+		usbvision->frame[i].data = usbvision->fbuf +
+			i * usbvision->max_frame_size;
+		/*
+		 * Set default sizes for read operation.
+		 */
+		usbvision->stretch_width = 1;
+		usbvision->stretch_height = 1;
+		usbvision->frame[i].width = usbvision->curwidth;
+		usbvision->frame[i].height = usbvision->curheight;
+		usbvision->frame[i].bytes_read = 0;
+	}
+	return 0;
+}
+
+/*
+ * usbvision_frames_free
+ * frees memory allocated for the frames
+ */
+void usbvision_frames_free(struct usb_usbvision *usbvision)
+{
+	/* Have to free all that memory */
+	if (usbvision->fbuf != NULL) {
+		usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
+		usbvision->fbuf = NULL;
+	}
+}
+/*
+ * usbvision_empty_framequeues()
+ * prepare queues for incoming and outgoing frames
+ */
+void usbvision_empty_framequeues(struct usb_usbvision *usbvision)
+{
+	u32 i;
+
+	INIT_LIST_HEAD(&(usbvision->inqueue));
+	INIT_LIST_HEAD(&(usbvision->outqueue));
+
+	for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+		usbvision->frame[i].grabstate = FrameState_Unused;
+		usbvision->frame[i].bytes_read = 0;
+	}
+}
+
+/*
+ * usbvision_stream_interrupt()
+ * stops streaming
+ */
+int usbvision_stream_interrupt(struct usb_usbvision *usbvision)
+{
+	int ret = 0;
+
+	/* stop reading from the device */
+
+	usbvision->streaming = Stream_Interrupt;
+	ret = wait_event_timeout(usbvision->wait_stream,
+				 (usbvision->streaming == Stream_Idle),
+				 msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES));
+	return ret;
+}
+
+/*
+ * usbvision_set_compress_params()
+ *
+ */
+
+static int usbvision_set_compress_params(struct usb_usbvision *usbvision)
+{
+	static const char proc[] = "usbvision_set_compresion_params: ";
+	int rc;
+	unsigned char value[6];
+
+	value[0] = 0x0F;    // Intra-Compression cycle
+	value[1] = 0x01;    // Reg.45 one line per strip
+	value[2] = 0x00;    // Reg.46 Force intra mode on all new frames
+	value[3] = 0x00;    // Reg.47 FORCE_UP <- 0 normal operation (not force)
+	value[4] = 0xA2;    // Reg.48 BUF_THR I'm not sure if this does something in not compressed mode.
+	value[5] = 0x00;    // Reg.49 DVI_YUV This has nothing to do with compression
+
+	//catched values for NT1004
+	// value[0] = 0xFF; // Never apply intra mode automatically
+	// value[1] = 0xF1; // Use full frame height for virtual strip width; One line per strip
+	// value[2] = 0x01; // Force intra mode on all new frames
+	// value[3] = 0x00; // Strip size 400 Bytes; do not force up
+	// value[4] = 0xA2; //
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return 0;
+
+	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+			     USBVISION_OP_CODE,
+			     USB_DIR_OUT | USB_TYPE_VENDOR |
+			     USB_RECIP_ENDPOINT, 0,
+			     (__u16) USBVISION_INTRA_CYC, value, 5, HZ);
+
+	if (rc < 0) {
+		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+		       "reconnect or reload driver.\n", proc, rc);
+		return rc;
+	}
+
+	if (usbvision->bridgeType == BRIDGE_NT1004) {
+		value[0] =  20; // PCM Threshold 1
+		value[1] =  12; // PCM Threshold 2
+		value[2] = 255; // Distorsion Threshold inter
+		value[3] = 255; // Distorsion Threshold intra
+		value[4] =  43; // Max Distorsion inter
+		value[5] =  43; // Max Distorsion intra
+	}
+	else {
+		value[0] =  20; // PCM Threshold 1
+		value[1] =  12; // PCM Threshold 2
+		value[2] = 255; // Distorsion Threshold d7-d0
+		value[3] =   0; // Distorsion Threshold d11-d8
+		value[4] =  43; // Max Distorsion d7-d0
+		value[5] =   0; // Max Distorsion d8
+	}
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return 0;
+
+	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+			     USBVISION_OP_CODE,
+			     USB_DIR_OUT | USB_TYPE_VENDOR |
+			     USB_RECIP_ENDPOINT, 0,
+			     (__u16) USBVISION_PCM_THR1, value, 6, HZ);
+
+	if (rc < 0) {
+		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+		       "reconnect or reload driver.\n", proc, rc);
+		return rc;
+	}
+
+
+	return rc;
+}
+
+
+/*
+ * usbvision_set_input()
+ *
+ * Set the input (saa711x, ...) size x y and other misc input params
+ * I've no idea if this parameters are right
+ *
+ */
+int usbvision_set_input(struct usb_usbvision *usbvision)
+{
+	static const char proc[] = "usbvision_set_input: ";
+	int rc;
+	unsigned char value[8];
+	unsigned char dvi_yuv_value;
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return 0;
+
+	/* Set input format expected from decoder*/
+	if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) {
+		value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff;
+	} else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
+		/* SAA7113 uses 8 bit output */
+		value[0] = USBVISION_8_422_SYNC;
+	} else {
+		/* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses
+		 * as that is how saa7111 is configured */
+		value[0] = USBVISION_16_422_SYNC;
+		/* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/
+	}
+
+	rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]);
+	if (rc < 0) {
+		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+		       "reconnect or reload driver.\n", proc, rc);
+		return rc;
+	}
+
+
+	if (usbvision->tvnorm->id & V4L2_STD_PAL) {
+		value[0] = 0xC0;
+		value[1] = 0x02;	//0x02C0 -> 704 Input video line length
+		value[2] = 0x20;
+		value[3] = 0x01;	//0x0120 -> 288 Input video n. of lines
+		value[4] = 0x60;
+		value[5] = 0x00;	//0x0060 -> 96 Input video h offset
+		value[6] = 0x16;
+		value[7] = 0x00;	//0x0016 -> 22 Input video v offset
+	} else if (usbvision->tvnorm->id & V4L2_STD_SECAM) {
+		value[0] = 0xC0;
+		value[1] = 0x02;	//0x02C0 -> 704 Input video line length
+		value[2] = 0x20;
+		value[3] = 0x01;	//0x0120 -> 288 Input video n. of lines
+		value[4] = 0x01;
+		value[5] = 0x00;	//0x0001 -> 01 Input video h offset
+		value[6] = 0x01;
+		value[7] = 0x00;	//0x0001 -> 01 Input video v offset
+	} else {	/* V4L2_STD_NTSC */
+		value[0] = 0xD0;
+		value[1] = 0x02;	//0x02D0 -> 720 Input video line length
+		value[2] = 0xF0;
+		value[3] = 0x00;	//0x00F0 -> 240 Input video number of lines
+		value[4] = 0x50;
+		value[5] = 0x00;	//0x0050 -> 80 Input video h offset
+		value[6] = 0x10;
+		value[7] = 0x00;	//0x0010 -> 16 Input video v offset
+	}
+
+	if (usbvision_device_data[usbvision->DevModel].X_Offset >= 0) {
+		value[4]=usbvision_device_data[usbvision->DevModel].X_Offset & 0xff;
+		value[5]=(usbvision_device_data[usbvision->DevModel].X_Offset & 0x0300) >> 8;
+	}
+
+	if (usbvision_device_data[usbvision->DevModel].Y_Offset >= 0) {
+		value[6]=usbvision_device_data[usbvision->DevModel].Y_Offset & 0xff;
+		value[7]=(usbvision_device_data[usbvision->DevModel].Y_Offset & 0x0300) >> 8;
+	}
+
+	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+			     USBVISION_OP_CODE,	/* USBVISION specific code */
+			     USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0,
+			     (__u16) USBVISION_LXSIZE_I, value, 8, HZ);
+	if (rc < 0) {
+		printk(KERN_ERR "%sERROR=%d. USBVISION stopped - "
+		       "reconnect or reload driver.\n", proc, rc);
+		return rc;
+	}
+
+
+	dvi_yuv_value = 0x00;	/* U comes after V, Ya comes after U/V, Yb comes after Yb */
+
+	if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){
+		dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff;
+	}
+	else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) {
+	/* This changes as the fine sync control changes. Further investigation necessary */
+		dvi_yuv_value = 0x06;
+	}
+
+	return (usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value));
+}
+
+
+/*
+ * usbvision_set_dram_settings()
+ *
+ * Set the buffer address needed by the usbvision dram to operate
+ * This values has been taken with usbsnoop.
+ *
+ */
+
+static int usbvision_set_dram_settings(struct usb_usbvision *usbvision)
+{
+	int rc;
+	unsigned char value[8];
+
+	if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
+		value[0] = 0x42;
+		value[1] = 0x71;
+		value[2] = 0xff;
+		value[3] = 0x00;
+		value[4] = 0x98;
+		value[5] = 0xe0;
+		value[6] = 0x71;
+		value[7] = 0xff;
+		// UR:  0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte)
+		// FDL: 0x00000-0x0E099 =  57498 Words
+		// VDW: 0x0E3FF-0x3FFFF
+	}
+	else {
+		value[0] = 0x42;
+		value[1] = 0x00;
+		value[2] = 0xff;
+		value[3] = 0x00;
+		value[4] = 0x00;
+		value[5] = 0x00;
+		value[6] = 0x00;
+		value[7] = 0xff;
+	}
+	/* These are the values of the address of the video buffer,
+	 * they have to be loaded into the USBVISION_DRM_PRM1-8
+	 *
+	 * Start address of video output buffer for read: 	drm_prm1-2 -> 0x00000
+	 * End address of video output buffer for read: 	drm_prm1-3 -> 0x1ffff
+	 * Start address of video frame delay buffer: 		drm_prm1-4 -> 0x20000
+	 *    Only used in compressed mode
+	 * End address of video frame delay buffer: 		drm_prm1-5-6 -> 0x3ffff
+	 *    Only used in compressed mode
+	 * Start address of video output buffer for write: 	drm_prm1-7 -> 0x00000
+	 * End address of video output buffer for write: 	drm_prm1-8 -> 0x1ffff
+	 */
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return 0;
+
+	rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1),
+			     USBVISION_OP_CODE,	/* USBVISION specific code */
+			     USB_DIR_OUT | USB_TYPE_VENDOR |
+			     USB_RECIP_ENDPOINT, 0,
+			     (__u16) USBVISION_DRM_PRM1, value, 8, HZ);
+
+	if (rc < 0) {
+		err("%sERROR=%d", __FUNCTION__, rc);
+		return rc;
+	}
+
+	/* Restart the video buffer logic */
+	if ((rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR |
+				   USBVISION_RES_FDL | USBVISION_RES_VDW)) < 0)
+		return rc;
+	rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00);
+
+	return rc;
+}
+
+/*
+ * ()
+ *
+ * Power on the device, enables suspend-resume logic
+ * &  reset the isoc End-Point
+ *
+ */
+
+int usbvision_power_on(struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+
+	PDEBUG(DBG_FUNC, "");
+
+	usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN);
+	usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+			 USBVISION_SSPND_EN | USBVISION_RES2);
+	usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+			 USBVISION_SSPND_EN | USBVISION_PWR_VID);
+	errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+						USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2);
+	if (errCode == 1) {
+		usbvision->power = 1;
+	}
+	PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode<0)?"ERROR":"power is on", errCode);
+	return errCode;
+}
+
+
+/*
+ * usbvision timer stuff
+ */
+
+// to call usbvision_power_off from task queue
+static void call_usbvision_power_off(struct work_struct *work)
+{
+	struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, powerOffWork);
+
+	PDEBUG(DBG_FUNC, "");
+	down_interruptible(&usbvision->lock);
+	if(usbvision->user == 0) {
+		usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+
+		usbvision_power_off(usbvision);
+		usbvision->initialized = 0;
+	}
+	up(&usbvision->lock);
+}
+
+static void usbvision_powerOffTimer(unsigned long data)
+{
+	struct usb_usbvision *usbvision = (void *) data;
+
+	PDEBUG(DBG_FUNC, "");
+	del_timer(&usbvision->powerOffTimer);
+	INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off);
+	(void) schedule_work(&usbvision->powerOffWork);
+
+}
+
+void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision)
+{
+	init_timer(&usbvision->powerOffTimer);
+	usbvision->powerOffTimer.data = (long) usbvision;
+	usbvision->powerOffTimer.function = usbvision_powerOffTimer;
+}
+
+void usbvision_set_powerOffTimer(struct usb_usbvision *usbvision)
+{
+	mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME);
+}
+
+void usbvision_reset_powerOffTimer(struct usb_usbvision *usbvision)
+{
+	if (timer_pending(&usbvision->powerOffTimer)) {
+		del_timer(&usbvision->powerOffTimer);
+	}
+}
+
+/*
+ * usbvision_begin_streaming()
+ * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no
+ * idea about the rest
+ */
+int usbvision_begin_streaming(struct usb_usbvision *usbvision)
+{
+	int errCode = 0;
+
+	if (usbvision->isocMode == ISOC_MODE_COMPRESS) {
+		usbvision_init_compression(usbvision);
+	}
+	errCode = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, USBVISION_NOHVALID |
+										usbvision->Vin_Reg2_Preset);
+	return errCode;
+}
+
+/*
+ * usbvision_restart_isoc()
+ * Not sure yet if touching here PWR_REG make loose the config
+ */
+
+int usbvision_restart_isoc(struct usb_usbvision *usbvision)
+{
+	int ret;
+
+	if (
+	    (ret =
+	     usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+			      USBVISION_SSPND_EN | USBVISION_PWR_VID)) < 0)
+		return ret;
+	if (
+	    (ret =
+	     usbvision_write_reg(usbvision, USBVISION_PWR_REG,
+			      USBVISION_SSPND_EN | USBVISION_PWR_VID |
+			      USBVISION_RES2)) < 0)
+		return ret;
+	if (
+	    (ret =
+	     usbvision_write_reg(usbvision, USBVISION_VIN_REG2,
+			      USBVISION_KEEP_BLANK | USBVISION_NOHVALID |
+				  usbvision->Vin_Reg2_Preset)) < 0) return ret;
+
+	/* TODO: schedule timeout */
+	while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) && 0x01) != 1);
+
+	return 0;
+}
+
+int usbvision_audio_off(struct usb_usbvision *usbvision)
+{
+	if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) {
+		printk(KERN_ERR "usbvision_audio_off: can't wirte reg\n");
+		return -1;
+	}
+	usbvision->AudioMute = 0;
+	usbvision->AudioChannel = USBVISION_AUDIO_MUTE;
+	return 0;
+}
+
+int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel)
+{
+	if (!usbvision->AudioMute) {
+		if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, AudioChannel) < 0) {
+			printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n");
+			return -1;
+		}
+	}
+	usbvision->AudioChannel = AudioChannel;
+	return 0;
+}
+
+int usbvision_setup(struct usb_usbvision *usbvision,int format)
+{
+	usbvision_set_video_format(usbvision, format);
+	usbvision_set_dram_settings(usbvision);
+	usbvision_set_compress_params(usbvision);
+	usbvision_set_input(usbvision);
+	usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT);
+	usbvision_restart_isoc(usbvision);
+
+	/* cosas del PCM */
+	return USBVISION_IS_OPERATIONAL(usbvision);
+}
+
+
+int usbvision_sbuf_alloc(struct usb_usbvision *usbvision)
+{
+	int i, errCode = 0;
+	const int sb_size = USBVISION_URB_FRAMES * USBVISION_MAX_ISOC_PACKET_SIZE;
+
+	/* Clean pointers so we know if we allocated something */
+	for (i = 0; i < USBVISION_NUMSBUF; i++)
+		usbvision->sbuf[i].data = NULL;
+
+	for (i = 0; i < USBVISION_NUMSBUF; i++) {
+		usbvision->sbuf[i].data = kzalloc(sb_size, GFP_KERNEL);
+		if (usbvision->sbuf[i].data == NULL) {
+			err("%s: unable to allocate %d bytes for sbuf", __FUNCTION__, sb_size);
+			errCode = -ENOMEM;
+			break;
+		}
+	}
+	return errCode;
+}
+
+
+void usbvision_sbuf_free(struct usb_usbvision *usbvision)
+{
+	int i;
+
+	for (i = 0; i < USBVISION_NUMSBUF; i++) {
+		if (usbvision->sbuf[i].data != NULL) {
+			kfree(usbvision->sbuf[i].data);
+			usbvision->sbuf[i].data = NULL;
+		}
+	}
+}
+
+/*
+ * usbvision_init_isoc()
+ *
+ */
+int usbvision_init_isoc(struct usb_usbvision *usbvision)
+{
+	struct usb_device *dev = usbvision->dev;
+	int bufIdx, errCode, regValue;
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return -EFAULT;
+
+	usbvision->curFrame = NULL;
+	scratch_reset(usbvision);
+
+	/* Alternate interface 1 is is the biggest frame size */
+	errCode = usb_set_interface(dev, usbvision->iface, usbvision->ifaceAltActive);
+	if (errCode < 0) {
+		usbvision->last_error = errCode;
+		return -EBUSY;
+	}
+
+	regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
+	usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1;
+	PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize);
+
+	usbvision->usb_bandwidth = regValue >> 1;
+	PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth);
+
+
+
+	/* We double buffer the Iso lists */
+
+	for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
+		int j, k;
+		struct urb *urb;
+
+		urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+		if (urb == NULL) {
+			err("%s: usb_alloc_urb() failed", __FUNCTION__);
+			return -ENOMEM;
+		}
+		usbvision->sbuf[bufIdx].urb = urb;
+		urb->dev = dev;
+		urb->context = usbvision;
+		urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp);
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->interval = 1;
+		urb->transfer_buffer = usbvision->sbuf[bufIdx].data;
+		urb->complete = usbvision_isocIrq;
+		urb->number_of_packets = USBVISION_URB_FRAMES;
+		urb->transfer_buffer_length =
+		    usbvision->isocPacketSize * USBVISION_URB_FRAMES;
+		for (j = k = 0; j < USBVISION_URB_FRAMES; j++,
+		     k += usbvision->isocPacketSize) {
+			urb->iso_frame_desc[j].offset = k;
+			urb->iso_frame_desc[j].length = usbvision->isocPacketSize;
+		}
+	}
+
+
+	/* Submit all URBs */
+	for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
+			errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb, GFP_KERNEL);
+		if (errCode) {
+			err("%s: usb_submit_urb(%d) failed: error %d", __FUNCTION__, bufIdx, errCode);
+		}
+	}
+
+	usbvision->streaming = Stream_Idle;
+	PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", __FUNCTION__, usbvision->video_endp);
+	return 0;
+}
+
+/*
+ * usbvision_stop_isoc()
+ *
+ * This procedure stops streaming and deallocates URBs. Then it
+ * activates zero-bandwidth alt. setting of the video interface.
+ *
+ */
+void usbvision_stop_isoc(struct usb_usbvision *usbvision)
+{
+	int bufIdx, errCode, regValue;
+
+	if ((usbvision->streaming == Stream_Off) || (usbvision->dev == NULL))
+		return;
+
+	/* Unschedule all of the iso td's */
+	for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) {
+		usb_kill_urb(usbvision->sbuf[bufIdx].urb);
+		usb_free_urb(usbvision->sbuf[bufIdx].urb);
+		usbvision->sbuf[bufIdx].urb = NULL;
+	}
+
+
+	PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __FUNCTION__);
+	usbvision->streaming = Stream_Off;
+
+	if (!usbvision->remove_pending) {
+
+		/* Set packet size to 0 */
+		errCode = usb_set_interface(usbvision->dev, usbvision->iface,
+				      usbvision->ifaceAltInactive);
+		if (errCode < 0) {
+			err("%s: usb_set_interface() failed: error %d", __FUNCTION__, errCode);
+			usbvision->last_error = errCode;
+		}
+		regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
+		usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1;
+		PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize);
+
+		usbvision->usb_bandwidth = regValue >> 1;
+		PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth);
+	}
+}
+
+int usbvision_muxsel(struct usb_usbvision *usbvision, int channel)
+{
+	int mode[4];
+	int audio[]= {1, 0, 0, 0};
+	struct v4l2_routing route;
+	//channel 0 is TV with audiochannel 1 (tuner mono)
+	//channel 1 is Composite with audio channel 0 (line in)
+	//channel 2 is S-Video with audio channel 0 (line in)
+	//channel 3 is additional video inputs to the device with audio channel 0 (line in)
+
+	RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs);
+	usbvision->ctl_input = channel;
+	  route.input = SAA7115_COMPOSITE1;
+	  call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route);
+	  call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input);
+
+	// set the new channel
+	// Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video
+	// Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red
+
+	switch (usbvision_device_data[usbvision->DevModel].Codec) {
+		case CODEC_SAA7113:
+			if (SwitchSVideoInput) { // To handle problems with S-Video Input for some devices.  Use SwitchSVideoInput parameter when loading the module.
+				mode[2] = 1;
+			}
+			else {
+				mode[2] = 7;
+			}
+			if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+				mode[0] = 0; mode[1] = 2; mode[3] = 3;  // Special for four input devices
+			}
+			else {
+				mode[0] = 0; mode[1] = 2; //modes for regular saa7113 devices
+			}
+			break;
+		case CODEC_SAA7111:
+			mode[0] = 0; mode[1] = 1; mode[2] = 7; //modes for saa7111
+			break;
+		default:
+			mode[0] = 0; mode[1] = 1; mode[2] = 7; //default modes
+	}
+	route.input = mode[channel];
+	call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route);
+	usbvision->channel = channel;
+	usbvision_set_audio(usbvision, audio[channel]);
+	return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
new file mode 100644
index 0000000..92bf9a1
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-i2c.c
@@ -0,0 +1,567 @@
+/*
+ * I2C_ALGO_USB.C
+ *  i2c algorithm for USB-I2C Bridges
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *                         Dwaine Garden <dwainegarden@rogers.com>
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include "usbvision.h"
+
+#define DBG_I2C		1<<0
+#define DBG_ALGO	1<<1
+
+static int i2c_debug = 0;
+
+module_param (i2c_debug, int, 0644);			// debug_i2c_usb mode of the device driver
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define PDEBUG(level, fmt, args...) \
+		if (i2c_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
+
+static inline int try_write_address(struct i2c_adapter *i2c_adap,
+				    unsigned char addr, int retries)
+{
+	struct i2c_algo_usb_data *adap = i2c_adap->algo_data;
+	void *data;
+	int i, ret = -1;
+	char buf[4];
+
+	data = i2c_get_adapdata(i2c_adap);
+	buf[0] = 0x00;
+	for (i = 0; i <= retries; i++) {
+		ret = (adap->outb(data, addr, buf, 1));
+		if (ret == 1)
+			break;	/* success! */
+		udelay(5 /*adap->udelay */ );
+		if (i == retries)	/* no success */
+			break;
+		udelay(adap->udelay);
+	}
+	if (i) {
+		PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
+		PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+	}
+	return ret;
+}
+
+static inline int try_read_address(struct i2c_adapter *i2c_adap,
+				   unsigned char addr, int retries)
+{
+	struct i2c_algo_usb_data *adap = i2c_adap->algo_data;
+	void *data;
+	int i, ret = -1;
+	char buf[4];
+
+	data = i2c_get_adapdata(i2c_adap);
+	for (i = 0; i <= retries; i++) {
+		ret = (adap->inb(data, addr, buf, 1));
+		if (ret == 1)
+			break;	/* success! */
+		udelay(5 /*adap->udelay */ );
+		if (i == retries)	/* no success */
+			break;
+		udelay(adap->udelay);
+	}
+	if (i) {
+		PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr);
+		PDEBUG(DBG_ALGO,"Maybe there's no device at this address");
+	}
+	return ret;
+}
+
+static inline int usb_find_address(struct i2c_adapter *i2c_adap,
+				   struct i2c_msg *msg, int retries,
+				   unsigned char *add)
+{
+	unsigned short flags = msg->flags;
+
+	unsigned char addr;
+	int ret;
+	if ((flags & I2C_M_TEN)) {
+		/* a ten bit address */
+		addr = 0xf0 | ((msg->addr >> 7) & 0x03);
+		/* try extended address code... */
+		ret = try_write_address(i2c_adap, addr, retries);
+		if (ret != 1) {
+			err("died at extended address code, while writing");
+			return -EREMOTEIO;
+		}
+		add[0] = addr;
+		if (flags & I2C_M_RD) {
+			/* okay, now switch into reading mode */
+			addr |= 0x01;
+			ret = try_read_address(i2c_adap, addr, retries);
+			if (ret != 1) {
+				err("died at extended address code, while reading");
+				return -EREMOTEIO;
+			}
+		}
+
+	} else {		/* normal 7bit address  */
+		addr = (msg->addr << 1);
+		if (flags & I2C_M_RD)
+			addr |= 1;
+		if (flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;
+
+		add[0] = addr;
+		if (flags & I2C_M_RD)
+			ret = try_read_address(i2c_adap, addr, retries);
+		else
+			ret = try_write_address(i2c_adap, addr, retries);
+
+		if (ret != 1) {
+			return -EREMOTEIO;
+		}
+	}
+	return 0;
+}
+
+static int
+usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+	struct i2c_msg *pmsg;
+	struct i2c_algo_usb_data *adap = i2c_adap->algo_data;
+	void *data;
+	int i, ret;
+	unsigned char addr;
+
+	data = i2c_get_adapdata(i2c_adap);
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+		ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr);
+		if (ret != 0) {
+			PDEBUG(DBG_ALGO,"got NAK from device, message #%d", i);
+			return (ret < 0) ? ret : -EREMOTEIO;
+		}
+
+		if (pmsg->flags & I2C_M_RD) {
+			/* read bytes into buffer */
+			ret = (adap->inb(data, addr, pmsg->buf, pmsg->len));
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret : -EREMOTEIO;
+			}
+		} else {
+			/* write bytes from buffer */
+			ret = (adap->outb(data, addr, pmsg->buf, pmsg->len));
+			if (ret < pmsg->len) {
+				return (ret < 0) ? ret : -EREMOTEIO;
+			}
+		}
+	}
+	return num;
+}
+
+static int algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg)
+{
+	return 0;
+}
+
+static u32 usb_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+
+/* -----exported algorithm data: -------------------------------------	*/
+
+static struct i2c_algorithm i2c_usb_algo = {
+	.master_xfer   = usb_xfer,
+	.smbus_xfer    = NULL,
+	.algo_control  = algo_control,
+	.functionality = usb_func,
+};
+
+
+/*
+ * registering functions to load algorithms at runtime
+ */
+int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap)
+{
+	PDEBUG(DBG_I2C, "I2C   debugging is enabled [i2c]");
+	PDEBUG(DBG_ALGO, "ALGO   debugging is enabled [i2c]");
+
+	/* register new adapter to i2c module... */
+
+	adap->algo = &i2c_usb_algo;
+
+	adap->timeout = 100;	/* default values, should       */
+	adap->retries = 3;	/* be replaced by defines       */
+
+	i2c_add_adapter(adap);
+
+	PDEBUG(DBG_ALGO,"i2c bus for %s registered", adap->name);
+
+	return 0;
+}
+
+
+int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap)
+{
+
+	i2c_del_adapter(adap);
+
+	PDEBUG(DBG_ALGO,"i2c bus for %s unregistered", adap->name);
+
+	return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* usbvision specific I2C functions                                        */
+/* ----------------------------------------------------------------------- */
+static struct i2c_adapter i2c_adap_template;
+static struct i2c_algo_usb_data i2c_algo_template;
+static struct i2c_client i2c_client_template;
+
+int usbvision_init_i2c(struct usb_usbvision *usbvision)
+{
+	memcpy(&usbvision->i2c_adap, &i2c_adap_template,
+	       sizeof(struct i2c_adapter));
+	memcpy(&usbvision->i2c_algo, &i2c_algo_template,
+	       sizeof(struct i2c_algo_usb_data));
+	memcpy(&usbvision->i2c_client, &i2c_client_template,
+	       sizeof(struct i2c_client));
+
+	sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name),
+		" #%d", usbvision->vdev->minor & 0x1f);
+	PDEBUG(DBG_I2C,"Adaptername: %s", usbvision->i2c_adap.name);
+
+	i2c_set_adapdata(&usbvision->i2c_adap, usbvision);
+	i2c_set_clientdata(&usbvision->i2c_client, usbvision);
+	i2c_set_algo_usb_data(&usbvision->i2c_algo, usbvision);
+
+	usbvision->i2c_adap.algo_data = &usbvision->i2c_algo;
+	usbvision->i2c_client.adapter = &usbvision->i2c_adap;
+
+	if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) {
+		printk(KERN_ERR "usbvision_init_i2c: can't write reg\n");
+		return -EBUSY;
+	}
+
+#ifdef CONFIG_MODULES
+	/* Request the load of the i2c modules we need */
+	switch (usbvision_device_data[usbvision->DevModel].Codec) {
+	case CODEC_SAA7113:
+		request_module("saa7115");
+		break;
+	case CODEC_SAA7111:
+		request_module("saa7115");
+		break;
+	}
+	if (usbvision_device_data[usbvision->DevModel].Tuner == 1) {
+		request_module("tuner");
+	}
+#endif
+
+	return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap);
+}
+
+void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,
+		      void *arg)
+{
+	BUG_ON(NULL == usbvision->i2c_adap.algo_data);
+	i2c_clients_command(&usbvision->i2c_adap, cmd, arg);
+}
+
+static int attach_inform(struct i2c_client *client)
+{
+	struct usb_usbvision *usbvision;
+
+	usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
+
+	switch (client->addr << 1) {
+		case 0x43:
+		case 0x4b:
+		{
+			struct tuner_setup tun_setup;
+
+			tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+			tun_setup.type = TUNER_TDA9887;
+			tun_setup.addr = client->addr;
+
+			call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup);
+
+			break;
+		}
+		case 0x42:
+			PDEBUG(DBG_I2C,"attach_inform: saa7114 detected.");
+			break;
+		case 0x4a:
+			PDEBUG(DBG_I2C,"attach_inform: saa7113 detected.");
+			break;
+		case 0xa0:
+			PDEBUG(DBG_I2C,"attach_inform: eeprom detected.");
+			break;
+
+		default:
+			{
+				struct tuner_setup tun_setup;
+
+				PDEBUG(DBG_I2C,"attach inform: detected I2C address %x", client->addr << 1);
+				usbvision->tuner_addr = client->addr;
+
+				if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) {
+					tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+					tun_setup.type = usbvision->tuner_type;
+					tun_setup.addr = usbvision->tuner_addr;
+					call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup);
+				}
+			}
+			break;
+	}
+	return 0;
+}
+
+static int detach_inform(struct i2c_client *client)
+{
+	struct usb_usbvision *usbvision;
+
+	usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
+
+	PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name);
+	return 0;
+}
+
+static int
+usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr,
+		     char *buf, short len)
+{
+	int rc, retries;
+
+	for (retries = 5;;) {
+		rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr);
+		if (rc < 0)
+			return rc;
+
+		/* Initiate byte read cycle                    */
+		/* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */
+		/*                    d3 0=Wr 1=Rd             */
+		rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
+				      (len & 0x07) | 0x18);
+		if (rc < 0)
+			return rc;
+
+		/* Test for Busy and ACK */
+		do {
+			/* USBVISION_SER_CONT -> d4 == 0 busy */
+			rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
+		} while (rc > 0 && ((rc & 0x10) != 0));	/* Retry while busy */
+		if (rc < 0)
+			return rc;
+
+		/* USBVISION_SER_CONT -> d5 == 1 Not ack */
+		if ((rc & 0x20) == 0)	/* Ack? */
+			break;
+
+		/* I2C abort */
+		rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);
+		if (rc < 0)
+			return rc;
+
+		if (--retries < 0)
+			return -1;
+	}
+
+	switch (len) {
+	case 4:
+		buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4);
+	case 3:
+		buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3);
+	case 2:
+		buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2);
+	case 1:
+		buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1);
+		break;
+	default:
+		printk(KERN_ERR
+		       "usbvision_i2c_read_max4: buffer length > 4\n");
+	}
+
+	if (i2c_debug & DBG_I2C) {
+		int idx;
+		for (idx = 0; idx < len; idx++) {
+			PDEBUG(DBG_I2C,"read %x from address %x", (unsigned char)buf[idx], addr);
+		}
+	}
+	return len;
+}
+
+
+static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision,
+				 unsigned char addr, const char *buf,
+				 short len)
+{
+	int rc, retries;
+	int i;
+	unsigned char value[6];
+	unsigned char ser_cont;
+
+	ser_cont = (len & 0x07) | 0x10;
+
+	value[0] = addr;
+	value[1] = ser_cont;
+	for (i = 0; i < len; i++)
+		value[i + 2] = buf[i];
+
+	for (retries = 5;;) {
+		rc = usb_control_msg(usbvision->dev,
+				     usb_sndctrlpipe(usbvision->dev, 1),
+				     USBVISION_OP_CODE,
+				     USB_DIR_OUT | USB_TYPE_VENDOR |
+				     USB_RECIP_ENDPOINT, 0,
+				     (__u16) USBVISION_SER_ADRS, value,
+				     len + 2, HZ);
+
+		if (rc < 0)
+			return rc;
+
+		rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT,
+				      (len & 0x07) | 0x10);
+		if (rc < 0)
+			return rc;
+
+		/* Test for Busy and ACK */
+		do {
+			rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT);
+		} while (rc > 0 && ((rc & 0x10) != 0));	/* Retry while busy */
+		if (rc < 0)
+			return rc;
+
+		if ((rc & 0x20) == 0)	/* Ack? */
+			break;
+
+		/* I2C abort */
+		usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00);
+
+		if (--retries < 0)
+			return -1;
+
+	}
+
+	if (i2c_debug & DBG_I2C) {
+		int idx;
+		for (idx = 0; idx < len; idx++) {
+			PDEBUG(DBG_I2C,"wrote %x at address %x", (unsigned char)buf[idx], addr);
+		}
+	}
+	return len;
+}
+
+static int usbvision_i2c_write(void *data, unsigned char addr, char *buf,
+			    short len)
+{
+	char *bufPtr = buf;
+	int retval;
+	int wrcount = 0;
+	int count;
+	int maxLen = 4;
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
+
+	while (len > 0) {
+		count = (len > maxLen) ? maxLen : len;
+		retval = usbvision_i2c_write_max4(usbvision, addr, bufPtr, count);
+		if (retval > 0) {
+			len -= count;
+			bufPtr += count;
+			wrcount += count;
+		} else
+			return (retval < 0) ? retval : -EFAULT;
+	}
+	return wrcount;
+}
+
+static int usbvision_i2c_read(void *data, unsigned char addr, char *buf,
+			   short len)
+{
+	char temp[4];
+	int retval, i;
+	int rdcount = 0;
+	int count;
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) data;
+
+	while (len > 0) {
+		count = (len > 3) ? 4 : len;
+		retval = usbvision_i2c_read_max4(usbvision, addr, temp, count);
+		if (retval > 0) {
+			for (i = 0; i < len; i++)
+				buf[rdcount + i] = temp[i];
+			len -= count;
+			rdcount += count;
+		} else
+			return (retval < 0) ? retval : -EFAULT;
+	}
+	return rdcount;
+}
+
+static struct i2c_algo_usb_data i2c_algo_template = {
+	.data		= NULL,
+	.inb		= usbvision_i2c_read,
+	.outb		= usbvision_i2c_write,
+	.udelay		= 10,
+	.mdelay		= 10,
+	.timeout	= 100,
+};
+
+static struct i2c_adapter i2c_adap_template = {
+	.owner = THIS_MODULE,
+	.name              = "usbvision",
+	.id                = I2C_HW_B_BT848, /* FIXME */
+	.algo              = NULL,
+	.algo_data         = NULL,
+	.client_register   = attach_inform,
+	.client_unregister = detach_inform,
+#ifdef I2C_ADAP_CLASS_TV_ANALOG
+	.class             = I2C_ADAP_CLASS_TV_ANALOG,
+#else
+	.class		   = I2C_CLASS_TV_ANALOG,
+#endif
+};
+
+static struct i2c_client i2c_client_template = {
+	.name		= "usbvision internal",
+};
+
+EXPORT_SYMBOL(usbvision_i2c_usb_add_bus);
+EXPORT_SYMBOL(usbvision_i2c_usb_del_bus);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
new file mode 100644
index 0000000..864446c
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -0,0 +1,2051 @@
+/*
+ * USB USBVISION Video device driver 0.9.9
+ *
+ *
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *
+ * This module is part of usbvision driver project.
+ *
+ * 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.
+ *
+ * Let's call the version 0.... until compression decoding is completely
+ * implemented.
+ *
+ * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach.
+ * It was based on USB CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt
+ * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler &
+ * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ *
+ * TODO:
+ *     - use submit_urb for all setup packets
+ *     - Fix memory settings for nt1004. It is 4 times as big as the
+ *       nt1003 memory.
+ *     - Add audio on endpoint 3 for nt1004 chip.  Seems impossible, needs a codec interface.  Which one?
+ *     - Clean up the driver.
+ *     - optimization for performance.
+ *     - Add Videotext capability (VBI).  Working on it.....
+ *     - Check audio for other devices
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/highmem.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/videodev2.h>
+#include <linux/video_decoder.h>
+#include <linux/i2c.h>
+
+#include <media/saa7115.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <media/audiochip.h>
+
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "usbvision.h"
+
+#define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>"
+#define DRIVER_NAME "usbvision"
+#define DRIVER_ALIAS "USBVision"
+#define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
+#define DRIVER_LICENSE "GPL"
+#define USBVISION_DRIVER_VERSION_MAJOR 0
+#define USBVISION_DRIVER_VERSION_MINOR 9
+#define USBVISION_DRIVER_VERSION_PATCHLEVEL 9
+#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL)
+#define USBVISION_VERSION_STRING __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL)
+
+#define	ENABLE_HEXDUMP	0	/* Enable if you need it */
+
+
+#ifdef USBVISION_DEBUG
+	#define PDEBUG(level, fmt, args...) \
+		if (video_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args)
+#else
+	#define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+#define DBG_IOCTL	1<<0
+#define DBG_IO		1<<1
+#define DBG_PROBE	1<<2
+#define DBG_MMAP	1<<3
+
+//String operations
+#define rmspace(str)	while(*str==' ') str++;
+#define goto2next(str)	while(*str!=' ') str++; while(*str==' ') str++;
+
+
+static int usbvision_nr = 0;			// sequential number of usbvision device
+
+static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = {
+	{ 1, 1,  8, V4L2_PIX_FMT_GREY    , "GREY" },
+	{ 1, 2, 16, V4L2_PIX_FMT_RGB565  , "RGB565" },
+	{ 1, 3, 24, V4L2_PIX_FMT_RGB24   , "RGB24" },
+	{ 1, 4, 32, V4L2_PIX_FMT_RGB32   , "RGB32" },
+	{ 1, 2, 16, V4L2_PIX_FMT_RGB555  , "RGB555" },
+	{ 1, 2, 16, V4L2_PIX_FMT_YUYV    , "YUV422" },
+	{ 1, 2, 12, V4L2_PIX_FMT_YVU420  , "YUV420P" }, // 1.5 !
+	{ 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" }
+};
+
+/* supported tv norms */
+static struct usbvision_tvnorm tvnorms[] = {
+	{
+		.name = "PAL",
+		.id = V4L2_STD_PAL,
+	}, {
+		.name = "NTSC",
+		.id = V4L2_STD_NTSC,
+	}, {
+		 .name = "SECAM",
+		 .id = V4L2_STD_SECAM,
+	}, {
+		.name = "PAL-M",
+		.id = V4L2_STD_PAL_M,
+	}
+};
+
+#define TVNORMS ARRAY_SIZE(tvnorms)
+
+// Function prototypes
+static void usbvision_release(struct usb_usbvision *usbvision);
+
+// Default initalization of device driver parameters
+static int isocMode = ISOC_MODE_COMPRESS;		// Set the default format for ISOC endpoint
+static int video_debug = 0;				// Set the default Debug Mode of the device driver
+static int PowerOnAtOpen = 1;				// Set the default device to power on at startup
+static int video_nr = -1;				// Sequential Number of Video Device
+static int radio_nr = -1;				// Sequential Number of Radio Device
+static int vbi_nr = -1;					// Sequential Number of VBI Device
+static char *CustomDevice=NULL;				// Set as nothing....
+
+// Grab parameters for the device driver
+
+#if defined(module_param)                               // Showing parameters under SYSFS
+module_param(isocMode, int, 0444);
+module_param(video_debug, int, 0444);
+module_param(PowerOnAtOpen, int, 0444);
+module_param(video_nr, int, 0444);
+module_param(radio_nr, int, 0444);
+module_param(vbi_nr, int, 0444);
+module_param(CustomDevice, charp, 0444);
+#else							// Old Style
+MODULE_PARAM(isocMode, "i");
+MODULE_PARM(video_debug, "i");				// Grab the Debug Mode of the device driver
+MODULE_PARM(adjustCompression, "i");			// Grab the compression to be adaptive
+MODULE_PARM(PowerOnAtOpen, "i");			// Grab the device to power on at startup
+MODULE_PARM(SwitchSVideoInput, "i");			// To help people with Black and White output with using s-video input.  Some cables and input device are wired differently.
+MODULE_PARM(video_nr, "i");				// video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...)
+MODULE_PARM(radio_nr, "i");				// radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...)
+MODULE_PARM(vbi_nr, "i");				// vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...)
+MODULE_PARM(CustomDevice, "s");				// .... CustomDevice
+#endif
+
+MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint.  Default: 0x60 (Compression On)");
+MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver.  Default: 0 (Off)");
+MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened.  Default: 1 (On)");
+MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX).  Default: -1 (autodetect)");
+MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX).  Default: -1 (autodetect)");
+MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX).  Default: -1 (autodetect)");
+MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device.  Default: null");
+
+
+// Misc stuff
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_VERSION(USBVISION_VERSION_STRING);
+MODULE_ALIAS(DRIVER_ALIAS);
+
+
+/****************************************************************************************/
+/* SYSFS Code - Copied from the stv680.c usb module.					*/
+/* Device information is located at /sys/class/video4linux/video0			*/
+/* Device parameters information is located at /sys/module/usbvision                    */
+/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber    */
+/****************************************************************************************/
+
+
+#define YES_NO(x) ((x) ? "Yes" : "No")
+
+static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd)
+{
+	struct video_device *vdev = to_video_device(cd);
+	return video_get_drvdata(vdev);
+}
+
+static ssize_t show_version(struct class_device *cd, char *buf)
+{
+	return sprintf(buf, "%s\n", USBVISION_VERSION_STRING);
+}
+static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t show_model(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString);
+}
+static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
+
+static ssize_t show_hue(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	struct v4l2_control ctrl;
+	ctrl.id = V4L2_CID_HUE;
+	ctrl.value = 0;
+	call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+	return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
+
+static ssize_t show_contrast(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	struct v4l2_control ctrl;
+	ctrl.id = V4L2_CID_CONTRAST;
+	ctrl.value = 0;
+	call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+	return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+
+static ssize_t show_brightness(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	struct v4l2_control ctrl;
+	ctrl.id = V4L2_CID_BRIGHTNESS;
+	ctrl.value = 0;
+	call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+	return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
+
+static ssize_t show_saturation(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	struct v4l2_control ctrl;
+	ctrl.id = V4L2_CID_SATURATION;
+	ctrl.value = 0;
+	call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
+	return sprintf(buf, "%d\n", ctrl.value >> 8);
+}
+static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
+
+static ssize_t show_streaming(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0));
+}
+static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL);
+
+static ssize_t show_compression(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS));
+}
+static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL);
+
+static ssize_t show_device_bridge(struct class_device *class_dev, char *buf)
+{
+	struct video_device *vdev = to_video_device(class_dev);
+	struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+	return sprintf(buf, "%d\n", usbvision->bridgeType);
+}
+static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL);
+
+static void usbvision_create_sysfs(struct video_device *vdev)
+{
+	int res;
+	if (vdev) {
+		res=video_device_create_file(vdev, &class_device_attr_version);
+		res=video_device_create_file(vdev, &class_device_attr_model);
+		res=video_device_create_file(vdev, &class_device_attr_hue);
+		res=video_device_create_file(vdev, &class_device_attr_contrast);
+		res=video_device_create_file(vdev, &class_device_attr_brightness);
+		res=video_device_create_file(vdev, &class_device_attr_saturation);
+		res=video_device_create_file(vdev, &class_device_attr_streaming);
+		res=video_device_create_file(vdev, &class_device_attr_compression);
+		res=video_device_create_file(vdev, &class_device_attr_bridge);
+	}
+}
+
+static void usbvision_remove_sysfs(struct video_device *vdev)
+{
+	if (vdev) {
+		video_device_remove_file(vdev, &class_device_attr_version);
+		video_device_remove_file(vdev, &class_device_attr_model);
+		video_device_remove_file(vdev, &class_device_attr_hue);
+		video_device_remove_file(vdev, &class_device_attr_contrast);
+		video_device_remove_file(vdev, &class_device_attr_brightness);
+		video_device_remove_file(vdev, &class_device_attr_saturation);
+		video_device_remove_file(vdev, &class_device_attr_streaming);
+		video_device_remove_file(vdev, &class_device_attr_compression);
+		video_device_remove_file(vdev, &class_device_attr_bridge);
+	}
+}
+
+
+/*
+ * usbvision_open()
+ *
+ * This is part of Video 4 Linux API. The driver can be opened by one
+ * client only (checks internal counter 'usbvision->user'). The procedure
+ * then allocates buffers needed for video processing.
+ *
+ */
+static int usbvision_v4l2_open(struct inode *inode, struct file *file)
+{
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+	int errCode = 0;
+
+	PDEBUG(DBG_IO, "open");
+
+
+	usbvision_reset_powerOffTimer(usbvision);
+
+	if (usbvision->user)
+		errCode = -EBUSY;
+	else {
+		/* Allocate memory for the frame buffers */
+		errCode = usbvision_frames_alloc(usbvision);
+		if(!errCode) {
+			/* Allocate memory for the scratch ring buffer */
+			errCode = usbvision_scratch_alloc(usbvision);
+			if(!errCode) {
+				/* Allocate memory for the USB S buffers */
+				errCode = usbvision_sbuf_alloc(usbvision);
+				if ((!errCode) && (usbvision->isocMode==ISOC_MODE_COMPRESS)) {
+					/* Allocate intermediate decompression buffers only if needed */
+					errCode = usbvision_decompress_alloc(usbvision);
+				}
+			}
+		}
+		if (errCode) {
+			/* Deallocate all buffers if trouble */
+			usbvision_frames_free(usbvision);
+			usbvision_scratch_free(usbvision);
+			usbvision_sbuf_free(usbvision);
+			usbvision_decompress_free(usbvision);
+		}
+	}
+
+	/* If so far no errors then we shall start the camera */
+	if (!errCode) {
+		down(&usbvision->lock);
+		if (usbvision->power == 0) {
+			usbvision_power_on(usbvision);
+			usbvision_init_i2c(usbvision);
+		}
+
+		/* Send init sequence only once, it's large! */
+		if (!usbvision->initialized) {
+			int setup_ok = 0;
+			setup_ok = usbvision_setup(usbvision,isocMode);
+			if (setup_ok)
+				usbvision->initialized = 1;
+			else
+				errCode = -EBUSY;
+		}
+
+		if (!errCode) {
+			usbvision_begin_streaming(usbvision);
+			errCode = usbvision_init_isoc(usbvision);
+			/* device needs to be initialized before isoc transfer */
+			usbvision_muxsel(usbvision,0);
+			usbvision->user++;
+		}
+		else {
+			if (PowerOnAtOpen) {
+				usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+				usbvision_power_off(usbvision);
+				usbvision->initialized = 0;
+			}
+		}
+		up(&usbvision->lock);
+	}
+
+	if (errCode) {
+	}
+
+	/* prepare queues */
+	usbvision_empty_framequeues(usbvision);
+
+	PDEBUG(DBG_IO, "success");
+	return errCode;
+}
+
+/*
+ * usbvision_v4l2_close()
+ *
+ * This is part of Video 4 Linux API. The procedure
+ * stops streaming and deallocates all buffers that were earlier
+ * allocated in usbvision_v4l2_open().
+ *
+ */
+static int usbvision_v4l2_close(struct inode *inode, struct file *file)
+{
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+	PDEBUG(DBG_IO, "close");
+	down(&usbvision->lock);
+
+	usbvision_audio_off(usbvision);
+	usbvision_restart_isoc(usbvision);
+	usbvision_stop_isoc(usbvision);
+
+	usbvision_decompress_free(usbvision);
+	usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size);
+	usbvision_scratch_free(usbvision);
+	usbvision_sbuf_free(usbvision);
+
+	usbvision->user--;
+
+	if (PowerOnAtOpen) {
+		/* power off in a little while to avoid off/on every close/open short sequences */
+		usbvision_set_powerOffTimer(usbvision);
+		usbvision->initialized = 0;
+	}
+
+	up(&usbvision->lock);
+
+	if (usbvision->remove_pending) {
+		info("%s: Final disconnect", __FUNCTION__);
+		usbvision_release(usbvision);
+	}
+
+	PDEBUG(DBG_IO, "success");
+
+
+	return 0;
+}
+
+
+/*
+ * usbvision_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ */
+static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file,
+				 unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return -EFAULT;
+
+	switch (cmd) {
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+		/* ioctls to allow direct acces to the NT100x registers */
+		case VIDIOC_INT_G_REGISTER:
+		{
+			struct v4l2_register *reg = arg;
+			int errCode;
+
+			if (reg->i2c_id != 0)
+				return -EINVAL;
+			/* NT100x has a 8-bit register space */
+			errCode = usbvision_read_reg(usbvision, reg->reg&0xff);
+			if (errCode < 0) {
+				err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode);
+			}
+			else {
+				reg->val=(unsigned char)errCode;
+				PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X",
+							(unsigned int)reg->reg, reg->val);
+				errCode = 0; // No error
+			}
+			return errCode;
+		}
+		case VIDIOC_INT_S_REGISTER:
+		{
+			struct v4l2_register *reg = arg;
+			int errCode;
+
+			if (reg->i2c_id != 0)
+				return -EINVAL;
+			if (!capable(CAP_SYS_ADMIN))
+				return -EPERM;
+			errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val);
+			if (errCode < 0) {
+				err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode);
+			}
+			else {
+				PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X",
+							(unsigned int)reg->reg, reg->val);
+				errCode = 0;
+			}
+			return 0;
+		}
+#endif
+		case VIDIOC_QUERYCAP:
+		{
+			struct v4l2_capability *vc=arg;
+
+			memset(vc, 0, sizeof(*vc));
+			strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
+			strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString,
+				sizeof(vc->card));
+			strlcpy(vc->bus_info, usbvision->dev->dev.bus_id,
+				sizeof(vc->bus_info));
+			vc->version = USBVISION_DRIVER_VERSION;
+			vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+				V4L2_CAP_AUDIO |
+				V4L2_CAP_READWRITE |
+				V4L2_CAP_STREAMING |
+				(usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
+			PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP");
+			return 0;
+		}
+		case VIDIOC_ENUMINPUT:
+		{
+			struct v4l2_input *vi = arg;
+			int chan;
+
+			if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) )
+				return -EINVAL;
+			if (usbvision->have_tuner) {
+				chan = vi->index;
+			}
+			else {
+				chan = vi->index + 1; //skip Television string
+			}
+			switch(chan) {
+				case 0:
+					if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+						strcpy(vi->name, "White Video Input");
+					}
+					else {
+						strcpy(vi->name, "Television");
+						vi->type = V4L2_INPUT_TYPE_TUNER;
+						vi->audioset = 1;
+						vi->tuner = chan;
+						vi->std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM;
+					}
+					break;
+				case 1:
+					vi->type = V4L2_INPUT_TYPE_CAMERA;
+					if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+						strcpy(vi->name, "Green Video Input");
+					}
+					else {
+						strcpy(vi->name, "Composite Video Input");
+					}
+					vi->std = V4L2_STD_PAL;
+					break;
+				case 2:
+					vi->type = V4L2_INPUT_TYPE_CAMERA;
+					if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) {
+						strcpy(vi->name, "Yellow Video Input");
+					}
+					else {
+					strcpy(vi->name, "S-Video Input");
+					}
+					vi->std = V4L2_STD_PAL;
+					break;
+				case 3:
+					vi->type = V4L2_INPUT_TYPE_CAMERA;
+					strcpy(vi->name, "Red Video Input");
+					vi->std = V4L2_STD_PAL;
+					break;
+			}
+			PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x",
+			       vi->name, vi->index, vi->tuner,vi->type,(int)vi->std);
+			return 0;
+		}
+		case VIDIOC_ENUMSTD:
+		{
+			struct v4l2_standard *e = arg;
+			unsigned int i;
+			int ret;
+
+			i = e->index;
+			if (i >= TVNORMS)
+				return -EINVAL;
+			ret = v4l2_video_std_construct(e, tvnorms[e->index].id,
+						       tvnorms[e->index].name);
+			e->index = i;
+			if (ret < 0)
+				return ret;
+			return 0;
+		}
+		case VIDIOC_G_INPUT:
+		{
+			int *input = arg;
+			*input = usbvision->ctl_input;
+			return 0;
+		}
+		case VIDIOC_S_INPUT:
+		{
+			int *input = arg;
+			if ((*input >= usbvision->video_inputs) || (*input < 0) )
+				return -EINVAL;
+			usbvision->ctl_input = *input;
+
+			down(&usbvision->lock);
+			usbvision_muxsel(usbvision, usbvision->ctl_input);
+			usbvision_set_input(usbvision);
+			usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight);
+			up(&usbvision->lock);
+			return 0;
+		}
+		case VIDIOC_G_STD:
+		{
+			v4l2_std_id *id = arg;
+
+			*id = usbvision->tvnorm->id;
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%s", usbvision->tvnorm->name);
+			return 0;
+		}
+		case VIDIOC_S_STD:
+		{
+			v4l2_std_id *id = arg;
+			unsigned int i;
+
+			for (i = 0; i < TVNORMS; i++)
+				if (*id == tvnorms[i].id)
+					break;
+			if (i == TVNORMS)
+				for (i = 0; i < TVNORMS; i++)
+					if (*id & tvnorms[i].id)
+						break;
+			if (i == TVNORMS)
+				return -EINVAL;
+
+			down(&usbvision->lock);
+			usbvision->tvnorm = &tvnorms[i];
+
+			call_i2c_clients(usbvision, VIDIOC_S_STD,
+					 &usbvision->tvnorm->id);
+
+			up(&usbvision->lock);
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%s", usbvision->tvnorm->name);
+			return 0;
+		}
+		case VIDIOC_G_TUNER:
+		{
+			struct v4l2_tuner *vt = arg;
+
+			if (!usbvision->have_tuner || vt->index)	// Only tuner 0
+				return -EINVAL;
+			strcpy(vt->name, "Television");
+			/* Let clients fill in the remainder of this struct */
+			call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt);
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER signal=%x, afc=%x",vt->signal,vt->afc);
+			return 0;
+		}
+		case VIDIOC_S_TUNER:
+		{
+			struct v4l2_tuner *vt = arg;
+
+			// Only no or one tuner for now
+			if (!usbvision->have_tuner || vt->index)
+				return -EINVAL;
+			/* let clients handle this */
+			call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt);
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER");
+			return 0;
+		}
+		case VIDIOC_G_FREQUENCY:
+		{
+			struct v4l2_frequency *freq = arg;
+
+			freq->tuner = 0; // Only one tuner
+			freq->type = V4L2_TUNER_ANALOG_TV;
+			freq->frequency = usbvision->freq;
+			PDEBUG(DBG_IOCTL, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
+			return 0;
+		}
+		case VIDIOC_S_FREQUENCY:
+		{
+			struct v4l2_frequency *freq = arg;
+
+			// Only no or one tuner for now
+			if (!usbvision->have_tuner || freq->tuner)
+				return -EINVAL;
+
+			usbvision->freq = freq->frequency;
+			call_i2c_clients(usbvision, cmd, freq);
+			PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency);
+			return 0;
+		}
+		case VIDIOC_G_AUDIO:
+		{
+			struct v4l2_audio *v = arg;
+			memset(v,0, sizeof(v));
+			strcpy(v->name, "TV");
+			PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO");
+			return 0;
+		}
+		case VIDIOC_S_AUDIO:
+		{
+			struct v4l2_audio *v = arg;
+			if(v->index) {
+				return -EINVAL;
+			}
+			PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO");
+			return 0;
+		}
+		case VIDIOC_QUERYCTRL:
+		{
+			struct v4l2_queryctrl *ctrl = arg;
+			int id=ctrl->id;
+
+			memset(ctrl,0,sizeof(*ctrl));
+			ctrl->id=id;
+
+			call_i2c_clients(usbvision, cmd, arg);
+
+			if (ctrl->type)
+				return 0;
+			else
+				return -EINVAL;
+
+			PDEBUG(DBG_IOCTL,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type);
+		}
+		case VIDIOC_G_CTRL:
+		{
+			struct v4l2_control *ctrl = arg;
+			PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+			call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl);
+			return 0;
+		}
+		case VIDIOC_S_CTRL:
+		{
+			struct v4l2_control *ctrl = arg;
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+			call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);
+			return 0;
+		}
+		case VIDIOC_REQBUFS:
+		{
+			struct v4l2_requestbuffers *vr = arg;
+			int ret;
+
+			RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES);
+
+			// Check input validity : the user must do a VIDEO CAPTURE and MMAP method.
+			if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+			   (vr->memory != V4L2_MEMORY_MMAP))
+				return -EINVAL;
+
+			if(usbvision->streaming == Stream_On) {
+				if ((ret = usbvision_stream_interrupt(usbvision)))
+				    return ret;
+			}
+
+			usbvision_empty_framequeues(usbvision);
+
+			usbvision->curFrame = NULL;
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_REQBUFS count=%d",vr->count);
+			return 0;
+		}
+		case VIDIOC_QUERYBUF:
+		{
+			struct v4l2_buffer *vb = arg;
+			struct usbvision_frame *frame;
+
+			// FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called)
+
+			if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
+				return -EINVAL;
+			}
+			if(vb->index>=USBVISION_NUMFRAMES)  {
+				return -EINVAL;
+			}
+			// Updating the corresponding frame state
+			vb->flags = 0;
+			frame = &usbvision->frame[vb->index];
+			if(frame->grabstate >= FrameState_Ready)
+				vb->flags |= V4L2_BUF_FLAG_QUEUED;
+			if(frame->grabstate >= FrameState_Done)
+				vb->flags |= V4L2_BUF_FLAG_DONE;
+			if(frame->grabstate == FrameState_Unused)
+				vb->flags |= V4L2_BUF_FLAG_MAPPED;
+			vb->memory = V4L2_MEMORY_MMAP;
+
+			vb->m.offset = vb->index*usbvision->max_frame_size;
+
+			vb->memory = V4L2_MEMORY_MMAP;
+			vb->field = V4L2_FIELD_NONE;
+			vb->length = usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel;
+			vb->timestamp = usbvision->frame[vb->index].timestamp;
+			vb->sequence = usbvision->frame[vb->index].sequence;
+			return 0;
+		}
+		case VIDIOC_QBUF:
+		{
+			struct v4l2_buffer *vb = arg;
+			struct usbvision_frame *frame;
+			unsigned long lock_flags;
+
+			// FIXME : works only on VIDEO_CAPTURE MODE, MMAP.
+			if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
+				return -EINVAL;
+			}
+			if(vb->index>=USBVISION_NUMFRAMES)  {
+				return -EINVAL;
+			}
+
+			frame = &usbvision->frame[vb->index];
+
+			if (frame->grabstate != FrameState_Unused) {
+				return -EAGAIN;
+			}
+
+			/* Mark it as ready and enqueue frame */
+			frame->grabstate = FrameState_Ready;
+			frame->scanstate = ScanState_Scanning;
+			frame->scanlength = 0;	/* Accumulated in usbvision_parse_data() */
+
+			vb->flags &= ~V4L2_BUF_FLAG_DONE;
+
+			/* set v4l2_format index */
+			frame->v4l2_format = usbvision->palette;
+
+			spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+			list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue);
+			spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame #%d",vb->index);
+			return 0;
+		}
+		case VIDIOC_DQBUF:
+		{
+			struct v4l2_buffer *vb = arg;
+			int ret;
+			struct usbvision_frame *f;
+			unsigned long lock_flags;
+
+			if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+				return -EINVAL;
+
+			if (list_empty(&(usbvision->outqueue))) {
+				if (usbvision->streaming == Stream_Idle)
+					return -EINVAL;
+				ret = wait_event_interruptible
+					(usbvision->wait_frame,
+					 !list_empty(&(usbvision->outqueue)));
+				if (ret)
+					return ret;
+			}
+
+			spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+			f = list_entry(usbvision->outqueue.next,
+				       struct usbvision_frame, frame);
+			list_del(usbvision->outqueue.next);
+			spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+			f->grabstate = FrameState_Unused;
+
+			vb->memory = V4L2_MEMORY_MMAP;
+			vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE;
+			vb->index = f->index;
+			vb->sequence = f->sequence;
+			vb->timestamp = f->timestamp;
+			vb->field = V4L2_FIELD_NONE;
+			vb->bytesused = f->scanlength;
+
+			return 0;
+		}
+		case VIDIOC_STREAMON:
+		{
+			int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+			usbvision->streaming = Stream_On;
+
+			call_i2c_clients(usbvision,VIDIOC_STREAMON , &b);
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON");
+
+			return 0;
+		}
+		case VIDIOC_STREAMOFF:
+		{
+			int *type = arg;
+			int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+			if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+				return -EINVAL;
+
+			if(usbvision->streaming == Stream_On) {
+				usbvision_stream_interrupt(usbvision);
+				// Stop all video streamings
+				call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b);
+			}
+			usbvision_empty_framequeues(usbvision);
+
+			PDEBUG(DBG_IOCTL, "VIDIOC_STREAMOFF");
+			return 0;
+		}
+		case VIDIOC_ENUM_FMT:
+		{
+			struct v4l2_fmtdesc *vfd = arg;
+
+			if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) {
+				return -EINVAL;
+			}
+			vfd->flags = 0;
+			vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+			strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc);
+			vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
+			memset(vfd->reserved, 0, sizeof(vfd->reserved));
+			return 0;
+		}
+		case VIDIOC_G_FMT:
+		{
+			struct v4l2_format *vf = arg;
+
+			switch (vf->type) {
+				case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+				{
+					vf->fmt.pix.width = usbvision->curwidth;
+					vf->fmt.pix.height = usbvision->curheight;
+					vf->fmt.pix.pixelformat = usbvision->palette.format;
+					vf->fmt.pix.bytesperline =  usbvision->curwidth*usbvision->palette.bytes_per_pixel;
+					vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight;
+					vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+					vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */
+					PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d, format=%s",
+					       vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc);
+					return 0;
+				}
+				default:
+					PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT invalid type %d",vf->type);
+					return -EINVAL;
+			}
+			return 0;
+		}
+		case VIDIOC_TRY_FMT:
+		case VIDIOC_S_FMT:
+		{
+			struct v4l2_format *vf = arg;
+			int formatIdx,ret;
+
+			switch(vf->type) {
+				case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+				{
+					/* Find requested format in available ones */
+					for(formatIdx=0;formatIdx<USBVISION_SUPPORTED_PALETTES;formatIdx++) {
+						if(vf->fmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) {
+							usbvision->palette = usbvision_v4l2_format[formatIdx];
+							break;
+						}
+					}
+					/* robustness */
+					if(formatIdx == USBVISION_SUPPORTED_PALETTES) {
+						return -EINVAL;
+					}
+					RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
+					RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
+
+					vf->fmt.pix.bytesperline = vf->fmt.pix.width*usbvision->palette.bytes_per_pixel;
+					vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height;
+
+					if(cmd == VIDIOC_TRY_FMT) {
+						PDEBUG(DBG_IOCTL, "VIDIOC_TRY_FMT grabdisplay w=%d, h=%d, format=%s",
+					       vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc);
+						return 0;
+					}
+
+					/* stop io in case it is already in progress */
+					if(usbvision->streaming == Stream_On) {
+						if ((ret = usbvision_stream_interrupt(usbvision)))
+							return ret;
+					}
+					usbvision_empty_framequeues(usbvision);
+
+					usbvision->curFrame = NULL;
+
+					// by now we are committed to the new data...
+					down(&usbvision->lock);
+					usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);
+					up(&usbvision->lock);
+
+					PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, format=%s",
+					       vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc);
+					return 0;
+				}
+				default:
+					return -EINVAL;
+			}
+		}
+		default:
+			return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static int usbvision_v4l2_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, usbvision_v4l2_do_ioctl);
+}
+
+
+static ssize_t usbvision_v4l2_read(struct file *file, char *buf,
+		      size_t count, loff_t *ppos)
+{
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+	int noblock = file->f_flags & O_NONBLOCK;
+	unsigned long lock_flags;
+
+	int frmx = -1;
+	int ret,i;
+	struct usbvision_frame *frame;
+
+	PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock);
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL))
+		return -EFAULT;
+
+	/* no stream is running, make it running ! */
+	usbvision->streaming = Stream_On;
+	call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL);
+
+	/* First, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */
+	for(i=0;i<USBVISION_NUMFRAMES;i++) {
+		frame = &usbvision->frame[i];
+		if(frame->grabstate == FrameState_Unused) {
+			/* Mark it as ready and enqueue frame */
+			frame->grabstate = FrameState_Ready;
+			frame->scanstate = ScanState_Scanning;
+			frame->scanlength = 0;	/* Accumulated in usbvision_parse_data() */
+
+			/* set v4l2_format index */
+			frame->v4l2_format = usbvision->palette;
+
+			spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+			list_add_tail(&frame->frame, &usbvision->inqueue);
+			spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+		}
+	}
+
+	/* Then try to steal a frame (like a VIDIOC_DQBUF would do) */
+	if (list_empty(&(usbvision->outqueue))) {
+		if(noblock)
+			return -EAGAIN;
+
+		ret = wait_event_interruptible
+			(usbvision->wait_frame,
+			 !list_empty(&(usbvision->outqueue)));
+		if (ret)
+			return ret;
+	}
+
+	spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+	frame = list_entry(usbvision->outqueue.next,
+			   struct usbvision_frame, frame);
+	list_del(usbvision->outqueue.next);
+	spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+	/* An error returns an empty frame */
+	if (frame->grabstate == FrameState_Error) {
+		frame->bytes_read = 0;
+		return 0;
+	}
+
+	PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__,
+		       frame->index, frame->bytes_read, frame->scanlength);
+
+	/* copy bytes to user space; we allow for partials reads */
+	if ((count + frame->bytes_read) > (unsigned long)frame->scanlength)
+		count = frame->scanlength - frame->bytes_read;
+
+	if (copy_to_user(buf, frame->data + frame->bytes_read, count)) {
+		return -EFAULT;
+	}
+
+	frame->bytes_read += count;
+	PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__,
+		       (unsigned long)count, frame->bytes_read);
+
+	// For now, forget the frame if it has not been read in one shot.
+/* 	if (frame->bytes_read >= frame->scanlength) {// All data has been read */
+		frame->bytes_read = 0;
+
+		/* Mark it as available to be used again. */
+		usbvision->frame[frmx].grabstate = FrameState_Unused;
+/* 	} */
+
+	return count;
+}
+
+static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	unsigned long size = vma->vm_end - vma->vm_start,
+		start = vma->vm_start;
+	void *pos;
+	u32 i;
+
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+	down(&usbvision->lock);
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision)) {
+		up(&usbvision->lock);
+		return -EFAULT;
+	}
+
+	if (!(vma->vm_flags & VM_WRITE) ||
+	    size != PAGE_ALIGN(usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel)) {
+		up(&usbvision->lock);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < USBVISION_NUMFRAMES; i++) {
+		if (((usbvision->max_frame_size*i) >> PAGE_SHIFT) == vma->vm_pgoff)
+			break;
+	}
+	if (i == USBVISION_NUMFRAMES) {
+		PDEBUG(DBG_MMAP, "mmap: user supplied mapping address is out of range");
+		up(&usbvision->lock);
+		return -EINVAL;
+	}
+
+	/* VM_IO is eventually going to replace PageReserved altogether */
+	vma->vm_flags |= VM_IO;
+	vma->vm_flags |= VM_RESERVED;	/* avoid to swap out this VMA */
+
+	pos = usbvision->frame[i].data;
+	while (size > 0) {
+
+		if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+			PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed");
+			up(&usbvision->lock);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		size -= PAGE_SIZE;
+	}
+
+	up(&usbvision->lock);
+	return 0;
+}
+
+
+/*
+ * Here comes the stuff for radio on usbvision based devices
+ *
+ */
+static int usbvision_radio_open(struct inode *inode, struct file *file)
+{
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+	struct v4l2_frequency freq;
+	int errCode = 0;
+
+	PDEBUG(DBG_IO, "%s:", __FUNCTION__);
+
+	down(&usbvision->lock);
+
+	if (usbvision->user) {
+		err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__);
+		errCode = -EBUSY;
+	}
+	else {
+		if(PowerOnAtOpen) {
+			usbvision_reset_powerOffTimer(usbvision);
+			if (usbvision->power == 0) {
+				usbvision_power_on(usbvision);
+				usbvision_init_i2c(usbvision);
+			}
+		}
+
+		// If so far no errors then we shall start the radio
+		usbvision->radio = 1;
+		call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type);
+		freq.frequency = 1517; //SWR3 @ 94.8MHz
+		call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq);
+		usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
+		usbvision->user++;
+	}
+
+	if (errCode) {
+		if (PowerOnAtOpen) {
+			usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+			usbvision_power_off(usbvision);
+			usbvision->initialized = 0;
+		}
+	}
+	up(&usbvision->lock);
+	return errCode;
+}
+
+
+static int usbvision_radio_close(struct inode *inode, struct file *file)
+{
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+	int errCode = 0;
+
+	PDEBUG(DBG_IO, "");
+
+	down(&usbvision->lock);
+
+	usbvision_audio_off(usbvision);
+	usbvision->radio=0;
+	usbvision->user--;
+
+	if (PowerOnAtOpen) {
+		usbvision_set_powerOffTimer(usbvision);
+		usbvision->initialized = 0;
+	}
+
+	up(&usbvision->lock);
+
+	if (usbvision->remove_pending) {
+		info("%s: Final disconnect", __FUNCTION__);
+		usbvision_release(usbvision);
+	}
+
+
+	PDEBUG(DBG_IO, "success");
+
+	return errCode;
+}
+
+static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file,
+				 unsigned int cmd, void *arg)
+{
+	struct video_device *dev = video_devdata(file);
+	struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev);
+
+	if (!USBVISION_IS_OPERATIONAL(usbvision))
+		return -EIO;
+
+	switch (cmd) {
+		case VIDIOC_QUERYCAP:
+		{
+			struct v4l2_capability *vc=arg;
+
+			memset(vc, 0, sizeof(*vc));
+			strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
+			strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString,
+				sizeof(vc->card));
+			strlcpy(vc->bus_info, usbvision->dev->dev.bus_id,
+				sizeof(vc->bus_info));
+			vc->version = USBVISION_DRIVER_VERSION;
+			vc->capabilities = (usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
+			PDEBUG(DBG_IO, "VIDIOC_QUERYCAP");
+			return 0;
+		}
+		case VIDIOC_QUERYCTRL:
+		{
+			struct v4l2_queryctrl *ctrl = arg;
+			int id=ctrl->id;
+
+			memset(ctrl,0,sizeof(*ctrl));
+			ctrl->id=id;
+
+			call_i2c_clients(usbvision, cmd, arg);
+			PDEBUG(DBG_IO,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type);
+
+			if (ctrl->type)
+				return 0;
+			else
+				return -EINVAL;
+
+		}
+		case VIDIOC_G_CTRL:
+		{
+			struct v4l2_control *ctrl = arg;
+
+			call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl);
+			PDEBUG(DBG_IO,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+			return 0;
+		}
+		case VIDIOC_S_CTRL:
+		{
+			struct v4l2_control *ctrl = arg;
+
+			call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);
+			PDEBUG(DBG_IO, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value);
+			return 0;
+		}
+		case VIDIOC_G_TUNER:
+		{
+			struct v4l2_tuner *t = arg;
+
+			if (t->index > 0)
+				return -EINVAL;
+
+			memset(t,0,sizeof(*t));
+			strcpy(t->name, "Radio");
+			t->type = V4L2_TUNER_RADIO;
+
+			/* Let clients fill in the remainder of this struct */
+			call_i2c_clients(usbvision,VIDIOC_G_TUNER,t);
+			PDEBUG(DBG_IO, "VIDIOC_G_TUNER signal=%x, afc=%x",t->signal,t->afc);
+			return 0;
+		}
+		case VIDIOC_S_TUNER:
+		{
+			struct v4l2_tuner *vt = arg;
+
+			// Only no or one tuner for now
+			if (!usbvision->have_tuner || vt->index)
+				return -EINVAL;
+			/* let clients handle this */
+			call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt);
+
+			PDEBUG(DBG_IO, "VIDIOC_S_TUNER");
+			return 0;
+		}
+		case VIDIOC_G_AUDIO:
+		{
+			struct v4l2_audio *a = arg;
+
+			memset(a,0,sizeof(*a));
+			strcpy(a->name,"Radio");
+			PDEBUG(DBG_IO, "VIDIOC_G_AUDIO");
+			return 0;
+		}
+		case VIDIOC_S_AUDIO:
+		case VIDIOC_S_INPUT:
+		case VIDIOC_S_STD:
+		return 0;
+
+		case VIDIOC_G_FREQUENCY:
+		{
+			struct v4l2_frequency *f = arg;
+
+			memset(f,0,sizeof(*f));
+
+			f->type = V4L2_TUNER_RADIO;
+			f->frequency = usbvision->freq;
+			call_i2c_clients(usbvision, cmd, f);
+			PDEBUG(DBG_IO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)f->frequency);
+
+			return 0;
+		}
+		case VIDIOC_S_FREQUENCY:
+		{
+			struct v4l2_frequency *f = arg;
+
+			if (f->tuner != 0)
+				return -EINVAL;
+			usbvision->freq = f->frequency;
+			call_i2c_clients(usbvision, cmd, f);
+			PDEBUG(DBG_IO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)f->frequency);
+
+			return 0;
+		}
+		default:
+		{
+			PDEBUG(DBG_IO, "%s: Unknown command %x", __FUNCTION__, cmd);
+			return -ENOIOCTLCMD;
+		}
+	}
+	return 0;
+}
+
+
+static int usbvision_radio_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, usbvision_do_radio_ioctl);
+}
+
+
+/*
+ * Here comes the stuff for vbi on usbvision based devices
+ *
+ */
+static int usbvision_vbi_open(struct inode *inode, struct file *file)
+{
+	/* TODO */
+	return -EINVAL;
+
+}
+
+static int usbvision_vbi_close(struct inode *inode, struct file *file)
+{
+	/* TODO */
+	return -EINVAL;
+}
+
+static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file,
+				 unsigned int cmd, void *arg)
+{
+	/* TODO */
+	return -EINVAL;
+}
+
+static int usbvision_vbi_ioctl(struct inode *inode, struct file *file,
+		       unsigned int cmd, unsigned long arg)
+{
+	return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl);
+}
+
+
+//
+// Video registration stuff
+//
+
+// Video template
+static struct file_operations usbvision_fops = {
+	.owner             = THIS_MODULE,
+	.open		= usbvision_v4l2_open,
+	.release	= usbvision_v4l2_close,
+	.read		= usbvision_v4l2_read,
+	.mmap		= usbvision_v4l2_mmap,
+	.ioctl		= usbvision_v4l2_ioctl,
+	.llseek		= no_llseek,
+};
+static struct video_device usbvision_video_template = {
+	.owner             = THIS_MODULE,
+	.type		= VID_TYPE_TUNER | VID_TYPE_CAPTURE,
+	.hardware	= VID_HARDWARE_USBVISION,
+	.fops		= &usbvision_fops,
+	.name           = "usbvision-video",
+	.release	= video_device_release,
+	.minor		= -1,
+};
+
+
+// Radio template
+static struct file_operations usbvision_radio_fops = {
+	.owner             = THIS_MODULE,
+	.open		= usbvision_radio_open,
+	.release	= usbvision_radio_close,
+	.ioctl		= usbvision_radio_ioctl,
+	.llseek		= no_llseek,
+};
+
+static struct video_device usbvision_radio_template=
+{
+	.owner             = THIS_MODULE,
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_USBVISION,
+	.fops		= &usbvision_radio_fops,
+	.release	= video_device_release,
+	.name           = "usbvision-radio",
+	.minor		= -1,
+};
+
+
+// vbi template
+static struct file_operations usbvision_vbi_fops = {
+	.owner             = THIS_MODULE,
+	.open		= usbvision_vbi_open,
+	.release	= usbvision_vbi_close,
+	.ioctl		= usbvision_vbi_ioctl,
+	.llseek		= no_llseek,
+};
+
+static struct video_device usbvision_vbi_template=
+{
+	.owner             = THIS_MODULE,
+	.type		= VID_TYPE_TUNER,
+	.hardware	= VID_HARDWARE_USBVISION,
+	.fops		= &usbvision_vbi_fops,
+	.release	= video_device_release,
+	.name           = "usbvision-vbi",
+	.minor		= -1,
+};
+
+
+static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
+					struct video_device *vdev_template,
+					char *name)
+{
+	struct usb_device *usb_dev = usbvision->dev;
+	struct video_device *vdev;
+
+	if (usb_dev == NULL) {
+		err("%s: usbvision->dev is not set", __FUNCTION__);
+		return NULL;
+	}
+
+	vdev = video_device_alloc();
+	if (NULL == vdev) {
+		return NULL;
+	}
+	*vdev = *vdev_template;
+//	vdev->minor   = -1;
+	vdev->dev     = &usb_dev->dev;
+	snprintf(vdev->name, sizeof(vdev->name), "%s", name);
+	video_set_drvdata(vdev, usbvision);
+	return vdev;
+}
+
+// unregister video4linux devices
+static void usbvision_unregister_video(struct usb_usbvision *usbvision)
+{
+	// vbi Device:
+	if (usbvision->vbi) {
+		PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", usbvision->vbi->minor & 0x1f);
+		if (usbvision->vbi->minor != -1) {
+			video_unregister_device(usbvision->vbi);
+		}
+		else {
+			video_device_release(usbvision->vbi);
+		}
+		usbvision->vbi = NULL;
+	}
+
+	// Radio Device:
+	if (usbvision->rdev) {
+		PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f);
+		if (usbvision->rdev->minor != -1) {
+			video_unregister_device(usbvision->rdev);
+		}
+		else {
+			video_device_release(usbvision->rdev);
+		}
+		usbvision->rdev = NULL;
+	}
+
+	// Video Device:
+	if (usbvision->vdev) {
+		PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", usbvision->vdev->minor & 0x1f);
+		if (usbvision->vdev->minor != -1) {
+			video_unregister_device(usbvision->vdev);
+		}
+		else {
+			video_device_release(usbvision->vdev);
+		}
+		usbvision->vdev = NULL;
+	}
+}
+
+// register video4linux devices
+static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
+{
+	// Video Device:
+	usbvision->vdev = usbvision_vdev_init(usbvision, &usbvision_video_template, "USBVision Video");
+	if (usbvision->vdev == NULL) {
+		goto err_exit;
+	}
+	if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) {
+		goto err_exit;
+	}
+	info("USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]", usbvision->nr,usbvision->vdev->minor & 0x1f);
+
+	// Radio Device:
+	if (usbvision_device_data[usbvision->DevModel].Radio) {
+		// usbvision has radio
+		usbvision->rdev = usbvision_vdev_init(usbvision, &usbvision_radio_template, "USBVision Radio");
+		if (usbvision->rdev == NULL) {
+			goto err_exit;
+		}
+		if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) {
+			goto err_exit;
+		}
+		info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f);
+	}
+	// vbi Device:
+	if (usbvision_device_data[usbvision->DevModel].vbi) {
+		usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI");
+		if (usbvision->vdev == NULL) {
+			goto err_exit;
+		}
+		if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) {
+			goto err_exit;
+		}
+		info("USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)", usbvision->nr,usbvision->vbi->minor & 0x1f);
+	}
+	// all done
+	return 0;
+
+ err_exit:
+	err("USBVision[%d]: video_register_device() failed", usbvision->nr);
+	usbvision_unregister_video(usbvision);
+	return -1;
+}
+
+/*
+ * usbvision_alloc()
+ *
+ * This code allocates the struct usb_usbvision. It is filled with default values.
+ *
+ * Returns NULL on error, a pointer to usb_usbvision else.
+ *
+ */
+static struct usb_usbvision *usbvision_alloc(struct usb_device *dev)
+{
+	struct usb_usbvision *usbvision;
+
+	if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) {
+		goto err_exit;
+	}
+
+	usbvision->dev = dev;
+
+	init_MUTEX(&usbvision->lock);	/* to 1 == available */
+
+	// prepare control urb for control messages during interrupts
+	usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+	if (usbvision->ctrlUrb == NULL) {
+		goto err_exit;
+	}
+	init_waitqueue_head(&usbvision->ctrlUrb_wq);
+	init_MUTEX(&usbvision->ctrlUrbLock);	/* to 1 == available */
+
+	usbvision_init_powerOffTimer(usbvision);
+
+	return usbvision;
+
+err_exit:
+	if (usbvision && usbvision->ctrlUrb) {
+		usb_free_urb(usbvision->ctrlUrb);
+	}
+	if (usbvision) {
+		kfree(usbvision);
+	}
+	return NULL;
+}
+
+/*
+ * usbvision_release()
+ *
+ * This code does final release of struct usb_usbvision. This happens
+ * after the device is disconnected -and- all clients closed their files.
+ *
+ */
+static void usbvision_release(struct usb_usbvision *usbvision)
+{
+	PDEBUG(DBG_PROBE, "");
+
+	down(&usbvision->lock);
+
+	usbvision_reset_powerOffTimer(usbvision);
+
+	usbvision->initialized = 0;
+
+	up(&usbvision->lock);
+
+	usbvision_remove_sysfs(usbvision->vdev);
+	usbvision_unregister_video(usbvision);
+
+	if (usbvision->ctrlUrb) {
+		usb_free_urb(usbvision->ctrlUrb);
+	}
+
+	kfree(usbvision);
+
+	PDEBUG(DBG_PROBE, "success");
+}
+
+
+/******************************** usb interface *****************************************/
+
+static void usbvision_configure_video(struct usb_usbvision *usbvision)
+{
+	int model,i;
+
+	if (usbvision == NULL)
+		return;
+
+	model = usbvision->DevModel;
+	usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24;
+
+	if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) {
+		usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff;
+	} else {
+		usbvision->Vin_Reg2_Preset = 0;
+	}
+
+	for (i = 0; i < TVNORMS; i++)
+		if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode)
+			break;
+	if (i == TVNORMS)
+		i = 0;
+	usbvision->tvnorm = &tvnorms[i];        /* set default norm */
+
+	usbvision->video_inputs = usbvision_device_data[model].VideoChannels;
+	usbvision->ctl_input = 0;
+
+	/* This should be here to make i2c clients to be able to register */
+	usbvision_audio_off(usbvision);	//first switch off audio
+	if (!PowerOnAtOpen) {
+		usbvision_power_on(usbvision);	//and then power up the noisy tuner
+		usbvision_init_i2c(usbvision);
+	}
+}
+
+/*
+ * usbvision_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like USBVISION video device
+ *
+ */
+static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	__u8 ifnum = intf->altsetting->desc.bInterfaceNumber;
+	const struct usb_host_interface *interface;
+	struct usb_usbvision *usbvision = NULL;
+	const struct usb_endpoint_descriptor *endpoint;
+	int model;
+
+	PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
+					dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+	/* Is it an USBVISION video dev? */
+	model = 0;
+	for(model = 0; usbvision_device_data[model].idVendor; model++) {
+		if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) {
+			continue;
+		}
+		if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) {
+			continue;
+		}
+
+		info("%s: %s found", __FUNCTION__, usbvision_device_data[model].ModelString);
+		break;
+	}
+
+	if (usbvision_device_data[model].idVendor == 0) {
+		return -ENODEV; //no matching device
+	}
+	if (usbvision_device_data[model].Interface >= 0) {
+		interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0];
+	}
+	else {
+		interface = &dev->actconfig->interface[ifnum]->altsetting[0];
+	}
+	endpoint = &interface->endpoint[1].desc;
+	if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) {
+		err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum);
+		err("%s: Endpoint attribures %d", __FUNCTION__, endpoint->bmAttributes);
+		return -ENODEV;
+	}
+	if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) {
+		err("%s: interface %d. has ISO OUT endpoint!", __FUNCTION__, ifnum);
+		return -ENODEV;
+	}
+
+	usb_get_dev(dev);
+
+	if ((usbvision = usbvision_alloc(dev)) == NULL) {
+		err("%s: couldn't allocate USBVision struct", __FUNCTION__);
+		return -ENOMEM;
+	}
+	if (dev->descriptor.bNumConfigurations > 1) {
+		usbvision->bridgeType = BRIDGE_NT1004;
+	}
+	else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") {
+		usbvision->bridgeType = BRIDGE_NT1005;
+	}
+	else {
+		usbvision->bridgeType = BRIDGE_NT1003;
+	}
+	PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType);
+
+	down(&usbvision->lock);
+
+	usbvision->nr = usbvision_nr++;
+
+	usbvision->have_tuner = usbvision_device_data[model].Tuner;
+	if (usbvision->have_tuner) {
+		usbvision->tuner_type = usbvision_device_data[model].TunerType;
+	}
+
+	usbvision->tuner_addr = ADDR_UNSET;
+
+	usbvision->DevModel = model;
+	usbvision->remove_pending = 0;
+	usbvision->iface = ifnum;
+	usbvision->ifaceAltInactive = 0;
+	usbvision->ifaceAltActive = 1;
+	usbvision->video_endp = endpoint->bEndpointAddress;
+	usbvision->isocPacketSize = 0;
+	usbvision->usb_bandwidth = 0;
+	usbvision->user = 0;
+	usbvision->streaming = Stream_Off;
+	usbvision_register_video(usbvision);
+	usbvision_configure_video(usbvision);
+	up(&usbvision->lock);
+
+
+	usb_set_intfdata (intf, usbvision);
+	usbvision_create_sysfs(usbvision->vdev);
+
+	PDEBUG(DBG_PROBE, "success");
+	return 0;
+}
+
+
+/*
+ * usbvision_disconnect()
+ *
+ * This procedure stops all driver activity, deallocates interface-private
+ * structure (pointed by 'ptr') and after that driver should be removable
+ * with no ill consequences.
+ *
+ */
+static void __devexit usbvision_disconnect(struct usb_interface *intf)
+{
+	struct usb_usbvision *usbvision = usb_get_intfdata(intf);
+
+	PDEBUG(DBG_PROBE, "");
+
+	if (usbvision == NULL) {
+		err("%s: usb_get_intfdata() failed", __FUNCTION__);
+		return;
+	}
+	usb_set_intfdata (intf, NULL);
+
+	down(&usbvision->lock);
+
+	// At this time we ask to cancel outstanding URBs
+	usbvision_stop_isoc(usbvision);
+
+	if (usbvision->power) {
+		usbvision_i2c_usb_del_bus(&usbvision->i2c_adap);
+		usbvision_power_off(usbvision);
+	}
+	usbvision->remove_pending = 1;	// Now all ISO data will be ignored
+
+	usb_put_dev(usbvision->dev);
+	usbvision->dev = NULL;	// USB device is no more
+
+	up(&usbvision->lock);
+
+	if (usbvision->user) {
+		info("%s: In use, disconnect pending", __FUNCTION__);
+		wake_up_interruptible(&usbvision->wait_frame);
+		wake_up_interruptible(&usbvision->wait_stream);
+	}
+	else {
+		usbvision_release(usbvision);
+	}
+
+	PDEBUG(DBG_PROBE, "success");
+
+}
+
+static struct usb_driver usbvision_driver = {
+	.name		= "usbvision",
+	.id_table	= usbvision_table,
+	.probe		= usbvision_probe,
+	.disconnect	= usbvision_disconnect
+};
+
+/*
+ * customdevice_process()
+ *
+ * This procedure preprocesses CustomDevice parameter if any
+ *
+ */
+void customdevice_process(void)
+{
+	usbvision_device_data[0]=usbvision_device_data[1];
+	usbvision_table[0]=usbvision_table[1];
+
+	if(CustomDevice)
+	{
+		char *parse=CustomDevice;
+
+		PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice);
+
+		/*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1"
+		usbvision_device_data[0].idVendor;
+		usbvision_device_data[0].idProduct;
+		usbvision_device_data[0].Interface;
+		usbvision_device_data[0].Codec;
+		usbvision_device_data[0].VideoChannels;
+		usbvision_device_data[0].VideoNorm;
+		usbvision_device_data[0].AudioChannels;
+		usbvision_device_data[0].Radio;
+		usbvision_device_data[0].Tuner;
+		usbvision_device_data[0].TunerType;
+		usbvision_device_data[0].Vin_Reg1;
+		usbvision_device_data[0].Vin_Reg2;
+		usbvision_device_data[0].X_Offset;
+		usbvision_device_data[0].Y_Offset;
+		usbvision_device_data[0].Dvi_yuv;
+		usbvision_device_data[0].ModelString;
+		*/
+
+		rmspace(parse);
+		usbvision_device_data[0].ModelString="USBVISION Custom Device";
+
+		parse+=2;
+		sscanf(parse,"%x",&usbvision_device_data[0].idVendor);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor);
+		parse+=2;
+		sscanf(parse,"%x",&usbvision_device_data[0].idProduct);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct);
+		sscanf(parse,"%d",&usbvision_device_data[0].Interface);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface);
+		sscanf(parse,"%d",&usbvision_device_data[0].Codec);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec);
+		sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels);
+
+		switch(*parse)
+		{
+			case 'P':
+				PDEBUG(DBG_PROBE, "VideoNorm=PAL");
+				usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL;
+				break;
+
+			case 'S':
+				PDEBUG(DBG_PROBE, "VideoNorm=SECAM");
+				usbvision_device_data[0].VideoNorm=VIDEO_MODE_SECAM;
+				break;
+
+			case 'N':
+				PDEBUG(DBG_PROBE, "VideoNorm=NTSC");
+				usbvision_device_data[0].VideoNorm=VIDEO_MODE_NTSC;
+				break;
+
+			default:
+				PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)");
+				usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL;
+				break;
+		}
+		goto2next(parse);
+
+		sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels);
+		sscanf(parse,"%d",&usbvision_device_data[0].Radio);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio);
+		sscanf(parse,"%d",&usbvision_device_data[0].Tuner);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner);
+		sscanf(parse,"%d",&usbvision_device_data[0].TunerType);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType);
+		sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1);
+		sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2);
+		sscanf(parse,"%d",&usbvision_device_data[0].X_Offset);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset);
+		sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset);
+		goto2next(parse);
+		PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset);
+		sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv);
+		PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv);
+
+		//add to usbvision_table also
+		usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE;
+		usbvision_table[0].idVendor=usbvision_device_data[0].idVendor;
+		usbvision_table[0].idProduct=usbvision_device_data[0].idProduct;
+
+	}
+}
+
+
+
+/*
+ * usbvision_init()
+ *
+ * This code is run to initialize the driver.
+ *
+ */
+static int __init usbvision_init(void)
+{
+	int errCode;
+
+	PDEBUG(DBG_PROBE, "");
+
+	PDEBUG(DBG_IOCTL, "IOCTL   debugging is enabled [video]");
+	PDEBUG(DBG_IO,  "IO      debugging is enabled [video]");
+	PDEBUG(DBG_PROBE, "PROBE   debugging is enabled [video]");
+	PDEBUG(DBG_MMAP, "MMAP    debugging is enabled [video]");
+
+	/* disable planar mode support unless compression enabled */
+	if (isocMode != ISOC_MODE_COMPRESS ) {
+		// FIXME : not the right way to set supported flag
+		usbvision_v4l2_format[6].supported = 0; // V4L2_PIX_FMT_YVU420
+		usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P
+	}
+
+	customdevice_process();
+
+	errCode = usb_register(&usbvision_driver);
+
+	if (errCode == 0) {
+		info(DRIVER_DESC " : " USBVISION_VERSION_STRING);
+		PDEBUG(DBG_PROBE, "success");
+	}
+	return errCode;
+}
+
+static void __exit usbvision_exit(void)
+{
+ PDEBUG(DBG_PROBE, "");
+
+ usb_deregister(&usbvision_driver);
+ PDEBUG(DBG_PROBE, "success");
+}
+
+module_init(usbvision_init);
+module_exit(usbvision_exit);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
new file mode 100644
index 0000000..0e7e3d6
--- /dev/null
+++ b/drivers/media/video/usbvision/usbvision.h
@@ -0,0 +1,558 @@
+/*
+ * USBVISION.H
+ *  usbvision header file
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *                         Dwaine Garden <dwainegarden@rogers.com>
+ *
+ *
+ * Report problems to v4l MailingList : http://www.redhat.com/mailman/listinfo/video4linux-list
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+ * v4l2 conversion by Thierry Merle <thierry.merle@free.fr>
+ *
+ * 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.
+ */
+
+
+#ifndef __LINUX_USBVISION_H
+#define __LINUX_USBVISION_H
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+#include <linux/videodev2.h>
+
+#define USBVISION_DEBUG		/* Turn on debug messages */
+
+#ifndef VID_HARDWARE_USBVISION
+	#define VID_HARDWARE_USBVISION 34   /* USBVision Video Grabber */
+#endif
+
+#define USBVISION_PWR_REG		0x00
+	#define USBVISION_SSPND_EN		(1 << 1)
+	#define USBVISION_RES2			(1 << 2)
+	#define USBVISION_PWR_VID		(1 << 5)
+	#define USBVISION_E2_EN			(1 << 7)
+#define USBVISION_CONFIG_REG		0x01
+#define USBVISION_ADRS_REG		0x02
+#define USBVISION_ALTER_REG		0x03
+#define USBVISION_FORCE_ALTER_REG	0x04
+#define USBVISION_STATUS_REG		0x05
+#define USBVISION_IOPIN_REG		0x06
+	#define USBVISION_IO_1			(1 << 0)
+	#define USBVISION_IO_2			(1 << 1)
+	#define USBVISION_AUDIO_IN		0
+	#define USBVISION_AUDIO_TV		1
+	#define USBVISION_AUDIO_RADIO		2
+	#define USBVISION_AUDIO_MUTE		3
+#define USBVISION_SER_MODE		0x07
+#define USBVISION_SER_ADRS		0x08
+#define USBVISION_SER_CONT		0x09
+#define USBVISION_SER_DAT1		0x0A
+#define USBVISION_SER_DAT2		0x0B
+#define USBVISION_SER_DAT3		0x0C
+#define USBVISION_SER_DAT4		0x0D
+#define USBVISION_EE_DATA		0x0E
+#define USBVISION_EE_LSBAD		0x0F
+#define USBVISION_EE_CONT		0x10
+#define USBVISION_DRM_CONT			0x12
+	#define USBVISION_REF			(1 << 0)
+	#define USBVISION_RES_UR		(1 << 2)
+	#define USBVISION_RES_FDL		(1 << 3)
+	#define USBVISION_RES_VDW		(1 << 4)
+#define USBVISION_DRM_PRM1		0x13
+#define USBVISION_DRM_PRM2		0x14
+#define USBVISION_DRM_PRM3		0x15
+#define USBVISION_DRM_PRM4		0x16
+#define USBVISION_DRM_PRM5		0x17
+#define USBVISION_DRM_PRM6		0x18
+#define USBVISION_DRM_PRM7		0x19
+#define USBVISION_DRM_PRM8		0x1A
+#define USBVISION_VIN_REG1		0x1B
+	#define USBVISION_8_422_SYNC		0x01
+	#define USBVISION_16_422_SYNC		0x02
+	#define USBVISION_VSNC_POL		(1 << 3)
+	#define USBVISION_HSNC_POL		(1 << 4)
+	#define USBVISION_FID_POL		(1 << 5)
+	#define USBVISION_HVALID_PO		(1 << 6)
+	#define USBVISION_VCLK_POL		(1 << 7)
+#define USBVISION_VIN_REG2		0x1C
+	#define USBVISION_AUTO_FID		(1 << 0)
+	#define USBVISION_NONE_INTER		(1 << 1)
+	#define USBVISION_NOHVALID		(1 << 2)
+	#define USBVISION_UV_ID			(1 << 3)
+	#define USBVISION_FIX_2C		(1 << 4)
+	#define USBVISION_SEND_FID		(1 << 5)
+	#define USBVISION_KEEP_BLANK		(1 << 7)
+#define USBVISION_LXSIZE_I		0x1D
+#define USBVISION_MXSIZE_I		0x1E
+#define USBVISION_LYSIZE_I		0x1F
+#define USBVISION_MYSIZE_I		0x20
+#define USBVISION_LX_OFFST		0x21
+#define USBVISION_MX_OFFST		0x22
+#define USBVISION_LY_OFFST		0x23
+#define USBVISION_MY_OFFST		0x24
+#define USBVISION_FRM_RATE		0x25
+#define USBVISION_LXSIZE_O		0x26
+#define USBVISION_MXSIZE_O		0x27
+#define USBVISION_LYSIZE_O		0x28
+#define USBVISION_MYSIZE_O		0x29
+#define USBVISION_FILT_CONT		0x2A
+#define USBVISION_VO_MODE		0x2B
+#define USBVISION_INTRA_CYC		0x2C
+#define USBVISION_STRIP_SZ		0x2D
+#define USBVISION_FORCE_INTRA		0x2E
+#define USBVISION_FORCE_UP		0x2F
+#define USBVISION_BUF_THR		0x30
+#define USBVISION_DVI_YUV		0x31
+#define USBVISION_AUDIO_CONT		0x32
+#define USBVISION_AUD_PK_LEN		0x33
+#define USBVISION_BLK_PK_LEN		0x34
+#define USBVISION_PCM_THR1		0x38
+#define USBVISION_PCM_THR2		0x39
+#define USBVISION_DIST_THR_L		0x3A
+#define USBVISION_DIST_THR_H		0x3B
+#define USBVISION_MAX_DIST_L		0x3C
+#define USBVISION_MAX_DIST_H		0x3D
+#define USBVISION_OP_CODE		0x33
+
+#define MAX_BYTES_PER_PIXEL		4
+
+#define MIN_FRAME_WIDTH			64
+#define MAX_USB_WIDTH			320  //384
+#define MAX_FRAME_WIDTH			320  //384			/*streching sometimes causes crashes*/
+
+#define MIN_FRAME_HEIGHT		48
+#define MAX_USB_HEIGHT			240  //288
+#define MAX_FRAME_HEIGHT		240  //288			/*Streching sometimes causes crashes*/
+
+#define MAX_FRAME_SIZE     		(MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL)
+#define USBVISION_CLIPMASK_SIZE		(MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) //bytesize of clipmask
+
+#define USBVISION_URB_FRAMES		32
+#define USBVISION_MAX_ISOC_PACKET_SIZE 	959			// NT1003 Specs Document says 1023
+
+#define USBVISION_NUM_HEADERMARKER	20
+#define USBVISION_NUMFRAMES		3  /* Maximum number of frames an application can get */
+#define USBVISION_NUMSBUF		2 /* Dimensioning the USB S buffering */
+
+#define USBVISION_POWEROFF_TIME		3 * (HZ)		// 3 seconds
+
+
+#define FRAMERATE_MIN	0
+#define FRAMERATE_MAX	31
+
+enum {
+	ISOC_MODE_YUV422 = 0x03,
+	ISOC_MODE_YUV420 = 0x14,
+	ISOC_MODE_COMPRESS = 0x60,
+};
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
+
+/*
+ * We use macros to do YUV -> RGB conversion because this is
+ * very important for speed and totally unimportant for size.
+ *
+ * YUV -> RGB Conversion
+ * ---------------------
+ *
+ * B = 1.164*(Y-16)		    + 2.018*(V-128)
+ * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
+ * R = 1.164*(Y-16) + 1.596*(U-128)
+ *
+ * If you fancy integer arithmetics (as you should), hear this:
+ *
+ * 65536*B = 76284*(Y-16)		  + 132252*(V-128)
+ * 65536*G = 76284*(Y-16) -  53281*(U-128) -  25625*(V-128)
+ * 65536*R = 76284*(Y-16) + 104595*(U-128)
+ *
+ * Make sure the output values are within [0..255] range.
+ */
+#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
+#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \
+    int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
+    mm_y = (my) - 16;  \
+    mm_u = (mu) - 128; \
+    mm_v = (mv) - 128; \
+    mm_yc= mm_y * 76284; \
+    mm_b = (mm_yc		+ 132252*mm_v	) >> 16; \
+    mm_g = (mm_yc -  53281*mm_u -  25625*mm_v	) >> 16; \
+    mm_r = (mm_yc + 104595*mm_u			) >> 16; \
+    mb = LIMIT_RGB(mm_b); \
+    mg = LIMIT_RGB(mm_g); \
+    mr = LIMIT_RGB(mm_r); \
+}
+
+/* Debugging aid */
+#define USBVISION_SAY_AND_WAIT(what) { \
+	wait_queue_head_t wq; \
+	init_waitqueue_head(&wq); \
+	printk(KERN_INFO "Say: %s\n", what); \
+	interruptible_sleep_on_timeout (&wq, HZ*3); \
+}
+
+/*
+ * This macro checks if usbvision is still operational. The 'usbvision'
+ * pointer must be valid, usbvision->dev must be valid, we are not
+ * removing the device and the device has not erred on us.
+ */
+#define USBVISION_IS_OPERATIONAL(udevice) (\
+	(udevice != NULL) && \
+	((udevice)->dev != NULL) && \
+	((udevice)->last_error == 0) && \
+	(!(udevice)->remove_pending))
+
+/* I2C structures */
+struct i2c_algo_usb_data {
+	void *data;		/* private data for lowlevel routines */
+	int (*inb) (void *data, unsigned char addr, char *buf, short len);
+	int (*outb) (void *data, unsigned char addr, char *buf, short len);
+
+	/* local settings */
+	int udelay;
+	int mdelay;
+	int timeout;
+};
+
+#define I2C_USB_ADAP_MAX	16
+
+/* ----------------------------------------------------------------- */
+/* usbvision video structures                                        */
+/* ----------------------------------------------------------------- */
+enum ScanState {
+	ScanState_Scanning,	/* Scanning for header */
+	ScanState_Lines		/* Parsing lines */
+};
+
+/* Completion states of the data parser */
+enum ParseState {
+	ParseState_Continue,	/* Just parse next item */
+	ParseState_NextFrame,	/* Frame done, send it to V4L */
+	ParseState_Out,		/* Not enough data for frame */
+	ParseState_EndParse	/* End parsing */
+};
+
+enum FrameState {
+	FrameState_Unused,	/* Unused (no MCAPTURE) */
+	FrameState_Ready,	/* Ready to start grabbing */
+	FrameState_Grabbing,	/* In the process of being grabbed into */
+	FrameState_Done,	/* Finished grabbing, but not been synced yet */
+	FrameState_DoneHold,	/* Are syncing or reading */
+	FrameState_Error,	/* Something bad happened while processing */
+};
+
+/* stream states */
+enum StreamState {
+	Stream_Off,		/* Driver streaming is completely OFF */
+	Stream_Idle,		/* Driver streaming is ready to be put ON by the application */
+	Stream_Interrupt,	/* Driver streaming must be interrupted */
+	Stream_On,		/* Driver streaming is put ON by the application */
+};
+
+enum IsocState {
+	IsocState_InFrame,	/* Isoc packet is member of frame */
+	IsocState_NoFrame,	/* Isoc packet is not member of any frame */
+};
+
+struct usb_device;
+
+struct usbvision_sbuf {
+	char *data;
+	struct urb *urb;
+};
+
+#define USBVISION_MAGIC_1      			0x55
+#define USBVISION_MAGIC_2      			0xAA
+#define USBVISION_HEADER_LENGTH			0x0c
+#define USBVISION_SAA7111_ADDR			0x48
+#define USBVISION_SAA7113_ADDR			0x4a
+#define USBVISION_IIC_LRACK			0x20
+#define USBVISION_IIC_LRNACK			0x30
+#define USBVISION_FRAME_FORMAT_PARAM_INTRA	(1<<7)
+
+struct usbvision_v4l2_format_st {
+	int		supported;
+	int		bytes_per_pixel;
+	int		depth;
+	int		format;
+	char		*desc;
+};
+#define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format)
+
+struct usbvision_frame_header {
+	unsigned char magic_1;				/* 0 magic */
+	unsigned char magic_2;				/* 1  magic */
+	unsigned char headerLength;			/* 2 */
+	unsigned char frameNum;				/* 3 */
+	unsigned char framePhase;			/* 4 */
+	unsigned char frameLatency;			/* 5 */
+	unsigned char dataFormat;			/* 6 */
+	unsigned char formatParam;			/* 7 */
+	unsigned char frameWidthLo;			/* 8 */
+	unsigned char frameWidthHi;			/* 9 */
+	unsigned char frameHeightLo;			/* 10 */
+	unsigned char frameHeightHi;			/* 11 */
+	__u16 frameWidth;				/* 8 - 9 after endian correction*/
+	__u16 frameHeight;				/* 10 - 11 after endian correction*/
+};
+
+/* tvnorms */
+struct usbvision_tvnorm {
+	char *name;
+	v4l2_std_id id;
+	/* mode for saa7113h */
+	int mode;
+};
+
+struct usbvision_frame {
+	char *data;					/* Frame buffer */
+	struct usbvision_frame_header isocHeader;	/* Header from stream */
+
+	int width;					/* Width application is expecting */
+	int height;					/* Height */
+	int index;					/* Frame index */
+	int frmwidth;					/* Width the frame actually is */
+	int frmheight;					/* Height */
+
+	volatile int grabstate;				/* State of grabbing */
+	int scanstate;					/* State of scanning */
+
+	struct list_head frame;
+
+	int curline;					/* Line of frame we're working on */
+
+	long scanlength;				/* uncompressed, raw data length of frame */
+	long bytes_read;				/* amount of scanlength that has been read from data */
+	struct usbvision_v4l2_format_st v4l2_format;	/* format the user needs*/
+	int v4l2_linesize;				/* bytes for one videoline*/
+	struct timeval timestamp;
+	int sequence;					// How many video frames we send to user
+};
+
+#define CODEC_SAA7113	7113
+#define CODEC_SAA7111	7111
+#define BRIDGE_NT1003	1003
+#define BRIDGE_NT1004	1004
+#define BRIDGE_NT1005   1005
+
+struct usbvision_device_data_st {
+	int idVendor;
+	int idProduct;
+	int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */
+	int Codec;
+	int VideoChannels;
+	__u64 VideoNorm;
+	int AudioChannels;
+	int Radio;
+	int vbi;
+	int Tuner;
+	int TunerType;
+	int Vin_Reg1;
+	int Vin_Reg2;
+	int X_Offset;
+	int Y_Offset;
+	int Dvi_yuv;
+	char *ModelString;
+};
+
+/* Declared on usbvision-cards.c */
+extern struct usbvision_device_data_st usbvision_device_data[];
+extern struct usb_device_id usbvision_table[];
+
+struct usb_usbvision {
+	struct video_device *vdev;         				/* Video Device */
+	struct video_device *rdev;               			/* Radio Device */
+	struct video_device *vbi; 					/* VBI Device   */
+
+	/* i2c Declaration Section*/
+	struct i2c_adapter i2c_adap;
+	struct i2c_algo_usb_data i2c_algo;
+	struct i2c_client i2c_client;
+
+	struct urb *ctrlUrb;
+	unsigned char ctrlUrbBuffer[8];
+	int ctrlUrbBusy;
+	struct usb_ctrlrequest ctrlUrbSetup;
+	wait_queue_head_t ctrlUrb_wq;					// Processes waiting
+	struct semaphore ctrlUrbLock;
+
+	/* configuration part */
+	int have_tuner;
+	int tuner_type;
+	int tuner_addr;
+	int bridgeType;							// NT1003, NT1004, NT1005
+	int channel;
+	int radio;
+	int video_inputs;						// # of inputs
+	unsigned long freq;
+	int AudioMute;
+	int AudioChannel;
+	int isocMode;							// format of video data for the usb isoc-transfer
+	unsigned int nr;						// Number of the device
+
+	/* Device structure */
+	struct usb_device *dev;
+	unsigned char iface;						/* Video interface number */
+	unsigned char ifaceAltActive, ifaceAltInactive;			/* Alt settings */
+	unsigned char Vin_Reg2_Preset;
+	struct semaphore lock;
+	struct timer_list powerOffTimer;
+	struct work_struct powerOffWork;
+	int power;							/* is the device powered on? */
+	int user;							/* user count for exclusive use */
+	int initialized;						/* Had we already sent init sequence? */
+	int DevModel;							/* What type of USBVISION device we got? */
+	enum StreamState streaming;					/* Are we streaming Isochronous? */
+	int last_error;							/* What calamity struck us? */
+	int curwidth;							/* width of the frame the device is currently set to*/
+	int curheight;      						/* height of the frame the device is currently set to*/
+	int stretch_width;						/* stretch-factor for frame width (from usb to screen)*/
+	int stretch_height;						/* stretch-factor for frame height (from usb to screen)*/
+	char *fbuf;							/* Videodev buffer area for mmap*/
+	int max_frame_size;						/* Bytes in one video frame */
+	int fbuf_size;							/* Videodev buffer size */
+	spinlock_t queue_lock;						/* spinlock for protecting mods on inqueue and outqueue */
+	struct list_head inqueue, outqueue;                             /* queued frame list and ready to dequeue frame list */
+	wait_queue_head_t wait_frame;					/* Processes waiting */
+	wait_queue_head_t wait_stream;					/* Processes waiting */
+	struct usbvision_frame *curFrame;				// pointer to current frame, set by usbvision_find_header
+	struct usbvision_frame frame[USBVISION_NUMFRAMES];		// frame buffer
+	struct usbvision_sbuf sbuf[USBVISION_NUMSBUF];			// S buffering
+	volatile int remove_pending;					/* If set then about to exit */
+
+	/* Scratch space from the Isochronous Pipe.*/
+	unsigned char *scratch;
+	int scratch_read_ptr;
+	int scratch_write_ptr;
+	int scratch_headermarker[USBVISION_NUM_HEADERMARKER];
+	int scratch_headermarker_read_ptr;
+	int scratch_headermarker_write_ptr;
+	enum IsocState isocstate;
+	struct usbvision_v4l2_format_st palette;
+
+	struct v4l2_capability vcap;					/* Video capabilities */
+	unsigned int ctl_input;						/* selected input */
+	struct usbvision_tvnorm *tvnorm;				/* selected tv norm */
+	unsigned char video_endp;					/* 0x82 for USBVISION devices based */
+
+	// Decompression stuff:
+	unsigned char *IntraFrameBuffer;				/* Buffer for reference frame */
+	int BlockPos; 							//for test only
+	int requestIntra;						// 0 = normal; 1 = intra frame is requested;
+	int lastIsocFrameNum;						// check for lost isoc frames
+	int isocPacketSize;						// need to calculate usedBandwidth
+	int usedBandwidth;						// used bandwidth 0-100%, need to set comprLevel
+	int comprLevel;							// How strong (100) or weak (0) is compression
+	int lastComprLevel;						// How strong (100) or weak (0) was compression
+	int usb_bandwidth;						/* Mbit/s */
+
+	/* Statistics that can be overlayed on the screen */
+	unsigned long isocUrbCount;			// How many URBs we received so far
+	unsigned long urb_length;			/* Length of last URB */
+	unsigned long isocDataCount;			/* How many bytes we received */
+	unsigned long header_count;			/* How many frame headers we found */
+	unsigned long scratch_ovf_count;		/* How many times we overflowed scratch */
+	unsigned long isocSkipCount;			/* How many empty ISO packets received */
+	unsigned long isocErrCount;			/* How many bad ISO packets received */
+	unsigned long isocPacketCount;			// How many packets we totally got
+	unsigned long timeInIrq;			// How long do we need for interrupt
+	int isocMeasureBandwidthCount;
+	int frame_num;					// How many video frames we send to user
+	int maxStripLen;				// How big is the biggest strip
+	int comprBlockPos;
+	int stripLenErrors;				// How many times was BlockPos greater than StripLen
+	int stripMagicErrors;
+	int stripLineNumberErrors;
+	int ComprBlockTypes[4];
+};
+
+
+/* --------------------------------------------------------------- */
+/* defined in usbvision-i2c.c                                      */
+/* i2c-algo-usb declaration                                        */
+/* --------------------------------------------------------------- */
+
+int usbvision_i2c_usb_add_bus(struct i2c_adapter *);
+int usbvision_i2c_usb_del_bus(struct i2c_adapter *);
+
+static inline void *i2c_get_algo_usb_data (struct i2c_algo_usb_data *dev)
+{
+	return dev->data;
+}
+
+static inline void i2c_set_algo_usb_data (struct i2c_algo_usb_data *dev, void *data)
+{
+	dev->data = data;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* usbvision specific I2C functions                                        */
+/* ----------------------------------------------------------------------- */
+int usbvision_init_i2c(struct usb_usbvision *usbvision);
+void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg);
+
+/* defined in usbvision-core.c                                      */
+void *usbvision_rvmalloc(unsigned long size);
+void usbvision_rvfree(void *mem, unsigned long size);
+int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg);
+int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
+			unsigned char value);
+
+int usbvision_frames_alloc(struct usb_usbvision *usbvision);
+void usbvision_frames_free(struct usb_usbvision *usbvision);
+int usbvision_scratch_alloc(struct usb_usbvision *usbvision);
+void usbvision_scratch_free(struct usb_usbvision *usbvision);
+int usbvision_sbuf_alloc(struct usb_usbvision *usbvision);
+void usbvision_sbuf_free(struct usb_usbvision *usbvision);
+int usbvision_decompress_alloc(struct usb_usbvision *usbvision);
+void usbvision_decompress_free(struct usb_usbvision *usbvision);
+
+int usbvision_setup(struct usb_usbvision *usbvision,int format);
+int usbvision_init_isoc(struct usb_usbvision *usbvision);
+int usbvision_restart_isoc(struct usb_usbvision *usbvision);
+void usbvision_stop_isoc(struct usb_usbvision *usbvision);
+
+int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel);
+int usbvision_audio_off(struct usb_usbvision *usbvision);
+
+int usbvision_begin_streaming(struct usb_usbvision *usbvision);
+void usbvision_empty_framequeues(struct usb_usbvision *dev);
+int usbvision_stream_interrupt(struct usb_usbvision *dev);
+
+int usbvision_muxsel(struct usb_usbvision *usbvision, int channel);
+int usbvision_set_input(struct usb_usbvision *usbvision);
+int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height);
+
+void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision);
+void usbvision_set_powerOffTimer(struct usb_usbvision *usbvision);
+void usbvision_reset_powerOffTimer(struct usb_usbvision *usbvision);
+int usbvision_power_off(struct usb_usbvision *usbvision);
+int usbvision_power_on(struct usb_usbvision *usbvision);
+
+#endif									/* __LINUX_USBVISION_H */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c
index 1d899e2..8a13e59 100644
--- a/drivers/media/video/v4l1-compat.c
+++ b/drivers/media/video/v4l1-compat.c
@@ -350,6 +350,7 @@
 		struct video_buffer	*buffer = arg;
 
 		memset(buffer, 0, sizeof(*buffer));
+		memset(&fbuf2, 0, sizeof(fbuf2));
 
 		err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2);
 		if (err < 0) {
@@ -616,6 +617,7 @@
 	case VIDIOCSPICT: /*  set tone controls & partial capture format  */
 	{
 		struct video_picture	*pict = arg;
+		memset(&fbuf2, 0, sizeof(fbuf2));
 
 		set_v4l_control(inode, file,
 				V4L2_CID_BRIGHTNESS, pict->brightness, drv);
@@ -708,12 +710,22 @@
 	}
 	case VIDIOCSTUNER: /*  select a tuner input  */
 	{
-		err = 0;
+		struct video_tuner	*tun = arg;
+		struct v4l2_tuner	t;
+		memset(&t,0,sizeof(t));
+
+		t.index=tun->tuner;
+
+		err = drv(inode, file, VIDIOC_S_INPUT, &t);
+		if (err < 0)
+			dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err);
+
 		break;
 	}
 	case VIDIOCGFREQ: /*  get frequency  */
 	{
 		unsigned long *freq = arg;
+		memset(&freq2,0,sizeof(freq2));
 
 		freq2.tuner = 0;
 		err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2);
@@ -726,8 +738,8 @@
 	case VIDIOCSFREQ: /*  set frequency  */
 	{
 		unsigned long *freq = arg;
+		memset(&freq2,0,sizeof(freq2));
 
-		freq2.tuner = 0;
 		drv(inode, file, VIDIOC_G_FREQUENCY, &freq2);
 		freq2.frequency = *freq;
 		err = drv(inode, file, VIDIOC_S_FREQUENCY, &freq2);
@@ -738,6 +750,7 @@
 	case VIDIOCGAUDIO: /*  get audio properties/controls  */
 	{
 		struct video_audio	*aud = arg;
+		memset(&aud2,0,sizeof(aud2));
 
 		err = drv(inode, file, VIDIOC_G_AUDIO, &aud2);
 		if (err < 0) {
@@ -898,6 +911,7 @@
 	{
 		int			*i = arg;
 
+		memset(&buf2,0,sizeof(buf2));
 		buf2.index = *i;
 		buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 		err = drv(inode, file, VIDIOC_QUERYBUF, &buf2);
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 78d28b0..752c82c 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -87,6 +87,78 @@
  */
 
 
+char *v4l2_norm_to_name(v4l2_std_id id)
+{
+	char *name;
+
+	switch (id) {
+	case V4L2_STD_PAL:
+		name="PAL";		break;
+	case V4L2_STD_PAL_BG:
+		name="PAL-BG";		break;
+	case V4L2_STD_PAL_DK:
+		name="PAL-DK";		break;
+	case V4L2_STD_PAL_B:
+		name="PAL-B";		break;
+	case V4L2_STD_PAL_B1:
+		name="PAL-B1";		break;
+	case V4L2_STD_PAL_G:
+		name="PAL-G";		break;
+	case V4L2_STD_PAL_H:
+		name="PAL-H";		break;
+	case V4L2_STD_PAL_I:
+		name="PAL-I";		break;
+	case V4L2_STD_PAL_D:
+		name="PAL-D";		break;
+	case V4L2_STD_PAL_D1:
+		name="PAL-D1";		break;
+	case V4L2_STD_PAL_K:
+		name="PAL-K";		break;
+	case V4L2_STD_PAL_M:
+		name="PAL-M";		break;
+	case V4L2_STD_PAL_N:
+		name="PAL-N";		break;
+	case V4L2_STD_PAL_Nc:
+		name="PAL-Nc";		break;
+	case V4L2_STD_PAL_60:
+		name="PAL-60";		break;
+	case V4L2_STD_NTSC:
+		name="NTSC";		break;
+	case V4L2_STD_NTSC_M:
+		name="NTSC-M";		break;
+	case V4L2_STD_NTSC_M_JP:
+		name="NTSC-M-JP";	break;
+	case V4L2_STD_NTSC_443:
+		name="NTSC-443";	break;
+	case V4L2_STD_NTSC_M_KR:
+		name="NTSC-M-KR";	break;
+	case V4L2_STD_SECAM:
+		name="SECAM";		break;
+	case V4L2_STD_SECAM_DK:
+		name="SECAM-DK";	break;
+	case V4L2_STD_SECAM_B:
+		name="SECAM-B";		break;
+	case V4L2_STD_SECAM_D:
+		name="SECAM-D";		break;
+	case V4L2_STD_SECAM_G:
+		name="SECAM-G";		break;
+	case V4L2_STD_SECAM_H:
+		name="SECAM-H";		break;
+	case V4L2_STD_SECAM_K:
+		name="SECAM-K";		break;
+	case V4L2_STD_SECAM_K1:
+		name="SECAM-K1";	break;
+	case V4L2_STD_SECAM_L:
+		name="SECAM-L";		break;
+	case V4L2_STD_SECAM_LC:
+		name="SECAM-LC";	break;
+	default:
+		name="Unknown";		break;
+	}
+
+	return name;
+}
+
 /* Fill in the fields of a v4l2_standard structure according to the
    'id' and 'transmission' parameters.  Returns negative on error.  */
 int v4l2_video_std_construct(struct v4l2_standard *vs,
@@ -184,11 +256,13 @@
 };
 
 char *v4l2_type_names[] = {
-	[V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap",
-	[V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over",
-	[V4L2_BUF_TYPE_VIDEO_OUTPUT]  = "video-out",
-	[V4L2_BUF_TYPE_VBI_CAPTURE]   = "vbi-cap",
-	[V4L2_BUF_TYPE_VBI_OUTPUT]    = "vbi-out",
+	[V4L2_BUF_TYPE_VIDEO_CAPTURE]      = "video-cap",
+	[V4L2_BUF_TYPE_VIDEO_OVERLAY]      = "video-over",
+	[V4L2_BUF_TYPE_VIDEO_OUTPUT]       = "video-out",
+	[V4L2_BUF_TYPE_VBI_CAPTURE]        = "vbi-cap",
+	[V4L2_BUF_TYPE_VBI_OUTPUT]         = "vbi-out",
+	[V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
+	[V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "slicec-vbi-out",
 };
 
 static char *v4l2_memory_names[] = {
@@ -1451,6 +1525,7 @@
 
 /* ----------------------------------------------------------------- */
 
+EXPORT_SYMBOL(v4l2_norm_to_name);
 EXPORT_SYMBOL(v4l2_video_std_construct);
 
 EXPORT_SYMBOL(v4l2_prio_init);
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 41ec0c4..6a0e8ca 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -342,7 +342,7 @@
 
 	dbgarg (cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
 		"bytesused=%d, flags=0x%08d, "
-		"field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx\n",
+		"field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n",
 			(p->timestamp.tv_sec/3600),
 			(int)(p->timestamp.tv_sec/60)%60,
 			(int)(p->timestamp.tv_sec%60),
@@ -352,7 +352,7 @@
 			p->bytesused,p->flags,
 			p->field,p->sequence,
 			prt_names(p->memory,v4l2_memory_names),
-			p->m.userptr);
+			p->m.userptr, p->length);
 	dbgarg2 ("timecode= %02d:%02d:%02d type=%d, "
 		"flags=0x%08d, frames=%d, userbits=0x%08x\n",
 			tc->hours,tc->minutes,tc->seconds,
@@ -369,9 +369,13 @@
 static inline void v4l_print_pix_fmt (struct video_device *vfd,
 						struct v4l2_pix_format *fmt)
 {
-	dbgarg2 ("width=%d, height=%d, format=0x%08x, field=%s, "
+	dbgarg2 ("width=%d, height=%d, format=%c%c%c%c, field=%s, "
 		"bytesperline=%d sizeimage=%d, colorspace=%d\n",
-		fmt->width,fmt->height,fmt->pixelformat,
+		fmt->width,fmt->height,
+		(fmt->pixelformat & 0xff),
+		(fmt->pixelformat >>  8) & 0xff,
+		(fmt->pixelformat >> 16) & 0xff,
+		(fmt->pixelformat >> 24) & 0xff,
 		prt_names(fmt->field,v4l2_field_names_FIXME),
 		fmt->bytesperline,fmt->sizeimage,fmt->colorspace);
 };
@@ -428,6 +432,10 @@
 		v4l_print_ioctl(vfd->name, cmd);
 	}
 
+	if (_IOC_TYPE(cmd)=='v')
+		return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+						__video_do_ioctl);
+
 	switch(cmd) {
 	/* --- capabilities ------------------------------------------ */
 	case VIDIOC_QUERYCAP:
@@ -526,12 +534,13 @@
 		}
 		if (!ret)
 			dbgarg (cmd, "index=%d, type=%d, flags=%d, "
-					"description=%s,"
-					" pixelformat=0x%8x\n",
+					"pixelformat=%c%c%c%c, description='%s'\n",
 					f->index, f->type, f->flags,
-					f->description,
-					f->pixelformat);
-
+					(f->pixelformat & 0xff),
+					(f->pixelformat >>  8) & 0xff,
+					(f->pixelformat >> 16) & 0xff,
+					(f->pixelformat >> 24) & 0xff,
+					f->description);
 		break;
 	}
 	case VIDIOC_G_FMT:
@@ -829,20 +838,85 @@
 	case VIDIOC_ENUMSTD:
 	{
 		struct v4l2_standard *p = arg;
-		unsigned int index = p->index;
+		v4l2_std_id id = vfd->tvnorms,curr_id=0;
+		unsigned int index = p->index,i;
 
-		if (!vfd->tvnormsize) {
-			printk (KERN_WARNING "%s: no TV norms defined!\n",
-						vfd->name);
-			break;
-		}
-
-		if (index<0 || index >= vfd->tvnormsize) {
+		if (index<0) {
 			ret=-EINVAL;
 			break;
 		}
-		v4l2_video_std_construct(p, vfd->tvnorms[p->index].id,
-					 vfd->tvnorms[p->index].name);
+
+		/* Return norm array on a canonical way */
+		for (i=0;i<= index && id; i++) {
+			if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) {
+				curr_id = V4L2_STD_PAL;
+			} else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) {
+				curr_id = V4L2_STD_PAL_BG;
+			} else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) {
+				curr_id = V4L2_STD_PAL_DK;
+			} else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) {
+				curr_id = V4L2_STD_PAL_B;
+			} else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) {
+				curr_id = V4L2_STD_PAL_B1;
+			} else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) {
+				curr_id = V4L2_STD_PAL_G;
+			} else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) {
+				curr_id = V4L2_STD_PAL_H;
+			} else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) {
+				curr_id = V4L2_STD_PAL_I;
+			} else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) {
+				curr_id = V4L2_STD_PAL_D;
+			} else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) {
+				curr_id = V4L2_STD_PAL_D1;
+			} else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) {
+				curr_id = V4L2_STD_PAL_K;
+			} else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) {
+				curr_id = V4L2_STD_PAL_M;
+			} else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) {
+				curr_id = V4L2_STD_PAL_N;
+			} else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) {
+				curr_id = V4L2_STD_PAL_Nc;
+			} else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) {
+				curr_id = V4L2_STD_PAL_60;
+			} else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) {
+				curr_id = V4L2_STD_NTSC;
+			} else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) {
+				curr_id = V4L2_STD_NTSC_M;
+			} else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) {
+				curr_id = V4L2_STD_NTSC_M_JP;
+			} else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) {
+				curr_id = V4L2_STD_NTSC_443;
+			} else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) {
+				curr_id = V4L2_STD_NTSC_M_KR;
+			} else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) {
+				curr_id = V4L2_STD_SECAM;
+			} else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) {
+				curr_id = V4L2_STD_SECAM_DK;
+			} else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) {
+				curr_id = V4L2_STD_SECAM_B;
+			} else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) {
+				curr_id = V4L2_STD_SECAM_D;
+			} else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) {
+				curr_id = V4L2_STD_SECAM_G;
+			} else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) {
+				curr_id = V4L2_STD_SECAM_H;
+			} else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) {
+				curr_id = V4L2_STD_SECAM_K;
+			} else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) {
+				curr_id = V4L2_STD_SECAM_K1;
+			} else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) {
+				curr_id = V4L2_STD_SECAM_L;
+			} else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) {
+				curr_id = V4L2_STD_SECAM_LC;
+			} else {
+				break;
+			}
+			id &= ~curr_id;
+		}
+		if (i<=index)
+			return -EINVAL;
+
+		v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id));
 		p->index = index;
 
 		dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, "
@@ -868,39 +942,23 @@
 	}
 	case VIDIOC_S_STD:
 	{
-		v4l2_std_id *id = arg;
-		unsigned int i;
-
-		if (!vfd->tvnormsize) {
-			printk (KERN_WARNING "%s: no TV norms defined!\n",
-						vfd->name);
-			break;
-		}
+		v4l2_std_id *id = arg,norm;
 
 		dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id);
 
-		/* First search for exact match */
-		for (i = 0; i < vfd->tvnormsize; i++)
-			if (*id == vfd->tvnorms[i].id)
-				break;
-		/* Then for a generic video std that contains desired std */
-		if (i == vfd->tvnormsize)
-			for (i = 0; i < vfd->tvnormsize; i++)
-				if (*id & vfd->tvnorms[i].id)
-					break;
-		if (i == vfd->tvnormsize) {
+		norm = (*id) & vfd->tvnorms;
+		if ( vfd->tvnorms && !norm)	/* Check if std is supported */
 			break;
-		}
 
 		/* Calls the specific handler */
 		if (vfd->vidioc_s_std)
-			ret=vfd->vidioc_s_std(file, fh, i);
+			ret=vfd->vidioc_s_std(file, fh, &norm);
 		else
 			ret=-EINVAL;
 
 		/* Updates standard information */
-		if (!ret)
-			vfd->current_norm=*id;
+		if (ret>=0)
+			vfd->current_norm=norm;
 
 		break;
 	}
@@ -1088,9 +1146,13 @@
 	case VIDIOC_G_AUDIO:
 	{
 		struct v4l2_audio *p=arg;
+		__u32 index=p->index;
 
 		if (!vfd->vidioc_g_audio)
 			break;
+
+		memset(p,0,sizeof(*p));
+		p->index=index;
 		dbgarg(cmd, "Get for index=%d\n", p->index);
 		ret=vfd->vidioc_g_audio(file, fh, p);
 		if (!ret)
@@ -1288,25 +1350,12 @@
 			ret=vfd->vidioc_g_parm(file, fh, p);
 		} else {
 			struct v4l2_standard s;
-			int i;
-
-			if (!vfd->tvnormsize) {
-				printk (KERN_WARNING "%s: no TV norms defined!\n",
-							vfd->name);
-				break;
-			}
 
 			if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 				return -EINVAL;
 
-			for (i = 0; i < vfd->tvnormsize; i++)
-				if (vfd->tvnorms[i].id == vfd->current_norm)
-					break;
-			if (i >= vfd->tvnormsize)
-				return -EINVAL;
-
 			v4l2_video_std_construct(&s, vfd->current_norm,
-						 vfd->tvnorms[i].name);
+						 v4l2_norm_to_name(vfd->current_norm));
 
 			memset(p,0,sizeof(*p));
 
@@ -1329,8 +1378,14 @@
 	case VIDIOC_G_TUNER:
 	{
 		struct v4l2_tuner *p=arg;
+		__u32 index=p->index;
+
 		if (!vfd->vidioc_g_tuner)
 			break;
+
+		memset(p,0,sizeof(*p));
+		p->index=index;
+
 		ret=vfd->vidioc_g_tuner(file, fh, p);
 		if (!ret)
 			dbgarg (cmd, "index=%d, name=%s, type=%d, "
@@ -1363,6 +1418,9 @@
 		struct v4l2_frequency *p=arg;
 		if (!vfd->vidioc_g_frequency)
 			break;
+
+		memset(p,0,sizeof(*p));
+
 		ret=vfd->vidioc_g_frequency(file, fh, p);
 		if (!ret)
 			dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n",
@@ -1396,12 +1454,7 @@
 		ret=vfd->vidioc_log_status(file, fh);
 		break;
 	}
-
-	/* --- Others --------------------------------------------- */
-
-	default:
-		ret=v4l_compat_translate_ioctl(inode,file,cmd,arg,__video_do_ioctl);
-	}
+	} /* switch */
 
 	if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
 		if (ret<0) {
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 9986de5..474ddb7 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -1044,16 +1044,8 @@
 	return (0);
 }
 
-static struct v4l2_tvnorm tvnorms[] = {
-	{
-		.name      = "NTSC-M",
-		.id        = V4L2_STD_NTSC_M,
-	}
-};
-
-static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id a)
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *i)
 {
-
 	return 0;
 }
 
@@ -1333,8 +1325,8 @@
 #ifdef CONFIG_VIDEO_V4L1_COMPAT
 	.vidiocgmbuf          = vidiocgmbuf,
 #endif
-	.tvnorms              = tvnorms,
-	.tvnormsize           = ARRAY_SIZE(tvnorms),
+	.tvnorms              = V4L2_STD_NTSC_M,
+	.current_norm         = V4L2_STD_NTSC_M,
 };
 /* -----------------------------------------------------------------
 	Initialization and module stuff
@@ -1361,8 +1353,6 @@
 	dev->vidq.timeout.data     = (unsigned long)dev;
 	init_timer(&dev->vidq.timeout);
 
-	vivi.current_norm         = tvnorms[0].id;
-
 	ret = video_register_device(&vivi, VFL_TYPE_GRABBER, video_nr);
 	printk(KERN_INFO "Video Technology Magazine Virtual Video Capture Board (Load status: %d)\n", ret);
 	return ret;
diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c
deleted file mode 100644
index 0cbf564..0000000
--- a/drivers/media/video/zr36120.c
+++ /dev/null
@@ -1,2079 +0,0 @@
-/*
-    zr36120.c - Zoran 36120/36125 based framegrabbers
-
-    Copyright (C) 1998-1999 Pauline Middelink <middelin@polyware.nl>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <linux/signal.h>
-#include <linux/wait.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <linux/sched.h>
-#include <linux/video_decoder.h>
-
-#include <asm/uaccess.h>
-
-#include "tuner.h"
-#include "zr36120.h"
-#include "zr36120_mem.h"
-
-/* mark an required function argument unused - lintism */
-#define	UNUSED(x)	(void)(x)
-
-/* sensible default */
-#ifndef CARDTYPE
-#define CARDTYPE 0
-#endif
-
-/* Anybody who uses more than four? */
-#define ZORAN_MAX 4
-
-static unsigned int triton1=0;			/* triton1 chipset? */
-static unsigned int cardtype[ZORAN_MAX]={ [ 0 ... ZORAN_MAX-1 ] = CARDTYPE };
-static int video_nr = -1;
-static int vbi_nr = -1;
-
-static struct pci_device_id zr36120_pci_tbl[] = {
-	{ PCI_VENDOR_ID_ZORAN,PCI_DEVICE_ID_ZORAN_36120,
-	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-	{ 0 }
-};
-MODULE_DEVICE_TABLE(pci, zr36120_pci_tbl);
-
-MODULE_AUTHOR("Pauline Middelink <middelin@polyware.nl>");
-MODULE_DESCRIPTION("Zoran ZR36120 based framegrabber");
-MODULE_LICENSE("GPL");
-
-module_param(triton1, uint, 0);
-module_param_array(cardtype, uint, NULL, 0);
-module_param(video_nr, int, 0);
-module_param(vbi_nr, int, 0);
-
-static int zoran_cards;
-static struct zoran zorans[ZORAN_MAX];
-
-/*
- * the meaning of each element can be found in zr36120.h
- * Determining the value of gpdir/gpval can be tricky. The
- * best way is to run the card under the original software
- * and read the values from the general purpose registers
- * 0x28 and 0x2C. How you do that is left as an exercise
- * to the impatient reader :)
- */
-#define T 1	/* to separate the bools from the ints */
-#define F 0
-static struct tvcard tvcards[] = {
-	/* reported working by <middelin@polyware.nl> */
-/*0*/	{ "Trust Victor II",
-	  2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } },
-	/* reported working by <Michael.Paxton@aihw.gov.au>  */
-/*1*/   { "Aitech WaveWatcher TV-PCI",
-	  3, 0, T, F, T, T, 0x7F, 0x80, { 1, TUNER(3), SVHS(6) }, { 0 } },
-	/* reported working by ? */
-/*2*/	{ "Genius Video Wonder PCI Video Capture Card",
-	  2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } },
-	/* reported working by <Pascal.Gabriel@wanadoo.fr> */
-/*3*/	{ "Guillemot Maxi-TV PCI",
-	  2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } },
-	/* reported working by "Craig Whitmore <lennon@igrin.co.nz> */
-/*4*/	{ "Quadrant Buster",
-	  3, 3, T, F, T, T, 0x7F, 0x80, { SVHS(1), TUNER(2), 3 }, { 1, 2, 3 } },
-	/* a debug entry which has all inputs mapped */
-/*5*/	{ "ZR36120 based framegrabber (all inputs enabled)",
-	  6, 0, T, T, T, T, 0x7F, 0x80, { 1, 2, 3, 4, 5, 6 }, { 0 } }
-};
-#undef T
-#undef F
-#define NRTVCARDS (sizeof(tvcards)/sizeof(tvcards[0]))
-
-#ifdef __sparc__
-#define	ENDIANESS	0
-#else
-#define	ENDIANESS	ZORAN_VFEC_LE
-#endif
-
-static struct { const char name[8]; uint mode; uint bpp; } palette2fmt[] = {
-/* n/a     */	{ "n/a",     0, 0 },
-/* GREY    */	{ "GRAY",    0, 0 },
-/* HI240   */	{ "HI240",   0, 0 },
-/* RGB565  */	{ "RGB565",  ZORAN_VFEC_RGB_RGB565|ENDIANESS, 2 },
-/* RGB24   */	{ "RGB24",   ZORAN_VFEC_RGB_RGB888|ENDIANESS|ZORAN_VFEC_PACK24, 3 },
-/* RGB32   */	{ "RGB32",   ZORAN_VFEC_RGB_RGB888|ENDIANESS, 4 },
-/* RGB555  */	{ "RGB555",  ZORAN_VFEC_RGB_RGB555|ENDIANESS, 2 },
-/* YUV422  */	{ "YUV422",  ZORAN_VFEC_RGB_YUV422|ENDIANESS, 2 },
-/* YUYV    */	{ "YUYV",    0, 0 },
-/* UYVY    */	{ "UYVY",    0, 0 },
-/* YUV420  */	{ "YUV420",  0, 0 },
-/* YUV411  */	{ "YUV411",  0, 0 },
-/* RAW     */	{ "RAW",     0, 0 },
-/* YUV422P */	{ "YUV422P", 0, 0 },
-/* YUV411P */	{ "YUV411P", 0, 0 }};
-#define NRPALETTES (sizeof(palette2fmt)/sizeof(palette2fmt[0]))
-#undef ENDIANESS
-
-/* ----------------------------------------------------------------------- */
-/* ZORAN chipset detector                                                 */
-/* shamelessly stolen from bttv.c                                         */
-/* Reason for beeing here: we need to detect if we are running on a        */
-/* Triton based chipset, and if so, enable a certain bit                   */
-/* ----------------------------------------------------------------------- */
-static
-void __init handle_chipset(void)
-{
-	/* Just in case some nut set this to something dangerous */
-	if (triton1)
-		triton1 = ZORAN_VDC_TRICOM;
-
-	if (pci_pci_problems & PCIPCI_TRITON) {
-		printk(KERN_INFO "zoran: Host bridge 82437FX Triton PIIX\n");
-		triton1 = ZORAN_VDC_TRICOM;
-	}
-}
-
-/* ----------------------------------------------------------------------- */
-/* ZORAN functions							   */
-/* ----------------------------------------------------------------------- */
-
-static void zoran_set_geo(struct zoran* ztv, struct vidinfo* i);
-
-#if 0 /* unused */
-static
-void zoran_dump(struct zoran *ztv)
-{
-	char	str[256];
-	char	*p=str; /* shut up, gcc! */
-	int	i;
-
-	for (i=0; i<0x60; i+=4) {
-		if ((i % 16) == 0) {
-			if (i) printk("%s\n",str);
-			p = str;
-			p+= sprintf(str, KERN_DEBUG "       %04x: ",i);
-		}
-		p += sprintf(p, "%08x ",zrread(i));
-	}
-}
-#endif /* unused */
-
-static
-void reap_states(struct zoran* ztv)
-{
-	/* count frames */
-	ztv->fieldnr++;
-
-	/*
-	 * Are we busy at all?
-	 * This depends on if there is a workqueue AND the
-	 * videotransfer is enabled on the chip...
-	 */
-	if (ztv->workqueue && (zrread(ZORAN_VDC) & ZORAN_VDC_VIDEN))
-	{
-		struct vidinfo* newitem;
-
-		/* did we get a complete frame? */
-		if (zrread(ZORAN_VSTR) & ZORAN_VSTR_GRAB)
-			return;
-
-DEBUG(printk(CARD_DEBUG "completed %s at %p\n",CARD,ztv->workqueue->kindof==FBUFFER_GRAB?"grab":"read",ztv->workqueue));
-
-		/* we are done with this buffer, tell everyone */
-		ztv->workqueue->status = FBUFFER_DONE;
-		ztv->workqueue->fieldnr = ztv->fieldnr;
-		/* not good, here for BTTV_FIELDNR reasons */
-		ztv->lastfieldnr = ztv->fieldnr;
-
-		switch (ztv->workqueue->kindof) {
-		 case FBUFFER_GRAB:
-			wake_up_interruptible(&ztv->grabq);
-			break;
-		 case FBUFFER_VBI:
-			wake_up_interruptible(&ztv->vbiq);
-			break;
-		 default:
-			printk(CARD_INFO "somebody killed the workqueue (kindof=%d)!\n",CARD,ztv->workqueue->kindof);
-		}
-
-		/* item completed, skip to next item in queue */
-		write_lock(&ztv->lock);
-		newitem = ztv->workqueue->next;
-		ztv->workqueue->next = 0;	/* mark completed */
-		ztv->workqueue = newitem;
-		write_unlock(&ztv->lock);
-	}
-
-	/*
-	 * ok, so it seems we have nothing in progress right now.
-	 * Lets see if we can find some work.
-	 */
-	if (ztv->workqueue)
-	{
-		struct vidinfo* newitem;
-again:
-
-DEBUG(printk(CARD_DEBUG "starting %s at %p\n",CARD,ztv->workqueue->kindof==FBUFFER_GRAB?"grab":"read",ztv->workqueue));
-
-		/* loadup the frame settings */
-		read_lock(&ztv->lock);
-		zoran_set_geo(ztv,ztv->workqueue);
-		read_unlock(&ztv->lock);
-
-		switch (ztv->workqueue->kindof) {
-		 case FBUFFER_GRAB:
-		 case FBUFFER_VBI:
-			zrand(~ZORAN_OCR_OVLEN, ZORAN_OCR);
-			zror(ZORAN_VSTR_SNAPSHOT,ZORAN_VSTR);
-			zror(ZORAN_VDC_VIDEN,ZORAN_VDC);
-
-			/* start single-shot grab */
-			zror(ZORAN_VSTR_GRAB, ZORAN_VSTR);
-			break;
-		 default:
-			printk(CARD_INFO "what is this doing on the queue? (kindof=%d)\n",CARD,ztv->workqueue->kindof);
-			write_lock(&ztv->lock);
-			newitem = ztv->workqueue->next;
-			ztv->workqueue->next = 0;
-			ztv->workqueue = newitem;
-			write_unlock(&ztv->lock);
-			if (newitem)
-				goto again;	/* yeah, sure.. */
-		}
-		/* bye for now */
-		return;
-	}
-DEBUG(printk(CARD_DEBUG "nothing in queue\n",CARD));
-
-	/*
-	 * What? Even the workqueue is empty? Am i really here
-	 * for nothing? Did i come all that way to... do nothing?
-	 */
-
-	/* do we need to overlay? */
-	if (test_bit(STATE_OVERLAY, &ztv->state))
-	{
-		/* are we already overlaying? */
-		if (!(zrread(ZORAN_OCR) & ZORAN_OCR_OVLEN) ||
-		    !(zrread(ZORAN_VDC) & ZORAN_VDC_VIDEN))
-		{
-DEBUG(printk(CARD_DEBUG "starting overlay\n",CARD));
-
-			read_lock(&ztv->lock);
-			zoran_set_geo(ztv,&ztv->overinfo);
-			read_unlock(&ztv->lock);
-
-			zror(ZORAN_OCR_OVLEN, ZORAN_OCR);
-			zrand(~ZORAN_VSTR_SNAPSHOT,ZORAN_VSTR);
-			zror(ZORAN_VDC_VIDEN,ZORAN_VDC);
-		}
-
-		/*
-		 * leave overlaying on, but turn interrupts off.
-		 */
-		zrand(~ZORAN_ICR_EN,ZORAN_ICR);
-		return;
-	}
-
-	/* do we have any VBI idle time processing? */
-	if (test_bit(STATE_VBI, &ztv->state))
-	{
-		struct vidinfo* item;
-		struct vidinfo* lastitem;
-
-		/* protect the workqueue */
-		write_lock(&ztv->lock);
-		lastitem = ztv->workqueue;
-		if (lastitem)
-			while (lastitem->next) lastitem = lastitem->next;
-		for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
-			if (item->next == 0 && item->status == FBUFFER_FREE)
-			{
-DEBUG(printk(CARD_DEBUG "%p added to queue\n",CARD,item));
-				item->status = FBUFFER_BUSY;
-				if (!lastitem)
-					ztv->workqueue = item;
-				else
-					lastitem->next = item;
-				lastitem = item;
-			}
-		write_unlock(&ztv->lock);
-		if (ztv->workqueue)
-			goto again;	/* hey, _i_ graduated :) */
-	}
-
-	/*
-	 * Then we must be realy IDLE
-	 */
-DEBUG(printk(CARD_DEBUG "turning off\n",CARD));
-	/* nothing further to do, disable DMA and further IRQs */
-	zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC);
-	zrand(~ZORAN_ICR_EN,ZORAN_ICR);
-}
-
-static
-void zoran_irq(int irq, void *dev_id)
-{
-	u32 stat,estat;
-	int count = 0;
-	struct zoran *ztv = dev_id;
-
-	UNUSED(irq);
-	for (;;) {
-		/* get/clear interrupt status bits */
-		stat=zrread(ZORAN_ISR);
-		estat=stat & zrread(ZORAN_ICR);
-		if (!estat)
-			return;
-		zrwrite(estat,ZORAN_ISR);
-		IDEBUG(printk(CARD_DEBUG "estat %08x\n",CARD,estat));
-		IDEBUG(printk(CARD_DEBUG " stat %08x\n",CARD,stat));
-
-		if (estat & ZORAN_ISR_CODE)
-		{
-			IDEBUG(printk(CARD_DEBUG "CodReplIRQ\n",CARD));
-		}
-		if (estat & ZORAN_ISR_GIRQ0)
-		{
-			IDEBUG(printk(CARD_DEBUG "GIRQ0\n",CARD));
-			if (!ztv->card->usegirq1)
-				reap_states(ztv);
-		}
-		if (estat & ZORAN_ISR_GIRQ1)
-		{
-			IDEBUG(printk(CARD_DEBUG "GIRQ1\n",CARD));
-			if (ztv->card->usegirq1)
-				reap_states(ztv);
-		}
-
-		count++;
-		if (count > 10)
-			printk(CARD_ERR "irq loop %d (%x)\n",CARD,count,estat);
-		if (count > 20)
-		{
-			zrwrite(0, ZORAN_ICR);
-			printk(CARD_ERR "IRQ lockup, cleared int mask\n",CARD);
-		}
-	}
-}
-
-static
-int zoran_muxsel(struct zoran* ztv, int channel, int norm)
-{
-	int	rv;
-
-	/* set the new video norm */
-	rv = i2c_control_device(&(ztv->i2c), I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &norm);
-	if (rv)
-		return rv;
-	ztv->norm = norm;
-
-	/* map the given channel to the cards decoder's channel */
-	channel = ztv->card->video_mux[channel] & CHANNEL_MASK;
-
-	/* set the new channel */
-	rv = i2c_control_device(&(ztv->i2c), I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &channel);
-	return rv;
-}
-
-/* Tell the interrupt handler what to to.  */
-static
-void zoran_cap(struct zoran* ztv, int on)
-{
-DEBUG(printk(CARD_DEBUG "zoran_cap(%d) state=%x\n",CARD,on,ztv->state));
-
-	if (on) {
-		ztv->running = 1;
-
-		/*
-		 * turn interrupts (back) on. The DMA will be enabled
-		 * inside the irq handler when it detects a restart.
-		 */
-		zror(ZORAN_ICR_EN,ZORAN_ICR);
-	}
-	else {
-		/*
-		 * turn both interrupts and DMA off
-		 */
-		zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC);
-		zrand(~ZORAN_ICR_EN,ZORAN_ICR);
-
-		ztv->running = 0;
-	}
-}
-
-static ulong dmask[] = {
-	0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8,
-	0xFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80,
-	0xFFFFFF00, 0xFFFFFE00, 0xFFFFFC00, 0xFFFFF800,
-	0xFFFFF000, 0xFFFFE000, 0xFFFFC000, 0xFFFF8000,
-	0xFFFF0000, 0xFFFE0000, 0xFFFC0000, 0xFFF80000,
-	0xFFF00000, 0xFFE00000, 0xFFC00000, 0xFF800000,
-	0xFF000000, 0xFE000000, 0xFC000000, 0xF8000000,
-	0xF0000000, 0xE0000000, 0xC0000000, 0x80000000
-};
-
-static
-void zoran_built_overlay(struct zoran* ztv, int count, struct video_clip *vcp)
-{
-	ulong*	mtop;
-	int	ystep = (ztv->vidXshift + ztv->vidWidth+31)/32;	/* next DWORD */
-	int	i;
-
-DEBUG(printk(KERN_DEBUG "       overlay at %p, ystep=%d, clips=%d\n",ztv->overinfo.overlay,ystep,count));
-
-	for (i=0; i<count; i++) {
-		struct video_clip *vp = vcp+i;
-		UNUSED(vp);
-DEBUG(printk(KERN_DEBUG "       %d: clip(%d,%d,%d,%d)\n", i,vp->x,vp->y,vp->width,vp->height));
-	}
-
-	/*
-	 * activate the visible portion of the screen
-	 * Note we take some shortcuts here, because we
-	 * know the width can never be < 32. (I.e. a DWORD)
-	 * We also assume the overlay starts somewhere in
-	 * the FIRST dword.
-	 */
-	{
-		int start = ztv->vidXshift;
-		ulong firstd = dmask[start];
-		ulong lastd = ~dmask[(start + ztv->overinfo.w) & 31];
-		mtop = ztv->overinfo.overlay;
-		for (i=0; i<ztv->overinfo.h; i++) {
-			int w = ztv->vidWidth;
-			ulong* line = mtop;
-			if (start & 31) {
-				*line++ = firstd;
-				w -= 32-(start&31);
-			}
-			memset(line, ~0, w/8);
-			if (w & 31)
-				line[w/32] = lastd;
-			mtop += ystep;
-		}
-	}
-
-	/* process clipping regions */
-	for (i=0; i<count; i++) {
-		int h;
-		if (vcp->x < 0 || (uint)vcp->x > ztv->overinfo.w ||
-		    vcp->y < 0 || vcp->y > ztv->overinfo.h ||
-		    vcp->width < 0 || (uint)(vcp->x+vcp->width) > ztv->overinfo.w ||
-		    vcp->height < 0 || (vcp->y+vcp->height) > ztv->overinfo.h)
-		{
-			DEBUG(printk(CARD_DEBUG "invalid clipzone (%d,%d,%d,%d) not in (0,0,%d,%d), adapting\n",CARD,vcp->x,vcp->y,vcp->width,vcp->height,ztv->overinfo.w,ztv->overinfo.h));
-			if (vcp->x < 0) vcp->x = 0;
-			if ((uint)vcp->x > ztv->overinfo.w) vcp->x = ztv->overinfo.w;
-			if (vcp->y < 0) vcp->y = 0;
-			if (vcp->y > ztv->overinfo.h) vcp->y = ztv->overinfo.h;
-			if (vcp->width < 0) vcp->width = 0;
-			if ((uint)(vcp->x+vcp->width) > ztv->overinfo.w) vcp->width = ztv->overinfo.w - vcp->x;
-			if (vcp->height < 0) vcp->height = 0;
-			if (vcp->y+vcp->height > ztv->overinfo.h) vcp->height = ztv->overinfo.h - vcp->y;
-//			continue;
-		}
-
-		mtop = &ztv->overinfo.overlay[vcp->y*ystep];
-		for (h=0; h<=vcp->height; h++) {
-			int w;
-			int x = ztv->vidXshift + vcp->x;
-			for (w=0; w<=vcp->width; w++) {
-				clear_bit(x&31, &mtop[x/32]);
-				x++;
-			}
-			mtop += ystep;
-		}
-		++vcp;
-	}
-
-	mtop = ztv->overinfo.overlay;
-	zrwrite(virt_to_bus(mtop), ZORAN_MTOP);
-	zrwrite(virt_to_bus(mtop+ystep), ZORAN_MBOT);
-	zraor((ztv->vidInterlace*ystep)<<0,~ZORAN_OCR_MASKSTRIDE,ZORAN_OCR);
-}
-
-struct tvnorm
-{
-	u16 Wt, Wa, Ht, Ha, HStart, VStart;
-};
-
-static struct tvnorm tvnorms[] = {
-	/* PAL-BDGHI */
-/*	{ 864, 720, 625, 576, 131, 21 },*/
-/*00*/	{ 864, 768, 625, 576, 81, 17 },
-	/* NTSC */
-/*01*/	{ 858, 720, 525, 480, 121, 10 },
-	/* SECAM */
-/*02*/	{ 864, 720, 625, 576, 131, 21 },
-	/* BW50 */
-/*03*/	{ 864, 720, 625, 576, 131, 21 },
-	/* BW60 */
-/*04*/	{ 858, 720, 525, 480, 121, 10 }
-};
-#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
-
-/*
- * Program the chip for a setup as described in the vidinfo struct.
- *
- * Side-effects: calculates vidXshift, vidInterlace,
- * vidHeight, vidWidth which are used in a later stage
- * to calculate the overlay mask
- *
- * This is an internal function, as such it does not check the
- * validity of the struct members... Spectaculair crashes will
- * follow /very/ quick when you're wrong and the chip right :)
- */
-static
-void zoran_set_geo(struct zoran* ztv, struct vidinfo* i)
-{
-	ulong	top, bot;
-	int	stride;
-	int	winWidth, winHeight;
-	int	maxWidth, maxHeight, maxXOffset, maxYOffset;
-	long	vfec;
-
-DEBUG(printk(CARD_DEBUG "set_geo(rect=(%d,%d,%d,%d), norm=%d, format=%d, bpp=%d, bpl=%d, busadr=%lx, overlay=%p)\n",CARD,i->x,i->y,i->w,i->h,ztv->norm,i->format,i->bpp,i->bpl,i->busadr,i->overlay));
-
-	/*
-	 * make sure the DMA transfers are inhibited during our
-	 * reprogramming of the chip
-	 */
-	zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC);
-
-	maxWidth = tvnorms[ztv->norm].Wa;
-	maxHeight = tvnorms[ztv->norm].Ha/2;
-	maxXOffset = tvnorms[ztv->norm].HStart;
-	maxYOffset = tvnorms[ztv->norm].VStart;
-
-	/* setup vfec register (keep ExtFl,TopField and VCLKPol settings) */
-	vfec = (zrread(ZORAN_VFEC) & (ZORAN_VFEC_EXTFL|ZORAN_VFEC_TOPFIELD|ZORAN_VFEC_VCLKPOL)) |
-	       (palette2fmt[i->format].mode & (ZORAN_VFEC_RGB|ZORAN_VFEC_ERRDIF|ZORAN_VFEC_LE|ZORAN_VFEC_PACK24));
-
-	/*
-	 * Set top, bottom ptrs. Since these must be DWORD aligned,
-	 * possible adjust the x and the width of the window.
-	 * so the endposition stay the same. The vidXshift will make
-	 * sure we are not writing pixels before the requested x.
-	 */
-	ztv->vidXshift = 0;
-	winWidth = i->w;
-	if (winWidth < 0)
-		winWidth = -winWidth;
-	top = i->busadr + i->x*i->bpp + i->y*i->bpl;
-	if (top & 3) {
-		ztv->vidXshift = (top & 3) / i->bpp;
-		winWidth += ztv->vidXshift;
-		DEBUG(printk(KERN_DEBUG "       window-x shifted %d pixels left\n",ztv->vidXshift));
-		top &= ~3;
-	}
-
-	/*
-	 * bottom points to next frame but in interleaved mode we want
-	 * to 'mix' the 2 frames to one capture, so 'bot' points to one
-	 * (physical) line below the top line.
-	 */
-	bot = top + i->bpl;
-	zrwrite(top,ZORAN_VTOP);
-	zrwrite(bot,ZORAN_VBOT);
-
-	/*
-	 * Make sure the winWidth is DWORD aligned too,
-	 * thereby automaticly making sure the stride to the
-	 * next line is DWORD aligned too (as required by spec).
-	 */
-	if ((winWidth*i->bpp) & 3) {
-DEBUG(printk(KERN_DEBUG "       window-width enlarged by %d pixels\n",(winWidth*i->bpp) & 3));
-		winWidth += (winWidth*i->bpp) & 3;
-	}
-
-	/* determine the DispMode and stride */
-	if (i->h >= 0 && i->h <= maxHeight) {
-		/* single frame grab suffices for this height. */
-		vfec |= ZORAN_VFEC_DISPMOD;
-		ztv->vidInterlace = 0;
-		stride = i->bpl - (winWidth*i->bpp);
-		winHeight = i->h;
-	}
-	else {
-		/* interleaving needed for this height */
-		ztv->vidInterlace = 1;
-		stride = i->bpl*2 - (winWidth*i->bpp);
-		winHeight = i->h/2;
-	}
-	if (winHeight < 0)	/* can happen for VBI! */
-		winHeight = -winHeight;
-
-	/* safety net, sometimes bpl is too short??? */
-	if (stride<0) {
-DEBUG(printk(CARD_DEBUG "WARNING stride = %d\n",CARD,stride));
-		stride = 0;
-	}
-
-	zraor((winHeight<<12)|(winWidth<<0),~(ZORAN_VDC_VIDWINHT|ZORAN_VDC_VIDWINWID), ZORAN_VDC);
-	zraor(stride<<16,~ZORAN_VSTR_DISPSTRIDE,ZORAN_VSTR);
-
-	/* remember vidWidth, vidHeight for overlay calculations */
-	ztv->vidWidth = winWidth;
-	ztv->vidHeight = winHeight;
-DEBUG(printk(KERN_DEBUG "       top=%08lx, bottom=%08lx\n",top,bot));
-DEBUG(printk(KERN_DEBUG "       winWidth=%d, winHeight=%d\n",winWidth,winHeight));
-DEBUG(printk(KERN_DEBUG "       maxWidth=%d, maxHeight=%d\n",maxWidth,maxHeight));
-DEBUG(printk(KERN_DEBUG "       stride=%d\n",stride));
-
-	/*
-	 * determine horizontal scales and crops
-	 */
-	if (i->w < 0) {
-		int Hstart = 1;
-		int Hend = Hstart + winWidth;
-DEBUG(printk(KERN_DEBUG "       Y: scale=0, start=%d, end=%d\n", Hstart, Hend));
-		zraor((Hstart<<10)|(Hend<<0),~(ZORAN_VFEH_HSTART|ZORAN_VFEH_HEND),ZORAN_VFEH);
-	}
-	else {
-		int Wa = maxWidth;
-		int X = (winWidth*64+Wa-1)/Wa;
-		int We = winWidth*64/X;
-		int HorDcm = 64-X;
-		int hcrop1 = 2*(Wa-We)/4;
-		/*
-		 * BUGFIX: Juha Nurmela <junki@qn-lpr2-165.quicknet.inet.fi>
-		 * found the solution to the color phase shift.
-		 * See ChangeLog for the full explanation)
-		 */
-		int Hstart = (maxXOffset + hcrop1) | 1;
-		int Hend = Hstart + We - 1;
-
-DEBUG(printk(KERN_DEBUG "       X: scale=%d, start=%d, end=%d\n", HorDcm, Hstart, Hend));
-
-		zraor((Hstart<<10)|(Hend<<0),~(ZORAN_VFEH_HSTART|ZORAN_VFEH_HEND),ZORAN_VFEH);
-		vfec |= HorDcm<<14;
-
-		if (HorDcm<16)
-			vfec |= ZORAN_VFEC_HFILTER_1; /* no filter */
-		else if (HorDcm<32)
-			vfec |= ZORAN_VFEC_HFILTER_3; /* 3 tap filter */
-		else if (HorDcm<48)
-			vfec |= ZORAN_VFEC_HFILTER_4; /* 4 tap filter */
-		else	vfec |= ZORAN_VFEC_HFILTER_5; /* 5 tap filter */
-	}
-
-	/*
-	 * Determine vertical scales and crops
-	 *
-	 * when height is negative, we want to read starting at line 0
-	 * One day someone might need access to these lines...
-	 */
-	if (i->h < 0) {
-		int Vstart = 0;
-		int Vend = Vstart + winHeight;
-DEBUG(printk(KERN_DEBUG "       Y: scale=0, start=%d, end=%d\n", Vstart, Vend));
-		zraor((Vstart<<10)|(Vend<<0),~(ZORAN_VFEV_VSTART|ZORAN_VFEV_VEND),ZORAN_VFEV);
-	}
-	else {
-		int Ha = maxHeight;
-		int Y = (winHeight*64+Ha-1)/Ha;
-		int He = winHeight*64/Y;
-		int VerDcm = 64-Y;
-		int vcrop1 = 2*(Ha-He)/4;
-		int Vstart = maxYOffset + vcrop1;
-		int Vend = Vstart + He - 1;
-
-DEBUG(printk(KERN_DEBUG "       Y: scale=%d, start=%d, end=%d\n", VerDcm, Vstart, Vend));
-		zraor((Vstart<<10)|(Vend<<0),~(ZORAN_VFEV_VSTART|ZORAN_VFEV_VEND),ZORAN_VFEV);
-		vfec |= VerDcm<<8;
-	}
-
-DEBUG(printk(KERN_DEBUG "       F: format=%d(=%s)\n",i->format,palette2fmt[i->format].name));
-
-	/* setup the requested format */
-	zrwrite(vfec, ZORAN_VFEC);
-}
-
-static
-void zoran_common_open(struct zoran* ztv, int flags)
-{
-	UNUSED(flags);
-
-	/* already opened? */
-	if (ztv->users++ != 0)
-		return;
-
-	/* unmute audio */
-	/* /what/ audio? */
-
-	ztv->state = 0;
-
-	/* setup the encoder to the initial values */
-	ztv->picture.colour=254<<7;
-	ztv->picture.brightness=128<<8;
-	ztv->picture.hue=128<<8;
-	ztv->picture.contrast=216<<7;
-	i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &ztv->picture);
-
-	/* default to the composite input since my camera is there */
-	zoran_muxsel(ztv, 0, VIDEO_MODE_PAL);
-}
-
-static
-void zoran_common_close(struct zoran* ztv)
-{
-	if (--ztv->users != 0)
-		return;
-
-	/* mute audio */
-	/* /what/ audio? */
-
-	/* stop the chip */
-	zoran_cap(ztv, 0);
-}
-
-/*
- * Open a zoran card. Right now the flags are just a hack
- */
-static int zoran_open(struct video_device *dev, int flags)
-{
-	struct zoran *ztv = (struct zoran*)dev;
-	struct vidinfo* item;
-	char* pos;
-
-	DEBUG(printk(CARD_DEBUG "open(dev,%d)\n",CARD,flags));
-
-	/*********************************************
-	 * We really should be doing lazy allocing...
-	 *********************************************/
-	/* allocate a frame buffer */
-	if (!ztv->fbuffer)
-		ztv->fbuffer = bmalloc(ZORAN_MAX_FBUFSIZE);
-	if (!ztv->fbuffer) {
-		/* could not get a buffer, bail out */
-		return -ENOBUFS;
-	}
-	/* at this time we _always_ have a framebuffer */
-	memset(ztv->fbuffer,0,ZORAN_MAX_FBUFSIZE);
-
-	if (!ztv->overinfo.overlay)
-		ztv->overinfo.overlay = kmalloc(1024*1024/8, GFP_KERNEL);
-	if (!ztv->overinfo.overlay) {
-		/* could not get an overlay buffer, bail out */
-		bfree(ztv->fbuffer, ZORAN_MAX_FBUFSIZE);
-		return -ENOBUFS;
-	}
-	/* at this time we _always_ have a overlay */
-
-	/* clear buffer status, and give them a DMAable address */
-	pos = ztv->fbuffer;
-	for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++)
-	{
-		item->status = FBUFFER_FREE;
-		item->memadr = pos;
-		item->busadr = virt_to_bus(pos);
-		pos += ZORAN_MAX_FBUFFER;
-	}
-
-	/* do the common part of all open's */
-	zoran_common_open(ztv, flags);
-
-	return 0;
-}
-
-static
-void zoran_close(struct video_device* dev)
-{
-	struct zoran *ztv = (struct zoran*)dev;
-
-	DEBUG(printk(CARD_DEBUG "close(dev)\n",CARD));
-
-	/* driver specific closure */
-	clear_bit(STATE_OVERLAY, &ztv->state);
-
-	zoran_common_close(ztv);
-
-	/*
-	 *      This is sucky but right now I can't find a good way to
-	 *      be sure its safe to free the buffer. We wait 5-6 fields
-	 *      which is more than sufficient to be sure.
-	 */
-	msleep(100);			/* Wait 1/10th of a second */
-
-	/* free the allocated framebuffer */
-	bfree(ztv->fbuffer, ZORAN_MAX_FBUFSIZE);
-	ztv->fbuffer = 0;
-	kfree(ztv->overinfo.overlay);
-	ztv->overinfo.overlay = 0;
-
-}
-
-/*
- * This read function could be used reentrant in a SMP situation.
- *
- * This is made possible by the spinlock which is kept till we
- * found and marked a buffer for our own use. The lock must
- * be released as soon as possible to prevent lock contention.
- */
-static
-long zoran_read(struct video_device* dev, char* buf, unsigned long count, int nonblock)
-{
-	struct zoran *ztv = (struct zoran*)dev;
-	unsigned long max;
-	struct vidinfo* unused = 0;
-	struct vidinfo* done = 0;
-
-	DEBUG(printk(CARD_DEBUG "zoran_read(%p,%ld,%d)\n",CARD,buf,count,nonblock));
-
-	/* find ourself a free or completed buffer */
-	for (;;) {
-		struct vidinfo* item;
-
-		write_lock_irq(&ztv->lock);
-		for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++)
-		{
-			if (!unused && item->status == FBUFFER_FREE)
-				unused = item;
-			if (!done && item->status == FBUFFER_DONE)
-				done = item;
-		}
-		if (done || unused)
-			break;
-
-		/* no more free buffers, wait for them. */
-		write_unlock_irq(&ztv->lock);
-		if (nonblock)
-			return -EWOULDBLOCK;
-		interruptible_sleep_on(&ztv->grabq);
-		if (signal_pending(current))
-			return -EINTR;
-	}
-
-	/* Do we have 'ready' data? */
-	if (!done) {
-		/* no? than this will take a while... */
-		if (nonblock) {
-			write_unlock_irq(&ztv->lock);
-			return -EWOULDBLOCK;
-		}
-
-		/* mark the unused buffer as wanted */
-		unused->status = FBUFFER_BUSY;
-		unused->w = 320;
-		unused->h = 240;
-		unused->format = VIDEO_PALETTE_RGB24;
-		unused->bpp = palette2fmt[unused->format].bpp;
-		unused->bpl = unused->w * unused->bpp;
-		unused->next = 0;
-		{ /* add to tail of queue */
-		  struct vidinfo* oldframe = ztv->workqueue;
-		  if (!oldframe) ztv->workqueue = unused;
-		  else {
-		    while (oldframe->next) oldframe = oldframe->next;
-		    oldframe->next = unused;
-		  }
-		}
-		write_unlock_irq(&ztv->lock);
-
-		/* tell the state machine we want it filled /NOW/ */
-		zoran_cap(ztv, 1);
-
-		/* wait till this buffer gets grabbed */
-		wait_event_interruptible(ztv->grabq,
-				(unused->status != FBUFFER_BUSY));
-		/* see if a signal did it */
-		if (signal_pending(current))
-			return -EINTR;
-		done = unused;
-	}
-	else
-		write_unlock_irq(&ztv->lock);
-
-	/* Yes! we got data! */
-	max = done->bpl * done->h;
-	if (count > max)
-		count = max;
-	if (copy_to_user((void*)buf, done->memadr, count))
-		count = -EFAULT;
-
-	/* keep the engine running */
-	done->status = FBUFFER_FREE;
-//	zoran_cap(ztv,1);
-
-	/* tell listeners this buffer became free */
-	wake_up_interruptible(&ztv->grabq);
-
-	/* goodbye */
-	DEBUG(printk(CARD_DEBUG "zoran_read() returns %lu\n",CARD,count));
-	return count;
-}
-
-static
-long zoran_write(struct video_device* dev, const char* buf, unsigned long count, int nonblock)
-{
-	struct zoran *ztv = (struct zoran *)dev;
-	UNUSED(ztv); UNUSED(dev); UNUSED(buf); UNUSED(count); UNUSED(nonblock);
-	DEBUG(printk(CARD_DEBUG "zoran_write\n",CARD));
-	return -EINVAL;
-}
-
-static
-unsigned int zoran_poll(struct video_device *dev, struct file *file, poll_table *wait)
-{
-	struct zoran *ztv = (struct zoran *)dev;
-	struct vidinfo* item;
-	unsigned int mask = 0;
-
-	poll_wait(file, &ztv->grabq, wait);
-
-	for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++)
-		if (item->status == FBUFFER_DONE)
-		{
-			mask |= (POLLIN | POLLRDNORM);
-			break;
-		}
-
-	DEBUG(printk(CARD_DEBUG "zoran_poll()=%x\n",CARD,mask));
-
-	return mask;
-}
-
-/* append a new clipregion to the vector of video_clips */
-static
-void new_clip(struct video_window* vw, struct video_clip* vcp, int x, int y, int w, int h)
-{
-	vcp[vw->clipcount].x = x;
-	vcp[vw->clipcount].y = y;
-	vcp[vw->clipcount].width = w;
-	vcp[vw->clipcount].height = h;
-	vw->clipcount++;
-}
-
-static
-int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg)
-{
-	struct zoran* ztv = (struct zoran*)dev;
-
-	switch (cmd) {
-	 case VIDIOCGCAP:
-	 {
-		struct video_capability c;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGCAP\n",CARD));
-
-		strcpy(c.name,ztv->video_dev.name);
-		c.type = VID_TYPE_CAPTURE|
-			 VID_TYPE_OVERLAY|
-			 VID_TYPE_CLIPPING|
-			 VID_TYPE_FRAMERAM|
-			 VID_TYPE_SCALES;
-		if (ztv->have_tuner)
-			c.type |= VID_TYPE_TUNER;
-		if (pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL))
-			c.type &= ~VID_TYPE_OVERLAY;
-		if (ztv->have_decoder) {
-			c.channels = ztv->card->video_inputs;
-			c.audios = ztv->card->audio_inputs;
-		} else
-			/* no decoder -> no channels */
-			c.channels = c.audios = 0;
-		c.maxwidth = 768;
-		c.maxheight = 576;
-		c.minwidth = 32;
-		c.minheight = 32;
-		if (copy_to_user(arg,&c,sizeof(c)))
-			return -EFAULT;
-		break;
-	 }
-
-	 case VIDIOCGCHAN:
-	 {
-		struct video_channel v;
-		int mux;
-		if (copy_from_user(&v, arg,sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGCHAN(%d)\n",CARD,v.channel));
-		v.flags=VIDEO_VC_AUDIO
-#ifdef VIDEO_VC_NORM
-			|VIDEO_VC_NORM
-#endif
-			;
-		v.tuners=0;
-		v.type=VIDEO_TYPE_CAMERA;
-#ifdef I_EXPECT_POSSIBLE_NORMS_IN_THE_API
-		v.norm=VIDEO_MODE_PAL|
-		       VIDEO_MODE_NTSC|
-		       VIDEO_MODE_SECAM;
-#else
-		v.norm=VIDEO_MODE_PAL;
-#endif
-		/* too many inputs? no decoder -> no channels */
-		if (!ztv->have_decoder || v.channel < 0 ||  v.channel >= ztv->card->video_inputs)
-			return -EINVAL;
-
-		/* now determine the name of the channel */
-		mux = ztv->card->video_mux[v.channel];
-		if (mux & IS_TUNER) {
-			/* lets assume only one tuner, yes? */
-			strcpy(v.name,"Television");
-			v.type = VIDEO_TYPE_TV;
-			if (ztv->have_tuner) {
-				v.flags |= VIDEO_VC_TUNER;
-				v.tuners = 1;
-			}
-		}
-		else if (mux & IS_SVHS)
-			sprintf(v.name,"S-Video-%d",v.channel);
-		else
-			sprintf(v.name,"CVBS-%d",v.channel);
-
-		if (copy_to_user(arg,&v,sizeof(v)))
-			return -EFAULT;
-		break;
-	 }
-	 case VIDIOCSCHAN:
-	 {	/* set video channel */
-		struct video_channel v;
-		if (copy_from_user(&v, arg,sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCSCHAN(%d,%d)\n",CARD,v.channel,v.norm));
-
-		/* too many inputs? no decoder -> no channels */
-		if (!ztv->have_decoder || v.channel >= ztv->card->video_inputs || v.channel < 0)
-			return -EINVAL;
-
-		if (v.norm != VIDEO_MODE_PAL &&
-		    v.norm != VIDEO_MODE_NTSC &&
-		    v.norm != VIDEO_MODE_SECAM &&
-		    v.norm != VIDEO_MODE_AUTO)
-			return -EOPNOTSUPP;
-
-		/* make it happen, nr1! */
-		return zoran_muxsel(ztv,v.channel,v.norm);
-	 }
-
-	 case VIDIOCGTUNER:
-	 {
-		struct video_tuner v;
-		if (copy_from_user(&v, arg,sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGTUNER(%d)\n",CARD,v.tuner));
-
-		/* Only no or one tuner for now */
-		if (!ztv->have_tuner || v.tuner)
-			return -EINVAL;
-
-		strcpy(v.name,"Television");
-		v.rangelow  = 0;
-		v.rangehigh = ~0;
-		v.flags     = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
-		v.mode      = ztv->norm;
-		v.signal    = 0xFFFF; /* unknown */
-
-		if (copy_to_user(arg,&v,sizeof(v)))
-			return -EFAULT;
-		break;
-	 }
-	 case VIDIOCSTUNER:
-	 {
-		struct video_tuner v;
-		if (copy_from_user(&v, arg, sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCSTUNER(%d,%d)\n",CARD,v.tuner,v.mode));
-
-		/* Only no or one tuner for now */
-		if (!ztv->have_tuner || v.tuner)
-			return -EINVAL;
-
-		/* and it only has certain valid modes */
-		if( v.mode != VIDEO_MODE_PAL &&
-		    v.mode != VIDEO_MODE_NTSC &&
-		    v.mode != VIDEO_MODE_SECAM)
-			return -EOPNOTSUPP;
-
-		/* engage! */
-		return zoran_muxsel(ztv,v.tuner,v.mode);
-	 }
-
-	 case VIDIOCGPICT:
-	 {
-		struct video_picture p = ztv->picture;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGPICT\n",CARD));
-		p.depth = ztv->depth;
-		switch (p.depth) {
-		 case  8: p.palette=VIDEO_PALETTE_YUV422;
-			  break;
-		 case 15: p.palette=VIDEO_PALETTE_RGB555;
-			  break;
-		 case 16: p.palette=VIDEO_PALETTE_RGB565;
-			  break;
-		 case 24: p.palette=VIDEO_PALETTE_RGB24;
-			  break;
-		 case 32: p.palette=VIDEO_PALETTE_RGB32;
-			  break;
-		}
-		if (copy_to_user(arg, &p, sizeof(p)))
-			return -EFAULT;
-		break;
-	 }
-	 case VIDIOCSPICT:
-	 {
-		struct video_picture p;
-		if (copy_from_user(&p, arg,sizeof(p)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCSPICT(%d,%d,%d,%d,%d,%d,%d)\n",CARD,p.brightness,p.hue,p.colour,p.contrast,p.whiteness,p.depth,p.palette));
-
-		/* depth must match with framebuffer */
-		if (p.depth != ztv->depth)
-			return -EINVAL;
-
-		/* check if palette matches this bpp */
-		if (p.palette>NRPALETTES ||
-		    palette2fmt[p.palette].bpp != ztv->overinfo.bpp)
-			return -EINVAL;
-
-		write_lock_irq(&ztv->lock);
-		ztv->overinfo.format = p.palette;
-		ztv->picture = p;
-		write_unlock_irq(&ztv->lock);
-
-		/* tell the decoder */
-		i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &p);
-		break;
-	 }
-
-	 case VIDIOCGWIN:
-	 {
-		struct video_window vw;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGWIN\n",CARD));
-		read_lock(&ztv->lock);
-		vw.x      = ztv->overinfo.x;
-		vw.y      = ztv->overinfo.y;
-		vw.width  = ztv->overinfo.w;
-		vw.height = ztv->overinfo.h;
-		vw.chromakey= 0;
-		vw.flags  = 0;
-		if (ztv->vidInterlace)
-			vw.flags|=VIDEO_WINDOW_INTERLACE;
-		read_unlock(&ztv->lock);
-		if (copy_to_user(arg,&vw,sizeof(vw)))
-			return -EFAULT;
-		break;
-	 }
-	 case VIDIOCSWIN:
-	 {
-		struct video_window vw;
-		struct video_clip *vcp;
-		int on;
-		if (copy_from_user(&vw,arg,sizeof(vw)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCSWIN(%d,%d,%d,%d,%x,%d)\n",CARD,vw.x,vw.y,vw.width,vw.height,vw.flags,vw.clipcount));
-
-		if (vw.flags)
-			return -EINVAL;
-
-		if (vw.clipcount <0 || vw.clipcount>256)
-			return -EDOM;   /* Too many! */
-
-		/*
-		 *      Do any clips.
-		 */
-		vcp = vmalloc(sizeof(struct video_clip)*(vw.clipcount+4));
-		if (vcp==NULL)
-			return -ENOMEM;
-		if (vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount)) {
-			vfree(vcp);
-			return -EFAULT;
-		}
-
-		on = ztv->running;
-		if (on)
-			zoran_cap(ztv, 0);
-
-		/*
-		 * strange, it seems xawtv sometimes calls us with 0
-		 * width and/or height. Ignore these values
-		 */
-		if (vw.x == 0)
-			vw.x = ztv->overinfo.x;
-		if (vw.y == 0)
-			vw.y = ztv->overinfo.y;
-
-		/* by now we are committed to the new data... */
-		write_lock_irq(&ztv->lock);
-		ztv->overinfo.x = vw.x;
-		ztv->overinfo.y = vw.y;
-		ztv->overinfo.w = vw.width;
-		ztv->overinfo.h = vw.height;
-		write_unlock_irq(&ztv->lock);
-
-		/*
-		 *      Impose display clips
-		 */
-		if (vw.x+vw.width > ztv->swidth)
-			new_clip(&vw, vcp, ztv->swidth-vw.x, 0, vw.width-1, vw.height-1);
-		if (vw.y+vw.height > ztv->sheight)
-			new_clip(&vw, vcp, 0, ztv->sheight-vw.y, vw.width-1, vw.height-1);
-
-		/* built the requested clipping zones */
-		zoran_set_geo(ztv, &ztv->overinfo);
-		zoran_built_overlay(ztv, vw.clipcount, vcp);
-		vfree(vcp);
-
-		/* if we were on, restart the video engine */
-		if (on)
-			zoran_cap(ztv, 1);
-		break;
-	 }
-
-	 case VIDIOCCAPTURE:
-	 {
-		int v;
-		if (get_user(v, (int *)arg))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCCAPTURE(%d)\n",CARD,v));
-
-		if (v==0) {
-			clear_bit(STATE_OVERLAY, &ztv->state);
-			zoran_cap(ztv, 1);
-		}
-		else {
-			/* is VIDIOCSFBUF, VIDIOCSWIN done? */
-			if (ztv->overinfo.busadr==0 || ztv->overinfo.w==0 || ztv->overinfo.h==0)
-				return -EINVAL;
-
-			set_bit(STATE_OVERLAY, &ztv->state);
-			zoran_cap(ztv, 1);
-		}
-		break;
-	 }
-
-	 case VIDIOCGFBUF:
-	 {
-		struct video_buffer v;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGFBUF\n",CARD));
-		read_lock(&ztv->lock);
-		v.base   = (void *)ztv->overinfo.busadr;
-		v.height = ztv->sheight;
-		v.width  = ztv->swidth;
-		v.depth  = ztv->depth;
-		v.bytesperline = ztv->overinfo.bpl;
-		read_unlock(&ztv->lock);
-		if(copy_to_user(arg, &v,sizeof(v)))
-			return -EFAULT;
-		break;
-	 }
-	 case VIDIOCSFBUF:
-	 {
-		struct video_buffer v;
-		if(!capable(CAP_SYS_ADMIN))
-			return -EPERM;
-		if (pcipci_problems & (PCIPCI_FAIL|PCIAGP_FAIL))
-			return -ENXIO;
-		if (copy_from_user(&v, arg,sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCSFBUF(%p,%d,%d,%d,%d)\n",CARD,v.base, v.width,v.height,v.depth,v.bytesperline));
-
-		if (v.depth!=15 && v.depth!=16 && v.depth!=24 && v.depth!=32)
-			return -EINVAL;
-		if (v.bytesperline<1)
-			return -EINVAL;
-		if (ztv->running)
-			return -EBUSY;
-		write_lock_irq(&ztv->lock);
-		ztv->overinfo.busadr  = (ulong)v.base;
-		ztv->sheight      = v.height;
-		ztv->swidth       = v.width;
-		ztv->depth        = v.depth;		/* bits per pixel */
-		ztv->overinfo.bpp = ((v.depth+1)&0x38)/8;/* bytes per pixel */
-		ztv->overinfo.bpl = v.bytesperline;	/* bytes per line */
-		write_unlock_irq(&ztv->lock);
-		break;
-	 }
-
-	 case VIDIOCKEY:
-	 {
-		/* Will be handled higher up .. */
-		break;
-	 }
-
-	 case VIDIOCSYNC:
-	 {
-		int i;
-		if (get_user(i, (int *) arg))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDEOCSYNC(%d)\n",CARD,i));
-		if (i<0 || i>ZORAN_MAX_FBUFFERS)
-			return -EINVAL;
-		switch (ztv->grabinfo[i].status) {
-		 case FBUFFER_FREE:
-			return -EINVAL;
-		 case FBUFFER_BUSY:
-			/* wait till this buffer gets grabbed */
-			wait_event_interruptible(ztv->grabq,
-					(ztv->grabinfo[i].status != FBUFFER_BUSY));
-			/* see if a signal did it */
-			if (signal_pending(current))
-				return -EINTR;
-			/* don't fall through; a DONE buffer is not UNUSED */
-			break;
-		 case FBUFFER_DONE:
-			ztv->grabinfo[i].status = FBUFFER_FREE;
-			/* tell ppl we have a spare buffer */
-			wake_up_interruptible(&ztv->grabq);
-			break;
-		}
-		DEBUG(printk(CARD_DEBUG "VIDEOCSYNC(%d) returns\n",CARD,i));
-		break;
-	 }
-
-	 case VIDIOCMCAPTURE:
-	 {
-		struct video_mmap vm;
-		struct vidinfo* frame;
-		if (copy_from_user(&vm,arg,sizeof(vm)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCMCAPTURE(%d,(%d,%d),%d)\n",CARD,vm.frame,vm.width,vm.height,vm.format));
-		if (vm.frame<0 || vm.frame>ZORAN_MAX_FBUFFERS ||
-		    vm.width<32 || vm.width>768 ||
-		    vm.height<32 || vm.height>576 ||
-		    vm.format>NRPALETTES ||
-		    palette2fmt[vm.format].mode == 0)
-			return -EINVAL;
-
-		/* we are allowed to take over UNUSED and DONE buffers */
-		frame = &ztv->grabinfo[vm.frame];
-		if (frame->status == FBUFFER_BUSY)
-			return -EBUSY;
-
-		/* setup the other parameters if they are given */
-		write_lock_irq(&ztv->lock);
-		frame->w = vm.width;
-		frame->h = vm.height;
-		frame->format = vm.format;
-		frame->bpp = palette2fmt[frame->format].bpp;
-		frame->bpl = frame->w*frame->bpp;
-		frame->status = FBUFFER_BUSY;
-		frame->next = 0;
-		{ /* add to tail of queue */
-		  struct vidinfo* oldframe = ztv->workqueue;
-		  if (!oldframe) ztv->workqueue = frame;
-		  else {
-		    while (oldframe->next) oldframe = oldframe->next;
-		    oldframe->next = frame;
-		  }
-		}
-		write_unlock_irq(&ztv->lock);
-		zoran_cap(ztv, 1);
-		break;
-	 }
-
-	 case VIDIOCGMBUF:
-	 {
-		struct video_mbuf mb;
-		int i;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGMBUF\n",CARD));
-		mb.size = ZORAN_MAX_FBUFSIZE;
-		mb.frames = ZORAN_MAX_FBUFFERS;
-		for (i=0; i<ZORAN_MAX_FBUFFERS; i++)
-			mb.offsets[i] = i*ZORAN_MAX_FBUFFER;
-		if(copy_to_user(arg, &mb,sizeof(mb)))
-			return -EFAULT;
-		break;
-	 }
-
-	 case VIDIOCGUNIT:
-	 {
-		struct video_unit vu;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGUNIT\n",CARD));
-		vu.video = ztv->video_dev.minor;
-		vu.vbi = ztv->vbi_dev.minor;
-		vu.radio = VIDEO_NO_UNIT;
-		vu.audio = VIDEO_NO_UNIT;
-		vu.teletext = VIDEO_NO_UNIT;
-		if(copy_to_user(arg, &vu,sizeof(vu)))
-			return -EFAULT;
-		break;
-	 }
-
-	 case VIDIOCGFREQ:
-	 {
-		unsigned long v = ztv->tuner_freq;
-		if (copy_to_user(arg,&v,sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGFREQ\n",CARD));
-		break;
-	 }
-	 case VIDIOCSFREQ:
-	 {
-		unsigned long v;
-		if (copy_from_user(&v, arg, sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCSFREQ\n",CARD));
-
-		if (ztv->have_tuner) {
-			int fixme = v;
-			if (i2c_control_device(&(ztv->i2c), I2C_DRIVERID_TUNER, TUNER_SET_TVFREQ, &fixme) < 0)
-				return -EAGAIN;
-		}
-		ztv->tuner_freq = v;
-		break;
-	 }
-
-	 /* Why isn't this in the API?
-	  * And why doesn't it take a buffer number?
-	 case BTTV_FIELDNR:
-	 {
-		unsigned long v = ztv->lastfieldnr;
-		if (copy_to_user(arg,&v,sizeof(v)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "BTTV_FIELDNR\n",CARD));
-		break;
-	 }
-	 */
-
-	 default:
-		return -ENOIOCTLCMD;
-	}
-	return 0;
-}
-
-static
-int zoran_mmap(struct vm_area_struct *vma, struct video_device* dev, const char* adr, unsigned long size)
-{
-	struct zoran* ztv = (struct zoran*)dev;
-	unsigned long start = (unsigned long)adr;
-	unsigned long pos;
-
-	DEBUG(printk(CARD_DEBUG "zoran_mmap(0x%p,%ld)\n",CARD,adr,size));
-
-	/* sanity checks */
-	if (size > ZORAN_MAX_FBUFSIZE || !ztv->fbuffer)
-		return -EINVAL;
-
-	/* start mapping the whole shabang to user memory */
-	pos = (unsigned long)ztv->fbuffer;
-	while (size>0) {
-		unsigned long pfn = virt_to_phys((void*)pos) >> PAGE_SHIFT;
-		if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
-			return -EAGAIN;
-		start += PAGE_SIZE;
-		pos += PAGE_SIZE;
-		size -= PAGE_SIZE;
-	}
-	return 0;
-}
-
-static struct video_device zr36120_template=
-{
-	.owner		= THIS_MODULE,
-	.name		= "UNSET",
-	.type		= VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY,
-	.hardware	= VID_HARDWARE_ZR36120,
-	.open		= zoran_open,
-	.close		= zoran_close,
-	.read		= zoran_read,
-	.write		= zoran_write,
-	.poll		= zoran_poll,
-	.ioctl		= zoran_ioctl,
-	.compat_ioctl	= v4l_compat_ioctl32,
-	.mmap		= zoran_mmap,
-	.minor		= -1,
-};
-
-static
-int vbi_open(struct video_device *dev, int flags)
-{
-	struct zoran *ztv = dev->priv;
-	struct vidinfo* item;
-
-	DEBUG(printk(CARD_DEBUG "vbi_open(dev,%d)\n",CARD,flags));
-
-	/*
-	 * During VBI device open, we continiously grab VBI-like
-	 * data in the vbi buffer when we have nothing to do.
-	 * Only when there is an explicit request for VBI data
-	 * (read call) we /force/ a read.
-	 */
-
-	/* allocate buffers */
-	for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
-	{
-		item->status = FBUFFER_FREE;
-
-		/* alloc */
-		if (!item->memadr) {
-			item->memadr = bmalloc(ZORAN_VBI_BUFSIZE);
-			if (!item->memadr) {
-				/* could not get a buffer, bail out */
-				while (item != ztv->readinfo) {
-					item--;
-					bfree(item->memadr, ZORAN_VBI_BUFSIZE);
-					item->memadr = 0;
-					item->busadr = 0;
-				}
-				return -ENOBUFS;
-			}
-		}
-
-		/* determine the DMAable address */
-		item->busadr = virt_to_bus(item->memadr);
-	}
-
-	/* do the common part of all open's */
-	zoran_common_open(ztv, flags);
-
-	set_bit(STATE_VBI, &ztv->state);
-	/* start read-ahead */
-	zoran_cap(ztv, 1);
-
-	return 0;
-}
-
-static
-void vbi_close(struct video_device *dev)
-{
-	struct zoran *ztv = dev->priv;
-	struct vidinfo* item;
-
-	DEBUG(printk(CARD_DEBUG "vbi_close(dev)\n",CARD));
-
-	/* driver specific closure */
-	clear_bit(STATE_VBI, &ztv->state);
-
-	zoran_common_close(ztv);
-
-	/*
-	 *      This is sucky but right now I can't find a good way to
-	 *      be sure its safe to free the buffer. We wait 5-6 fields
-	 *      which is more than sufficient to be sure.
-	 */
-	msleep(100);			/* Wait 1/10th of a second */
-
-	for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
-	{
-		if (item->memadr)
-			bfree(item->memadr, ZORAN_VBI_BUFSIZE);
-		item->memadr = 0;
-	}
-
-}
-
-/*
- * This read function could be used reentrant in a SMP situation.
- *
- * This is made possible by the spinlock which is kept till we
- * found and marked a buffer for our own use. The lock must
- * be released as soon as possible to prevent lock contention.
- */
-static
-long vbi_read(struct video_device* dev, char* buf, unsigned long count, int nonblock)
-{
-	struct zoran *ztv = dev->priv;
-	unsigned long max;
-	struct vidinfo* unused = 0;
-	struct vidinfo* done = 0;
-
-	DEBUG(printk(CARD_DEBUG "vbi_read(0x%p,%ld,%d)\n",CARD,buf,count,nonblock));
-
-	/* find ourself a free or completed buffer */
-	for (;;) {
-		struct vidinfo* item;
-
-		write_lock_irq(&ztv->lock);
-		for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) {
-			if (!unused && item->status == FBUFFER_FREE)
-				unused = item;
-			if (!done && item->status == FBUFFER_DONE)
-				done = item;
-		}
-		if (done || unused)
-			break;
-
-		/* no more free buffers, wait for them. */
-		write_unlock_irq(&ztv->lock);
-		if (nonblock)
-			return -EWOULDBLOCK;
-		interruptible_sleep_on(&ztv->vbiq);
-		if (signal_pending(current))
-			return -EINTR;
-	}
-
-	/* Do we have 'ready' data? */
-	if (!done) {
-		/* no? than this will take a while... */
-		if (nonblock) {
-			write_unlock_irq(&ztv->lock);
-			return -EWOULDBLOCK;
-		}
-
-		/* mark the unused buffer as wanted */
-		unused->status = FBUFFER_BUSY;
-		unused->next = 0;
-		{ /* add to tail of queue */
-		  struct vidinfo* oldframe = ztv->workqueue;
-		  if (!oldframe) ztv->workqueue = unused;
-		  else {
-		    while (oldframe->next) oldframe = oldframe->next;
-		    oldframe->next = unused;
-		  }
-		}
-		write_unlock_irq(&ztv->lock);
-
-		/* tell the state machine we want it filled /NOW/ */
-		zoran_cap(ztv, 1);
-
-		/* wait till this buffer gets grabbed */
-		wait_event_interruptible(ztv->vbiq,
-				(unused->status != FBUFFER_BUSY));
-		/* see if a signal did it */
-		if (signal_pending(current))
-			return -EINTR;
-		done = unused;
-	}
-	else
-		write_unlock_irq(&ztv->lock);
-
-	/* Yes! we got data! */
-	max = done->bpl * -done->h;
-	if (count > max)
-		count = max;
-
-	/* check if the user gave us enough room to write the data */
-	if (!access_ok(VERIFY_WRITE, buf, count)) {
-		count = -EFAULT;
-		goto out;
-	}
-
-	/*
-	 * Now transform/strip the data from YUV to Y-only
-	 * NB. Assume the Y is in the LSB of the YUV data.
-	 */
-	{
-	unsigned char* optr = buf;
-	unsigned char* eptr = buf+count;
-
-	/* are we beeing accessed from an old driver? */
-	if (count == 2*19*2048) {
-		/*
-		 * Extreme HACK, old VBI programs expect 2048 points
-		 * of data, and we only got 864 orso. Double each
-		 * datapoint and clear the rest of the line.
-		 * This way we have appear to have a
-		 * sample_frequency of 29.5 Mc.
-		 */
-		int x,y;
-		unsigned char* iptr = done->memadr+1;
-		for (y=done->h; optr<eptr && y<0; y++)
-		{
-			/* copy to doubled data to userland */
-			for (x=0; optr+1<eptr && x<-done->w; x++)
-			{
-				unsigned char a = iptr[x*2];
-				__put_user(a, optr++);
-				__put_user(a, optr++);
-			}
-			/* and clear the rest of the line */
-			for (x*=2; optr<eptr && x<done->bpl; x++)
-				__put_user(0, optr++);
-			/* next line */
-			iptr += done->bpl;
-		}
-	}
-	else {
-		/*
-		 * Other (probably newer) programs asked
-		 * us what geometry we are using, and are
-		 * reading the correct size.
-		 */
-		int x,y;
-		unsigned char* iptr = done->memadr+1;
-		for (y=done->h; optr<eptr && y<0; y++)
-		{
-			/* copy to doubled data to userland */
-			for (x=0; optr<eptr && x<-done->w; x++)
-				__put_user(iptr[x*2], optr++);
-			/* and clear the rest of the line */
-			for (;optr<eptr && x<done->bpl; x++)
-				__put_user(0, optr++);
-			/* next line */
-			iptr += done->bpl;
-		}
-	}
-
-	/* API compliance:
-	 * place the framenumber (half fieldnr) in the last long
-	 */
-	__put_user(done->fieldnr/2, ((ulong*)eptr)[-1]);
-	}
-
-	/* keep the engine running */
-	done->status = FBUFFER_FREE;
-	zoran_cap(ztv, 1);
-
-	/* tell listeners this buffer just became free */
-	wake_up_interruptible(&ztv->vbiq);
-
-	/* goodbye */
-out:
-	DEBUG(printk(CARD_DEBUG "vbi_read() returns %lu\n",CARD,count));
-	return count;
-}
-
-static
-unsigned int vbi_poll(struct video_device *dev, struct file *file, poll_table *wait)
-{
-	struct zoran *ztv = dev->priv;
-	struct vidinfo* item;
-	unsigned int mask = 0;
-
-	poll_wait(file, &ztv->vbiq, wait);
-
-	for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++)
-		if (item->status == FBUFFER_DONE)
-		{
-			mask |= (POLLIN | POLLRDNORM);
-			break;
-		}
-
-	DEBUG(printk(CARD_DEBUG "vbi_poll()=%x\n",CARD,mask));
-
-	return mask;
-}
-
-static
-int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
-{
-	struct zoran* ztv = dev->priv;
-
-	switch (cmd) {
-	 case VIDIOCGVBIFMT:
-	 {
-		struct vbi_format f;
-		DEBUG(printk(CARD_DEBUG "VIDIOCGVBIINFO\n",CARD));
-		f.sampling_rate = 14750000UL;
-		f.samples_per_line = -ztv->readinfo[0].w;
-		f.sample_format = VIDEO_PALETTE_RAW;
-		f.start[0] = f.start[1] = ztv->readinfo[0].y;
-		f.start[1] += 312;
-		f.count[0] = f.count[1] = -ztv->readinfo[0].h;
-		f.flags = VBI_INTERLACED;
-		if (copy_to_user(arg,&f,sizeof(f)))
-			return -EFAULT;
-		break;
-	 }
-	 case VIDIOCSVBIFMT:
-	 {
-		struct vbi_format f;
-		int i;
-		if (copy_from_user(&f, arg,sizeof(f)))
-			return -EFAULT;
-		DEBUG(printk(CARD_DEBUG "VIDIOCSVBIINFO(%d,%d,%d,%d,%d,%d,%d,%x)\n",CARD,f.sampling_rate,f.samples_per_line,f.sample_format,f.start[0],f.start[1],f.count[0],f.count[1],f.flags));
-
-		/* lots of parameters are fixed... (PAL) */
-		if (f.sampling_rate != 14750000UL ||
-		    f.samples_per_line > 864 ||
-		    f.sample_format != VIDEO_PALETTE_RAW ||
-		    f.start[0] < 0 ||
-		    f.start[0] != f.start[1]-312 ||
-		    f.count[0] != f.count[1] ||
-		    f.start[0]+f.count[0] >= 288 ||
-		    f.flags != VBI_INTERLACED)
-			return -EINVAL;
-
-		write_lock_irq(&ztv->lock);
-		ztv->readinfo[0].y = f.start[0];
-		ztv->readinfo[0].w = -f.samples_per_line;
-		ztv->readinfo[0].h = -f.count[0];
-		ztv->readinfo[0].bpl = f.samples_per_line*ztv->readinfo[0].bpp;
-		for (i=1; i<ZORAN_VBI_BUFFERS; i++)
-			ztv->readinfo[i] = ztv->readinfo[i];
-		write_unlock_irq(&ztv->lock);
-		break;
-	 }
-	 default:
-		return -ENOIOCTLCMD;
-	}
-	return 0;
-}
-
-static struct video_device vbi_template=
-{
-	.owner		= THIS_MODULE,
-	.name		= "UNSET",
-	.type		= VID_TYPE_CAPTURE|VID_TYPE_TELETEXT,
-	.hardware	= VID_HARDWARE_ZR36120,
-	.open		= vbi_open,
-	.close		= vbi_close,
-	.read		= vbi_read,
-	.write		= zoran_write,
-	.poll		= vbi_poll,
-	.ioctl		= vbi_ioctl,
-	.minor		= -1,
-};
-
-/*
- *      Scan for a Zoran chip, request the irq and map the io memory
- */
-static
-int __init find_zoran(void)
-{
-	int result;
-	struct zoran *ztv;
-	struct pci_dev *dev = NULL;
-	unsigned char revision;
-	int zoran_num = 0;
-
-	while ((dev = pci_get_device(PCI_VENDOR_ID_ZORAN,PCI_DEVICE_ID_ZORAN_36120, dev)))
-	{
-		/* Ok, a ZR36120/ZR36125 found! */
-		ztv = &zorans[zoran_num];
-		ztv->dev = dev;
-
-		if (pci_enable_device(dev))
-			continue;
-
-		pci_read_config_byte(dev, PCI_CLASS_REVISION, &revision);
-		printk(KERN_INFO "zoran: Zoran %x (rev %d) ",
-			dev->device, revision);
-		printk("bus: %d, devfn: %d, irq: %d, ",
-			dev->bus->number, dev->devfn, dev->irq);
-		printk("memory: 0x%08lx.\n", ztv->zoran_adr);
-
-		ztv->zoran_mem = ioremap(ztv->zoran_adr, 0x1000);
-		DEBUG(printk(KERN_DEBUG "zoran: mapped-memory at 0x%p\n",ztv->zoran_mem));
-
-		result = request_irq(dev->irq, zoran_irq,
-			IRQF_SHARED|IRQF_DISABLED,"zoran", ztv);
-		if (result==-EINVAL)
-		{
-			iounmap(ztv->zoran_mem);
-			printk(KERN_ERR "zoran: Bad irq number or handler\n");
-			continue;
-		}
-		if (result==-EBUSY)
-			printk(KERN_ERR "zoran: IRQ %d busy, change your PnP config in BIOS\n",dev->irq);
-		if (result < 0) {
-			iounmap(ztv->zoran_mem);
-			continue;
-		}
-		/* Enable bus-mastering */
-		pci_set_master(dev);
-		/* Keep a reference */
-		pci_dev_get(dev);
-		zoran_num++;
-	}
-	if(zoran_num)
-		printk(KERN_INFO "zoran: %d Zoran card(s) found.\n",zoran_num);
-	return zoran_num;
-}
-
-static
-int __init init_zoran(int card)
-{
-	struct zoran *ztv = &zorans[card];
-	int	i;
-
-	/* if the given cardtype valid? */
-	if (cardtype[card]>=NRTVCARDS) {
-		printk(KERN_INFO "invalid cardtype(%d) detected\n",cardtype[card]);
-		return -1;
-	}
-
-	/* reset the zoran */
-	zrand(~ZORAN_PCI_SOFTRESET,ZORAN_PCI);
-	udelay(10);
-	zror(ZORAN_PCI_SOFTRESET,ZORAN_PCI);
-	udelay(10);
-
-	/* zoran chip specific details */
-	ztv->card = tvcards+cardtype[card];	/* point to the selected card */
-	ztv->norm = 0;				/* PAL */
-	ztv->tuner_freq = 0;
-
-	/* videocard details */
-	ztv->swidth = 800;
-	ztv->sheight = 600;
-	ztv->depth = 16;
-
-	/* State details */
-	ztv->fbuffer = 0;
-	ztv->overinfo.kindof = FBUFFER_OVERLAY;
-	ztv->overinfo.status = FBUFFER_FREE;
-	ztv->overinfo.x = 0;
-	ztv->overinfo.y = 0;
-	ztv->overinfo.w = 768; /* 640 */
-	ztv->overinfo.h = 576; /* 480 */
-	ztv->overinfo.format = VIDEO_PALETTE_RGB565;
-	ztv->overinfo.bpp = palette2fmt[ztv->overinfo.format].bpp;
-	ztv->overinfo.bpl = ztv->overinfo.bpp*ztv->swidth;
-	ztv->overinfo.busadr = 0;
-	ztv->overinfo.memadr = 0;
-	ztv->overinfo.overlay = 0;
-	for (i=0; i<ZORAN_MAX_FBUFFERS; i++) {
-		ztv->grabinfo[i] = ztv->overinfo;
-		ztv->grabinfo[i].kindof = FBUFFER_GRAB;
-	}
-	init_waitqueue_head(&ztv->grabq);
-
-	/* VBI details */
-	ztv->readinfo[0] = ztv->overinfo;
-	ztv->readinfo[0].kindof = FBUFFER_VBI;
-	ztv->readinfo[0].w = -864;
-	ztv->readinfo[0].h = -38;
-	ztv->readinfo[0].format = VIDEO_PALETTE_YUV422;
-	ztv->readinfo[0].bpp = palette2fmt[ztv->readinfo[0].format].bpp;
-	ztv->readinfo[0].bpl = 1024*ztv->readinfo[0].bpp;
-	for (i=1; i<ZORAN_VBI_BUFFERS; i++)
-		ztv->readinfo[i] = ztv->readinfo[0];
-	init_waitqueue_head(&ztv->vbiq);
-
-	/* maintenance data */
-	ztv->have_decoder = 0;
-	ztv->have_tuner = 0;
-	ztv->tuner_type = 0;
-	ztv->running = 0;
-	ztv->users = 0;
-	rwlock_init(&ztv->lock);
-	ztv->workqueue = 0;
-	ztv->fieldnr = 0;
-	ztv->lastfieldnr = 0;
-
-	if (triton1)
-		zrand(~ZORAN_VDC_TRICOM, ZORAN_VDC);
-
-	/* external FL determines TOP frame */
-	zror(ZORAN_VFEC_EXTFL, ZORAN_VFEC);
-
-	/* set HSpol */
-	if (ztv->card->hsync_pos)
-		zrwrite(ZORAN_VFEH_HSPOL, ZORAN_VFEH);
-	/* set VSpol */
-	if (ztv->card->vsync_pos)
-		zrwrite(ZORAN_VFEV_VSPOL, ZORAN_VFEV);
-
-	/* Set the proper General Purpuse register bits */
-	/* implicit: no softreset, 0 waitstates */
-	zrwrite(ZORAN_PCI_SOFTRESET|(ztv->card->gpdir<<0),ZORAN_PCI);
-	/* implicit: 3 duration and recovery PCI clocks on guest 0-3 */
-	zrwrite(ztv->card->gpval<<24,ZORAN_GUEST);
-
-	/* clear interrupt status */
-	zrwrite(~0, ZORAN_ISR);
-
-	/*
-	 * i2c template
-	 */
-	ztv->i2c = zoran_i2c_bus_template;
-	sprintf(ztv->i2c.name,"zoran-%d",card);
-	ztv->i2c.data = ztv;
-
-	/*
-	 * Now add the template and register the device unit
-	 */
-	ztv->video_dev = zr36120_template;
-	strcpy(ztv->video_dev.name, ztv->i2c.name);
-	ztv->video_dev.priv = ztv;
-	if (video_register_device(&ztv->video_dev, VFL_TYPE_GRABBER, video_nr) < 0)
-		return -1;
-
-	ztv->vbi_dev = vbi_template;
-	strcpy(ztv->vbi_dev.name, ztv->i2c.name);
-	ztv->vbi_dev.priv = ztv;
-	if (video_register_device(&ztv->vbi_dev, VFL_TYPE_VBI, vbi_nr) < 0) {
-		video_unregister_device(&ztv->video_dev);
-		return -1;
-	}
-	i2c_register_bus(&ztv->i2c);
-
-	/* set interrupt mask - the PIN enable will be set later */
-	zrwrite(ZORAN_ICR_GIRQ0|ZORAN_ICR_GIRQ1|ZORAN_ICR_CODE, ZORAN_ICR);
-
-	printk(KERN_INFO "%s: installed %s\n",ztv->i2c.name,ztv->card->name);
-	return 0;
-}
-
-static
-void release_zoran(int max)
-{
-	struct zoran *ztv;
-	int i;
-
-	for (i=0;i<max; i++)
-	{
-		ztv = &zorans[i];
-
-		/* turn off all capturing, DMA and IRQs */
-		/* reset the zoran */
-		zrand(~ZORAN_PCI_SOFTRESET,ZORAN_PCI);
-		udelay(10);
-		zror(ZORAN_PCI_SOFTRESET,ZORAN_PCI);
-		udelay(10);
-
-		/* first disable interrupts before unmapping the memory! */
-		zrwrite(0, ZORAN_ICR);
-		zrwrite(0xffffffffUL,ZORAN_ISR);
-
-		/* free it */
-		free_irq(ztv->dev->irq,ztv);
-
-		/* unregister i2c_bus */
-		i2c_unregister_bus((&ztv->i2c));
-
-		/* unmap and free memory */
-		if (ztv->zoran_mem)
-			iounmap(ztv->zoran_mem);
-
-		/* Drop PCI device */
-		pci_dev_put(ztv->dev);
-
-		video_unregister_device(&ztv->video_dev);
-		video_unregister_device(&ztv->vbi_dev);
-	}
-}
-
-void __exit zr36120_exit(void)
-{
-	release_zoran(zoran_cards);
-}
-
-int __init zr36120_init(void)
-{
-	int	card;
-
-	handle_chipset();
-	zoran_cards = find_zoran();
-	if (zoran_cards <= 0)
-		return -EIO;
-
-	/* initialize Zorans */
-	for (card=0; card<zoran_cards; card++) {
-		if (init_zoran(card) < 0) {
-			/* only release the zorans we have registered */
-			release_zoran(card);
-			return -EIO;
-		}
-	}
-	return 0;
-}
-
-module_init(zr36120_init);
-module_exit(zr36120_exit);
diff --git a/drivers/media/video/zr36120.h b/drivers/media/video/zr36120.h
deleted file mode 100644
index a71e485..0000000
--- a/drivers/media/video/zr36120.h
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
-    zr36120.h - Zoran 36120/36125 based framegrabbers
-
-    Copyright (C) 1998-1999 Pauline Middelink (middelin@polyware.nl)
-
-    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.
-*/
-
-#ifndef _ZR36120_H
-#define _ZR36120_H
-
-#ifdef __KERNEL__
-
-#include <linux/types.h>
-#include <linux/wait.h>
-
-#include <linux/i2c-old.h>
-#include <linux/videodev.h>
-
-#include <asm/io.h>
-
-/*
- * Debug macro's, place an x behind the ) for actual debug-compilation
- * E.g. #define DEBUG(x...)	x
- */
-#define DEBUG(x...)			/* Debug driver */
-#define IDEBUG(x...)			/* Debug interrupt handler */
-#define PDEBUG		0		/* Debug PCI writes */
-
-/* defined in zr36120_i2c */
-extern struct i2c_bus zoran_i2c_bus_template;
-
-#define	ZORAN_MAX_FBUFFERS	2
-#define	ZORAN_MAX_FBUFFER	(768*576*2)
-#define	ZORAN_MAX_FBUFSIZE	(ZORAN_MAX_FBUFFERS*ZORAN_MAX_FBUFFER)
-
-#define	ZORAN_VBI_BUFFERS	2
-#define	ZORAN_VBI_BUFSIZE	(22*1024*2)
-
-struct tvcard {
-	char*	name;		/* name of the cardtype */
-	int	video_inputs;	/* number of channels defined in video_mux */
-	int	audio_inputs;	/* number of channels defined in audio_mux */
-	__u32	swapi2c:1,	/* need to swap i2c wires SDA/SCL? */
-		usegirq1:1,	/* VSYNC at GIRQ1 instead of GIRQ0? */
-		vsync_pos:1,	/* positive VSYNC signal? */
-		hsync_pos:1,	/* positive HSYNC signal? */
-		gpdir:8,	/* General Purpose Direction register */
-		gpval:8;	/* General Purpose Value register */
-	int	video_mux[6];	/* mapping channel number to physical input */
-#define		IS_TUNER	0x80
-#define		IS_SVHS		0x40
-#define		CHANNEL_MASK	0x3F
-	int	audio_mux[6];	/* mapping channel number to physical input */
-};
-#define	TUNER(x)	((x)|IS_TUNER)
-#define	SVHS(x)		((x)|IS_SVHS)
-
-struct vidinfo {
-	struct	vidinfo* next;	/* next active buffer			*/
-	uint	kindof;
-#define	FBUFFER_OVERLAY		0
-#define	FBUFFER_GRAB		1
-#define	FBUFFER_VBI		2
-	uint	status;
-#define FBUFFER_FREE		0
-#define FBUFFER_BUSY		1
-#define FBUFFER_DONE		2
-	ulong	fieldnr;	/* # of field, not framer!		*/
-	uint	x,y;
-	int	w,h;		/* w,h can be negative!			*/
-	uint	format;		/* index in palette2fmt[]		*/
-	uint	bpp;		/* lookup from palette2fmt[]		*/
-	uint	bpl;		/* calc: width * bpp			*/
-	ulong	busadr;		/* bus addr for DMA engine		*/
-	char*	memadr;		/* kernel addr for making copies	*/
-	ulong*	overlay;	/* kernel addr of overlay mask		*/
-};
-
-struct zoran
-{
-	struct video_device video_dev;
-#define CARD_DEBUG	KERN_DEBUG "%s(%lu): "
-#define CARD_INFO	KERN_INFO "%s(%lu): "
-#define CARD_ERR	KERN_ERR "%s(%lu): "
-#define CARD		ztv->video_dev.name,ztv->fieldnr
-
-	/* zoran chip specific details */
-	struct i2c_bus	i2c;		/* i2c registration data	*/
-	struct pci_dev*	dev;		/* ptr to PCI device		*/
-	ulong		zoran_adr;	/* bus address of IO memory	*/
-	char*		zoran_mem;	/* kernel address of IO memory	*/
-	struct tvcard*	card;		/* the cardtype			*/
-	uint		norm;		/* 0=PAL, 1=NTSC, 2=SECAM	*/
-	uint		tuner_freq;	/* Current freq in kHz		*/
-	struct video_picture picture;	/* Current picture params	*/
-
-	/* videocard details */
-	uint		swidth;		/* screen width			*/
-	uint		sheight;	/* screen height		*/
-	uint		depth;		/* depth in bits		*/
-
-	/* State details */
-	char*		fbuffer;	/* framebuffers for mmap	*/
-	struct vidinfo	overinfo;	/* overlay data			*/
-	struct vidinfo	grabinfo[ZORAN_MAX_FBUFFERS];	/* grabbing data*/
-	wait_queue_head_t grabq;	/* grabbers queue		*/
-
-	/* VBI details */
-	struct video_device vbi_dev;
-	struct vidinfo	readinfo[2];	/* VBI data - flip buffers	*/
-	wait_queue_head_t vbiq;		/* vbi queue			*/
-
-	/* maintenance data */
-	int		have_decoder;	/* did we detect a mux?		*/
-	int		have_tuner;	/* did we detect a tuner?	*/
-	int		users;		/* howmany video/vbi open?	*/
-	int		tuner_type;	/* tuner type, when found	*/
-	int		running;	/* are we rolling?		*/
-	rwlock_t	lock;
-	long		state;		/* what is requested of us?	*/
-#define STATE_OVERLAY	0
-#define STATE_VBI	1
-	struct vidinfo*	workqueue;	/* buffers to grab, head is active */
-	ulong		fieldnr;	/* #field, ticked every VSYNC	*/
-	ulong		lastfieldnr;	/* #field, ticked every GRAB	*/
-
-	int		vidInterlace;	/* calculated */
-	int		vidXshift;	/* calculated */
-	uint		vidWidth;	/* calculated */
-	uint		vidHeight;	/* calculated */
-};
-
-#define zrwrite(dat,adr)    writel((dat),(char *) (ztv->zoran_mem+(adr)))
-#define zrread(adr)         readl(ztv->zoran_mem+(adr))
-
-#if PDEBUG == 0
-#define zrand(dat,adr)      zrwrite((dat) & zrread(adr), adr)
-#define zror(dat,adr)       zrwrite((dat) | zrread(adr), adr)
-#define zraor(dat,mask,adr) zrwrite( ((dat)&~(mask)) | ((mask)&zrread(adr)), adr)
-#else
-#define zrand(dat, adr) \
-do { \
-	ulong data = (dat) & zrread((adr)); \
-	zrwrite(data, (adr)); \
-	if (0 != (~(dat) & zrread((adr)))) \
-		printk(KERN_DEBUG "zoran: zrand at %d(%d) detected set bits(%x)\n", __LINE__, (adr), (dat)); \
-} while(0)
-
-#define zror(dat, adr) \
-do { \
-	ulong data = (dat) | zrread((adr)); \
-	zrwrite(data, (adr)); \
-	if ((dat) != ((dat) & zrread(adr))) \
-		printk(KERN_DEBUG "zoran: zror at %d(%d) detected unset bits(%x)\n", __LINE__, (adr), (dat)); \
-} while(0)
-
-#define zraor(dat, mask, adr) \
-do { \
-	ulong data; \
-	if ((dat) & (mask)) \
-		printk(KERN_DEBUG "zoran: zraor at %d(%d) detected bits(%x:%x)\n", __LINE__, (adr), (dat), (mask)); \
-	data = ((dat)&~(mask)) | ((mask) & zrread((adr))); \
-	zrwrite(data,(adr)); \
-	if ( (dat) != (~(mask) & zrread((adr))) ) \
-		printk(KERN_DEBUG "zoran: zraor at %d(%d) could not set all bits(%x:%x)\n", __LINE__, (adr), (dat), (mask)); \
-} while(0)
-#endif
-
-#endif
-
-/* zoran PCI address space */
-#define ZORAN_VFEH		0x000	/* Video Front End Horizontal Conf. */
-#define	ZORAN_VFEH_HSPOL	(1<<30)
-#define	ZORAN_VFEH_HSTART	(0x3FF<<10)
-#define	ZORAN_VFEH_HEND		(0x3FF<<0)
-
-#define ZORAN_VFEV		0x004	/* Video Front End Vertical Conf. */
-#define	ZORAN_VFEV_VSPOL	(1<<30)
-#define	ZORAN_VFEV_VSTART	(0x3FF<<10)
-#define	ZORAN_VFEV_VEND		(0x3FF<<0)
-
-#define	ZORAN_VFEC		0x008	/* Video Front End Scaler and Pixel */
-#define ZORAN_VFEC_EXTFL	(1<<26)
-#define	ZORAN_VFEC_TOPFIELD	(1<<25)
-#define	ZORAN_VFEC_VCLKPOL	(1<<24)
-#define	ZORAN_VFEC_HFILTER	(7<<21)
-#define	ZORAN_VFEC_HFILTER_1	(0<<21)	/* no lumi,    3-tap chromo */
-#define	ZORAN_VFEC_HFILTER_2	(1<<21)	/* 3-tap lumi, 3-tap chromo */
-#define	ZORAN_VFEC_HFILTER_3	(2<<21)	/* 4-tap lumi, 4-tap chromo */
-#define	ZORAN_VFEC_HFILTER_4	(3<<21)	/* 5-tap lumi, 4-tap chromo */
-#define	ZORAN_VFEC_HFILTER_5	(4<<21)	/* 4-tap lumi, 4-tap chromo */
-#define	ZORAN_VFEC_DUPFLD	(1<<20)
-#define	ZORAN_VFEC_HORDCM	(63<<14)
-#define	ZORAN_VFEC_VERDCM	(63<<8)
-#define	ZORAN_VFEC_DISPMOD	(1<<6)
-#define	ZORAN_VFEC_RGB		(3<<3)
-#define	ZORAN_VFEC_RGB_YUV422	(0<<3)
-#define	ZORAN_VFEC_RGB_RGB888	(1<<3)
-#define	ZORAN_VFEC_RGB_RGB565	(2<<3)
-#define	ZORAN_VFEC_RGB_RGB555	(3<<3)
-#define	ZORAN_VFEC_ERRDIF	(1<<2)
-#define	ZORAN_VFEC_PACK24	(1<<1)
-#define	ZORAN_VFEC_LE		(1<<0)
-
-#define	ZORAN_VTOP		0x00C	/* Video Display "Top" */
-
-#define	ZORAN_VBOT		0x010	/* Video Display "Bottom" */
-
-#define	ZORAN_VSTR		0x014	/* Video Display Stride */
-#define	ZORAN_VSTR_DISPSTRIDE	(0xFFFF<<16)
-#define	ZORAN_VSTR_VIDOVF	(1<<8)
-#define	ZORAN_VSTR_SNAPSHOT	(1<<1)
-#define	ZORAN_VSTR_GRAB		(1<<0)
-
-#define	ZORAN_VDC		0x018	/* Video Display Conf. */
-#define	ZORAN_VDC_VIDEN		(1<<31)
-#define	ZORAN_VDC_MINPIX	(0x1F<<25)
-#define	ZORAN_VDC_TRICOM	(1<<24)
-#define	ZORAN_VDC_VIDWINHT	(0x3FF<<12)
-#define	ZORAN_VDC_VIDWINWID	(0x3FF<<0)
-
-#define	ZORAN_MTOP		0x01C	/* Masking Map "Top" */
-
-#define	ZORAN_MBOT		0x020	/* Masking Map "Bottom" */
-
-#define	ZORAN_OCR		0x024	/* Overlay Control */
-#define	ZORAN_OCR_OVLEN		(1<<15)
-#define	ZORAN_OCR_MASKSTRIDE	(0xFF<<0)
-
-#define	ZORAN_PCI		0x028	/* System, PCI and GPP Control */
-#define	ZORAN_PCI_SOFTRESET	(1<<24)
-#define	ZORAN_PCI_WAITSTATE	(3<<16)
-#define	ZORAN_PCI_GENPURDIR	(0xFF<<0)
-
-#define	ZORAN_GUEST		0x02C	/* GuestBus Control */
-
-#define	ZORAN_CSOURCE		0x030	/* Code Source Address */
-
-#define	ZORAN_CTRANS		0x034	/* Code Transfer Control */
-
-#define	ZORAN_CMEM		0x038	/* Code Memory Pointer */
-
-#define	ZORAN_ISR		0x03C	/* Interrupt Status Register */
-#define	ZORAN_ISR_CODE		(1<<28)
-#define	ZORAN_ISR_GIRQ0		(1<<29)
-#define	ZORAN_ISR_GIRQ1		(1<<30)
-
-#define	ZORAN_ICR		0x040	/* Interrupt Control Register */
-#define	ZORAN_ICR_EN		(1<<24)
-#define	ZORAN_ICR_CODE		(1<<28)
-#define	ZORAN_ICR_GIRQ0		(1<<29)
-#define	ZORAN_ICR_GIRQ1		(1<<30)
-
-#define	ZORAN_I2C		0x044	/* I2C-Bus */
-#define ZORAN_I2C_SCL		(1<<1)
-#define ZORAN_I2C_SDA		(1<<0)
-
-#define	ZORAN_POST		0x48	/* PostOffice */
-#define	ZORAN_POST_PEN		(1<<25)
-#define	ZORAN_POST_TIME		(1<<24)
-#define	ZORAN_POST_DIR		(1<<23)
-#define	ZORAN_POST_GUESTID	(3<<20)
-#define	ZORAN_POST_GUEST	(7<<16)
-#define	ZORAN_POST_DATA		(0xFF<<0)
-
-#endif
diff --git a/drivers/media/video/zr36120_i2c.c b/drivers/media/video/zr36120_i2c.c
deleted file mode 100644
index 21fde43..0000000
--- a/drivers/media/video/zr36120_i2c.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
-    zr36120_i2c.c - Zoran 36120/36125 based framegrabbers
-
-    Copyright (C) 1998-1999 Pauline Middelink <middelin@polyware.nl>
-
-    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/types.h>
-#include <linux/delay.h>
-#include <asm/io.h>
-
-#include <linux/video_decoder.h>
-#include <asm/uaccess.h>
-
-#include "tuner.h"
-#include "zr36120.h"
-
-/* ----------------------------------------------------------------------- */
-/* I2C functions							   */
-/* ----------------------------------------------------------------------- */
-
-/* software I2C functions */
-
-#define I2C_DELAY   10
-
-static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data)
-{
-	struct zoran *ztv = (struct zoran*)bus->data;
-	unsigned int b = 0;
-	if (data) b |= ztv->card->swapi2c ? ZORAN_I2C_SCL : ZORAN_I2C_SDA;
-	if (ctrl) b |= ztv->card->swapi2c ? ZORAN_I2C_SDA : ZORAN_I2C_SCL;
-	zrwrite(b, ZORAN_I2C);
-	udelay(I2C_DELAY);
-}
-
-static int i2c_getdataline(struct i2c_bus *bus)
-{
-	struct zoran *ztv = (struct zoran*)bus->data;
-	if (ztv->card->swapi2c)
-		return zrread(ZORAN_I2C) & ZORAN_I2C_SCL;
-	return zrread(ZORAN_I2C) & ZORAN_I2C_SDA;
-}
-
-static
-void attach_inform(struct i2c_bus *bus, int id)
-{
-	struct zoran *ztv = (struct zoran*)bus->data;
-	struct video_decoder_capability dc;
-	int rv;
-
-	switch (id) {
-	 case I2C_DRIVERID_VIDEODECODER:
-		DEBUG(printk(CARD_INFO "decoder attached\n",CARD));
-
-		/* fetch the capabilities of the decoder */
-		rv = i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_GET_CAPABILITIES, &dc);
-		if (rv) {
-			DEBUG(printk(CARD_DEBUG "decoder is not V4L aware!\n",CARD));
-			break;
-		}
-		DEBUG(printk(CARD_DEBUG "capabilities %d %d %d\n",CARD,dc.flags,dc.inputs,dc.outputs));
-
-		/* Test if the decoder can de VBI transfers */
-		if (dc.flags & 16 /*VIDEO_DECODER_VBI*/)
-			ztv->have_decoder = 2;
-		else
-			ztv->have_decoder = 1;
-		break;
-	 case I2C_DRIVERID_TUNER:
-		ztv->have_tuner = 1;
-		DEBUG(printk(CARD_INFO "tuner attached\n",CARD));
-		if (ztv->tuner_type >= 0)
-		{
-			if (i2c_control_device(&ztv->i2c,I2C_DRIVERID_TUNER,TUNER_SET_TYPE,&ztv->tuner_type)<0)
-			DEBUG(printk(CARD_INFO "attach_inform; tuner won't be set to type %d\n",CARD,ztv->tuner_type));
-		}
-		break;
-	 default:
-		DEBUG(printk(CARD_INFO "attach_inform; unknown device id=%d\n",CARD,id));
-		break;
-	}
-}
-
-static
-void detach_inform(struct i2c_bus *bus, int id)
-{
-	struct zoran *ztv = (struct zoran*)bus->data;
-
-	switch (id) {
-	 case I2C_DRIVERID_VIDEODECODER:
-		ztv->have_decoder = 0;
-		DEBUG(printk(CARD_INFO "decoder detached\n",CARD));
-		break;
-	 case I2C_DRIVERID_TUNER:
-		ztv->have_tuner = 0;
-		DEBUG(printk(CARD_INFO "tuner detached\n",CARD));
-		break;
-	 default:
-		DEBUG(printk(CARD_INFO "detach_inform; unknown device id=%d\n",CARD,id));
-		break;
-	}
-}
-
-struct i2c_bus zoran_i2c_bus_template =
-{
-	"ZR36120",
-	I2C_BUSID_ZORAN,
-	NULL,
-
-	SPIN_LOCK_UNLOCKED,
-
-	attach_inform,
-	detach_inform,
-
-	i2c_setlines,
-	i2c_getdataline,
-	NULL,
-	NULL
-};
diff --git a/drivers/media/video/zr36120_mem.c b/drivers/media/video/zr36120_mem.c
deleted file mode 100644
index 416eaa9..0000000
--- a/drivers/media/video/zr36120_mem.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-    zr36120_mem.c - Zoran 36120/36125 based framegrabbers
-
-    Copyright (C) 1998-1999 Pauline Middelink <middelin@polyware.nl>
-
-    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/mm.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <asm/io.h>
-#ifdef CONFIG_BIGPHYS_AREA
-#include <linux/bigphysarea.h>
-#endif
-
-#include "zr36120.h"
-#include "zr36120_mem.h"
-
-/*******************************/
-/* Memory management functions */
-/*******************************/
-
-void* bmalloc(unsigned long size)
-{
-	void* mem;
-#ifdef CONFIG_BIGPHYS_AREA
-	mem = bigphysarea_alloc_pages(size/PAGE_SIZE, 1, GFP_KERNEL);
-#else
-	/*
-	 * The following function got a lot of memory at boottime,
-	 * so we know its always there...
-	 */
-	mem = (void*)__get_free_pages(GFP_USER|GFP_DMA,get_order(size));
-#endif
-	if (mem) {
-		unsigned long adr = (unsigned long)mem;
-		while (size > 0) {
-			SetPageReserved(virt_to_page(phys_to_virt(adr)));
-			adr += PAGE_SIZE;
-			size -= PAGE_SIZE;
-		}
-	}
-	return mem;
-}
-
-void bfree(void* mem, unsigned long size)
-{
-	if (mem) {
-		unsigned long adr = (unsigned long)mem;
-		unsigned long siz = size;
-		while (siz > 0) {
-			ClearPageReserved(virt_to_page(phys_to_virt(adr)));
-			adr += PAGE_SIZE;
-			siz -= PAGE_SIZE;
-		}
-#ifdef CONFIG_BIGPHYS_AREA
-		bigphysarea_free_pages(mem);
-#else
-		free_pages((unsigned long)mem,get_order(size));
-#endif
-	}
-}
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/zr36120_mem.h b/drivers/media/video/zr36120_mem.h
deleted file mode 100644
index aad117a..0000000
--- a/drivers/media/video/zr36120_mem.h
+++ /dev/null
@@ -1,3 +0,0 @@
-/* either kmalloc() or bigphysarea() alloced memory - continuous */
-void*	bmalloc(unsigned long size);
-void	bfree(void* mem, unsigned long size);
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index 0a8f750c..0f4cf34 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -159,6 +159,7 @@
 #define I2C_DRIVERID_ASB100 1043
 #define I2C_DRIVERID_FSCHER 1046
 #define I2C_DRIVERID_W83L785TS 1047
+#define I2C_DRIVERID_OV7670 1048	/* Omnivision 7670 camera */
 
 /*
  * ---- Adapter types ----------------------------------------------------
@@ -250,6 +251,7 @@
 #define I2C_HW_SMBUS_OV518	0x04000f /* OV518(+) USB 1.1 webcam ICs */
 #define I2C_HW_SMBUS_OV519	0x040010 /* OV519 USB 1.1 webcam IC */
 #define I2C_HW_SMBUS_OVFX2	0x040011 /* Cypress/OmniVision FX2 webcam */
+#define I2C_HW_SMBUS_CAFE	0x040012 /* Marvell 88ALP01 "CAFE" cam  */
 
 /* --- ISA pseudo-adapter						*/
 #define I2C_HW_ISA		0x050000
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index df5c465..5cb380a 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -244,6 +244,7 @@
 #define V4L2_PIX_FMT_YYUV    v4l2_fourcc('Y','Y','U','V') /* 16  YUV 4:2:2     */
 #define V4L2_PIX_FMT_HI240   v4l2_fourcc('H','I','2','4') /*  8  8-bit color   */
 #define V4L2_PIX_FMT_HM12    v4l2_fourcc('H','M','1','2') /*  8  YUV 4:2:0 16x16 macroblocks */
+#define V4L2_PIX_FMT_RGB444  v4l2_fourcc('R','4','4','4') /* 16  xxxxrrrr ggggbbbb */
 
 /* see http://www.siliconimaging.com/RGB%20Bayer.htm */
 #define V4L2_PIX_FMT_SBGGR8  v4l2_fourcc('B','A','8','1') /*  8  BGBG.. GRGR.. */
diff --git a/include/media/ir-common.h b/include/media/ir-common.h
index 8f58406..2b25f5c 100644
--- a/include/media/ir-common.h
+++ b/include/media/ir-common.h
@@ -92,6 +92,7 @@
 extern IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE];
 extern IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE];
 extern IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE];
+extern IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE];
 
 #endif
 
diff --git a/include/media/saa7146.h b/include/media/saa7146.h
index fee579f..796bcf1 100644
--- a/include/media/saa7146.h
+++ b/include/media/saa7146.h
@@ -42,10 +42,6 @@
 #define DEB_INT(x)  if (0!=(DEBUG_VARIABLE&0x20)) { DEBUG_PROLOG; printk x; } /* interrupt debug messages */
 #define DEB_CAP(x)  if (0!=(DEBUG_VARIABLE&0x40)) { DEBUG_PROLOG; printk x; } /* capture debug messages */
 
-#define SAA7146_IER_DISABLE(x,y) \
-	saa7146_write(x, IER, saa7146_read(x, IER) & ~(y));
-#define SAA7146_IER_ENABLE(x,y) \
-	saa7146_write(x, IER, saa7146_read(x, IER) | (y));
 #define SAA7146_ISR_CLEAR(x,y) \
 	saa7146_write(x, ISR, (y));
 
@@ -441,4 +437,20 @@
 #define SAA7146_I2C_BUS_BIT_RATE_80	(0x200)
 #define SAA7146_I2C_BUS_BIT_RATE_60	(0x300)
 
+static inline void SAA7146_IER_DISABLE(struct saa7146_dev *x, unsigned y)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&x->int_slock, flags);
+	saa7146_write(x, IER, saa7146_read(x, IER) & ~y);
+	spin_unlock_irqrestore(&x->int_slock, flags);
+}
+
+static inline void SAA7146_IER_ENABLE(struct saa7146_dev *x, unsigned y)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&x->int_slock, flags);
+	saa7146_write(x, IER, saa7146_read(x, IER) | y);
+	spin_unlock_irqrestore(&x->int_slock, flags);
+}
+
 #endif
diff --git a/include/media/tuner-types.h b/include/media/tuner-types.h
index 37dad07..e5ad3fc 100644
--- a/include/media/tuner-types.h
+++ b/include/media/tuner-types.h
@@ -50,6 +50,10 @@
 	   sensitivity. If this setting is 1, then set PORT2 to 1 to
 	   get proper FM reception. */
 	unsigned int port2_fm_high_sensitivity:1;
+	/* Some Philips tuners use tda9887 cGainNormal to select the FM radio
+	   sensitivity. If this setting is 1, e register will use cGainNormal
+	   instead of cGainLow. */
+	unsigned int fm_gain_normal:1;
 	/* Most tuners with a tda9887 use QSS mode. Some (cheaper) tuners
 	   use Intercarrier mode. If this setting is 1, then the tuner
 	   needs to be set to intercarrier mode. */
diff --git a/include/media/tuner.h b/include/media/tuner.h
index 3116e75..99acf84 100644
--- a/include/media/tuner.h
+++ b/include/media/tuner.h
@@ -145,6 +145,7 @@
 #define TDA9887_DEEMPHASIS_75 		(3<<16)
 #define TDA9887_AUTOMUTE 		(1<<18)
 #define TDA9887_GATING_18		(1<<19)
+#define TDA9887_GAIN_NORMAL		(1<<20)
 
 #ifdef __KERNEL__
 
diff --git a/include/media/tveeprom.h b/include/media/tveeprom.h
index e9fc1a7..5660ea2 100644
--- a/include/media/tveeprom.h
+++ b/include/media/tveeprom.h
@@ -3,7 +3,7 @@
 
 struct tveeprom {
 	u32 has_radio;
-	u32 has_ir;     /* 0: no IR, 1: IR present, 2: unknown */
+	u32 has_ir;     /* bit 0: IR receiver present, bit 1: IR transmitter (blaster) present. -1 == unknown */
 	u32 has_MAC_address; /* 0: no MAC, 1: MAC present, 2: unknown */
 
 	u32 tuner_type;
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index aecc946..91b1992 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -144,6 +144,9 @@
 	V4L2_IDENT_CX25841 = 241,
 	V4L2_IDENT_CX25842 = 242,
 	V4L2_IDENT_CX25843 = 243,
+
+	/* OmniVision sensors - range 250-299 */
+	V4L2_IDENT_OV7670 = 250,
 };
 
 /* audio ioctls */
@@ -251,4 +254,8 @@
    If the frequency is not supported, then -EINVAL is returned. */
 #define VIDIOC_INT_S_CRYSTAL_FREQ 	_IOW ('d', 113, struct v4l2_crystal_freq)
 
+/* Initialize the sensor registors to some sort of reasonable
+   default values. */
+#define VIDIOC_INT_INIT			_IOW ('d', 114, u32)
+
 #endif /* V4L2_COMMON_H_ */
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 6a11d77..fb96472 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -43,6 +43,7 @@
 
 /*  Video standard functions  */
 extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs);
+extern char *v4l2_norm_to_name(v4l2_std_id id);
 extern int v4l2_video_std_construct(struct v4l2_standard *vs,
 				    int id, char *name);
 
@@ -81,12 +82,6 @@
  * 	This version moves redundant code from video device code to
  *	the common handler
  */
-struct v4l2_tvnorm {
-	char          *name;
-	v4l2_std_id   id;
-
-	void          *priv_data;
-};
 
 struct video_device
 {
@@ -104,9 +99,8 @@
 	int debug;	/* Activates debug level*/
 
 	/* Video standard vars */
-	int tvnormsize;	/* Size of tvnorm array */
-	v4l2_std_id current_norm; /* Current tvnorm */
-	struct v4l2_tvnorm *tvnorms;
+	v4l2_std_id tvnorms;		/* Supported tv norms */
+	v4l2_std_id current_norm;	/* Current tvnorm */
 
 	/* callbacks */
 	void (*release)(struct video_device *vfd);
@@ -211,7 +205,7 @@
 		/* Standard handling
 			G_STD and ENUMSTD are handled by videodev.c
 		 */
-	int (*vidioc_s_std)    (struct file *file, void *fh, v4l2_std_id a);
+	int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id *norm);
 	int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a);
 
 		/* Input handling */