Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
new file mode 100644
index 0000000..883ec08
--- /dev/null
+++ b/drivers/media/dvb/Kconfig
@@ -0,0 +1,47 @@
+#
+# Multimedia device configuration
+#
+
+menu "Digital Video Broadcasting Devices"
+
+config DVB
+	bool "DVB For Linux"
+	depends on NET && INET
+	---help---
+	  Support Digital Video Broadcasting hardware.  Enable this if you
+	  own a DVB adapter and want to use it or if you compile Linux for
+	  a digital SetTopBox.
+
+	  API specs and user tools are available from <http://www.linuxtv.org/>.
+
+	  Please report problems regarding this driver to the LinuxDVB
+	  mailing list.
+
+	  If unsure say N.
+
+source "drivers/media/dvb/dvb-core/Kconfig"
+
+comment "Supported SAA7146 based PCI Adapters"
+	depends on DVB_CORE && PCI
+source "drivers/media/dvb/ttpci/Kconfig"
+
+comment "Supported USB Adapters"
+	depends on DVB_CORE && USB
+source "drivers/media/dvb/ttusb-budget/Kconfig"
+source "drivers/media/dvb/ttusb-dec/Kconfig"
+source "drivers/media/dvb/dibusb/Kconfig"
+source "drivers/media/dvb/cinergyT2/Kconfig"
+
+comment "Supported FlexCopII (B2C2) Adapters"
+	depends on DVB_CORE && PCI
+source "drivers/media/dvb/b2c2/Kconfig"
+
+comment "Supported BT878 Adapters"
+	depends on DVB_CORE && PCI
+source "drivers/media/dvb/bt8xx/Kconfig"
+
+comment "Supported DVB Frontends"
+	depends on DVB_CORE
+source "drivers/media/dvb/frontends/Kconfig"
+
+endmenu
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
new file mode 100644
index 0000000..520fc39
--- /dev/null
+++ b/drivers/media/dvb/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the kernel multimedia device drivers.
+#
+
+obj-y        := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dibusb/ cinergyT2/
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
new file mode 100644
index 0000000..5259690
--- /dev/null
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -0,0 +1,26 @@
+config DVB_B2C2_SKYSTAR
+	tristate "B2C2/Technisat Air/Sky/CableStar 2 PCI"
+	depends on DVB_CORE && PCI
+	select DVB_STV0299
+	select DVB_MT352
+	select DVB_MT312
+	select DVB_NXT2002
+	help
+	  Support for the Skystar2 PCI DVB card by Technisat, which
+	  is equipped with the FlexCopII chipset by B2C2, and
+	  for the B2C2/BBTI Air2PC-ATSC card.
+
+	  Say Y if you own such a device and want to use it.
+
+config DVB_B2C2_USB
+	tristate "B2C2/Technisat Air/Sky/Cable2PC USB"
+	depends on DVB_CORE && USB && EXPERIMENTAL
+	select DVB_STV0299
+	select DVB_MT352
+	help
+	  Support for the Air/Sky/Cable2PC USB DVB device by B2C2. Currently
+	  the does nothing, but providing basic function for the used usb 
+	  protocol.
+
+	  Say Y if you own such a device and want to use it.
+
diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile
new file mode 100644
index 0000000..9fb1247
--- /dev/null
+++ b/drivers/media/dvb/b2c2/Makefile
@@ -0,0 +1,6 @@
+obj-b2c2-usb = b2c2-usb-core.o b2c2-common.o
+
+obj-$(CONFIG_DVB_B2C2_SKYSTAR) += skystar2.o
+obj-$(CONFIG_DVB_B2C2_USB) + = b2c2-usb.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/b2c2/b2c2-common.c b/drivers/media/dvb/b2c2/b2c2-common.c
new file mode 100644
index 0000000..000d60c
--- /dev/null
+++ b/drivers/media/dvb/b2c2/b2c2-common.c
@@ -0,0 +1,214 @@
+/*
+ * b2c2-common.c - common methods for the B2C2/Technisat SkyStar2 PCI DVB card and
+ *                 for the B2C2/Technisat Sky/Cable/AirStar USB devices
+ *                 based on the FlexCopII/FlexCopIII by B2C2, Inc.
+ *
+ * Copyright (C) 2003  Vadim Catana, skystar@moldova.cc
+ *
+ * FIX: DISEQC Tone Burst in flexcop_diseqc_ioctl()
+ * FIX: FULL soft DiSEqC for skystar2 (FlexCopII rev 130) VP310 equipped
+ *     Vincenzo Di Massa, hawk.it at tiscalinet.it
+ *
+ * Converted to Linux coding style
+ * Misc reorganization, polishing, restyling
+ *     Roberto Ragusa, r.ragusa at libero.it
+ *
+ * Added hardware filtering support,
+ *     Niklas Peinecke, peinecke at gdv.uni-hannover.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+#include "stv0299.h"
+#include "mt352.h"
+#include "mt312.h"
+
+static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+	else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+	else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+	else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+	else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+	else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
+
+	stv0299_writereg (fe, 0x13, aclk);
+	stv0299_writereg (fe, 0x14, bclk);
+	stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg (fe, 0x20, (ratio >>  8) & 0xff);
+	stv0299_writereg (fe, 0x21, (ratio      ) & 0xf0);
+
+	return 0;
+}
+
+static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+//	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	div = params->frequency / 125;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x84;  // 0xC4
+	buf[3] = 0x08;
+
+	if (params->frequency < 1500000) buf[3] |= 0x10;
+
+//	if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static u8 samsung_tbmu24112_inittab[] = {
+	     0x01, 0x15,
+	     0x02, 0x30,
+	     0x03, 0x00,
+	     0x04, 0x7D,
+	     0x05, 0x35,
+	     0x06, 0x02,
+	     0x07, 0x00,
+	     0x08, 0xC3,
+	     0x0C, 0x00,
+	     0x0D, 0x81,
+	     0x0E, 0x23,
+	     0x0F, 0x12,
+	     0x10, 0x7E,
+	     0x11, 0x84,
+	     0x12, 0xB9,
+	     0x13, 0x88,
+	     0x14, 0x89,
+	     0x15, 0xC9,
+	     0x16, 0x00,
+	     0x17, 0x5C,
+	     0x18, 0x00,
+	     0x19, 0x00,
+	     0x1A, 0x00,
+	     0x1C, 0x00,
+	     0x1D, 0x00,
+	     0x1E, 0x00,
+	     0x1F, 0x3A,
+	     0x20, 0x2E,
+	     0x21, 0x80,
+	     0x22, 0xFF,
+	     0x23, 0xC1,
+	     0x28, 0x00,
+	     0x29, 0x1E,
+	     0x2A, 0x14,
+	     0x2B, 0x0F,
+	     0x2C, 0x09,
+	     0x2D, 0x05,
+	     0x31, 0x1F,
+	     0x32, 0x19,
+	     0x33, 0xFE,
+	     0x34, 0x93,
+	     0xff, 0xff,
+};
+
+static struct stv0299_config samsung_tbmu24112_config = {
+	.demod_address = 0x68,
+	.inittab = samsung_tbmu24112_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_LK,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
+   	.pll_set = samsung_tbmu24112_pll_set,
+};
+
+
+
+
+
+static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+	static u8 mt352_clock_config [] = { 0x89, 0x10, 0x2d };
+	static u8 mt352_reset [] = { 0x50, 0x80 };
+	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
+	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+	mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+	return 0;
+}
+
+static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+	u32 div;
+	unsigned char bs = 0;
+
+	#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+	div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+	if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
+	if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
+	if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
+
+	pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
+	pllbuf[1] = div >> 8;
+   	pllbuf[2] = div & 0xff;
+   	pllbuf[3] = 0xcc;
+   	pllbuf[4] = bs;
+
+	return 0;
+}
+
+static struct mt352_config samsung_tdtc9251dh0_config = {
+
+	.demod_address = 0x0f,
+	.demod_init = samsung_tdtc9251dh0_demod_init,
+   	.pll_set = samsung_tdtc9251dh0_pll_set,
+};
+
+static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+//	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	div = (params->frequency + (125/2)) / 125;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = (div >> 0) & 0xff;
+	buf[2] = 0x84 | ((div >> 10) & 0x60);
+	buf[3] = 0x80;
+
+	if (params->frequency < 1550000)
+		buf[3] |= 0x02;
+
+	//if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct mt312_config skystar23_samsung_tbdu18132_config = {
+
+	.demod_address = 0x0e,
+   	.pll_set = skystar23_samsung_tbdu18132_pll_set,
+};
diff --git a/drivers/media/dvb/b2c2/b2c2-usb-core.c b/drivers/media/dvb/b2c2/b2c2-usb-core.c
new file mode 100644
index 0000000..9306da0
--- /dev/null
+++ b/drivers/media/dvb/b2c2/b2c2-usb-core.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2004 Patrick Boettcher <patrick.boettcher@desy.de>,
+ *                    Luca Bertagnolio <>,
+ *
+ * based on information provided by John Jurrius from BBTI, Inc.
+ *
+ *	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/config.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "dvb_frontend.h"
+
+/* debug */
+#define dprintk(level,args...) \
+	    do { if ((debug & level)) { printk(args); } } while (0)
+#define debug_dump(b,l) if (debug) {\
+	int i; deb_xfer("%s: %d > ",__FUNCTION__,l); \
+	for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \
+	deb_xfer("\n");\
+}
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4 (or-able)).");
+
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_ts(args...)   dprintk(0x02,args)
+#define deb_ctrl(args...) dprintk(0x04,args)
+
+/* Version information */
+#define DRIVER_VERSION "0.0"
+#define DRIVER_DESC "Driver for B2C2/Technisat Air/Cable/Sky-2-PC USB devices"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+/* transfer parameters */
+#define B2C2_USB_FRAMES_PER_ISO		4
+#define B2C2_USB_NUM_ISO_URB		4    /* TODO check out a good value */
+
+#define B2C2_USB_CTRL_PIPE_IN		usb_rcvctrlpipe(b2c2->udev,0)
+#define B2C2_USB_CTRL_PIPE_OUT		usb_sndctrlpipe(b2c2->udev,0)
+#define B2C2_USB_DATA_PIPE			usb_rcvisocpipe(b2c2->udev,0x81)
+
+struct usb_b2c2_usb {
+	struct usb_device *udev;
+	struct usb_interface *uintf;
+
+	u8 *iso_buffer;
+	int buffer_size;
+	dma_addr_t iso_dma_handle;
+	struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
+};
+
+
+/*
+ * USB
+ * 10 90 34 12 78 56 04 00
+ * usb_control_msg(udev, usb_sndctrlpipe(udev,0),
+ * 0x90,
+ * 0x10,
+ * 0x1234,
+ * 0x5678,
+ * buf,
+ * 4,
+ * 5*HZ);
+ *
+ * extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
+ * __u8 request,
+ * __u8 requesttype,
+ * __u16 value,
+ * __u16 index,
+ * void *data,
+ * __u16 size,
+ * int timeout);
+ *
+ */
+
+/* request types */
+typedef enum {
+
+/* something is wrong with this part
+	RTYPE_READ_DW         = (1 << 6),
+	RTYPE_WRITE_DW_1      = (3 << 6),
+	RTYPE_READ_V8_MEMORY  = (6 << 6),
+	RTYPE_WRITE_V8_MEMORY = (7 << 6),
+	RTYPE_WRITE_V8_FLASH  = (8 << 6),
+	RTYPE_GENERIC         = (9 << 6),
+*/
+	RTYPE_READ_DW = (3 << 6),
+	RTYPE_WRITE_DW_1 = (1 << 6),
+	
+	RTYPE_READ_V8_MEMORY  = (6 << 6),
+	RTYPE_WRITE_V8_MEMORY = (7 << 6),
+	RTYPE_WRITE_V8_FLASH  = (8 << 6),
+	RTYPE_GENERIC         = (9 << 6),
+} b2c2_usb_request_type_t;
+
+/* request */
+typedef enum {
+	B2C2_USB_WRITE_V8_MEM = 0x04,
+	B2C2_USB_READ_V8_MEM  = 0x05,
+	B2C2_USB_READ_REG     = 0x08,
+	B2C2_USB_WRITE_REG    = 0x0A,
+/*	B2C2_USB_WRITEREGLO   = 0x0A, */
+	B2C2_USB_WRITEREGHI   = 0x0B,
+	B2C2_USB_FLASH_BLOCK  = 0x10,
+	B2C2_USB_I2C_REQUEST  = 0x11,
+	B2C2_USB_UTILITY      = 0x12,
+} b2c2_usb_request_t;
+
+/* function definition for I2C_REQUEST */
+typedef enum {
+	USB_FUNC_I2C_WRITE       = 0x01,
+	USB_FUNC_I2C_MULTIWRITE  = 0x02,
+	USB_FUNC_I2C_READ        = 0x03,
+	USB_FUNC_I2C_REPEATWRITE = 0x04,
+	USB_FUNC_GET_DESCRIPTOR  = 0x05,
+	USB_FUNC_I2C_REPEATREAD  = 0x06,
+/* DKT 020208 - add this to support special case of DiSEqC */
+	USB_FUNC_I2C_CHECKWRITE  = 0x07,
+	USB_FUNC_I2C_CHECKRESULT = 0x08,
+} b2c2_usb_i2c_function_t;
+
+/*
+ * function definition for UTILITY request 0x12
+ * DKT 020304 - new utility function
+ */
+typedef enum {
+	UTILITY_SET_FILTER          = 0x01,
+	UTILITY_DATA_ENABLE         = 0x02,
+	UTILITY_FLEX_MULTIWRITE     = 0x03,
+	UTILITY_SET_BUFFER_SIZE     = 0x04,
+	UTILITY_FLEX_OPERATOR       = 0x05,
+	UTILITY_FLEX_RESET300_START = 0x06,
+	UTILITY_FLEX_RESET300_STOP  = 0x07,
+	UTILITY_FLEX_RESET300       = 0x08,
+	UTILITY_SET_ISO_SIZE        = 0x09,
+	UTILITY_DATA_RESET          = 0x0A,
+	UTILITY_GET_DATA_STATUS     = 0x10,
+	UTILITY_GET_V8_REG          = 0x11,
+/* DKT 020326 - add function for v1.14 */
+	UTILITY_SRAM_WRITE          = 0x12,
+	UTILITY_SRAM_READ           = 0x13,
+	UTILITY_SRAM_TESTFILL       = 0x14,
+	UTILITY_SRAM_TESTSET        = 0x15,
+	UTILITY_SRAM_TESTVERIFY     = 0x16,
+} b2c2_usb_utility_function_t;
+
+#define B2C2_WAIT_FOR_OPERATION_RW  1  // 1 s
+#define B2C2_WAIT_FOR_OPERATION_RDW 3  // 3 s
+#define B2C2_WAIT_FOR_OPERATION_WDW 1  // 1 s
+
+#define B2C2_WAIT_FOR_OPERATION_V8READ   3  // 3 s
+#define B2C2_WAIT_FOR_OPERATION_V8WRITE  3  // 3 s
+#define B2C2_WAIT_FOR_OPERATION_V8FLASH  3  // 3 s
+
+/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
+ * in the IBI address, to make the V8 code simpler.
+ * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (these are the six bits used)
+ *                  in general: 0000 0HHH 000L LL00
+ * IBI ADDRESS FORMAT:                    RHHH BLLL
+ *
+ * where R is the read(1)/write(0) bit, B is the busy bit
+ * and HHH and LLL are the two sets of three bits from the PCI address.
+ */
+#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
+#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
+
+/*
+ * DKT 020228 - forget about this VENDOR_BUFFER_SIZE, read and write register
+ * deal with DWORD or 4 bytes, that should be should from now on
+ */
+static u32 b2c2_usb_read_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI)
+{
+	u32 val;
+	u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | 0x0080;
+	int len = usb_control_msg(b2c2->udev,
+			B2C2_USB_CTRL_PIPE_IN,
+			B2C2_USB_READ_REG,
+			RTYPE_READ_DW,
+			wAddress,
+			0,
+			&val,
+			sizeof(u32),
+			B2C2_WAIT_FOR_OPERATION_RDW * 1000);
+
+	if (len != sizeof(u32)) {
+		err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
+		return -EIO;
+	} else
+		return val;
+}
+
+/*
+ * DKT 020228 - from now on, we don't support anything older than firm 1.00
+ * I eliminated the write register as a 2 trip of writing hi word and lo word
+ * and force this to write only 4 bytes at a time.
+ * NOTE: this should work with all the firmware from 1.00 and newer
+ */
+static int b2c2_usb_write_dw(struct usb_b2c2_usb *b2c2, u16 wRegOffsPCI, u32 val)
+{
+	u16 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI);
+	int len = usb_control_msg(b2c2->udev,
+			B2C2_USB_CTRL_PIPE_OUT,
+			B2C2_USB_WRITE_REG,
+			RTYPE_WRITE_DW_1,
+			wAddress,
+			0,
+			&val,
+			sizeof(u32),
+			B2C2_WAIT_FOR_OPERATION_RDW * 1000);
+
+	if (len != sizeof(u32)) {
+		err("error while reading dword from %d (%d).",wAddress,wRegOffsPCI);
+		return -EIO;
+	} else
+		return 0;
+}
+
+/*
+ * DKT 010817 - add support for V8 memory read/write and flash update
+ */
+static int b2c2_usb_v8_memory_req(struct usb_b2c2_usb *b2c2,
+		b2c2_usb_request_t req, u8 page, u16 wAddress,
+		u16 buflen, u8 *pbBuffer)
+{
+	u8 dwRequestType;
+	u16 wIndex;
+	int nWaitTime,pipe,len;
+
+	wIndex = page << 8;
+
+	switch (req) {
+		case B2C2_USB_READ_V8_MEM:
+			nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
+			dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
+			pipe = B2C2_USB_CTRL_PIPE_IN;
+		break;
+		case B2C2_USB_WRITE_V8_MEM:
+			wIndex |= pbBuffer[0];
+			nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
+			dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
+			pipe = B2C2_USB_CTRL_PIPE_OUT;
+		break;
+		case B2C2_USB_FLASH_BLOCK:
+			nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
+			dwRequestType = (u8) RTYPE_WRITE_V8_FLASH;
+			pipe = B2C2_USB_CTRL_PIPE_OUT;
+		break;
+		default:
+			deb_info("unsupported request for v8_mem_req %x.\n",req);
+		return -EINVAL;
+	}
+	len = usb_control_msg(b2c2->udev,pipe,
+			req,
+			dwRequestType,
+			wAddress,
+			wIndex,
+			pbBuffer,
+			buflen,
+			nWaitTime * 1000);
+	return len == buflen ? 0 : -EIO;
+}
+
+static int b2c2_usb_i2c_req(struct usb_b2c2_usb *b2c2,
+		b2c2_usb_request_t req, b2c2_usb_i2c_function_t func,
+		u8 port, u8 chipaddr, u8 addr, u8 buflen, u8 *buf)
+{
+	u16 wValue, wIndex;
+	int nWaitTime,pipe,len;
+	u8 dwRequestType;
+
+	switch (func) {
+		case USB_FUNC_I2C_WRITE:
+		case USB_FUNC_I2C_MULTIWRITE:
+		case USB_FUNC_I2C_REPEATWRITE:
+		/* DKT 020208 - add this to support special case of DiSEqC */
+		case USB_FUNC_I2C_CHECKWRITE:
+			pipe = B2C2_USB_CTRL_PIPE_OUT;
+			nWaitTime = 2;
+			dwRequestType = (u8) RTYPE_GENERIC;
+		break;
+		case USB_FUNC_I2C_READ:
+		case USB_FUNC_I2C_REPEATREAD:
+			pipe = B2C2_USB_CTRL_PIPE_IN;
+			nWaitTime = 2;
+			dwRequestType = (u8) RTYPE_GENERIC;
+		break;
+		default:
+			deb_info("unsupported function for i2c_req %x\n",func);
+			return -EINVAL;
+	}
+	wValue = (func << 8 ) | port;
+	wIndex = (chipaddr << 8 ) | addr;
+
+	len = usb_control_msg(b2c2->udev,pipe,
+			req,
+			dwRequestType,
+			addr,
+			wIndex,
+			buf,
+			buflen,
+			nWaitTime * 1000);
+	return len == buflen ? 0 : -EIO;
+}
+
+int static b2c2_usb_utility_req(struct usb_b2c2_usb *b2c2, int set,
+		b2c2_usb_utility_function_t func, u8 extra, u16 wIndex,
+		u16 buflen, u8 *pvBuffer)
+{
+	u16 wValue;
+	int nWaitTime = 2,
+		pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
+		len;
+
+	wValue = (func << 8) | extra;
+
+	len = usb_control_msg(b2c2->udev,pipe,
+			B2C2_USB_UTILITY,
+			(u8) RTYPE_GENERIC,
+			wValue,
+			wIndex,
+			pvBuffer,
+			buflen,
+			nWaitTime * 1000);
+	return len == buflen ? 0 : -EIO;
+}
+
+
+
+static void b2c2_dumpfourreg(struct usb_b2c2_usb *b2c2, u16 offs)
+{
+	u32 r0,r1,r2,r3;
+	r0 = r1 = r2 = r3 = 0;
+	r0 = b2c2_usb_read_dw(b2c2,offs);
+	r1 = b2c2_usb_read_dw(b2c2,offs + 0x04);
+	r2 = b2c2_usb_read_dw(b2c2,offs + 0x08);
+	r3 = b2c2_usb_read_dw(b2c2,offs + 0x0c);
+	deb_ctrl("dump: offset: %03x, %08x, %08x, %08x, %08x\n",offs,r0,r1,r2,r3);
+}
+
+static void b2c2_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+	struct usb_b2c2_usb *b2c2 = urb->context;
+	deb_ts("urb completed, bufsize: %d\n",urb->transfer_buffer_length);
+
+//	urb_submit_urb(urb,GFP_ATOMIC); enable for real action
+}
+
+static void b2c2_exit_usb(struct usb_b2c2_usb *b2c2)
+{
+	int i;
+	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+		if (b2c2->iso_urb[i] != NULL) { /* not sure about unlink_urb and iso-urbs TODO */
+			deb_info("unlinking/killing urb no. %d\n",i);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,7)
+			usb_unlink_urb(b2c2->iso_urb[i]);
+#else
+			usb_kill_urb(b2c2->iso_urb[i]);
+#endif
+			usb_free_urb(b2c2->iso_urb[i]);
+		}
+
+	if (b2c2->iso_buffer != NULL)
+		pci_free_consistent(NULL,b2c2->buffer_size, b2c2->iso_buffer, b2c2->iso_dma_handle);
+
+}
+
+static int b2c2_init_usb(struct usb_b2c2_usb *b2c2)
+{
+	u16 frame_size = le16_to_cpu(b2c2->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
+	int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret;
+	int buffer_offset = 0;
+
+	deb_info("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
+			B2C2_USB_NUM_ISO_URB, B2C2_USB_FRAMES_PER_ISO, frame_size,bufsize);
+
+	b2c2->iso_buffer = pci_alloc_consistent(NULL,bufsize,&b2c2->iso_dma_handle);
+	if (b2c2->iso_buffer == NULL)
+		return -ENOMEM;
+	memset(b2c2->iso_buffer, 0, bufsize);
+	b2c2->buffer_size = bufsize;
+
+	/* creating iso urbs */
+	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+		if (!(b2c2->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,GFP_ATOMIC))) {
+			ret = -ENOMEM;
+			goto urb_error;
+		}
+	/* initialising and submitting iso urbs */
+	for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
+		int frame_offset = 0;
+		struct urb *urb = b2c2->iso_urb[i];
+		deb_info("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
+
+		urb->dev = b2c2->udev;
+		urb->context = b2c2;
+		urb->complete = b2c2_urb_complete;
+		urb->pipe = B2C2_USB_DATA_PIPE;
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->interval = 1;
+		urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
+		urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
+		urb->transfer_buffer = b2c2->iso_buffer + buffer_offset;
+
+		buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
+		for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
+			deb_info("urb no: %d, frame: %d, frame_offset: %d\n",i,j,frame_offset);
+			urb->iso_frame_desc[j].offset = frame_offset;
+			urb->iso_frame_desc[j].length = frame_size;
+			frame_offset += frame_size;
+		}
+
+		if ((ret = usb_submit_urb(b2c2->iso_urb[i],GFP_ATOMIC))) {
+			err("submitting urb %d failed with %d.",i,ret);
+			goto urb_error;
+		}
+		deb_info("submitted urb no. %d.\n",i);
+	}
+
+	ret = 0;
+	goto success;
+urb_error:
+	b2c2_exit_usb(b2c2);
+success:
+	return ret;
+}
+
+static int b2c2_usb_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_b2c2_usb *b2c2 = NULL;
+	int ret;
+
+	b2c2 = kmalloc(sizeof(struct usb_b2c2_usb),GFP_KERNEL);
+	if (b2c2 == NULL) {
+		err("no memory");
+		return -ENOMEM;
+	}
+	b2c2->udev = udev;
+	b2c2->uintf = intf;
+
+	/* use the alternate setting with the larges buffer */
+	usb_set_interface(udev,0,1);
+
+	if ((ret = b2c2_init_usb(b2c2)))
+		goto usb_init_error;
+
+	usb_set_intfdata(intf,b2c2);
+
+	switch (udev->speed) {
+		case USB_SPEED_LOW:
+			err("cannot handle USB speed because it is to sLOW.");
+			break;
+		case USB_SPEED_FULL:
+			info("running at FULL speed.");
+			break;
+		case USB_SPEED_HIGH:
+			info("running at HIGH speed.");
+			break;
+		case USB_SPEED_UNKNOWN: /* fall through */
+		default:
+			err("cannot handle USB speed because it is unkown.");
+		break;
+	}
+
+	b2c2_dumpfourreg(b2c2,0x200);
+	b2c2_dumpfourreg(b2c2,0x300);
+	b2c2_dumpfourreg(b2c2,0x400);
+	b2c2_dumpfourreg(b2c2,0x700);
+
+
+	if (ret == 0)
+		info("%s successfully initialized and connected.",DRIVER_DESC);
+	else
+		info("%s error while loading driver (%d)",DRIVER_DESC,ret);
+
+	ret = 0;
+	goto success;
+
+usb_init_error:
+	kfree(b2c2);
+success:
+	return ret;
+}
+
+static void b2c2_usb_disconnect(struct usb_interface *intf)
+{
+	struct usb_b2c2_usb *b2c2 = usb_get_intfdata(intf);
+	usb_set_intfdata(intf,NULL);
+	if (b2c2 != NULL) {
+		b2c2_exit_usb(b2c2);
+		kfree(b2c2);
+	}
+	info("%s successfully deinitialized and disconnected.",DRIVER_DESC);
+
+}
+
+static struct usb_device_id b2c2_usb_table [] = {
+	    { USB_DEVICE(0x0af7, 0x0101) }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver b2c2_usb_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "dvb_b2c2_usb",
+	.probe 		= b2c2_usb_probe,
+	.disconnect = b2c2_usb_disconnect,
+	.id_table 	= b2c2_usb_table,
+};
+
+/* module stuff */
+static int __init b2c2_usb_init(void)
+{
+	int result;
+	if ((result = usb_register(&b2c2_usb_driver))) {
+		err("usb_register failed. Error number %d",result);
+		return result;
+	}
+
+	return 0;
+}
+
+static void __exit b2c2_usb_exit(void)
+{
+	/* deregister this driver from the USB subsystem */
+	usb_deregister(&b2c2_usb_driver);
+}
+
+module_init (b2c2_usb_init);
+module_exit (b2c2_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, b2c2_usb_table);
diff --git a/drivers/media/dvb/b2c2/skystar2.c b/drivers/media/dvb/b2c2/skystar2.c
new file mode 100644
index 0000000..336c178
--- /dev/null
+++ b/drivers/media/dvb/b2c2/skystar2.c
@@ -0,0 +1,2644 @@
+/*
+ * skystar2.c - driver for the Technisat SkyStar2 PCI DVB card
+ *              based on the FlexCopII by B2C2,Inc.
+ *
+ * Copyright (C) 2003  Vadim Catana, skystar@moldova.cc
+ *
+ * FIX: DISEQC Tone Burst in flexcop_diseqc_ioctl()
+ * FIX: FULL soft DiSEqC for skystar2 (FlexCopII rev 130) VP310 equipped
+ *     Vincenzo Di Massa, hawk.it at tiscalinet.it
+ *
+ * Converted to Linux coding style
+ * Misc reorganization, polishing, restyling
+ *     Roberto Ragusa, skystar2-c5b8 at robertoragusa dot it
+ *
+ * Added hardware filtering support,
+ *     Niklas Peinecke, peinecke at gdv.uni-hannover.de
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/version.h>
+
+#include <asm/io.h>
+
+#include "dvb_frontend.h"
+
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_net.h"
+#include "stv0299.h"
+#include "mt352.h"
+#include "mt312.h"
+#include "nxt2002.h"
+
+static int debug;
+static int enable_hw_filters = 2;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Set debugging level (0 = default, 1 = most messages, 2 = all messages).");
+module_param(enable_hw_filters, int, 0444);
+MODULE_PARM_DESC(enable_hw_filters, "enable hardware filters: supported values: 0 (none), 1, 2");
+
+#define dprintk(x...)	do { if (debug>=1) printk(x); } while (0)
+#define ddprintk(x...)	do { if (debug>=2) printk(x); } while (0)
+
+#define SIZE_OF_BUF_DMA1	0x3ac00
+#define SIZE_OF_BUF_DMA2	0x758
+
+#define MAX_N_HW_FILTERS	(6+32)
+#define N_PID_SLOTS		256
+
+struct dmaq {
+	u32 bus_addr;
+	u32 head;
+	u32 tail;
+	u32 buffer_size;
+	u8 *buffer;
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
+#define __iomem
+#endif
+
+struct adapter {
+	struct pci_dev *pdev;
+
+	u8 card_revision;
+	u32 b2c2_revision;
+	u32 pid_filter_max;
+	u32 mac_filter_max;
+	u32 irq;
+	void __iomem *io_mem;
+	unsigned long io_port;
+	u8 mac_addr[8];
+	u32 dw_sram_type;
+
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+	struct i2c_adapter i2c_adap;
+	struct dvb_net dvbnet;
+
+	struct semaphore i2c_sem;
+
+	struct dmaq dmaq1;
+	struct dmaq dmaq2;
+
+	u32 dma_ctrl;
+	u32 dma_status;
+
+	int capturing;
+
+	spinlock_t lock;
+
+	int useable_hw_filters;
+	u16 hw_pids[MAX_N_HW_FILTERS];
+	u16 pid_list[N_PID_SLOTS];
+	int pid_rc[N_PID_SLOTS];	// ref counters for the pids
+	int pid_count;
+	int whole_bandwidth_count;
+	u32 mac_filter;
+
+	struct dvb_frontend* fe;
+	int (*fe_sleep)(struct dvb_frontend* fe);
+};
+
+#define write_reg_dw(adapter,reg,value) writel(value, adapter->io_mem + reg)
+#define read_reg_dw(adapter,reg) readl(adapter->io_mem + reg)
+
+static void write_reg_bitfield(struct adapter *adapter, u32 reg, u32 zeromask, u32 orvalue)
+{
+	u32 tmp;
+
+	tmp = read_reg_dw(adapter, reg);
+	tmp = (tmp & ~zeromask) | orvalue;
+	write_reg_dw(adapter, reg, tmp);
+}
+
+/* i2c functions */
+static int i2c_main_write_for_flex2(struct adapter *adapter, u32 command, u8 *buf, int retries)
+{
+	int i;
+	u32 value;
+
+	write_reg_dw(adapter, 0x100, 0);
+	write_reg_dw(adapter, 0x100, command);
+
+	for (i = 0; i < retries; i++) {
+		value = read_reg_dw(adapter, 0x100);
+
+		if ((value & 0x40000000) == 0) {
+			if ((value & 0x81000000) == 0x80000000) {
+				if (buf != 0)
+					*buf = (value >> 0x10) & 0xff;
+
+				return 1;
+			}
+		} else {
+			write_reg_dw(adapter, 0x100, 0);
+			write_reg_dw(adapter, 0x100, command);
+		}
+	}
+
+	return 0;
+}
+
+/* device = 0x10000000 for tuner, 0x20000000 for eeprom */
+static void i2c_main_setup(u32 device, u32 chip_addr, u8 op, u8 addr, u32 value, u32 len, u32 *command)
+{
+	*command = device | ((len - 1) << 26) | (value << 16) | (addr << 8) | chip_addr;
+
+	if (op != 0)
+		*command = *command | 0x03000000;
+	else
+		*command = *command | 0x01000000;
+}
+
+static int flex_i2c_read4(struct adapter *adapter, u32 device, u32 chip_addr, u16 addr, u8 *buf, u8 len)
+{
+	u32 command;
+	u32 value;
+
+	int result, i;
+
+	i2c_main_setup(device, chip_addr, 1, addr, 0, len, &command);
+
+	result = i2c_main_write_for_flex2(adapter, command, buf, 100000);
+
+	if ((result & 0xff) != 0) {
+		if (len > 1) {
+			value = read_reg_dw(adapter, 0x104);
+
+			for (i = 1; i < len; i++) {
+				buf[i] = value & 0xff;
+				value = value >> 8;
+			}
+		}
+	}
+
+	return result;
+}
+
+static int flex_i2c_write4(struct adapter *adapter, u32 device, u32 chip_addr, u32 addr, u8 *buf, u8 len)
+{
+	u32 command;
+	u32 value;
+	int i;
+
+	if (len > 1) {
+		value = 0;
+
+		for (i = len; i > 1; i--) {
+			value = value << 8;
+			value = value | buf[i - 1];
+		}
+
+		write_reg_dw(adapter, 0x104, value);
+	}
+
+	i2c_main_setup(device, chip_addr, 0, addr, buf[0], len, &command);
+
+	return i2c_main_write_for_flex2(adapter, command, NULL, 100000);
+}
+
+static void fixchipaddr(u32 device, u32 bus, u32 addr, u32 *ret)
+{
+	if (device == 0x20000000)
+		*ret = bus | ((addr >> 8) & 3);
+	else
+		*ret = bus;
+}
+
+static u32 flex_i2c_read(struct adapter *adapter, u32 device, u32 bus, u32 addr, u8 *buf, u32 len)
+{
+	u32 chipaddr;
+	u32 bytes_to_transfer;
+	u8 *start;
+
+	ddprintk("%s:\n", __FUNCTION__);
+
+	start = buf;
+
+	while (len != 0) {
+		bytes_to_transfer = len;
+
+		if (bytes_to_transfer > 4)
+			bytes_to_transfer = 4;
+
+		fixchipaddr(device, bus, addr, &chipaddr);
+
+		if (flex_i2c_read4(adapter, device, chipaddr, addr, buf, bytes_to_transfer) == 0)
+			return buf - start;
+
+		buf = buf + bytes_to_transfer;
+		addr = addr + bytes_to_transfer;
+		len = len - bytes_to_transfer;
+	};
+
+	return buf - start;
+}
+
+static u32 flex_i2c_write(struct adapter *adapter, u32 device, u32 bus, u32 addr, u8 *buf, u32 len)
+{
+	u32 chipaddr;
+	u32 bytes_to_transfer;
+	u8 *start;
+
+	ddprintk("%s:\n", __FUNCTION__);
+
+	start = buf;
+
+	while (len != 0) {
+		bytes_to_transfer = len;
+
+		if (bytes_to_transfer > 4)
+			bytes_to_transfer = 4;
+
+		fixchipaddr(device, bus, addr, &chipaddr);
+
+		if (flex_i2c_write4(adapter, device, chipaddr, addr, buf, bytes_to_transfer) == 0)
+			return buf - start;
+
+		buf = buf + bytes_to_transfer;
+		addr = addr + bytes_to_transfer;
+		len = len - bytes_to_transfer;
+	}
+
+	return buf - start;
+}
+
+static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg *msgs, int num)
+{
+	struct adapter *tmp = i2c_get_adapdata(adapter);
+	int i, ret = 0;
+
+	if (down_interruptible(&tmp->i2c_sem))
+		return -ERESTARTSYS;
+
+	ddprintk("%s: %d messages to transfer\n", __FUNCTION__, num);
+
+		for (i = 0; i < num; i++) {
+		ddprintk("message %d: flags=0x%x, addr=0x%x, buf=0x%x, len=%d \n", i,
+			 msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len);
+	}
+
+	// read command
+	if ((num == 2) && (msgs[0].flags == 0) && (msgs[1].flags == I2C_M_RD) && (msgs[0].buf != NULL) && (msgs[1].buf != NULL)) {
+
+		ret = flex_i2c_read(tmp, 0x10000000, msgs[0].addr, msgs[0].buf[0], msgs[1].buf, msgs[1].len);
+
+		up(&tmp->i2c_sem);
+
+		if (ret != msgs[1].len) {
+			dprintk("%s: read error !\n", __FUNCTION__);
+
+			for (i = 0; i < 2; i++) {
+				dprintk("message %d: flags=0x%x, addr=0x%x, buf=0x%x, len=%d \n", i,
+				       msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len);
+		}
+
+			return -EREMOTEIO;
+		}
+
+		return num;
+	}
+	// write command
+	for (i = 0; i < num; i++) {
+
+		if ((msgs[i].flags != 0) || (msgs[i].buf == NULL) || (msgs[i].len < 2))
+			return -EINVAL;
+
+		ret = flex_i2c_write(tmp, 0x10000000, msgs[i].addr, msgs[i].buf[0], &msgs[i].buf[1], msgs[i].len - 1);
+
+		up(&tmp->i2c_sem);
+
+		if (ret != msgs[0].len - 1) {
+			dprintk("%s: write error %i !\n", __FUNCTION__, ret);
+
+			dprintk("message %d: flags=0x%x, addr=0x%x, buf[0]=0x%x, len=%d \n", i,
+			       msgs[i].flags, msgs[i].addr, msgs[i].buf[0], msgs[i].len);
+
+			return -EREMOTEIO;
+		}
+
+		return num;
+	}
+
+	printk("%s: unknown command format !\n", __FUNCTION__);
+
+	return -EINVAL;
+}
+
+/* SRAM (Skystar2 rev2.3 has one "ISSI IS61LV256" chip on board,
+   but it seems that FlexCopII can work with more than one chip) */
+static void sram_set_net_dest(struct adapter *adapter, u8 dest)
+{
+	u32 tmp;
+
+	udelay(1000);
+
+	tmp = (read_reg_dw(adapter, 0x714) & 0xfffffffc) | (dest & 3);
+
+	udelay(1000);
+
+	write_reg_dw(adapter, 0x714, tmp);
+	write_reg_dw(adapter, 0x714, tmp);
+
+	udelay(1000);
+
+	/* return value is never used? */
+/*	return tmp; */
+}
+
+static void sram_set_cai_dest(struct adapter *adapter, u8 dest)
+{
+	u32 tmp;
+
+	udelay(1000);
+
+	tmp = (read_reg_dw(adapter, 0x714) & 0xfffffff3) | ((dest & 3) << 2);
+
+	udelay(1000);
+	udelay(1000);
+
+	write_reg_dw(adapter, 0x714, tmp);
+	write_reg_dw(adapter, 0x714, tmp);
+
+	udelay(1000);
+
+	/* return value is never used? */
+/*	return tmp; */
+}
+
+static void sram_set_cao_dest(struct adapter *adapter, u8 dest)
+{
+	u32 tmp;
+
+	udelay(1000);
+
+	tmp = (read_reg_dw(adapter, 0x714) & 0xffffffcf) | ((dest & 3) << 4);
+
+	udelay(1000);
+	udelay(1000);
+
+	write_reg_dw(adapter, 0x714, tmp);
+	write_reg_dw(adapter, 0x714, tmp);
+
+	udelay(1000);
+
+	/* return value is never used? */
+/*	return tmp; */
+}
+
+static void sram_set_media_dest(struct adapter *adapter, u8 dest)
+{
+	u32 tmp;
+
+	udelay(1000);
+
+	tmp = (read_reg_dw(adapter, 0x714) & 0xffffff3f) | ((dest & 3) << 6);
+
+	udelay(1000);
+	udelay(1000);
+
+	write_reg_dw(adapter, 0x714, tmp);
+	write_reg_dw(adapter, 0x714, tmp);
+
+	udelay(1000);
+
+	/* return value is never used? */
+/*	return tmp; */
+}
+
+/* SRAM memory is accessed through a buffer register in the FlexCop
+   chip (0x700). This register has the following structure:
+    bits 0-14  : address
+    bit  15    : read/write flag
+    bits 16-23 : 8-bit word to write
+    bits 24-27 : = 4
+    bits 28-29 : memory bank selector
+    bit  31    : busy flag
+*/
+static void flex_sram_write(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
+{
+	int i, retries;
+	u32 command;
+
+	for (i = 0; i < len; i++) {
+		command = bank | addr | 0x04000000 | (*buf << 0x10);
+
+		retries = 2;
+
+		while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+			mdelay(1);
+			retries--;
+		};
+
+		if (retries == 0)
+			printk("%s: SRAM timeout\n", __FUNCTION__);
+
+		write_reg_dw(adapter, 0x700, command);
+
+		buf++;
+		addr++;
+	}
+}
+
+static void flex_sram_read(struct adapter *adapter, u32 bank, u32 addr, u8 *buf, u32 len)
+{
+	int i, retries;
+	u32 command, value;
+
+	for (i = 0; i < len; i++) {
+		command = bank | addr | 0x04008000;
+
+		retries = 10000;
+
+		while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+			mdelay(1);
+			retries--;
+		};
+
+		if (retries == 0)
+			printk("%s: SRAM timeout\n", __FUNCTION__);
+
+		write_reg_dw(adapter, 0x700, command);
+
+		retries = 10000;
+
+		while (((read_reg_dw(adapter, 0x700) & 0x80000000) != 0) && (retries > 0)) {
+			mdelay(1);
+			retries--;
+		};
+
+		if (retries == 0)
+			printk("%s: SRAM timeout\n", __FUNCTION__);
+
+		value = read_reg_dw(adapter, 0x700) >> 0x10;
+
+		*buf = (value & 0xff);
+
+		addr++;
+		buf++;
+	}
+}
+
+static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+{
+	u32 bank;
+
+	bank = 0;
+
+	if (adapter->dw_sram_type == 0x20000) {
+		bank = (addr & 0x18000) << 0x0d;
+	}
+
+	if (adapter->dw_sram_type == 0x00000) {
+		if ((addr >> 0x0f) == 0)
+			bank = 0x20000000;
+		else
+			bank = 0x10000000;
+	}
+
+	flex_sram_write(adapter, bank, addr & 0x7fff, buf, len);
+}
+
+static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+{
+	u32 bank;
+
+	bank = 0;
+
+	if (adapter->dw_sram_type == 0x20000) {
+		bank = (addr & 0x18000) << 0x0d;
+	}
+
+	if (adapter->dw_sram_type == 0x00000) {
+		if ((addr >> 0x0f) == 0)
+			bank = 0x20000000;
+		else
+			bank = 0x10000000;
+	}
+
+	flex_sram_read(adapter, bank, addr & 0x7fff, buf, len);
+}
+
+static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+{
+	u32 length;
+
+	while (len != 0) {
+		length = len;
+
+		// check if the address range belongs to the same 
+		// 32K memory chip. If not, the data is read from 
+		// one chip at a time.
+		if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+			length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+		}
+
+		sram_read_chunk(adapter, addr, buf, length);
+
+		addr = addr + length;
+		buf = buf + length;
+		len = len - length;
+	}
+}
+
+static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+{
+	u32 length;
+
+	while (len != 0) {
+		length = len;
+
+		// check if the address range belongs to the same 
+		// 32K memory chip. If not, the data is written to
+		// one chip at a time.
+		if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+			length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+		}
+
+		sram_write_chunk(adapter, addr, buf, length);
+
+		addr = addr + length;
+		buf = buf + length;
+		len = len - length;
+	}
+}
+
+static void sram_set_size(struct adapter *adapter, u32 mask)
+{
+	write_reg_dw(adapter, 0x71c, (mask | (~0x30000 & read_reg_dw(adapter, 0x71c))));
+}
+
+static void sram_init(struct adapter *adapter)
+{
+	u32 tmp;
+
+	tmp = read_reg_dw(adapter, 0x71c);
+
+	write_reg_dw(adapter, 0x71c, 1);
+
+	if (read_reg_dw(adapter, 0x71c) != 0) {
+		write_reg_dw(adapter, 0x71c, tmp);
+
+		adapter->dw_sram_type = tmp & 0x30000;
+
+		ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
+
+	} else {
+
+		adapter->dw_sram_type = 0x10000;
+
+		ddprintk("%s: dw_sram_type = %x\n", __FUNCTION__, adapter->dw_sram_type);
+	}
+
+	/* return value is never used? */
+/*	return adapter->dw_sram_type; */
+}
+
+static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
+{
+	u8 tmp1, tmp2;
+
+	dprintk("%s: mask = %x, addr = %x\n", __FUNCTION__, mask, addr);
+
+	sram_set_size(adapter, mask);
+	sram_init(adapter);
+
+	tmp2 = 0xa5;
+	tmp1 = 0x4f;
+
+	sram_write(adapter, addr, &tmp2, 1);
+	sram_write(adapter, addr + 4, &tmp1, 1);
+
+	tmp2 = 0;
+
+	mdelay(20);
+
+	sram_read(adapter, addr, &tmp2, 1);
+	sram_read(adapter, addr, &tmp2, 1);
+
+	dprintk("%s: wrote 0xa5, read 0x%2x\n", __FUNCTION__, tmp2);
+
+	if (tmp2 != 0xa5)
+		return 0;
+
+	tmp2 = 0x5a;
+	tmp1 = 0xf4;
+
+	sram_write(adapter, addr, &tmp2, 1);
+	sram_write(adapter, addr + 4, &tmp1, 1);
+
+	tmp2 = 0;
+
+	mdelay(20);
+
+	sram_read(adapter, addr, &tmp2, 1);
+	sram_read(adapter, addr, &tmp2, 1);
+
+	dprintk("%s: wrote 0x5a, read 0x%2x\n", __FUNCTION__, tmp2);
+
+	if (tmp2 != 0x5a)
+		return 0;
+
+	return 1;
+}
+
+static u32 sram_length(struct adapter *adapter)
+{
+	if (adapter->dw_sram_type == 0x10000)
+		return 32768;	//  32K
+	if (adapter->dw_sram_type == 0x00000)
+		return 65536;	//  64K        
+	if (adapter->dw_sram_type == 0x20000)
+		return 131072;	// 128K
+
+	return 32768;		// 32K
+}
+
+/* FlexcopII can work with 32K, 64K or 128K of external SRAM memory.
+    - for 128K there are 4x32K chips at bank 0,1,2,3.
+    - for  64K there are 2x32K chips at bank 1,2.
+    - for  32K there is one 32K chip at bank 0.
+
+   FlexCop works only with one bank at a time. The bank is selected
+   by bits 28-29 of the 0x700 register.
+  
+   bank 0 covers addresses 0x00000-0x07fff
+   bank 1 covers addresses 0x08000-0x0ffff
+   bank 2 covers addresses 0x10000-0x17fff
+   bank 3 covers addresses 0x18000-0x1ffff
+*/
+static int sram_detect_for_flex2(struct adapter *adapter)
+{
+	u32 tmp, tmp2, tmp3;
+
+	dprintk("%s:\n", __FUNCTION__);
+
+	tmp = read_reg_dw(adapter, 0x208);
+	write_reg_dw(adapter, 0x208, 0);
+
+	tmp2 = read_reg_dw(adapter, 0x71c);
+
+	dprintk("%s: tmp2 = %x\n", __FUNCTION__, tmp2);
+
+	write_reg_dw(adapter, 0x71c, 1);
+
+	tmp3 = read_reg_dw(adapter, 0x71c);
+
+	dprintk("%s: tmp3 = %x\n", __FUNCTION__, tmp3);
+
+	write_reg_dw(adapter, 0x71c, tmp2);
+
+	// check for internal SRAM ???
+	tmp3--;
+	if (tmp3 != 0) {
+		sram_set_size(adapter, 0x10000);
+		sram_init(adapter);
+		write_reg_dw(adapter, 0x208, tmp);
+
+		dprintk("%s: sram size = 32K\n", __FUNCTION__);
+
+		return 32;
+	}
+
+	if (sram_test_location(adapter, 0x20000, 0x18000) != 0) {
+		sram_set_size(adapter, 0x20000);
+		sram_init(adapter);
+		write_reg_dw(adapter, 0x208, tmp);
+
+		dprintk("%s: sram size = 128K\n", __FUNCTION__);
+
+		return 128;
+	}
+
+	if (sram_test_location(adapter, 0x00000, 0x10000) != 0) {
+		sram_set_size(adapter, 0x00000);
+		sram_init(adapter);
+		write_reg_dw(adapter, 0x208, tmp);
+
+		dprintk("%s: sram size = 64K\n", __FUNCTION__);
+
+		return 64;
+	}
+
+	if (sram_test_location(adapter, 0x10000, 0x00000) != 0) {
+		sram_set_size(adapter, 0x10000);
+		sram_init(adapter);
+		write_reg_dw(adapter, 0x208, tmp);
+
+		dprintk("%s: sram size = 32K\n", __FUNCTION__);
+
+		return 32;
+	}
+
+	sram_set_size(adapter, 0x10000);
+	sram_init(adapter);
+	write_reg_dw(adapter, 0x208, tmp);
+
+	dprintk("%s: SRAM detection failed. Set to 32K \n", __FUNCTION__);
+
+	return 0;
+}
+
+static void sll_detect_sram_size(struct adapter *adapter)
+{
+	sram_detect_for_flex2(adapter);
+}
+
+/* EEPROM (Skystar2 has one "24LC08B" chip on board) */
+/*
+static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
+{
+	return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len);
+}
+*/
+
+static int eeprom_read(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
+{
+	return flex_i2c_read(adapter, 0x20000000, 0x50, addr, buf, len);
+}
+
+static u8 calc_lrc(u8 *buf, int len)
+{
+	int i;
+	u8 sum;
+
+	sum = 0;
+
+	for (i = 0; i < len; i++)
+		sum = sum ^ buf[i];
+
+	return sum;
+}
+
+static int eeprom_lrc_read(struct adapter *adapter, u32 addr, u32 len, u8 *buf, int retries)
+{
+	int i;
+
+	for (i = 0; i < retries; i++) {
+		if (eeprom_read(adapter, addr, buf, len) == len) {
+			if (calc_lrc(buf, len - 1) == buf[len - 1])
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+static int eeprom_lrc_write(struct adapter *adapter, u32 addr, u32 len, u8 *wbuf, u8 *rbuf, int retries)
+{
+	int i;
+
+	for (i = 0; i < retries; i++) {
+		if (eeprom_write(adapter, addr, wbuf, len) == len) {
+			if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1)
+				return 1;
+		}
+	}
+
+	return 0;
+}
+*/
+
+
+/* These functions could be used to unlock SkyStar2 cards. */
+
+/*
+static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len)
+{
+	u8 rbuf[20];
+	u8 wbuf[20];
+
+	if (len != 16)
+		return 0;
+
+	memcpy(wbuf, key, len);
+
+	wbuf[16] = 0;
+	wbuf[17] = 0;
+	wbuf[18] = 0;
+	wbuf[19] = calc_lrc(wbuf, 19);
+
+	return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4);
+}
+
+static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len)
+{
+	u8 buf[20];
+
+	if (len != 16)
+		return 0;
+
+	if (eeprom_lrc_read(adapter, 0x3e4, 20, buf, 4) == 0)
+		return 0;
+
+	memcpy(key, buf, len);
+
+	return 1;
+}
+*/
+
+static int eeprom_get_mac_addr(struct adapter *adapter, char type, u8 *mac)
+{
+	u8 tmp[8];
+
+	if (eeprom_lrc_read(adapter, 0x3f8, 8, tmp, 4) != 0) {
+		if (type != 0) {
+			mac[0] = tmp[0];
+			mac[1] = tmp[1];
+			mac[2] = tmp[2];
+			mac[3] = 0xfe;
+			mac[4] = 0xff;
+			mac[5] = tmp[3];
+			mac[6] = tmp[4];
+			mac[7] = tmp[5];
+
+		} else {
+
+			mac[0] = tmp[0];
+			mac[1] = tmp[1];
+			mac[2] = tmp[2];
+			mac[3] = tmp[3];
+			mac[4] = tmp[4];
+			mac[5] = tmp[5];
+		}
+
+		return 1;
+
+	} else {
+
+		if (type == 0) {
+			memset(mac, 0, 6);
+
+		} else {
+
+			memset(mac, 0, 8);
+		}
+
+		return 0;
+	}
+}
+
+/*
+static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac)
+{
+	u8 tmp[8];
+
+	if (type != 0) {
+		tmp[0] = mac[0];
+		tmp[1] = mac[1];
+		tmp[2] = mac[2];
+		tmp[3] = mac[5];
+		tmp[4] = mac[6];
+		tmp[5] = mac[7];
+
+	} else {
+
+		tmp[0] = mac[0];
+		tmp[1] = mac[1];
+		tmp[2] = mac[2];
+		tmp[3] = mac[3];
+		tmp[4] = mac[4];
+		tmp[5] = mac[5];
+	}
+
+	tmp[6] = 0;
+	tmp[7] = calc_lrc(tmp, 7);
+
+	if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8)
+		return 1;
+
+	return 0;
+}
+*/
+
+/* PID filter */
+
+/* every flexcop has 6 "lower" hw PID filters     */
+/* these are enabled by setting bits 0-5 of 0x208 */
+/* for the 32 additional filters we have to select one */
+/* of them through 0x310 and modify through 0x314 */
+/* op: 0=disable, 1=enable */
+static void filter_enable_hw_filter(struct adapter *adapter, int id, u8 op)
+{
+	dprintk("%s: id=%d op=%d\n", __FUNCTION__, id, op);
+	if (id <= 5) {
+		u32 mask = (0x00000001 << id);
+		write_reg_bitfield(adapter, 0x208, mask, op ? mask : 0);
+	} else {
+		/* select */
+		write_reg_bitfield(adapter, 0x310, 0x1f, (id - 6) & 0x1f);
+		/* modify */
+		write_reg_bitfield(adapter, 0x314, 0x00006000, op ? 0x00004000 : 0);
+	}
+}
+
+/* this sets the PID that should pass the specified filter */
+static void pid_set_hw_pid(struct adapter *adapter, int id, u16 pid)
+{
+	dprintk("%s: id=%d  pid=%d\n", __FUNCTION__, id, pid);
+	if (id <= 5) {
+		u32 adr = 0x300 + ((id & 6) << 1);
+		int shift = (id & 1) ? 16 : 0;
+		dprintk("%s: id=%d  addr=%x %c  pid=%d\n", __FUNCTION__, id, adr, (id & 1) ? 'h' : 'l', pid);
+		write_reg_bitfield(adapter, adr, (0x7fff) << shift, (pid & 0x1fff) << shift);
+	} else {
+		/* select */
+		write_reg_bitfield(adapter, 0x310, 0x1f, (id - 6) & 0x1f);
+		/* modify */
+		write_reg_bitfield(adapter, 0x314, 0x1fff, pid & 0x1fff);
+	}
+}
+
+
+/*
+static void filter_enable_null_filter(struct adapter *adapter, u32 op)
+{
+	dprintk("%s: op=%x\n", __FUNCTION__, op);
+
+	write_reg_bitfield(adapter, 0x208, 0x00000040, op?0x00000040:0);
+}
+*/
+
+static void filter_enable_mask_filter(struct adapter *adapter, u32 op)
+{
+	dprintk("%s: op=%x\n", __FUNCTION__, op);
+
+	write_reg_bitfield(adapter, 0x208, 0x00000080, op ? 0x00000080 : 0);
+}
+
+
+static void ctrl_enable_mac(struct adapter *adapter, u32 op)
+{
+	write_reg_bitfield(adapter, 0x208, 0x00004000, op ? 0x00004000 : 0);
+}
+
+static int ca_set_mac_dst_addr_filter(struct adapter *adapter, u8 *mac)
+{
+	u32 tmp1, tmp2;
+
+	tmp1 = (mac[3] << 0x18) | (mac[2] << 0x10) | (mac[1] << 0x08) | mac[0];
+	tmp2 = (mac[5] << 0x08) | mac[4];
+
+	write_reg_dw(adapter, 0x418, tmp1);
+	write_reg_dw(adapter, 0x41c, tmp2);
+
+	return 0;
+}
+
+/*
+static void set_ignore_mac_filter(struct adapter *adapter, u8 op)
+{
+	if (op != 0) {
+		write_reg_bitfield(adapter, 0x208, 0x00004000, 0);
+		adapter->mac_filter = 1;
+	} else {
+		if (adapter->mac_filter != 0) {
+			adapter->mac_filter = 0;
+			write_reg_bitfield(adapter, 0x208, 0x00004000, 0x00004000);
+		}
+	}
+}
+*/
+
+/*
+static void check_null_filter_enable(struct adapter *adapter)
+{
+	filter_enable_null_filter(adapter, 1);
+	filter_enable_mask_filter(adapter, 1);
+}
+*/
+
+static void pid_set_group_pid(struct adapter *adapter, u16 pid)
+{
+	u32 value;
+
+	dprintk("%s: pid=%x\n", __FUNCTION__, pid);
+	value = (pid & 0x3fff) | (read_reg_dw(adapter, 0x30c) & 0xffff0000);
+	write_reg_dw(adapter, 0x30c, value);
+}
+
+static void pid_set_group_mask(struct adapter *adapter, u16 pid)
+{
+	u32 value;
+
+	dprintk("%s: pid=%x\n", __FUNCTION__, pid);
+	value = ((pid & 0x3fff) << 0x10) | (read_reg_dw(adapter, 0x30c) & 0xffff);
+	write_reg_dw(adapter, 0x30c, value);
+}
+
+/*
+static int pid_get_group_pid(struct adapter *adapter)
+{
+	return read_reg_dw(adapter, 0x30c) & 0x00001fff;
+}
+
+static int pid_get_group_mask(struct adapter *adapter)
+{
+	return (read_reg_dw(adapter, 0x30c) >> 0x10)& 0x00001fff;
+}
+*/
+
+/*
+static void reset_hardware_pid_filter(struct adapter *adapter)
+{
+	pid_set_stream1_pid(adapter, 0x1fff);
+
+	pid_set_stream2_pid(adapter, 0x1fff);
+	filter_enable_stream2_filter(adapter, 0);
+
+	pid_set_pcr_pid(adapter, 0x1fff);
+	filter_enable_pcr_filter(adapter, 0);
+
+	pid_set_pmt_pid(adapter, 0x1fff);
+	filter_enable_pmt_filter(adapter, 0);
+
+	pid_set_ecm_pid(adapter, 0x1fff);
+	filter_enable_ecm_filter(adapter, 0);
+
+	pid_set_emm_pid(adapter, 0x1fff);
+	filter_enable_emm_filter(adapter, 0);
+}
+*/
+
+static void init_pids(struct adapter *adapter)
+{
+	int i;
+
+	adapter->pid_count = 0;
+	adapter->whole_bandwidth_count = 0;
+	for (i = 0; i < adapter->useable_hw_filters; i++) {
+		dprintk("%s: setting filter %d to 0x1fff\n", __FUNCTION__, i);
+		adapter->hw_pids[i] = 0x1fff;
+		pid_set_hw_pid(adapter, i, 0x1fff);
+}
+
+	pid_set_group_pid(adapter, 0);
+	pid_set_group_mask(adapter, 0x1fe0);
+}
+
+static void open_whole_bandwidth(struct adapter *adapter)
+{
+	dprintk("%s:\n", __FUNCTION__);
+	pid_set_group_pid(adapter, 0);
+	pid_set_group_mask(adapter, 0);
+/*
+	filter_enable_mask_filter(adapter, 1);
+*/
+}
+
+static void close_whole_bandwidth(struct adapter *adapter)
+{
+	dprintk("%s:\n", __FUNCTION__);
+	pid_set_group_pid(adapter, 0);
+	pid_set_group_mask(adapter, 0x1fe0);
+/*
+	filter_enable_mask_filter(adapter, 1);
+*/
+}
+
+static void whole_bandwidth_inc(struct adapter *adapter)
+{
+	if (adapter->whole_bandwidth_count++ == 0)
+		open_whole_bandwidth(adapter);
+}
+
+static void whole_bandwidth_dec(struct adapter *adapter)
+{
+	if (--adapter->whole_bandwidth_count <= 0)
+		close_whole_bandwidth(adapter);
+}
+
+/* The specified PID has to be let through the
+   hw filters.
+   We try to allocate an hardware filter and open whole
+   bandwidth when allocation is impossible.
+   All pids<=0x1f pass through the group filter.
+   Returns 1 on success, -1 on error */
+static int add_hw_pid(struct adapter *adapter, u16 pid)
+{
+	int i;
+
+	dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+	if (pid <= 0x1f)
+		return 1;
+
+	/* we can't use a filter for 0x2000, so no search */
+	if (pid != 0x2000) {
+		/* find an unused hardware filter */
+		for (i = 0; i < adapter->useable_hw_filters; i++) {
+			dprintk("%s: pid=%d searching slot=%d\n", __FUNCTION__, pid, i);
+			if (adapter->hw_pids[i] == 0x1fff) {
+				dprintk("%s: pid=%d slot=%d\n", __FUNCTION__, pid, i);
+				adapter->hw_pids[i] = pid;
+				pid_set_hw_pid(adapter, i, pid);
+				filter_enable_hw_filter(adapter, i, 1);
+		return 1;
+	}
+	}
+	}
+	/* if we have not used a filter, this pid depends on whole bandwidth */
+	dprintk("%s: pid=%d whole_bandwidth\n", __FUNCTION__, pid);
+	whole_bandwidth_inc(adapter);
+		return 1;
+	}
+
+/* returns -1 if the pid was not present in the filters */
+static int remove_hw_pid(struct adapter *adapter, u16 pid)
+{
+	int i;
+
+	dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+	if (pid <= 0x1f)
+		return 1;
+
+	/* we can't use a filter for 0x2000, so no search */
+	if (pid != 0x2000) {
+		for (i = 0; i < adapter->useable_hw_filters; i++) {
+			dprintk("%s: pid=%d searching slot=%d\n", __FUNCTION__, pid, i);
+			if (adapter->hw_pids[i] == pid) {	// find the pid slot
+				dprintk("%s: pid=%d slot=%d\n", __FUNCTION__, pid, i);
+				adapter->hw_pids[i] = 0x1fff;
+				pid_set_hw_pid(adapter, i, 0x1fff);
+				filter_enable_hw_filter(adapter, i, 0);
+		return 1;
+	}
+	}
+	}
+	/* if we have not used a filter, this pid depended on whole bandwith */
+	dprintk("%s: pid=%d whole_bandwidth\n", __FUNCTION__, pid);
+	whole_bandwidth_dec(adapter);
+		return 1;
+	}
+
+/* Adds a PID to the filters.
+   Adding a pid more than once is possible, we keep reference counts.
+   Whole stream available through pid==0x2000.
+   Returns 1 on success, -1 on error */
+static int add_pid(struct adapter *adapter, u16 pid)
+{
+	int i;
+
+	dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+	if (pid > 0x1ffe && pid != 0x2000)
+		return -1;
+
+	// check if the pid is already present
+	for (i = 0; i < adapter->pid_count; i++)
+		if (adapter->pid_list[i] == pid) {
+			adapter->pid_rc[i]++;	// increment ref counter
+		return 1;
+		}
+
+	if (adapter->pid_count == N_PID_SLOTS)
+		return -1;	// no more pids can be added
+	adapter->pid_list[adapter->pid_count] = pid;	// register pid
+	adapter->pid_rc[adapter->pid_count] = 1;
+	adapter->pid_count++;
+	// hardware setting
+	add_hw_pid(adapter, pid);
+
+			return 1;
+		}
+
+/* Removes a PID from the filters. */
+static int remove_pid(struct adapter *adapter, u16 pid)
+{
+	int i;
+
+	dprintk("%s: pid=%d\n", __FUNCTION__, pid);
+
+	if (pid > 0x1ffe && pid != 0x2000)
+		return -1;
+
+	// check if the pid is present (it must be!)
+	for (i = 0; i < adapter->pid_count; i++) {
+		if (adapter->pid_list[i] == pid) {
+			adapter->pid_rc[i]--;
+			if (adapter->pid_rc[i] <= 0) {
+				// remove from the list
+				adapter->pid_count--;
+				adapter->pid_list[i]=adapter->pid_list[adapter->pid_count];
+				adapter->pid_rc[i] = adapter->pid_rc[adapter->pid_count];
+				// hardware setting
+				remove_hw_pid(adapter, pid);
+			}
+			return 1;
+		}
+	}
+
+	return -1;
+}
+
+
+/* dma & irq */
+static void ctrl_enable_smc(struct adapter *adapter, u32 op)
+{
+	write_reg_bitfield(adapter, 0x208, 0x00000800, op ? 0x00000800 : 0);
+}
+
+static void dma_enable_disable_irq(struct adapter *adapter, u32 flag1, u32 flag2, u32 flag3)
+{
+	adapter->dma_ctrl = adapter->dma_ctrl & 0x000f0000;
+
+	if (flag1 == 0) {
+		if (flag2 == 0)
+			adapter->dma_ctrl = adapter->dma_ctrl & ~0x00010000;
+		else
+			adapter->dma_ctrl = adapter->dma_ctrl | 0x00010000;
+
+		if (flag3 == 0)
+			adapter->dma_ctrl = adapter->dma_ctrl & ~0x00020000;
+		else
+			adapter->dma_ctrl = adapter->dma_ctrl | 0x00020000;
+
+	} else {
+
+		if (flag2 == 0)
+			adapter->dma_ctrl = adapter->dma_ctrl & ~0x00040000;
+		else
+			adapter->dma_ctrl = adapter->dma_ctrl | 0x00040000;
+
+		if (flag3 == 0)
+			adapter->dma_ctrl = adapter->dma_ctrl & ~0x00080000;
+		else
+			adapter->dma_ctrl = adapter->dma_ctrl | 0x00080000;
+	}
+}
+
+static void irq_dma_enable_disable_irq(struct adapter *adapter, u32 op)
+{
+	u32 value;
+
+	value = read_reg_dw(adapter, 0x208) & 0xfff0ffff;
+
+	if (op != 0)
+		value = value | (adapter->dma_ctrl & 0x000f0000);
+
+	write_reg_dw(adapter, 0x208, value);
+}
+
+/* FlexCopII has 2 dma channels. DMA1 is used to transfer TS data to
+   system memory.
+
+   The DMA1 buffer is divided in 2 subbuffers of equal size.
+   FlexCopII will transfer TS data to one subbuffer, signal an interrupt
+   when the subbuffer is full and continue fillig the second subbuffer.
+
+   For DMA1:
+       subbuffer size in 32-bit words is stored in the first 24 bits of
+       register 0x004. The last 8 bits of register 0x004 contain the number
+       of subbuffers.
+       
+       the first 30 bits of register 0x000 contain the address of the first
+       subbuffer. The last 2 bits contain 0, when dma1 is disabled and 1,
+       when dma1 is enabled.
+
+       the first 30 bits of register 0x00c contain the address of the second
+       subbuffer. the last 2 bits contain 1.
+
+       register 0x008 will contain the address of the subbuffer that was filled
+       with TS data, when FlexCopII will generate an interrupt.
+
+   For DMA2:
+       subbuffer size in 32-bit words is stored in the first 24 bits of
+       register 0x014. The last 8 bits of register 0x014 contain the number
+       of subbuffers.
+       
+       the first 30 bits of register 0x010 contain the address of the first
+       subbuffer.  The last 2 bits contain 0, when dma1 is disabled and 1,
+       when dma1 is enabled.
+
+       the first 30 bits of register 0x01c contain the address of the second
+       subbuffer. the last 2 bits contain 1.
+
+       register 0x018 contains the address of the subbuffer that was filled
+       with TS data, when FlexCopII generates an interrupt.
+*/
+static int dma_init_dma(struct adapter *adapter, u32 dma_channel)
+{
+	u32 subbuffers, subbufsize, subbuf0, subbuf1;
+
+	if (dma_channel == 0) {
+		dprintk("%s: Initializing DMA1 channel\n", __FUNCTION__);
+
+		subbuffers = 2;
+
+		subbufsize = (((adapter->dmaq1.buffer_size / 2) / 4) << 8) | subbuffers;
+
+		subbuf0 = adapter->dmaq1.bus_addr & 0xfffffffc;
+
+		subbuf1 = ((adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) & 0xfffffffc) | 1;
+
+		dprintk("%s: first subbuffer address = 0x%x\n", __FUNCTION__, subbuf0);
+		udelay(1000);
+		write_reg_dw(adapter, 0x000, subbuf0);
+
+		dprintk("%s: subbuffer size = 0x%x\n", __FUNCTION__, (subbufsize >> 8) * 4);
+		udelay(1000);
+		write_reg_dw(adapter, 0x004, subbufsize);
+
+		dprintk("%s: second subbuffer address = 0x%x\n", __FUNCTION__, subbuf1);
+		udelay(1000);
+		write_reg_dw(adapter, 0x00c, subbuf1);
+
+		dprintk("%s: counter = 0x%x\n", __FUNCTION__, adapter->dmaq1.bus_addr & 0xfffffffc);
+		write_reg_dw(adapter, 0x008, adapter->dmaq1.bus_addr & 0xfffffffc);
+		udelay(1000);
+
+		dma_enable_disable_irq(adapter, 0, 1, subbuffers ? 1 : 0);
+
+		irq_dma_enable_disable_irq(adapter, 1);
+
+		sram_set_media_dest(adapter, 1);
+		sram_set_net_dest(adapter, 1);
+		sram_set_cai_dest(adapter, 2);
+		sram_set_cao_dest(adapter, 2);
+	}
+
+	if (dma_channel == 1) {
+		dprintk("%s: Initializing DMA2 channel\n", __FUNCTION__);
+
+		subbuffers = 2;
+
+		subbufsize = (((adapter->dmaq2.buffer_size / 2) / 4) << 8) | subbuffers;
+
+		subbuf0 = adapter->dmaq2.bus_addr & 0xfffffffc;
+
+		subbuf1 = ((adapter->dmaq2.bus_addr + adapter->dmaq2.buffer_size / 2) & 0xfffffffc) | 1;
+
+		dprintk("%s: first subbuffer address = 0x%x\n", __FUNCTION__, subbuf0);
+		udelay(1000);
+		write_reg_dw(adapter, 0x010, subbuf0);
+
+		dprintk("%s: subbuffer size = 0x%x\n", __FUNCTION__, (subbufsize >> 8) * 4);
+		udelay(1000);
+		write_reg_dw(adapter, 0x014, subbufsize);
+
+		dprintk("%s: second buffer address = 0x%x\n", __FUNCTION__, subbuf1);
+		udelay(1000);
+		write_reg_dw(adapter, 0x01c, subbuf1);
+
+		sram_set_cai_dest(adapter, 2);
+	}
+
+	return 0;
+}
+
+static void ctrl_enable_receive_data(struct adapter *adapter, u32 op)
+{
+	if (op == 0) {
+		write_reg_bitfield(adapter, 0x208, 0x00008000, 0);
+		adapter->dma_status = adapter->dma_status & ~0x00000004;
+	} else {
+		write_reg_bitfield(adapter, 0x208, 0x00008000, 0x00008000);
+		adapter->dma_status = adapter->dma_status | 0x00000004;
+	}
+}
+
+/* bit 0 of dma_mask is set to 1 if dma1 channel has to be enabled/disabled
+   bit 1 of dma_mask is set to 1 if dma2 channel has to be enabled/disabled
+*/
+static void dma_start_stop(struct adapter *adapter, u32 dma_mask, int start_stop)
+{
+	u32 dma_enable, dma1_enable, dma2_enable;
+
+	dprintk("%s: dma_mask=%x\n", __FUNCTION__, dma_mask);
+
+	if (start_stop == 1) {
+		dprintk("%s: starting dma\n", __FUNCTION__);
+
+		dma1_enable = 0;
+		dma2_enable = 0;
+
+		if (((dma_mask & 1) != 0) && ((adapter->dma_status & 1) == 0) && (adapter->dmaq1.bus_addr != 0)) {
+			adapter->dma_status = adapter->dma_status | 1;
+			dma1_enable = 1;
+		}
+
+		if (((dma_mask & 2) != 0) && ((adapter->dma_status & 2) == 0) && (adapter->dmaq2.bus_addr != 0)) {
+			adapter->dma_status = adapter->dma_status | 2;
+			dma2_enable = 1;
+		}
+		// enable dma1 and dma2
+		if ((dma1_enable == 1) && (dma2_enable == 1)) {
+			write_reg_dw(adapter, 0x000, adapter->dmaq1.bus_addr | 1);
+			write_reg_dw(adapter, 0x00c, (adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) | 1);
+			write_reg_dw(adapter, 0x010, adapter->dmaq2.bus_addr | 1);
+
+			ctrl_enable_receive_data(adapter, 1);
+
+			return;
+		}
+		// enable dma1
+		if ((dma1_enable == 1) && (dma2_enable == 0)) {
+			write_reg_dw(adapter, 0x000, adapter->dmaq1.bus_addr | 1);
+			write_reg_dw(adapter, 0x00c, (adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) | 1);
+
+			ctrl_enable_receive_data(adapter, 1);
+
+			return;
+		}
+		// enable dma2
+		if ((dma1_enable == 0) && (dma2_enable == 1)) {
+			write_reg_dw(adapter, 0x010, adapter->dmaq2.bus_addr | 1);
+
+			ctrl_enable_receive_data(adapter, 1);
+
+			return;
+		}
+		// start dma
+		if ((dma1_enable == 0) && (dma2_enable == 0)) {
+			ctrl_enable_receive_data(adapter, 1);
+
+			return;
+		}
+
+	} else {
+
+		dprintk("%s: stopping dma\n", __FUNCTION__);
+
+		dma_enable = adapter->dma_status & 0x00000003;
+
+		if (((dma_mask & 1) != 0) && ((adapter->dma_status & 1) != 0)) {
+			dma_enable = dma_enable & 0xfffffffe;
+		}
+
+		if (((dma_mask & 2) != 0) && ((adapter->dma_status & 2) != 0)) {
+			dma_enable = dma_enable & 0xfffffffd;
+		}
+		//stop dma
+		if ((dma_enable == 0) && ((adapter->dma_status & 4) != 0)) {
+			ctrl_enable_receive_data(adapter, 0);
+
+			udelay(3000);
+		}
+		//disable dma1
+		if (((dma_mask & 1) != 0) && ((adapter->dma_status & 1) != 0) && (adapter->dmaq1.bus_addr != 0)) {
+			write_reg_dw(adapter, 0x000, adapter->dmaq1.bus_addr);
+			write_reg_dw(adapter, 0x00c, (adapter->dmaq1.bus_addr + adapter->dmaq1.buffer_size / 2) | 1);
+
+			adapter->dma_status = adapter->dma_status & ~0x00000001;
+		}
+		//disable dma2
+		if (((dma_mask & 2) != 0) && ((adapter->dma_status & 2) != 0) && (adapter->dmaq2.bus_addr != 0)) {
+			write_reg_dw(adapter, 0x010, adapter->dmaq2.bus_addr);
+
+			adapter->dma_status = adapter->dma_status & ~0x00000002;
+		}
+	}
+}
+
+static void open_stream(struct adapter *adapter, u16 pid)
+{
+	u32 dma_mask;
+
+	++adapter->capturing;
+
+	filter_enable_mask_filter(adapter, 1);
+
+	add_pid(adapter, pid);
+
+	dprintk("%s: adapter->dma_status=%x\n", __FUNCTION__, adapter->dma_status);
+
+	if ((adapter->dma_status & 7) != 7) {
+		dma_mask = 0;
+
+		if (((adapter->dma_status & 0x10000000) != 0) && ((adapter->dma_status & 1) == 0)) {
+			dma_mask = dma_mask | 1;
+
+			adapter->dmaq1.head = 0;
+			adapter->dmaq1.tail = 0;
+
+			memset(adapter->dmaq1.buffer, 0, adapter->dmaq1.buffer_size);
+		}
+
+		if (((adapter->dma_status & 0x20000000) != 0) && ((adapter->dma_status & 2) == 0)) {
+			dma_mask = dma_mask | 2;
+
+			adapter->dmaq2.head = 0;
+			adapter->dmaq2.tail = 0;
+		}
+
+		if (dma_mask != 0) {
+			irq_dma_enable_disable_irq(adapter, 1);
+
+			dma_start_stop(adapter, dma_mask, 1);
+		}
+	}
+}
+
+static void close_stream(struct adapter *adapter, u16 pid)
+{
+	if (adapter->capturing > 0)
+		--adapter->capturing;
+
+	dprintk("%s: dma_status=%x\n", __FUNCTION__, adapter->dma_status);
+
+	if (adapter->capturing == 0) {
+		u32 dma_mask = 0;
+
+	if ((adapter->dma_status & 1) != 0)
+		dma_mask = dma_mask | 0x00000001;
+	if ((adapter->dma_status & 2) != 0)
+		dma_mask = dma_mask | 0x00000002;
+
+	if (dma_mask != 0) {
+			dma_start_stop(adapter, dma_mask, 0);
+	}
+	}
+	remove_pid(adapter, pid);
+}
+
+static void interrupt_service_dma1(struct adapter *adapter)
+{
+	struct dvb_demux *dvbdmx = &adapter->demux;
+
+	int n_cur_dma_counter;
+	u32 n_num_bytes_parsed;
+	u32 n_num_new_bytes_transferred;
+	u32 dw_default_packet_size = 188;
+	u8 gb_tmp_buffer[188];
+	u8 *pb_dma_buf_cur_pos;
+
+	n_cur_dma_counter = readl(adapter->io_mem + 0x008) - adapter->dmaq1.bus_addr;
+	n_cur_dma_counter = (n_cur_dma_counter / dw_default_packet_size) * dw_default_packet_size;
+
+	if ((n_cur_dma_counter < 0) || (n_cur_dma_counter > adapter->dmaq1.buffer_size)) {
+		dprintk("%s: dma counter outside dma buffer\n", __FUNCTION__);
+		return;
+	}
+
+	adapter->dmaq1.head = n_cur_dma_counter;
+
+	if (adapter->dmaq1.tail <= n_cur_dma_counter) {
+		n_num_new_bytes_transferred = n_cur_dma_counter - adapter->dmaq1.tail;
+
+	} else {
+
+		n_num_new_bytes_transferred = (adapter->dmaq1.buffer_size - adapter->dmaq1.tail) + n_cur_dma_counter;
+	}
+
+	ddprintk("%s: n_cur_dma_counter = %d\n", __FUNCTION__, n_cur_dma_counter);
+	ddprintk("%s: dmaq1.tail        = %d\n", __FUNCTION__, adapter->dmaq1.tail);
+	ddprintk("%s: bytes_transferred = %d\n", __FUNCTION__, n_num_new_bytes_transferred);
+
+	if (n_num_new_bytes_transferred < dw_default_packet_size)
+		return;
+
+	n_num_bytes_parsed = 0;
+
+	while (n_num_bytes_parsed < n_num_new_bytes_transferred) {
+		pb_dma_buf_cur_pos = adapter->dmaq1.buffer + adapter->dmaq1.tail;
+
+		if (adapter->dmaq1.buffer + adapter->dmaq1.buffer_size < adapter->dmaq1.buffer + adapter->dmaq1.tail + 188) {
+			memcpy(gb_tmp_buffer, adapter->dmaq1.buffer + adapter->dmaq1.tail,
+			       adapter->dmaq1.buffer_size - adapter->dmaq1.tail);
+			memcpy(gb_tmp_buffer + (adapter->dmaq1.buffer_size - adapter->dmaq1.tail), adapter->dmaq1.buffer,
+			       (188 - (adapter->dmaq1.buffer_size - adapter->dmaq1.tail)));
+
+			pb_dma_buf_cur_pos = gb_tmp_buffer;
+		}
+
+		if (adapter->capturing != 0) {
+			dvb_dmx_swfilter_packets(dvbdmx, pb_dma_buf_cur_pos, dw_default_packet_size / 188);
+		}
+
+		n_num_bytes_parsed = n_num_bytes_parsed + dw_default_packet_size;
+
+		adapter->dmaq1.tail = adapter->dmaq1.tail + dw_default_packet_size;
+
+		if (adapter->dmaq1.tail >= adapter->dmaq1.buffer_size)
+			adapter->dmaq1.tail = adapter->dmaq1.tail - adapter->dmaq1.buffer_size;
+	};
+}
+
+static void interrupt_service_dma2(struct adapter *adapter)
+{
+	printk("%s:\n", __FUNCTION__);
+}
+
+static irqreturn_t isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct adapter *tmp = dev_id;
+
+	u32 value;
+
+	ddprintk("%s:\n", __FUNCTION__);
+
+	spin_lock_irq(&tmp->lock);
+
+	if (0 == ((value = read_reg_dw(tmp, 0x20c)) & 0x0f)) {
+		spin_unlock_irq(&tmp->lock);
+		return IRQ_NONE;
+	}
+	
+	while (value != 0) {
+		if ((value & 0x03) != 0)
+			interrupt_service_dma1(tmp);
+		if ((value & 0x0c) != 0)
+			interrupt_service_dma2(tmp);
+		value = read_reg_dw(tmp, 0x20c) & 0x0f;
+	}
+
+	spin_unlock_irq(&tmp->lock);
+	return IRQ_HANDLED;
+}
+
+static int init_dma_queue_one(struct adapter *adapter, struct dmaq *dmaq,
+			      int size, int dmaq_offset)
+{
+	struct pci_dev *pdev = adapter->pdev;
+	dma_addr_t dma_addr;
+
+	dmaq->head = 0;
+	dmaq->tail = 0;
+
+	dmaq->buffer = pci_alloc_consistent(pdev, size + 0x80, &dma_addr);
+	if (!dmaq->buffer)
+		return -ENOMEM;
+
+	dmaq->bus_addr = dma_addr;
+	dmaq->buffer_size = size;
+
+	dma_init_dma(adapter, dmaq_offset);
+
+	ddprintk("%s: allocated dma buffer at 0x%p, length=%d\n",
+		 __FUNCTION__, dmaq->buffer, size);
+
+	return 0;
+	}
+
+static int init_dma_queue(struct adapter *adapter)
+{
+	struct {
+		struct dmaq *dmaq;
+		u32 dma_status;
+		int size;
+	} dmaq_desc[] = {
+		{ &adapter->dmaq1, 0x10000000, SIZE_OF_BUF_DMA1 },
+		{ &adapter->dmaq2, 0x20000000, SIZE_OF_BUF_DMA2 }
+	}, *p = dmaq_desc;
+	int i;
+
+	for (i = 0; i < 2; i++, p++) {
+		if (init_dma_queue_one(adapter, p->dmaq, p->size, i) < 0)
+			adapter->dma_status &= ~p->dma_status;
+		else
+			adapter->dma_status |= p->dma_status;
+	}
+	return (adapter->dma_status & 0x30000000) ? 0 : -ENOMEM;
+}
+
+static void free_dma_queue_one(struct adapter *adapter, struct dmaq *dmaq)
+{
+	if (dmaq->buffer) {
+		pci_free_consistent(adapter->pdev, dmaq->buffer_size + 0x80,
+				    dmaq->buffer, dmaq->bus_addr);
+		memset(dmaq, 0, sizeof(*dmaq));
+	}
+}
+
+static void free_dma_queue(struct adapter *adapter)
+{
+	struct dmaq *dmaq[] = {
+		&adapter->dmaq1,
+		&adapter->dmaq2,
+		NULL
+	}, **p;
+
+	for (p = dmaq; *p; p++)
+		free_dma_queue_one(adapter, *p);
+	}
+
+static void release_adapter(struct adapter *adapter)
+{
+	struct pci_dev *pdev = adapter->pdev;
+
+	iounmap(adapter->io_mem);
+	pci_disable_device(pdev);
+	pci_release_region(pdev, 0);
+	pci_release_region(pdev, 1);
+}
+
+static void free_adapter_object(struct adapter *adapter)
+{
+	dprintk("%s:\n", __FUNCTION__);
+
+	close_stream(adapter, 0);
+		free_irq(adapter->irq, adapter);
+	free_dma_queue(adapter);
+	release_adapter(adapter);
+	kfree(adapter);
+}
+
+static struct pci_driver skystar2_pci_driver;
+
+static int claim_adapter(struct adapter *adapter)
+{
+	struct pci_dev *pdev = adapter->pdev;
+	u16 var;
+	int ret;
+
+	ret = pci_request_region(pdev, 1, skystar2_pci_driver.name);
+	if (ret < 0)
+		goto out;
+
+	ret = pci_request_region(pdev, 0, skystar2_pci_driver.name);
+	if (ret < 0)
+		goto err_pci_release_1;
+
+	pci_read_config_byte(pdev, PCI_CLASS_REVISION, &adapter->card_revision);
+
+	dprintk("%s: card revision %x \n", __FUNCTION__, adapter->card_revision);
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto err_pci_release_0;
+
+	pci_read_config_word(pdev, 4, &var);
+
+	if ((var & 4) == 0)
+		pci_set_master(pdev);
+
+	adapter->io_port = pdev->resource[1].start;
+
+	adapter->io_mem = ioremap(pdev->resource[0].start, 0x800);
+
+	if (!adapter->io_mem) {
+		dprintk("%s: can not map io memory\n", __FUNCTION__);
+		ret = -EIO;
+		goto err_pci_disable;
+	}
+
+	dprintk("%s: io memory maped at %p\n", __FUNCTION__, adapter->io_mem);
+
+	ret = 1;
+out:
+	return ret;
+
+err_pci_disable:
+	pci_disable_device(pdev);
+err_pci_release_0:
+	pci_release_region(pdev, 0);
+err_pci_release_1:
+	pci_release_region(pdev, 1);
+	goto out;
+}
+
+/*
+static int sll_reset_flexcop(struct adapter *adapter)
+{
+	write_reg_dw(adapter, 0x208, 0);
+	write_reg_dw(adapter, 0x210, 0xb2ff);
+
+	return 0;
+}
+*/
+
+static void decide_how_many_hw_filters(struct adapter *adapter)
+{
+	int hw_filters;
+	int mod_option_hw_filters;
+
+	// FlexCop IIb & III have 6+32 hw filters    
+	// FlexCop II has 6 hw filters, every other should have at least 6
+	switch (adapter->b2c2_revision) {
+	case 0x82:		/* II */
+		hw_filters = 6;
+		break;
+	case 0xc3:		/* IIB */
+		hw_filters = 6 + 32;
+		break;
+	case 0xc0:		/* III */
+		hw_filters = 6 + 32;
+		break;
+	default:
+		hw_filters = 6;
+		break;
+	}
+	printk("%s: the chip has %i hardware filters", __FILE__, hw_filters);
+
+	mod_option_hw_filters = 0;
+	if (enable_hw_filters >= 1)
+		mod_option_hw_filters += 6;
+	if (enable_hw_filters >= 2)
+		mod_option_hw_filters += 32;
+
+	if (mod_option_hw_filters >= hw_filters) {
+		adapter->useable_hw_filters = hw_filters;
+	} else {
+		adapter->useable_hw_filters = mod_option_hw_filters;
+		printk(", but only %d will be used because of module option", mod_option_hw_filters);
+	}
+	printk("\n");
+	dprintk("%s: useable_hardware_filters set to %i\n", __FILE__, adapter->useable_hw_filters);
+}
+
+static int driver_initialize(struct pci_dev *pdev)
+{
+	struct adapter *adapter;
+	u32 tmp;
+	int ret = -ENOMEM;
+
+	adapter = kmalloc(sizeof(struct adapter), GFP_KERNEL);
+	if (!adapter) {
+		dprintk("%s: out of memory!\n", __FUNCTION__);
+		goto out;
+	}
+
+	memset(adapter, 0, sizeof(struct adapter));
+
+	pci_set_drvdata(pdev,adapter);
+
+	adapter->pdev = pdev;
+	adapter->irq = pdev->irq;
+
+	ret = claim_adapter(adapter);
+	if (ret < 0)
+		goto err_kfree;
+
+	irq_dma_enable_disable_irq(adapter, 0);
+
+	ret = request_irq(pdev->irq, isr, 0x4000000, "Skystar2", adapter);
+	if (ret < 0) {
+		dprintk("%s: unable to allocate irq=%d !\n", __FUNCTION__, pdev->irq);
+		goto err_release_adapter;
+	}
+
+	read_reg_dw(adapter, 0x208);
+	write_reg_dw(adapter, 0x208, 0);
+	write_reg_dw(adapter, 0x210, 0xb2ff);
+	write_reg_dw(adapter, 0x208, 0x40);
+
+	ret = init_dma_queue(adapter);
+	if (ret < 0)
+		goto err_free_irq;
+
+	adapter->b2c2_revision = (read_reg_dw(adapter, 0x204) >> 0x18);
+
+	switch (adapter->b2c2_revision) {
+	case 0x82:
+		printk("%s: FlexCopII(rev.130) chip found\n", __FILE__);
+		break;
+	case 0xc3:
+		printk("%s: FlexCopIIB(rev.195) chip found\n", __FILE__);
+		break;
+	case 0xc0:
+		printk("%s: FlexCopIII(rev.192) chip found\n", __FILE__);
+		break;
+	default:
+		printk("%s: The revision of the FlexCop chip on your card is %d\n", __FILE__, adapter->b2c2_revision);
+		printk("%s: This driver works only with FlexCopII(rev.130), FlexCopIIB(rev.195) and FlexCopIII(rev.192).\n", __FILE__);
+		ret = -ENODEV;
+		goto err_free_dma_queue;
+		}
+
+	decide_how_many_hw_filters(adapter);
+
+	init_pids(adapter);
+
+	tmp = read_reg_dw(adapter, 0x204);
+
+	write_reg_dw(adapter, 0x204, 0);
+	mdelay(20);
+
+	write_reg_dw(adapter, 0x204, tmp);
+	mdelay(10);
+
+	tmp = read_reg_dw(adapter, 0x308);
+	write_reg_dw(adapter, 0x308, 0x4000 | tmp);
+
+	adapter->dw_sram_type = 0x10000;
+
+	sll_detect_sram_size(adapter);
+
+	dprintk("%s sram length = %d, sram type= %x\n", __FUNCTION__, sram_length(adapter), adapter->dw_sram_type);
+
+	sram_set_media_dest(adapter, 1);
+	sram_set_net_dest(adapter, 1);
+
+	ctrl_enable_smc(adapter, 0);
+
+	sram_set_cai_dest(adapter, 2);
+	sram_set_cao_dest(adapter, 2);
+
+	dma_enable_disable_irq(adapter, 1, 0, 0);
+
+	if (eeprom_get_mac_addr(adapter, 0, adapter->mac_addr) != 0) {
+		printk("%s MAC address = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x \n", __FUNCTION__, adapter->mac_addr[0],
+		       adapter->mac_addr[1], adapter->mac_addr[2], adapter->mac_addr[3], adapter->mac_addr[4], adapter->mac_addr[5],
+		       adapter->mac_addr[6], adapter->mac_addr[7]
+		    );
+
+		ca_set_mac_dst_addr_filter(adapter, adapter->mac_addr);
+		ctrl_enable_mac(adapter, 1);
+	}
+
+	spin_lock_init(&adapter->lock);
+
+out:
+	return ret;
+
+err_free_dma_queue:
+	free_dma_queue(adapter);
+err_free_irq:
+	free_irq(pdev->irq, adapter);
+err_release_adapter:
+	release_adapter(adapter);
+err_kfree:
+	pci_set_drvdata(pdev, NULL);
+	kfree(adapter);
+	goto out;
+}
+
+static void driver_halt(struct pci_dev *pdev)
+{
+	struct adapter *adapter = pci_get_drvdata(pdev);
+
+	irq_dma_enable_disable_irq(adapter, 0);
+
+	ctrl_enable_receive_data(adapter, 0);
+
+	free_adapter_object(adapter);
+
+	pci_set_drvdata(pdev, NULL);
+}
+
+static int dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct adapter *adapter = (struct adapter *) dvbdmx->priv;
+
+	dprintk("%s: PID=%d, type=%d\n", __FUNCTION__, dvbdmxfeed->pid, dvbdmxfeed->type);
+
+	open_stream(adapter, dvbdmxfeed->pid);
+
+	return 0;
+}
+
+static int dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct adapter *adapter = (struct adapter *) dvbdmx->priv;
+
+	dprintk("%s: PID=%d, type=%d\n", __FUNCTION__, dvbdmxfeed->pid, dvbdmxfeed->type);
+
+	close_stream(adapter, dvbdmxfeed->pid);
+
+	return 0;
+}
+
+/* lnb control */
+static void set_tuner_tone(struct adapter *adapter, u8 tone)
+{
+	u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc };
+	u16 ax;
+
+	dprintk("%s: %u\n", __FUNCTION__, tone);
+
+	switch (tone) {
+	case 1:
+		ax = wz_half_period_for_45_mhz[0];
+		break;
+	case 2:
+		ax = wz_half_period_for_45_mhz[1];
+		break;
+	case 3:
+		ax = wz_half_period_for_45_mhz[2];
+		break;
+	case 4:
+		ax = wz_half_period_for_45_mhz[3];
+		break;
+
+	default:
+		ax = 0;
+	}
+
+	if (ax != 0) {
+		write_reg_dw(adapter, 0x200, ((ax << 0x0f) + (ax & 0x7fff)) | 0x40000000);
+
+	} else {
+
+		write_reg_dw(adapter, 0x200, 0x40ff8000);
+	}
+}
+
+static void set_tuner_polarity(struct adapter *adapter, u8 polarity)
+{
+	u32 var;
+
+	dprintk("%s : polarity = %u \n", __FUNCTION__, polarity);
+
+	var = read_reg_dw(adapter, 0x204);
+
+	if (polarity == 0) {
+		dprintk("%s: LNB power off\n", __FUNCTION__);
+		var = var | 1;
+	};
+
+	if (polarity == 1) {
+		var = var & ~1;
+		var = var & ~4;
+	};
+
+	if (polarity == 2) {
+		var = var & ~1;
+		var = var | 4;
+	}
+
+	write_reg_dw(adapter, 0x204, var);
+}
+
+static void diseqc_send_bit(struct adapter *adapter, int data)
+{
+	set_tuner_tone(adapter, 1);
+	udelay(data ? 500 : 1000);
+	set_tuner_tone(adapter, 0);
+	udelay(data ? 1000 : 500);
+}
+
+
+static void diseqc_send_byte(struct adapter *adapter, int data)
+		{
+	int i, par = 1, d;
+
+	for (i = 7; i >= 0; i--) {
+		d = (data >> i) & 1;
+		par ^= d;
+		diseqc_send_bit(adapter, d);
+	}
+
+	diseqc_send_bit(adapter, par);
+		}
+
+
+static int send_diseqc_msg(struct adapter *adapter, int len, u8 *msg, unsigned long burst)
+{
+	int i;
+
+	set_tuner_tone(adapter, 0);
+	mdelay(16);
+
+	for (i = 0; i < len; i++)
+		diseqc_send_byte(adapter, msg[i]);
+
+	mdelay(16);
+
+	if (burst != -1) {
+		if (burst)
+			diseqc_send_byte(adapter, 0xff);
+		else {
+			set_tuner_tone(adapter, 1);
+			udelay(12500);
+			set_tuner_tone(adapter, 0);
+		}
+		msleep(20);
+	}
+
+	return 0;
+}
+
+static int flexcop_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	switch(tone) {
+		case SEC_TONE_ON:
+			set_tuner_tone(adapter, 1);
+			break;
+		case SEC_TONE_OFF:
+			set_tuner_tone(adapter, 0);
+				break;
+			default:
+				return -EINVAL;
+			};
+
+	return 0;
+}
+
+static int flexcop_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+		{
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+			send_diseqc_msg(adapter, cmd->msg_len, cmd->msg, 0);
+
+	return 0;
+		}
+
+static int flexcop_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	send_diseqc_msg(adapter, 0, NULL, minicmd);
+
+	return 0;
+}
+
+static int flexcop_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+		{
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	dprintk("%s: FE_SET_VOLTAGE\n", __FUNCTION__);
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		dprintk("%s: SEC_VOLTAGE_13, %x\n", __FUNCTION__, SEC_VOLTAGE_13);
+		set_tuner_polarity(adapter, 1);
+		return 0;
+
+	case SEC_VOLTAGE_18:
+		dprintk("%s: SEC_VOLTAGE_18, %x\n", __FUNCTION__, SEC_VOLTAGE_18);
+		set_tuner_polarity(adapter, 2);
+			return 0;
+
+	default:
+		return -EINVAL;
+	}
+	}
+
+static int flexcop_sleep(struct dvb_frontend* fe)
+		{
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	dprintk("%s: FE_SLEEP\n", __FUNCTION__);
+			set_tuner_polarity(adapter, 0);
+
+	if (adapter->fe_sleep) return adapter->fe_sleep(fe);
+	return 0;
+		}
+
+static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
+		{
+	printk("flexcop_i2c_func\n");
+
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm    flexcop_algo = {
+	.name		= "flexcop i2c algorithm",
+	.id		= I2C_ALGO_BIT,
+	.master_xfer	= master_xfer,
+	.functionality	= flexcop_i2c_func,
+};
+
+
+
+
+static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+	else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+	else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+	else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+	else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+	else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
+
+	stv0299_writereg (fe, 0x13, aclk);
+	stv0299_writereg (fe, 0x14, bclk);
+	stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg (fe, 0x20, (ratio >>  8) & 0xff);
+	stv0299_writereg (fe, 0x21, (ratio      ) & 0xf0);
+
+	return 0;
+}
+
+static int samsung_tbmu24112_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	div = params->frequency / 125;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x84;  // 0xC4
+	buf[3] = 0x08;
+
+	if (params->frequency < 1500000) buf[3] |= 0x10;
+
+	if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static u8 samsung_tbmu24112_inittab[] = {
+	     0x01, 0x15,
+	     0x02, 0x30,
+	     0x03, 0x00,
+	     0x04, 0x7D,
+	     0x05, 0x35,
+	     0x06, 0x02,
+	     0x07, 0x00,
+	     0x08, 0xC3,
+	     0x0C, 0x00,
+	     0x0D, 0x81,
+	     0x0E, 0x23,
+	     0x0F, 0x12,
+	     0x10, 0x7E,
+	     0x11, 0x84,
+	     0x12, 0xB9,
+	     0x13, 0x88,
+	     0x14, 0x89,
+	     0x15, 0xC9,
+	     0x16, 0x00,
+	     0x17, 0x5C,
+	     0x18, 0x00,
+	     0x19, 0x00,
+	     0x1A, 0x00,
+	     0x1C, 0x00,
+	     0x1D, 0x00,
+	     0x1E, 0x00,
+	     0x1F, 0x3A,
+	     0x20, 0x2E,
+	     0x21, 0x80,
+	     0x22, 0xFF,
+	     0x23, 0xC1,
+	     0x28, 0x00,
+	     0x29, 0x1E,
+	     0x2A, 0x14,
+	     0x2B, 0x0F,
+	     0x2C, 0x09,
+	     0x2D, 0x05,
+	     0x31, 0x1F,
+	     0x32, 0x19,
+	     0x33, 0xFE,
+	     0x34, 0x93,
+	     0xff, 0xff,
+			};
+
+static struct stv0299_config samsung_tbmu24112_config = {
+	.demod_address = 0x68,
+	.inittab = samsung_tbmu24112_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_LK,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
+   	.pll_set = samsung_tbmu24112_pll_set,
+};
+
+
+
+static int nxt2002_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	return request_firmware(fw, name, &adapter->pdev->dev);
+}
+
+
+static struct nxt2002_config samsung_tbmv_config = {
+	.demod_address = 0x0A,
+	.request_firmware = nxt2002_request_firmware,
+};
+
+static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+	static u8 mt352_clock_config [] = { 0x89, 0x18, 0x2d };
+	static u8 mt352_reset [] = { 0x50, 0x80 };
+	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0xa1 };
+	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+	mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+	return 0;
+}
+
+static int samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+	u32 div;
+	unsigned char bs = 0;
+
+	#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+	div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+	if (params->frequency >= 48000000 && params->frequency <= 154000000) bs = 0x09;
+	if (params->frequency >= 161000000 && params->frequency <= 439000000) bs = 0x0a;
+	if (params->frequency >= 447000000 && params->frequency <= 863000000) bs = 0x08;
+
+	pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
+	pllbuf[1] = div >> 8;
+   	pllbuf[2] = div & 0xff;
+   	pllbuf[3] = 0xcc;
+   	pllbuf[4] = bs;
+
+	return 0;
+}
+
+static struct mt352_config samsung_tdtc9251dh0_config = {
+
+	.demod_address = 0x0f,
+	.demod_init = samsung_tdtc9251dh0_demod_init,
+   	.pll_set = samsung_tdtc9251dh0_pll_set,
+};
+
+static int skystar23_samsung_tbdu18132_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	struct adapter* adapter = (struct adapter*) fe->dvb->priv;
+
+	div = (params->frequency + (125/2)) / 125;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = (div >> 0) & 0xff;
+	buf[2] = 0x84 | ((div >> 10) & 0x60);
+	buf[3] = 0x80;
+
+	if (params->frequency < 1550000)
+		buf[3] |= 0x02;
+
+	if (i2c_transfer (&adapter->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct mt312_config skystar23_samsung_tbdu18132_config = {
+
+	.demod_address = 0x0e,
+   	.pll_set = skystar23_samsung_tbdu18132_pll_set,
+};
+
+
+
+
+static void frontend_init(struct adapter *skystar2)
+{
+	switch(skystar2->pdev->device) {
+	case 0x2103: // Technisat Skystar2 OR Technisat Airstar2 (DVB-T or ATSC)
+
+		// Attempt to load the Nextwave nxt2002 for ATSC support 
+		skystar2->fe = nxt2002_attach(&samsung_tbmv_config, &skystar2->i2c_adap);
+		if (skystar2->fe != NULL) {
+			skystar2->fe_sleep = skystar2->fe->ops->sleep;
+			skystar2->fe->ops->sleep = flexcop_sleep;
+			break;
+		}
+
+		// try the skystar2 v2.6 first (stv0299/Samsung tbmu24112(sl1935))
+		skystar2->fe = stv0299_attach(&samsung_tbmu24112_config, &skystar2->i2c_adap);
+		if (skystar2->fe != NULL) {
+			skystar2->fe->ops->set_voltage = flexcop_set_voltage;
+			skystar2->fe_sleep = skystar2->fe->ops->sleep;
+			skystar2->fe->ops->sleep = flexcop_sleep;
+			break;
+}
+
+		// try the airstar2 (mt352/Samsung tdtc9251dh0(??))
+		skystar2->fe = mt352_attach(&samsung_tdtc9251dh0_config, &skystar2->i2c_adap);
+		if (skystar2->fe != NULL) {
+			skystar2->fe->ops->info.frequency_min = 474000000;
+			skystar2->fe->ops->info.frequency_max = 858000000;
+			break;
+		}
+
+		// try the skystar2 v2.3 (vp310/Samsung tbdu18132(tsa5059))
+		skystar2->fe = vp310_attach(&skystar23_samsung_tbdu18132_config, &skystar2->i2c_adap);
+		if (skystar2->fe != NULL) {
+			skystar2->fe->ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
+			skystar2->fe->ops->diseqc_send_burst = flexcop_diseqc_send_burst;
+			skystar2->fe->ops->set_tone = flexcop_set_tone;
+			skystar2->fe->ops->set_voltage = flexcop_set_voltage;
+			skystar2->fe_sleep = skystar2->fe->ops->sleep;
+			skystar2->fe->ops->sleep = flexcop_sleep;
+			break;
+		}
+		break;
+	}
+
+	if (skystar2->fe == NULL) {
+		printk("skystar2: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		       skystar2->pdev->vendor,
+		       skystar2->pdev->device,
+		       skystar2->pdev->subsystem_vendor,
+		       skystar2->pdev->subsystem_device);
+	} else {
+		if (dvb_register_frontend(skystar2->dvb_adapter, skystar2->fe)) {
+			printk("skystar2: Frontend registration failed!\n");
+			if (skystar2->fe->ops->release)
+				skystar2->fe->ops->release(skystar2->fe);
+			skystar2->fe = NULL;
+		}
+	}
+}
+
+
+static int skystar2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct adapter *adapter;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+	int ret = -ENODEV;
+
+	if (!pdev)
+		goto out;
+
+	ret = driver_initialize(pdev);
+	if (ret < 0)
+		goto out;
+
+	ret = dvb_register_adapter(&dvb_adapter, skystar2_pci_driver.name,
+				   THIS_MODULE);
+	if (ret < 0) {
+		printk("%s: Error registering DVB adapter\n", __FUNCTION__);
+		goto err_halt;
+	}
+
+	adapter = pci_get_drvdata(pdev);
+
+	dvb_adapter->priv = adapter;
+	adapter->dvb_adapter = dvb_adapter;
+
+
+	init_MUTEX(&adapter->i2c_sem);
+
+
+	memset(&adapter->i2c_adap, 0, sizeof(struct i2c_adapter));
+	strcpy(adapter->i2c_adap.name, "SkyStar2");
+
+	i2c_set_adapdata(&adapter->i2c_adap, adapter);
+
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+	adapter->i2c_adap.class 	    = I2C_ADAP_CLASS_TV_DIGITAL;
+#else
+	adapter->i2c_adap.class 	    = I2C_CLASS_TV_DIGITAL;
+#endif
+	adapter->i2c_adap.algo              = &flexcop_algo;
+	adapter->i2c_adap.algo_data         = NULL;
+	adapter->i2c_adap.id                = I2C_ALGO_BIT;
+
+	ret = i2c_add_adapter(&adapter->i2c_adap);
+	if (ret < 0)
+		goto err_dvb_unregister;
+
+	dvbdemux = &adapter->demux;
+
+	dvbdemux->priv = adapter;
+	dvbdemux->filternum = N_PID_SLOTS;
+	dvbdemux->feednum = N_PID_SLOTS;
+	dvbdemux->start_feed = dvb_start_feed;
+	dvbdemux->stop_feed = dvb_stop_feed;
+	dvbdemux->write_to_decoder = NULL;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+
+	ret = dvb_dmx_init(&adapter->demux);
+	if (ret < 0)
+		goto err_i2c_del;
+
+	dmx = &dvbdemux->dmx;
+
+	adapter->hw_frontend.source = DMX_FRONTEND_0;
+	adapter->dmxdev.filternum = N_PID_SLOTS;
+	adapter->dmxdev.demux = dmx;
+	adapter->dmxdev.capabilities = 0;
+
+	ret = dvb_dmxdev_init(&adapter->dmxdev, adapter->dvb_adapter);
+	if (ret < 0)
+		goto err_dmx_release;
+
+	ret = dmx->add_frontend(dmx, &adapter->hw_frontend);
+	if (ret < 0)
+		goto err_dmxdev_release;
+
+	adapter->mem_frontend.source = DMX_MEMORY_FE;
+
+	ret = dmx->add_frontend(dmx, &adapter->mem_frontend);
+	if (ret < 0)
+		goto err_remove_hw_frontend;
+
+	ret = dmx->connect_frontend(dmx, &adapter->hw_frontend);
+	if (ret < 0)
+		goto err_remove_mem_frontend;
+
+	dvb_net_init(adapter->dvb_adapter, &adapter->dvbnet, &dvbdemux->dmx);
+
+	frontend_init(adapter);
+out:
+	return ret;
+
+err_remove_mem_frontend:
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &adapter->mem_frontend);
+err_remove_hw_frontend:
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &adapter->hw_frontend);
+err_dmxdev_release:
+	dvb_dmxdev_release(&adapter->dmxdev);
+err_dmx_release:
+	dvb_dmx_release(&adapter->demux);
+err_i2c_del:
+	i2c_del_adapter(&adapter->i2c_adap);
+err_dvb_unregister:
+	dvb_unregister_adapter(adapter->dvb_adapter);
+err_halt:
+	driver_halt(pdev);
+	goto out;
+}
+
+static void skystar2_remove(struct pci_dev *pdev)
+{
+	struct adapter *adapter = pci_get_drvdata(pdev);
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+
+	if (!adapter)
+		return;
+
+		dvb_net_release(&adapter->dvbnet);
+		dvbdemux = &adapter->demux;
+	dmx = &dvbdemux->dmx;
+
+	dmx->close(dmx);
+	dmx->remove_frontend(dmx, &adapter->hw_frontend);
+	dmx->remove_frontend(dmx, &adapter->mem_frontend);
+
+		dvb_dmxdev_release(&adapter->dmxdev);
+	dvb_dmx_release(dvbdemux);
+
+	if (adapter->fe != NULL)
+		dvb_unregister_frontend(adapter->fe);
+
+	dvb_unregister_adapter(adapter->dvb_adapter);
+
+			i2c_del_adapter(&adapter->i2c_adap);
+
+		driver_halt(pdev);
+	}
+
+static struct pci_device_id skystar2_pci_tbl[] = {
+	{0x000013d0, 0x00002103, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000},
+/*	{0x000013d0, 0x00002200, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000}, UNDEFINED HARDWARE - mail linuxtv.org list */	//FCIII
+	{0,},
+};
+
+MODULE_DEVICE_TABLE(pci, skystar2_pci_tbl);
+
+static struct pci_driver skystar2_pci_driver = {
+	.name = "SkyStar2",
+	.id_table = skystar2_pci_tbl,
+	.probe = skystar2_probe,
+	.remove = skystar2_remove,
+};
+
+static int skystar2_init(void)
+{
+	return pci_register_driver(&skystar2_pci_driver);
+}
+
+static void skystar2_cleanup(void)
+{
+	pci_unregister_driver(&skystar2_pci_driver);
+}
+
+module_init(skystar2_init);
+module_exit(skystar2_cleanup);
+
+MODULE_DESCRIPTION("Technisat SkyStar2 DVB PCI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
new file mode 100644
index 0000000..e7d11e0
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -0,0 +1,19 @@
+config DVB_BT8XX
+	tristate "Nebula/Pinnacle PCTV/Twinhan PCI cards"
+	depends on DVB_CORE && PCI && VIDEO_BT848
+	select DVB_MT352
+	select DVB_SP887X
+	select DVB_NXT6000
+	select DVB_CX24110
+	select DVB_OR51211
+	help
+	  Support for PCI cards based on the Bt8xx PCI bridge. Examples are
+	  the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards and
+	  pcHDTV HD2000 cards.
+
+          Since these cards have no MPEG decoder onboard, they transmit
+	  only compressed MPEG data over the PCI bus, so you need
+	  an external software decoder to watch TV on your computer.
+
+	  Say Y if you own such a device and want to use it.
+
diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile
new file mode 100644
index 0000000..9da8604
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/Makefile
@@ -0,0 +1,5 @@
+
+obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/video -Idrivers/media/dvb/frontends
+
diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c
new file mode 100644
index 0000000..213ff79
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/bt878.c
@@ -0,0 +1,588 @@
+/*
+ * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card
+ *
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
+ *
+ * large parts based on the bttv driver
+ * Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+ *                        & Marcus Metzler (mocm@thp.uni-koeln.de)
+ * (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.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; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ * 
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "bt878.h"
+#include "dst_priv.h"
+
+
+/**************************************/
+/* Miscellaneous utility  definitions */
+/**************************************/
+
+static unsigned int bt878_verbose = 1;
+static unsigned int bt878_debug;
+
+module_param_named(verbose, bt878_verbose, int, 0444);
+MODULE_PARM_DESC(verbose,
+		 "verbose startup messages, default is 1 (yes)");
+module_param_named(debug, bt878_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+int bt878_num;
+struct bt878 bt878[BT878_MAX];
+
+EXPORT_SYMBOL(bt878_debug);
+EXPORT_SYMBOL(bt878_verbose);
+EXPORT_SYMBOL(bt878_num);
+EXPORT_SYMBOL(bt878);
+
+#define btwrite(dat,adr)    bmtwrite((dat), (bt->bt878_mem+(adr)))
+#define btread(adr)         bmtread(bt->bt878_mem+(adr))
+
+#define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#if defined(dprintk)
+#undef dprintk
+#endif
+#define dprintk if(bt878_debug) printk
+
+static void bt878_mem_free(struct bt878 *bt)
+{
+	if (bt->buf_cpu) {
+		pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu,
+				    bt->buf_dma);
+		bt->buf_cpu = NULL;
+	}
+
+	if (bt->risc_cpu) {
+		pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu,
+				    bt->risc_dma);
+		bt->risc_cpu = NULL;
+	}
+}
+
+static int bt878_mem_alloc(struct bt878 *bt)
+{
+	if (!bt->buf_cpu) {
+		bt->buf_size = 128 * 1024;
+
+		bt->buf_cpu =
+		    pci_alloc_consistent(bt->dev, bt->buf_size,
+					 &bt->buf_dma);
+
+		if (!bt->buf_cpu)
+			return -ENOMEM;
+
+		memset(bt->buf_cpu, 0, bt->buf_size);
+	}
+
+	if (!bt->risc_cpu) {
+		bt->risc_size = PAGE_SIZE;
+		bt->risc_cpu =
+		    pci_alloc_consistent(bt->dev, bt->risc_size,
+					 &bt->risc_dma);
+
+		if (!bt->risc_cpu) {
+			bt878_mem_free(bt);
+			return -ENOMEM;
+		}
+
+		memset(bt->risc_cpu, 0, bt->risc_size);
+	}
+
+	return 0;
+}
+
+/* RISC instructions */
+#define RISC_WRITE        	(0x01 << 28)
+#define RISC_JUMP         	(0x07 << 28)
+#define RISC_SYNC         	(0x08 << 28)
+
+/* RISC bits */
+#define RISC_WR_SOL       	(1 << 27)
+#define RISC_WR_EOL       	(1 << 26)
+#define RISC_IRQ          	(1 << 24)
+#define RISC_STATUS(status)	((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16))
+#define RISC_SYNC_RESYNC  	(1 << 15)
+#define RISC_SYNC_FM1     	0x06
+#define RISC_SYNC_VRO     	0x0C
+
+#define RISC_FLUSH()		bt->risc_pos = 0
+#define RISC_INSTR(instr) 	bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr)
+
+static int bt878_make_risc(struct bt878 *bt)
+{
+	bt->block_bytes = bt->buf_size >> 4;
+	bt->block_count = 1 << 4;
+	bt->line_bytes = bt->block_bytes;
+	bt->line_count = bt->block_count;
+
+	while (bt->line_bytes > 4095) {
+		bt->line_bytes >>= 1;
+		bt->line_count <<= 1;
+	}
+
+	if (bt->line_count > 255) {
+		printk("bt878: buffer size error!\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin)
+{
+	u32 buf_pos = 0;
+	u32 line;
+
+	RISC_FLUSH();
+	RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin);
+	RISC_INSTR(0);
+
+	dprintk("bt878: risc len lines %u, bytes per line %u\n", 
+			bt->line_count, bt->line_bytes);
+	for (line = 0; line < bt->line_count; line++) {
+		// At the beginning of every block we issue an IRQ with previous (finished) block number set
+		if (!(buf_pos % bt->block_bytes))
+			RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
+				   RISC_IRQ |
+				   RISC_STATUS(((buf_pos /
+						 bt->block_bytes) +
+						(bt->block_count -
+						 1)) %
+					       bt->block_count) | bt->
+				   line_bytes);
+		else
+			RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL |
+				   bt->line_bytes);
+		RISC_INSTR(bt->buf_dma + buf_pos);
+		buf_pos += bt->line_bytes;
+	}
+
+	RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO);
+	RISC_INSTR(0);
+
+	RISC_INSTR(RISC_JUMP);
+	RISC_INSTR(bt->risc_dma);
+
+	btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN);
+}
+
+/*****************************/
+/* Start/Stop grabbing funcs */
+/*****************************/
+
+void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
+		u32 irq_err_ignore)
+{
+	u32 int_mask;
+
+	dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg);
+	/* complete the writing of the risc dma program now we have
+	 * the card specifics
+	 */
+	bt878_risc_program(bt, op_sync_orin);
+	controlreg &= ~0x1f;
+	controlreg |= 0x1b;
+
+	btwrite(cpu_to_le32(bt->risc_dma), BT878_ARISC_START);
+
+	/* original int mask had :
+	 *    6    2    8    4    0
+	 * 1111 1111 1000 0000 0000
+	 * SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI
+	 * Hacked for DST to:
+	 * SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI
+	 */
+	int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT | 
+		BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT | 
+		BT878_AFBUS | BT878_ARISCI;
+
+
+	/* ignore pesky bits */
+	int_mask &= ~irq_err_ignore;
+	
+	btwrite(int_mask, BT878_AINT_MASK);
+	btwrite(controlreg, BT878_AGPIO_DMA_CTL);
+}
+
+void bt878_stop(struct bt878 *bt)
+{
+	u32 stat;
+	int i = 0;
+
+	dprintk("bt878 debug: bt878_stop\n");
+
+	btwrite(0, BT878_AINT_MASK);
+	btand(~0x13, BT878_AGPIO_DMA_CTL);
+
+	do {
+		stat = btread(BT878_AINT_STAT);
+		if (!(stat & BT878_ARISC_EN))
+			break;
+		i++;
+	} while (i < 500);
+
+	dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n",
+		bt->nr, i, stat);
+}
+
+EXPORT_SYMBOL(bt878_start);
+EXPORT_SYMBOL(bt878_stop);
+
+/*****************************/
+/* Interrupt service routine */
+/*****************************/
+
+static irqreturn_t bt878_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u32 stat, astat, mask;
+	int count;
+	struct bt878 *bt;
+
+	bt = (struct bt878 *) dev_id;
+
+	count = 0;
+	while (1) {
+		stat = btread(BT878_AINT_STAT);
+		mask = btread(BT878_AINT_MASK);
+		if (!(astat = (stat & mask)))
+			return IRQ_NONE;	/* this interrupt is not for me */
+/*		dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */
+		btwrite(astat, BT878_AINT_STAT);	/* try to clear interupt condition */
+
+
+		if (astat & (BT878_ASCERR | BT878_AOCERR)) {
+			if (bt878_verbose) {
+				printk("bt878(%d): irq%s%s risc_pc=%08x\n",
+				       bt->nr,
+				       (astat & BT878_ASCERR) ? " SCERR" :
+				       "",
+				       (astat & BT878_AOCERR) ? " OCERR" :
+				       "", btread(BT878_ARISC_PC));
+			}
+		}
+		if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) {
+			if (bt878_verbose) {
+				printk
+				    ("bt878(%d): irq%s%s%s risc_pc=%08x\n",
+				     bt->nr,
+				     (astat & BT878_APABORT) ? " PABORT" :
+				     "",
+				     (astat & BT878_ARIPERR) ? " RIPERR" :
+				     "",
+				     (astat & BT878_APPERR) ? " PPERR" :
+				     "", btread(BT878_ARISC_PC));
+			}
+		}
+		if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) {
+			if (bt878_verbose) {
+				printk
+				    ("bt878(%d): irq%s%s%s risc_pc=%08x\n",
+				     bt->nr,
+				     (astat & BT878_AFDSR) ? " FDSR" : "",
+				     (astat & BT878_AFTRGT) ? " FTRGT" :
+				     "",
+				     (astat & BT878_AFBUS) ? " FBUS" : "",
+				     btread(BT878_ARISC_PC));
+			}
+		}
+		if (astat & BT878_ARISCI) {
+			bt->finished_block = (stat & BT878_ARISCS) >> 28;
+			tasklet_schedule(&bt->tasklet);
+			break;
+		}
+		count++;
+		if (count > 20) {
+			btwrite(0, BT878_AINT_MASK);
+			printk(KERN_ERR
+			       "bt878(%d): IRQ lockup, cleared int mask\n",
+			       bt->nr);
+			break;
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+int
+bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp)
+{
+	int retval;
+
+	retval = 0;
+	if (down_interruptible (&bt->gpio_lock))
+		return -ERESTARTSYS;
+	/* special gpio signal */
+	switch (cmd) {
+	    case DST_IG_ENABLE:
+		// dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable);
+		retval = bttv_gpio_enable(bt->bttv_nr,
+				mp->enb.mask,
+				mp->enb.enable);
+		break;
+	    case DST_IG_WRITE:
+		// dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals);
+		retval = bttv_write_gpio(bt->bttv_nr,
+				mp->outp.mask,
+				mp->outp.highvals);
+
+		break;
+	    case DST_IG_READ:
+		/* read */
+		retval =  bttv_read_gpio(bt->bttv_nr, &mp->rd.value);
+		// dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value);
+		break;
+	    case DST_IG_TS:
+		/* Set packet size */
+		bt->TS_Size = mp->psize;
+		break;
+
+	    default:
+		retval = -EINVAL;
+		break;
+	}
+	up(&bt->gpio_lock);
+	return retval;
+}
+
+EXPORT_SYMBOL(bt878_device_control);
+
+/***********************/
+/* PCI device handling */
+/***********************/
+
+static int __devinit bt878_probe(struct pci_dev *dev,
+				 const struct pci_device_id *pci_id)
+{
+	int result;
+	unsigned char lat;
+	struct bt878 *bt;
+#if defined(__powerpc__)
+	unsigned int cmd;
+#endif
+
+	printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n",
+	       bt878_num);
+	if (pci_enable_device(dev))
+		return -EIO;
+
+	bt = &bt878[bt878_num];
+	bt->dev = dev;
+	bt->nr = bt878_num;
+	bt->shutdown = 0;
+
+	bt->id = dev->device;
+	bt->irq = dev->irq;
+	bt->bt878_adr = pci_resource_start(dev, 0);
+	if (!request_mem_region(pci_resource_start(dev, 0),
+				pci_resource_len(dev, 0), "bt878")) {
+		result = -EBUSY;
+		goto fail0;
+	}
+
+	pci_read_config_byte(dev, PCI_CLASS_REVISION, &bt->revision);
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+	printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ",
+	       bt878_num, bt->id, bt->revision, dev->bus->number,
+	       PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
+	printk("irq: %d, latency: %d, memory: 0x%lx\n",
+	       bt->irq, lat, bt->bt878_adr);
+
+
+#if defined(__powerpc__)
+	/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+	/* response on cards with no firmware is not enabled by OF */
+	pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+	cmd = (cmd | PCI_COMMAND_MEMORY);
+	pci_write_config_dword(dev, PCI_COMMAND, cmd);
+#endif
+
+#ifdef __sparc__
+	bt->bt878_mem = (unsigned char *) bt->bt878_adr;
+#else
+	bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000);
+#endif
+
+	/* clear interrupt mask */
+	btwrite(0, BT848_INT_MASK);
+
+	result = request_irq(bt->irq, bt878_irq,
+			     SA_SHIRQ | SA_INTERRUPT, "bt878",
+			     (void *) bt);
+	if (result == -EINVAL) {
+		printk(KERN_ERR "bt878(%d): Bad irq number or handler\n",
+		       bt878_num);
+		goto fail1;
+	}
+	if (result == -EBUSY) {
+		printk(KERN_ERR
+		       "bt878(%d): IRQ %d busy, change your PnP config in BIOS\n",
+		       bt878_num, bt->irq);
+		goto fail1;
+	}
+	if (result < 0)
+		goto fail1;
+
+	pci_set_master(dev);
+	pci_set_drvdata(dev, bt);
+
+/*        if(init_bt878(btv) < 0) {
+                bt878_remove(dev);
+                return -EIO;
+        }
+*/
+
+	if ((result = bt878_mem_alloc(bt))) {
+		printk("bt878: failed to allocate memory!\n");
+		goto fail2;
+	}
+
+	bt878_make_risc(bt);
+	btwrite(0, BT878_AINT_MASK);
+	bt878_num++;
+
+	return 0;
+
+      fail2:
+	free_irq(bt->irq, bt);
+      fail1:
+	release_mem_region(pci_resource_start(bt->dev, 0),
+			   pci_resource_len(bt->dev, 0));
+      fail0:
+	pci_disable_device(dev);
+	return result;
+}
+
+static void __devexit bt878_remove(struct pci_dev *pci_dev)
+{
+	u8 command;
+	struct bt878 *bt = pci_get_drvdata(pci_dev);
+
+	if (bt878_verbose)
+		printk("bt878(%d): unloading\n", bt->nr);
+
+	/* turn off all capturing, DMA and IRQs */
+	btand(~0x13, BT878_AGPIO_DMA_CTL);
+
+	/* first disable interrupts before unmapping the memory! */
+	btwrite(0, BT878_AINT_MASK);
+	btwrite(~0U, BT878_AINT_STAT);
+
+	/* disable PCI bus-mastering */
+	pci_read_config_byte(bt->dev, PCI_COMMAND, &command);
+	/* Should this be &=~ ?? */
+	command &= ~PCI_COMMAND_MASTER;
+	pci_write_config_byte(bt->dev, PCI_COMMAND, command);
+
+	free_irq(bt->irq, bt);
+	printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem);
+	if (bt->bt878_mem)
+		iounmap(bt->bt878_mem);
+
+	release_mem_region(pci_resource_start(bt->dev, 0),
+			   pci_resource_len(bt->dev, 0));
+	/* wake up any waiting processes
+	   because shutdown flag is set, no new processes (in this queue)
+	   are expected
+	 */
+	bt->shutdown = 1;
+	bt878_mem_free(bt);
+
+	pci_set_drvdata(pci_dev, NULL);
+	pci_disable_device(pci_dev);
+	return;
+}
+
+static struct pci_device_id bt878_pci_tbl[] __devinitdata = {
+	{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BROOKTREE_878,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, bt878_pci_tbl);
+
+static struct pci_driver bt878_pci_driver = {
+      .name 	= "bt878",
+      .id_table = bt878_pci_tbl,
+      .probe 	= bt878_probe,
+      .remove 	= bt878_remove,
+};
+
+static int bt878_pci_driver_registered = 0;
+
+/*******************************/
+/* Module management functions */
+/*******************************/
+
+static int bt878_init_module(void)
+{
+	bt878_num = 0;
+	bt878_pci_driver_registered = 0;
+
+	printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n",
+	       (BT878_VERSION_CODE >> 16) & 0xff,
+	       (BT878_VERSION_CODE >> 8) & 0xff,
+	       BT878_VERSION_CODE & 0xff);
+/*
+        bt878_check_chipset();
+*/
+	/* later we register inside of bt878_find_audio_dma()
+	 * because we may want to ignore certain cards */
+	bt878_pci_driver_registered = 1;
+	return pci_register_driver(&bt878_pci_driver);
+}
+
+static void bt878_cleanup_module(void)
+{
+	if (bt878_pci_driver_registered) {
+		bt878_pci_driver_registered = 0;
+		pci_unregister_driver(&bt878_pci_driver);
+	}
+	return;
+}
+
+module_init(bt878_init_module);
+module_exit(bt878_cleanup_module);
+
+//MODULE_AUTHOR("XXX");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/bt8xx/bt878.h b/drivers/media/dvb/bt8xx/bt878.h
new file mode 100644
index 0000000..e1b9809
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/bt878.h
@@ -0,0 +1,147 @@
+/* 
+    bt878.h - Bt878 audio module (register offsets)
+
+    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.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; 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 _BT878_H_
+#define _BT878_H_
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include "bt848.h"
+#include "bttv.h"
+
+#define BT878_VERSION_CODE 0x000000
+
+#define BT878_AINT_STAT		0x100
+#define BT878_ARISCS		(0xf<<28)
+#define BT878_ARISC_EN		(1<<27)
+#define BT878_ASCERR		(1<<19)
+#define BT878_AOCERR		(1<<18)
+#define BT878_APABORT		(1<<17)
+#define BT878_ARIPERR		(1<<16)
+#define BT878_APPERR		(1<<15)
+#define BT878_AFDSR		(1<<14)
+#define BT878_AFTRGT		(1<<13)
+#define BT878_AFBUS		(1<<12)
+#define BT878_ARISCI		(1<<11)
+#define BT878_AOFLOW		(1<<3)
+
+#define BT878_AINT_MASK		0x104
+
+#define BT878_AGPIO_DMA_CTL	0x10c
+#define BT878_A_GAIN		(0xf<<28)
+#define BT878_A_G2X		(1<<27)
+#define BT878_A_PWRDN		(1<<26)
+#define BT878_A_SEL		(3<<24)
+#define BT878_DA_SCE		(1<<23)
+#define BT878_DA_LRI		(1<<22)
+#define BT878_DA_MLB		(1<<21)
+#define BT878_DA_LRD		(0x1f<<16)
+#define BT878_DA_DPM		(1<<15)
+#define BT878_DA_SBR		(1<<14)
+#define BT878_DA_ES2		(1<<13)
+#define BT878_DA_LMT		(1<<12)
+#define BT878_DA_SDR		(0xf<<8)
+#define BT878_DA_IOM		(3<<6)
+#define BT878_DA_APP		(1<<5)
+#define BT878_ACAP_EN		(1<<4)
+#define BT878_PKTP		(3<<2)
+#define BT878_RISC_EN		(1<<1)
+#define BT878_FIFO_EN		1
+
+#define BT878_APACK_LEN		0x110
+#define BT878_AFP_LEN		(0xff<<16)
+#define BT878_ALP_LEN		0xfff
+
+#define BT878_ARISC_START	0x114
+
+#define BT878_ARISC_PC		0x120
+
+/* BT878 FUNCTION 0 REGISTERS */
+#define BT878_GPIO_DMA_CTL	0x10c
+
+/* Interrupt register */
+#define BT878_INT_STAT		0x100
+#define BT878_INT_MASK		0x104
+#define BT878_I2CRACK		(1<<25)
+#define BT878_I2CDONE		(1<<8)
+
+#define BT878_MAX 4
+
+#define BT878_RISC_SYNC_MASK	(1 << 15)
+
+extern int bt878_num;
+
+struct bt878 {
+	struct semaphore  gpio_lock;
+	unsigned int nr;
+	unsigned int bttv_nr;
+	struct i2c_adapter *adapter;
+	struct pci_dev *dev;
+	unsigned int id;
+	unsigned int TS_Size;
+	unsigned char revision;
+	unsigned int irq;
+	unsigned long bt878_adr;
+	volatile void __iomem *bt878_mem; /* function 1 */
+
+	volatile u32 finished_block;
+	volatile u32 last_block;
+	u32 block_count;
+	u32 block_bytes;
+	u32 line_bytes;
+	u32 line_count;
+
+	u32 buf_size;
+	u8 *buf_cpu;
+	dma_addr_t buf_dma;
+
+	u32 risc_size;
+	u32 *risc_cpu;
+	dma_addr_t risc_dma;
+	u32 risc_pos;
+
+	struct tasklet_struct tasklet;
+	int shutdown;	
+};
+
+extern struct bt878 bt878[BT878_MAX];
+
+void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin,
+		u32 irq_err_ignore);
+void bt878_stop(struct bt878 *bt);	     
+
+#if defined(__powerpc__)	/* big-endian */
+extern __inline__ void io_st_le32(volatile unsigned __iomem *addr, unsigned val)
+{
+	__asm__ __volatile__("stwbrx %1,0,%2":"=m"(*addr):"r"(val),
+			     "r"(addr));
+	__asm__ __volatile__("eieio":::"memory");
+}
+
+#define bmtwrite(dat,adr)  io_st_le32((adr),(dat))
+#define bmtread(adr)       ld_le32((adr))
+#else
+#define bmtwrite(dat,adr)  writel((dat), (adr))
+#define bmtread(adr)       readl(adr)
+#endif
+
+#endif
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
new file mode 100644
index 0000000..eac8376
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -0,0 +1,1089 @@
+/*
+    Frontend-driver for TwinHan DST Frontend
+
+    Copyright (C) 2003 Jamie Honan
+
+    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/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "dst_priv.h"
+#include "dst.h"
+
+struct dst_state {
+
+	struct i2c_adapter* i2c;
+
+	struct bt878* bt;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct dst_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* private demodulator data */
+	u8 tx_tuna[10];
+	u8 rx_tuna[10];
+	u8 rxbuffer[10];
+	u8 diseq_flags;
+	u8 dst_type;
+	u32 type_flags;
+	u32 frequency;		/* intermediate frequency in kHz for QPSK */
+	fe_spectral_inversion_t inversion;
+	u32 symbol_rate;	/* symbol rate in Symbols per second */
+	fe_code_rate_t fec;
+	fe_sec_voltage_t voltage;
+	fe_sec_tone_mode_t tone;
+	u32 decode_freq;
+	u8 decode_lock;
+	u16 decode_strength;
+	u16 decode_snr;
+	unsigned long cur_jiff;
+	u8 k22;
+	fe_bandwidth_t bandwidth;
+};
+
+static unsigned int dst_verbose = 0;
+module_param(dst_verbose, int, 0644);
+MODULE_PARM_DESC(dst_verbose, "verbose startup messages, default is 1 (yes)");
+static unsigned int dst_debug = 0;
+module_param(dst_debug, int, 0644);
+MODULE_PARM_DESC(dst_debug, "debug messages, default is 0 (no)");
+
+#define dprintk	if (dst_debug) printk
+
+#define DST_TYPE_IS_SAT		0
+#define DST_TYPE_IS_TERR	1
+#define DST_TYPE_IS_CABLE	2
+
+#define DST_TYPE_HAS_NEWTUNE	1
+#define DST_TYPE_HAS_TS204	2
+#define DST_TYPE_HAS_SYMDIV	4
+
+#define HAS_LOCK	1
+#define ATTEMPT_TUNE	2
+#define HAS_POWER	4
+
+static void dst_packsize(struct dst_state* state, int psize)
+{
+	union dst_gpio_packet bits;
+
+	bits.psize = psize;
+	bt878_device_control(state->bt, DST_IG_TS, &bits);
+}
+
+static int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh)
+{
+	union dst_gpio_packet enb;
+	union dst_gpio_packet bits;
+	int err;
+
+	enb.enb.mask = mask;
+	enb.enb.enable = enbb;
+	if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) {
+		dprintk("%s: dst_gpio_enb error (err == %i, mask == 0x%02x, enb == 0x%02x)\n", __FUNCTION__, err, mask, enbb);
+		return -EREMOTEIO;
+	}
+
+	/* because complete disabling means no output, no need to do output packet */
+	if (enbb == 0)
+		return 0;
+
+	bits.outp.mask = enbb;
+	bits.outp.highvals = outhigh;
+
+	if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) {
+		dprintk("%s: dst_gpio_outb error (err == %i, enbb == 0x%02x, outhigh == 0x%02x)\n", __FUNCTION__, err, enbb, outhigh);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int dst_gpio_inb(struct dst_state *state, u8 * result)
+{
+	union dst_gpio_packet rd_packet;
+	int err;
+
+	*result = 0;
+
+	if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) {
+		dprintk("%s: dst_gpio_inb error (err == %i)\n", __FUNCTION__, err);
+		return -EREMOTEIO;
+	}
+
+	*result = (u8) rd_packet.rd.value;
+	return 0;
+}
+
+#define DST_I2C_ENABLE	1
+#define DST_8820	2
+
+static int dst_reset8820(struct dst_state *state)
+{
+	int retval;
+	/* pull 8820 gpio pin low, wait, high, wait, then low */
+	// dprintk ("%s: reset 8820\n", __FUNCTION__);
+	retval = dst_gpio_outb(state, DST_8820, DST_8820, 0);
+	if (retval < 0)
+		return retval;
+	msleep(10);
+	retval = dst_gpio_outb(state, DST_8820, DST_8820, DST_8820);
+	if (retval < 0)
+		return retval;
+	/* wait for more feedback on what works here *
+	   msleep(10);
+	   retval = dst_gpio_outb(dst, DST_8820, DST_8820, 0);
+	   if (retval < 0)
+	   return retval;
+	 */
+	return 0;
+}
+
+static int dst_i2c_enable(struct dst_state *state)
+{
+	int retval;
+	/* pull I2C enable gpio pin low, wait */
+	// dprintk ("%s: i2c enable\n", __FUNCTION__);
+	retval = dst_gpio_outb(state, ~0, DST_I2C_ENABLE, 0);
+	if (retval < 0)
+		return retval;
+	// dprintk ("%s: i2c enable delay\n", __FUNCTION__);
+	msleep(33);
+	return 0;
+}
+
+static int dst_i2c_disable(struct dst_state *state)
+{
+	int retval;
+	/* release I2C enable gpio pin, wait */
+	// dprintk ("%s: i2c disable\n", __FUNCTION__);
+	retval = dst_gpio_outb(state, ~0, 0, 0);
+	if (retval < 0)
+		return retval;
+	// dprintk ("%s: i2c disable delay\n", __FUNCTION__);
+	msleep(33);
+	return 0;
+}
+
+static int dst_wait_dst_ready(struct dst_state *state)
+{
+	u8 reply;
+	int retval;
+	int i;
+	for (i = 0; i < 200; i++) {
+		retval = dst_gpio_inb(state, &reply);
+		if (retval < 0)
+			return retval;
+		if ((reply & DST_I2C_ENABLE) == 0) {
+			dprintk("%s: dst wait ready after %d\n", __FUNCTION__, i);
+			return 1;
+		}
+		msleep(10);
+	}
+	dprintk("%s: dst wait NOT ready after %d\n", __FUNCTION__, i);
+	return 0;
+}
+
+static int write_dst(struct dst_state *state, u8 * data, u8 len)
+{
+	struct i2c_msg msg = {
+		.addr = state->config->demod_address,.flags = 0,.buf = data,.len = len
+	};
+	int err;
+	int cnt;
+
+	if (dst_debug && dst_verbose) {
+		u8 i;
+		dprintk("%s writing", __FUNCTION__);
+		for (i = 0; i < len; i++) {
+			dprintk(" 0x%02x", data[i]);
+		}
+		dprintk("\n");
+	}
+	msleep(30);
+	for (cnt = 0; cnt < 4; cnt++) {
+		if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+			dprintk("%s: write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, data[0]);
+			dst_i2c_disable(state);
+			msleep(500);
+			dst_i2c_enable(state);
+			msleep(500);
+			continue;
+		} else
+			break;
+	}
+	if (cnt >= 4)
+		return -EREMOTEIO;
+	return 0;
+}
+
+static int read_dst(struct dst_state *state, u8 * ret, u8 len)
+{
+	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = ret,.len = len };
+	int err;
+	int cnt;
+
+	for (cnt = 0; cnt < 4; cnt++) {
+		if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) {
+			dprintk("%s: read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)\n", __FUNCTION__, err, len, ret[0]);
+			dst_i2c_disable(state);
+			dst_i2c_enable(state);
+			continue;
+		} else
+			break;
+	}
+	if (cnt >= 4)
+		return -EREMOTEIO;
+	dprintk("%s reply is 0x%x\n", __FUNCTION__, ret[0]);
+	if (dst_debug && dst_verbose) {
+		for (err = 1; err < len; err++)
+			dprintk(" 0x%x", ret[err]);
+		if (err > 1)
+			dprintk("\n");
+	}
+	return 0;
+}
+
+static int dst_set_freq(struct dst_state *state, u32 freq)
+{
+	u8 *val;
+
+	state->frequency = freq;
+
+	// dprintk("%s: set frequency %u\n", __FUNCTION__, freq);
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		freq = freq / 1000;
+		if (freq < 950 || freq > 2150)
+			return -EINVAL;
+		val = &state->tx_tuna[0];
+		val[2] = (freq >> 8) & 0x7f;
+		val[3] = (u8) freq;
+		val[4] = 1;
+		val[8] &= ~4;
+		if (freq < 1531)
+			val[8] |= 4;
+	} else if (state->dst_type == DST_TYPE_IS_TERR) {
+		freq = freq / 1000;
+		if (freq < 137000 || freq > 858000)
+			return -EINVAL;
+		val = &state->tx_tuna[0];
+		val[2] = (freq >> 16) & 0xff;
+		val[3] = (freq >> 8) & 0xff;
+		val[4] = (u8) freq;
+		val[5] = 0;
+		switch (state->bandwidth) {
+		case BANDWIDTH_6_MHZ:
+			val[6] = 6;
+			break;
+
+		case BANDWIDTH_7_MHZ:
+		case BANDWIDTH_AUTO:
+			val[6] = 7;
+			break;
+
+		case BANDWIDTH_8_MHZ:
+			val[6] = 8;
+			break;
+		}
+
+		val[7] = 0;
+		val[8] = 0;
+	} else if (state->dst_type == DST_TYPE_IS_CABLE) {
+		/* guess till will get one */
+		freq = freq / 1000;
+		val = &state->tx_tuna[0];
+		val[2] = (freq >> 16) & 0xff;
+		val[3] = (freq >> 8) & 0xff;
+		val[4] = (u8) freq;
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+static int dst_set_bandwidth(struct dst_state* state, fe_bandwidth_t bandwidth)
+{
+	u8 *val;
+
+	state->bandwidth = bandwidth;
+
+	if (state->dst_type != DST_TYPE_IS_TERR)
+		return 0;
+
+	val = &state->tx_tuna[0];
+	switch (bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		val[6] = 6;
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		val[6] = 7;
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		val[6] = 8;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dst_set_inversion(struct dst_state* state, fe_spectral_inversion_t inversion)
+{
+	u8 *val;
+
+	state->inversion = inversion;
+
+	val = &state->tx_tuna[0];
+
+	val[8] &= ~0x80;
+
+	switch (inversion) {
+	case INVERSION_OFF:
+		break;
+	case INVERSION_ON:
+		val[8] |= 0x80;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dst_set_fec(struct dst_state* state, fe_code_rate_t fec)
+{
+	state->fec = fec;
+	return 0;
+}
+
+static fe_code_rate_t dst_get_fec(struct dst_state* state)
+{
+	return state->fec;
+}
+
+static int dst_set_symbolrate(struct dst_state* state, u32 srate)
+{
+	u8 *val;
+	u32 symcalc;
+	u64 sval;
+
+	state->symbol_rate = srate;
+
+	if (state->dst_type == DST_TYPE_IS_TERR) {
+		return 0;
+	}
+	// dprintk("%s: set srate %u\n", __FUNCTION__, srate);
+	srate /= 1000;
+	val = &state->tx_tuna[0];
+
+	if (state->type_flags & DST_TYPE_HAS_SYMDIV) {
+		sval = srate;
+		sval <<= 20;
+		do_div(sval, 88000);
+		symcalc = (u32) sval;
+		// dprintk("%s: set symcalc %u\n", __FUNCTION__, symcalc);
+		val[5] = (u8) (symcalc >> 12);
+		val[6] = (u8) (symcalc >> 4);
+		val[7] = (u8) (symcalc << 4);
+	} else {
+		val[5] = (u8) (srate >> 16) & 0x7f;
+		val[6] = (u8) (srate >> 8);
+		val[7] = (u8) srate;
+	}
+	val[8] &= ~0x20;
+	if (srate > 8000)
+		val[8] |= 0x20;
+	return 0;
+}
+
+static u8 dst_check_sum(u8 * buf, u32 len)
+{
+	u32 i;
+	u8 val = 0;
+	if (!len)
+		return 0;
+	for (i = 0; i < len; i++) {
+		val += buf[i];
+	}
+	return ((~val) + 1);
+}
+
+struct dst_types {
+	char *mstr;
+	int offs;
+	u8 dst_type;
+	u32 type_flags;
+};
+
+static struct dst_types dst_tlist[] = {
+	{"DST-020", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV},
+	{"DST-030", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE},
+	{"DST-03T", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204},
+	{"DST-MOT", 0, DST_TYPE_IS_SAT, DST_TYPE_HAS_SYMDIV},
+	{"DST-CI",  1, DST_TYPE_IS_SAT, DST_TYPE_HAS_TS204 | DST_TYPE_HAS_NEWTUNE},
+	{"DSTMCI",  1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE},
+	{"DSTFCI",  1, DST_TYPE_IS_SAT, DST_TYPE_HAS_NEWTUNE},
+	{"DCTNEW",  1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE},
+	{"DCT-CI",  1, DST_TYPE_IS_CABLE, DST_TYPE_HAS_NEWTUNE | DST_TYPE_HAS_TS204},
+	{"DTTDIG",  1, DST_TYPE_IS_TERR, 0}
+};
+
+/* DCTNEW and DCT-CI are guesses */
+
+static void dst_type_flags_print(u32 type_flags)
+{
+	printk("DST type flags :");
+	if (type_flags & DST_TYPE_HAS_NEWTUNE)
+		printk(" 0x%x newtuner", DST_TYPE_HAS_NEWTUNE);
+	if (type_flags & DST_TYPE_HAS_TS204)
+		printk(" 0x%x ts204", DST_TYPE_HAS_TS204);
+	if (type_flags & DST_TYPE_HAS_SYMDIV)
+		printk(" 0x%x symdiv", DST_TYPE_HAS_SYMDIV);
+	printk("\n");
+}
+
+static int dst_type_print(u8 type)
+{
+	char *otype;
+	switch (type) {
+	case DST_TYPE_IS_SAT:
+		otype = "satellite";
+		break;
+	case DST_TYPE_IS_TERR:
+		otype = "terrestrial";
+		break;
+	case DST_TYPE_IS_CABLE:
+		otype = "cable";
+		break;
+	default:
+		printk("%s: invalid dst type %d\n", __FUNCTION__, type);
+		return -EINVAL;
+	}
+	printk("DST type : %s\n", otype);
+	return 0;
+}
+
+static int dst_check_ci(struct dst_state *state)
+{
+	u8 txbuf[8];
+	u8 rxbuf[8];
+	int retval;
+	int i;
+	struct dst_types *dsp;
+	u8 use_dst_type;
+	u32 use_type_flags;
+
+	memset(txbuf, 0, sizeof(txbuf));
+	txbuf[1] = 6;
+	txbuf[7] = dst_check_sum(txbuf, 7);
+
+	dst_i2c_enable(state);
+	dst_reset8820(state);
+	retval = write_dst(state, txbuf, 8);
+	if (retval < 0) {
+		dst_i2c_disable(state);
+		dprintk("%s: write not successful, maybe no card?\n", __FUNCTION__);
+		return retval;
+	}
+	msleep(3);
+	retval = read_dst(state, rxbuf, 1);
+	dst_i2c_disable(state);
+	if (retval < 0) {
+		dprintk("%s: read not successful, maybe no card?\n", __FUNCTION__);
+		return retval;
+	}
+	if (rxbuf[0] != 0xff) {
+		dprintk("%s: write reply not 0xff, not ci (%02x)\n", __FUNCTION__, rxbuf[0]);
+		return retval;
+	}
+	if (!dst_wait_dst_ready(state))
+		return 0;
+	// dst_i2c_enable(i2c); Dimitri
+	retval = read_dst(state, rxbuf, 8);
+	dst_i2c_disable(state);
+	if (retval < 0) {
+		dprintk("%s: read not successful\n", __FUNCTION__);
+		return retval;
+	}
+	if (rxbuf[7] != dst_check_sum(rxbuf, 7)) {
+		dprintk("%s: checksum failure\n", __FUNCTION__);
+		return retval;
+	}
+	rxbuf[7] = '\0';
+	for (i = 0, dsp = &dst_tlist[0]; i < sizeof(dst_tlist) / sizeof(dst_tlist[0]); i++, dsp++) {
+		if (!strncmp(&rxbuf[dsp->offs], dsp->mstr, strlen(dsp->mstr))) {
+			use_type_flags = dsp->type_flags;
+			use_dst_type = dsp->dst_type;
+			printk("%s: recognize %s\n", __FUNCTION__, dsp->mstr);
+			break;
+		}
+	}
+	if (i >= sizeof(dst_tlist) / sizeof(dst_tlist[0])) {
+		printk("%s: unable to recognize %s or %s\n", __FUNCTION__, &rxbuf[0], &rxbuf[1]);
+		printk("%s please email linux-dvb@linuxtv.org with this type in\n", __FUNCTION__);
+		use_dst_type = DST_TYPE_IS_SAT;
+		use_type_flags = DST_TYPE_HAS_SYMDIV;
+	}
+	dst_type_print(use_dst_type);
+
+	state->type_flags = use_type_flags;
+	state->dst_type = use_dst_type;
+	dst_type_flags_print(state->type_flags);
+
+	if (state->type_flags & DST_TYPE_HAS_TS204) {
+		dst_packsize(state, 204);
+	}
+	return 0;
+}
+
+static int dst_command(struct dst_state* state, u8 * data, u8 len)
+{
+	int retval;
+	u8 reply;
+
+	dst_i2c_enable(state);
+	dst_reset8820(state);
+	retval = write_dst(state, data, len);
+	if (retval < 0) {
+		dst_i2c_disable(state);
+		dprintk("%s: write not successful\n", __FUNCTION__);
+		return retval;
+	}
+	msleep(33);
+	retval = read_dst(state, &reply, 1);
+	dst_i2c_disable(state);
+	if (retval < 0) {
+		dprintk("%s: read verify  not successful\n", __FUNCTION__);
+		return retval;
+	}
+	if (reply != 0xff) {
+		dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply);
+		return 0;
+	}
+	if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3))
+		return 0;
+	if (!dst_wait_dst_ready(state))
+		return 0;
+	// dst_i2c_enable(i2c); Per dimitri
+	retval = read_dst(state, state->rxbuffer, 8);
+	dst_i2c_disable(state);
+	if (retval < 0) {
+		dprintk("%s: read not successful\n", __FUNCTION__);
+		return 0;
+	}
+	if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) {
+		dprintk("%s: checksum failure\n", __FUNCTION__);
+		return 0;
+	}
+	return 0;
+}
+
+static int dst_get_signal(struct dst_state* state)
+{
+	int retval;
+	u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb };
+
+	if ((state->diseq_flags & ATTEMPT_TUNE) == 0) {
+		state->decode_lock = state->decode_strength = state->decode_snr = 0;
+		return 0;
+	}
+	if (0 == (state->diseq_flags & HAS_LOCK)) {
+		state->decode_lock = state->decode_strength = state->decode_snr = 0;
+		return 0;
+	}
+	if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) {
+		retval = dst_command(state, get_signal, 8);
+		if (retval < 0)
+			return retval;
+		if (state->dst_type == DST_TYPE_IS_SAT) {
+			state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0;
+			state->decode_strength = state->rxbuffer[5] << 8;
+			state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3];
+		} else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) {
+			state->decode_lock = (state->rxbuffer[1]) ? 1 : 0;
+			state->decode_strength = state->rxbuffer[4] << 8;
+			state->decode_snr = state->rxbuffer[3] << 8;
+		}
+		state->cur_jiff = jiffies;
+	}
+	return 0;
+}
+
+static int dst_tone_power_cmd(struct dst_state* state)
+{
+	u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 };
+
+	if (state->dst_type == DST_TYPE_IS_TERR)
+		return 0;
+
+	if (state->voltage == SEC_VOLTAGE_OFF)
+		paket[4] = 0;
+	else
+		paket[4] = 1;
+	if (state->tone == SEC_TONE_ON)
+		paket[2] = state->k22;
+	else
+		paket[2] = 0;
+	paket[7] = dst_check_sum(&paket[0], 7);
+	dst_command(state, paket, 8);
+	return 0;
+}
+
+static int dst_get_tuna(struct dst_state* state)
+{
+	int retval;
+	if ((state->diseq_flags & ATTEMPT_TUNE) == 0)
+		return 0;
+	state->diseq_flags &= ~(HAS_LOCK);
+	if (!dst_wait_dst_ready(state))
+		return 0;
+	if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+		/* how to get variable length reply ???? */
+		retval = read_dst(state, state->rx_tuna, 10);
+	} else {
+		retval = read_dst(state, &state->rx_tuna[2], 8);
+	}
+	if (retval < 0) {
+		dprintk("%s: read not successful\n", __FUNCTION__);
+		return 0;
+	}
+	if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+		if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) {
+			dprintk("%s: checksum failure?\n", __FUNCTION__);
+			return 0;
+		}
+	} else {
+		if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) {
+			dprintk("%s: checksum failure?\n", __FUNCTION__);
+			return 0;
+		}
+	}
+	if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0)
+		return 0;
+	state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3];
+
+	state->decode_lock = 1;
+	/*
+	   dst->decode_n1 = (dst->rx_tuna[4] << 8) +
+	   (dst->rx_tuna[5]);
+
+	   dst->decode_n2 = (dst->rx_tuna[8] << 8) +
+	   (dst->rx_tuna[7]);
+	 */
+	state->diseq_flags |= HAS_LOCK;
+	/* dst->cur_jiff = jiffies; */
+	return 1;
+}
+
+static int dst_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+
+static int dst_write_tuna(struct dvb_frontend* fe)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+	int retval;
+	u8 reply;
+
+	dprintk("%s: type_flags 0x%x \n", __FUNCTION__, state->type_flags);
+	state->decode_freq = 0;
+	state->decode_lock = state->decode_strength = state->decode_snr = 0;
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		if (!(state->diseq_flags & HAS_POWER))
+			dst_set_voltage(fe, SEC_VOLTAGE_13);
+	}
+	state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE);
+	dst_i2c_enable(state);
+	if (state->type_flags & DST_TYPE_HAS_NEWTUNE) {
+		dst_reset8820(state);
+		state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9);
+		retval = write_dst(state, &state->tx_tuna[0], 10);
+	} else {
+		state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7);
+		retval = write_dst(state, &state->tx_tuna[2], 8);
+	}
+	if (retval < 0) {
+		dst_i2c_disable(state);
+		dprintk("%s: write not successful\n", __FUNCTION__);
+		return retval;
+	}
+	msleep(3);
+	retval = read_dst(state, &reply, 1);
+	dst_i2c_disable(state);
+	if (retval < 0) {
+		dprintk("%s: read verify  not successful\n", __FUNCTION__);
+		return retval;
+	}
+	if (reply != 0xff) {
+		dprintk("%s: write reply not 0xff 0x%02x \n", __FUNCTION__, reply);
+		return 0;
+	}
+	state->diseq_flags |= ATTEMPT_TUNE;
+	return dst_get_tuna(state);
+}
+
+/*
+ * line22k0    0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k1    0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00
+ * line22k2    0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00
+ * tone        0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00
+ * data        0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00
+ * power_off   0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00
+ * power_on    0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00
+ * Diseqc 1    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec
+ * Diseqc 2    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8
+ * Diseqc 3    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4
+ * Diseqc 4    0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0
+ */
+
+static int dst_set_diseqc(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+	u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec };
+
+	if (state->dst_type == DST_TYPE_IS_TERR)
+		return 0;
+
+	if (cmd->msg_len == 0 || cmd->msg_len > 4)
+		return -EINVAL;
+	memcpy(&paket[3], cmd->msg, cmd->msg_len);
+	paket[7] = dst_check_sum(&paket[0], 7);
+	dst_command(state, paket, 8);
+	return 0;
+}
+
+static int dst_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	u8 *val;
+	int need_cmd;
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+	state->voltage = voltage;
+
+	if (state->dst_type == DST_TYPE_IS_TERR)
+		return 0;
+
+	need_cmd = 0;
+	val = &state->tx_tuna[0];
+	val[8] &= ~0x40;
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		if ((state->diseq_flags & HAS_POWER) == 0)
+			need_cmd = 1;
+		state->diseq_flags |= HAS_POWER;
+		break;
+	case SEC_VOLTAGE_18:
+		if ((state->diseq_flags & HAS_POWER) == 0)
+			need_cmd = 1;
+		state->diseq_flags |= HAS_POWER;
+		val[8] |= 0x40;
+		break;
+	case SEC_VOLTAGE_OFF:
+		need_cmd = 1;
+		state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (need_cmd) {
+		dst_tone_power_cmd(state);
+	}
+	return 0;
+}
+
+static int dst_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	u8 *val;
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+	state->tone = tone;
+
+	if (state->dst_type == DST_TYPE_IS_TERR)
+		return 0;
+
+	val = &state->tx_tuna[0];
+
+	val[8] &= ~0x1;
+
+	switch (tone) {
+	case SEC_TONE_OFF:
+		break;
+	case SEC_TONE_ON:
+		val[8] |= 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	dst_tone_power_cmd(state);
+	return 0;
+}
+
+static int dst_init(struct dvb_frontend* fe)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+	static u8 ini_satci_tuna[] = { 9, 0, 3, 0xb6, 1, 0, 0x73, 0x21, 0, 0 };
+	static u8 ini_satfta_tuna[] = { 0, 0, 3, 0xb6, 1, 0x55, 0xbd, 0x50, 0, 0 };
+	static u8 ini_tvfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+	static u8 ini_tvci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+	static u8 ini_cabfta_tuna[] = { 0, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+	static u8 ini_cabci_tuna[] = { 9, 0, 3, 0xb6, 1, 7, 0x0, 0x0, 0, 0 };
+	state->inversion = INVERSION_ON;
+	state->voltage = SEC_VOLTAGE_13;
+	state->tone = SEC_TONE_OFF;
+	state->symbol_rate = 29473000;
+	state->fec = FEC_AUTO;
+	state->diseq_flags = 0;
+	state->k22 = 0x02;
+	state->bandwidth = BANDWIDTH_7_MHZ;
+	state->cur_jiff = jiffies;
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		state->frequency = 950000;
+		memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_satci_tuna : ini_satfta_tuna), sizeof(ini_satfta_tuna));
+	} else if (state->dst_type == DST_TYPE_IS_TERR) {
+		state->frequency = 137000000;
+		memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_tvci_tuna : ini_tvfta_tuna), sizeof(ini_tvfta_tuna));
+	} else if (state->dst_type == DST_TYPE_IS_CABLE) {
+		state->frequency = 51000000;
+		memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_NEWTUNE) ? ini_cabci_tuna : ini_cabfta_tuna), sizeof(ini_cabfta_tuna));
+	}
+
+	return 0;
+}
+
+static int dst_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+	*status = 0;
+	if (state->diseq_flags & HAS_LOCK) {
+		dst_get_signal(state);
+		if (state->decode_lock)
+			*status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI;
+	}
+
+	return 0;
+}
+
+static int dst_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+	dst_get_signal(state);
+	*strength = state->decode_strength;
+
+	return 0;
+}
+
+static int dst_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+	dst_get_signal(state);
+	*snr = state->decode_snr;
+
+	return 0;
+}
+
+static int dst_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+	dst_set_freq(state, p->frequency);
+	dst_set_inversion(state, p->inversion);
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		dst_set_fec(state, p->u.qpsk.fec_inner);
+		dst_set_symbolrate(state, p->u.qpsk.symbol_rate);
+	} else if (state->dst_type == DST_TYPE_IS_TERR) {
+		dst_set_bandwidth(state, p->u.ofdm.bandwidth);
+	} else if (state->dst_type == DST_TYPE_IS_CABLE) {
+		dst_set_fec(state, p->u.qam.fec_inner);
+		dst_set_symbolrate(state, p->u.qam.symbol_rate);
+	}
+	dst_write_tuna(fe);
+
+	return 0;
+}
+
+static int dst_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+
+	p->frequency = state->decode_freq;
+	p->inversion = state->inversion;
+	if (state->dst_type == DST_TYPE_IS_SAT) {
+		p->u.qpsk.symbol_rate = state->symbol_rate;
+		p->u.qpsk.fec_inner = dst_get_fec(state);
+	} else if (state->dst_type == DST_TYPE_IS_TERR) {
+		p->u.ofdm.bandwidth = state->bandwidth;
+	} else if (state->dst_type == DST_TYPE_IS_CABLE) {
+		p->u.qam.symbol_rate = state->symbol_rate;
+		p->u.qam.fec_inner = dst_get_fec(state);
+		p->u.qam.modulation = QAM_AUTO;
+	}
+
+	return 0;
+}
+
+static void dst_release(struct dvb_frontend* fe)
+{
+	struct dst_state* state = (struct dst_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops dst_dvbt_ops;
+static struct dvb_frontend_ops dst_dvbs_ops;
+static struct dvb_frontend_ops dst_dvbc_ops;
+
+struct dvb_frontend* dst_attach(const struct dst_config* config,
+				struct i2c_adapter* i2c,
+				struct bt878 *bt)
+{
+	struct dst_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct dst_state*) kmalloc(sizeof(struct dst_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	state->bt = bt;
+
+	/* check if the demod is there */
+	if (dst_check_ci(state) < 0) goto error;
+
+	/* determine settings based on type */
+	switch (state->dst_type) {
+	case DST_TYPE_IS_TERR:
+		memcpy(&state->ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops));
+		break;
+	case DST_TYPE_IS_CABLE:
+		memcpy(&state->ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops));
+		break;
+	case DST_TYPE_IS_SAT:
+		memcpy(&state->ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops));
+		break;
+	default:
+		printk("dst: unknown frontend type. please report to the LinuxTV.org DVB mailinglist.\n");
+		goto error;
+	}
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops dst_dvbt_ops = {
+
+	.info = {
+		.name = "DST DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 137000000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166667,
+		.caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+	},
+
+	.release = dst_release,
+
+	.init = dst_init,
+
+	.set_frontend = dst_set_frontend,
+	.get_frontend = dst_get_frontend,
+
+	.read_status = dst_read_status,
+	.read_signal_strength = dst_read_signal_strength,
+	.read_snr = dst_read_snr,
+};
+
+static struct dvb_frontend_ops dst_dvbs_ops = {
+
+	.info = {
+		.name = "DST DVB-S",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1000,	/* kHz for QPSK frontends */
+		.frequency_tolerance = 29500,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+	/*     . symbol_rate_tolerance	=	???,*/
+		.caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK
+	},
+
+	.release = dst_release,
+
+	.init = dst_init,
+
+	.set_frontend = dst_set_frontend,
+	.get_frontend = dst_get_frontend,
+
+	.read_status = dst_read_status,
+	.read_signal_strength = dst_read_signal_strength,
+	.read_snr = dst_read_snr,
+
+	.diseqc_send_master_cmd = dst_set_diseqc,
+	.set_voltage = dst_set_voltage,
+	.set_tone = dst_set_tone,
+};
+
+static struct dvb_frontend_ops dst_dvbc_ops = {
+
+	.info = {
+		.name = "DST DVB-C",
+		.type = FE_QAM,
+		.frequency_stepsize = 62500,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+	/*     . symbol_rate_tolerance	=	???,*/
+		.caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO
+	},
+
+	.release = dst_release,
+
+	.init = dst_init,
+
+	.set_frontend = dst_set_frontend,
+	.get_frontend = dst_get_frontend,
+
+	.read_status = dst_read_status,
+	.read_signal_strength = dst_read_signal_strength,
+	.read_snr = dst_read_snr,
+};
+
+MODULE_DESCRIPTION("DST DVB-S/T/C Combo Frontend driver");
+MODULE_AUTHOR("Jamie Honan");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dst_attach);
diff --git a/drivers/media/dvb/bt8xx/dst.h b/drivers/media/dvb/bt8xx/dst.h
new file mode 100644
index 0000000..bcb418c
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dst.h
@@ -0,0 +1,40 @@
+/*
+    Frontend-driver for TwinHan DST Frontend
+
+    Copyright (C) 2003 Jamie Honan
+
+    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 DST_H
+#define DST_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/device.h>
+#include "bt878.h"
+
+struct dst_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+};
+
+extern struct dvb_frontend* dst_attach(const struct dst_config* config,
+				       struct i2c_adapter* i2c,
+				       struct bt878 *bt);
+
+#endif // DST_H
diff --git a/drivers/media/dvb/bt8xx/dst_priv.h b/drivers/media/dvb/bt8xx/dst_priv.h
new file mode 100644
index 0000000..80488aa
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dst_priv.h
@@ -0,0 +1,36 @@
+/*
+ * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend
+ *
+ * Copyright (C) 2003 Jamie Honan
+ */
+
+struct dst_gpio_enable {
+	u32	mask;
+	u32	enable;
+};
+
+struct dst_gpio_output {
+	u32	mask;
+	u32	highvals;
+};
+
+struct dst_gpio_read {
+	unsigned long value;
+};
+
+union dst_gpio_packet {
+	struct dst_gpio_enable enb;
+	struct dst_gpio_output outp;
+	struct dst_gpio_read rd;
+	int    psize;
+};
+
+#define DST_IG_ENABLE	0
+#define DST_IG_WRITE	1
+#define DST_IG_READ	2
+#define DST_IG_TS       3
+
+struct bt878;
+
+int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp);
+
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
new file mode 100644
index 0000000..b735397
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -0,0 +1,797 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+
+#include "dvb-bt8xx.h"
+
+#include "bt878.h"
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk( args... ) \
+	do { \
+		if (debug) printk(KERN_DEBUG args); \
+	} while (0)
+
+static void dvb_bt8xx_task(unsigned long data)
+{
+	struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data;
+
+	//printk("%d ", card->bt->finished_block);
+
+	while (card->bt->last_block != card->bt->finished_block) {
+		(card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter)
+			(&card->demux,
+			 &card->bt->buf_cpu[card->bt->last_block *
+					    card->bt->block_bytes],
+			 card->bt->block_bytes);
+		card->bt->last_block = (card->bt->last_block + 1) %
+					card->bt->block_count;
+	}
+}
+
+static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct dvb_bt8xx_card *card = dvbdmx->priv;
+	int rc;
+
+	dprintk("dvb_bt8xx: start_feed\n");
+
+	if (!dvbdmx->dmx.frontend)
+		return -EINVAL;
+
+	down(&card->lock);
+	card->nfeeds++;
+	rc = card->nfeeds;
+	if (card->nfeeds == 1)
+		bt878_start(card->bt, card->gpio_mode,
+			    card->op_sync_orin, card->irq_err_ignore);
+	up(&card->lock);
+	return rc;
+}
+
+static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct dvb_bt8xx_card *card = dvbdmx->priv;
+
+	dprintk("dvb_bt8xx: stop_feed\n");
+
+	if (!dvbdmx->dmx.frontend)
+		return -EINVAL;
+
+	down(&card->lock);
+	card->nfeeds--;
+	if (card->nfeeds == 0)
+		bt878_stop(card->bt);
+	up(&card->lock);
+
+	return 0;
+}
+
+static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev)
+{
+	if ((adev->subsystem_vendor == bdev->subsystem_vendor) &&
+		(adev->subsystem_device == bdev->subsystem_device) &&
+		(adev->bus->number == bdev->bus->number) &&
+		(PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn)))
+		return 1;
+	return 0;
+}
+
+static struct bt878 __init *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev)
+{
+	unsigned int card_nr;
+
+	/* Hmm, n squared. Hope n is small */
+	for (card_nr = 0; card_nr < bt878_num; card_nr++) {
+		if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev))
+			return &bt878[card_nr];
+	}
+	return NULL;
+}
+
+
+static int thomson_dtt7579_demod_init(struct dvb_frontend* fe)
+{
+	static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 };
+	static u8 mt352_reset [] = { 0x50, 0x80 };
+	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 };
+	static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 };
+	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+	mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+        mt352_write(fe, mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
+	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+	return 0;
+}
+
+static int thomson_dtt7579_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+	u32 div;
+	unsigned char bs = 0;
+	unsigned char cp = 0;
+
+	#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+	div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+	if (params->frequency < 542000000) cp = 0xb4;
+	else if (params->frequency < 771000000) cp = 0xbc;
+	else cp = 0xf4;
+
+        if (params->frequency == 0) bs = 0x03;
+	else if (params->frequency < 443250000) bs = 0x02;
+	else bs = 0x08;
+
+	pllbuf[0] = 0xc0; // Note: non-linux standard PLL i2c address
+	pllbuf[1] = div >> 8;
+	pllbuf[2] = div & 0xff;
+	pllbuf[3] = cp;
+	pllbuf[4] = bs;
+
+	return 0;
+}
+
+static struct mt352_config thomson_dtt7579_config = {
+
+	.demod_address = 0x0f,
+	.demod_init = thomson_dtt7579_demod_init,
+	.pll_set = thomson_dtt7579_pll_set,
+};
+
+static int cx24108_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+   u32 freq = params->frequency;
+
+   int i, a, n, pump;
+   u32 band, pll;
+
+
+   u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000,
+               1576000,1718000,1856000,2036000,2150000};
+   u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000,
+               0x00102000,0x00104000,0x00108000,0x00110000,
+               0x00120000,0x00140000};
+
+#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
+        printk("cx24108 debug: entering SetTunerFreq, freq=%d\n",freq);
+
+        /* This is really the bit driving the tuner chip cx24108 */
+
+        if(freq<950000) freq=950000; /* kHz */
+        if(freq>2150000) freq=2150000; /* satellite IF is 950..2150MHz */
+
+        /* decide which VCO to use for the input frequency */
+        for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++);
+        printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
+        band=bandsel[i];
+        /* the gain values must be set by SetSymbolrate */
+        /* compute the pll divider needed, from Conexant data sheet,
+           resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
+           depending on the divider bit. It is set to /4 on the 2 lowest
+           bands  */
+        n=((i<=2?2:1)*freq*10L)/(XTAL/100);
+        a=n%32; n/=32; if(a==0) n--;
+        pump=(freq<(osci[i-1]+osci[i])/2);
+        pll=0xf8000000|
+            ((pump?1:2)<<(14+11))|
+            ((n&0x1ff)<<(5+11))|
+            ((a&0x1f)<<11);
+        /* everything is shifted left 11 bits to left-align the bits in the
+           32bit word. Output to the tuner goes MSB-aligned, after all */
+        printk("cx24108 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
+        cx24110_pll_write(fe,band);
+        /* set vga and vca to their widest-band settings, as a precaution.
+           SetSymbolrate might not be called to set this up */
+        cx24110_pll_write(fe,0x500c0000);
+        cx24110_pll_write(fe,0x83f1f800);
+        cx24110_pll_write(fe,pll);
+/*        writereg(client,0x56,0x7f);*/
+
+	return 0;
+}
+
+static int pinnsat_pll_init(struct dvb_frontend* fe)
+{
+   return 0;
+}
+
+
+static struct cx24110_config pctvsat_config = {
+
+	.demod_address = 0x55,
+	.pll_init = pinnsat_pll_init,
+	.pll_set = cx24108_pll_set,
+};
+
+
+static int microtune_mt7202dtf_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
+	u8 cfg, cpump, band_select;
+	u8 data[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (36000000 + params->frequency + 83333) / 166666;
+	cfg = 0x88;
+
+	if (params->frequency < 175000000) cpump = 2;
+	else if (params->frequency < 390000000) cpump = 1;
+	else if (params->frequency < 470000000) cpump = 2;
+	else if (params->frequency < 750000000) cpump = 2;
+	else cpump = 3;
+
+	if (params->frequency < 175000000) band_select = 0x0e;
+	else if (params->frequency < 470000000) band_select = 0x05;
+	else band_select = 0x03;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = ((div >> 10) & 0x60) | cfg;
+	data[3] = cpump | band_select;
+
+	i2c_transfer(card->i2c_adapter, &msg, 1);
+	return (div * 166666 - 36000000);
+}
+
+static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+	struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
+
+	return request_firmware(fw, name, &bt->bt->dev->dev);
+}
+
+static struct sp887x_config microtune_mt7202dtf_config = {
+
+	.demod_address = 0x70,
+	.pll_set = microtune_mt7202dtf_pll_set,
+	.request_firmware = microtune_mt7202dtf_request_firmware,
+};
+
+
+
+static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe)
+{
+	static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d };
+	static u8 mt352_reset [] = { 0x50, 0x80 };
+	static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
+	                               0x00, 0xFF, 0x00, 0x40, 0x40 };
+	static u8 mt352_av771_extra[] = { 0xB5, 0x7A };
+	static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
+
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+
+	mt352_write(fe, mt352_agc_cfg,sizeof(mt352_agc_cfg));
+	udelay(2000);
+	mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra));
+	mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
+
+	return 0;
+}
+
+static int advbt771_samsung_tdtc9251dh0_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf)
+{
+	u32 div;
+	unsigned char bs = 0;
+	unsigned char cp = 0;
+
+	#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+	div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
+
+	if (params->frequency < 150000000) cp = 0xB4;
+	else if (params->frequency < 173000000) cp = 0xBC;
+	else if (params->frequency < 250000000) cp = 0xB4;
+	else if (params->frequency < 400000000) cp = 0xBC;
+	else if (params->frequency < 420000000) cp = 0xF4;
+	else if (params->frequency < 470000000) cp = 0xFC;
+	else if (params->frequency < 600000000) cp = 0xBC;
+	else if (params->frequency < 730000000) cp = 0xF4;
+	else cp = 0xFC;
+
+	if (params->frequency < 150000000) bs = 0x01;
+	else if (params->frequency < 173000000) bs = 0x01;
+	else if (params->frequency < 250000000) bs = 0x02;
+	else if (params->frequency < 400000000) bs = 0x02;
+	else if (params->frequency < 420000000) bs = 0x02;
+	else if (params->frequency < 470000000) bs = 0x02;
+	else if (params->frequency < 600000000) bs = 0x08;
+	else if (params->frequency < 730000000) bs = 0x08;
+	else bs = 0x08;
+
+	pllbuf[0] = 0xc2; // Note: non-linux standard PLL i2c address
+	pllbuf[1] = div >> 8;
+	pllbuf[2] = div & 0xff;
+	pllbuf[3] = cp;
+	pllbuf[4] = bs;
+
+	return 0;
+}
+
+static struct mt352_config advbt771_samsung_tdtc9251dh0_config = {
+
+	.demod_address = 0x0f,
+	.demod_init = advbt771_samsung_tdtc9251dh0_demod_init,
+	.pll_set = advbt771_samsung_tdtc9251dh0_pll_set,
+};
+
+
+static struct dst_config dst_config = {
+
+	.demod_address = 0x55,
+};
+
+
+static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+	struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv;
+
+	return request_firmware(fw, name, &bt->bt->dev->dev);
+}
+
+static void or51211_setmode(struct dvb_frontend * fe, int mode)
+{
+	struct dvb_bt8xx_card *bt = fe->dvb->priv;
+	bttv_write_gpio(bt->bttv_nr, 0x0002, mode);   /* Reset */
+	msleep(20);
+}
+
+static void or51211_reset(struct dvb_frontend * fe)
+{
+	struct dvb_bt8xx_card *bt = fe->dvb->priv;
+
+	/* RESET DEVICE
+	 * reset is controled by GPIO-0
+	 * when set to 0 causes reset and when to 1 for normal op
+	 * must remain reset for 128 clock cycles on a 50Mhz clock
+	 * also PRM1 PRM2 & PRM4 are controled by GPIO-1,GPIO-2 & GPIO-4
+	 * We assume that the reset has be held low long enough or we
+	 * have been reset by a power on.  When the driver is unloaded
+	 * reset set to 0 so if reloaded we have been reset.
+	 */
+	/* reset & PRM1,2&4 are outputs */
+	int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F);
+	if (ret != 0) {
+		printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR "
+		       "(%i)\n", ret);
+	}
+	bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000);   /* Reset */
+	msleep(20);
+	/* Now set for normal operation */
+	bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001);
+	/* wait for operation to begin */
+	msleep(500);
+}
+
+static void or51211_sleep(struct dvb_frontend * fe)
+{
+	struct dvb_bt8xx_card *bt = fe->dvb->priv;
+	bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000);
+}
+
+static struct or51211_config or51211_config = {
+
+	.demod_address = 0x15,
+	.request_firmware = or51211_request_firmware,
+	.setmode = or51211_setmode,
+	.reset = or51211_reset,
+	.sleep = or51211_sleep,
+};
+
+
+static int vp3021_alps_tded4_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv;
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) };
+
+	div = (params->frequency + 36166667) / 166667;
+
+	buf[0] = (div >> 8) & 0x7F;
+	buf[1] = div & 0xFF;
+	buf[2] = 0x85;
+	if ((params->frequency >= 47000000) && (params->frequency < 153000000))
+		buf[3] = 0x01;
+	else if ((params->frequency >= 153000000) && (params->frequency < 430000000))
+		buf[3] = 0x02;
+	else if ((params->frequency >= 430000000) && (params->frequency < 824000000))
+		buf[3] = 0x0C;
+	else if ((params->frequency >= 824000000) && (params->frequency < 863000000))
+		buf[3] = 0x8C;
+	else
+		return -EINVAL;
+
+	i2c_transfer(card->i2c_adapter, &msg, 1);
+	return 0;
+}
+
+static struct nxt6000_config vp3021_alps_tded4_config = {
+
+	.demod_address = 0x0a,
+	.clock_inversion = 1,
+	.pll_set = vp3021_alps_tded4_pll_set,
+};
+
+
+static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
+{
+	switch(type) {
+#ifdef BTTV_DVICO_DVBT_LITE
+	case BTTV_DVICO_DVBT_LITE:
+		card->fe = mt352_attach(&thomson_dtt7579_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			card->fe->ops->info.frequency_min = 174000000;
+			card->fe->ops->info.frequency_max = 862000000;
+			break;
+		}
+		break;
+#endif
+
+#ifdef BTTV_TWINHAN_VP3021
+	case BTTV_TWINHAN_VP3021:
+#else
+	case BTTV_NEBULA_DIGITV:
+#endif
+		card->fe = nxt6000_attach(&vp3021_alps_tded4_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			break;
+		}
+		break;
+
+	case BTTV_AVDVBT_761:
+		card->fe = sp887x_attach(&microtune_mt7202dtf_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			break;
+		}
+		break;
+
+	case BTTV_AVDVBT_771:
+		card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			card->fe->ops->info.frequency_min = 174000000;
+			card->fe->ops->info.frequency_max = 862000000;
+			break;
+		}
+		break;
+
+	case BTTV_TWINHAN_DST:
+		card->fe = dst_attach(&dst_config, card->i2c_adapter, card->bt);
+		if (card->fe != NULL) {
+			break;
+		}
+		break;
+
+	case BTTV_PINNACLESAT:
+		card->fe = cx24110_attach(&pctvsat_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			break;
+		}
+		break;
+
+	case BTTV_PC_HDTV:
+		card->fe = or51211_attach(&or51211_config, card->i2c_adapter);
+		if (card->fe != NULL) {
+			break;
+		}
+		break;
+	}
+
+	if (card->fe == NULL) {
+		printk("dvb-bt8xx: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		       card->bt->dev->vendor,
+		       card->bt->dev->device,
+		       card->bt->dev->subsystem_vendor,
+		       card->bt->dev->subsystem_device);
+	} else {
+		if (dvb_register_frontend(card->dvb_adapter, card->fe)) {
+			printk("dvb-bt8xx: Frontend registration failed!\n");
+			if (card->fe->ops->release)
+				card->fe->ops->release(card->fe);
+			card->fe = NULL;
+		}
+	}
+}
+
+static int __init dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type)
+{
+	int result;
+
+	if ((result = dvb_register_adapter(&card->dvb_adapter, card->card_name,
+					   THIS_MODULE)) < 0) {
+		printk("dvb_bt8xx: dvb_register_adapter failed (errno = %d)\n", result);
+		return result;
+
+	}
+	card->dvb_adapter->priv = card;
+
+	card->bt->adapter = card->i2c_adapter;
+
+	memset(&card->demux, 0, sizeof(struct dvb_demux));
+
+	card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING;
+
+	card->demux.priv = card;
+	card->demux.filternum = 256;
+	card->demux.feednum = 256;
+	card->demux.start_feed = dvb_bt8xx_start_feed;
+	card->demux.stop_feed = dvb_bt8xx_stop_feed;
+	card->demux.write_to_decoder = NULL;
+
+	if ((result = dvb_dmx_init(&card->demux)) < 0) {
+		printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+		dvb_unregister_adapter(card->dvb_adapter);
+		return result;
+	}
+
+	card->dmxdev.filternum = 256;
+	card->dmxdev.demux = &card->demux.dmx;
+	card->dmxdev.capabilities = 0;
+
+	if ((result = dvb_dmxdev_init(&card->dmxdev, card->dvb_adapter)) < 0) {
+		printk("dvb_bt8xx: dvb_dmxdev_init failed (errno = %d)\n", result);
+
+		dvb_dmx_release(&card->demux);
+		dvb_unregister_adapter(card->dvb_adapter);
+		return result;
+	}
+
+	card->fe_hw.source = DMX_FRONTEND_0;
+
+	if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw)) < 0) {
+		printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+		dvb_dmxdev_release(&card->dmxdev);
+		dvb_dmx_release(&card->demux);
+		dvb_unregister_adapter(card->dvb_adapter);
+		return result;
+	}
+
+	card->fe_mem.source = DMX_MEMORY_FE;
+
+	if ((result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem)) < 0) {
+		printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+		card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+		dvb_dmxdev_release(&card->dmxdev);
+		dvb_dmx_release(&card->demux);
+		dvb_unregister_adapter(card->dvb_adapter);
+		return result;
+	}
+
+	if ((result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw)) < 0) {
+		printk("dvb_bt8xx: dvb_dmx_init failed (errno = %d)\n", result);
+
+		card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
+		card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+		dvb_dmxdev_release(&card->dmxdev);
+		dvb_dmx_release(&card->demux);
+		dvb_unregister_adapter(card->dvb_adapter);
+		return result;
+	}
+
+	dvb_net_init(card->dvb_adapter, &card->dvbnet, &card->demux.dmx);
+
+	tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card);
+
+	frontend_init(card, type);
+
+	return 0;
+}
+
+static int dvb_bt8xx_probe(struct device *dev)
+{
+	struct bttv_sub_device *sub = to_bttv_sub_dev(dev);
+	struct dvb_bt8xx_card *card;
+	struct pci_dev* bttv_pci_dev;
+	int ret;
+
+	if (!(card = kmalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(card, 0, sizeof(*card));
+	init_MUTEX(&card->lock);
+	card->bttv_nr = sub->core->nr;
+	strncpy(card->card_name, sub->core->name, sizeof(sub->core->name));
+	card->i2c_adapter = &sub->core->i2c_adap;
+
+	switch(sub->core->type)
+	{
+	case BTTV_PINNACLESAT:
+		card->gpio_mode = 0x0400c060;
+		/* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR,
+		              BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */
+		card->op_sync_orin = 0;
+		card->irq_err_ignore = 0;
+		break;
+
+#ifdef BTTV_DVICO_DVBT_LITE
+	case BTTV_DVICO_DVBT_LITE:
+#endif
+		card->gpio_mode = 0x0400C060;
+		card->op_sync_orin = 0;
+		card->irq_err_ignore = 0;
+		/* 26, 15, 14, 6, 5
+		 * A_PWRDN  DA_DPM DA_SBR DA_IOM_DA
+		 * DA_APP(parallel) */
+		break;
+
+#ifdef BTTV_TWINHAN_VP3021
+	case BTTV_TWINHAN_VP3021:
+#else
+	case BTTV_NEBULA_DIGITV:
+#endif
+	case BTTV_AVDVBT_761:
+		card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5);
+		card->op_sync_orin = 0;
+		card->irq_err_ignore = 0;
+		/* A_PWRDN DA_SBR DA_APP (high speed serial) */
+		break;
+
+	case BTTV_AVDVBT_771: //case 0x07711461:
+		card->gpio_mode = 0x0400402B;
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = 0;
+		/* A_PWRDN DA_SBR  DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/
+		break;
+
+	case BTTV_TWINHAN_DST:
+		card->gpio_mode = 0x2204f2c;
+		card->op_sync_orin = BT878_RISC_SYNC_MASK;
+		card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR |
+				       BT878_APPERR | BT878_AFBUS;
+		/* 25,21,14,11,10,9,8,3,2 then
+		 * 0x33 = 5,4,1,0
+		 * A_SEL=SML, DA_MLB, DA_SBR,
+		 * DA_SDR=f, fifo trigger = 32 DWORDS
+		 * IOM = 0 == audio A/D
+		 * DPM = 0 == digital audio mode
+		 * == async data parallel port
+		 * then 0x33 (13 is set by start_capture)
+		 * DA_APP = async data parallel port,
+		 * ACAP_EN = 1,
+		 * RISC+FIFO ENABLE */
+		break;
+
+	case BTTV_PC_HDTV:
+		card->gpio_mode = 0x0100EC7B;
+		card->op_sync_orin = 0;
+		card->irq_err_ignore = 0;
+		break;
+
+	default:
+		printk(KERN_WARNING "dvb_bt8xx: Unknown bttv card type: %d.\n",
+				sub->core->type);
+		kfree(card);
+		return -ENODEV;
+	}
+
+	dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name);
+
+	if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) {
+		printk("dvb_bt8xx: no pci device for card %d\n", card->bttv_nr);
+		kfree(card);
+		return -EFAULT;
+	}
+
+	if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) {
+		printk("dvb_bt8xx: unable to determine DMA core of card %d,\n",
+		       card->bttv_nr);
+		printk("dvb_bt8xx: if you have the ALSA bt87x audio driver "
+		       "installed, try removing it.\n");
+
+		kfree(card);
+		return -EFAULT;
+
+	}
+
+	init_MUTEX(&card->bt->gpio_lock);
+	card->bt->bttv_nr = sub->core->nr;
+
+	if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) {
+		kfree(card);
+		return ret;
+	}
+
+	dev_set_drvdata(dev, card);
+	return 0;
+}
+
+static int dvb_bt8xx_remove(struct device *dev)
+{
+	struct dvb_bt8xx_card *card = dev_get_drvdata(dev);
+
+	dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr);
+
+	bt878_stop(card->bt);
+	tasklet_kill(&card->bt->tasklet);
+	dvb_net_release(&card->dvbnet);
+	card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem);
+	card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
+	dvb_dmxdev_release(&card->dmxdev);
+	dvb_dmx_release(&card->demux);
+	if (card->fe) dvb_unregister_frontend(card->fe);
+	dvb_unregister_adapter(card->dvb_adapter);
+
+	kfree(card);
+
+	return 0;
+}
+
+static struct bttv_sub_driver driver = {
+	.drv = {
+		.name		= "dvb-bt8xx",
+		.probe		= dvb_bt8xx_probe,
+		.remove		= dvb_bt8xx_remove,
+		/* FIXME:
+		 * .shutdown	= dvb_bt8xx_shutdown,
+		 * .suspend	= dvb_bt8xx_suspend,
+		 * .resume	= dvb_bt8xx_resume,
+		 */
+	},
+};
+
+static int __init dvb_bt8xx_init(void)
+{
+	return bttv_sub_register(&driver, "dvb");
+}
+
+static void __exit dvb_bt8xx_exit(void)
+{
+	bttv_sub_unregister(&driver);
+}
+
+module_init(dvb_bt8xx_init);
+module_exit(dvb_bt8xx_exit);
+
+MODULE_DESCRIPTION("Bt8xx based DVB adapter driver");
+MODULE_AUTHOR("Florian Schirmer <jolt@tuxbox.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
new file mode 100644
index 0000000..80ef189
--- /dev/null
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h
@@ -0,0 +1,59 @@
+/*
+ * Bt8xx based DVB adapter driver
+ *
+ * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
+ * Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de>
+ * Copyright (C) 1999-2001 Ralph  Metzler & Marcus Metzler for convergence integrated media GmbH
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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; 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 DVB_BT8XX_H
+#define DVB_BT8XX_H
+
+#include <linux/i2c.h>
+#include "dvbdev.h"
+#include "dvb_net.h"
+#include "bttv.h"
+#include "mt352.h"
+#include "sp887x.h"
+#include "dst.h"
+#include "nxt6000.h"
+#include "cx24110.h"
+#include "or51211.h"
+
+struct dvb_bt8xx_card {
+	struct semaphore lock;
+	int nfeeds;
+	char card_name[32];
+	struct dvb_adapter *dvb_adapter;
+	struct bt878 *bt;
+	unsigned int bttv_nr;
+	struct dvb_demux demux;
+	struct dmxdev dmxdev;
+	struct dmx_frontend fe_hw;
+	struct dmx_frontend fe_mem;
+	u32 gpio_mode;
+	u32 op_sync_orin;
+	u32 irq_err_ignore;
+	struct i2c_adapter *i2c_adapter;
+	struct dvb_net dvbnet;
+
+	struct dvb_frontend* fe;
+};
+
+#endif /* DVB_BT8XX_H */
diff --git a/drivers/media/dvb/cinergyT2/Kconfig b/drivers/media/dvb/cinergyT2/Kconfig
new file mode 100644
index 0000000..2267140
--- /dev/null
+++ b/drivers/media/dvb/cinergyT2/Kconfig
@@ -0,0 +1,85 @@
+config DVB_CINERGYT2
+	tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver"
+	depends on DVB_CORE && USB
+	help
+	  Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
+
+	  Say Y if you own such a device and want to use it.
+
+
+config DVB_CINERGYT2_TUNING
+	bool "sophisticated fine-tuning for CinergyT2 cards"
+	depends on DVB_CINERGYT2
+	help
+	  Here you can fine-tune some parameters of the CinergyT2 driver.
+
+	  Normally you don't need to touch this, but in exotic setups you
+	  may fine-tune your setup and adjust e.g. DMA buffer sizes for
+	  a particular application.
+
+
+config DVB_CINERGYT2_STREAM_URB_COUNT
+	int "Number of queued USB Request Blocks for Highspeed Stream Transfers"
+	depends on DVB_CINERGYT2_TUNING
+        default "32"
+	help
+	  USB Request Blocks for Highspeed Stream transfers are scheduled in
+	  a queue for the Host Controller.
+
+	  Usually the default value is a safe choice.
+
+	  You may increase this number if you are using this device in a 
+	  Server Environment with many high-traffic USB Highspeed devices 
+	  sharing the same USB bus.
+
+
+config DVB_CINERGYT2_STREAM_BUF_SIZE
+	int "Size of URB Stream Buffers for Highspeed Transfers"
+	depends on DVB_CINERGYT2_TUNING
+        default "512"
+	help
+	  Should be a multiple of native buffer size of 512 bytes.
+	  Default value is a safe choice.
+
+	  You may increase this number if you are using this device in a 
+	  Server Environment with many high-traffic USB Highspeed devices 
+	  sharing the same USB bus.
+
+
+config DVB_CINERGYT2_QUERY_INTERVAL
+	int "Status update interval [milliseconds]"
+	depends on DVB_CINERGYT2_TUNING
+        default "250"
+	help
+	  This is the interval for status readouts from the demodulator.
+	  You may try lower values if you need more responsive signal quality
+	  measurements.
+
+	  Please keep in mind that these updates cause traffic on the tuner
+	  control bus and thus may or may not affect receiption sensitivity.
+
+	  The default value should be a safe choice for common applications.
+
+
+config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+	bool "Register the onboard IR Remote Control Receiver as Input Device"
+	depends on DVB_CINERGYT2_TUNING
+        default "yes"
+	help
+	  Enable this option if you want to use the onboard Infrared Remote 
+	  Control Receiver as Linux-Input device.
+
+	  Right now only the keycode table for the default Remote Control
+	  delivered with the device is supported, please see the driver
+	  source code to find out how to add support for other controls.
+
+
+config DVB_CINERGYT2_RC_QUERY_INTERVAL
+	int "Infrared Remote Controller update interval [milliseconds]"
+	depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+        default "100"
+	help
+	  If you have a very fast-repeating remote control you can try lower
+	  values, for normal consumer receivers the default value should be
+	  a safe choice.
+
diff --git a/drivers/media/dvb/cinergyT2/Makefile b/drivers/media/dvb/cinergyT2/Makefile
new file mode 100644
index 0000000..c51aece
--- /dev/null
+++ b/drivers/media/dvb/cinergyT2/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
new file mode 100644
index 0000000..f1f5397
--- /dev/null
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -0,0 +1,965 @@
+/*
+ * TerraTec Cinergy T²/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ *		    Holger Waechtler <holger@qanu.de>
+ *
+ *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * 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/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/pci.h>
+#include <linux/input.h>
+#include <linux/dvb/frontend.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+
+
+#ifdef CONFIG_DVB_CINERGYT2_TUNING
+	#define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT)
+	#define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE)
+	#define QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_QUERY_INTERVAL)
+	#ifdef CONFIG_DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
+		#define RC_QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_RC_QUERY_INTERVAL)
+		#define ENABLE_RC (1)
+	#endif
+#else
+	#define STREAM_URB_COUNT (32)
+	#define STREAM_BUF_SIZE (512)	/* bytes */
+	#define ENABLE_RC (1)
+	#define RC_QUERY_INTERVAL (100)	/* milliseconds */
+	#define QUERY_INTERVAL (333)	/* milliseconds */
+#endif
+
+#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
+
+static int debug;
+module_param_named(debug, debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(level, args...)						\
+do {									\
+	if ((debug & level)) {						\
+		printk("%s: %s(): ", __stringify(KBUILD_MODNAME),	\
+		       __FUNCTION__);					\
+		printk(args); }						\
+} while (0)
+
+enum cinergyt2_ep1_cmd {
+	CINERGYT2_EP1_PID_TABLE_RESET		= 0x01,
+	CINERGYT2_EP1_PID_SETUP			= 0x02,
+	CINERGYT2_EP1_CONTROL_STREAM_TRANSFER	= 0x03,
+	CINERGYT2_EP1_SET_TUNER_PARAMETERS	= 0x04,
+	CINERGYT2_EP1_GET_TUNER_STATUS		= 0x05,
+	CINERGYT2_EP1_START_SCAN		= 0x06,
+	CINERGYT2_EP1_CONTINUE_SCAN		= 0x07,
+	CINERGYT2_EP1_GET_RC_EVENTS		= 0x08,
+	CINERGYT2_EP1_SLEEP_MODE		= 0x09
+};
+
+struct dvbt_set_parameters_msg {
+	uint8_t cmd;
+	uint32_t freq;
+	uint8_t bandwidth;
+	uint16_t tps;
+	uint8_t flags;
+} __attribute__((packed));
+
+struct dvbt_get_status_msg {
+	uint32_t freq;
+	uint8_t bandwidth;
+	uint16_t tps;
+	uint8_t flags;
+	uint16_t gain;
+	uint8_t snr;
+	uint32_t viterbi_error_rate;
+	uint32_t rs_error_rate;
+	uint32_t uncorrected_block_count;
+	uint8_t lock_bits;
+	uint8_t prev_lock_bits;
+} __attribute__((packed));
+
+static struct dvb_frontend_info cinergyt2_fe_info = {
+	.name = DRIVER_NAME,
+	.type = FE_OFDM,
+	.frequency_min = 174000000,
+	.frequency_max = 862000000,
+	.frequency_stepsize = 166667,
+	.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_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS
+};
+
+struct cinergyt2 {
+	struct dvb_demux demux;
+	struct usb_device *udev;
+	struct semaphore sem;
+	struct dvb_adapter *adapter;
+	struct dvb_device *fedev;
+	struct dmxdev dmxdev;
+	struct dvb_net dvbnet;
+
+	int streaming;
+	int sleeping;
+
+	struct dvbt_set_parameters_msg param;
+	struct dvbt_get_status_msg status;
+	struct work_struct query_work;
+
+	wait_queue_head_t poll_wq;
+	int pending_fe_events;
+
+	void *streambuf;
+	dma_addr_t streambuf_dmahandle;
+	struct urb *stream_urb [STREAM_URB_COUNT];
+
+#ifdef ENABLE_RC
+	struct input_dev rc_input_dev;
+	struct work_struct rc_query_work;
+	int rc_input_event;
+#endif
+};
+
+enum {
+	CINERGYT2_RC_EVENT_TYPE_NONE = 0x00,
+	CINERGYT2_RC_EVENT_TYPE_NEC  = 0x01,
+	CINERGYT2_RC_EVENT_TYPE_RC5  = 0x02
+};
+
+struct cinergyt2_rc_event {
+	char type;
+	uint32_t value;
+} __attribute__((packed));
+
+static const uint32_t rc_keys [] = {
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfe01eb04,	KEY_POWER,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfd02eb04,	KEY_1,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfc03eb04,	KEY_2,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfb04eb04,	KEY_3,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfa05eb04,	KEY_4,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf906eb04,	KEY_5,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf807eb04,	KEY_6,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf708eb04,	KEY_7,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf609eb04,	KEY_8,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf50aeb04,	KEY_9,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf30ceb04,	KEY_0,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf40beb04,	KEY_VIDEO,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf20deb04,	KEY_REFRESH,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf10eeb04,	KEY_SELECT,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf00feb04,	KEY_EPG,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xef10eb04,	KEY_UP,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xeb14eb04,	KEY_DOWN,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xee11eb04,	KEY_LEFT,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xec13eb04,	KEY_RIGHT,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xed12eb04,	KEY_OK,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xea15eb04,	KEY_TEXT,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe916eb04,	KEY_INFO,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe817eb04,	KEY_RED,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe718eb04,	KEY_GREEN,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe619eb04,	KEY_YELLOW,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe51aeb04,	KEY_BLUE,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe31ceb04,	KEY_VOLUMEUP,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe11eeb04,	KEY_VOLUMEDOWN,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe21deb04,	KEY_MUTE,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe41beb04,	KEY_CHANNELUP,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe01feb04,	KEY_CHANNELDOWN,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xbf40eb04,	KEY_PAUSE,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xb34ceb04,	KEY_PLAY,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xa758eb04,	KEY_RECORD,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xab54eb04,	KEY_PREVIOUS,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xb748eb04,	KEY_STOP,
+	CINERGYT2_RC_EVENT_TYPE_NEC,	0xa35ceb04,	KEY_NEXT
+};
+
+static int cinergyt2_command (struct cinergyt2 *cinergyt2,
+			      char *send_buf, int send_buf_len,
+			      char *recv_buf, int recv_buf_len)
+{
+	int actual_len;
+	char dummy;
+	int ret;
+
+	ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1),
+			   send_buf, send_buf_len, &actual_len, 1000);
+
+	if (ret)
+		dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret);
+
+	if (!recv_buf)
+		recv_buf = &dummy;
+
+	ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1),
+			   recv_buf, recv_buf_len, &actual_len, 1000);
+
+	if (ret)
+		dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret);
+
+	return ret ? ret : actual_len;
+}
+
+static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable)
+{
+	char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
+	cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
+}
+
+static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep)
+{
+	char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 };
+	cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
+	cinergyt2->sleeping = sleep;
+}
+
+static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs);
+
+static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb)
+{
+	int err;
+
+	usb_fill_bulk_urb(urb,
+			  cinergyt2->udev,
+			  usb_rcvbulkpipe(cinergyt2->udev, 0x2),
+			  urb->transfer_buffer,
+			  STREAM_BUF_SIZE,
+			  cinergyt2_stream_irq,
+			  cinergyt2);
+
+	if ((err = usb_submit_urb(urb, GFP_ATOMIC)))
+		dprintk(1, "urb submission failed (err = %i)!\n", err);
+
+	return err;
+}
+
+static void cinergyt2_stream_irq (struct urb *urb, struct pt_regs *regs)
+{
+	struct cinergyt2 *cinergyt2 = urb->context;
+
+	if (urb->actual_length > 0)
+		dvb_dmx_swfilter(&cinergyt2->demux,
+				 urb->transfer_buffer, urb->actual_length);
+
+	if (cinergyt2->streaming)
+		cinergyt2_submit_stream_urb(cinergyt2, urb);
+}
+
+static void cinergyt2_free_stream_urbs (struct cinergyt2 *cinergyt2)
+{
+	int i;
+
+	for (i=0; i<STREAM_URB_COUNT; i++)
+		if (cinergyt2->stream_urb[i])
+			usb_free_urb(cinergyt2->stream_urb[i]);
+
+	pci_free_consistent(NULL, STREAM_URB_COUNT*STREAM_BUF_SIZE,
+			    cinergyt2->streambuf, cinergyt2->streambuf_dmahandle);
+}
+
+static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2)
+{
+	int i;
+
+	cinergyt2->streambuf = pci_alloc_consistent(NULL,
+					      STREAM_URB_COUNT*STREAM_BUF_SIZE,
+					      &cinergyt2->streambuf_dmahandle);
+	if (!cinergyt2->streambuf) {
+		dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n");
+		return -ENOMEM;
+	}
+
+	memset(cinergyt2->streambuf, 0, STREAM_URB_COUNT*STREAM_BUF_SIZE);
+
+	for (i=0; i<STREAM_URB_COUNT; i++) {
+		struct urb *urb;
+
+		if (!(urb = usb_alloc_urb(0, GFP_ATOMIC))) {
+			dprintk(1, "failed to alloc consistent stream urbs, bailing out!\n");
+			cinergyt2_free_stream_urbs(cinergyt2);
+			return -ENOMEM;
+		}
+
+		urb->transfer_buffer = cinergyt2->streambuf + i * STREAM_BUF_SIZE;
+		urb->transfer_buffer_length = STREAM_BUF_SIZE;
+
+		cinergyt2->stream_urb[i] = urb;
+	}
+
+	return 0;
+}
+
+static void cinergyt2_stop_stream_xfer (struct cinergyt2 *cinergyt2)
+{
+	int i;
+
+	cinergyt2_control_stream_transfer(cinergyt2, 0);
+
+	for (i=0; i<STREAM_URB_COUNT; i++)
+		if (cinergyt2->stream_urb[i])
+			usb_kill_urb(cinergyt2->stream_urb[i]);
+}
+
+static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2)
+{
+	int i, err;
+
+	for (i=0; i<STREAM_URB_COUNT; i++) {
+		if ((err = cinergyt2_submit_stream_urb(cinergyt2, cinergyt2->stream_urb[i]))) {
+			cinergyt2_stop_stream_xfer(cinergyt2);
+			dprintk(1, "failed urb submission (%i: err = %i)!\n", i, err);
+			return err;
+		}
+	}
+
+	cinergyt2_control_stream_transfer(cinergyt2, 1);
+	return 0;
+}
+
+static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *demux = dvbdmxfeed->demux;
+	struct cinergyt2 *cinergyt2 = demux->priv;
+
+	if (down_interruptible(&cinergyt2->sem))
+		return -ERESTARTSYS;
+
+	if (cinergyt2->streaming == 0)
+		cinergyt2_start_stream_xfer(cinergyt2);
+
+	cinergyt2->streaming++;
+	up(&cinergyt2->sem);
+	return 0;
+}
+
+static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *demux = dvbdmxfeed->demux;
+	struct cinergyt2 *cinergyt2 = demux->priv;
+
+	if (down_interruptible(&cinergyt2->sem))
+		return -ERESTARTSYS;
+
+	if (--cinergyt2->streaming == 0)
+		cinergyt2_stop_stream_xfer(cinergyt2);
+
+	up(&cinergyt2->sem);
+	return 0;
+}
+
+/**
+ *  convert linux-dvb frontend parameter set into TPS.
+ *  See ETSI ETS-300744, section 4.6.2, table 9 for details.
+ *
+ *  This function is probably reusable and may better get placed in a support
+ *  library.
+ *
+ *  We replace errornous fields by default TPS fields (the ones with value 0).
+ */
+static uint16_t compute_tps (struct dvb_frontend_parameters *p)
+{
+	struct dvb_ofdm_parameters *op = &p->u.ofdm;
+	uint16_t tps = 0;
+
+	switch (op->code_rate_HP) {
+		case FEC_2_3:
+			tps |= (1 << 7);
+			break;
+		case FEC_3_4:
+			tps |= (2 << 7);
+			break;
+		case FEC_5_6:
+			tps |= (3 << 7);
+			break;
+		case FEC_7_8:
+			tps |= (4 << 7);
+			break;
+		case FEC_1_2:
+		case FEC_AUTO:
+		default:
+			/* tps |= (0 << 7) */;
+	}
+
+	switch (op->code_rate_LP) {
+		case FEC_2_3:
+			tps |= (1 << 4);
+			break;
+		case FEC_3_4:
+			tps |= (2 << 4);
+			break;
+		case FEC_5_6:
+			tps |= (3 << 4);
+			break;
+		case FEC_7_8:
+			tps |= (4 << 4);
+			break;
+		case FEC_1_2:
+		case FEC_AUTO:
+		default:
+			/* tps |= (0 << 4) */;
+	}
+
+	switch (op->constellation) {
+		case QAM_16:
+			tps |= (1 << 13);
+			break;
+		case QAM_64:
+			tps |= (2 << 13);
+			break;
+		case QPSK:
+		default:
+			/* tps |= (0 << 13) */;
+	}
+
+	switch (op->transmission_mode) {
+		case TRANSMISSION_MODE_8K:
+			tps |= (1 << 0);
+			break;
+		case TRANSMISSION_MODE_2K:
+		default:
+			/* tps |= (0 << 0) */;
+	}
+
+	switch (op->guard_interval) {
+		case GUARD_INTERVAL_1_16:
+			tps |= (1 << 2);
+			break;
+		case GUARD_INTERVAL_1_8:
+			tps |= (2 << 2);
+			break;
+		case GUARD_INTERVAL_1_4:
+			tps |= (3 << 2);
+			break;
+		case GUARD_INTERVAL_1_32:
+		default:
+			/* tps |= (0 << 2) */;
+	}
+
+	switch (op->hierarchy_information) {
+		case HIERARCHY_1:
+			tps |= (1 << 10);
+			break;
+		case HIERARCHY_2:
+			tps |= (2 << 10);
+			break;
+		case HIERARCHY_4:
+			tps |= (3 << 10);
+			break;
+		case HIERARCHY_NONE:
+		default:
+			/* tps |= (0 << 10) */;
+	}
+
+	return tps;
+}
+
+static int cinergyt2_open (struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct cinergyt2 *cinergyt2 = dvbdev->priv;
+	int err;
+
+	if ((err = dvb_generic_open(inode, file)))
+		return err;
+
+	if (down_interruptible(&cinergyt2->sem))
+		return -ERESTARTSYS;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		cinergyt2_sleep(cinergyt2, 0);
+		schedule_delayed_work(&cinergyt2->query_work, HZ/2);
+	}
+
+	up(&cinergyt2->sem);
+	return 0;
+}
+
+static int cinergyt2_release (struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct cinergyt2 *cinergyt2 = dvbdev->priv;
+
+	if (down_interruptible(&cinergyt2->sem))
+		return -ERESTARTSYS;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		cancel_delayed_work(&cinergyt2->query_work);
+		flush_scheduled_work();
+		cinergyt2_sleep(cinergyt2, 1);
+	}
+
+	up(&cinergyt2->sem);
+
+	return dvb_generic_release(inode, file);
+}
+
+static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct cinergyt2 *cinergyt2 = dvbdev->priv;
+	poll_wait(file, &cinergyt2->poll_wq, wait);
+	return (POLLIN | POLLRDNORM | POLLPRI);
+}
+
+
+static int cinergyt2_ioctl (struct inode *inode, struct file *file,
+		     unsigned cmd, unsigned long arg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct cinergyt2 *cinergyt2 = dvbdev->priv;
+	struct dvbt_get_status_msg *stat = &cinergyt2->status;
+	fe_status_t status = 0;
+
+	switch (cmd) {
+	case FE_GET_INFO:
+		return copy_to_user((void __user*) arg, &cinergyt2_fe_info,
+				    sizeof(struct dvb_frontend_info));
+
+	case FE_READ_STATUS:
+		if (0xffff - le16_to_cpu(stat->gain) > 30)
+			status |= FE_HAS_SIGNAL;
+		if (stat->lock_bits & (1 << 6))
+			status |= FE_HAS_LOCK;
+		if (stat->lock_bits & (1 << 5))
+			status |= FE_HAS_SYNC;
+		if (stat->lock_bits & (1 << 4))
+			status |= FE_HAS_CARRIER;
+		if (stat->lock_bits & (1 << 1))
+			status |= FE_HAS_VITERBI;
+
+		return copy_to_user((void  __user*) arg, &status, sizeof(status));
+
+	case FE_READ_BER:
+		return put_user(le32_to_cpu(stat->viterbi_error_rate),
+				(__u32 __user *) arg);
+
+	case FE_READ_SIGNAL_STRENGTH:
+		return put_user(0xffff - le16_to_cpu(stat->gain),
+				(__u16 __user *) arg);
+
+	case FE_READ_SNR:
+		return put_user((stat->snr << 8) | stat->snr,
+				(__u16 __user *) arg);
+
+	case FE_READ_UNCORRECTED_BLOCKS:
+		/* UNC are already converted to host byte order... */
+		return put_user(stat->uncorrected_block_count,
+				(__u32 __user *) arg);
+
+	case FE_SET_FRONTEND:
+	{
+		struct dvbt_set_parameters_msg *param = &cinergyt2->param;
+		struct dvb_frontend_parameters p;
+		int err;
+
+		if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+			return -EPERM;
+
+		if (copy_from_user(&p, (void  __user*) arg, sizeof(p)))
+			return -EFAULT;
+
+		if (down_interruptible(&cinergyt2->sem))
+			return -ERESTARTSYS;
+
+		param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+		param->tps = cpu_to_le16(compute_tps(&p));
+		param->freq = cpu_to_le32(p.frequency / 1000);
+		param->bandwidth = 8 - p.u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+
+		stat->lock_bits = 0;
+		cinergyt2->pending_fe_events++;
+		wake_up_interruptible(&cinergyt2->poll_wq);
+
+		err = cinergyt2_command(cinergyt2,
+					(char *) param, sizeof(*param),
+					NULL, 0);
+
+		up(&cinergyt2->sem);
+
+		return (err < 0) ? err : 0;
+	}
+
+	case FE_GET_FRONTEND:
+		/**
+		 *  trivial to implement (see struct dvbt_get_status_msg).
+		 *  equivalent to FE_READ ioctls, but needs
+		 *  TPS -> linux-dvb parameter set conversion. Feel free
+		 *  to implement this and send us a patch if you need this
+		 *  functionality.
+		 */
+		break;
+
+	case FE_GET_EVENT:
+	{
+		/**
+		 *  for now we only fill the status field. the parameters
+		 *  are trivial to fill as soon FE_GET_FRONTEND is done.
+		 */
+		struct dvb_frontend_event __user *e = (void __user *) arg;
+		if (cinergyt2->pending_fe_events == 0) {
+			if (file->f_flags & O_NONBLOCK)
+				return -EWOULDBLOCK;
+			wait_event_interruptible(cinergyt2->poll_wq,
+						 cinergyt2->pending_fe_events > 0);
+		}
+		cinergyt2->pending_fe_events = 0;
+		return cinergyt2_ioctl(inode, file, FE_READ_STATUS,
+					(unsigned long) &e->status);
+	}
+
+	default:
+		;
+	}
+
+	return -EINVAL;
+}
+
+static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct cinergyt2 *cinergyt2 = dvbdev->priv;
+	int ret = 0;
+
+	lock_kernel();
+
+	if (vma->vm_flags & (VM_WRITE | VM_EXEC)) {
+		ret = -EPERM;
+		goto bailout;
+	}
+
+	if (vma->vm_end > vma->vm_start + STREAM_URB_COUNT * STREAM_BUF_SIZE) {
+		ret = -EINVAL;
+		goto bailout;
+	}
+
+	vma->vm_flags |= (VM_IO | VM_DONTCOPY);
+	vma->vm_file = file;
+
+	ret = remap_pfn_range(vma, vma->vm_start,
+			      virt_to_phys(cinergyt2->streambuf) >> PAGE_SHIFT,
+			      vma->vm_end - vma->vm_start,
+			      vma->vm_page_prot) ? -EAGAIN : 0;
+bailout:
+	unlock_kernel();
+	return ret;
+}
+
+static struct file_operations cinergyt2_fops = {
+	.owner          = THIS_MODULE,
+	.ioctl		= cinergyt2_ioctl,
+	.poll           = cinergyt2_poll,
+	.open           = cinergyt2_open,
+	.release        = cinergyt2_release,
+	.mmap		= cinergyt2_mmap
+};
+
+static struct dvb_device cinergyt2_fe_template = {
+	.users = ~0,
+	.writers = 1,
+	.readers = (~0)-1,
+	.fops = &cinergyt2_fops
+};
+
+#ifdef ENABLE_RC
+static void cinergyt2_query_rc (void *data)
+{
+	struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data;
+	char buf [1] = { CINERGYT2_EP1_GET_RC_EVENTS };
+	struct cinergyt2_rc_event rc_events[12];
+	int n, len;
+
+	if (down_interruptible(&cinergyt2->sem))
+		return;
+
+	len = cinergyt2_command(cinergyt2, buf, sizeof(buf),
+			     (char *) rc_events, sizeof(rc_events));
+
+	for (n=0; len>0 && n<(len/sizeof(rc_events[0])); n++) {
+		int i;
+
+		if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC &&
+		    rc_events[n].value == ~0)
+		{
+			/**
+			 * keyrepeat bit. If we would handle this properly
+			 * we would need to emit down events as long the
+			 * keyrepeat goes, a up event if no further
+			 * repeat bits occur. Would need a timer to implement
+			 * and no other driver does this, so we simply
+			 * emit the last key up/down sequence again.
+			 */
+		} else {
+			cinergyt2->rc_input_event = KEY_MAX;
+			for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3) {
+				if (rc_keys[i+0] == rc_events[n].type &&
+				    rc_keys[i+1] == rc_events[n].value)
+				{
+					cinergyt2->rc_input_event = rc_keys[i+2];
+					break;
+				}
+			}
+		}
+
+		if (cinergyt2->rc_input_event != KEY_MAX) {
+			input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 1);
+			input_report_key(&cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 0);
+			input_sync(&cinergyt2->rc_input_dev);
+		}
+	}
+
+	schedule_delayed_work(&cinergyt2->rc_query_work,
+			      msecs_to_jiffies(RC_QUERY_INTERVAL));
+
+	up(&cinergyt2->sem);
+}
+#endif
+
+static void cinergyt2_query (void *data)
+{
+	struct cinergyt2 *cinergyt2 = (struct cinergyt2 *) data;
+	char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+	struct dvbt_get_status_msg *s = &cinergyt2->status;
+	uint8_t lock_bits;
+	uint32_t unc;
+
+	if (down_interruptible(&cinergyt2->sem))
+		return;
+
+	unc = s->uncorrected_block_count;
+	lock_bits = s->lock_bits;
+
+	cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s));
+
+	unc += le32_to_cpu(s->uncorrected_block_count);
+	s->uncorrected_block_count = unc;
+
+	if (lock_bits != s->lock_bits) {
+		wake_up_interruptible(&cinergyt2->poll_wq);
+		cinergyt2->pending_fe_events++;
+	}
+
+	schedule_delayed_work(&cinergyt2->query_work,
+			      msecs_to_jiffies(QUERY_INTERVAL));
+
+	up(&cinergyt2->sem);
+}
+
+static int cinergyt2_probe (struct usb_interface *intf,
+		  const struct usb_device_id *id)
+{
+	struct cinergyt2 *cinergyt2;
+	int i, err;
+
+	if (!(cinergyt2 = kmalloc (sizeof(struct cinergyt2), GFP_KERNEL))) {
+		dprintk(1, "out of memory?!?\n");
+		return -ENOMEM;
+	}
+
+	memset (cinergyt2, 0, sizeof (struct cinergyt2));
+	usb_set_intfdata (intf, (void *) cinergyt2);
+
+	init_MUTEX(&cinergyt2->sem);
+	init_waitqueue_head (&cinergyt2->poll_wq);
+	INIT_WORK(&cinergyt2->query_work, cinergyt2_query, cinergyt2);
+
+	cinergyt2->udev = interface_to_usbdev(intf);
+	cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+
+	if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) {
+		dprintk(1, "unable to allocate stream urbs\n");
+		kfree(cinergyt2);
+		return -ENOMEM;
+	}
+
+	dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME, THIS_MODULE);
+
+	cinergyt2->demux.priv = cinergyt2;
+	cinergyt2->demux.filternum = 256;
+	cinergyt2->demux.feednum = 256;
+	cinergyt2->demux.start_feed = cinergyt2_start_feed;
+	cinergyt2->demux.stop_feed = cinergyt2_stop_feed;
+	cinergyt2->demux.dmx.capabilities = DMX_TS_FILTERING |
+					    DMX_SECTION_FILTERING |
+					    DMX_MEMORY_BASED_FILTERING;
+
+	if ((err = dvb_dmx_init(&cinergyt2->demux)) < 0) {
+		dprintk(1, "dvb_dmx_init() failed (err = %d)\n", err);
+		goto bailout;
+	}
+
+	cinergyt2->dmxdev.filternum = cinergyt2->demux.filternum;
+	cinergyt2->dmxdev.demux = &cinergyt2->demux.dmx;
+	cinergyt2->dmxdev.capabilities = 0;
+
+	if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, cinergyt2->adapter)) < 0) {
+		dprintk(1, "dvb_dmxdev_init() failed (err = %d)\n", err);
+		goto bailout;
+	}
+
+	if (dvb_net_init(cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx))
+		dprintk(1, "dvb_net_init() failed!\n");
+
+	dvb_register_device(cinergyt2->adapter, &cinergyt2->fedev,
+			    &cinergyt2_fe_template, cinergyt2,
+			    DVB_DEVICE_FRONTEND);
+
+#ifdef ENABLE_RC
+	init_input_dev(&cinergyt2->rc_input_dev);
+
+	cinergyt2->rc_input_dev.evbit[0] = BIT(EV_KEY);
+	cinergyt2->rc_input_dev.keycodesize = sizeof(unsigned char);
+	cinergyt2->rc_input_dev.keycodemax = KEY_MAX;
+	cinergyt2->rc_input_dev.name = DRIVER_NAME " remote control";
+
+	for (i=0; i<sizeof(rc_keys)/sizeof(rc_keys[0]); i+=3)
+		set_bit(rc_keys[i+2], cinergyt2->rc_input_dev.keybit);
+
+	input_register_device(&cinergyt2->rc_input_dev);
+
+	cinergyt2->rc_input_event = KEY_MAX;
+
+	INIT_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc, cinergyt2);
+	schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+#endif
+	return 0;
+
+bailout:
+	dvb_dmxdev_release(&cinergyt2->dmxdev);
+	dvb_dmx_release(&cinergyt2->demux);
+	dvb_unregister_adapter (cinergyt2->adapter);
+	cinergyt2_free_stream_urbs (cinergyt2);
+	kfree(cinergyt2);
+	return -ENOMEM;
+}
+
+static void cinergyt2_disconnect (struct usb_interface *intf)
+{
+	struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+
+	if (down_interruptible(&cinergyt2->sem))
+		return;
+
+#ifdef ENABLE_RC
+	cancel_delayed_work(&cinergyt2->rc_query_work);
+	flush_scheduled_work();
+	input_unregister_device(&cinergyt2->rc_input_dev);
+#endif
+
+	cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
+	dvb_net_release(&cinergyt2->dvbnet);
+	dvb_dmxdev_release(&cinergyt2->dmxdev);
+	dvb_dmx_release(&cinergyt2->demux);
+	dvb_unregister_device(cinergyt2->fedev);
+	dvb_unregister_adapter(cinergyt2->adapter);
+
+	cinergyt2_free_stream_urbs(cinergyt2);
+	up(&cinergyt2->sem);
+	kfree(cinergyt2);
+}
+
+static int cinergyt2_suspend (struct usb_interface *intf, u32 state)
+{
+	struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+
+	if (down_interruptible(&cinergyt2->sem))
+		return -ERESTARTSYS;
+
+	if (state > 0) {	/* state 0 seems to mean DEVICE_PM_ON */
+		struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+#ifdef ENABLE_RC
+		cancel_delayed_work(&cinergyt2->rc_query_work);
+#endif
+		cancel_delayed_work(&cinergyt2->query_work);
+		if (cinergyt2->streaming)
+			cinergyt2_stop_stream_xfer(cinergyt2);
+		flush_scheduled_work();
+		cinergyt2_sleep(cinergyt2, 1);
+	}
+
+	up(&cinergyt2->sem);
+	return 0;
+}
+
+static int cinergyt2_resume (struct usb_interface *intf)
+{
+	struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
+	struct dvbt_set_parameters_msg *param = &cinergyt2->param;
+
+	if (down_interruptible(&cinergyt2->sem))
+		return -ERESTARTSYS;
+
+	if (!cinergyt2->sleeping) {
+		cinergyt2_sleep(cinergyt2, 0);
+		cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0);
+		if (cinergyt2->streaming)
+			cinergyt2_start_stream_xfer(cinergyt2);
+		schedule_delayed_work(&cinergyt2->query_work, HZ/2);
+	}
+
+#ifdef ENABLE_RC
+	schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
+#endif
+	up(&cinergyt2->sem);
+	return 0;
+}
+
+static const struct usb_device_id cinergyt2_table [] __devinitdata = {
+	{ USB_DEVICE(0x0ccd, 0x0038) },
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(usb, cinergyt2_table);
+
+static struct usb_driver cinergyt2_driver = {
+	.owner	= THIS_MODULE,
+	.name	= "cinergyT2",
+	.probe	= cinergyt2_probe,
+	.disconnect	= cinergyt2_disconnect,
+	.suspend	= cinergyt2_suspend,
+	.resume		= cinergyt2_resume,
+	.id_table	= cinergyt2_table
+};
+
+static int __init cinergyt2_init (void)
+{
+	int err;
+
+	if ((err = usb_register(&cinergyt2_driver)) < 0)
+		dprintk(1, "usb_register() failed! (err %i)\n", err);
+
+	return err;
+}
+
+static void __exit cinergyt2_exit (void)
+{
+	usb_deregister(&cinergyt2_driver);
+}
+
+module_init (cinergyt2_init);
+module_exit (cinergyt2_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Holger Waechtler, Daniel Mack");
+
diff --git a/drivers/media/dvb/dibusb/Kconfig b/drivers/media/dvb/dibusb/Kconfig
new file mode 100644
index 0000000..74dfc73
--- /dev/null
+++ b/drivers/media/dvb/dibusb/Kconfig
@@ -0,0 +1,62 @@
+config DVB_DIBUSB
+	tristate "DiBcom USB DVB-T devices (see help for a complete device list)"
+	depends on DVB_CORE && USB
+	select FW_LOADER
+	select DVB_DIB3000MB
+	select DVB_DIB3000MC
+	select DVB_MT352
+	help
+	  Support for USB 1.1 and 2.0 DVB-T devices based on reference designs made by
+	  DiBcom (http://www.dibcom.fr) and C&E.
+
+	  Devices supported by this driver:
+
+	    TwinhanDTV USB-Ter (VP7041)
+		TwinhanDTV Magic Box (VP7041e)
+	    KWorld/JetWay/ADSTech V-Stream XPERT DTV - DVB-T USB1.1 and USB2.0
+	    Hama DVB-T USB-Box
+	    DiBcom reference devices (non-public)
+	    Ultima Electronic/Artec T1 USB TVBOX
+	    Compro Videomate DVB-U2000 - DVB-T USB
+	    Grandtec DVB-T USB
+	    Avermedia AverTV DVBT USB
+	    Artec T1 USB1.1 and USB2.0 boxes
+	    Yakumo/Typhoon DVB-T USB2.0
+	    Hanftek UMT-010 USB2.0
+	    Hauppauge WinTV NOVA-T USB2
+
+	  The VP7041 seems to be identical to "CTS Portable" (Chinese
+	  Television System).
+
+	  These devices can be understood as budget ones, they "only" deliver
+	  (a part of) the MPEG2 transport stream.
+
+	  A firmware is needed to get the device working. See Documentation/dvb/README.dibusb
+	  details.
+
+	  Say Y if you own such a device and want to use it. You should build it as
+	  a module.
+
+config DVB_DIBUSB_MISDESIGNED_DEVICES
+	bool "Enable support for some misdesigned (see help) devices, which identify with wrong IDs"
+	depends on DVB_DIBUSB
+	help
+	  Somehow Artec/Ultima Electronic forgot to program the eeprom of some of their
+	  USB1.1/USB2.0 devices.
+	  So comes that they identify with the default Vendor and Product ID of the Cypress
+	  CY7C64613 (AN2235) or Cypress FX2.
+
+	  Affected device IDs:
+	    0x0574:0x2235 (Artec T1 USB1.1, cold)
+	    0x04b4:0x8613 (Artec T1 USB2.0, cold)
+	    0x0574:0x1002 (Artec T1 USB2.0, warm)
+	    0x0574:0x2131 (aged DiBcom USB1.1 test device)
+
+	  Say Y if your device has one of the mentioned IDs.
+
+config DVB_DIBCOM_DEBUG
+	bool "Enable extended debug support for DiBcom USB device"
+	depends on DVB_DIBUSB
+	help
+	  Say Y if you want to enable debuging. See modinfo dvb-dibusb for
+	  debug levels.
diff --git a/drivers/media/dvb/dibusb/Makefile b/drivers/media/dvb/dibusb/Makefile
new file mode 100644
index 0000000..e941c50
--- /dev/null
+++ b/drivers/media/dvb/dibusb/Makefile
@@ -0,0 +1,11 @@
+dvb-dibusb-objs = dvb-dibusb-core.o \
+	dvb-dibusb-dvb.o \
+	dvb-dibusb-fe-i2c.o \
+	dvb-dibusb-firmware.o \
+	dvb-dibusb-remote.o \
+	dvb-dibusb-usb.o \
+	dvb-fe-dtt200u.o
+
+obj-$(CONFIG_DVB_DIBUSB) += dvb-dibusb.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-core.c b/drivers/media/dvb/dibusb/dvb-dibusb-core.c
new file mode 100644
index 0000000..26235f9
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-core.c
@@ -0,0 +1,558 @@
+/*
+ * Driver for mobile USB Budget DVB-T devices based on reference 
+ * design made by DiBcom (http://www.dibcom.fr/)
+ * 
+ * dvb-dibusb-core.c
+ * 
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ * 
+ * based on GPL code from DiBcom, which has
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ * Remote control code added by David Matthews (dm@prolingua.co.uk)
+ * 
+ *	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.
+ *
+ * Acknowledgements
+ * 
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dib3000mb/mc/p frontends) are based.
+ * 
+ * see Documentation/dvb/README.dibusb for more information
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/moduleparam.h>
+
+/* debug */
+int dvb_dibusb_debug;
+module_param_named(debug, dvb_dibusb_debug,  int, 0644);
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define DBSTATUS ""
+#else
+#define DBSTATUS " (debugging is not enabled)"
+#endif
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=alotmore,8=ts,16=err,32=rc (|-able))." DBSTATUS);
+#undef DBSTATUS
+
+static int pid_parse;
+module_param(pid_parse, int, 0644);
+MODULE_PARM_DESC(pid_parse, "enable pid parsing (filtering) when running at USB2.0");
+
+static int rc_query_interval = 100;
+module_param(rc_query_interval, int, 0644);
+MODULE_PARM_DESC(rc_query_interval, "interval in msecs for remote control query (default: 100; min: 40)");
+
+static int rc_key_repeat_count = 2;
+module_param(rc_key_repeat_count, int, 0644);
+MODULE_PARM_DESC(rc_key_repeat_count, "how many key repeats will be dropped before passing the key event again (default: 2)");
+
+/* Vendor IDs */
+#define USB_VID_ADSTECH						0x06e1
+#define USB_VID_ANCHOR						0x0547
+#define USB_VID_AVERMEDIA					0x14aa
+#define USB_VID_COMPRO						0x185b
+#define USB_VID_COMPRO_UNK					0x145f
+#define USB_VID_CYPRESS						0x04b4
+#define USB_VID_DIBCOM						0x10b8
+#define USB_VID_EMPIA						0xeb1a
+#define USB_VID_GRANDTEC					0x5032
+#define USB_VID_HANFTEK						0x15f4
+#define USB_VID_HAUPPAUGE					0x2040
+#define USB_VID_HYPER_PALTEK				0x1025
+#define USB_VID_IMC_NETWORKS				0x13d3
+#define USB_VID_TWINHAN						0x1822
+#define USB_VID_ULTIMA_ELECTRONIC			0x05d8
+
+/* Product IDs */
+#define USB_PID_ADSTECH_USB2_COLD			0xa333
+#define USB_PID_ADSTECH_USB2_WARM			0xa334
+#define USB_PID_AVERMEDIA_DVBT_USB_COLD		0x0001
+#define USB_PID_AVERMEDIA_DVBT_USB_WARM		0x0002
+#define USB_PID_COMPRO_DVBU2000_COLD		0xd000
+#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_DIBCOM_MOD3000_COLD			0x0bb8
+#define USB_PID_DIBCOM_MOD3000_WARM			0x0bb9
+#define USB_PID_DIBCOM_MOD3001_COLD			0x0bc6
+#define USB_PID_DIBCOM_MOD3001_WARM			0x0bc7
+#define USB_PID_DIBCOM_ANCHOR_2135_COLD		0x2131
+#define USB_PID_GRANDTEC_DVBT_USB_COLD		0x0fa0
+#define USB_PID_GRANDTEC_DVBT_USB_WARM		0x0fa1
+#define USB_PID_KWORLD_VSTREAM_COLD			0x17de
+#define USB_PID_KWORLD_VSTREAM_WARM			0x17df
+#define USB_PID_TWINHAN_VP7041_COLD			0x3201
+#define USB_PID_TWINHAN_VP7041_WARM			0x3202
+#define USB_PID_ULTIMA_TVBOX_COLD			0x8105
+#define USB_PID_ULTIMA_TVBOX_WARM			0x8106
+#define USB_PID_ULTIMA_TVBOX_AN2235_COLD	0x8107
+#define USB_PID_ULTIMA_TVBOX_AN2235_WARM	0x8108
+#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD	0x2235
+#define USB_PID_ULTIMA_TVBOX_USB2_COLD		0x8109
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD	0x8613
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM	0x1002
+#define USB_PID_UNK_HYPER_PALTEK_COLD		0x005e
+#define USB_PID_UNK_HYPER_PALTEK_WARM		0x005f
+#define USB_PID_HANFTEK_UMT_010_COLD		0x0001
+#define USB_PID_HANFTEK_UMT_010_WARM		0x0015
+#define USB_PID_YAKUMO_DTT200U_COLD			0x0201
+#define USB_PID_YAKUMO_DTT200U_WARM			0x0301
+#define USB_PID_WINTV_NOVA_T_USB2_COLD		0x9300
+#define USB_PID_WINTV_NOVA_T_USB2_WARM		0x9301
+
+/* USB Driver stuff
+ * table of devices that this driver is working with
+ *
+ * ATTENTION: Never ever change the order of this table, the particular 
+ * devices depend on this order 
+ *
+ * Each entry is used as a reference in the device_struct. Currently this is 
+ * the only non-redundant way of assigning USB ids to actual devices I'm aware 
+ * of, because there is only one place in the code where the assignment of 
+ * vendor and product id is done, here.
+ */
+static struct usb_device_id dib_table [] = {
+/* 00 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_AVERMEDIA_DVBT_USB_COLD)},
+/* 01 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_AVERMEDIA_DVBT_USB_WARM)},
+/* 02 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_YAKUMO_DTT200U_COLD) },
+/* 03 */	{ USB_DEVICE(USB_VID_AVERMEDIA,		USB_PID_YAKUMO_DTT200U_WARM) },
+
+/* 04 */	{ USB_DEVICE(USB_VID_COMPRO,		USB_PID_COMPRO_DVBU2000_COLD) },
+/* 05 */	{ USB_DEVICE(USB_VID_COMPRO,		USB_PID_COMPRO_DVBU2000_WARM) },
+/* 06 */	{ USB_DEVICE(USB_VID_COMPRO_UNK,	USB_PID_COMPRO_DVBU2000_UNK_COLD) },
+/* 07 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3000_COLD) },
+/* 08 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3000_WARM) },
+/* 09 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3001_COLD) },
+/* 10 */	{ USB_DEVICE(USB_VID_DIBCOM,		USB_PID_DIBCOM_MOD3001_WARM) },
+/* 11 */	{ USB_DEVICE(USB_VID_EMPIA,			USB_PID_KWORLD_VSTREAM_COLD) },
+/* 12 */	{ USB_DEVICE(USB_VID_EMPIA,			USB_PID_KWORLD_VSTREAM_WARM) },
+/* 13 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_GRANDTEC_DVBT_USB_COLD) },
+/* 14 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_GRANDTEC_DVBT_USB_WARM) },
+/* 15 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_DIBCOM_MOD3000_COLD) },
+/* 16 */	{ USB_DEVICE(USB_VID_GRANDTEC,		USB_PID_DIBCOM_MOD3000_WARM) },
+/* 17 */	{ USB_DEVICE(USB_VID_HYPER_PALTEK,	USB_PID_UNK_HYPER_PALTEK_COLD) },
+/* 18 */	{ USB_DEVICE(USB_VID_HYPER_PALTEK,	USB_PID_UNK_HYPER_PALTEK_WARM) },
+/* 19 */	{ USB_DEVICE(USB_VID_IMC_NETWORKS,	USB_PID_TWINHAN_VP7041_COLD) },
+/* 20 */	{ USB_DEVICE(USB_VID_IMC_NETWORKS,	USB_PID_TWINHAN_VP7041_WARM) },
+/* 21 */	{ USB_DEVICE(USB_VID_TWINHAN, 		USB_PID_TWINHAN_VP7041_COLD) },
+/* 22 */	{ USB_DEVICE(USB_VID_TWINHAN, 		USB_PID_TWINHAN_VP7041_WARM) },
+/* 23 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) },
+/* 24 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) },
+/* 25 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) },
+/* 26 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) },
+/* 27 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC,	USB_PID_ULTIMA_TVBOX_USB2_COLD) },
+	
+/* 28 */	{ USB_DEVICE(USB_VID_HANFTEK,		USB_PID_HANFTEK_UMT_010_COLD) },
+/* 29 */	{ USB_DEVICE(USB_VID_HANFTEK,		USB_PID_HANFTEK_UMT_010_WARM) },
+
+/* 30 */	{ USB_DEVICE(USB_VID_HAUPPAUGE,		USB_PID_WINTV_NOVA_T_USB2_COLD) },
+/* 31 */	{ USB_DEVICE(USB_VID_HAUPPAUGE,		USB_PID_WINTV_NOVA_T_USB2_WARM) },
+/* 32 */	{ USB_DEVICE(USB_VID_ADSTECH,		USB_PID_ADSTECH_USB2_COLD) },
+/* 33 */	{ USB_DEVICE(USB_VID_ADSTECH,		USB_PID_ADSTECH_USB2_WARM) },
+/* 
+ * activate the following define when you have one of the devices and want to 
+ * build it from build-2.6 in dvb-kernel
+ */
+// #define CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+/* 34 */	{ USB_DEVICE(USB_VID_ANCHOR,		USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) },
+/* 35 */	{ USB_DEVICE(USB_VID_CYPRESS,		USB_PID_ULTIMA_TVBOX_USB2_FX_COLD) },
+/* 36 */	{ USB_DEVICE(USB_VID_ANCHOR,		USB_PID_ULTIMA_TVBOX_USB2_FX_WARM) },
+/* 37 */	{ USB_DEVICE(USB_VID_ANCHOR,		USB_PID_DIBCOM_ANCHOR_2135_COLD) },
+#endif
+			{ }		/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, dib_table);
+
+static struct dibusb_usb_controller dibusb_usb_ctrl[] = {
+	{ .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 },
+	{ .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 },
+	{ .name = "Cypress FX2",    .cpu_cs_register = 0xe600 },
+};
+
+struct dibusb_tuner dibusb_tuner[] = {
+	{ DIBUSB_TUNER_CABLE_THOMSON, 
+	  0x61 
+	},
+	{ DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5,
+	  0x60 
+	},
+	{ DIBUSB_TUNER_CABLE_LG_TDTP_E102P,
+	  0x61
+	},
+	{ DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5,
+	  0x60
+	},
+};
+
+static struct dibusb_demod dibusb_demod[] = {
+	{ DIBUSB_DIB3000MB,
+	  16,
+	  { 0x8, 0 },
+	},
+	{ DIBUSB_DIB3000MC,
+	  32,
+	  { 0x9, 0xa, 0xb, 0xc }, 
+	},
+	{ DIBUSB_MT352,
+	  254,
+	  { 0xf, 0 }, 
+	},
+	{ DTT200U_FE,
+	  8,
+	  { 0xff,0 }, /* there is no i2c bus in this device */
+	}
+};
+
+static struct dibusb_device_class dibusb_device_classes[] = {
+	{ .id = DIBUSB1_1, .usb_ctrl = &dibusb_usb_ctrl[0],
+	  .firmware = "dvb-dibusb-5.0.0.11.fw",
+	  .pipe_cmd = 0x01, .pipe_data = 0x02, 
+	  .urb_count = 7, .urb_buffer_size = 4096,
+	  DIBUSB_RC_NEC_PROTOCOL,
+	  &dibusb_demod[DIBUSB_DIB3000MB],
+	  &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+	},
+	{ DIBUSB1_1_AN2235, &dibusb_usb_ctrl[1],
+	  "dvb-dibusb-an2235-1.fw",
+	  0x01, 0x02, 
+	  7, 4096,
+	  DIBUSB_RC_NEC_PROTOCOL,
+	  &dibusb_demod[DIBUSB_DIB3000MB],
+	  &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+	},
+	{ DIBUSB2_0,&dibusb_usb_ctrl[2],
+	  "dvb-dibusb-6.0.0.5.fw",
+	  0x01, 0x06, 
+	  7, 4096,
+	  DIBUSB_RC_NEC_PROTOCOL,
+	  &dibusb_demod[DIBUSB_DIB3000MC],
+	  &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5],
+	},
+	{ UMT2_0, &dibusb_usb_ctrl[2],
+	  "dvb-dibusb-umt-2.fw",
+	  0x01, 0x06,
+	  20, 512,
+	  DIBUSB_RC_NO,
+	  &dibusb_demod[DIBUSB_MT352],
+	  &dibusb_tuner[DIBUSB_TUNER_CABLE_LG_TDTP_E102P],
+	},
+	{ DIBUSB2_0B,&dibusb_usb_ctrl[2],
+	  "dvb-dibusb-adstech-usb2-1.fw",
+	  0x01, 0x06,
+	  7, 4096,
+	  DIBUSB_RC_NEC_PROTOCOL,
+	  &dibusb_demod[DIBUSB_DIB3000MB],
+	  &dibusb_tuner[DIBUSB_TUNER_CABLE_THOMSON],
+	},
+	{ NOVAT_USB2,&dibusb_usb_ctrl[2],
+	  "dvb-dibusb-nova-t-1.fw",
+	  0x01, 0x06,
+	  7, 4096,
+	  DIBUSB_RC_HAUPPAUGE_PROTO,
+	  &dibusb_demod[DIBUSB_DIB3000MC],
+	  &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5],
+	},
+	{ DTT200U,&dibusb_usb_ctrl[2],
+	  "dvb-dtt200u-1.fw",
+	  0x01, 0x02,
+	  7, 4096,
+	  DIBUSB_RC_NO,
+	  &dibusb_demod[DTT200U_FE],
+	  NULL, /* no explicit tuner/pll-programming necessary (it has the ENV57H1XD5) */
+	},
+};
+
+static struct dibusb_usb_device dibusb_devices[] = {
+	{	"TwinhanDTV USB1.1 / Magic Box / HAMA USB1.1 DVB-T device",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[19], &dib_table[21], NULL},
+		{ &dib_table[20], &dib_table[22], NULL},
+	},
+	{	"KWorld V-Stream XPERT DTV - DVB-T USB1.1",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[11], NULL },
+		{ &dib_table[12], NULL },
+	},
+	{	"Grandtec USB1.1 DVB-T",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[13], &dib_table[15], NULL },
+		{ &dib_table[14], &dib_table[16], NULL },
+	},
+	{	"DiBcom USB1.1 DVB-T reference design (MOD3000)",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[7],  NULL },
+		{ &dib_table[8],  NULL },
+	},
+	{	"Artec T1 USB1.1 TVBOX with AN2135",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[23], NULL },
+		{ &dib_table[24], NULL },
+	},
+	{	"Artec T1 USB1.1 TVBOX with AN2235",
+		&dibusb_device_classes[DIBUSB1_1_AN2235],
+		{ &dib_table[25], NULL },
+		{ &dib_table[26], NULL },
+	},
+	{	"Avermedia AverTV DVBT USB1.1",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[0],  NULL },
+		{ &dib_table[1],  NULL },
+	},
+	{	"Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[4], &dib_table[6], NULL},
+		{ &dib_table[5], NULL },
+	},
+	{	"Unkown USB1.1 DVB-T device ???? please report the name to the author",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[17], NULL },
+		{ &dib_table[18], NULL },
+	},
+	{	"DiBcom USB2.0 DVB-T reference design (MOD3000P)",
+		&dibusb_device_classes[DIBUSB2_0],
+		{ &dib_table[9],  NULL },
+		{ &dib_table[10], NULL },
+	},
+	{	"Artec T1 USB2.0 TVBOX (please report the warm ID)",
+		&dibusb_device_classes[DIBUSB2_0],
+		{ &dib_table[27], NULL },
+		{ NULL },
+	},
+	{	"Hauppauge WinTV NOVA-T USB2",
+		&dibusb_device_classes[NOVAT_USB2],
+		{ &dib_table[30], NULL },
+		{ &dib_table[31], NULL },
+	},
+	{	"DTT200U (Yakumo/Hama/Typhoon) DVB-T USB2.0",
+		&dibusb_device_classes[DTT200U],
+		{ &dib_table[2], NULL },
+		{ &dib_table[3], NULL },
+	},	
+	{	"Hanftek UMT-010 DVB-T USB2.0",
+		&dibusb_device_classes[UMT2_0],
+		{ &dib_table[28], NULL },
+		{ &dib_table[29], NULL },
+	},	
+	{	"KWorld/ADSTech Instant DVB-T USB 2.0",
+		&dibusb_device_classes[DIBUSB2_0B],
+		{ &dib_table[32], NULL },
+		{ &dib_table[33], NULL }, /* device ID with default DIBUSB2_0-firmware */
+	},
+#ifdef CONFIG_DVB_DIBUSB_MISDESIGNED_DEVICES
+	{	"Artec T1 USB1.1 TVBOX with AN2235 (misdesigned)",
+		&dibusb_device_classes[DIBUSB1_1_AN2235],
+		{ &dib_table[34], NULL },
+		{ NULL },
+	},
+	{	"Artec T1 USB2.0 TVBOX with FX2 IDs (misdesigned, please report the warm ID)",
+		&dibusb_device_classes[DTT200U],
+		{ &dib_table[35], NULL },
+		{ &dib_table[36], NULL }, /* undefined, it could be that the device will get another USB ID in warm state */
+	},
+	{	"DiBcom USB1.1 DVB-T reference design (MOD3000) with AN2135 default IDs",
+		&dibusb_device_classes[DIBUSB1_1],
+		{ &dib_table[37], NULL },
+		{ NULL },
+	},
+#endif
+};
+
+static int dibusb_exit(struct usb_dibusb *dib)
+{
+	deb_info("init_state before exiting everything: %x\n",dib->init_state);
+	dibusb_remote_exit(dib);
+	dibusb_fe_exit(dib);
+	dibusb_i2c_exit(dib);
+	dibusb_dvb_exit(dib);
+	dibusb_urb_exit(dib);
+	deb_info("init_state should be zero now: %x\n",dib->init_state);
+	dib->init_state = DIBUSB_STATE_INIT;
+	kfree(dib);
+	return 0;
+}
+
+static int dibusb_init(struct usb_dibusb *dib)
+{
+	int ret = 0;
+	sema_init(&dib->usb_sem, 1);
+	sema_init(&dib->i2c_sem, 1);
+
+	dib->init_state = DIBUSB_STATE_INIT;
+	
+	if ((ret = dibusb_urb_init(dib)) ||
+		(ret = dibusb_dvb_init(dib)) || 
+		(ret = dibusb_i2c_init(dib))) {
+		dibusb_exit(dib);
+		return ret;
+	}
+
+	if ((ret = dibusb_fe_init(dib)))
+		err("could not initialize a frontend.");
+	
+	if ((ret = dibusb_remote_init(dib)))
+		err("could not initialize remote control.");
+	
+	return 0;
+}
+
+static struct dibusb_usb_device * dibusb_device_class_quirk(struct usb_device *udev, struct dibusb_usb_device *dev)
+{
+	int i;
+
+	/* Quirk for the Kworld/ADSTech Instant USB2.0 device. It has the same USB
+	 * IDs like the USB1.1 KWorld after loading the firmware. Which is a bad
+	 * idea and make this quirk necessary.
+	 */
+	if (dev->dev_cl->id == DIBUSB1_1 && udev->speed == USB_SPEED_HIGH) {
+		info("this seems to be the Kworld/ADSTech Instant USB2.0 device or equal.");
+		for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) {
+			if (dibusb_devices[i].dev_cl->id == DIBUSB2_0B) {
+				dev = &dibusb_devices[i];
+				break;
+			}
+		}
+	}
+
+	return dev;
+}
+
+static struct dibusb_usb_device * dibusb_find_device (struct usb_device *udev,int *cold)
+{
+	int i,j;
+	struct dibusb_usb_device *dev = NULL;
+	*cold = -1;
+
+	for (i = 0; i < sizeof(dibusb_devices)/sizeof(struct dibusb_usb_device); i++) {
+		for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].cold_ids[j] != NULL; j++) {
+			deb_info("check for cold %x %x\n",dibusb_devices[i].cold_ids[j]->idVendor, dibusb_devices[i].cold_ids[j]->idProduct);
+			if (dibusb_devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
+				dibusb_devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
+				*cold = 1;
+				dev = &dibusb_devices[i];
+				break;
+			}
+		}
+
+		if (dev != NULL)
+			break;
+
+		for (j = 0; j < DIBUSB_ID_MAX_NUM && dibusb_devices[i].warm_ids[j] != NULL; j++) {
+			deb_info("check for warm %x %x\n",dibusb_devices[i].warm_ids[j]->idVendor, dibusb_devices[i].warm_ids[j]->idProduct);
+			if (dibusb_devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) &&
+				dibusb_devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) {
+				*cold = 0;
+				dev = &dibusb_devices[i];
+				break;
+			}
+		}
+	}
+
+	if (dev != NULL)
+		dev = dibusb_device_class_quirk(udev,dev);
+
+	return dev;
+}
+
+/*
+ * USB 
+ */
+static int dibusb_probe(struct usb_interface *intf, 
+		const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_dibusb *dib = NULL;
+	struct dibusb_usb_device *dibdev = NULL;
+	
+	int ret = -ENOMEM,cold=0;
+
+	if ((dibdev = dibusb_find_device(udev,&cold)) == NULL) {
+		err("something went very wrong, "
+				"unknown product ID: %.4x",le16_to_cpu(udev->descriptor.idProduct));
+		return -ENODEV;
+	}
+	
+	if (cold == 1) {
+		info("found a '%s' in cold state, will try to load a firmware",dibdev->name);
+		ret = dibusb_loadfirmware(udev,dibdev);
+	} else {
+		info("found a '%s' in warm state.",dibdev->name);
+		dib = kmalloc(sizeof(struct usb_dibusb),GFP_KERNEL);
+		if (dib == NULL) {
+			err("no memory");
+			return ret;
+		}
+		memset(dib,0,sizeof(struct usb_dibusb));
+		
+		dib->udev = udev;
+		dib->dibdev = dibdev;
+
+		/* store parameters to structures */
+		dib->rc_query_interval = rc_query_interval;
+		dib->pid_parse = pid_parse;
+		dib->rc_key_repeat_count = rc_key_repeat_count;
+
+		usb_set_intfdata(intf, dib);
+		
+		ret = dibusb_init(dib);
+	}
+	
+	if (ret == 0)
+		info("%s successfully initialized and connected.",dibdev->name);
+	else 
+		info("%s error while loading driver (%d)",dibdev->name,ret);
+	return ret;
+}
+
+static void dibusb_disconnect(struct usb_interface *intf)
+{
+	struct usb_dibusb *dib = usb_get_intfdata(intf);
+	const char *name = DRIVER_DESC;
+	
+	usb_set_intfdata(intf,NULL);
+	if (dib != NULL && dib->dibdev != NULL) {
+		name = dib->dibdev->name;
+		dibusb_exit(dib);
+	}
+	info("%s successfully deinitialized and disconnected.",name);
+	
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver dibusb_driver = {
+	.owner		= THIS_MODULE,
+	.name		= DRIVER_DESC,
+	.probe 		= dibusb_probe,
+	.disconnect = dibusb_disconnect,
+	.id_table 	= dib_table,
+};
+
+/* module stuff */
+static int __init usb_dibusb_init(void)
+{
+	int result;
+	if ((result = usb_register(&dibusb_driver))) {
+		err("usb_register failed. Error number %d",result);
+		return result;
+	}
+	
+	return 0;
+}
+
+static void __exit usb_dibusb_exit(void)
+{
+	/* deregister this driver from the USB subsystem */
+	usb_deregister(&dibusb_driver);
+}
+
+module_init (usb_dibusb_init);
+module_exit (usb_dibusb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
new file mode 100644
index 0000000..04e54ec
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-dvb.c
@@ -0,0 +1,185 @@
+/*
+ * dvb-dibusb-dvb.c is part of the driver for mobile USB Budget DVB-T devices 
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the 
+ * linux-dvb API.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/usb.h>
+#include <linux/version.h>
+
+static u32 urb_compl_count;
+
+/*
+ * MPEG2 TS DVB stuff 
+ */
+void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
+{
+	struct usb_dibusb *dib = urb->context;
+
+	deb_ts("urb complete feedcount: %d, status: %d, length: %d\n",dib->feedcount,urb->status,
+			urb->actual_length);
+
+	urb_compl_count++;
+	if (urb_compl_count % 1000 == 0)
+		deb_info("%d urbs completed so far.\n",urb_compl_count);
+
+	switch (urb->status) {
+		case 0:         /* success */
+		case -ETIMEDOUT:    /* NAK */
+			break;
+		case -ECONNRESET:   /* kill */
+		case -ENOENT:
+		case -ESHUTDOWN:
+			return;
+		default:        /* error */
+			deb_ts("urb completition error %d.", urb->status);
+			break;
+	}
+
+	if (dib->feedcount > 0 && urb->actual_length > 0) {
+		if (dib->init_state & DIBUSB_STATE_DVB)
+			dvb_dmx_swfilter(&dib->demux, (u8*) urb->transfer_buffer,urb->actual_length);
+	} else 
+		deb_ts("URB dropped because of feedcount.\n");
+
+	usb_submit_urb(urb,GFP_ATOMIC);
+}
+
+static int dibusb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) 
+{
+	struct usb_dibusb *dib = dvbdmxfeed->demux->priv;
+	int newfeedcount;
+	
+	if (dib == NULL)
+		return -ENODEV;
+
+	newfeedcount = dib->feedcount + (onoff ? 1 : -1);
+
+	/* 
+	 * stop feed before setting a new pid if there will be no pid anymore 
+	 */
+	if (newfeedcount == 0) {
+		deb_ts("stop feeding\n");
+		if (dib->xfer_ops.fifo_ctrl != NULL) {
+			if (dib->xfer_ops.fifo_ctrl(dib->fe,0)) {
+				err("error while inhibiting fifo.");
+				return -ENODEV;
+			}
+		}
+		dibusb_streaming(dib,0);
+	}
+	
+	dib->feedcount = newfeedcount;
+
+	/* activate the pid on the device specific pid_filter */
+	deb_ts("setting pid: %5d %04x at index %d '%s'\n",dvbdmxfeed->pid,dvbdmxfeed->pid,dvbdmxfeed->index,onoff ? "on" : "off");
+	if (dib->pid_parse && dib->xfer_ops.pid_ctrl != NULL)
+		dib->xfer_ops.pid_ctrl(dib->fe,dvbdmxfeed->index,dvbdmxfeed->pid,onoff);
+
+	/* 
+	 * start the feed if this was the first pid to set and there is still a pid
+	 * for reception.
+	 */
+	if (dib->feedcount == onoff && dib->feedcount > 0) {
+
+		deb_ts("controlling pid parser\n");
+		if (dib->xfer_ops.pid_parse != NULL) {
+			if (dib->xfer_ops.pid_parse(dib->fe,dib->pid_parse) < 0) {
+				err("could not handle pid_parser");
+			}
+		}
+		
+		deb_ts("start feeding\n");
+		if (dib->xfer_ops.fifo_ctrl != NULL) {
+			if (dib->xfer_ops.fifo_ctrl(dib->fe,1)) {
+				err("error while enabling fifo.");
+				return -ENODEV;
+			}
+		}
+		dibusb_streaming(dib,1);
+	}
+	return 0;
+}
+
+static int dibusb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
+	return dibusb_ctrl_feed(dvbdmxfeed,1);
+}
+
+static int dibusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type);
+	return dibusb_ctrl_feed(dvbdmxfeed,0);
+}
+
+int dibusb_dvb_init(struct usb_dibusb *dib)
+{
+	int ret;
+
+	urb_compl_count = 0;
+
+	if ((ret = dvb_register_adapter(&dib->adapter, DRIVER_DESC,
+			THIS_MODULE)) < 0) {
+		deb_info("dvb_register_adapter failed: error %d", ret);
+		goto err;
+	}
+	dib->adapter->priv = dib;
+	
+/* i2c is done in dibusb_i2c_init */
+	
+	dib->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+
+	dib->demux.priv = (void *)dib;
+	/* get pidcount from demod */
+	dib->demux.feednum = dib->demux.filternum = 255;
+	dib->demux.start_feed = dibusb_start_feed;
+	dib->demux.stop_feed = dibusb_stop_feed;
+	dib->demux.write_to_decoder = NULL;
+	if ((ret = dvb_dmx_init(&dib->demux)) < 0) {
+		err("dvb_dmx_init failed: error %d",ret);
+		goto err_dmx;
+	}
+
+	dib->dmxdev.filternum = dib->demux.filternum;
+	dib->dmxdev.demux = &dib->demux.dmx;
+	dib->dmxdev.capabilities = 0;
+	if ((ret = dvb_dmxdev_init(&dib->dmxdev, dib->adapter)) < 0) {
+		err("dvb_dmxdev_init failed: error %d",ret);
+		goto err_dmx_dev;
+	}
+
+	dvb_net_init(dib->adapter, &dib->dvb_net, &dib->demux.dmx);
+
+	goto success;
+err_dmx_dev:
+	dvb_dmx_release(&dib->demux);
+err_dmx:
+	dvb_unregister_adapter(dib->adapter);
+err:
+	return ret;
+success:
+	dib->init_state |= DIBUSB_STATE_DVB;
+	return 0;
+}
+
+int dibusb_dvb_exit(struct usb_dibusb *dib)
+{
+	if (dib->init_state & DIBUSB_STATE_DVB) {
+		dib->init_state &= ~DIBUSB_STATE_DVB;
+		deb_info("unregistering DVB part\n");
+		dvb_net_release(&dib->dvb_net);
+		dib->demux.dmx.close(&dib->demux.dmx);
+		dvb_dmxdev_release(&dib->dmxdev);
+		dvb_dmx_release(&dib->demux);
+		dvb_unregister_adapter(dib->adapter);
+	}
+	return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c
new file mode 100644
index 0000000..2ed8948
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-fe-i2c.c
@@ -0,0 +1,582 @@
+/*
+ * dvb-dibusb-fe-i2c.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for attaching, initializing of an appropriate
+ * demodulator/frontend. I2C-stuff is also located here.
+ *
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/usb.h>
+
+static int dibusb_i2c_msg(struct usb_dibusb *dib, u8 addr,
+			  u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+	u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */
+	/* write only ? */
+	int wo = (rbuf == NULL || rlen == 0),
+		len = 2 + wlen + (wo ? 0 : 2);
+
+	sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ;
+	sndbuf[1] = (addr << 1) | (wo ? 0 : 1);
+
+	memcpy(&sndbuf[2],wbuf,wlen);
+
+	if (!wo) {
+		sndbuf[wlen+2] = (rlen >> 8) & 0xff;
+		sndbuf[wlen+3] = rlen & 0xff;
+	}
+
+	return dibusb_readwrite_usb(dib,sndbuf,len,rbuf,rlen);
+}
+
+/*
+ * I2C master xfer function
+ */
+static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num)
+{
+	struct usb_dibusb *dib = i2c_get_adapdata(adap);
+	int i;
+
+	if (down_interruptible(&dib->i2c_sem) < 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++) {
+		/* write/read request */
+		if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+			if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,
+						msg[i+1].buf,msg[i+1].len) < 0)
+				break;
+			i++;
+		} else
+			if (dibusb_i2c_msg(dib, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0)
+				break;
+	}
+
+	up(&dib->i2c_sem);
+	return i;
+}
+
+static u32 dibusb_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dibusb_algo = {
+	.name			= "DiBcom USB i2c algorithm",
+	.id				= I2C_ALGO_BIT,
+	.master_xfer	= dibusb_i2c_xfer,
+	.functionality	= dibusb_i2c_func,
+};
+
+static int dibusb_general_demod_init(struct dvb_frontend *fe);
+static u8 dibusb_general_pll_addr(struct dvb_frontend *fe);
+static int dibusb_general_pll_init(struct dvb_frontend *fe, u8 pll_buf[5]);
+static int dibusb_general_pll_set(struct dvb_frontend *fe,
+		struct dvb_frontend_parameters* params, u8 pll_buf[5]);
+
+static struct mt352_config mt352_hanftek_umt_010_config = {
+	.demod_address = 0x1e,
+	.demod_init = dibusb_general_demod_init,
+	.pll_set = dibusb_general_pll_set,
+};
+
+static int dibusb_tuner_quirk(struct usb_dibusb *dib)
+{
+	switch (dib->dibdev->dev_cl->id) {
+		case DIBUSB1_1: /* some these device have the ENV77H11D5 and some the THOMSON CABLE */
+		case DIBUSB1_1_AN2235: { /* actually its this device, but in warm state they are indistinguishable */
+			struct dibusb_tuner *t;
+			u8 b[2] = { 0,0 } ,b2[1];
+			struct i2c_msg msg[2] = {
+				{ .flags = 0, .buf = b, .len = 2 },
+				{ .flags = I2C_M_RD, .buf = b2, .len = 1},
+			};
+
+			t = &dibusb_tuner[DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5];
+
+			msg[0].addr = msg[1].addr = t->pll_addr;
+
+			if (dib->xfer_ops.tuner_pass_ctrl != NULL)
+				dib->xfer_ops.tuner_pass_ctrl(dib->fe,1,t->pll_addr);
+			dibusb_i2c_xfer(&dib->i2c_adap,msg,2);
+			if (dib->xfer_ops.tuner_pass_ctrl != NULL)
+				dib->xfer_ops.tuner_pass_ctrl(dib->fe,0,t->pll_addr);
+
+			if (b2[0] == 0xfe)
+				info("this device has the Thomson Cable onboard. Which is default.");
+			else {
+				dib->tuner = t;
+				info("this device has the Panasonic ENV77H11D5 onboard.");
+			}
+			break;
+		}
+		default:
+			break;
+	}
+	return 0;
+}
+
+int dibusb_fe_init(struct usb_dibusb* dib)
+{
+	struct dib3000_config demod_cfg;
+	int i;
+
+	if (dib->init_state & DIBUSB_STATE_I2C) {
+		for (i = 0; i < sizeof(dib->dibdev->dev_cl->demod->i2c_addrs) / sizeof(unsigned char) &&
+				dib->dibdev->dev_cl->demod->i2c_addrs[i] != 0; i++) {
+
+			demod_cfg.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i];
+			demod_cfg.pll_addr = dibusb_general_pll_addr;
+			demod_cfg.pll_set = dibusb_general_pll_set;
+			demod_cfg.pll_init = dibusb_general_pll_init;
+
+			deb_info("demod id: %d %d\n",dib->dibdev->dev_cl->demod->id,DTT200U_FE);
+
+			switch (dib->dibdev->dev_cl->demod->id) {
+				case DIBUSB_DIB3000MB:
+					dib->fe = dib3000mb_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
+				break;
+				case DIBUSB_DIB3000MC:
+					dib->fe = dib3000mc_attach(&demod_cfg,&dib->i2c_adap,&dib->xfer_ops);
+				break;
+				case DIBUSB_MT352:
+					mt352_hanftek_umt_010_config.demod_address = dib->dibdev->dev_cl->demod->i2c_addrs[i];
+					dib->fe = mt352_attach(&mt352_hanftek_umt_010_config, &dib->i2c_adap);
+				break;
+				case DTT200U_FE:
+					dib->fe = dtt200u_fe_attach(dib,&dib->xfer_ops);
+				break;
+			}
+			if (dib->fe != NULL) {
+				info("found demodulator at i2c address 0x%x",dib->dibdev->dev_cl->demod->i2c_addrs[i]);
+				break;
+			}
+		}
+		/* if a frontend was found */
+		if (dib->fe != NULL) {
+			if (dib->fe->ops->sleep != NULL)
+				dib->fe_sleep = dib->fe->ops->sleep;
+			dib->fe->ops->sleep = dibusb_hw_sleep;
+
+			if (dib->fe->ops->init != NULL )
+				dib->fe_init = dib->fe->ops->init;
+			dib->fe->ops->init = dibusb_hw_wakeup;
+
+			/* setting the default tuner */
+			dib->tuner = dib->dibdev->dev_cl->tuner;
+
+			/* check which tuner is mounted on this device, in case this is unsure */
+			dibusb_tuner_quirk(dib);
+		}
+	}
+	if (dib->fe == NULL) {
+		err("A frontend driver was not found for device '%s'.",
+		       dib->dibdev->name);
+		return -ENODEV;
+	} else {
+		if (dvb_register_frontend(dib->adapter, dib->fe)) {
+			err("Frontend registration failed.");
+			if (dib->fe->ops->release)
+				dib->fe->ops->release(dib->fe);
+			dib->fe = NULL;
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+int dibusb_fe_exit(struct usb_dibusb *dib)
+{
+	if (dib->fe != NULL)
+		dvb_unregister_frontend(dib->fe);
+	return 0;
+}
+
+int dibusb_i2c_init(struct usb_dibusb *dib)
+{
+	int ret = 0;
+
+	dib->adapter->priv = dib;
+
+	strncpy(dib->i2c_adap.name,dib->dibdev->name,I2C_NAME_SIZE);
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+	dib->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+	dib->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+	dib->i2c_adap.algo		= &dibusb_algo;
+	dib->i2c_adap.algo_data = NULL;
+	dib->i2c_adap.id		= I2C_ALGO_BIT;
+
+	i2c_set_adapdata(&dib->i2c_adap, dib);
+
+	if ((ret = i2c_add_adapter(&dib->i2c_adap)) < 0)
+		err("could not add i2c adapter");
+
+	dib->init_state |= DIBUSB_STATE_I2C;
+
+	return ret;
+}
+
+int dibusb_i2c_exit(struct usb_dibusb *dib)
+{
+	if (dib->init_state & DIBUSB_STATE_I2C)
+		i2c_del_adapter(&dib->i2c_adap);
+	dib->init_state &= ~DIBUSB_STATE_I2C;
+	return 0;
+}
+
+
+/* pll stuff, maybe removed soon (thx to Gerd/Andrew in advance) */
+static int thomson_cable_eu_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4])
+{
+	u32 tfreq = (fep->frequency + 36125000) / 62500;
+	int vu,p0,p1,p2;
+
+	if (fep->frequency > 403250000)
+		vu = 1, p2 = 1, p1 = 0, p0 = 1;
+	else if (fep->frequency > 115750000)
+		vu = 0, p2 = 1, p1 = 1, p0 = 0;
+	else if (fep->frequency > 44250000)
+		vu = 0, p2 = 0, p1 = 1, p0 = 1;
+	else
+		return -EINVAL;
+
+	pllbuf[0] = (tfreq >> 8) & 0x7f;
+	pllbuf[1] = tfreq & 0xff;
+	pllbuf[2] = 0x8e;
+	pllbuf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0;
+	return 0;
+}
+
+static int panasonic_cofdm_env57h1xd5_pll_set(struct dvb_frontend_parameters *fep, u8 pllbuf[4])
+{
+	u32 freq_khz = fep->frequency / 1000;
+	u32 tfreq = ((freq_khz + 36125)*6 + 500) / 1000;
+	u8 TA, T210, R210, ctrl1, cp210, p4321;
+	if (freq_khz > 858000) {
+		err("frequency cannot be larger than 858 MHz.");
+		return -EINVAL;
+	}
+
+	// contol data 1 : 1 | T/A=1 | T2,T1,T0 = 0,0,0 | R2,R1,R0 = 0,1,0
+	TA = 1;
+	T210 = 0;
+	R210 = 0x2;
+	ctrl1 = (1 << 7) | (TA << 6) | (T210 << 3) | R210;
+
+// ********    CHARGE PUMP CONFIG vs RF FREQUENCIES     *****************
+	if (freq_khz < 470000)
+		cp210 = 2;  // VHF Low and High band ch E12 to E4 to E12
+	else if (freq_khz < 526000)
+		cp210 = 4;  // UHF band Ch E21 to E27
+	else // if (freq < 862000000)
+		cp210 = 5;  // UHF band ch E28 to E69
+
+//*********************    BW select  *******************************
+	if (freq_khz < 153000)
+		p4321  = 1; // BW selected for VHF low
+	else if (freq_khz < 470000)
+		p4321  = 2; // BW selected for VHF high E5 to E12
+	else // if (freq < 862000000)
+		p4321  = 4; // BW selection for UHF E21 to E69
+
+	pllbuf[0] = (tfreq >> 8) & 0xff;
+	pllbuf[1] = (tfreq >> 0) & 0xff;
+	pllbuf[2] = 0xff & ctrl1;
+	pllbuf[3] =  (cp210 << 5) | (p4321);
+
+	return 0;
+}
+
+/*
+ *			    7	6		5	4	3	2	1	0
+ * Address Byte             1	1		0	0	0	MA1	MA0	R/~W=0
+ *
+ * Program divider byte 1   0	n14		n13	n12	n11	n10	n9	n8
+ * Program divider byte 2	n7	n6		n5	n4	n3	n2	n1	n0
+ *
+ * Control byte 1           1	T/A=1	T2	T1	T0	R2	R1	R0
+ *                          1	T/A=0	0	0	ATC	AL2	AL1	AL0
+ *
+ * Control byte 2           CP2	CP1		CP0	BS5	BS4	BS3	BS2	BS1
+ *
+ * MA0/1 = programmable address bits
+ * R/~W  = read/write bit (0 for writing)
+ * N14-0 = programmable LO frequency
+ *
+ * T/A   = test AGC bit (0 = next 6 bits AGC setting,
+ *                       1 = next 6 bits test and reference divider ratio settings)
+ * T2-0  = test bits
+ * R2-0  = reference divider ratio and programmable frequency step
+ * ATC   = AGC current setting and time constant
+ *         ATC = 0: AGC current = 220nA, AGC time constant = 2s
+ *         ATC = 1: AGC current = 9uA, AGC time constant = 50ms
+ * AL2-0 = AGC take-over point bits
+ * CP2-0 = charge pump current
+ * BS5-1 = PMOS ports control bits;
+ *             BSn = 0 corresponding port is off, high-impedance state (at power-on)
+ *             BSn = 1 corresponding port is on
+ */
+static int panasonic_cofdm_env77h11d5_tda6650_init(struct dvb_frontend *fe, u8 pllbuf[4])
+{
+	pllbuf[0] = 0x0b;
+	pllbuf[1] = 0xf5;
+	pllbuf[2] = 0x85;
+	pllbuf[3] = 0xab;
+	return 0;
+}
+
+static int panasonic_cofdm_env77h11d5_tda6650_set (struct dvb_frontend_parameters *fep,u8 pllbuf[4])
+{
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = fep->frequency + 36166000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000)
+		cp = 3;
+	else if (tuner_frequency < 160000000)
+		cp = 5;
+	else if (tuner_frequency < 200000000)
+		cp = 6;
+	else if (tuner_frequency < 290000000)
+		cp = 3;
+	else if (tuner_frequency < 420000000)
+		cp = 5;
+	else if (tuner_frequency < 480000000)
+		cp = 6;
+	else if (tuner_frequency < 620000000)
+		cp = 3;
+	else if (tuner_frequency < 830000000)
+		cp = 5;
+	else if (tuner_frequency < 895000000)
+		cp = 7;
+	else
+		return -EINVAL;
+
+	// determine band
+	if (fep->frequency < 49000000)
+		return -EINVAL;
+	else if (fep->frequency < 161000000)
+		band = 1;
+	else if (fep->frequency < 444000000)
+		band = 2;
+	else if (fep->frequency < 861000000)
+		band = 4;
+	else
+		return -EINVAL;
+
+	// setup PLL filter
+	switch (fep->u.ofdm.bandwidth) {
+		case BANDWIDTH_6_MHZ:
+		case BANDWIDTH_7_MHZ:
+			filter = 0;
+			break;
+		case BANDWIDTH_8_MHZ:
+			filter = 1;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	// calculate divisor
+	// ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+	tuner_frequency = (((fep->frequency / 1000) * 6) + 217496) / 1000;
+
+	// setup tuner buffer
+	pllbuf[0] = (tuner_frequency >> 8) & 0x7f;
+	pllbuf[1] = tuner_frequency & 0xff;
+	pllbuf[2] = 0xca;
+	pllbuf[3] = (cp << 5) | (filter << 3) | band;
+	return 0;
+}
+
+/*
+ *			    7	6	5	4	3	2	1	0
+ * Address Byte             1	1	0	0	0	MA1	MA0	R/~W=0
+ *
+ * Program divider byte 1   0	n14	n13	n12	n11	n10	n9	n8
+ * Program divider byte 2	n7	n6	n5	n4	n3	n2	n1	n0
+ *
+ * Control byte             1	CP	T2	T1	T0	RSA	RSB	OS
+ *
+ * Band Switch byte         X	X	X	P4	P3	P2	P1	P0
+ *
+ * Auxiliary byte           ATC	AL2	AL1	AL0	0	0	0	0
+ *
+ * Address: MA1	MA0	Address
+ *          0	0	c0
+ *          0	1	c2 (always valid)
+ *          1	0	c4
+ *          1	1	c6
+ */
+static int lg_tdtp_e102p_tua6034(struct dvb_frontend_parameters* fep, u8 pllbuf[4])
+{
+	u32 div;
+	u8 p210, p3;
+
+#define TUNER_MUL 62500
+
+	div = (fep->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;
+//	div = ((fep->frequency/1000 + 36166) * 6) / 1000;
+
+	if (fep->frequency < 174500000)
+		p210 = 1; // not supported by the tdtp_e102p
+	else if (fep->frequency < 230000000) // VHF
+		p210 = 2;
+	else
+		p210 = 4;
+
+	if (fep->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+		p3 = 0;
+	else
+		p3 = 1;
+
+	pllbuf[0] = (div >> 8) & 0x7f;
+	pllbuf[1] = div & 0xff;
+	pllbuf[2] = 0xce;
+//	pllbuf[2] = 0xcc;
+	pllbuf[3] = (p3 << 3) | p210;
+
+	return 0;
+}
+
+static int lg_tdtp_e102p_mt352_demod_init(struct dvb_frontend *fe)
+{
+	static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d };
+	static u8 mt352_reset[] = { 0x50, 0x80 };
+	static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 };
+	static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 };
+	static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 };
+
+	static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff };
+	static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff };
+	static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 };
+	static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 };
+	static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f };
+
+	static u8 mt352_acq_ctl[] = { 0x53, 0x50 };
+	static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 };
+
+	mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
+	udelay(2000);
+	mt352_write(fe, mt352_reset, sizeof(mt352_reset));
+	mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio));
+
+	mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+	mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
+
+	mt352_write(fe, mt352_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1));
+	mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2));
+	mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3));
+	mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4));
+	mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5));
+
+	mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl));
+	mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1));
+
+	return 0;
+}
+
+static int dibusb_general_demod_init(struct dvb_frontend *fe)
+{
+	struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+	switch (dib->dibdev->dev_cl->id) {
+		case UMT2_0:
+			return lg_tdtp_e102p_mt352_demod_init(fe);
+		default: /* other device classes do not have device specific demod inits */
+			break;
+	}
+	return 0;
+}
+
+static u8 dibusb_general_pll_addr(struct dvb_frontend *fe)
+{
+	struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+	return dib->tuner->pll_addr;
+}
+
+static int dibusb_pll_i2c_helper(struct usb_dibusb *dib, u8 pll_buf[5], u8 buf[4])
+{
+	if (pll_buf == NULL) {
+		struct i2c_msg msg = {
+			.addr = dib->tuner->pll_addr,
+			.flags = 0,
+			.buf = buf,
+			.len = sizeof(buf)
+		};
+		if (i2c_transfer (&dib->i2c_adap, &msg, 1) != 1)
+			return -EIO;
+		msleep(1);
+	} else {
+		pll_buf[0] = dib->tuner->pll_addr << 1;
+		memcpy(&pll_buf[1],buf,4);
+	}
+
+	return 0;
+}
+
+static int dibusb_general_pll_init(struct dvb_frontend *fe,
+		u8 pll_buf[5])
+{
+	struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+	u8 buf[4];
+	int ret=0;
+	switch (dib->tuner->id) {
+		case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5:
+			ret = panasonic_cofdm_env77h11d5_tda6650_init(fe,buf);
+			break;
+		default:
+			break;
+	}
+
+	if (ret)
+		return ret;
+
+	return dibusb_pll_i2c_helper(dib,pll_buf,buf);
+}
+
+static int dibusb_general_pll_set(struct dvb_frontend *fe,
+		struct dvb_frontend_parameters *fep, u8 pll_buf[5])
+{
+	struct usb_dibusb* dib = (struct usb_dibusb*) fe->dvb->priv;
+	u8 buf[4];
+	int ret=0;
+
+	switch (dib->tuner->id) {
+		case DIBUSB_TUNER_CABLE_THOMSON:
+			ret = thomson_cable_eu_pll_set(fep, buf);
+			break;
+		case DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5:
+			ret = panasonic_cofdm_env57h1xd5_pll_set(fep, buf);
+			break;
+		case DIBUSB_TUNER_CABLE_LG_TDTP_E102P:
+			ret = lg_tdtp_e102p_tua6034(fep, buf);
+			break;
+		case DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5:
+			ret = panasonic_cofdm_env77h11d5_tda6650_set(fep,buf);
+			break;
+		default:
+			warn("no pll programming routine found for tuner %d.\n",dib->tuner->id);
+			ret = -ENODEV;
+			break;
+	}
+
+	if (ret)
+		return ret;
+
+	return dibusb_pll_i2c_helper(dib,pll_buf,buf);
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c
new file mode 100644
index 0000000..504ba47
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-firmware.c
@@ -0,0 +1,87 @@
+/*
+ * dvb-dibusb-firmware.c is part of the driver for mobile USB Budget DVB-T devices 
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for downloading the firmware to the device.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/firmware.h>
+#include <linux/usb.h>
+
+/*
+ * load a firmware packet to the device 
+ */
+static int dibusb_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 len)
+{
+	return usb_control_msg(udev, usb_sndctrlpipe(udev,0),
+			0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
+}
+
+int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev)
+{
+	const struct firmware *fw = NULL;
+	u16 addr;
+	u8 *b,*p;
+	int ret = 0,i;
+	
+	if ((ret = request_firmware(&fw, dibdev->dev_cl->firmware, &udev->dev)) != 0) {
+		err("did not find the firmware file. (%s) "
+			"Please see linux/Documentation/dvb/ for more details on firmware-problems.",
+			dibdev->dev_cl->firmware);
+		return ret;
+	}
+
+	info("downloading firmware from file '%s'.",dibdev->dev_cl->firmware);
+	
+	p = kmalloc(fw->size,GFP_KERNEL);	
+	if (p != NULL) {
+		u8 reset;
+		/*
+		 * you cannot use the fw->data as buffer for 
+		 * usb_control_msg, a new buffer has to be
+		 * created
+		 */
+		memcpy(p,fw->data,fw->size);
+
+		/* stop the CPU */
+		reset = 1;
+		if ((ret = dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1)) != 1) 
+			err("could not stop the USB controller CPU.");
+		for(i = 0; p[i+3] == 0 && i < fw->size; ) { 
+			b = (u8 *) &p[i];
+			addr = *((u16 *) &b[1]);
+
+			ret = dibusb_writemem(udev,addr,&b[4],b[0]);
+		
+			if (ret != b[0]) {
+				err("error while transferring firmware "
+					"(transferred size: %d, block size: %d)",
+					ret,b[0]);
+				ret = -EINVAL;
+				break;
+			}
+			i += 5 + b[0];
+		}
+		/* length in ret */
+		if (ret > 0)
+			ret = 0;
+		/* restart the CPU */
+		reset = 0;
+		if (ret || dibusb_writemem(udev,dibdev->dev_cl->usb_ctrl->cpu_cs_register,&reset,1) != 1) {
+			err("could not restart the USB controller CPU.");
+			ret = -EINVAL;
+		}
+
+		kfree(p);
+	} else { 
+		ret = -ENOMEM;
+	}
+	release_firmware(fw);
+
+	return ret;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-remote.c b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
new file mode 100644
index 0000000..9dc8b15
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-remote.c
@@ -0,0 +1,316 @@
+/*
+ * dvb-dibusb-remote.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for handling the event device on the software
+ * side and the remote control on the hardware side.
+ */
+#include "dvb-dibusb.h"
+
+/* Table to map raw key codes to key events.  This should not be hard-wired
+   into the kernel.  */
+static const struct { u8 c0, c1, c2; uint32_t key; } nec_rc_keys [] =
+{
+	/* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */
+	{ 0x00, 0xff, 0x16, KEY_POWER },
+	{ 0x00, 0xff, 0x10, KEY_MUTE },
+	{ 0x00, 0xff, 0x03, KEY_1 },
+	{ 0x00, 0xff, 0x01, KEY_2 },
+	{ 0x00, 0xff, 0x06, KEY_3 },
+	{ 0x00, 0xff, 0x09, KEY_4 },
+	{ 0x00, 0xff, 0x1d, KEY_5 },
+	{ 0x00, 0xff, 0x1f, KEY_6 },
+	{ 0x00, 0xff, 0x0d, KEY_7 },
+	{ 0x00, 0xff, 0x19, KEY_8 },
+	{ 0x00, 0xff, 0x1b, KEY_9 },
+	{ 0x00, 0xff, 0x15, KEY_0 },
+	{ 0x00, 0xff, 0x05, KEY_CHANNELUP },
+	{ 0x00, 0xff, 0x02, KEY_CHANNELDOWN },
+	{ 0x00, 0xff, 0x1e, KEY_VOLUMEUP },
+	{ 0x00, 0xff, 0x0a, KEY_VOLUMEDOWN },
+	{ 0x00, 0xff, 0x11, KEY_RECORD },
+	{ 0x00, 0xff, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */
+	{ 0x00, 0xff, 0x14, KEY_PLAY },
+	{ 0x00, 0xff, 0x1a, KEY_STOP },
+	{ 0x00, 0xff, 0x40, KEY_REWIND },
+	{ 0x00, 0xff, 0x12, KEY_FASTFORWARD },
+	{ 0x00, 0xff, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */
+	{ 0x00, 0xff, 0x4c, KEY_PAUSE },
+	{ 0x00, 0xff, 0x4d, KEY_SCREEN }, /* Full screen mode. */
+	{ 0x00, 0xff, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
+	/* additional keys TwinHan VisionPlus, the Artec seemingly not have */
+	{ 0x00, 0xff, 0x0c, KEY_CANCEL }, /* Cancel */
+	{ 0x00, 0xff, 0x1c, KEY_EPG }, /* EPG */
+	{ 0x00, 0xff, 0x00, KEY_TAB }, /* Tab */
+	{ 0x00, 0xff, 0x48, KEY_INFO }, /* Preview */
+	{ 0x00, 0xff, 0x04, KEY_LIST }, /* RecordList */
+	{ 0x00, 0xff, 0x0f, KEY_TEXT }, /* Teletext */
+	/* Key codes for the KWorld/ADSTech/JetWay remote. */
+	{ 0x86, 0x6b, 0x12, KEY_POWER },
+	{ 0x86, 0x6b, 0x0f, KEY_SELECT }, /* source */
+	{ 0x86, 0x6b, 0x0c, KEY_UNKNOWN }, /* scan */
+	{ 0x86, 0x6b, 0x0b, KEY_EPG },
+	{ 0x86, 0x6b, 0x10, KEY_MUTE },
+	{ 0x86, 0x6b, 0x01, KEY_1 },
+	{ 0x86, 0x6b, 0x02, KEY_2 },
+	{ 0x86, 0x6b, 0x03, KEY_3 },
+	{ 0x86, 0x6b, 0x04, KEY_4 },
+	{ 0x86, 0x6b, 0x05, KEY_5 },
+	{ 0x86, 0x6b, 0x06, KEY_6 },
+	{ 0x86, 0x6b, 0x07, KEY_7 },
+	{ 0x86, 0x6b, 0x08, KEY_8 },
+	{ 0x86, 0x6b, 0x09, KEY_9 },
+	{ 0x86, 0x6b, 0x0a, KEY_0 },
+	{ 0x86, 0x6b, 0x18, KEY_ZOOM },
+	{ 0x86, 0x6b, 0x1c, KEY_UNKNOWN }, /* preview */
+	{ 0x86, 0x6b, 0x13, KEY_UNKNOWN }, /* snap */
+	{ 0x86, 0x6b, 0x00, KEY_UNDO },
+	{ 0x86, 0x6b, 0x1d, KEY_RECORD },
+	{ 0x86, 0x6b, 0x0d, KEY_STOP },
+	{ 0x86, 0x6b, 0x0e, KEY_PAUSE },
+	{ 0x86, 0x6b, 0x16, KEY_PLAY },
+	{ 0x86, 0x6b, 0x11, KEY_BACK },
+	{ 0x86, 0x6b, 0x19, KEY_FORWARD },
+	{ 0x86, 0x6b, 0x14, KEY_UNKNOWN }, /* pip */
+	{ 0x86, 0x6b, 0x15, KEY_ESC },
+	{ 0x86, 0x6b, 0x1a, KEY_UP },
+	{ 0x86, 0x6b, 0x1e, KEY_DOWN },
+	{ 0x86, 0x6b, 0x1f, KEY_LEFT },
+	{ 0x86, 0x6b, 0x1b, KEY_RIGHT },
+};
+
+/* Hauppauge NOVA-T USB2 keys */
+static const struct { u16 raw; uint32_t key; } haupp_rc_keys [] = {
+	{ 0xddf, KEY_GOTO },
+	{ 0xdef, KEY_POWER },
+	{ 0xce7, KEY_TV },
+	{ 0xcc7, KEY_VIDEO },
+	{ 0xccf, KEY_AUDIO },
+	{ 0xcd7, KEY_MEDIA },
+	{ 0xcdf, KEY_EPG },
+	{ 0xca7, KEY_UP },
+	{ 0xc67, KEY_RADIO },
+	{ 0xcb7, KEY_LEFT },
+	{ 0xd2f, KEY_OK },
+	{ 0xcbf, KEY_RIGHT },
+	{ 0xcff, KEY_BACK },
+	{ 0xcaf, KEY_DOWN },
+	{ 0xc6f, KEY_MENU },
+	{ 0xc87, KEY_VOLUMEUP },
+	{ 0xc8f, KEY_VOLUMEDOWN },
+	{ 0xc97, KEY_CHANNEL },
+	{ 0xc7f, KEY_MUTE },
+	{ 0xd07, KEY_CHANNELUP },
+	{ 0xd0f, KEY_CHANNELDOWN },
+	{ 0xdbf, KEY_RECORD },
+	{ 0xdb7, KEY_STOP },
+	{ 0xd97, KEY_REWIND },
+	{ 0xdaf, KEY_PLAY },
+	{ 0xda7, KEY_FASTFORWARD },
+	{ 0xd27, KEY_LAST }, /* Skip backwards */
+	{ 0xd87, KEY_PAUSE },
+	{ 0xcf7, KEY_NEXT },
+	{ 0xc07, KEY_0 },
+	{ 0xc0f, KEY_1 },
+	{ 0xc17, KEY_2 },
+	{ 0xc1f, KEY_3 },
+	{ 0xc27, KEY_4 },
+	{ 0xc2f, KEY_5 },
+	{ 0xc37, KEY_6 },
+	{ 0xc3f, KEY_7 },
+	{ 0xc47, KEY_8 },
+	{ 0xc4f, KEY_9 },
+	{ 0xc57, KEY_KPASTERISK },
+	{ 0xc77, KEY_GRAVE }, /* # */
+	{ 0xc5f, KEY_RED },
+	{ 0xd77, KEY_GREEN },
+	{ 0xdc7, KEY_YELLOW },
+	{ 0xd4f, KEY_BLUE},
+};
+
+static int dibusb_key2event_nec(struct usb_dibusb *dib,u8 rb[5])
+{
+	int i;
+	switch (rb[0]) {
+		case DIBUSB_RC_NEC_KEY_PRESSED:
+			/* rb[1-3] is the actual key, rb[4] is a checksum */
+			deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+				rb[1], rb[2], rb[3], rb[4]);
+
+			if ((0xff - rb[3]) != rb[4]) {
+				deb_rc("remote control checksum failed.\n");
+				break;
+			}
+
+			/* See if we can match the raw key code. */
+			for (i = 0; i < sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++) {
+				if (nec_rc_keys[i].c0 == rb[1] &&
+					nec_rc_keys[i].c1 == rb[2] &&
+					nec_rc_keys[i].c2 == rb[3]) {
+
+					dib->last_event = nec_rc_keys[i].key;
+					return 1;
+				}
+			}
+			break;
+		case DIBUSB_RC_NEC_KEY_REPEATED:
+			/* rb[1]..rb[4] are always zero.*/
+			/* Repeats often seem to occur so for the moment just ignore this. */
+			return 0;
+		case DIBUSB_RC_NEC_EMPTY: /* No (more) remote control keys. */
+		default:
+			break;
+	}
+	return -1;
+}
+
+static int dibusb_key2event_hauppauge(struct usb_dibusb *dib,u8 rb[4])
+{
+	u16 raw;
+	int i,state;
+	switch (rb[0]) {
+		case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED:
+			raw = ((rb[1] & 0x0f) << 8) | rb[2];
+
+			state = !!(rb[1] & 0x40);
+
+			deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to %04x state: %d\n",rb[1],rb[2],rb[3],raw,state);
+			for (i = 0; i < sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++) {
+				if (haupp_rc_keys[i].raw == raw) {
+					if (dib->last_event == haupp_rc_keys[i].key &&
+						dib->last_state == state) {
+						deb_rc("key repeat\n");
+						return 0;
+					} else {
+						dib->last_event = haupp_rc_keys[i].key;
+						dib->last_state = state;
+						return 1;
+					}
+				}
+			}
+
+			break;
+		case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY:
+		default:
+			break;
+	}
+	return -1;
+}
+
+/*
+ * Read the remote control and feed the appropriate event.
+ * NEC protocol is used for remote controls
+ */
+static int dibusb_read_remote_control(struct usb_dibusb *dib)
+{
+	u8 b[1] = { DIBUSB_REQ_POLL_REMOTE }, rb[5];
+	int ret,event = 0;
+
+	if ((ret = dibusb_readwrite_usb(dib,b,1,rb,5)))
+		return ret;
+
+	switch (dib->dibdev->dev_cl->remote_type) {
+		case DIBUSB_RC_NEC_PROTOCOL:
+			event = dibusb_key2event_nec(dib,rb);
+			break;
+		case DIBUSB_RC_HAUPPAUGE_PROTO:
+			event = dibusb_key2event_hauppauge(dib,rb);
+		default:
+			break;
+	}
+
+	/* key repeat */
+	if (event == 0)
+		if (++dib->repeat_key_count < dib->rc_key_repeat_count) {
+			deb_rc("key repeat dropped. (%d)\n",dib->repeat_key_count);
+			event = -1; /* skip this key repeat */
+		}
+
+	if (event == 1 || event == 0) {
+		deb_rc("Translated key 0x%04x\n",event);
+
+		/* Signal down and up events for this key. */
+		input_report_key(&dib->rc_input_dev, dib->last_event, 1);
+		input_report_key(&dib->rc_input_dev, dib->last_event, 0);
+		input_sync(&dib->rc_input_dev);
+
+		if (event == 1)
+			dib->repeat_key_count = 0;
+	}
+	return 0;
+}
+
+/* Remote-control poll function - called every dib->rc_query_interval ms to see
+   whether the remote control has received anything. */
+static void dibusb_remote_query(void *data)
+{
+	struct usb_dibusb *dib = (struct usb_dibusb *) data;
+	/* TODO: need a lock here.  We can simply skip checking for the remote control
+	   if we're busy. */
+	dibusb_read_remote_control(dib);
+	schedule_delayed_work(&dib->rc_query_work,
+			      msecs_to_jiffies(dib->rc_query_interval));
+}
+
+int dibusb_remote_init(struct usb_dibusb *dib)
+{
+	int i;
+
+	if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
+		return 0;
+	
+	/* Initialise the remote-control structures.*/
+	init_input_dev(&dib->rc_input_dev);
+
+	dib->rc_input_dev.evbit[0] = BIT(EV_KEY);
+	dib->rc_input_dev.keycodesize = sizeof(unsigned char);
+	dib->rc_input_dev.keycodemax = KEY_MAX;
+	dib->rc_input_dev.name = DRIVER_DESC " remote control";
+
+	switch (dib->dibdev->dev_cl->remote_type) {
+		case DIBUSB_RC_NEC_PROTOCOL:
+			for (i=0; i<sizeof(nec_rc_keys)/sizeof(nec_rc_keys[0]); i++)
+				set_bit(nec_rc_keys[i].key, dib->rc_input_dev.keybit);
+			break;
+		case DIBUSB_RC_HAUPPAUGE_PROTO:
+			for (i=0; i<sizeof(haupp_rc_keys)/sizeof(haupp_rc_keys[0]); i++)
+				set_bit(haupp_rc_keys[i].key, dib->rc_input_dev.keybit);
+			break;
+		default:
+			break;
+	}
+
+
+	input_register_device(&dib->rc_input_dev);
+
+	INIT_WORK(&dib->rc_query_work, dibusb_remote_query, dib);
+
+	/* Start the remote-control polling. */
+	if (dib->rc_query_interval < 40)
+		dib->rc_query_interval = 100; /* default */
+
+	info("schedule remote query interval to %d msecs.",dib->rc_query_interval);
+	schedule_delayed_work(&dib->rc_query_work,msecs_to_jiffies(dib->rc_query_interval));
+
+	dib->init_state |= DIBUSB_STATE_REMOTE;
+	
+	return 0;
+}
+
+int dibusb_remote_exit(struct usb_dibusb *dib)
+{
+	if (dib->dibdev->dev_cl->remote_type == DIBUSB_RC_NO)
+		return 0;
+
+	if (dib->init_state & DIBUSB_STATE_REMOTE) {
+		cancel_delayed_work(&dib->rc_query_work);
+		flush_scheduled_work();
+		input_unregister_device(&dib->rc_input_dev);
+	}
+	dib->init_state &= ~DIBUSB_STATE_REMOTE;
+	return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb-usb.c b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c
new file mode 100644
index 0000000..642f059
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb-usb.c
@@ -0,0 +1,303 @@
+/*
+ * dvb-dibusb-usb.c is part of the driver for mobile USB Budget DVB-T devices
+ * based on reference design made by DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * see dvb-dibusb-core.c for more copyright details.
+ *
+ * This file contains functions for initializing and handling the
+ * usb specific stuff.
+ */
+#include "dvb-dibusb.h"
+
+#include <linux/version.h>
+#include <linux/pci.h>
+
+int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf,
+		u16 rlen)
+{
+	int actlen,ret = -ENOMEM;
+
+	if (wbuf == NULL || wlen == 0)
+		return -EINVAL;
+
+	if ((ret = down_interruptible(&dib->usb_sem)))
+		return ret;
+
+	debug_dump(wbuf,wlen);
+
+	ret = usb_bulk_msg(dib->udev,usb_sndbulkpipe(dib->udev,
+			dib->dibdev->dev_cl->pipe_cmd), wbuf,wlen,&actlen,
+			DIBUSB_I2C_TIMEOUT);
+
+	if (ret)
+		err("bulk message failed: %d (%d/%d)",ret,wlen,actlen);
+	else
+		ret = actlen != wlen ? -1 : 0;
+
+	/* an answer is expected, and no error before */
+	if (!ret && rbuf && rlen) {
+		ret = usb_bulk_msg(dib->udev,usb_rcvbulkpipe(dib->udev,
+				dib->dibdev->dev_cl->pipe_cmd),rbuf,rlen,&actlen,
+				DIBUSB_I2C_TIMEOUT);
+
+		if (ret)
+			err("recv bulk message failed: %d",ret);
+		else {
+			deb_alot("rlen: %d\n",rlen);
+			debug_dump(rbuf,actlen);
+		}
+	}
+
+	up(&dib->usb_sem);
+	return ret;
+}
+
+/*
+ * Cypress controls
+ */
+int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len)
+{
+	return dibusb_readwrite_usb(dib,buf,len,NULL,0);
+}
+
+#if 0
+/*
+ * #if 0'ing the following functions as they are not in use _now_,
+ * but probably will be sometime.
+ */
+/*
+ * do not use this, just a workaround for a bug,
+ * which will hopefully never occur :).
+ */
+int dibusb_interrupt_read_loop(struct usb_dibusb *dib)
+{
+	u8 b[1] = { DIBUSB_REQ_INTR_READ };
+	return dibusb_write_usb(dib,b,1);
+}
+#endif
+
+/*
+ * ioctl for the firmware
+ */
+static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen)
+{
+	u8 b[34];
+	int size = plen > 32 ? 32 : plen;
+	memset(b,0,34);
+	b[0] = DIBUSB_REQ_SET_IOCTL;
+	b[1] = cmd;
+
+	if (size > 0)
+		memcpy(&b[2],param,size);
+
+	return dibusb_write_usb(dib,b,34); //2+size);
+}
+
+/*
+ * ioctl for power control
+ */
+int dibusb_hw_wakeup(struct dvb_frontend *fe)
+{
+	struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv;
+	u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP };
+	deb_info("dibusb-device is getting up.\n");
+
+	switch (dib->dibdev->dev_cl->id) {
+		case DTT200U:
+			break;
+		default:
+			dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1);
+			break;
+	}
+
+	if (dib->fe_init)
+		return dib->fe_init(fe);
+
+	return 0;
+}
+
+int dibusb_hw_sleep(struct dvb_frontend *fe)
+{
+	struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv;
+	u8 b[1] = { DIBUSB_IOCTL_POWER_SLEEP };
+	deb_info("dibusb-device is going to bed.\n");
+	/* workaround, something is wrong, when dibusb 1.1 device are going to bed too late */
+	switch (dib->dibdev->dev_cl->id) {
+		case DIBUSB1_1:
+		case NOVAT_USB2:
+		case DTT200U:
+			break;
+		default:
+			dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1);
+			break;
+	}
+	if (dib->fe_sleep)
+		return dib->fe_sleep(fe);
+
+	return 0;
+}
+
+int dibusb_set_streaming_mode(struct usb_dibusb *dib,u8 mode)
+{
+	u8 b[2] = { DIBUSB_REQ_SET_STREAMING_MODE, mode };
+	return dibusb_readwrite_usb(dib,b,2,NULL,0);
+}
+
+static int dibusb_urb_kill(struct usb_dibusb *dib)
+{
+	int i;
+deb_info("trying to kill urbs\n");
+	if (dib->init_state & DIBUSB_STATE_URB_SUBMIT) {
+		for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+			deb_info("killing URB no. %d.\n",i);
+
+			/* stop the URB */
+			usb_kill_urb(dib->urb_list[i]);
+		}
+	} else
+	deb_info(" URBs not killed.\n");
+	dib->init_state &= ~DIBUSB_STATE_URB_SUBMIT;
+	return 0;
+}
+
+static int dibusb_urb_submit(struct usb_dibusb *dib)
+{
+	int i,ret;
+	if (dib->init_state & DIBUSB_STATE_URB_INIT) {
+		for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+			deb_info("submitting URB no. %d\n",i);
+			if ((ret = usb_submit_urb(dib->urb_list[i],GFP_ATOMIC))) {
+				err("could not submit buffer urb no. %d - get them all back\n",i);
+				dibusb_urb_kill(dib);
+				return ret;
+			}
+			dib->init_state |= DIBUSB_STATE_URB_SUBMIT;
+		}
+	}
+	return 0;
+}
+
+int dibusb_streaming(struct usb_dibusb *dib,int onoff)
+{
+	if (onoff)
+		dibusb_urb_submit(dib);
+	else
+		dibusb_urb_kill(dib);
+
+	switch (dib->dibdev->dev_cl->id) {
+		case DIBUSB2_0:
+		case DIBUSB2_0B:
+		case NOVAT_USB2:
+		case UMT2_0:
+			if (onoff)
+				return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_ENABLE_STREAM,NULL,0);
+			else
+				return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_DISABLE_STREAM,NULL,0);
+			break;
+		default:
+			break;
+	}
+	return 0;
+}
+
+int dibusb_urb_init(struct usb_dibusb *dib)
+{
+	int i,bufsize,def_pid_parse = 1;
+
+	/*
+	 * when reloading the driver w/o replugging the device
+	 * a timeout occures, this helps
+	 */
+	usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd));
+	usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd));
+	usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data));
+
+	/* allocate the array for the data transfer URBs */
+	dib->urb_list = kmalloc(dib->dibdev->dev_cl->urb_count*sizeof(struct urb *),GFP_KERNEL);
+	if (dib->urb_list == NULL)
+		return -ENOMEM;
+	memset(dib->urb_list,0,dib->dibdev->dev_cl->urb_count*sizeof(struct urb *));
+
+	dib->init_state |= DIBUSB_STATE_URB_LIST;
+
+	bufsize = dib->dibdev->dev_cl->urb_count*dib->dibdev->dev_cl->urb_buffer_size;
+	deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize);
+	/* allocate the actual buffer for the URBs */
+	if ((dib->buffer = pci_alloc_consistent(NULL,bufsize,&dib->dma_handle)) == NULL) {
+		deb_info("not enough memory.\n");
+		return -ENOMEM;
+	}
+	deb_info("allocation complete\n");
+	memset(dib->buffer,0,bufsize);
+
+	dib->init_state |= DIBUSB_STATE_URB_BUF;
+
+	/* allocate and submit the URBs */
+	for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+		if (!(dib->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) {
+			return -ENOMEM;
+		}
+
+		usb_fill_bulk_urb( dib->urb_list[i], dib->udev,
+				usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data),
+				&dib->buffer[i*dib->dibdev->dev_cl->urb_buffer_size],
+				dib->dibdev->dev_cl->urb_buffer_size,
+				dibusb_urb_complete, dib);
+
+		dib->urb_list[i]->transfer_flags = 0;
+
+		dib->init_state |= DIBUSB_STATE_URB_INIT;
+	}
+
+	/* dib->pid_parse here contains the value of the module parameter */
+	/* decide if pid parsing can be deactivated:
+	 * is possible (by device type) and wanted (by user)
+	 */
+	switch (dib->dibdev->dev_cl->id) {
+		case DIBUSB2_0:
+		case DIBUSB2_0B:
+			if (dib->udev->speed == USB_SPEED_HIGH && !dib->pid_parse) {
+				def_pid_parse = 0;
+				info("running at HIGH speed, will deliver the complete TS.");
+			} else
+				info("will use pid_parsing.");
+			break;
+		default:
+			break;
+	}
+	/* from here on it contains the device and user decision */
+	dib->pid_parse = def_pid_parse;
+
+	return 0;
+}
+
+int dibusb_urb_exit(struct usb_dibusb *dib)
+{
+	int i;
+
+	dibusb_urb_kill(dib);
+
+	if (dib->init_state & DIBUSB_STATE_URB_LIST) {
+		for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) {
+			if (dib->urb_list[i] != NULL) {
+				deb_info("freeing URB no. %d.\n",i);
+				/* free the URBs */
+				usb_free_urb(dib->urb_list[i]);
+			}
+		}
+		/* free the urb array */
+		kfree(dib->urb_list);
+		dib->init_state &= ~DIBUSB_STATE_URB_LIST;
+	}
+
+	if (dib->init_state & DIBUSB_STATE_URB_BUF)
+		pci_free_consistent(NULL,
+			dib->dibdev->dev_cl->urb_buffer_size*dib->dibdev->dev_cl->urb_count,
+			dib->buffer,dib->dma_handle);
+
+	dib->init_state &= ~DIBUSB_STATE_URB_BUF;
+	dib->init_state &= ~DIBUSB_STATE_URB_INIT;
+	return 0;
+}
diff --git a/drivers/media/dvb/dibusb/dvb-dibusb.h b/drivers/media/dvb/dibusb/dvb-dibusb.h
new file mode 100644
index 0000000..52cd35d
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-dibusb.h
@@ -0,0 +1,327 @@
+/*
+ * dvb-dibusb.h
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.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.
+ *
+ * for more information see dvb-dibusb-core.c .
+ */
+#ifndef __DVB_DIBUSB_H__
+#define __DVB_DIBUSB_H__
+
+#include <linux/input.h>
+#include <linux/config.h>
+#include <linux/usb.h>
+
+#include "dvb_frontend.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+#include "dmxdev.h"
+
+#include "dib3000.h"
+#include "mt352.h"
+
+/* debug */
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define dprintk(level,args...) \
+	    do { if ((dvb_dibusb_debug & level)) { printk(args); } } while (0)
+
+#define debug_dump(b,l) {\
+	int i; \
+	for (i = 0; i < l; i++) deb_xfer("%02x ", b[i]); \
+	deb_xfer("\n");\
+}
+
+#else
+#define dprintk(args...)
+#define debug_dump(b,l)
+#endif
+
+extern int dvb_dibusb_debug;
+
+/* Version information */
+#define DRIVER_VERSION "0.3"
+#define DRIVER_DESC "DiBcom based USB Budget DVB-T device"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_xfer(args...) dprintk(0x02,args)
+#define deb_alot(args...) dprintk(0x04,args)
+#define deb_ts(args...)   dprintk(0x08,args)
+#define deb_err(args...)  dprintk(0x10,args)
+#define deb_rc(args...)   dprintk(0x20,args)
+
+/* generic log methods - taken from usb.h */
+#undef err
+#define err(format, arg...)  printk(KERN_ERR     "dvb-dibusb: " format "\n" , ## arg)
+#undef info
+#define info(format, arg...) printk(KERN_INFO    "dvb-dibusb: " format "\n" , ## arg)
+#undef warn
+#define warn(format, arg...) printk(KERN_WARNING "dvb-dibusb: " format "\n" , ## arg)
+
+struct dibusb_usb_controller {
+	const char *name;       /* name of the usb controller */
+	u16 cpu_cs_register;    /* needs to be restarted, when the firmware has been downloaded. */
+};
+
+typedef enum {
+	DIBUSB1_1 = 0,
+	DIBUSB1_1_AN2235,
+	DIBUSB2_0,
+	UMT2_0,
+	DIBUSB2_0B,
+	NOVAT_USB2,
+	DTT200U,
+} dibusb_class_t;
+
+typedef enum {
+	DIBUSB_TUNER_CABLE_THOMSON = 0,
+	DIBUSB_TUNER_COFDM_PANASONIC_ENV57H1XD5,
+	DIBUSB_TUNER_CABLE_LG_TDTP_E102P,
+	DIBUSB_TUNER_COFDM_PANASONIC_ENV77H11D5,
+} dibusb_tuner_t;
+
+typedef enum {
+	DIBUSB_DIB3000MB = 0,
+	DIBUSB_DIB3000MC,
+	DIBUSB_MT352,
+	DTT200U_FE,
+} dibusb_demodulator_t;
+
+typedef enum {
+	DIBUSB_RC_NO = 0,
+	DIBUSB_RC_NEC_PROTOCOL,
+	DIBUSB_RC_HAUPPAUGE_PROTO,
+} dibusb_remote_t;
+
+struct dibusb_tuner {
+	dibusb_tuner_t id;
+
+	u8 pll_addr;       /* tuner i2c address */
+};
+extern struct dibusb_tuner dibusb_tuner[];
+
+#define DIBUSB_POSSIBLE_I2C_ADDR_NUM 4
+struct dibusb_demod {
+	dibusb_demodulator_t id;
+
+	int pid_filter_count;                       /* counter of the internal pid_filter */
+	u8 i2c_addrs[DIBUSB_POSSIBLE_I2C_ADDR_NUM]; /* list of possible i2c addresses of the demod */
+};
+
+#define DIBUSB_MAX_TUNER_NUM 2
+struct dibusb_device_class {
+	dibusb_class_t id;
+
+	const struct dibusb_usb_controller *usb_ctrl; /* usb controller */
+	const char *firmware;                         /* valid firmware filenames */
+
+	int pipe_cmd;                                 /* command pipe (read/write) */
+	int pipe_data;                                /* data pipe */
+
+	int urb_count;                                /* number of data URBs to be submitted */
+	int urb_buffer_size;                          /* the size of the buffer for each URB */
+
+	dibusb_remote_t remote_type;                  /* does this device have a ir-receiver */
+
+	struct dibusb_demod *demod;                   /* which demodulator is mount */
+	struct dibusb_tuner *tuner;                   /* which tuner can be found here */
+};
+
+#define DIBUSB_ID_MAX_NUM 15
+struct dibusb_usb_device {
+	const char *name;                                 /* real name of the box */
+	struct dibusb_device_class *dev_cl;               /* which dibusb_device_class is this device part of */
+
+	struct usb_device_id *cold_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at pre firmware state */
+	struct usb_device_id *warm_ids[DIBUSB_ID_MAX_NUM]; /* list of USB ids when this device is at post firmware state */
+};
+
+/* a PID for the pid_filter list, when in use */
+struct dibusb_pid
+{
+	int index;
+	u16 pid;
+	int active;
+};
+
+struct usb_dibusb {
+	/* usb */
+	struct usb_device * udev;
+
+	struct dibusb_usb_device * dibdev;
+
+#define DIBUSB_STATE_INIT       0x000
+#define DIBUSB_STATE_URB_LIST   0x001
+#define DIBUSB_STATE_URB_BUF    0x002
+#define DIBUSB_STATE_URB_INIT	0x004
+#define DIBUSB_STATE_DVB        0x008
+#define DIBUSB_STATE_I2C        0x010
+#define DIBUSB_STATE_REMOTE		0x020
+#define DIBUSB_STATE_URB_SUBMIT 0x040
+	int init_state;
+
+	int feedcount;
+	struct dib_fe_xfer_ops xfer_ops;
+
+	struct dibusb_tuner *tuner;
+
+	struct urb **urb_list;
+	u8 *buffer;
+	dma_addr_t dma_handle;
+
+	/* I2C */
+	struct i2c_adapter i2c_adap;
+
+	/* locking */
+	struct semaphore usb_sem;
+	struct semaphore i2c_sem;
+
+	/* dvb */
+	struct dvb_adapter *adapter;
+	struct dmxdev dmxdev;
+	struct dvb_demux demux;
+	struct dvb_net dvb_net;
+	struct dvb_frontend* fe;
+
+	int (*fe_sleep) (struct dvb_frontend *);
+	int (*fe_init) (struct dvb_frontend *);
+
+	/* remote control */
+	struct input_dev rc_input_dev;
+	struct work_struct rc_query_work;
+	int last_event;
+	int last_state; /* for Hauppauge RC protocol */
+	int repeat_key_count;
+	int rc_key_repeat_count; /* module parameter */
+
+	/* module parameters */
+	int pid_parse;
+	int rc_query_interval;
+};
+
+/* commonly used functions in the separated files */
+
+/* dvb-dibusb-firmware.c */
+int dibusb_loadfirmware(struct usb_device *udev, struct dibusb_usb_device *dibdev);
+
+/* dvb-dibusb-remote.c */
+int dibusb_remote_exit(struct usb_dibusb *dib);
+int dibusb_remote_init(struct usb_dibusb *dib);
+
+/* dvb-dibusb-fe-i2c.c */
+int dibusb_fe_init(struct usb_dibusb* dib);
+int dibusb_fe_exit(struct usb_dibusb *dib);
+int dibusb_i2c_init(struct usb_dibusb *dib);
+int dibusb_i2c_exit(struct usb_dibusb *dib);
+
+/* dvb-dibusb-dvb.c */
+void dibusb_urb_complete(struct urb *urb, struct pt_regs *ptregs);
+int dibusb_dvb_init(struct usb_dibusb *dib);
+int dibusb_dvb_exit(struct usb_dibusb *dib);
+
+/* dvb-dibusb-usb.c */
+int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf,
+	u16 rlen);
+int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len);
+
+int dibusb_hw_wakeup(struct dvb_frontend *);
+int dibusb_hw_sleep(struct dvb_frontend *);
+int dibusb_set_streaming_mode(struct usb_dibusb *,u8);
+int dibusb_streaming(struct usb_dibusb *,int);
+
+int dibusb_urb_init(struct usb_dibusb *);
+int dibusb_urb_exit(struct usb_dibusb *);
+
+/* dvb-fe-dtt200u.c */
+struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *,struct dib_fe_xfer_ops *);
+
+/* i2c and transfer stuff */
+#define DIBUSB_I2C_TIMEOUT				5000
+
+/*
+ * protocol of all dibusb related devices
+ */
+
+/*
+ * bulk msg to/from endpoint 0x01
+ *
+ * general structure:
+ * request_byte parameter_bytes
+ */
+
+#define DIBUSB_REQ_START_READ			0x00
+#define DIBUSB_REQ_START_DEMOD			0x01
+
+/*
+ * i2c read
+ * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word
+ * bulk read:  byte_buffer (length_word bytes)
+ */
+#define DIBUSB_REQ_I2C_READ			0x02
+
+/*
+ * i2c write
+ * bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes
+ */
+#define DIBUSB_REQ_I2C_WRITE			0x03
+
+/*
+ * polling the value of the remote control
+ * bulk write: 0x04
+ * bulk read:  byte_buffer (5 bytes)
+ *
+ * first byte of byte_buffer shows the status (0x00, 0x01, 0x02)
+ */
+#define DIBUSB_REQ_POLL_REMOTE			0x04
+
+#define DIBUSB_RC_NEC_EMPTY				0x00
+#define DIBUSB_RC_NEC_KEY_PRESSED		0x01
+#define DIBUSB_RC_NEC_KEY_REPEATED		0x02
+
+/* additional status values for Hauppauge Remote Control Protocol */
+#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED	0x01
+#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY	0x03
+
+/* streaming mode:
+ * bulk write: 0x05 mode_byte
+ *
+ * mode_byte is mostly 0x00
+ */
+#define DIBUSB_REQ_SET_STREAMING_MODE	0x05
+
+/* interrupt the internal read loop, when blocking */
+#define DIBUSB_REQ_INTR_READ			0x06
+
+/* io control
+ * 0x07 cmd_byte param_bytes
+ *
+ * param_bytes can be up to 32 bytes
+ *
+ * cmd_byte function    parameter name
+ * 0x00     power mode
+ *                      0x00      sleep
+ *                      0x01      wakeup
+ *
+ * 0x01     enable streaming
+ * 0x02     disable streaming
+ *
+ *
+ */
+#define DIBUSB_REQ_SET_IOCTL			0x07
+
+/* IOCTL commands */
+
+/* change the power mode in firmware */
+#define DIBUSB_IOCTL_CMD_POWER_MODE		0x00
+#define DIBUSB_IOCTL_POWER_SLEEP			0x00
+#define DIBUSB_IOCTL_POWER_WAKEUP			0x01
+
+/* modify streaming of the FX2 */
+#define DIBUSB_IOCTL_CMD_ENABLE_STREAM	0x01
+#define DIBUSB_IOCTL_CMD_DISABLE_STREAM	0x02
+
+#endif
diff --git a/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c b/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c
new file mode 100644
index 0000000..1872aa6
--- /dev/null
+++ b/drivers/media/dvb/dibusb/dvb-fe-dtt200u.c
@@ -0,0 +1,263 @@
+/*
+ * dvb-dtt200u-fe.c is a driver which implements the frontend-part of the
+ * Yakumo/Typhoon/Hama USB2.0 boxes. It is hard-wired to the dibusb-driver as
+ * it uses the usb-transfer functions directly (maybe creating a
+ * generic-dvb-usb-lib for all usb-drivers will be reduce some more code.)
+ *
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ *
+ * see dvb-dibusb-core.c for copyright details.
+ */
+
+/* guessed protocol description (reverse engineered):
+ * read
+ *  00 - USB type 0x02 for usb2.0, 0x01 for usb1.1
+ *  81 - <TS_LOCK> <current frequency divided by 250000>
+ *  82 - crash - do not touch
+ *  83 - crash - do not touch
+ *  84 - remote control
+ *  85 - crash - do not touch (OK, stop testing here)
+ *  88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal)
+ *  89 - noise-to-signal
+ *	8a - unkown 1 byte - signal_strength
+ *  8c - ber ???
+ *  8d - ber
+ *  8e - unc
+ *
+ * write
+ *  02 - bandwidth
+ *  03 - frequency (divided by 250000)
+ *  04 - pid table (index pid(7:0) pid(12:8))
+ *  05 - reset the pid table
+ *  08 - demod transfer enabled or not (FX2 transfer is enabled by default)
+ */
+
+#include "dvb-dibusb.h"
+#include "dvb_frontend.h"
+
+struct dtt200u_fe_state {
+	struct usb_dibusb *dib;
+
+	struct dvb_frontend_parameters fep;
+	struct dvb_frontend frontend;
+};
+
+#define moan(which,what) info("unexpected value in '%s' for cmd '%02x' - please report to linux-dvb@linuxtv.org",which,what)
+
+static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	u8 bw[1] = { 0x81 };
+	u8 br[3] = { 0 };
+//	u8 bdeb[5] = { 0 };
+
+	dibusb_readwrite_usb(state->dib,bw,1,br,3);
+	switch (br[0]) {
+		case 0x01:
+			*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+			break;
+		case 0x00:
+			*stat = 0;
+			break;
+		default:
+			moan("br[0]",0x81);
+			break;
+	}
+
+//	bw[0] = 0x88;
+//	dibusb_readwrite_usb(state->dib,bw,1,bdeb,5);
+
+//	deb_info("%02x: %02x %02x %02x %02x %02x\n",bw[0],bdeb[0],bdeb[1],bdeb[2],bdeb[3],bdeb[4]);
+
+	return 0;
+}
+static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	u8 bw[1] = { 0x8d };
+	*ber = 0;
+	dibusb_readwrite_usb(state->dib,bw,1,(u8*) ber, 3);
+	return 0;
+}
+
+static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	u8 bw[1] = { 0x8c };
+	*unc = 0;
+	dibusb_readwrite_usb(state->dib,bw,1,(u8*) unc, 3);
+	return 0;
+}
+
+static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	u8 bw[1] = { 0x8a };
+	u8 b;
+	dibusb_readwrite_usb(state->dib,bw,1,&b, 1);
+	*strength = (b << 8) | b;
+	return 0;
+}
+
+static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	u8 bw[1] = { 0x89 };
+	u8 br[1] = { 0 };
+	dibusb_readwrite_usb(state->dib,bw,1,br,1);
+	*snr = ((0xff - br[0]) << 8) | (0xff - br[0]);
+	return 0;
+}
+
+static int dtt200u_fe_init(struct dvb_frontend* fe)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	u8 b[] = { 0x01 };
+	return dibusb_write_usb(state->dib,b,1);
+}
+
+static int dtt200u_fe_sleep(struct dvb_frontend* fe)
+{
+	return dtt200u_fe_init(fe);
+}
+
+static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+	tune->min_delay_ms = 1500;
+	tune->step_size = 166667;
+	tune->max_drift = 166667 * 2;
+	return 0;
+}
+
+static int dtt200u_fe_set_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	u16 freq = fep->frequency / 250000;
+	u8 bw,bwbuf[2] = { 0x03, 0 }, freqbuf[3] = { 0x02, 0, 0 };
+
+	switch (fep->u.ofdm.bandwidth) {
+		case BANDWIDTH_8_MHZ: bw = 8; break;
+		case BANDWIDTH_7_MHZ: bw = 7; break;
+		case BANDWIDTH_6_MHZ: bw = 6; break;
+		case BANDWIDTH_AUTO: return -EOPNOTSUPP;
+		default:
+			return -EINVAL;
+	}
+	deb_info("set_frontend\n");
+
+	bwbuf[1] = bw;
+	dibusb_write_usb(state->dib,bwbuf,2);
+
+	freqbuf[1] = freq & 0xff;
+	freqbuf[2] = (freq >> 8) & 0xff;
+	dibusb_write_usb(state->dib,freqbuf,3);
+
+	memcpy(&state->fep,fep,sizeof(struct dvb_frontend_parameters));
+
+	return 0;
+}
+
+static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep)
+{
+	struct dtt200u_fe_state *state = fe->demodulator_priv;
+	memcpy(fep,&state->fep,sizeof(struct dvb_frontend_parameters));
+	return 0;
+}
+
+static void dtt200u_fe_release(struct dvb_frontend* fe)
+{
+	struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static int dtt200u_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+{
+	struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+	u8 b_pid[4];
+	pid = onoff ? pid : 0;
+
+	b_pid[0] = 0x04;
+	b_pid[1] = index;
+	b_pid[2] = pid & 0xff;
+	b_pid[3] = (pid >> 8) & 0xff;
+
+	dibusb_write_usb(state->dib,b_pid,4);
+	return 0;
+}
+
+static int dtt200u_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+	struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv;
+	u8 b_streaming[2] = { 0x08, onoff };
+	u8 b_rst_pid[1] = { 0x05 };
+
+	dibusb_write_usb(state->dib,b_streaming,2);
+
+	if (!onoff)
+		dibusb_write_usb(state->dib,b_rst_pid,1);
+	return 0;
+}
+
+static struct dvb_frontend_ops dtt200u_fe_ops;
+
+struct dvb_frontend* dtt200u_fe_attach(struct usb_dibusb *dib, struct dib_fe_xfer_ops *xfer_ops)
+{
+	struct dtt200u_fe_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct dtt200u_fe_state*) kmalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+	memset(state,0,sizeof(struct dtt200u_fe_state));
+
+	deb_info("attaching frontend dtt200u\n");
+
+	state->dib = dib;
+
+	state->frontend.ops = &dtt200u_fe_ops;
+	state->frontend.demodulator_priv = state;
+
+	xfer_ops->fifo_ctrl = dtt200u_fifo_control;
+	xfer_ops->pid_ctrl = dtt200u_pid_control;
+
+	goto success;
+error:
+	return NULL;
+success:
+	return &state->frontend;
+}
+
+static struct dvb_frontend_ops dtt200u_fe_ops = {
+	.info = {
+		.name			= "DTT200U (Yakumo/Typhoon/Hama) DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 44250000,
+		.frequency_max		= 867250000,
+		.frequency_stepsize	= 250000,
+		.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 = dtt200u_fe_release,
+
+	.init = dtt200u_fe_init,
+	.sleep = dtt200u_fe_sleep,
+
+	.set_frontend = dtt200u_fe_set_frontend,
+	.get_frontend = dtt200u_fe_get_frontend,
+	.get_tune_settings = dtt200u_fe_get_tune_settings,
+
+	.read_status = dtt200u_fe_read_status,
+	.read_ber = dtt200u_fe_read_ber,
+	.read_signal_strength = dtt200u_fe_read_signal_strength,
+	.read_snr = dtt200u_fe_read_snr,
+	.read_ucblocks = dtt200u_fe_read_unc_blocks,
+};
diff --git a/drivers/media/dvb/dvb-core/Kconfig b/drivers/media/dvb/dvb-core/Kconfig
new file mode 100644
index 0000000..a9a7b34
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/Kconfig
@@ -0,0 +1,11 @@
+config DVB_CORE
+	tristate "DVB Core Support"
+	depends on DVB
+	select CRC32
+	help
+	  DVB core utility functions for device handling, software fallbacks etc.
+	  Say Y when you have a DVB card and want to use it. Say Y if your want
+	  to build your drivers outside the kernel, but need the DVB core. All 
+	  in-kernel drivers will select this automatically if needed.
+	  If unsure say N.
+
diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile
new file mode 100644
index 0000000..c6baac2
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the kernel DVB device drivers.
+#
+
+dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
+	        dvb_ca_en50221.o dvb_frontend.o \
+		dvb_net.o dvb_ringbuffer.o
+
+obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
new file mode 100644
index 0000000..fb55eaa
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -0,0 +1,301 @@
+/*
+ * demux.h
+ *
+ * Copyright (c) 2002 Convergence GmbH
+ *
+ * based on code:
+ * Copyright (c) 2000 Nokia Research Center
+ *                    Tampere, FINLAND
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef __DEMUX_H
+#define __DEMUX_H
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/time.h>
+
+/*--------------------------------------------------------------------------*/
+/* Common definitions */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter.
+ */
+
+#ifndef DMX_MAX_FILTER_SIZE
+#define DMX_MAX_FILTER_SIZE 18
+#endif
+
+/*
+ * DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed filter.
+ */
+
+#ifndef DMX_MAX_SECFEED_SIZE
+#define DMX_MAX_SECFEED_SIZE 4096
+#endif
+
+
+/*
+ * enum dmx_success: Success codes for the Demux Callback API.
+ */
+
+enum dmx_success {
+  DMX_OK = 0, /* Received Ok */
+  DMX_LENGTH_ERROR, /* Incorrect length */
+  DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
+  DMX_CRC_ERROR, /* Incorrect CRC */
+  DMX_FRAME_ERROR, /* Frame alignment error */
+  DMX_FIFO_ERROR, /* Receiver FIFO overrun */
+  DMX_MISSED_ERROR /* Receiver missed packet */
+} ;
+
+/*--------------------------------------------------------------------------*/
+/* TS packet reception */
+/*--------------------------------------------------------------------------*/
+
+/* TS filter type for set() */
+
+#define TS_PACKET       1   /* send TS packets (188 bytes) to callback (default) */
+#define	TS_PAYLOAD_ONLY 2   /* in case TS_PACKET is set, only send the TS
+			       payload (<=184 bytes per packet) to callback */
+#define TS_DECODER      4   /* send stream to built-in decoder (if present) */
+
+/* PES type for filters which write to built-in decoder */
+/* these should be kept identical to the types in dmx.h */
+
+enum dmx_ts_pes
+{  /* also send packets to decoder (if it exists) */
+        DMX_TS_PES_AUDIO0,
+	DMX_TS_PES_VIDEO0,
+	DMX_TS_PES_TELETEXT0,
+	DMX_TS_PES_SUBTITLE0,
+	DMX_TS_PES_PCR0,
+
+        DMX_TS_PES_AUDIO1,
+	DMX_TS_PES_VIDEO1,
+	DMX_TS_PES_TELETEXT1,
+	DMX_TS_PES_SUBTITLE1,
+	DMX_TS_PES_PCR1,
+
+        DMX_TS_PES_AUDIO2,
+	DMX_TS_PES_VIDEO2,
+	DMX_TS_PES_TELETEXT2,
+	DMX_TS_PES_SUBTITLE2,
+	DMX_TS_PES_PCR2,
+
+        DMX_TS_PES_AUDIO3,
+	DMX_TS_PES_VIDEO3,
+	DMX_TS_PES_TELETEXT3,
+	DMX_TS_PES_SUBTITLE3,
+	DMX_TS_PES_PCR3,
+
+	DMX_TS_PES_OTHER
+};
+
+#define DMX_TS_PES_AUDIO    DMX_TS_PES_AUDIO0
+#define DMX_TS_PES_VIDEO    DMX_TS_PES_VIDEO0
+#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0
+#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0
+#define DMX_TS_PES_PCR      DMX_TS_PES_PCR0
+
+
+struct dmx_ts_feed {
+        int is_filtering; /* Set to non-zero when filtering in progress */
+        struct dmx_demux *parent; /* Back-pointer */
+        void *priv; /* Pointer to private data of the API client */
+        int (*set) (struct dmx_ts_feed *feed,
+		    u16 pid,
+		    int type,
+		    enum dmx_ts_pes pes_type,
+		    size_t callback_length,
+		    size_t circular_buffer_size,
+		    int descramble,
+		    struct timespec timeout);
+        int (*start_filtering) (struct dmx_ts_feed* feed);
+        int (*stop_filtering) (struct dmx_ts_feed* feed);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Section reception */
+/*--------------------------------------------------------------------------*/
+
+struct dmx_section_filter {
+        u8 filter_value [DMX_MAX_FILTER_SIZE];
+        u8 filter_mask [DMX_MAX_FILTER_SIZE];
+        u8 filter_mode [DMX_MAX_FILTER_SIZE];
+        struct dmx_section_feed* parent; /* Back-pointer */
+        void* priv; /* Pointer to private data of the API client */
+};
+
+struct dmx_section_feed {
+        int is_filtering; /* Set to non-zero when filtering in progress */
+        struct dmx_demux* parent; /* Back-pointer */
+        void* priv; /* Pointer to private data of the API client */
+
+        int check_crc;
+	u32 crc_val;
+
+        u8 *secbuf;
+        u8 secbuf_base[DMX_MAX_SECFEED_SIZE];
+        u16 secbufp, seclen, tsfeedp;
+
+        int (*set) (struct dmx_section_feed* feed,
+		    u16 pid,
+		    size_t circular_buffer_size,
+		    int descramble,
+		    int check_crc);
+        int (*allocate_filter) (struct dmx_section_feed* feed,
+				struct dmx_section_filter** filter);
+        int (*release_filter) (struct dmx_section_feed* feed,
+			       struct dmx_section_filter* filter);
+        int (*start_filtering) (struct dmx_section_feed* feed);
+        int (*stop_filtering) (struct dmx_section_feed* feed);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Callback functions */
+/*--------------------------------------------------------------------------*/
+
+typedef int (*dmx_ts_cb) ( const u8 * buffer1,
+			   size_t buffer1_length,
+			   const u8 * buffer2,
+			   size_t buffer2_length,
+			   struct dmx_ts_feed* source,
+			   enum dmx_success success);
+
+typedef int (*dmx_section_cb) (	const u8 * buffer1,
+				size_t buffer1_len,
+				const u8 * buffer2,
+				size_t buffer2_len,
+				struct dmx_section_filter * source,
+				enum dmx_success success);
+
+/*--------------------------------------------------------------------------*/
+/* DVB Front-End */
+/*--------------------------------------------------------------------------*/
+
+enum dmx_frontend_source {
+	DMX_MEMORY_FE,
+	DMX_FRONTEND_0,
+	DMX_FRONTEND_1,
+	DMX_FRONTEND_2,
+	DMX_FRONTEND_3,
+	DMX_STREAM_0,    /* external stream input, e.g. LVDS */
+	DMX_STREAM_1,
+	DMX_STREAM_2,
+	DMX_STREAM_3
+};
+
+struct dmx_frontend {
+        struct list_head connectivity_list; /* List of front-ends that can
+					       be connected to a particular
+					       demux */
+        void* priv;     /* Pointer to private data of the API client */
+        enum dmx_frontend_source source;
+};
+
+/*--------------------------------------------------------------------------*/
+/* MPEG-2 TS Demux */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * Flags OR'ed in the capabilites field of struct dmx_demux.
+ */
+
+#define DMX_TS_FILTERING                        1
+#define DMX_PES_FILTERING                       2
+#define DMX_SECTION_FILTERING                   4
+#define DMX_MEMORY_BASED_FILTERING              8    /* write() available */
+#define DMX_CRC_CHECKING                        16
+#define DMX_TS_DESCRAMBLING                     32
+#define DMX_SECTION_PAYLOAD_DESCRAMBLING        64
+#define DMX_MAC_ADDRESS_DESCRAMBLING            128
+
+/*
+ * Demux resource type identifier.
+*/
+
+/*
+ * DMX_FE_ENTRY(): Casts elements in the list of registered
+ * front-ends from the generic type struct list_head
+ * to the type * struct dmx_frontend
+ *.
+*/
+
+#define DMX_FE_ENTRY(list) list_entry(list, struct dmx_frontend, connectivity_list)
+
+struct dmx_demux {
+        u32 capabilities;            /* Bitfield of capability flags */
+        struct dmx_frontend* frontend;    /* Front-end connected to the demux */
+        struct list_head reg_list;   /* List of registered demuxes */
+        void* priv;                  /* Pointer to private data of the API client */
+        int users;                   /* Number of users */
+        int (*open) (struct dmx_demux* demux);
+        int (*close) (struct dmx_demux* demux);
+        int (*write) (struct dmx_demux* demux, const char* buf, size_t count);
+        int (*allocate_ts_feed) (struct dmx_demux* demux,
+				 struct dmx_ts_feed** feed,
+				 dmx_ts_cb callback);
+        int (*release_ts_feed) (struct dmx_demux* demux,
+				struct dmx_ts_feed* feed);
+        int (*allocate_section_feed) (struct dmx_demux* demux,
+				      struct dmx_section_feed** feed,
+				      dmx_section_cb callback);
+        int (*release_section_feed) (struct dmx_demux* demux,
+				     struct dmx_section_feed* feed);
+        int (*descramble_mac_address) (struct dmx_demux* demux,
+				       u8* buffer1,
+				       size_t buffer1_length,
+				       u8* buffer2,
+				       size_t buffer2_length,
+				       u16 pid);
+        int (*descramble_section_payload) (struct dmx_demux* demux,
+					   u8* buffer1,
+					   size_t buffer1_length,
+					   u8* buffer2, size_t buffer2_length,
+					   u16 pid);
+        int (*add_frontend) (struct dmx_demux* demux,
+			     struct dmx_frontend* frontend);
+        int (*remove_frontend) (struct dmx_demux* demux,
+				struct dmx_frontend* frontend);
+        struct list_head* (*get_frontends) (struct dmx_demux* demux);
+        int (*connect_frontend) (struct dmx_demux* demux,
+				 struct dmx_frontend* frontend);
+        int (*disconnect_frontend) (struct dmx_demux* demux);
+
+        int (*get_pes_pids) (struct dmx_demux* demux, u16 *pids);
+
+        int (*get_stc) (struct dmx_demux* demux, unsigned int num,
+			u64 *stc, unsigned int *base);
+};
+
+/*--------------------------------------------------------------------------*/
+/* Demux directory */
+/*--------------------------------------------------------------------------*/
+
+/*
+ * DMX_DIR_ENTRY(): Casts elements in the list of registered
+ * demuxes from the generic type struct list_head* to the type struct dmx_demux
+ *.
+ */
+
+#define DMX_DIR_ENTRY(list) list_entry(list, struct dmx_demux, reg_list)
+
+#endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
new file mode 100644
index 0000000..1863f1d
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -0,0 +1,1137 @@
+/*
+ * dmxdev.c - DVB demultiplexer device
+ *
+ * Copyright (C) 2000 Ralph  Metzler <ralph@convergence.de>
+ *		  & Marcus Metzler <marcus@convergence.de>
+		      for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include "dmxdev.h"
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk	if (debug) printk
+
+static inline struct dmxdev_filter *
+dvb_dmxdev_file_to_filter(struct file *file)
+{
+	return (struct dmxdev_filter *) file->private_data;
+}
+
+static inline void dvb_dmxdev_buffer_init(struct dmxdev_buffer *buffer)
+{
+	buffer->data=NULL;
+	buffer->size=8192;
+	buffer->pread=0;
+	buffer->pwrite=0;
+	buffer->error=0;
+	init_waitqueue_head(&buffer->queue);
+}
+
+static inline int dvb_dmxdev_buffer_write(struct dmxdev_buffer *buf, const u8 *src, int len)
+{
+	int split;
+	int free;
+	int todo;
+
+	if (!len)
+		return 0;
+	if (!buf->data)
+		return 0;
+
+	free=buf->pread-buf->pwrite;
+	split=0;
+	if (free<=0) {
+		free+=buf->size;
+		split=buf->size-buf->pwrite;
+	}
+	if (len>=free) {
+		dprintk("dmxdev: buffer overflow\n");
+		return -1;
+	}
+	if (split>=len)
+		split=0;
+	todo=len;
+	if (split) {
+		memcpy(buf->data + buf->pwrite, src, split);
+		todo-=split;
+		buf->pwrite=0;
+	}
+	memcpy(buf->data + buf->pwrite, src+split, todo);
+	buf->pwrite=(buf->pwrite+todo)%buf->size;
+	return len;
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dmxdev_buffer *src,
+		int non_blocking, char __user *buf, size_t count, loff_t *ppos)
+{
+	unsigned long todo=count;
+	int split, avail, error;
+
+	if (!src->data)
+		return 0;
+
+	if ((error=src->error)) {
+		src->pwrite=src->pread;
+		src->error=0;
+		return error;
+	}
+
+	if (non_blocking && (src->pwrite==src->pread))
+		return -EWOULDBLOCK;
+
+	while (todo>0) {
+		if (non_blocking && (src->pwrite==src->pread))
+			return (count-todo) ? (count-todo) : -EWOULDBLOCK;
+
+		if (wait_event_interruptible(src->queue,
+					     (src->pread!=src->pwrite) ||
+					     (src->error))<0)
+			return count-todo;
+
+		if ((error=src->error)) {
+			src->pwrite=src->pread;
+			src->error=0;
+			return error;
+		}
+
+		split=src->size;
+		avail=src->pwrite - src->pread;
+		if (avail<0) {
+			avail+=src->size;
+			split=src->size - src->pread;
+		}
+		if (avail>todo)
+			avail=todo;
+		if (split<avail) {
+			if (copy_to_user(buf, src->data+src->pread, split))
+				  return -EFAULT;
+			buf+=split;
+			src->pread=0;
+			todo-=split;
+			avail-=split;
+		}
+		if (avail) {
+			if (copy_to_user(buf, src->data+src->pread, avail))
+				return -EFAULT;
+			src->pread = (src->pread + avail) % src->size;
+			todo-=avail;
+			buf+=avail;
+		}
+	}
+	return count;
+}
+
+static struct dmx_frontend * get_fe(struct dmx_demux *demux, int type)
+{
+	struct list_head *head, *pos;
+
+	head=demux->get_frontends(demux);
+	if (!head)
+		return NULL;
+	list_for_each(pos, head)
+		if (DMX_FE_ENTRY(pos)->source==type)
+			return DMX_FE_ENTRY(pos);
+
+	return NULL;
+}
+
+static inline void dvb_dmxdev_dvr_state_set(struct dmxdev_dvr *dmxdevdvr, int state)
+{
+	spin_lock_irq(&dmxdevdvr->dev->lock);
+	dmxdevdvr->state=state;
+	spin_unlock_irq(&dmxdevdvr->dev->lock);
+}
+
+static int dvb_dvr_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	struct dmx_frontend *front;
+
+	dprintk ("function : %s\n", __FUNCTION__);
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if ((file->f_flags&O_ACCMODE)==O_RDWR) {
+		if (!(dmxdev->capabilities&DMXDEV_CAP_DUPLEX)) {
+			up(&dmxdev->mutex);
+			return -EOPNOTSUPP;
+		}
+	}
+
+	if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
+	      dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer);
+	      dmxdev->dvr_buffer.size=DVR_BUFFER_SIZE;
+	      dmxdev->dvr_buffer.data=vmalloc(DVR_BUFFER_SIZE);
+	      if (!dmxdev->dvr_buffer.data) {
+		      up(&dmxdev->mutex);
+		      return -ENOMEM;
+	      }
+	}
+
+	if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
+		dmxdev->dvr_orig_fe=dmxdev->demux->frontend;
+
+		if (!dmxdev->demux->write) {
+			up(&dmxdev->mutex);
+			return -EOPNOTSUPP;
+		}
+
+		front=get_fe(dmxdev->demux, DMX_MEMORY_FE);
+
+		if (!front) {
+			up(&dmxdev->mutex);
+			return -EINVAL;
+		}
+		dmxdev->demux->disconnect_frontend(dmxdev->demux);
+		dmxdev->demux->connect_frontend(dmxdev->demux, front);
+	}
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+static int dvb_dvr_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if ((file->f_flags&O_ACCMODE)==O_WRONLY) {
+		dmxdev->demux->disconnect_frontend(dmxdev->demux);
+		dmxdev->demux->connect_frontend(dmxdev->demux,
+						dmxdev->dvr_orig_fe);
+	}
+	if ((file->f_flags&O_ACCMODE)==O_RDONLY) {
+		if (dmxdev->dvr_buffer.data) {
+			void *mem=dmxdev->dvr_buffer.data;
+			mb();
+			spin_lock_irq(&dmxdev->lock);
+			dmxdev->dvr_buffer.data=NULL;
+			spin_unlock_irq(&dmxdev->lock);
+			vfree(mem);
+		}
+	}
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	int ret;
+
+	if (!dmxdev->demux->write)
+		return -EOPNOTSUPP;
+	if ((file->f_flags&O_ACCMODE)!=O_WRONLY)
+		return -EINVAL;
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+	ret=dmxdev->demux->write(dmxdev->demux, buf, count);
+	up(&dmxdev->mutex);
+	return ret;
+}
+
+static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
+		loff_t *ppos)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	int ret;
+
+	//down(&dmxdev->mutex);
+	ret= dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+			      file->f_flags&O_NONBLOCK,
+			      buf, count, ppos);
+	//up(&dmxdev->mutex);
+	return ret;
+}
+
+static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter *dmxdevfilter, int state)
+{
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	dmxdevfilter->state=state;
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+}
+
+static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter, unsigned long size)
+{
+	struct dmxdev_buffer *buf=&dmxdevfilter->buffer;
+	void *mem;
+
+	if (buf->size==size)
+		return 0;
+	if (dmxdevfilter->state>=DMXDEV_STATE_GO)
+		return -EBUSY;
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	mem=buf->data;
+	buf->data=NULL;
+	buf->size=size;
+	buf->pwrite=buf->pread=0;
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+	vfree(mem);
+
+	if (buf->size) {
+		mem=vmalloc(dmxdevfilter->buffer.size);
+		if (!mem)
+			return -ENOMEM;
+		spin_lock_irq(&dmxdevfilter->dev->lock);
+		buf->data=mem;
+		spin_unlock_irq(&dmxdevfilter->dev->lock);
+	}
+	return 0;
+}
+
+static void dvb_dmxdev_filter_timeout(unsigned long data)
+{
+	struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *)data;
+
+	dmxdevfilter->buffer.error=-ETIMEDOUT;
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	dmxdevfilter->state=DMXDEV_STATE_TIMEDOUT;
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+	wake_up(&dmxdevfilter->buffer.queue);
+}
+
+static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
+{
+	struct dmx_sct_filter_params *para=&dmxdevfilter->params.sec;
+
+	del_timer(&dmxdevfilter->timer);
+	if (para->timeout) {
+		dmxdevfilter->timer.function=dvb_dmxdev_filter_timeout;
+		dmxdevfilter->timer.data=(unsigned long) dmxdevfilter;
+		dmxdevfilter->timer.expires=jiffies+1+(HZ/2+HZ*para->timeout)/1000;
+		add_timer(&dmxdevfilter->timer);
+	}
+}
+
+static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
+			    const u8 *buffer2, size_t buffer2_len,
+			    struct dmx_section_filter *filter, enum dmx_success success)
+{
+	struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) filter->priv;
+	int ret;
+
+	if (dmxdevfilter->buffer.error) {
+		wake_up(&dmxdevfilter->buffer.queue);
+		return 0;
+	}
+	spin_lock(&dmxdevfilter->dev->lock);
+	if (dmxdevfilter->state!=DMXDEV_STATE_GO) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+	del_timer(&dmxdevfilter->timer);
+	dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",
+		buffer1[0], buffer1[1],
+		buffer1[2], buffer1[3],
+		buffer1[4], buffer1[5]);
+	ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, buffer1_len);
+	if (ret==buffer1_len) {
+		ret=dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2, buffer2_len);
+	}
+	if (ret<0) {
+		dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread;
+		dmxdevfilter->buffer.error=-EOVERFLOW;
+	}
+	if (dmxdevfilter->params.sec.flags&DMX_ONESHOT)
+		dmxdevfilter->state=DMXDEV_STATE_DONE;
+	spin_unlock(&dmxdevfilter->dev->lock);
+	wake_up(&dmxdevfilter->buffer.queue);
+	return 0;
+}
+
+static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
+		       const u8 *buffer2, size_t buffer2_len,
+		       struct dmx_ts_feed *feed, enum dmx_success success)
+{
+	struct dmxdev_filter *dmxdevfilter=(struct dmxdev_filter *) feed->priv;
+	struct dmxdev_buffer *buffer;
+	int ret;
+
+	spin_lock(&dmxdevfilter->dev->lock);
+	if (dmxdevfilter->params.pes.output==DMX_OUT_DECODER) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+
+	if (dmxdevfilter->params.pes.output==DMX_OUT_TAP)
+		buffer=&dmxdevfilter->buffer;
+	else
+		buffer=&dmxdevfilter->dev->dvr_buffer;
+	if (buffer->error) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		wake_up(&buffer->queue);
+		return 0;
+	}
+	ret=dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+	if (ret==buffer1_len)
+		ret=dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
+	if (ret<0) {
+		buffer->pwrite=buffer->pread;
+		buffer->error=-EOVERFLOW;
+	}
+	spin_unlock(&dmxdevfilter->dev->lock);
+	wake_up(&buffer->queue);
+	return 0;
+}
+
+
+/* stop feed but only mark the specified filter as stopped (state set) */
+
+static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
+{
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+	switch (dmxdevfilter->type) {
+	case DMXDEV_TYPE_SEC:
+		del_timer(&dmxdevfilter->timer);
+		dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
+		break;
+	case DMXDEV_TYPE_PES:
+		dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+/* start feed associated with the specified filter */
+
+static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
+{
+	dvb_dmxdev_filter_state_set (filter, DMXDEV_STATE_GO);
+
+	switch (filter->type) {
+	case DMXDEV_TYPE_SEC:
+		return filter->feed.sec->start_filtering(filter->feed.sec);
+		break;
+	case DMXDEV_TYPE_PES:
+		return filter->feed.ts->start_filtering(filter->feed.ts);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+/* restart section feed if it has filters left associated with it,
+   otherwise release the feed */
+
+static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
+{
+	int i;
+	struct dmxdev *dmxdev = filter->dev;
+	u16 pid = filter->params.sec.pid;
+
+	for (i=0; i<dmxdev->filternum; i++)
+		if (dmxdev->filter[i].state>=DMXDEV_STATE_GO &&
+		    dmxdev->filter[i].type==DMXDEV_TYPE_SEC &&
+		    dmxdev->filter[i].pid==pid) {
+			dvb_dmxdev_feed_start(&dmxdev->filter[i]);
+			return 0;
+		}
+
+	filter->dev->demux->release_section_feed(dmxdev->demux, filter->feed.sec);
+
+	return 0;
+}
+
+static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
+{
+	if (dmxdevfilter->state<DMXDEV_STATE_GO)
+		return 0;
+
+	switch (dmxdevfilter->type) {
+	case DMXDEV_TYPE_SEC:
+		if (!dmxdevfilter->feed.sec)
+			break;
+		dvb_dmxdev_feed_stop(dmxdevfilter);
+		if (dmxdevfilter->filter.sec)
+			dmxdevfilter->feed.sec->
+				release_filter(dmxdevfilter->feed.sec,
+					       dmxdevfilter->filter.sec);
+		dvb_dmxdev_feed_restart(dmxdevfilter);
+		dmxdevfilter->feed.sec=NULL;
+		break;
+	case DMXDEV_TYPE_PES:
+		if (!dmxdevfilter->feed.ts)
+			break;
+		dvb_dmxdev_feed_stop(dmxdevfilter);
+		dmxdevfilter->dev->demux->
+			release_ts_feed(dmxdevfilter->dev->demux,
+					dmxdevfilter->feed.ts);
+		dmxdevfilter->feed.ts=NULL;
+		break;
+	default:
+		if (dmxdevfilter->state==DMXDEV_STATE_ALLOCATED)
+			return 0;
+		return -EINVAL;
+	}
+	dmxdevfilter->buffer.pwrite=dmxdevfilter->buffer.pread=0;
+	return 0;
+}
+
+static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
+{
+	if (dmxdevfilter->state<DMXDEV_STATE_SET)
+		return 0;
+
+	dmxdevfilter->type=DMXDEV_TYPE_NONE;
+	dmxdevfilter->pid=0xffff;
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+	return 0;
+}
+
+static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
+{
+	struct dmxdev *dmxdev = filter->dev;
+	void *mem;
+	int ret, i;
+
+	if (filter->state < DMXDEV_STATE_SET)
+		return -EINVAL;
+
+	if (filter->state >= DMXDEV_STATE_GO)
+		dvb_dmxdev_filter_stop(filter);
+
+	if (!(mem = filter->buffer.data)) {
+		mem = vmalloc(filter->buffer.size);
+		spin_lock_irq(&filter->dev->lock);
+		filter->buffer.data=mem;
+		spin_unlock_irq(&filter->dev->lock);
+		if (!filter->buffer.data)
+			return -ENOMEM;
+	}
+
+	filter->buffer.pwrite = filter->buffer.pread = 0;
+
+	switch (filter->type) {
+	case DMXDEV_TYPE_SEC:
+	{
+		struct dmx_sct_filter_params *para=&filter->params.sec;
+		struct dmx_section_filter **secfilter=&filter->filter.sec;
+		struct dmx_section_feed **secfeed=&filter->feed.sec;
+
+		*secfilter=NULL;
+		*secfeed=NULL;
+
+		/* find active filter/feed with same PID */
+		for (i=0; i<dmxdev->filternum; i++) {
+			if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+			    dmxdev->filter[i].pid == para->pid &&
+			    dmxdev->filter[i].type == DMXDEV_TYPE_SEC) {
+				*secfeed = dmxdev->filter[i].feed.sec;
+				break;
+			}
+		}
+
+		/* if no feed found, try to allocate new one */
+		if (!*secfeed) {
+			ret=dmxdev->demux->allocate_section_feed(dmxdev->demux,
+								 secfeed,
+						   dvb_dmxdev_section_callback);
+			if (ret<0) {
+				printk ("DVB (%s): could not alloc feed\n",
+					__FUNCTION__);
+				return ret;
+			}
+
+			ret=(*secfeed)->set(*secfeed, para->pid, 32768, 0,
+					    (para->flags & DMX_CHECK_CRC) ? 1 : 0);
+
+			if (ret<0) {
+				printk ("DVB (%s): could not set feed\n",
+					__FUNCTION__);
+				dvb_dmxdev_feed_restart(filter);
+				return ret;
+			}
+		} else {
+			dvb_dmxdev_feed_stop(filter);
+		}
+
+		ret=(*secfeed)->allocate_filter(*secfeed, secfilter);
+
+		if (ret < 0) {
+			dvb_dmxdev_feed_restart(filter);
+			filter->feed.sec->start_filtering(*secfeed);
+			dprintk ("could not get filter\n");
+			return ret;
+		}
+
+		(*secfilter)->priv = filter;
+
+		memcpy(&((*secfilter)->filter_value[3]),
+		       &(para->filter.filter[1]), DMX_FILTER_SIZE-1);
+		memcpy(&(*secfilter)->filter_mask[3],
+		       &para->filter.mask[1], DMX_FILTER_SIZE-1);
+		memcpy(&(*secfilter)->filter_mode[3],
+		       &para->filter.mode[1], DMX_FILTER_SIZE-1);
+
+		(*secfilter)->filter_value[0]=para->filter.filter[0];
+		(*secfilter)->filter_mask[0]=para->filter.mask[0];
+		(*secfilter)->filter_mode[0]=para->filter.mode[0];
+		(*secfilter)->filter_mask[1]=0;
+		(*secfilter)->filter_mask[2]=0;
+
+		filter->todo = 0;
+
+		ret = filter->feed.sec->start_filtering (filter->feed.sec);
+
+		if (ret < 0)
+			return ret;
+
+		dvb_dmxdev_filter_timer(filter);
+		break;
+	}
+
+	case DMXDEV_TYPE_PES:
+	{
+		struct timespec timeout = { 0 };
+		struct dmx_pes_filter_params *para = &filter->params.pes;
+		dmx_output_t otype;
+		int ret;
+		int ts_type;
+		enum dmx_ts_pes ts_pes;
+		struct dmx_ts_feed **tsfeed = &filter->feed.ts;
+
+		filter->feed.ts = NULL;
+		otype=para->output;
+
+		ts_pes=(enum dmx_ts_pes) para->pes_type;
+
+		if (ts_pes<DMX_PES_OTHER)
+			ts_type=TS_DECODER;
+		else
+			ts_type=0;
+
+		if (otype == DMX_OUT_TS_TAP)
+			ts_type |= TS_PACKET;
+
+		if (otype == DMX_OUT_TAP)
+			ts_type |= TS_PAYLOAD_ONLY|TS_PACKET;
+
+		ret=dmxdev->demux->allocate_ts_feed(dmxdev->demux,
+						    tsfeed,
+						    dvb_dmxdev_ts_callback);
+		if (ret<0)
+			return ret;
+
+		(*tsfeed)->priv = (void *) filter;
+
+		ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes,
+				     188, 32768, 0, timeout);
+
+		if (ret < 0) {
+			dmxdev->demux->release_ts_feed(dmxdev->demux, *tsfeed);
+			return ret;
+		}
+
+		ret = filter->feed.ts->start_filtering(filter->feed.ts);
+
+		if (ret < 0)
+			return ret;
+
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+	return 0;
+}
+
+static int dvb_demux_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+	int i;
+	struct dmxdev_filter *dmxdevfilter;
+
+	if (!dmxdev->filter)
+		return -EINVAL;
+
+	if (down_interruptible(&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	for (i=0; i<dmxdev->filternum; i++)
+		if (dmxdev->filter[i].state==DMXDEV_STATE_FREE)
+			break;
+
+	if (i==dmxdev->filternum) {
+		up(&dmxdev->mutex);
+		return -EMFILE;
+	}
+
+	dmxdevfilter=&dmxdev->filter[i];
+	sema_init(&dmxdevfilter->mutex, 1);
+	dmxdevfilter->dvbdev=dmxdev->dvbdev;
+	file->private_data=dmxdevfilter;
+
+	dvb_dmxdev_buffer_init(&dmxdevfilter->buffer);
+	dmxdevfilter->type=DMXDEV_TYPE_NONE;
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+	dmxdevfilter->feed.ts=NULL;
+	init_timer(&dmxdevfilter->timer);
+
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+
+static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter)
+{
+	if (down_interruptible(&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	if (down_interruptible(&dmxdevfilter->mutex)) {
+		up(&dmxdev->mutex);
+		return -ERESTARTSYS;
+	}
+
+	dvb_dmxdev_filter_stop(dmxdevfilter);
+	dvb_dmxdev_filter_reset(dmxdevfilter);
+
+	if (dmxdevfilter->buffer.data) {
+		void *mem=dmxdevfilter->buffer.data;
+
+		spin_lock_irq(&dmxdev->lock);
+		dmxdevfilter->buffer.data=NULL;
+		spin_unlock_irq(&dmxdev->lock);
+		vfree(mem);
+	}
+
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
+	wake_up(&dmxdevfilter->buffer.queue);
+	up(&dmxdevfilter->mutex);
+	up(&dmxdev->mutex);
+	return 0;
+}
+
+static inline void invert_mode(dmx_filter_t *filter)
+{
+	int i;
+
+	for (i=0; i<DMX_FILTER_SIZE; i++)
+		filter->mode[i]^=0xff;
+}
+
+
+static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
+		struct dmxdev_filter *dmxdevfilter,
+		struct dmx_sct_filter_params *params)
+{
+	dprintk ("function : %s\n", __FUNCTION__);
+
+	dvb_dmxdev_filter_stop(dmxdevfilter);
+
+	dmxdevfilter->type=DMXDEV_TYPE_SEC;
+	dmxdevfilter->pid=params->pid;
+	memcpy(&dmxdevfilter->params.sec,
+	       params, sizeof(struct dmx_sct_filter_params));
+	invert_mode(&dmxdevfilter->params.sec.filter);
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+	if (params->flags&DMX_IMMEDIATE_START)
+		return dvb_dmxdev_filter_start(dmxdevfilter);
+
+	return 0;
+}
+
+static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
+		   struct dmxdev_filter *dmxdevfilter,
+		   struct dmx_pes_filter_params *params)
+{
+	dvb_dmxdev_filter_stop(dmxdevfilter);
+
+	if (params->pes_type>DMX_PES_OTHER || params->pes_type<0)
+		return -EINVAL;
+
+	dmxdevfilter->type=DMXDEV_TYPE_PES;
+	dmxdevfilter->pid=params->pid;
+	memcpy(&dmxdevfilter->params, params, sizeof(struct dmx_pes_filter_params));
+
+	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+	if (params->flags&DMX_IMMEDIATE_START)
+		return dvb_dmxdev_filter_start(dmxdevfilter);
+
+	return 0;
+}
+
+static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
+		struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	int result, hcount;
+	int done=0;
+
+	if (dfil->todo<=0) {
+		hcount=3+dfil->todo;
+		if (hcount>count)
+			hcount=count;
+		result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK,
+					buf, hcount, ppos);
+		if (result<0) {
+			dfil->todo=0;
+			return result;
+		}
+		if (copy_from_user(dfil->secheader-dfil->todo, buf, result))
+			return -EFAULT;
+		buf+=result;
+		done=result;
+		count-=result;
+		dfil->todo-=result;
+		if (dfil->todo>-3)
+			return done;
+		dfil->todo=((dfil->secheader[1]<<8)|dfil->secheader[2])&0xfff;
+		if (!count)
+			return done;
+	}
+	if (count>dfil->todo)
+		count=dfil->todo;
+	result=dvb_dmxdev_buffer_read(&dfil->buffer, file->f_flags&O_NONBLOCK,
+				buf, count, ppos);
+	if (result<0)
+		return result;
+	dfil->todo-=result;
+	return (result+done);
+}
+
+
+static ssize_t
+dvb_demux_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file);
+	int ret=0;
+
+	if (down_interruptible(&dmxdevfilter->mutex))
+		return -ERESTARTSYS;
+
+	if (dmxdevfilter->type==DMXDEV_TYPE_SEC)
+		ret=dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
+	else
+		ret=dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
+				     file->f_flags&O_NONBLOCK,
+				     buf, count, ppos);
+
+	up(&dmxdevfilter->mutex);
+	return ret;
+}
+
+
+static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
+			      unsigned int cmd, void *parg)
+{
+	struct dmxdev_filter *dmxdevfilter=dvb_dmxdev_file_to_filter(file);
+	struct dmxdev *dmxdev=dmxdevfilter->dev;
+	unsigned long arg=(unsigned long) parg;
+	int ret=0;
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	switch (cmd) {
+	case DMX_START:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		if (dmxdevfilter->state<DMXDEV_STATE_SET)
+			ret = -EINVAL;
+		else
+			ret = dvb_dmxdev_filter_start(dmxdevfilter);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_STOP:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret=dvb_dmxdev_filter_stop(dmxdevfilter);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_FILTER:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter,
+				    (struct dmx_sct_filter_params *)parg);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_PES_FILTER:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret=dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter,
+					       (struct dmx_pes_filter_params *)parg);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_BUFFER_SIZE:
+		if (down_interruptible(&dmxdevfilter->mutex)) {
+			up(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret=dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
+		up(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_GET_EVENT:
+		break;
+
+	case DMX_GET_PES_PIDS:
+		if (!dmxdev->demux->get_pes_pids) {
+			ret=-EINVAL;
+			break;
+		}
+		dmxdev->demux->get_pes_pids(dmxdev->demux, (u16 *)parg);
+		break;
+
+	case DMX_GET_STC:
+		if (!dmxdev->demux->get_stc) {
+		        ret=-EINVAL;
+			break;
+		}
+		ret = dmxdev->demux->get_stc(dmxdev->demux,
+				((struct dmx_stc *)parg)->num,
+				&((struct dmx_stc *)parg)->stc,
+				&((struct dmx_stc *)parg)->base);
+		break;
+
+	default:
+		ret=-EINVAL;
+	}
+	up(&dmxdev->mutex);
+	return ret;
+}
+
+static int dvb_demux_ioctl(struct inode *inode, struct file *file,
+			   unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+
+static unsigned int dvb_demux_poll (struct file *file, poll_table *wait)
+{
+	struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file);
+	unsigned int mask = 0;
+
+	if (!dmxdevfilter)
+		return -EINVAL;
+
+	poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
+	if (dmxdevfilter->state != DMXDEV_STATE_GO &&
+	    dmxdevfilter->state != DMXDEV_STATE_DONE &&
+	    dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
+		return 0;
+
+	if (dmxdevfilter->buffer.error)
+		mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+	if (dmxdevfilter->buffer.pread != dmxdevfilter->buffer.pwrite)
+		mask |= (POLLIN | POLLRDNORM | POLLPRI);
+
+	return mask;
+}
+
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+	struct dmxdev_filter *dmxdevfilter = dvb_dmxdev_file_to_filter(file);
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+	return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+}
+
+
+static struct file_operations dvb_demux_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dvb_demux_read,
+	.ioctl		= dvb_demux_ioctl,
+	.open		= dvb_demux_open,
+	.release	= dvb_demux_release,
+	.poll		= dvb_demux_poll,
+};
+
+
+static struct dvb_device dvbdev_demux = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_demux_fops
+};
+
+
+static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file,
+		     unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev=(struct dmxdev *) dvbdev->priv;
+
+	int ret=0;
+
+	if (down_interruptible (&dmxdev->mutex))
+		return -ERESTARTSYS;
+
+	switch (cmd) {
+	case DMX_SET_BUFFER_SIZE:
+		// FIXME: implement
+		ret=0;
+		break;
+
+	default:
+		ret=-EINVAL;
+	}
+	up(&dmxdev->mutex);
+	return ret;
+}
+
+
+static int dvb_dvr_ioctl(struct inode *inode, struct file *file,
+		  unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+
+static unsigned int dvb_dvr_poll (struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dmxdev *dmxdev = (struct dmxdev *) dvbdev->priv;
+	unsigned int mask = 0;
+
+	dprintk ("function : %s\n", __FUNCTION__);
+
+	poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+	if ((file->f_flags&O_ACCMODE) == O_RDONLY) {
+		if (dmxdev->dvr_buffer.error)
+			mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+		if (dmxdev->dvr_buffer.pread!=dmxdev->dvr_buffer.pwrite)
+			mask |= (POLLIN | POLLRDNORM | POLLPRI);
+	} else
+		mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+
+	return mask;
+}
+
+
+static struct file_operations dvb_dvr_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dvb_dvr_read,
+	.write		= dvb_dvr_write,
+	.ioctl		= dvb_dvr_ioctl,
+	.open		= dvb_dvr_open,
+	.release	= dvb_dvr_release,
+	.poll		= dvb_dvr_poll,
+};
+
+static struct dvb_device dvbdev_dvr = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_dvr_fops
+};
+
+int
+dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
+{
+	int i;
+
+	if (dmxdev->demux->open(dmxdev->demux) < 0)
+		return -EUSERS;
+
+	dmxdev->filter = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_filter));
+	if (!dmxdev->filter)
+		return -ENOMEM;
+
+	dmxdev->dvr = vmalloc(dmxdev->filternum*sizeof(struct dmxdev_dvr));
+	if (!dmxdev->dvr) {
+		vfree(dmxdev->filter);
+		dmxdev->filter = NULL;
+		return -ENOMEM;
+	}
+
+	sema_init(&dmxdev->mutex, 1);
+	spin_lock_init(&dmxdev->lock);
+	for (i=0; i<dmxdev->filternum; i++) {
+		dmxdev->filter[i].dev=dmxdev;
+		dmxdev->filter[i].buffer.data=NULL;
+		dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+		dmxdev->dvr[i].dev=dmxdev;
+		dmxdev->dvr[i].buffer.data=NULL;
+		dvb_dmxdev_filter_state_set(&dmxdev->filter[i], DMXDEV_STATE_FREE);
+		dvb_dmxdev_dvr_state_set(&dmxdev->dvr[i], DMXDEV_STATE_FREE);
+	}
+
+	dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, DVB_DEVICE_DEMUX);
+	dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, dmxdev, DVB_DEVICE_DVR);
+
+	dvb_dmxdev_buffer_init(&dmxdev->dvr_buffer);
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_dmxdev_init);
+
+void
+dvb_dmxdev_release(struct dmxdev *dmxdev)
+{
+	dvb_unregister_device(dmxdev->dvbdev);
+	dvb_unregister_device(dmxdev->dvr_dvbdev);
+
+	vfree(dmxdev->filter);
+	dmxdev->filter=NULL;
+	vfree(dmxdev->dvr);
+	dmxdev->dvr=NULL;
+	dmxdev->demux->close(dmxdev->demux);
+}
+EXPORT_SYMBOL(dvb_dmxdev_release);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
new file mode 100644
index 0000000..395a9cd
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -0,0 +1,128 @@
+/*
+ * dmxdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DMXDEV_H_
+#define _DMXDEV_H_
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <asm/semaphore.h>
+
+#include <linux/dvb/dmx.h>
+
+#include "dvbdev.h"
+#include "demux.h"
+
+enum dmxdevype {
+	DMXDEV_TYPE_NONE,
+	DMXDEV_TYPE_SEC,
+	DMXDEV_TYPE_PES,
+};
+
+enum dmxdev_state {
+	DMXDEV_STATE_FREE,
+	DMXDEV_STATE_ALLOCATED,
+	DMXDEV_STATE_SET,
+	DMXDEV_STATE_GO,
+	DMXDEV_STATE_DONE,
+	DMXDEV_STATE_TIMEDOUT
+};
+
+struct dmxdev_buffer {
+        u8 *data;
+        int size;
+        int pread;
+        int pwrite;
+	wait_queue_head_t queue;
+        int error;
+};
+
+struct dmxdev_filter {
+	struct dvb_device *dvbdev;
+
+        union {
+	        struct dmx_section_filter *sec;
+	} filter;
+
+        union {
+                struct dmx_ts_feed *ts;
+                struct dmx_section_feed *sec;
+	} feed;
+
+        union {
+	        struct dmx_sct_filter_params sec;
+	        struct dmx_pes_filter_params pes;
+	} params;
+
+        int type;
+        enum dmxdev_state state;
+        struct dmxdev *dev;
+        struct dmxdev_buffer buffer;
+
+	struct semaphore mutex;
+
+        /* only for sections */
+        struct timer_list timer;
+        int todo;
+        u8 secheader[3];
+
+        u16 pid;
+};
+
+
+struct dmxdev_dvr {
+        int state;
+        struct dmxdev *dev;
+        struct dmxdev_buffer buffer;
+};
+
+
+struct dmxdev {
+	struct dvb_device *dvbdev;
+	struct dvb_device *dvr_dvbdev;
+
+        struct dmxdev_filter *filter;
+        struct dmxdev_dvr *dvr;
+        struct dmx_demux *demux;
+
+        int filternum;
+        int capabilities;
+#define DMXDEV_CAP_DUPLEX 1
+        struct dmx_frontend *dvr_orig_fe;
+
+        struct dmxdev_buffer dvr_buffer;
+#define DVR_BUFFER_SIZE (10*188*1024)
+
+	struct semaphore mutex;
+	spinlock_t lock;
+};
+
+
+int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *);
+void dvb_dmxdev_release(struct dmxdev *dmxdev);
+
+#endif /* _DMXDEV_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
new file mode 100644
index 0000000..c1ea89f
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -0,0 +1,1778 @@
+/*
+ * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * based on code:
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/rwsem.h>
+
+#include "dvb_ca_en50221.h"
+#include "dvb_ringbuffer.h"
+
+static int dvb_ca_en50221_debug;
+
+module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
+MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
+
+#define dprintk if (dvb_ca_en50221_debug) printk
+
+#define INIT_TIMEOUT_SECS 5
+
+#define HOST_LINK_BUF_SIZE 0x200
+
+#define RX_BUFFER_SIZE 65535
+
+#define MAX_RX_PACKETS_PER_ITERATION 10
+
+#define CTRLIF_DATA      0
+#define CTRLIF_COMMAND   1
+#define CTRLIF_STATUS    1
+#define CTRLIF_SIZE_LOW  2
+#define CTRLIF_SIZE_HIGH 3
+
+#define CMDREG_HC        1	/* Host control */
+#define CMDREG_SW        2	/* Size write */
+#define CMDREG_SR        4	/* Size read */
+#define CMDREG_RS        8	/* Reset interface */
+#define CMDREG_FRIE   0x40	/* Enable FR interrupt */
+#define CMDREG_DAIE   0x80	/* Enable DA interrupt */
+#define IRQEN (CMDREG_DAIE)
+
+#define STATUSREG_RE     1	/* read error */
+#define STATUSREG_WE     2	/* write error */
+#define STATUSREG_FR  0x40	/* module free */
+#define STATUSREG_DA  0x80	/* data available */
+#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE)	/* general transfer error */
+
+
+#define DVB_CA_SLOTSTATE_NONE           0
+#define DVB_CA_SLOTSTATE_UNINITIALISED  1
+#define DVB_CA_SLOTSTATE_RUNNING        2
+#define DVB_CA_SLOTSTATE_INVALID        3
+#define DVB_CA_SLOTSTATE_WAITREADY      4
+#define DVB_CA_SLOTSTATE_VALIDATE       5
+#define DVB_CA_SLOTSTATE_WAITFR         6
+#define DVB_CA_SLOTSTATE_LINKINIT       7
+
+
+/* Information on a CA slot */
+struct dvb_ca_slot {
+
+	/* current state of the CAM */
+	int slot_state;
+
+	/* Number of CAMCHANGES that have occurred since last processing */
+	atomic_t camchange_count;
+
+	/* Type of last CAMCHANGE */
+	int camchange_type;
+
+	/* base address of CAM config */
+	u32 config_base;
+
+	/* value to write into Config Control register */
+	u8 config_option;
+
+	/* if 1, the CAM supports DA IRQs */
+	u8 da_irq_supported:1;
+
+	/* size of the buffer to use when talking to the CAM */
+	int link_buf_size;
+
+	/* semaphore for syncing access to slot structure */
+	struct rw_semaphore sem;
+
+	/* buffer for incoming packets */
+	struct dvb_ringbuffer rx_buffer;
+
+	/* timer used during various states of the slot */
+	unsigned long timeout;
+};
+
+/* Private CA-interface information */
+struct dvb_ca_private {
+
+	/* pointer back to the public data structure */
+	struct dvb_ca_en50221 *pub;
+
+	/* the DVB device */
+	struct dvb_device *dvbdev;
+
+	/* Flags describing the interface (DVB_CA_FLAG_*) */
+	u32 flags;
+
+	/* number of slots supported by this CA interface */
+	unsigned int slot_count;
+
+	/* information on each slot */
+	struct dvb_ca_slot *slot_info;
+
+	/* wait queues for read() and write() operations */
+	wait_queue_head_t wait_queue;
+
+	/* PID of the monitoring thread */
+	pid_t thread_pid;
+
+	/* Wait queue used when shutting thread down */
+	wait_queue_head_t thread_queue;
+
+	/* Flag indicating when thread should exit */
+	unsigned int exit:1;
+
+	/* Flag indicating if the CA device is open */
+	unsigned int open:1;
+
+	/* Flag indicating the thread should wake up now */
+	unsigned int wakeup:1;
+
+	/* Delay the main thread should use */
+	unsigned long delay;
+
+	/* Slot to start looking for data to read from in the next user-space read operation */
+	int next_read_slot;
+};
+
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
+
+
+/**
+ * Safely find needle in haystack.
+ *
+ * @param haystack Buffer to look in.
+ * @param hlen Number of bytes in haystack.
+ * @param needle Buffer to find.
+ * @param nlen Number of bytes in needle.
+ * @return Pointer into haystack needle was found at, or NULL if not found.
+ */
+static u8 *findstr(u8 * haystack, int hlen, u8 * needle, int nlen)
+{
+	int i;
+
+	if (hlen < nlen)
+		return NULL;
+
+	for (i = 0; i <= hlen - nlen; i++) {
+		if (!strncmp(haystack + i, needle, nlen))
+			return haystack + i;
+	}
+
+	return NULL;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 physical interface functions */
+
+
+/**
+ * Check CAM status.
+ */
+static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
+{
+	int slot_status;
+	int cam_present_now;
+	int cam_changed;
+
+	/* IRQ mode */
+	if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
+		return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
+	}
+
+	/* poll mode */
+	slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
+
+	cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
+	cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
+	if (!cam_changed) {
+		int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
+		cam_changed = (cam_present_now != cam_present_old);
+	}
+
+	if (cam_changed) {
+		if (!cam_present_now) {
+			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+		} else {
+			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+		}
+		atomic_set(&ca->slot_info[slot].camchange_count, 1);
+	} else {
+		if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
+		    (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
+			// move to validate state if reset is completed
+			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+		}
+	}
+
+	return cam_changed;
+}
+
+
+/**
+ * Wait for flags to become set on the STATUS register on a CAM interface,
+ * checking for errors and timeout.
+ *
+ * @param ca CA instance.
+ * @param slot Slot on interface.
+ * @param waitfor Flags to wait for.
+ * @param timeout_ms Timeout in milliseconds.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
+					 u8 waitfor, int timeout_hz)
+{
+	unsigned long timeout;
+	unsigned long start;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* loop until timeout elapsed */
+	start = jiffies;
+	timeout = jiffies + timeout_hz;
+	while (1) {
+		/* read the status and check for error */
+		int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+		if (res < 0)
+			return -EIO;
+
+		/* if we got the flags, it was successful! */
+		if (res & waitfor) {
+			dprintk("%s succeeded timeout:%lu\n", __FUNCTION__, jiffies - start);
+			return 0;
+		}
+
+		/* check for timeout */
+		if (time_after(jiffies, timeout)) {
+			break;
+		}
+
+		/* wait for a bit */
+		msleep(1);
+	}
+
+	dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start);
+
+	/* if we get here, we've timed out */
+	return -ETIMEDOUT;
+}
+
+
+/**
+ * Initialise the link layer connection to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, nonzero on failure.
+ */
+static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
+{
+	int ret;
+	int buf_size;
+	u8 buf[2];
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* we'll be determining these during this function */
+	ca->slot_info[slot].da_irq_supported = 0;
+
+	/* set the host link buffer size temporarily. it will be overwritten with the
+	 * real negotiated size later. */
+	ca->slot_info[slot].link_buf_size = 2;
+
+	/* read the buffer size from the CAM */
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2)
+		return -EIO;
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
+		return ret;
+
+	/* store it, and choose the minimum of our buffer and the CAM's buffer size */
+	buf_size = (buf[0] << 8) | buf[1];
+	if (buf_size > HOST_LINK_BUF_SIZE)
+		buf_size = HOST_LINK_BUF_SIZE;
+	ca->slot_info[slot].link_buf_size = buf_size;
+	buf[0] = buf_size >> 8;
+	buf[1] = buf_size & 0xff;
+	dprintk("Chosen link buffer size of %i\n", buf_size);
+
+	/* write the buffer size to the CAM */
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0)
+		return ret;
+	if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2)
+		return -EIO;
+	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
+		return ret;
+
+	/* success */
+	return 0;
+}
+
+/**
+ * Read a tuple from attribute memory.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ * @param address Address to read from. Updated.
+ * @param tupleType Tuple id byte. Updated.
+ * @param tupleLength Tuple length. Updated.
+ * @param tuple Dest buffer for tuple (must be 256 bytes). Updated.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
+				     int *address, int *tupleType, int *tupleLength, u8 * tuple)
+{
+	int i;
+	int _tupleType;
+	int _tupleLength;
+	int _address = *address;
+
+	/* grab the next tuple length and type */
+	if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0)
+		return _tupleType;
+	if (_tupleType == 0xff) {
+		dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType);
+		*address += 2;
+		*tupleType = _tupleType;
+		*tupleLength = 0;
+		return 0;
+	}
+	if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0)
+		return _tupleLength;
+	_address += 4;
+
+	dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength);
+
+	/* read in the whole tuple */
+	for (i = 0; i < _tupleLength; i++) {
+		tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2));
+		dprintk("  0x%02x: 0x%02x %c\n",
+			i, tuple[i] & 0xff,
+			((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
+	}
+	_address += (_tupleLength * 2);
+
+	// success
+	*tupleType = _tupleType;
+	*tupleLength = _tupleLength;
+	*address = _address;
+	return 0;
+}
+
+
+/**
+ * Parse attribute memory of a CAM module, extracting Config register, and checking
+ * it is a DVB CAM module.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
+{
+	int address = 0;
+	int tupleLength;
+	int tupleType;
+	u8 tuple[257];
+	char *dvb_str;
+	int rasz;
+	int status;
+	int got_cftableentry = 0;
+	int end_chain = 0;
+	int i;
+	u16 manfid = 0;
+	u16 devid = 0;
+
+
+	// CISTPL_DEVICE_0A
+	if ((status =
+	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x1D)
+		return -EINVAL;
+
+
+
+	// CISTPL_DEVICE_0C
+	if ((status =
+	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x1C)
+		return -EINVAL;
+
+
+
+	// CISTPL_VERS_1
+	if ((status =
+	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x15)
+		return -EINVAL;
+
+
+
+	// CISTPL_MANFID
+	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+						&tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x20)
+		return -EINVAL;
+	if (tupleLength != 4)
+		return -EINVAL;
+	manfid = (tuple[1] << 8) | tuple[0];
+	devid = (tuple[3] << 8) | tuple[2];
+
+
+
+	// CISTPL_CONFIG
+	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+						&tupleLength, tuple)) < 0)
+		return status;
+	if (tupleType != 0x1A)
+		return -EINVAL;
+	if (tupleLength < 3)
+		return -EINVAL;
+
+	/* extract the configbase */
+	rasz = tuple[0] & 3;
+	if (tupleLength < (3 + rasz + 14))
+		return -EINVAL;
+	ca->slot_info[slot].config_base = 0;
+	for (i = 0; i < rasz + 1; i++) {
+		ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i));
+	}
+
+	/* check it contains the correct DVB string */
+	dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8);
+	if (dvb_str == NULL)
+		return -EINVAL;
+	if (tupleLength < ((dvb_str - (char *) tuple) + 12))
+		return -EINVAL;
+
+	/* is it a version we support? */
+	if (strncmp(dvb_str + 8, "1.00", 4)) {
+		printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
+		       ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
+		return -EINVAL;
+	}
+
+	/* process the CFTABLE_ENTRY tuples, and any after those */
+	while ((!end_chain) && (address < 0x1000)) {
+		if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
+						        &tupleLength, tuple)) < 0)
+			return status;
+		switch (tupleType) {
+		case 0x1B:	// CISTPL_CFTABLE_ENTRY
+			if (tupleLength < (2 + 11 + 17))
+				break;
+
+			/* if we've already parsed one, just use it */
+			if (got_cftableentry)
+				break;
+
+			/* get the config option */
+			ca->slot_info[slot].config_option = tuple[0] & 0x3f;
+
+			/* OK, check it contains the correct strings */
+			if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
+			    (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL))
+				break;
+
+			got_cftableentry = 1;
+			break;
+
+		case 0x14:	// CISTPL_NO_LINK
+			break;
+
+		case 0xFF:	// CISTPL_END
+			end_chain = 1;
+			break;
+
+		default:	/* Unknown tuple type - just skip this tuple and move to the next one */
+			dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType,
+				tupleLength);
+			break;
+		}
+	}
+
+	if ((address > 0x1000) || (!got_cftableentry))
+		return -EINVAL;
+
+	dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
+		manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option);
+
+	// success!
+	return 0;
+}
+
+
+/**
+ * Set CAM's configoption correctly.
+ *
+ * @param ca CA instance.
+ * @param slot Slot containing the CAM.
+ */
+static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
+{
+	int configoption;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* set the config option */
+	ca->pub->write_attribute_mem(ca->pub, slot,
+				     ca->slot_info[slot].config_base,
+				     ca->slot_info[slot].config_option);
+
+	/* check it */
+	configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
+	dprintk("Set configoption 0x%x, read configoption 0x%x\n",
+		ca->slot_info[slot].config_option, configoption & 0x3f);
+
+	/* fine! */
+	return 0;
+
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It reads a buffer of
+ * data from the CAM. The data can either be stored in a supplied buffer, or
+ * automatically be added to the slot's rx_buffer.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to read from.
+ * @param ebuf If non-NULL, the data will be written to this buffer. If NULL,
+ * the data will be added into the buffering system as a normal fragment.
+ * @param ecount Size of ebuf. Ignored if ebuf is NULL.
+ *
+ * @return Number of bytes read, or < 0 on error
+ */
+static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount)
+{
+	int bytes_read;
+	int status;
+	u8 buf[HOST_LINK_BUF_SIZE];
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* check if we have space for a link buf in the rx_buffer */
+	if (ebuf == NULL) {
+		int buf_free;
+
+		down_read(&ca->slot_info[slot].sem);
+		if (ca->slot_info[slot].rx_buffer.data == NULL) {
+			up_read(&ca->slot_info[slot].sem);
+			status = -EIO;
+			goto exit;
+		}
+		buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
+		up_read(&ca->slot_info[slot].sem);
+
+		if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+			status = -EAGAIN;
+			goto exit;
+		}
+	}
+
+	/* check if there is data available */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (!(status & STATUSREG_DA)) {
+		/* no data */
+		status = 0;
+		goto exit;
+	}
+
+	/* read the amount of data */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0)
+		goto exit;
+	bytes_read = status << 8;
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0)
+		goto exit;
+	bytes_read |= status;
+
+	/* check it will fit */
+	if (ebuf == NULL) {
+		if (bytes_read > ca->slot_info[slot].link_buf_size) {
+			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
+			       ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size);
+			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+			status = -EIO;
+			goto exit;
+		}
+		if (bytes_read < 2) {
+			printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
+			       ca->dvbdev->adapter->num);
+			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+			status = -EIO;
+			goto exit;
+		}
+	} else {
+		if (bytes_read > ecount) {
+			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
+			       ca->dvbdev->adapter->num);
+			status = -EIO;
+			goto exit;
+		}
+	}
+
+	/* fill the buffer */
+	for (i = 0; i < bytes_read; i++) {
+		/* read byte and check */
+		if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0)
+			goto exit;
+
+		/* OK, store it in the buffer */
+		buf[i] = status;
+	}
+
+	/* check for read error (RE should now be 0) */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (status & STATUSREG_RE) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+		status = -EIO;
+		goto exit;
+	}
+
+	/* OK, add it to the receive buffer, or copy into external buffer if supplied */
+	if (ebuf == NULL) {
+		down_read(&ca->slot_info[slot].sem);
+		if (ca->slot_info[slot].rx_buffer.data == NULL) {
+			up_read(&ca->slot_info[slot].sem);
+			status = -EIO;
+			goto exit;
+		}
+		dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
+		up_read(&ca->slot_info[slot].sem);
+	} else {
+		memcpy(ebuf, buf, bytes_read);
+	}
+
+	dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
+		buf[0], (buf[1] & 0x80) == 0, bytes_read);
+
+	/* wake up readers when a last_fragment is received */
+	if ((buf[1] & 0x80) == 0x00) {
+		wake_up_interruptible(&ca->wait_queue);
+	}
+	status = bytes_read;
+
+exit:
+	return status;
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It writes a buffer of data
+ * to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to write to.
+ * @param ebuf The data in this buffer is treated as a complete link-level packet to
+ * be written.
+ * @param count Size of ebuf.
+ *
+ * @return Number of bytes written, or < 0 on error.
+ */
+static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write)
+{
+	int status;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+
+	// sanity check
+	if (bytes_write > ca->slot_info[slot].link_buf_size)
+		return -EINVAL;
+
+	/* check if interface is actually waiting for us to read from it, or if a read is in progress */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exitnowrite;
+	if (status & (STATUSREG_DA | STATUSREG_RE)) {
+		status = -EAGAIN;
+		goto exitnowrite;
+	}
+
+	/* OK, set HC bit */
+	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
+						 IRQEN | CMDREG_HC)) != 0)
+		goto exit;
+
+	/* check if interface is still free */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (!(status & STATUSREG_FR)) {
+		/* it wasn't free => try again later */
+		status = -EAGAIN;
+		goto exit;
+	}
+
+	/* send the amount of data */
+	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0)
+		goto exit;
+	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
+						 bytes_write & 0xff)) != 0)
+		goto exit;
+
+	/* send the buffer */
+	for (i = 0; i < bytes_write; i++) {
+		if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0)
+			goto exit;
+	}
+
+	/* check for write error (WE should now be 0) */
+	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+		goto exit;
+	if (status & STATUSREG_WE) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+		status = -EIO;
+		goto exit;
+	}
+	status = bytes_write;
+
+	dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
+		buf[0], (buf[1] & 0x80) == 0, bytes_write);
+
+exit:
+	ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+
+exitnowrite:
+	return status;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
+
+
+
+/* ******************************************************************************** */
+/* EN50221 higher level functions */
+
+
+/**
+ * A CAM has been removed => shut it down.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to shut down.
+ */
+static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	down_write(&ca->slot_info[slot].sem);
+	ca->pub->slot_shutdown(ca->pub, slot);
+	ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+	vfree(ca->slot_info[slot].rx_buffer.data);
+	ca->slot_info[slot].rx_buffer.data = NULL;
+	up_write(&ca->slot_info[slot].sem);
+
+	/* need to wake up all processes to check if they're now
+	   trying to write to a defunct CAM */
+	wake_up_interruptible(&ca->wait_queue);
+
+	dprintk("Slot %i shutdown\n", slot);
+
+	/* success */
+	return 0;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
+
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values.
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+
+	dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
+
+	switch (change_type) {
+	case DVB_CA_EN50221_CAMCHANGE_REMOVED:
+	case DVB_CA_EN50221_CAMCHANGE_INSERTED:
+		break;
+
+	default:
+		return;
+	}
+
+	ca->slot_info[slot].camchange_type = change_type;
+	atomic_inc(&ca->slot_info[slot].camchange_count);
+	dvb_ca_en50221_thread_wakeup(ca);
+}
+EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
+
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+
+	dprintk("CAMREADY IRQ slot:%i\n", slot);
+
+	if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
+		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+		dvb_ca_en50221_thread_wakeup(ca);
+	}
+}
+
+
+/**
+ * An FR or DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+	int flags;
+
+	dprintk("FR/DA IRQ slot:%i\n", slot);
+
+	switch (ca->slot_info[slot].slot_state) {
+	case DVB_CA_SLOTSTATE_LINKINIT:
+		flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
+		if (flags & STATUSREG_DA) {
+			dprintk("CAM supports DA IRQ\n");
+			ca->slot_info[slot].da_irq_supported = 1;
+		}
+		break;
+
+	case DVB_CA_SLOTSTATE_RUNNING:
+		if (ca->open)
+			dvb_ca_en50221_read_data(ca, slot, NULL, 0);
+		break;
+	}
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 thread functions */
+
+/**
+ * Wake up the DVB CA thread
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
+{
+
+	dprintk("%s\n", __FUNCTION__);
+
+	ca->wakeup = 1;
+	mb();
+	wake_up_interruptible(&ca->thread_queue);
+}
+
+/**
+ * Used by the CA thread to determine if an early wakeup is necessary
+ *
+ * @param ca CA instance.
+ */
+static int dvb_ca_en50221_thread_should_wakeup(struct dvb_ca_private *ca)
+{
+	if (ca->wakeup) {
+		ca->wakeup = 0;
+		return 1;
+	}
+	if (ca->exit)
+		return 1;
+
+	return 0;
+}
+
+
+/**
+ * Update the delay used by the thread.
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
+{
+	int delay;
+	int curdelay = 100000000;
+	int slot;
+
+	for (slot = 0; slot < ca->slot_count; slot++) {
+		switch (ca->slot_info[slot].slot_state) {
+		default:
+		case DVB_CA_SLOTSTATE_NONE:
+		case DVB_CA_SLOTSTATE_INVALID:
+			delay = HZ * 60;
+			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) {
+				delay = HZ / 10;
+			}
+			break;
+
+		case DVB_CA_SLOTSTATE_UNINITIALISED:
+		case DVB_CA_SLOTSTATE_WAITREADY:
+		case DVB_CA_SLOTSTATE_VALIDATE:
+		case DVB_CA_SLOTSTATE_WAITFR:
+		case DVB_CA_SLOTSTATE_LINKINIT:
+			delay = HZ / 10;
+			break;
+
+		case DVB_CA_SLOTSTATE_RUNNING:
+			delay = HZ * 60;
+			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) {
+				delay = HZ / 10;
+			}
+			if (ca->open) {
+				if ((!ca->slot_info[slot].da_irq_supported) ||
+				    (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) {
+					delay = HZ / 10;
+				}
+			}
+			break;
+		}
+
+		if (delay < curdelay)
+			curdelay = delay;
+	}
+
+	ca->delay = curdelay;
+}
+
+
+
+/**
+ * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
+ */
+static int dvb_ca_en50221_thread(void *data)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) data;
+	char name[15];
+	int slot;
+	int flags;
+	int status;
+	int pktcount;
+	void *rxbuf;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* setup kernel thread */
+	snprintf(name, sizeof(name), "kdvb-ca-%i:%i", ca->dvbdev->adapter->num, ca->dvbdev->id);
+
+	lock_kernel();
+	daemonize(name);
+	sigfillset(&current->blocked);
+	unlock_kernel();
+
+	/* choose the correct initial delay */
+	dvb_ca_en50221_thread_update_delay(ca);
+
+	/* main loop */
+	while (!ca->exit) {
+		/* sleep for a bit */
+		if (!ca->wakeup) {
+			flags = wait_event_interruptible_timeout(ca->thread_queue,
+								 dvb_ca_en50221_thread_should_wakeup(ca),
+								 ca->delay);
+			if ((flags == -ERESTARTSYS) || ca->exit) {
+				/* got signal or quitting */
+				break;
+			}
+		}
+		ca->wakeup = 0;
+
+		/* go through all the slots processing them */
+		for (slot = 0; slot < ca->slot_count; slot++) {
+
+			// check the cam status + deal with CAMCHANGEs
+			while (dvb_ca_en50221_check_camstatus(ca, slot)) {
+				/* clear down an old CI slot if necessary */
+				if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE)
+					dvb_ca_en50221_slot_shutdown(ca, slot);
+
+				/* if a CAM is NOW present, initialise it */
+				if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) {
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
+				}
+
+				/* we've handled one CAMCHANGE */
+				dvb_ca_en50221_thread_update_delay(ca);
+				atomic_dec(&ca->slot_info[slot].camchange_count);
+			}
+
+			// CAM state machine
+			switch (ca->slot_info[slot].slot_state) {
+			case DVB_CA_SLOTSTATE_NONE:
+			case DVB_CA_SLOTSTATE_INVALID:
+				// no action needed
+				break;
+
+			case DVB_CA_SLOTSTATE_UNINITIALISED:
+				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY;
+				ca->pub->slot_reset(ca->pub, slot);
+				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+				break;
+
+			case DVB_CA_SLOTSTATE_WAITREADY:
+				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+					printk("dvb_ca adaptor %d: PC card did not respond :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				// no other action needed; will automatically change state when ready
+				break;
+
+			case DVB_CA_SLOTSTATE_VALIDATE:
+				if (dvb_ca_en50221_parse_attributes(ca, slot)
+				    != 0) {
+					printk("dvb_ca adapter %d: Invalid PC card inserted :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
+					printk("dvb_ca adapter %d: Unable to initialise CAM :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				if (ca->pub->write_cam_control(ca->pub, slot,
+							       CTRLIF_COMMAND, CMDREG_RS) != 0) {
+					printk("dvb_ca adapter %d: Unable to reset CAM IF\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				dprintk("DVB CAM validated successfully\n");
+
+				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR;
+				ca->wakeup = 1;
+				break;
+
+			case DVB_CA_SLOTSTATE_WAITFR:
+				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+					printk("dvb_ca adapter %d: DVB CAM did not respond :(\n",
+					       ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+
+				flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+				if (flags & STATUSREG_FR) {
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+					ca->wakeup = 1;
+				}
+				break;
+
+			case DVB_CA_SLOTSTATE_LINKINIT:
+				if (dvb_ca_en50221_link_init(ca, slot) != 0) {
+					printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+
+				rxbuf = vmalloc(RX_BUFFER_SIZE);
+				if (rxbuf == NULL) {
+					printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num);
+					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+					dvb_ca_en50221_thread_update_delay(ca);
+					break;
+				}
+				down_write(&ca->slot_info[slot].sem);
+				dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE);
+				up_write(&ca->slot_info[slot].sem);
+
+				ca->pub->slot_ts_enable(ca->pub, slot);
+				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING;
+				dvb_ca_en50221_thread_update_delay(ca);
+				printk("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num);
+				break;
+
+			case DVB_CA_SLOTSTATE_RUNNING:
+				if (!ca->open)
+					continue;
+
+				// no need to poll if the CAM supports IRQs
+				if (ca->slot_info[slot].da_irq_supported)
+					break;
+
+				// poll mode
+				pktcount = 0;
+				while ((status = dvb_ca_en50221_read_data(ca, slot, NULL, 0)) > 0) {
+					if (!ca->open)
+						break;
+
+					/* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */
+					if (dvb_ca_en50221_check_camstatus(ca, slot)) {
+						// we dont want to sleep on the next iteration so we can handle the cam change
+						ca->wakeup = 1;
+						break;
+					}
+
+					/* check if we've hit our limit this time */
+					if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
+						// dont sleep; there is likely to be more data to read
+						ca->wakeup = 1;
+						break;
+					}
+				}
+				break;
+			}
+		}
+	}
+
+	/* completed */
+	ca->thread_pid = 0;
+	mb();
+	wake_up_interruptible(&ca->thread_queue);
+	return 0;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 IO interface functions */
+
+/**
+ * Real ioctl implementation.
+ * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
+				      unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int err = 0;
+	int slot;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	switch (cmd) {
+	case CA_RESET:
+		for (slot = 0; slot < ca->slot_count; slot++) {
+			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
+				dvb_ca_en50221_slot_shutdown(ca, slot);
+				if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
+					dvb_ca_en50221_camchange_irq(ca->pub,
+								     slot,
+								     DVB_CA_EN50221_CAMCHANGE_INSERTED);
+			}
+		}
+		ca->next_read_slot = 0;
+		dvb_ca_en50221_thread_wakeup(ca);
+		break;
+
+	case CA_GET_CAP: {
+		struct ca_caps *caps = (struct ca_caps *) parg;
+
+		caps->slot_num = ca->slot_count;
+		caps->slot_type = CA_CI_LINK;
+		caps->descr_num = 0;
+		caps->descr_type = 0;
+		break;
+	}
+
+	case CA_GET_SLOT_INFO: {
+		struct ca_slot_info *info = (struct ca_slot_info *) parg;
+
+		if ((info->num > ca->slot_count) || (info->num < 0))
+			return -EINVAL;
+
+		info->type = CA_CI_LINK;
+		info->flags = 0;
+		if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE)
+			&& (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) {
+			info->flags = CA_CI_MODULE_PRESENT;
+		}
+		if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+			info->flags |= CA_CI_MODULE_READY;
+		}
+		break;
+	}
+
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	return err;
+}
+
+
+/**
+ * Wrapper for ioctl implementation.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file,
+				   unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
+}
+
+
+/**
+ * Implementation of write() syscall.
+ *
+ * @param file File structure.
+ * @param buf Source buffer.
+ * @param count Size of source buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_write(struct file *file,
+				       const char __user * buf, size_t count, loff_t * ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	u8 slot, connection_id;
+	int status;
+	char fragbuf[HOST_LINK_BUF_SIZE];
+	int fragpos = 0;
+	int fraglen;
+	unsigned long timeout;
+	int written;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+	if (count < 2)
+		return -EINVAL;
+
+	/* extract slot & connection id */
+	if (copy_from_user(&slot, buf, 1))
+		return -EFAULT;
+	if (copy_from_user(&connection_id, buf + 1, 1))
+		return -EFAULT;
+	buf += 2;
+	count -= 2;
+
+	/* check if the slot is actually running */
+	if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
+		return -EINVAL;
+
+	/* fragment the packets & store in the buffer */
+	while (fragpos < count) {
+		fraglen = ca->slot_info[slot].link_buf_size - 2;
+		if ((count - fragpos) < fraglen)
+			fraglen = count - fragpos;
+
+		fragbuf[0] = connection_id;
+		fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
+		if ((status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen)) != 0)
+			goto exit;
+
+		timeout = jiffies + HZ / 2;
+		written = 0;
+		while (!time_after(jiffies, timeout)) {
+			/* check the CAM hasn't been removed/reset in the meantime */
+			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) {
+				status = -EIO;
+				goto exit;
+			}
+
+			status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
+			if (status == (fraglen + 2)) {
+				written = 1;
+				break;
+			}
+			if (status != -EAGAIN)
+				goto exit;
+
+			msleep(1);
+		}
+		if (!written) {
+			status = -EIO;
+			goto exit;
+		}
+
+		fragpos += fraglen;
+	}
+	status = count + 2;
+
+exit:
+	return status;
+}
+
+
+/**
+ * Condition for waking up in dvb_ca_en50221_io_read_condition
+ */
+static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca, int *result, int *_slot)
+{
+	int slot;
+	int slot_count = 0;
+	int idx;
+	int fraglen;
+	int connection_id = -1;
+	int found = 0;
+	u8 hdr[2];
+
+	slot = ca->next_read_slot;
+	while ((slot_count < ca->slot_count) && (!found)) {
+		if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
+			goto nextslot;
+
+		down_read(&ca->slot_info[slot].sem);
+
+		if (ca->slot_info[slot].rx_buffer.data == NULL) {
+			up_read(&ca->slot_info[slot].sem);
+			return 0;
+		}
+
+		idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+		while (idx != -1) {
+			dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+			if (connection_id == -1)
+				connection_id = hdr[0];
+			if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
+				*_slot = slot;
+				found = 1;
+				break;
+			}
+
+			idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+		}
+
+		if (!found)
+			up_read(&ca->slot_info[slot].sem);
+
+	      nextslot:
+		slot = (slot + 1) % ca->slot_count;
+		slot_count++;
+	}
+
+	ca->next_read_slot = slot;
+	return found;
+}
+
+
+/**
+ * Implementation of read() syscall.
+ *
+ * @param file File structure.
+ * @param buf Destination buffer.
+ * @param count Size of destination buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
+				      size_t count, loff_t * ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int status;
+	int result = 0;
+	u8 hdr[2];
+	int slot;
+	int connection_id = -1;
+	size_t idx, idx2;
+	int last_fragment = 0;
+	size_t fraglen;
+	int pktlen;
+	int dispose = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+	if (count < 2)
+		return -EINVAL;
+
+	/* wait for some data */
+	if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) {
+
+		/* if we're in nonblocking mode, exit immediately */
+		if (file->f_flags & O_NONBLOCK)
+			return -EWOULDBLOCK;
+
+		/* wait for some data */
+		status = wait_event_interruptible(ca->wait_queue,
+						  dvb_ca_en50221_io_read_condition
+						  (ca, &result, &slot));
+	}
+	if ((status < 0) || (result < 0)) {
+		if (result)
+			return result;
+		return status;
+	}
+
+	idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+	pktlen = 2;
+	do {
+		if (idx == -1) {
+			printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num);
+			status = -EIO;
+			goto exit;
+		}
+
+		dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+		if (connection_id == -1)
+			connection_id = hdr[0];
+		if (hdr[0] == connection_id) {
+			if (pktlen < count) {
+				if ((pktlen + fraglen - 2) > count) {
+					fraglen = count - pktlen;
+				} else {
+					fraglen -= 2;
+				}
+
+				if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2,
+								      buf + pktlen, fraglen, 1)) < 0) {
+					goto exit;
+				}
+				pktlen += fraglen;
+			}
+
+			if ((hdr[1] & 0x80) == 0)
+				last_fragment = 1;
+			dispose = 1;
+		}
+
+		idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+		if (dispose)
+			dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx);
+		idx = idx2;
+		dispose = 0;
+	} while (!last_fragment);
+
+	hdr[0] = slot;
+	hdr[1] = connection_id;
+	if ((status = copy_to_user(buf, hdr, 2)) != 0)
+		goto exit;
+	status = pktlen;
+
+      exit:
+	up_read(&ca->slot_info[slot].sem);
+	return status;
+}
+
+
+/**
+ * Implementation of file open syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int err;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (!try_module_get(ca->pub->owner))
+		return -EIO;
+
+	err = dvb_generic_open(inode, file);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < ca->slot_count; i++) {
+
+		if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+			down_write(&ca->slot_info[i].sem);
+			if (ca->slot_info[i].rx_buffer.data != NULL) {
+				dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
+			}
+			up_write(&ca->slot_info[i].sem);
+		}
+	}
+
+	ca->open = 1;
+	dvb_ca_en50221_thread_update_delay(ca);
+	dvb_ca_en50221_thread_wakeup(ca);
+
+	return 0;
+}
+
+
+/**
+ * Implementation of file close syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	int err = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* mark the CA device as closed */
+	ca->open = 0;
+	dvb_ca_en50221_thread_update_delay(ca);
+
+	err = dvb_generic_release(inode, file);
+
+	module_put(ca->pub->owner);
+
+	return 0;
+}
+
+
+/**
+ * Implementation of poll() syscall.
+ *
+ * @param file File concerned.
+ * @param wait poll wait table.
+ *
+ * @return Standard poll mask.
+ */
+static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) dvbdev->priv;
+	unsigned int mask = 0;
+	int slot;
+	int result = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+		up_read(&ca->slot_info[slot].sem);
+		mask |= POLLIN;
+	}
+
+	/* if there is something, return now */
+	if (mask)
+		return mask;
+
+	/* wait for something to happen */
+	poll_wait(file, &ca->wait_queue, wait);
+
+	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+		up_read(&ca->slot_info[slot].sem);
+		mask |= POLLIN;
+	}
+
+	return mask;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_init);
+
+
+static struct file_operations dvb_ca_fops = {
+	.owner = THIS_MODULE,
+	.read = dvb_ca_en50221_io_read,
+	.write = dvb_ca_en50221_io_write,
+	.ioctl = dvb_ca_en50221_io_ioctl,
+	.open = dvb_ca_en50221_io_open,
+	.release = dvb_ca_en50221_io_release,
+	.poll = dvb_ca_en50221_io_poll,
+};
+
+static struct dvb_device dvbdev_ca = {
+	.priv = NULL,
+	.users = 1,
+	.readers = 1,
+	.writers = 1,
+	.fops = &dvb_ca_fops,
+};
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+
+/**
+ * Initialise a new DVB CA EN50221 interface device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
+			struct dvb_ca_en50221 *pubca, int flags, int slot_count)
+{
+	int ret;
+	struct dvb_ca_private *ca = NULL;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (slot_count < 1)
+		return -EINVAL;
+
+	/* initialise the system data */
+	if ((ca =
+	     (struct dvb_ca_private *) kmalloc(sizeof(struct dvb_ca_private),
+					       GFP_KERNEL)) == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+	memset(ca, 0, sizeof(struct dvb_ca_private));
+	ca->pub = pubca;
+	ca->flags = flags;
+	ca->slot_count = slot_count;
+	if ((ca->slot_info = kmalloc(sizeof(struct dvb_ca_slot) * slot_count, GFP_KERNEL)) == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+	memset(ca->slot_info, 0, sizeof(struct dvb_ca_slot) * slot_count);
+	init_waitqueue_head(&ca->wait_queue);
+	ca->thread_pid = 0;
+	init_waitqueue_head(&ca->thread_queue);
+	ca->exit = 0;
+	ca->open = 0;
+	ca->wakeup = 0;
+	ca->next_read_slot = 0;
+	pubca->private = ca;
+
+	/* register the DVB device */
+	ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
+	if (ret)
+		goto error;
+
+	/* now initialise each slot */
+	for (i = 0; i < slot_count; i++) {
+		memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot));
+		ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
+		atomic_set(&ca->slot_info[i].camchange_count, 0);
+		ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+		init_rwsem(&ca->slot_info[i].sem);
+	}
+
+	if (signal_pending(current)) {
+		ret = -EINTR;
+		goto error;
+	}
+	mb();
+
+	/* create a kthread for monitoring this CA device */
+
+	ret = kernel_thread(dvb_ca_en50221_thread, ca, 0);
+
+	if (ret < 0) {
+		printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ret);
+		goto error;
+	}
+	ca->thread_pid = ret;
+	return 0;
+
+      error:
+	if (ca != NULL) {
+		if (ca->dvbdev != NULL)
+			dvb_unregister_device(ca->dvbdev);
+		kfree(ca->slot_info);
+		kfree(ca);
+	}
+	pubca->private = NULL;
+	return ret;
+}
+EXPORT_SYMBOL(dvb_ca_en50221_release);
+
+
+
+/**
+ * Release a DVB CA EN50221 interface device.
+ *
+ * @param ca_dev The dvb_device_t instance for the CA device.
+ * @param ca The associated dvb_ca instance.
+ */
+void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
+{
+	struct dvb_ca_private *ca = (struct dvb_ca_private *) pubca->private;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* shutdown the thread if there was one */
+	if (ca->thread_pid) {
+		if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) {
+			printk("dvb_ca_release adapter %d: thread PID %d already died\n",
+			       ca->dvbdev->adapter->num, ca->thread_pid);
+		} else {
+			ca->exit = 1;
+			mb();
+			dvb_ca_en50221_thread_wakeup(ca);
+			wait_event_interruptible(ca->thread_queue, ca->thread_pid == 0);
+		}
+	}
+
+	for (i = 0; i < ca->slot_count; i++) {
+		dvb_ca_en50221_slot_shutdown(ca, i);
+	}
+	kfree(ca->slot_info);
+	dvb_unregister_device(ca->dvbdev);
+	kfree(ca);
+	pubca->private = NULL;
+}
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
new file mode 100644
index 0000000..8467e63
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
@@ -0,0 +1,134 @@
+/*
+ * dvb_ca.h: generic DVB functions for EN50221 CA interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _DVB_CA_EN50221_H_
+#define _DVB_CA_EN50221_H_
+
+#include <linux/list.h>
+#include <linux/dvb/ca.h>
+
+#include "dvbdev.h"
+
+#define DVB_CA_EN50221_POLL_CAM_PRESENT	1
+#define DVB_CA_EN50221_POLL_CAM_CHANGED	2
+#define DVB_CA_EN50221_POLL_CAM_READY		4
+
+#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE	1
+#define DVB_CA_EN50221_FLAG_IRQ_FR		2
+#define DVB_CA_EN50221_FLAG_IRQ_DA		4
+
+#define DVB_CA_EN50221_CAMCHANGE_REMOVED		0
+#define DVB_CA_EN50221_CAMCHANGE_INSERTED		1
+
+
+
+/* Structure describing a CA interface */
+struct dvb_ca_en50221 {
+
+	/* the module owning this structure */
+	struct module* owner;
+
+	/* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
+	 * they may be called from several threads at once */
+
+	/* functions for accessing attribute memory on the CAM */
+	int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
+	int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);
+
+	/* functions for accessing the control interface on the CAM */
+	int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address);
+	int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value);
+
+	/* Functions for controlling slots */
+	int (*slot_reset)(struct dvb_ca_en50221* ca, int slot);
+	int (*slot_shutdown)(struct dvb_ca_en50221* ca, int slot);
+	int (*slot_ts_enable)(struct dvb_ca_en50221* ca, int slot);
+
+	/*
+	* Poll slot status.
+	* Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set
+	*/
+	int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot, int open);
+
+	/* private data, used by caller */
+	void* data;
+
+	/* Opaque data used by the dvb_ca core. Do not modify! */
+	void* private;
+};
+
+
+
+
+/* ******************************************************************************** */
+/* Functions for reporting IRQ events */
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type);
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot);
+
+/**
+ * An FR or a DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot);
+
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+/**
+ * Initialise a new DVB CA device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count);
+
+/**
+ * Release a DVB CA device.
+ *
+ * @param ca The associated dvb_ca instance.
+ */
+extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca);
+
+
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
new file mode 100644
index 0000000..ac9889d
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -0,0 +1,1294 @@
+/*
+ * dvb_demux.c - DVB kernel demux API
+ *
+ * Copyright (C) 2000-2001 Ralph  Metzler <ralph@convergence.de>
+ *		       & Marcus Metzler <marcus@convergence.de>
+ *			 for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/crc32.h>
+#include <asm/uaccess.h>
+
+#include "dvb_demux.h"
+
+#define NOBUFS
+/*
+** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog
+*/
+// #define DVB_DEMUX_SECTION_LOSS_LOG
+
+
+static LIST_HEAD(dmx_muxs);
+
+
+static int dmx_register_demux(struct dmx_demux *demux)
+{
+	demux->users = 0;
+	list_add(&demux->reg_list, &dmx_muxs);
+	return 0;
+}
+
+static int dmx_unregister_demux(struct dmx_demux* demux)
+{
+	struct list_head *pos, *n, *head=&dmx_muxs;
+
+	list_for_each_safe (pos, n, head) {
+		if (DMX_DIR_ENTRY(pos) == demux) {
+			if (demux->users>0)
+				return -EINVAL;
+			list_del(pos);
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+
+/******************************************************************************
+ * static inlined helper functions
+ ******************************************************************************/
+
+
+static inline u16 section_length(const u8 *buf)
+{
+	return 3+((buf[1]&0x0f)<<8)+buf[2];
+}
+
+
+static inline u16 ts_pid(const u8 *buf)
+{
+	return ((buf[1]&0x1f)<<8)+buf[2];
+}
+
+
+static inline u8 payload(const u8 *tsp)
+{
+	if (!(tsp[3] & 0x10)) // no payload?
+		return 0;
+	if (tsp[3] & 0x20) {  // adaptation field?
+		if (tsp[4] > 183)    // corrupted data?
+			return 0;
+		else
+			return 184-1-tsp[4];
+	}
+	return 184;
+}
+
+
+static u32 dvb_dmx_crc32 (struct dvb_demux_feed *f, const u8 *src, size_t len)
+{
+	return (f->feed.sec.crc_val = crc32_be (f->feed.sec.crc_val, src, len));
+}
+
+
+static void dvb_dmx_memcopy (struct dvb_demux_feed *f, u8 *d, const u8 *s, size_t len)
+{
+	memcpy (d, s, len);
+}
+
+
+/******************************************************************************
+ * Software filter functions
+ ******************************************************************************/
+
+static inline int dvb_dmx_swfilter_payload (struct dvb_demux_feed *feed, const u8 *buf)
+{
+	int count = payload(buf);
+	int p;
+	//int ccok;
+	//u8 cc;
+
+	if (count == 0)
+		return -1;
+
+	p = 188-count;
+
+	/*
+	cc=buf[3]&0x0f;
+	ccok=((dvbdmxfeed->cc+1)&0x0f)==cc ? 1 : 0;
+	dvbdmxfeed->cc=cc;
+	if (!ccok)
+		printk("missed packet!\n");
+	*/
+
+	if (buf[1] & 0x40)  // PUSI ?
+		feed->peslen = 0xfffa;
+
+	feed->peslen += count;
+
+	return feed->cb.ts (&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
+}
+
+
+static int dvb_dmx_swfilter_sectionfilter (struct dvb_demux_feed *feed,
+				    struct dvb_demux_filter *f)
+{
+	u8 neq = 0;
+	int i;
+
+	for (i=0; i<DVB_DEMUX_MASK_MAX; i++) {
+		u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i];
+
+		if (f->maskandmode[i] & xor)
+			return 0;
+
+		neq |= f->maskandnotmode[i] & xor;
+	}
+
+	if (f->doneq && !neq)
+		return 0;
+
+	return feed->cb.sec (feed->feed.sec.secbuf, feed->feed.sec.seclen,
+			     NULL, 0, &f->filter, DMX_OK);
+}
+
+
+static inline int dvb_dmx_swfilter_section_feed (struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct dvb_demux_filter *f = feed->filter;
+	struct dmx_section_feed *sec = &feed->feed.sec;
+	int section_syntax_indicator;
+
+	if (!sec->is_filtering)
+		return 0;
+
+	if (!f)
+		return 0;
+
+	if (sec->check_crc) {
+		section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
+		if (section_syntax_indicator &&
+		    demux->check_crc32(feed, sec->secbuf, sec->seclen))
+			return -1;
+	}
+
+	do {
+		if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0)
+			return -1;
+	} while ((f = f->next) && sec->is_filtering);
+
+	sec->seclen = 0;
+
+	return 0;
+}
+
+
+static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
+{
+	struct dmx_section_feed *sec = &feed->feed.sec;
+
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+	if(sec->secbufp < sec->tsfeedp)
+	{
+		int i, n = sec->tsfeedp - sec->secbufp;
+
+		/* section padding is done with 0xff bytes entirely.
+		** due to speed reasons, we won't check all of them
+		** but just first and last
+		*/
+		if(sec->secbuf[0] != 0xff || sec->secbuf[n-1] != 0xff)
+		{
+			printk("dvb_demux.c section ts padding loss: %d/%d\n",
+			       n, sec->tsfeedp);
+			printk("dvb_demux.c pad data:");
+			for(i = 0; i < n; i++)
+				printk(" %02x", sec->secbuf[i]);
+			printk("\n");
+		}
+	}
+#endif
+
+	sec->tsfeedp = sec->secbufp = sec->seclen = 0;
+	sec->secbuf = sec->secbuf_base;
+}
+
+/*
+** Losless Section Demux 1.4.1 by Emard
+** Valsecchi Patrick:
+**  - middle of section A  (no PUSI)
+**  - end of section A and start of section B
+**    (with PUSI pointing to the start of the second section)
+**
+**  In this case, without feed->pusi_seen you'll receive a garbage section
+**  consisting of the end of section A. Basically because tsfeedp
+**  is incemented and the use=0 condition is not raised
+**  when the second packet arrives.
+**
+** Fix:
+** when demux is started, let feed->pusi_seen = 0 to
+** prevent initial feeding of garbage from the end of
+** previous section. When you for the first time see PUSI=1
+** then set feed->pusi_seen = 1
+*/
+static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, const u8 *buf, u8 len)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct dmx_section_feed *sec = &feed->feed.sec;
+	u16 limit, seclen, n;
+
+	if(sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
+		return 0;
+
+	if(sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE)
+	{
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		printk("dvb_demux.c section buffer full loss: %d/%d\n",
+		       sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, DMX_MAX_SECFEED_SIZE);
+#endif
+		len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
+	}
+
+	if(len <= 0)
+		return 0;
+
+	demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len);
+	sec->tsfeedp += len;
+
+	/* -----------------------------------------------------
+	** Dump all the sections we can find in the data (Emard)
+	*/
+
+	limit = sec->tsfeedp;
+	if(limit > DMX_MAX_SECFEED_SIZE)
+		return -1; /* internal error should never happen */
+
+	/* to be sure always set secbuf */
+	sec->secbuf = sec->secbuf_base + sec->secbufp;
+
+	for(n = 0; sec->secbufp + 2 < limit; n++)
+	{
+		seclen = section_length(sec->secbuf);
+		if(seclen <= 0 || seclen > DMX_MAX_SECFEED_SIZE
+		   || seclen + sec->secbufp > limit)
+			return 0;
+		sec->seclen = seclen;
+		sec->crc_val = ~0;
+		/* dump [secbuf .. secbuf+seclen) */
+		if(feed->pusi_seen)
+			dvb_dmx_swfilter_section_feed(feed);
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		else
+			printk("dvb_demux.c pusi not seen, discarding section data\n");
+#endif
+		sec->secbufp += seclen; /* secbufp and secbuf moving together is */
+		sec->secbuf += seclen; /* redundand but saves pointer arithmetic */
+	}
+
+	return 0;
+}
+
+
+static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, const u8 *buf)
+{
+	u8 p, count;
+	int ccok, dc_i = 0;
+	u8 cc;
+
+	count = payload(buf);
+
+	if (count == 0)  /* count == 0 if no payload or out of range */
+		return -1;
+
+	p = 188 - count; /* payload start */
+
+	cc = buf[3] & 0x0f;
+	ccok = ((feed->cc + 1) & 0x0f) == cc;
+	feed->cc = cc;
+
+	if (buf[3] & 0x20) {
+		/* adaption field present, check for discontinuity_indicator */
+		if ((buf[4] > 0) && (buf[5] & 0x80))
+			dc_i = 1;
+	}
+
+	if (!ccok || dc_i) {
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		printk("dvb_demux.c discontinuity detected %d bytes lost\n", count);
+		/* those bytes under sume circumstances will again be reported
+		** in the following dvb_dmx_swfilter_section_new
+		*/
+#endif
+		/* Discontinuity detected. Reset pusi_seen = 0 to
+		** stop feeding of suspicious data until next PUSI=1 arrives
+		*/
+		feed->pusi_seen = 0;
+		dvb_dmx_swfilter_section_new(feed);
+		return 0;
+	}
+
+	if (buf[1] & 0x40) {
+		// PUSI=1 (is set), section boundary is here
+		if (count > 1 && buf[p] < count) {
+			const u8 *before = buf+p+1;
+			u8 before_len = buf[p];
+			const u8 *after = before+before_len;
+			u8 after_len = count-1-before_len;
+
+			dvb_dmx_swfilter_section_copy_dump(feed, before, before_len);
+			/* before start of new section, set pusi_seen = 1 */
+			feed->pusi_seen = 1;
+			dvb_dmx_swfilter_section_new(feed);
+			dvb_dmx_swfilter_section_copy_dump(feed, after, after_len);
+		}
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+		else
+			if (count > 0)
+				printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count);
+#endif
+	} else {
+		// PUSI=0 (is not set), no section boundary
+		const u8 *entire = buf+p;
+		u8 entire_len = count;
+
+		dvb_dmx_swfilter_section_copy_dump(feed, entire, entire_len);
+	}
+	return 0;
+}
+
+
+static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, const u8 *buf)
+{
+	switch(feed->type) {
+	case DMX_TYPE_TS:
+		if (!feed->feed.ts.is_filtering)
+			break;
+		if (feed->ts_type & TS_PACKET) {
+			if (feed->ts_type & TS_PAYLOAD_ONLY)
+				dvb_dmx_swfilter_payload(feed, buf);
+			else
+				feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+		}
+		if (feed->ts_type & TS_DECODER)
+			if (feed->demux->write_to_decoder)
+				feed->demux->write_to_decoder(feed, buf, 188);
+		break;
+
+	case DMX_TYPE_SEC:
+		if (!feed->feed.sec.is_filtering)
+			break;
+		if (dvb_dmx_swfilter_section_packet(feed, buf) < 0)
+			feed->feed.sec.seclen = feed->feed.sec.secbufp=0;
+		break;
+
+	default:
+		break;
+	}
+}
+
+#define DVR_FEED(f)							\
+	(((f)->type == DMX_TYPE_TS) &&					\
+	((f)->feed.ts.is_filtering) &&					\
+	(((f)->ts_type & (TS_PACKET|TS_PAYLOAD_ONLY)) == TS_PACKET))
+
+static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
+{
+	struct dvb_demux_feed *feed;
+	struct list_head *pos, *head=&demux->feed_list;
+	u16 pid = ts_pid(buf);
+	int dvr_done = 0;
+
+	list_for_each(pos, head) {
+		feed = list_entry(pos, struct dvb_demux_feed, list_head);
+
+		if ((feed->pid != pid) && (feed->pid != 0x2000))
+			continue;
+
+		/* copy each packet only once to the dvr device, even
+		 * if a PID is in multiple filters (e.g. video + PCR) */
+		if ((DVR_FEED(feed)) && (dvr_done++))
+			continue;
+
+		if (feed->pid == pid) {
+			dvb_dmx_swfilter_packet_type(feed, buf);
+			if (DVR_FEED(feed))
+				continue;
+		}
+
+		if (feed->pid == 0x2000)
+			feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+	}
+}
+
+void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+	spin_lock(&demux->lock);
+
+	while (count--) {
+		if(buf[0] == 0x47) {
+		        dvb_dmx_swfilter_packet(demux, buf);
+		}
+		buf += 188;
+	}
+
+	spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
+
+
+void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+	int p = 0, i, j;
+
+	spin_lock(&demux->lock);
+
+	if ((i = demux->tsbufp)) {
+		if (count < (j=188-i)) {
+			memcpy(&demux->tsbuf[i], buf, count);
+			demux->tsbufp += count;
+			goto bailout;
+		}
+		memcpy(&demux->tsbuf[i], buf, j);
+		if (demux->tsbuf[0] == 0x47)
+			dvb_dmx_swfilter_packet(demux, demux->tsbuf);
+		demux->tsbufp = 0;
+		p += j;
+	}
+
+	while (p < count) {
+		if (buf[p] == 0x47) {
+			if (count-p >= 188) {
+				dvb_dmx_swfilter_packet(demux, buf+p);
+				p += 188;
+			} else {
+				i = count-p;
+				memcpy(demux->tsbuf, buf+p, i);
+				demux->tsbufp=i;
+				goto bailout;
+			}
+		} else
+			p++;
+	}
+
+bailout:
+	spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter);
+
+void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+	int p = 0,i, j;
+	u8 tmppack[188];
+	spin_lock(&demux->lock);
+
+	if ((i = demux->tsbufp)) {
+		if (count < (j=204-i)) {
+			memcpy(&demux->tsbuf[i], buf, count);
+			demux->tsbufp += count;
+			goto bailout;
+		}
+		memcpy(&demux->tsbuf[i], buf, j);
+		if ((demux->tsbuf[0] == 0x47)|(demux->tsbuf[0]==0xB8))  {
+			memcpy(tmppack, demux->tsbuf, 188);
+			if (tmppack[0] == 0xB8) tmppack[0] = 0x47;
+			dvb_dmx_swfilter_packet(demux, tmppack);
+		}
+		demux->tsbufp = 0;
+		p += j;
+	}
+
+	while (p < count) {
+		if ((buf[p] == 0x47)|(buf[p] == 0xB8)) {
+			if (count-p >= 204) {
+				memcpy(tmppack, buf+p, 188);
+				if (tmppack[0] == 0xB8) tmppack[0] = 0x47;
+				dvb_dmx_swfilter_packet(demux, tmppack);
+				p += 204;
+			} else {
+				i = count-p;
+				memcpy(demux->tsbuf, buf+p, i);
+				demux->tsbufp=i;
+				goto bailout;
+			}
+		} else {
+			p++;
+		}
+	}
+
+bailout:
+	spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_204);
+
+
+static struct dvb_demux_filter * dvb_dmx_filter_alloc(struct dvb_demux *demux)
+{
+	int i;
+
+	for (i=0; i<demux->filternum; i++)
+		if (demux->filter[i].state == DMX_STATE_FREE)
+			break;
+
+	if (i == demux->filternum)
+		return NULL;
+
+	demux->filter[i].state = DMX_STATE_ALLOCATED;
+
+	return &demux->filter[i];
+}
+
+static struct dvb_demux_feed * dvb_dmx_feed_alloc(struct dvb_demux *demux)
+{
+	int i;
+
+	for (i=0; i<demux->feednum; i++)
+		if (demux->feed[i].state == DMX_STATE_FREE)
+			break;
+
+	if (i == demux->feednum)
+		return NULL;
+
+	demux->feed[i].state = DMX_STATE_ALLOCATED;
+
+	return &demux->feed[i];
+}
+
+static int dvb_demux_feed_find(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux_feed *entry;
+
+	list_for_each_entry(entry, &feed->demux->feed_list, list_head)
+		if (entry == feed)
+			return 1;
+
+	return 0;
+}
+
+static void dvb_demux_feed_add(struct dvb_demux_feed *feed)
+{
+	spin_lock_irq(&feed->demux->lock);
+	if (dvb_demux_feed_find(feed)) {
+		printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",
+				__FUNCTION__, feed->type, feed->state, feed->pid);
+		goto out;
+	}
+
+	list_add(&feed->list_head, &feed->demux->feed_list);
+out:
+	spin_unlock_irq(&feed->demux->lock);
+}
+
+static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
+{
+	spin_lock_irq(&feed->demux->lock);
+	if (!(dvb_demux_feed_find(feed))) {
+		printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",
+				__FUNCTION__, feed->type, feed->state, feed->pid);
+		goto out;
+	}
+
+	list_del(&feed->list_head);
+out:
+	spin_unlock_irq(&feed->demux->lock);
+}
+
+static int dmx_ts_feed_set (struct dmx_ts_feed* ts_feed, u16 pid, int ts_type,
+		     enum dmx_ts_pes pes_type, size_t callback_length,
+		     size_t circular_buffer_size, int descramble,
+		     struct timespec timeout)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+	struct dvb_demux *demux = feed->demux;
+
+	if (pid > DMX_MAX_PID)
+		return -EINVAL;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (ts_type & TS_DECODER) {
+		if (pes_type >= DMX_TS_PES_OTHER) {
+			up(&demux->mutex);
+			return -EINVAL;
+		}
+
+		if (demux->pesfilter[pes_type] &&
+		    demux->pesfilter[pes_type] != feed) {
+			up(&demux->mutex);
+			return -EINVAL;
+		}
+
+		demux->pesfilter[pes_type] = feed;
+		demux->pids[pes_type] = pid;
+	}
+
+	dvb_demux_feed_add(feed);
+
+	feed->pid = pid;
+	feed->buffer_size = circular_buffer_size;
+	feed->descramble = descramble;
+	feed->timeout = timeout;
+	feed->cb_length = callback_length;
+	feed->ts_type = ts_type;
+	feed->pes_type = pes_type;
+
+	if (feed->descramble) {
+		up(&demux->mutex);
+		return -ENOSYS;
+	}
+
+	if (feed->buffer_size) {
+#ifdef NOBUFS
+		feed->buffer=NULL;
+#else
+		feed->buffer = vmalloc(feed->buffer_size);
+		if (!feed->buffer) {
+			up(&demux->mutex);
+			return -ENOMEM;
+		}
+#endif
+	}
+
+	feed->state = DMX_STATE_READY;
+	up(&demux->mutex);
+
+	return 0;
+}
+
+
+static int dmx_ts_feed_start_filtering(struct dmx_ts_feed* ts_feed)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+	struct dvb_demux *demux = feed->demux;
+	int ret;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
+		up(&demux->mutex);
+		return -EINVAL;
+	}
+
+	if (!demux->start_feed) {
+		up(&demux->mutex);
+		return -ENODEV;
+	}
+
+	if ((ret = demux->start_feed(feed)) < 0) {
+		up(&demux->mutex);
+		return ret;
+	}
+
+	spin_lock_irq(&demux->lock);
+	ts_feed->is_filtering = 1;
+	feed->state = DMX_STATE_GO;
+	spin_unlock_irq(&demux->lock);
+	up(&demux->mutex);
+
+	return 0;
+}
+
+static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed* ts_feed)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+	struct dvb_demux *demux = feed->demux;
+	int ret;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->state < DMX_STATE_GO) {
+		up(&demux->mutex);
+		return -EINVAL;
+	}
+
+	if (!demux->stop_feed) {
+		up(&demux->mutex);
+		return -ENODEV;
+	}
+
+	ret = demux->stop_feed(feed);
+
+	spin_lock_irq(&demux->lock);
+	ts_feed->is_filtering = 0;
+	feed->state = DMX_STATE_ALLOCATED;
+	spin_unlock_irq(&demux->lock);
+	up(&demux->mutex);
+
+	return ret;
+}
+
+static int dvbdmx_allocate_ts_feed (struct dmx_demux *dmx, struct dmx_ts_feed **ts_feed,
+			     dmx_ts_cb callback)
+{
+	struct dvb_demux *demux = (struct dvb_demux *) dmx;
+	struct dvb_demux_feed *feed;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (!(feed = dvb_dmx_feed_alloc(demux))) {
+		up(&demux->mutex);
+		return -EBUSY;
+	}
+
+	feed->type = DMX_TYPE_TS;
+	feed->cb.ts = callback;
+	feed->demux = demux;
+	feed->pid = 0xffff;
+	feed->peslen = 0xfffa;
+	feed->buffer = NULL;
+
+	(*ts_feed) = &feed->feed.ts;
+	(*ts_feed)->parent = dmx;
+	(*ts_feed)->priv = NULL;
+	(*ts_feed)->is_filtering = 0;
+	(*ts_feed)->start_filtering = dmx_ts_feed_start_filtering;
+	(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
+	(*ts_feed)->set = dmx_ts_feed_set;
+
+
+	if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
+		feed->state = DMX_STATE_FREE;
+		up(&demux->mutex);
+		return -EBUSY;
+	}
+
+	feed->filter->type = DMX_TYPE_TS;
+	feed->filter->feed = feed;
+	feed->filter->state = DMX_STATE_READY;
+
+	up(&demux->mutex);
+
+	return 0;
+}
+
+static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, struct dmx_ts_feed *ts_feed)
+{
+	struct dvb_demux *demux = (struct dvb_demux *) dmx;
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *) ts_feed;
+
+	if (down_interruptible (&demux->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->state == DMX_STATE_FREE) {
+		up(&demux->mutex);
+		return -EINVAL;
+	}
+
+#ifndef NOBUFS
+	vfree(feed->buffer);
+	feed->buffer=0;
+#endif
+
+	feed->state = DMX_STATE_FREE;
+	feed->filter->state = DMX_STATE_FREE;
+
+	dvb_demux_feed_del(feed);
+
+	feed->pid = 0xffff;
+
+	if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER)
+		demux->pesfilter[feed->pes_type] = NULL;
+
+	up(&demux->mutex);
+	return 0;
+}
+
+
+/******************************************************************************
+ * dmx_section_feed API calls
+ ******************************************************************************/
+
+static int dmx_section_feed_allocate_filter(struct dmx_section_feed* feed,
+				     struct dmx_section_filter** filter)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
+	struct dvb_demux_filter *dvbdmxfilter;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+
+	dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
+	if (!dvbdmxfilter) {
+		up(&dvbdemux->mutex);
+		return -EBUSY;
+	}
+
+	spin_lock_irq(&dvbdemux->lock);
+	*filter = &dvbdmxfilter->filter;
+	(*filter)->parent = feed;
+	(*filter)->priv = NULL;
+	dvbdmxfilter->feed = dvbdmxfeed;
+	dvbdmxfilter->type = DMX_TYPE_SEC;
+	dvbdmxfilter->state = DMX_STATE_READY;
+	dvbdmxfilter->next = dvbdmxfeed->filter;
+	dvbdmxfeed->filter = dvbdmxfilter;
+	spin_unlock_irq(&dvbdemux->lock);
+
+	up(&dvbdemux->mutex);
+	return 0;
+}
+
+
+static int dmx_section_feed_set(struct dmx_section_feed* feed,
+			 u16 pid, size_t circular_buffer_size,
+			 int descramble, int check_crc)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	if (pid > 0x1fff)
+		return -EINVAL;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	dvb_demux_feed_add(dvbdmxfeed);
+
+	dvbdmxfeed->pid = pid;
+	dvbdmxfeed->buffer_size = circular_buffer_size;
+	dvbdmxfeed->descramble = descramble;
+	if (dvbdmxfeed->descramble) {
+		up(&dvbdmx->mutex);
+		return -ENOSYS;
+	}
+
+	dvbdmxfeed->feed.sec.check_crc = check_crc;
+
+#ifdef NOBUFS
+	dvbdmxfeed->buffer = NULL;
+#else
+	dvbdmxfeed->buffer=vmalloc(dvbdmxfeed->buffer_size);
+	if (!dvbdmxfeed->buffer) {
+		up(&dvbdmx->mutex);
+		return -ENOMEM;
+	}
+#endif
+
+	dvbdmxfeed->state = DMX_STATE_READY;
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+
+static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed)
+{
+	int i;
+	struct dvb_demux_filter *f;
+	struct dmx_section_filter *sf;
+	u8 mask, mode, doneq;
+
+	if (!(f=dvbdmxfeed->filter))
+		return;
+	do {
+		sf = &f->filter;
+		doneq = 0;
+		for (i=0; i<DVB_DEMUX_MASK_MAX; i++) {
+			mode = sf->filter_mode[i];
+			mask = sf->filter_mask[i];
+			f->maskandmode[i] = mask & mode;
+			doneq |= f->maskandnotmode[i] = mask & ~mode;
+		}
+		f->doneq = doneq ? 1 : 0;
+	} while ((f = f->next));
+}
+
+
+static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	int ret;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (feed->is_filtering) {
+		up(&dvbdmx->mutex);
+		return -EBUSY;
+	}
+
+	if (!dvbdmxfeed->filter) {
+		up(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+
+	dvbdmxfeed->feed.sec.tsfeedp = 0;
+	dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
+	dvbdmxfeed->feed.sec.secbufp = 0;
+	dvbdmxfeed->feed.sec.seclen = 0;
+
+	if (!dvbdmx->start_feed) {
+		up(&dvbdmx->mutex);
+		return -ENODEV;
+	}
+
+	prepare_secfilters(dvbdmxfeed);
+
+	if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
+		up(&dvbdmx->mutex);
+		return ret;
+	}
+
+	spin_lock_irq(&dvbdmx->lock);
+	feed->is_filtering = 1;
+	dvbdmxfeed->state = DMX_STATE_GO;
+	spin_unlock_irq(&dvbdmx->lock);
+
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+
+static int dmx_section_feed_stop_filtering(struct dmx_section_feed* feed)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	int ret;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (!dvbdmx->stop_feed) {
+		up(&dvbdmx->mutex);
+		return -ENODEV;
+	}
+
+	ret = dvbdmx->stop_feed(dvbdmxfeed);
+
+	spin_lock_irq(&dvbdmx->lock);
+	dvbdmxfeed->state = DMX_STATE_READY;
+	feed->is_filtering = 0;
+	spin_unlock_irq(&dvbdmx->lock);
+
+	up(&dvbdmx->mutex);
+	return ret;
+}
+
+
+static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
+				struct dmx_section_filter* filter)
+{
+	struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *) filter, *f;
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (dvbdmxfilter->feed != dvbdmxfeed) {
+		up(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+
+	if (feed->is_filtering)
+		feed->stop_filtering(feed);
+
+	spin_lock_irq(&dvbdmx->lock);
+	f = dvbdmxfeed->filter;
+
+	if (f == dvbdmxfilter) {
+		dvbdmxfeed->filter = dvbdmxfilter->next;
+	} else {
+		while(f->next != dvbdmxfilter)
+			f = f->next;
+		f->next = f->next->next;
+	}
+
+	dvbdmxfilter->state = DMX_STATE_FREE;
+	spin_unlock_irq(&dvbdmx->lock);
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
+					struct dmx_section_feed **feed,
+					dmx_section_cb callback)
+{
+	struct dvb_demux *dvbdmx = (struct dvb_demux *) demux;
+	struct dvb_demux_feed *dvbdmxfeed;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
+		up(&dvbdmx->mutex);
+		return -EBUSY;
+	}
+
+	dvbdmxfeed->type = DMX_TYPE_SEC;
+	dvbdmxfeed->cb.sec = callback;
+	dvbdmxfeed->demux = dvbdmx;
+	dvbdmxfeed->pid = 0xffff;
+	dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
+	dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
+	dvbdmxfeed->feed.sec.tsfeedp = 0;
+	dvbdmxfeed->filter = NULL;
+	dvbdmxfeed->buffer = NULL;
+
+	(*feed)=&dvbdmxfeed->feed.sec;
+	(*feed)->is_filtering = 0;
+	(*feed)->parent = demux;
+	(*feed)->priv = NULL;
+
+	(*feed)->set = dmx_section_feed_set;
+	(*feed)->allocate_filter = dmx_section_feed_allocate_filter;
+	(*feed)->start_filtering = dmx_section_feed_start_filtering;
+	(*feed)->stop_filtering = dmx_section_feed_stop_filtering;
+	(*feed)->release_filter = dmx_section_feed_release_filter;
+
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+static int dvbdmx_release_section_feed(struct dmx_demux *demux,
+				       struct dmx_section_feed *feed)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) feed;
+	struct dvb_demux *dvbdmx = (struct dvb_demux *) demux;
+
+	if (down_interruptible (&dvbdmx->mutex))
+		return -ERESTARTSYS;
+
+	if (dvbdmxfeed->state==DMX_STATE_FREE) {
+		up(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+#ifndef NOBUFS
+	vfree(dvbdmxfeed->buffer);
+	dvbdmxfeed->buffer=0;
+#endif
+	dvbdmxfeed->state=DMX_STATE_FREE;
+
+	dvb_demux_feed_del(dvbdmxfeed);
+
+	dvbdmxfeed->pid = 0xffff;
+
+	up(&dvbdmx->mutex);
+	return 0;
+}
+
+
+/******************************************************************************
+ * dvb_demux kernel data API calls
+ ******************************************************************************/
+
+static int dvbdmx_open(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (dvbdemux->users >= MAX_DVB_DEMUX_USERS)
+		return -EUSERS;
+
+	dvbdemux->users++;
+	return 0;
+}
+
+
+static int dvbdmx_close(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (dvbdemux->users == 0)
+		return -ENODEV;
+
+	dvbdemux->users--;
+	//FIXME: release any unneeded resources if users==0
+	return 0;
+}
+
+
+static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count)
+{
+	struct dvb_demux *dvbdemux=(struct dvb_demux *) demux;
+
+	if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
+		return -EINVAL;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+	dvb_dmx_swfilter(dvbdemux, buf, count);
+	up(&dvbdemux->mutex);
+
+	if (signal_pending(current))
+		return -EINTR;
+	return count;
+}
+
+
+static int dvbdmx_add_frontend(struct dmx_demux *demux, struct dmx_frontend *frontend)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+	struct list_head *head = &dvbdemux->frontend_list;
+
+	list_add(&(frontend->connectivity_list), head);
+
+	return 0;
+}
+
+
+static int dvbdmx_remove_frontend(struct dmx_demux *demux, struct dmx_frontend *frontend)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+	struct list_head *pos, *n, *head = &dvbdemux->frontend_list;
+
+	list_for_each_safe (pos, n, head) {
+		if (DMX_FE_ENTRY(pos) == frontend) {
+			list_del(pos);
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+
+static struct list_head * dvbdmx_get_frontends(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (list_empty(&dvbdemux->frontend_list))
+		return NULL;
+	return &dvbdemux->frontend_list;
+}
+
+
+static int dvbdmx_connect_frontend(struct dmx_demux *demux, struct dmx_frontend *frontend)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (demux->frontend)
+		return -EINVAL;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+
+	demux->frontend = frontend;
+	up(&dvbdemux->mutex);
+	return 0;
+}
+
+
+static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	if (down_interruptible (&dvbdemux->mutex))
+		return -ERESTARTSYS;
+
+	demux->frontend = NULL;
+	up(&dvbdemux->mutex);
+	return 0;
+}
+
+
+static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 *pids)
+{
+	struct dvb_demux *dvbdemux = (struct dvb_demux *) demux;
+
+	memcpy(pids, dvbdemux->pids, 5*sizeof(u16));
+	return 0;
+}
+
+
+int dvb_dmx_init(struct dvb_demux *dvbdemux)
+{
+	int i, err;
+	struct dmx_demux *dmx = &dvbdemux->dmx;
+
+	dvbdemux->users = 0;
+	dvbdemux->filter = vmalloc(dvbdemux->filternum*sizeof(struct dvb_demux_filter));
+
+	if (!dvbdemux->filter)
+		return -ENOMEM;
+
+	dvbdemux->feed = vmalloc(dvbdemux->feednum*sizeof(struct dvb_demux_feed));
+	if (!dvbdemux->feed) {
+		vfree(dvbdemux->filter);
+		return -ENOMEM;
+	}
+	for (i=0; i<dvbdemux->filternum; i++) {
+		dvbdemux->filter[i].state = DMX_STATE_FREE;
+		dvbdemux->filter[i].index = i;
+	}
+	for (i=0; i<dvbdemux->feednum; i++) {
+		dvbdemux->feed[i].state = DMX_STATE_FREE;
+		dvbdemux->feed[i].index = i;
+	}
+	dvbdemux->frontend_list.next=
+	  dvbdemux->frontend_list.prev=
+	    &dvbdemux->frontend_list;
+	for (i=0; i<DMX_TS_PES_OTHER; i++) {
+		dvbdemux->pesfilter[i] = NULL;
+		dvbdemux->pids[i] = 0xffff;
+	}
+
+	INIT_LIST_HEAD(&dvbdemux->feed_list);
+
+	dvbdemux->playing = 0;
+	dvbdemux->recording = 0;
+	dvbdemux->tsbufp = 0;
+
+	if (!dvbdemux->check_crc32)
+		dvbdemux->check_crc32 = dvb_dmx_crc32;
+
+	 if (!dvbdemux->memcopy)
+		 dvbdemux->memcopy = dvb_dmx_memcopy;
+
+	dmx->frontend = NULL;
+	dmx->reg_list.prev = dmx->reg_list.next = &dmx->reg_list;
+	dmx->priv = (void *) dvbdemux;
+	dmx->open = dvbdmx_open;
+	dmx->close = dvbdmx_close;
+	dmx->write = dvbdmx_write;
+	dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
+	dmx->release_ts_feed = dvbdmx_release_ts_feed;
+	dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
+	dmx->release_section_feed = dvbdmx_release_section_feed;
+
+	dmx->descramble_mac_address = NULL;
+	dmx->descramble_section_payload = NULL;
+
+	dmx->add_frontend = dvbdmx_add_frontend;
+	dmx->remove_frontend = dvbdmx_remove_frontend;
+	dmx->get_frontends = dvbdmx_get_frontends;
+	dmx->connect_frontend = dvbdmx_connect_frontend;
+	dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
+	dmx->get_pes_pids = dvbdmx_get_pes_pids;
+
+	sema_init(&dvbdemux->mutex, 1);
+	spin_lock_init(&dvbdemux->lock);
+
+	if ((err = dmx_register_demux(dmx)) < 0)
+		return err;
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_dmx_init);
+
+
+int dvb_dmx_release(struct dvb_demux *dvbdemux)
+{
+	struct dmx_demux *dmx = &dvbdemux->dmx;
+
+	dmx_unregister_demux(dmx);
+	vfree(dvbdemux->filter);
+	vfree(dvbdemux->feed);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_dmx_release);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
new file mode 100644
index 0000000..c09beb39
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -0,0 +1,146 @@
+/*
+ * dvb_demux.h: DVB kernel demux API
+ *
+ * Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler
+ *                         for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+
+#ifndef _DVB_DEMUX_H_
+#define _DVB_DEMUX_H_
+
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+
+#include "demux.h"
+
+#define DMX_TYPE_TS  0
+#define DMX_TYPE_SEC 1
+#define DMX_TYPE_PES 2
+
+#define DMX_STATE_FREE      0
+#define DMX_STATE_ALLOCATED 1
+#define DMX_STATE_SET       2
+#define DMX_STATE_READY     3
+#define DMX_STATE_GO        4
+
+#define DVB_DEMUX_MASK_MAX 18
+
+struct dvb_demux_filter {
+        struct dmx_section_filter filter;
+        u8 maskandmode    [DMX_MAX_FILTER_SIZE];
+        u8 maskandnotmode [DMX_MAX_FILTER_SIZE];
+	int doneq;
+
+        struct dvb_demux_filter *next;
+        struct dvb_demux_feed *feed;
+        int index;
+        int state;
+        int type;
+	int pesto;
+
+        u16 handle;
+        u16 hw_handle;
+        struct timer_list timer;
+	int ts_state;
+};
+
+
+#define DMX_FEED_ENTRY(pos) list_entry(pos, struct dvb_demux_feed, list_head)
+
+struct dvb_demux_feed {
+        union {
+	        struct dmx_ts_feed ts;
+	        struct dmx_section_feed sec;
+	} feed;
+
+        union {
+	        dmx_ts_cb ts;
+	        dmx_section_cb sec;
+	} cb;
+
+        struct dvb_demux *demux;
+	void *priv;
+        int type;
+        int state;
+        u16 pid;
+        u8 *buffer;
+        int buffer_size;
+        int descramble;
+
+        struct timespec timeout;
+        struct dvb_demux_filter *filter;
+        int cb_length;
+
+        int ts_type;
+        enum dmx_ts_pes pes_type;
+
+        int cc;
+        int pusi_seen; /* prevents feeding of garbage from previous section */
+
+        u16 peslen;
+
+	struct list_head list_head;
+		int index; /* a unique index for each feed (can be used as hardware pid filter index) */
+};
+
+struct dvb_demux {
+        struct dmx_demux dmx;
+        void *priv;
+        int filternum;
+        int feednum;
+        int (*start_feed) (struct dvb_demux_feed *feed);
+        int (*stop_feed) (struct dvb_demux_feed *feed);
+        int (*write_to_decoder) (struct dvb_demux_feed *feed,
+				 const u8 *buf, size_t len);
+	u32 (*check_crc32) (struct dvb_demux_feed *feed,
+			    const u8 *buf, size_t len);
+	void (*memcopy) (struct dvb_demux_feed *feed, u8 *dst,
+			 const u8 *src, size_t len);
+
+        int users;
+#define MAX_DVB_DEMUX_USERS 10
+        struct dvb_demux_filter *filter;
+        struct dvb_demux_feed *feed;
+
+        struct list_head frontend_list;
+
+        struct dvb_demux_feed *pesfilter[DMX_TS_PES_OTHER];
+        u16 pids[DMX_TS_PES_OTHER];
+        int playing;
+        int recording;
+
+#define DMX_MAX_PID 0x2000
+	struct list_head feed_list;
+        u8 tsbuf[204];
+        int tsbufp;
+
+	struct semaphore mutex;
+	spinlock_t lock;
+};
+
+
+int dvb_dmx_init(struct dvb_demux *dvbdemux);
+int dvb_dmx_release(struct dvb_demux *dvbdemux);
+void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf, size_t count);
+void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
+void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count);
+
+#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_filter.c b/drivers/media/dvb/dvb-core/dvb_filter.c
new file mode 100644
index 0000000..bd51439
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_filter.c
@@ -0,0 +1,603 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "dvb_filter.h"
+
+#if 0
+static unsigned int bitrates[3][16] =
+{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
+ {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0},
+ {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}};
+#endif
+
+static u32 freq[4] = {480, 441, 320, 0};
+
+static unsigned int ac3_bitrates[32] =
+    {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640,
+     0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+static u32 ac3_frames[3][32] =
+    {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024,
+      1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0},
+     {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114,
+      1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0},
+     {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344,
+      1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+
+
+#if 0
+static void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv,
+		  void (*pes_write)(u8 *buf, int count, void *data),
+		  void *priv)
+{
+	dvb_filter_ipack_init(pa, IPACKS, pes_write);
+	dvb_filter_ipack_init(pv, IPACKS, pes_write);
+	pa->pid = pida;
+	pv->pid = pidv;
+	pa->data = priv;
+	pv->data = priv;
+}
+#endif
+
+#if 0
+static void ts_to_pes(ipack *p, u8 *buf) // don't need count (=188)
+{
+	u8 off = 0;
+
+	if (!buf || !p ){
+		printk("NULL POINTER IDIOT\n");
+		return;
+	}
+	if (buf[1]&PAY_START) {
+		if (p->plength == MMAX_PLENGTH-6 && p->found>6){
+			p->plength = p->found-6;
+			p->found = 0;
+			send_ipack(p);
+			dvb_filter_ipack_reset(p);
+		}
+	}
+	if (buf[3] & ADAPT_FIELD) {  // adaptation field?
+		off = buf[4] + 1;
+		if (off+4 > 187) return;
+	}
+	dvb_filter_instant_repack(buf+4+off, TS_SIZE-4-off, p);
+}
+#endif
+
+#if 0
+/* needs 5 byte input, returns picture coding type*/
+static int read_picture_header(u8 *headr, struct mpg_picture *pic, int field, int pr)
+{
+	u8 pct;
+
+	if (pr) printk( "Pic header: ");
+        pic->temporal_reference[field] = (( headr[0] << 2 ) |
+					  (headr[1] & 0x03) )& 0x03ff;
+	if (pr) printk( " temp ref: 0x%04x", pic->temporal_reference[field]);
+
+	pct = ( headr[1] >> 2 ) & 0x07;
+        pic->picture_coding_type[field] = pct;
+	if (pr) {
+		switch(pct){
+			case I_FRAME:
+				printk( "  I-FRAME");
+				break;
+			case B_FRAME:
+				printk( "  B-FRAME");
+				break;
+			case P_FRAME:
+				printk( "  P-FRAME");
+				break;
+		}
+	}
+
+
+        pic->vinfo.vbv_delay  = (( headr[1] >> 5 ) | ( headr[2] << 3) |
+				 ( (headr[3] & 0x1F) << 11) ) & 0xffff;
+
+	if (pr) printk( " vbv delay: 0x%04x", pic->vinfo.vbv_delay);
+
+        pic->picture_header_parameter = ( headr[3] & 0xe0 ) |
+		((headr[4] & 0x80) >> 3);
+
+        if ( pct == B_FRAME ){
+                pic->picture_header_parameter |= ( headr[4] >> 3 ) & 0x0f;
+        }
+	if (pr) printk( " pic head param: 0x%x",
+			pic->picture_header_parameter);
+
+	return pct;
+}
+#endif
+
+#if 0
+/* needs 4 byte input */
+static int read_gop_header(u8 *headr, struct mpg_picture *pic, int pr)
+{
+	if (pr) printk("GOP header: ");
+
+	pic->time_code  = (( headr[0] << 17 ) | ( headr[1] << 9) |
+			   ( headr[2] << 1 ) | (headr[3] &0x01)) & 0x1ffffff;
+
+	if (pr) printk(" time: %d:%d.%d ", (headr[0]>>2)& 0x1F,
+		       ((headr[0]<<4)& 0x30)| ((headr[1]>>4)& 0x0F),
+		       ((headr[1]<<3)& 0x38)| ((headr[2]>>5)& 0x0F));
+
+        if ( ( headr[3] & 0x40 ) != 0 ){
+                pic->closed_gop = 1;
+        } else {
+                pic->closed_gop = 0;
+        }
+	if (pr) printk("closed: %d", pic->closed_gop);
+
+        if ( ( headr[3] & 0x20 ) != 0 ){
+                pic->broken_link = 1;
+        } else {
+                pic->broken_link = 0;
+        }
+	if (pr) printk(" broken: %d\n", pic->broken_link);
+
+	return 0;
+}
+#endif
+
+#if 0
+/* needs 8 byte input */
+static int read_sequence_header(u8 *headr, struct dvb_video_info *vi, int pr)
+{
+        int sw;
+	int form = -1;
+
+	if (pr) printk("Reading sequence header\n");
+
+	vi->horizontal_size	= ((headr[1] &0xF0) >> 4) | (headr[0] << 4);
+	vi->vertical_size	= ((headr[1] &0x0F) << 8) | (headr[2]);
+
+        sw = (int)((headr[3]&0xF0) >> 4) ;
+
+        switch( sw ){
+	case 1:
+		if (pr)
+			printk("Videostream: ASPECT: 1:1");
+		vi->aspect_ratio = 100;
+		break;
+	case 2:
+		if (pr)
+			printk("Videostream: ASPECT: 4:3");
+                vi->aspect_ratio = 133;
+		break;
+	case 3:
+		if (pr)
+			printk("Videostream: ASPECT: 16:9");
+                vi->aspect_ratio = 177;
+		break;
+	case 4:
+		if (pr)
+			printk("Videostream: ASPECT: 2.21:1");
+                vi->aspect_ratio = 221;
+		break;
+
+        case 5 ... 15:
+		if (pr)
+			printk("Videostream: ASPECT: reserved");
+                vi->aspect_ratio = 0;
+		break;
+
+        default:
+                vi->aspect_ratio = 0;
+                return -1;
+	}
+
+	if (pr)
+		printk("  Size = %dx%d",vi->horizontal_size,vi->vertical_size);
+
+        sw = (int)(headr[3]&0x0F);
+
+        switch ( sw ) {
+	case 1:
+		if (pr)
+			printk("  FRate: 23.976 fps");
+                vi->framerate = 23976;
+		form = -1;
+		break;
+	case 2:
+		if (pr)
+			printk("  FRate: 24 fps");
+                vi->framerate = 24000;
+		form = -1;
+		break;
+	case 3:
+		if (pr)
+			printk("  FRate: 25 fps");
+                vi->framerate = 25000;
+		form = VIDEO_MODE_PAL;
+		break;
+	case 4:
+		if (pr)
+			printk("  FRate: 29.97 fps");
+                vi->framerate = 29970;
+		form = VIDEO_MODE_NTSC;
+		break;
+	case 5:
+		if (pr)
+			printk("  FRate: 30 fps");
+                vi->framerate = 30000;
+		form = VIDEO_MODE_NTSC;
+		break;
+	case 6:
+		if (pr)
+			printk("  FRate: 50 fps");
+                vi->framerate = 50000;
+		form = VIDEO_MODE_PAL;
+		break;
+	case 7:
+		if (pr)
+			printk("  FRate: 60 fps");
+                vi->framerate = 60000;
+		form = VIDEO_MODE_NTSC;
+		break;
+	}
+
+	vi->bit_rate = (headr[4] << 10) | (headr[5] << 2) | (headr[6] & 0x03);
+
+        vi->vbv_buffer_size
+                = (( headr[6] & 0xF8) >> 3 ) | (( headr[7] & 0x1F )<< 5);
+
+	if (pr){
+		printk("  BRate: %d Mbit/s",4*(vi->bit_rate)/10000);
+		printk("  vbvbuffer %d",16*1024*(vi->vbv_buffer_size));
+		printk("\n");
+	}
+
+        vi->video_format = form;
+
+	return 0;
+}
+#endif
+
+
+#if 0
+static int get_vinfo(u8 *mbuf, int count, struct dvb_video_info *vi, int pr)
+{
+	u8 *headr;
+	int found = 0;
+	int c = 0;
+
+	while (found < 4 && c+4 < count){
+		u8 *b;
+
+		b = mbuf+c;
+		if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01
+		     && b[3] == 0xb3) found = 4;
+		else {
+			c++;
+		}
+	}
+
+	if (! found) return -1;
+	c += 4;
+	if (c+12 >= count) return -1;
+	headr = mbuf+c;
+	if (read_sequence_header(headr, vi, pr) < 0) return -1;
+	vi->off = c-4;
+	return 0;
+}
+#endif
+
+
+#if 0
+static int get_ainfo(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr)
+{
+	u8 *headr;
+	int found = 0;
+	int c = 0;
+	int fr = 0;
+
+	while (found < 2 && c < count){
+		u8 b[2];
+		memcpy( b, mbuf+c, 2);
+
+		if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8)
+			found = 2;
+		else {
+			c++;
+		}
+	}
+
+	if (!found) return -1;
+
+	if (c+3 >= count) return -1;
+        headr = mbuf+c;
+
+	ai->layer = (headr[1] & 0x06) >> 1;
+
+	if (pr)
+		printk("Audiostream: Layer: %d", 4-ai->layer);
+
+
+	ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000;
+
+	if (pr){
+		if (ai->bit_rate == 0)
+			printk("  Bit rate: free");
+		else if (ai->bit_rate == 0xf)
+			printk("  BRate: reserved");
+		else
+			printk("  BRate: %d kb/s", ai->bit_rate/1000);
+	}
+
+	fr = (headr[2] & 0x0c ) >> 2;
+	ai->frequency = freq[fr]*100;
+	if (pr){
+		if (ai->frequency == 3)
+			printk("  Freq: reserved\n");
+		else
+			printk("  Freq: %d kHz\n",ai->frequency);
+
+	}
+	ai->off = c;
+	return 0;
+}
+#endif
+
+
+int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr)
+{
+	u8 *headr;
+	int found = 0;
+	int c = 0;
+	u8 frame = 0;
+	int fr = 0;
+
+	while ( !found  && c < count){
+		u8 *b = mbuf+c;
+
+		if ( b[0] == 0x0b &&  b[1] == 0x77 )
+			found = 1;
+		else {
+			c++;
+		}
+	}
+
+	if (!found) return -1;
+	if (pr)
+		printk("Audiostream: AC3");
+
+	ai->off = c;
+	if (c+5 >= count) return -1;
+
+	ai->layer = 0;  // 0 for AC3
+        headr = mbuf+c+2;
+
+	frame = (headr[2]&0x3f);
+	ai->bit_rate = ac3_bitrates[frame >> 1]*1000;
+
+	if (pr)
+		printk("  BRate: %d kb/s", (int) ai->bit_rate/1000);
+
+	ai->frequency = (headr[2] & 0xc0 ) >> 6;
+	fr = (headr[2] & 0xc0 ) >> 6;
+	ai->frequency = freq[fr]*100;
+	if (pr) printk ("  Freq: %d Hz\n", (int) ai->frequency);
+
+
+	ai->framesize = ac3_frames[fr][frame >> 1];
+	if ((frame & 1) &&  (fr == 1)) ai->framesize++;
+	ai->framesize = ai->framesize << 1;
+	if (pr) printk ("  Framesize %d\n",(int) ai->framesize);
+
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_filter_get_ac3info);
+
+
+#if 0
+static u8 *skip_pes_header(u8 **bufp)
+{
+        u8 *inbuf = *bufp;
+        u8 *buf = inbuf;
+        u8 *pts = NULL;
+        int skip = 0;
+
+	static const int mpeg1_skip_table[16] = {
+		1, 0xffff,      5,     10, 0xffff, 0xffff, 0xffff, 0xffff,
+	        0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+	};
+
+
+        if ((inbuf[6] & 0xc0) == 0x80){ /* mpeg2 */
+                if (buf[7] & PTS_ONLY)
+                        pts = buf+9;
+                else pts = NULL;
+                buf = inbuf + 9 + inbuf[8];
+        } else {        /* mpeg1 */
+                for (buf = inbuf + 6; *buf == 0xff; buf++)
+                        if (buf == inbuf + 6 + 16) {
+                                break;
+                        }
+                if ((*buf & 0xc0) == 0x40)
+                        buf += 2;
+                skip = mpeg1_skip_table [*buf >> 4];
+                if (skip == 5 || skip == 10) pts = buf;
+                else pts = NULL;
+
+                buf += mpeg1_skip_table [*buf >> 4];
+        }
+
+        *bufp = buf;
+        return pts;
+}
+#endif
+
+#if 0
+static void initialize_quant_matrix( u32 *matrix )
+{
+        int i;
+
+        matrix[0]  = 0x08101013;
+        matrix[1]  = 0x10131616;
+        matrix[2]  = 0x16161616;
+        matrix[3]  = 0x1a181a1b;
+        matrix[4]  = 0x1b1b1a1a;
+        matrix[5]  = 0x1a1a1b1b;
+        matrix[6]  = 0x1b1d1d1d;
+        matrix[7]  = 0x2222221d;
+        matrix[8]  = 0x1d1d1b1b;
+        matrix[9]  = 0x1d1d2020;
+        matrix[10] = 0x22222526;
+        matrix[11] = 0x25232322;
+        matrix[12] = 0x23262628;
+        matrix[13] = 0x28283030;
+        matrix[14] = 0x2e2e3838;
+        matrix[15] = 0x3a454553;
+
+        for ( i = 16 ; i < 32 ; i++ )
+                matrix[i] = 0x10101010;
+}
+#endif
+
+#if 0
+static void initialize_mpg_picture(struct mpg_picture *pic)
+{
+        int i;
+
+        /* set MPEG1 */
+        pic->mpeg1_flag = 1;
+        pic->profile_and_level = 0x4A ;        /* MP@LL */
+        pic->progressive_sequence = 1;
+        pic->low_delay = 0;
+
+        pic->sequence_display_extension_flag = 0;
+        for ( i = 0 ; i < 4 ; i++ ){
+                pic->frame_centre_horizontal_offset[i] = 0;
+                pic->frame_centre_vertical_offset[i] = 0;
+        }
+        pic->last_frame_centre_horizontal_offset = 0;
+        pic->last_frame_centre_vertical_offset = 0;
+
+        pic->picture_display_extension_flag[0] = 0;
+        pic->picture_display_extension_flag[1] = 0;
+        pic->sequence_header_flag = 0;
+	pic->gop_flag = 0;
+        pic->sequence_end_flag = 0;
+}
+#endif
+
+#if 0
+static void mpg_set_picture_parameter( int32_t field_type, struct mpg_picture *pic )
+{
+        int16_t last_h_offset;
+        int16_t last_v_offset;
+
+        int16_t *p_h_offset;
+        int16_t *p_v_offset;
+
+        if ( pic->mpeg1_flag ){
+                pic->picture_structure[field_type] = VIDEO_FRAME_PICTURE;
+                pic->top_field_first = 0;
+                pic->repeat_first_field = 0;
+                pic->progressive_frame = 1;
+                pic->picture_coding_parameter = 0x000010;
+        }
+
+        /* Reset flag */
+        pic->picture_display_extension_flag[field_type] = 0;
+
+        last_h_offset = pic->last_frame_centre_horizontal_offset;
+        last_v_offset = pic->last_frame_centre_vertical_offset;
+        if ( field_type == FIRST_FIELD ){
+                p_h_offset = pic->frame_centre_horizontal_offset;
+                p_v_offset = pic->frame_centre_vertical_offset;
+                *p_h_offset = last_h_offset;
+                *(p_h_offset + 1) = last_h_offset;
+                *(p_h_offset + 2) = last_h_offset;
+                *p_v_offset = last_v_offset;
+                *(p_v_offset + 1) = last_v_offset;
+                *(p_v_offset + 2) = last_v_offset;
+        } else {
+                pic->frame_centre_horizontal_offset[3] = last_h_offset;
+                pic->frame_centre_vertical_offset[3] = last_v_offset;
+        }
+}
+#endif
+
+#if 0
+static void init_mpg_picture( struct mpg_picture *pic, int chan, int32_t field_type)
+{
+        pic->picture_header = 0;
+        pic->sequence_header_data
+                = ( INIT_HORIZONTAL_SIZE << 20 )
+                        | ( INIT_VERTICAL_SIZE << 8 )
+                        | ( INIT_ASPECT_RATIO << 4 )
+                        | ( INIT_FRAME_RATE );
+        pic->mpeg1_flag = 0;
+        pic->vinfo.horizontal_size
+                = INIT_DISP_HORIZONTAL_SIZE;
+        pic->vinfo.vertical_size
+                = INIT_DISP_VERTICAL_SIZE;
+        pic->picture_display_extension_flag[field_type]
+                = 0;
+        pic->pts_flag[field_type] = 0;
+
+        pic->sequence_gop_header = 0;
+        pic->picture_header = 0;
+        pic->sequence_header_flag = 0;
+        pic->gop_flag = 0;
+        pic->sequence_end_flag = 0;
+        pic->sequence_display_extension_flag = 0;
+        pic->last_frame_centre_horizontal_offset = 0;
+        pic->last_frame_centre_vertical_offset = 0;
+	pic->channel = chan;
+}
+#endif
+
+void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid,
+			    dvb_filter_pes2ts_cb_t *cb, void *priv)
+{
+	unsigned char *buf=p2ts->buf;
+
+	buf[0]=0x47;
+	buf[1]=(pid>>8);
+	buf[2]=pid&0xff;
+	p2ts->cc=0;
+	p2ts->cb=cb;
+	p2ts->priv=priv;
+}
+EXPORT_SYMBOL(dvb_filter_pes2ts_init);
+
+int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes,
+		      int len, int payload_start)
+{
+	unsigned char *buf=p2ts->buf;
+	int ret=0, rest;
+
+	//len=6+((pes[4]<<8)|pes[5]);
+
+	if (payload_start)
+		buf[1]|=0x40;
+	else
+		buf[1]&=~0x40;
+	while (len>=184) {
+		buf[3]=0x10|((p2ts->cc++)&0x0f);
+		memcpy(buf+4, pes, 184);
+		if ((ret=p2ts->cb(p2ts->priv, buf)))
+			return ret;
+		len-=184; pes+=184;
+		buf[1]&=~0x40;
+	}
+	if (!len)
+	        return 0;
+	buf[3]=0x30|((p2ts->cc++)&0x0f);
+	rest=183-len;
+	if (rest) {
+	        buf[5]=0x00;
+		if (rest-1)
+			memset(buf+6, 0xff, rest-1);
+	}
+	buf[4]=rest;
+	memcpy(buf+5+rest, pes, len);
+	return p2ts->cb(p2ts->priv, buf);
+}
+EXPORT_SYMBOL(dvb_filter_pes2ts);
diff --git a/drivers/media/dvb/dvb-core/dvb_filter.h b/drivers/media/dvb/dvb-core/dvb_filter.h
new file mode 100644
index 0000000..b0848f7
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_filter.h
@@ -0,0 +1,246 @@
+/*
+ * dvb_filter.h
+ *
+ * Copyright (C) 2003 Convergence GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef _DVB_FILTER_H_
+#define _DVB_FILTER_H_
+
+#include <linux/slab.h>
+
+#include "demux.h"
+
+typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *);
+
+struct dvb_filter_pes2ts {
+	unsigned char buf[188];
+        unsigned char cc;
+        dvb_filter_pes2ts_cb_t *cb;
+	void *priv;
+};
+
+void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid,
+			    dvb_filter_pes2ts_cb_t *cb, void *priv);
+
+int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes,
+		      int len, int payload_start);
+
+
+#define PROG_STREAM_MAP  0xBC
+#define PRIVATE_STREAM1  0xBD
+#define PADDING_STREAM   0xBE
+#define PRIVATE_STREAM2  0xBF
+#define AUDIO_STREAM_S   0xC0
+#define AUDIO_STREAM_E   0xDF
+#define VIDEO_STREAM_S   0xE0
+#define VIDEO_STREAM_E   0xEF
+#define ECM_STREAM       0xF0
+#define EMM_STREAM       0xF1
+#define DSM_CC_STREAM    0xF2
+#define ISO13522_STREAM  0xF3
+#define PROG_STREAM_DIR  0xFF
+
+#define DVB_PICTURE_START    0x00
+#define DVB_USER_START       0xb2
+#define DVB_SEQUENCE_HEADER  0xb3
+#define DVB_SEQUENCE_ERROR   0xb4
+#define DVB_EXTENSION_START  0xb5
+#define DVB_SEQUENCE_END     0xb7
+#define DVB_GOP_START        0xb8
+#define DVB_EXCEPT_SLICE     0xb0
+
+#define SEQUENCE_EXTENSION           0x01
+#define SEQUENCE_DISPLAY_EXTENSION   0x02
+#define PICTURE_CODING_EXTENSION     0x08
+#define QUANT_MATRIX_EXTENSION       0x03
+#define PICTURE_DISPLAY_EXTENSION    0x07
+
+#define I_FRAME 0x01
+#define B_FRAME 0x02
+#define P_FRAME 0x03
+
+/* Initialize sequence_data */
+#define INIT_HORIZONTAL_SIZE        720
+#define INIT_VERTICAL_SIZE          576
+#define INIT_ASPECT_RATIO          0x02
+#define INIT_FRAME_RATE            0x03
+#define INIT_DISP_HORIZONTAL_SIZE   540
+#define INIT_DISP_VERTICAL_SIZE     576
+
+
+//flags2
+#define PTS_DTS_FLAGS    0xC0
+#define ESCR_FLAG        0x20
+#define ES_RATE_FLAG     0x10
+#define DSM_TRICK_FLAG   0x08
+#define ADD_CPY_FLAG     0x04
+#define PES_CRC_FLAG     0x02
+#define PES_EXT_FLAG     0x01
+
+//pts_dts flags
+#define PTS_ONLY         0x80
+#define PTS_DTS          0xC0
+
+#define TS_SIZE        188
+#define TRANS_ERROR    0x80
+#define PAY_START      0x40
+#define TRANS_PRIO     0x20
+#define PID_MASK_HI    0x1F
+//flags
+#define TRANS_SCRMBL1  0x80
+#define TRANS_SCRMBL2  0x40
+#define ADAPT_FIELD    0x20
+#define PAYLOAD        0x10
+#define COUNT_MASK     0x0F
+
+// adaptation flags
+#define DISCON_IND     0x80
+#define RAND_ACC_IND   0x40
+#define ES_PRI_IND     0x20
+#define PCR_FLAG       0x10
+#define OPCR_FLAG      0x08
+#define SPLICE_FLAG    0x04
+#define TRANS_PRIV     0x02
+#define ADAP_EXT_FLAG  0x01
+
+// adaptation extension flags
+#define LTW_FLAG       0x80
+#define PIECE_RATE     0x40
+#define SEAM_SPLICE    0x20
+
+
+#define MAX_PLENGTH 0xFFFF
+#define MMAX_PLENGTH (256*MAX_PLENGTH)
+
+#ifndef IPACKS
+#define IPACKS 2048
+#endif
+
+struct ipack {
+	int size;
+	int found;
+	u8 *buf;
+	u8 cid;
+	u32 plength;
+	u8 plen[2];
+	u8 flag1;
+	u8 flag2;
+	u8 hlength;
+	u8 pts[5];
+	u16 *pid;
+	int mpeg;
+	u8 check;
+	int which;
+	int done;
+	void *data;
+	void (*func)(u8 *buf,  int size, void *priv);
+	int count;
+	int repack_subids;
+};
+
+struct dvb_video_info {
+	u32 horizontal_size;
+	u32 vertical_size;
+	u32 aspect_ratio;
+	u32 framerate;
+	u32 video_format;
+	u32 bit_rate;
+	u32 comp_bit_rate;
+	u32 vbv_buffer_size;
+        s16 vbv_delay;
+	u32 CSPF;
+	u32 off;
+};
+
+#define OFF_SIZE 4
+#define FIRST_FIELD 0
+#define SECOND_FIELD 1
+#define VIDEO_FRAME_PICTURE 0x03
+
+struct mpg_picture {
+        int       channel;
+	struct dvb_video_info vinfo;
+        u32      *sequence_gop_header;
+        u32      *picture_header;
+        s32       time_code;
+        int       low_delay;
+        int       closed_gop;
+        int       broken_link;
+        int       sequence_header_flag;
+        int       gop_flag;
+        int       sequence_end_flag;
+
+        u8        profile_and_level;
+        s32       picture_coding_parameter;
+        u32       matrix[32];
+        s8        matrix_change_flag;
+
+        u8        picture_header_parameter;
+  /* bit 0 - 2: bwd f code
+     bit 3    : fpb vector
+     bit 4 - 6: fwd f code
+     bit 7    : fpf vector */
+
+        int       mpeg1_flag;
+        int       progressive_sequence;
+        int       sequence_display_extension_flag;
+        u32       sequence_header_data;
+        s16       last_frame_centre_horizontal_offset;
+        s16       last_frame_centre_vertical_offset;
+
+        u32       pts[2]; /* [0] 1st field, [1] 2nd field */
+        int       top_field_first;
+        int       repeat_first_field;
+        int       progressive_frame;
+        int       bank;
+        int       forward_bank;
+        int       backward_bank;
+        int       compress;
+        s16       frame_centre_horizontal_offset[OFF_SIZE];
+                  /* [0-2] 1st field, [3] 2nd field */
+        s16       frame_centre_vertical_offset[OFF_SIZE];
+                  /* [0-2] 1st field, [3] 2nd field */
+        s16       temporal_reference[2];
+                  /* [0] 1st field, [1] 2nd field */
+
+        s8        picture_coding_type[2];
+                  /* [0] 1st field, [1] 2nd field */
+        s8        picture_structure[2];
+                  /* [0] 1st field, [1] 2nd field */
+        s8        picture_display_extension_flag[2];
+                  /* [0] 1st field, [1] 2nd field */
+                  /* picture_display_extenion() 0:no 1:exit*/
+        s8        pts_flag[2];
+                  /* [0] 1st field, [1] 2nd field */
+};
+
+struct dvb_audio_info {
+	int layer;
+	u32 bit_rate;
+	u32 frequency;
+	u32 mode;
+	u32 mode_extension ;
+	u32 emphasis;
+	u32 framesize;
+	u32 off;
+};
+
+int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr);
+
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
new file mode 100644
index 0000000..59a9adf
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -0,0 +1,915 @@
+/*
+ * dvb_frontend.c: DVB frontend tuning interface/thread
+ *
+ *
+ * Copyright (C) 1999-2001 Ralph  Metzler
+ *			   Marcus Metzler
+ *			   Holger Waechtler
+ *				      for convergence integrated media GmbH
+ *
+ * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup)
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/list.h>
+#include <linux/suspend.h>
+#include <asm/processor.h>
+#include <asm/semaphore.h>
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+
+static int dvb_frontend_debug;
+static int dvb_shutdown_timeout = 5;
+static int dvb_force_auto_inversion;
+static int dvb_override_tune_delay;
+static int dvb_powerdown_on_sleep = 1;
+
+module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
+MODULE_PARM_DESC(dvb_frontend_debug, "Turn on/off frontend core debugging (default:off).");
+module_param(dvb_shutdown_timeout, int, 0444);
+MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
+module_param(dvb_force_auto_inversion, int, 0444);
+MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
+module_param(dvb_override_tune_delay, int, 0444);
+MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
+module_param(dvb_powerdown_on_sleep, int, 0444);
+MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB volatage off on sleep (default)");
+
+#define dprintk if (dvb_frontend_debug) printk
+
+#define FESTATE_IDLE 1
+#define FESTATE_RETUNE 2
+#define FESTATE_TUNING_FAST 4
+#define FESTATE_TUNING_SLOW 8
+#define FESTATE_TUNED 16
+#define FESTATE_ZIGZAG_FAST 32
+#define FESTATE_ZIGZAG_SLOW 64
+#define FESTATE_DISEQC 128
+#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
+#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
+#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
+#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW)
+/*
+ * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling.
+ * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune.
+ * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress.
+ * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower.
+ * FESTATE_TUNED. The frontend has successfully locked on.
+ * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it.
+ * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower.
+ * FESTATE_DISEQC. A DISEQC command has just been issued.
+ * FESTATE_WAITFORLOCK. When we're waiting for a lock.
+ * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan.
+ * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan.
+ * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
+ */
+
+static DECLARE_MUTEX(frontend_mutex);
+
+struct dvb_frontend_private {
+
+	struct dvb_device *dvbdev;
+	struct dvb_frontend_parameters parameters;
+	struct dvb_fe_events events;
+	struct semaphore sem;
+	struct list_head list_head;
+	wait_queue_head_t wait_queue;
+	pid_t thread_pid;
+	unsigned long release_jiffies;
+	int state;
+	int bending;
+	int lnb_drift;
+	int inversion;
+	int auto_step;
+	int auto_sub_step;
+	int started_auto_step;
+	int min_delay;
+	int max_drift;
+	int step_size;
+	int exit;
+	int wakeup;
+	fe_status_t status;
+};
+
+
+static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	struct dvb_fe_events *events = &fepriv->events;
+	struct dvb_frontend_event *e;
+	int wp;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (down_interruptible (&events->sem))
+		return;
+
+	wp = (events->eventw + 1) % MAX_EVENT;
+
+	if (wp == events->eventr) {
+		events->overflow = 1;
+		events->eventr = (events->eventr + 1) % MAX_EVENT;
+	}
+
+	e = &events->events[events->eventw];
+
+	memcpy (&e->parameters, &fepriv->parameters,
+		sizeof (struct dvb_frontend_parameters));
+
+	if (status & FE_HAS_LOCK)
+		if (fe->ops->get_frontend)
+			fe->ops->get_frontend(fe, &e->parameters);
+
+	events->eventw = wp;
+
+	up (&events->sem);
+
+	e->status = status;
+
+	wake_up_interruptible (&events->wait_queue);
+}
+
+static int dvb_frontend_get_event(struct dvb_frontend *fe,
+			    struct dvb_frontend_event *event, int flags)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	struct dvb_fe_events *events = &fepriv->events;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (events->overflow) {
+		events->overflow = 0;
+		return -EOVERFLOW;
+	}
+
+	if (events->eventw == events->eventr) {
+		int ret;
+
+		if (flags & O_NONBLOCK)
+			return -EWOULDBLOCK;
+
+		up(&fepriv->sem);
+
+		ret = wait_event_interruptible (events->wait_queue,
+						events->eventw != events->eventr);
+
+		if (down_interruptible (&fepriv->sem))
+			return -ERESTARTSYS;
+
+		if (ret < 0)
+			return ret;
+	}
+
+	if (down_interruptible (&events->sem))
+		return -ERESTARTSYS;
+
+	memcpy (event, &events->events[events->eventr],
+		sizeof(struct dvb_frontend_event));
+
+	events->eventr = (events->eventr + 1) % MAX_EVENT;
+
+	up (&events->sem);
+
+	return 0;
+}
+
+static void dvb_frontend_init(struct dvb_frontend *fe)
+{
+	dprintk ("DVB: initialising frontend %i (%s)...\n",
+		 fe->dvb->num,
+		 fe->ops->info.name);
+
+	if (fe->ops->init)
+		fe->ops->init(fe);
+}
+
+static void update_delay(int *quality, int *delay, int min_delay, int locked)
+{
+	    int q2;
+
+	    dprintk ("%s\n", __FUNCTION__);
+
+	    if (locked)
+		      (*quality) = (*quality * 220 + 36*256) / 256;
+	    else
+		      (*quality) = (*quality * 220 + 0) / 256;
+
+	    q2 = *quality - 128;
+	    q2 *= q2;
+
+	    *delay = min_delay + q2 * HZ / (128*128);
+}
+
+/**
+ * Performs automatic twiddling of frontend parameters.
+ *
+ * @param fe The frontend concerned.
+ * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT
+ * @returns Number of complete iterations that have been performed.
+ */
+static int dvb_frontend_autotune(struct dvb_frontend *fe, int check_wrapped)
+{
+	int autoinversion;
+	int ready = 0;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	int original_inversion = fepriv->parameters.inversion;
+	u32 original_frequency = fepriv->parameters.frequency;
+
+	/* are we using autoinversion? */
+	autoinversion = ((!(fe->ops->info.caps & FE_CAN_INVERSION_AUTO)) &&
+			 (fepriv->parameters.inversion == INVERSION_AUTO));
+
+	/* setup parameters correctly */
+	while(!ready) {
+		/* calculate the lnb_drift */
+		fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size;
+
+		/* wrap the auto_step if we've exceeded the maximum drift */
+		if (fepriv->lnb_drift > fepriv->max_drift) {
+			fepriv->auto_step = 0;
+			fepriv->auto_sub_step = 0;
+			fepriv->lnb_drift = 0;
+		}
+
+		/* perform inversion and +/- zigzag */
+		switch(fepriv->auto_sub_step) {
+		case 0:
+			/* try with the current inversion and current drift setting */
+			ready = 1;
+			break;
+
+		case 1:
+			if (!autoinversion) break;
+
+			fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+			ready = 1;
+			break;
+
+		case 2:
+			if (fepriv->lnb_drift == 0) break;
+
+			fepriv->lnb_drift = -fepriv->lnb_drift;
+			ready = 1;
+			break;
+
+		case 3:
+			if (fepriv->lnb_drift == 0) break;
+			if (!autoinversion) break;
+
+			fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+			fepriv->lnb_drift = -fepriv->lnb_drift;
+			ready = 1;
+			break;
+
+		default:
+			fepriv->auto_step++;
+			fepriv->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */
+			break;
+		}
+
+		if (!ready) fepriv->auto_sub_step++;
+	}
+
+	/* if this attempt would hit where we started, indicate a complete
+	 * iteration has occurred */
+	if ((fepriv->auto_step == fepriv->started_auto_step) &&
+	    (fepriv->auto_sub_step == 0) && check_wrapped) {
+		return 1;
+	}
+
+	dprintk("%s: drift:%i inversion:%i auto_step:%i "
+		"auto_sub_step:%i started_auto_step:%i\n",
+		__FUNCTION__, fepriv->lnb_drift, fepriv->inversion,
+		fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step);
+
+	/* set the frontend itself */
+	fepriv->parameters.frequency += fepriv->lnb_drift;
+	if (autoinversion)
+		fepriv->parameters.inversion = fepriv->inversion;
+	if (fe->ops->set_frontend)
+		fe->ops->set_frontend(fe, &fepriv->parameters);
+
+	fepriv->parameters.frequency = original_frequency;
+	fepriv->parameters.inversion = original_inversion;
+
+	fepriv->auto_sub_step++;
+	return 0;
+}
+
+static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	if (fepriv->exit)
+		return 1;
+
+	if (fepriv->dvbdev->writers == 1)
+		if (jiffies - fepriv->release_jiffies > dvb_shutdown_timeout * HZ)
+			return 1;
+
+	return 0;
+}
+
+static int dvb_frontend_should_wakeup(struct dvb_frontend *fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	if (fepriv->wakeup) {
+		fepriv->wakeup = 0;
+		return 1;
+	}
+	return dvb_frontend_is_exiting(fe);
+}
+
+static void dvb_frontend_wakeup(struct dvb_frontend *fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	fepriv->wakeup = 1;
+	wake_up_interruptible(&fepriv->wait_queue);
+}
+
+/*
+ * FIXME: use linux/kthread.h
+ */
+static int dvb_frontend_thread(void *data)
+{
+	struct dvb_frontend *fe = (struct dvb_frontend *) data;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	unsigned long timeout;
+	char name [15];
+	int quality = 0, delay = 3*HZ;
+	fe_status_t s;
+	int check_wrapped = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	snprintf (name, sizeof(name), "kdvb-fe-%i", fe->dvb->num);
+
+        lock_kernel();
+        daemonize(name);
+        sigfillset(&current->blocked);
+        unlock_kernel();
+
+	fepriv->status = 0;
+	dvb_frontend_init(fe);
+	fepriv->wakeup = 0;
+
+	while (1) {
+		up(&fepriv->sem);	    /* is locked when we enter the thread... */
+
+		timeout = wait_event_interruptible_timeout(fepriv->wait_queue,
+							   dvb_frontend_should_wakeup(fe),
+							   delay);
+		if (0 != dvb_frontend_is_exiting(fe)) {
+			/* got signal or quitting */
+			break;
+		}
+
+		if (current->flags & PF_FREEZE)
+			refrigerator(PF_FREEZE);
+
+		if (down_interruptible(&fepriv->sem))
+			break;
+
+		/* if we've got no parameters, just keep idling */
+		if (fepriv->state & FESTATE_IDLE) {
+			delay = 3*HZ;
+			quality = 0;
+			continue;
+		}
+
+		/* get the frontend status */
+		if (fepriv->state & FESTATE_RETUNE) {
+			s = 0;
+		} else {
+			if (fe->ops->read_status)
+				fe->ops->read_status(fe, &s);
+			if (s != fepriv->status) {
+				dvb_frontend_add_event(fe, s);
+				fepriv->status = s;
+			}
+		}
+		/* if we're not tuned, and we have a lock, move to the TUNED state */
+		if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+			fepriv->state = FESTATE_TUNED;
+
+			/* if we're tuned, then we have determined the correct inversion */
+			if ((!(fe->ops->info.caps & FE_CAN_INVERSION_AUTO)) &&
+			    (fepriv->parameters.inversion == INVERSION_AUTO)) {
+				fepriv->parameters.inversion = fepriv->inversion;
+			}
+			continue;
+		}
+
+		/* if we are tuned already, check we're still locked */
+		if (fepriv->state & FESTATE_TUNED) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+
+			/* we're tuned, and the lock is still good... */
+			if (s & FE_HAS_LOCK)
+				continue;
+			else {
+				/* if we _WERE_ tuned, but now don't have a lock,
+				 * need to zigzag */
+				fepriv->state = FESTATE_ZIGZAG_FAST;
+				fepriv->started_auto_step = fepriv->auto_step;
+				check_wrapped = 0;
+			}
+		}
+
+		/* don't actually do anything if we're in the LOSTLOCK state,
+		 * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */
+		if ((fepriv->state & FESTATE_LOSTLOCK) &&
+		    (fe->ops->info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+			continue;
+		}
+
+		/* don't do anything if we're in the DISEQC state, since this
+		 * might be someone with a motorized dish controlled by DISEQC.
+		 * If its actually a re-tune, there will be a SET_FRONTEND soon enough.	*/
+		if (fepriv->state & FESTATE_DISEQC) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+			continue;
+		}
+
+		/* if we're in the RETUNE state, set everything up for a brand
+		 * new scan, keeping the current inversion setting, as the next
+		 * tune is _very_ likely to require the same */
+		if (fepriv->state & FESTATE_RETUNE) {
+			fepriv->lnb_drift = 0;
+			fepriv->auto_step = 0;
+			fepriv->auto_sub_step = 0;
+			fepriv->started_auto_step = 0;
+			check_wrapped = 0;
+		}
+
+		/* fast zigzag. */
+		if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) {
+			delay = fepriv->min_delay;
+
+			/* peform a tune */
+			if (dvb_frontend_autotune(fe, check_wrapped)) {
+				/* OK, if we've run out of trials at the fast speed.
+				 * Drop back to slow for the _next_ attempt */
+				fepriv->state = FESTATE_SEARCHING_SLOW;
+				fepriv->started_auto_step = fepriv->auto_step;
+				continue;
+			}
+			check_wrapped = 1;
+
+			/* if we've just retuned, enter the ZIGZAG_FAST state.
+			 * This ensures we cannot return from an
+			 * FE_SET_FRONTEND ioctl before the first frontend tune
+			 * occurs */
+			if (fepriv->state & FESTATE_RETUNE) {
+				fepriv->state = FESTATE_TUNING_FAST;
+			}
+		}
+
+		/* slow zigzag */
+		if (fepriv->state & FESTATE_SEARCHING_SLOW) {
+			update_delay(&quality, &delay, fepriv->min_delay, s & FE_HAS_LOCK);
+
+			/* Note: don't bother checking for wrapping; we stay in this
+			 * state until we get a lock */
+			dvb_frontend_autotune(fe, 0);
+		}
+	}
+
+	if (dvb_shutdown_timeout) {
+		if (dvb_powerdown_on_sleep)
+			if (fe->ops->set_voltage)
+				fe->ops->set_voltage(fe, SEC_VOLTAGE_OFF);
+		if (fe->ops->sleep)
+			fe->ops->sleep(fe);
+	}
+
+	fepriv->thread_pid = 0;
+	mb();
+
+	dvb_frontend_wakeup(fe);
+	return 0;
+}
+
+static void dvb_frontend_stop(struct dvb_frontend *fe)
+{
+	unsigned long ret;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	fepriv->exit = 1;
+	mb();
+
+	if (!fepriv->thread_pid)
+		return;
+
+	/* check if the thread is really alive */
+	if (kill_proc(fepriv->thread_pid, 0, 1) == -ESRCH) {
+		printk("dvb_frontend_stop: thread PID %d already died\n",
+				fepriv->thread_pid);
+		/* make sure the mutex was not held by the thread */
+		init_MUTEX (&fepriv->sem);
+		return;
+	}
+
+	/* wake up the frontend thread, so it notices that fe->exit == 1 */
+	dvb_frontend_wakeup(fe);
+
+	/* wait until the frontend thread has exited */
+	ret = wait_event_interruptible(fepriv->wait_queue,0 == fepriv->thread_pid);
+	if (-ERESTARTSYS != ret) {
+		fepriv->state = FESTATE_IDLE;
+		return;
+	}
+	fepriv->state = FESTATE_IDLE;
+
+	/* paranoia check in case a signal arrived */
+	if (fepriv->thread_pid)
+		printk("dvb_frontend_stop: warning: thread PID %d won't exit\n",
+				fepriv->thread_pid);
+}
+
+static int dvb_frontend_start(struct dvb_frontend *fe)
+{
+	int ret;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (fepriv->thread_pid) {
+		if (!fepriv->exit)
+			return 0;
+		else
+			dvb_frontend_stop (fe);
+	}
+
+	if (signal_pending(current))
+		return -EINTR;
+	if (down_interruptible (&fepriv->sem))
+		return -EINTR;
+
+	fepriv->state = FESTATE_IDLE;
+	fepriv->exit = 0;
+	fepriv->thread_pid = 0;
+	mb();
+
+	ret = kernel_thread (dvb_frontend_thread, fe, 0);
+
+	if (ret < 0) {
+		printk("dvb_frontend_start: failed to start kernel_thread (%d)\n", ret);
+		up(&fepriv->sem);
+		return ret;
+	}
+	fepriv->thread_pid = ret;
+
+	return 0;
+}
+
+static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	int err = -EOPNOTSUPP;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (!fe || fepriv->exit)
+		return -ENODEV;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY &&
+	    (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT ||
+	     cmd == FE_DISEQC_RECV_SLAVE_REPLY))
+		return -EPERM;
+
+	if (down_interruptible (&fepriv->sem))
+		return -ERESTARTSYS;
+
+	switch (cmd) {
+	case FE_GET_INFO: {
+		struct dvb_frontend_info* info = (struct dvb_frontend_info*) parg;
+		memcpy(info, &fe->ops->info, sizeof(struct dvb_frontend_info));
+
+		/* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't
+		 * do it, it is done for it. */
+		info->caps |= FE_CAN_INVERSION_AUTO;
+		err = 0;
+		break;
+	}
+
+	case FE_READ_STATUS:
+		if (fe->ops->read_status)
+			err = fe->ops->read_status(fe, (fe_status_t*) parg);
+		break;
+
+	case FE_READ_BER:
+		if (fe->ops->read_ber)
+			err = fe->ops->read_ber(fe, (__u32*) parg);
+		break;
+
+	case FE_READ_SIGNAL_STRENGTH:
+		if (fe->ops->read_signal_strength)
+			err = fe->ops->read_signal_strength(fe, (__u16*) parg);
+		break;
+
+	case FE_READ_SNR:
+		if (fe->ops->read_snr)
+			err = fe->ops->read_snr(fe, (__u16*) parg);
+		break;
+
+	case FE_READ_UNCORRECTED_BLOCKS:
+		if (fe->ops->read_ucblocks)
+			err = fe->ops->read_ucblocks(fe, (__u32*) parg);
+		break;
+
+
+	case FE_DISEQC_RESET_OVERLOAD:
+		if (fe->ops->diseqc_reset_overload) {
+			err = fe->ops->diseqc_reset_overload(fe);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISEQC_SEND_MASTER_CMD:
+		if (fe->ops->diseqc_send_master_cmd) {
+			err = fe->ops->diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISEQC_SEND_BURST:
+		if (fe->ops->diseqc_send_burst) {
+			err = fe->ops->diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_SET_TONE:
+		if (fe->ops->set_tone) {
+			err = fe->ops->set_tone(fe, (fe_sec_tone_mode_t) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_SET_VOLTAGE:
+		if (fe->ops->set_voltage) {
+			err = fe->ops->set_voltage(fe, (fe_sec_voltage_t) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISHNETWORK_SEND_LEGACY_CMD:
+		if (fe->ops->dishnetwork_send_legacy_command) {
+			err = fe->ops->dishnetwork_send_legacy_command(fe, (unsigned int) parg);
+			fepriv->state = FESTATE_DISEQC;
+			fepriv->status = 0;
+		}
+		break;
+
+	case FE_DISEQC_RECV_SLAVE_REPLY:
+		if (fe->ops->diseqc_recv_slave_reply)
+			err = fe->ops->diseqc_recv_slave_reply(fe, (struct dvb_diseqc_slave_reply*) parg);
+		break;
+
+	case FE_ENABLE_HIGH_LNB_VOLTAGE:
+		if (fe->ops->enable_high_lnb_voltage)
+			err = fe->ops->enable_high_lnb_voltage(fe, (int) parg);
+		break;
+
+	case FE_SET_FRONTEND: {
+		struct dvb_frontend_tune_settings fetunesettings;
+
+		memcpy (&fepriv->parameters, parg,
+			sizeof (struct dvb_frontend_parameters));
+
+		memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
+		memcpy(&fetunesettings.parameters, parg,
+		       sizeof (struct dvb_frontend_parameters));
+
+		/* force auto frequency inversion if requested */
+		if (dvb_force_auto_inversion) {
+			fepriv->parameters.inversion = INVERSION_AUTO;
+			fetunesettings.parameters.inversion = INVERSION_AUTO;
+		}
+		if (fe->ops->info.type == FE_OFDM) {
+			/* without hierachical coding code_rate_LP is irrelevant,
+			 * so we tolerate the otherwise invalid FEC_NONE setting */
+			if (fepriv->parameters.u.ofdm.hierarchy_information == HIERARCHY_NONE &&
+			    fepriv->parameters.u.ofdm.code_rate_LP == FEC_NONE)
+				fepriv->parameters.u.ofdm.code_rate_LP = FEC_AUTO;
+		}
+
+		/* get frontend-specific tuning settings */
+		if (fe->ops->get_tune_settings && (fe->ops->get_tune_settings(fe, &fetunesettings) == 0)) {
+			fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
+			fepriv->max_drift = fetunesettings.max_drift;
+			fepriv->step_size = fetunesettings.step_size;
+		} else {
+			/* default values */
+			switch(fe->ops->info.type) {
+			case FE_QPSK:
+				fepriv->min_delay = HZ/20;
+				fepriv->step_size = fepriv->parameters.u.qpsk.symbol_rate / 16000;
+				fepriv->max_drift = fepriv->parameters.u.qpsk.symbol_rate / 2000;
+				break;
+
+			case FE_QAM:
+				fepriv->min_delay = HZ/20;
+				fepriv->step_size = 0; /* no zigzag */
+				fepriv->max_drift = 0;
+				break;
+
+			case FE_OFDM:
+				fepriv->min_delay = HZ/20;
+				fepriv->step_size = fe->ops->info.frequency_stepsize * 2;
+				fepriv->max_drift = (fe->ops->info.frequency_stepsize * 2) + 1;
+				break;
+			case FE_ATSC:
+				printk("dvb-core: FE_ATSC not handled yet.\n");
+				break;
+			}
+		}
+		if (dvb_override_tune_delay > 0)
+			fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+
+		fepriv->state = FESTATE_RETUNE;
+		dvb_frontend_wakeup(fe);
+		dvb_frontend_add_event(fe, 0);
+		fepriv->status = 0;
+		err = 0;
+		break;
+	}
+
+	case FE_GET_EVENT:
+		err = dvb_frontend_get_event (fe, parg, file->f_flags);
+		break;
+
+	case FE_GET_FRONTEND:
+		if (fe->ops->get_frontend) {
+			memcpy (parg, &fepriv->parameters, sizeof (struct dvb_frontend_parameters));
+			err = fe->ops->get_frontend(fe, (struct dvb_frontend_parameters*) parg);
+		}
+		break;
+	};
+
+	up (&fepriv->sem);
+	return err;
+}
+
+static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	poll_wait (file, &fepriv->events.wait_queue, wait);
+
+	if (fepriv->events.eventw != fepriv->events.eventr)
+		return (POLLIN | POLLRDNORM | POLLPRI);
+
+	return 0;
+}
+
+static int dvb_frontend_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	int ret;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if ((ret = dvb_generic_open (inode, file)) < 0)
+		return ret;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		ret = dvb_frontend_start (fe);
+		if (ret)
+			dvb_generic_release (inode, file);
+
+		/*  empty event queue */
+		fepriv->events.eventr = fepriv->events.eventw = 0;
+	}
+
+	return ret;
+}
+
+static int dvb_frontend_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+		fepriv->release_jiffies = jiffies;
+
+	return dvb_generic_release (inode, file);
+}
+
+static struct file_operations dvb_frontend_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= dvb_generic_ioctl,
+	.poll		= dvb_frontend_poll,
+	.open		= dvb_frontend_open,
+	.release	= dvb_frontend_release
+};
+
+int dvb_register_frontend(struct dvb_adapter* dvb,
+			  struct dvb_frontend* fe)
+{
+	struct dvb_frontend_private *fepriv;
+	static const struct dvb_device dvbdev_template = {
+		.users = ~0,
+		.writers = 1,
+		.readers = (~0)-1,
+		.fops = &dvb_frontend_fops,
+		.kernel_ioctl = dvb_frontend_ioctl
+	};
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (down_interruptible (&frontend_mutex))
+		return -ERESTARTSYS;
+
+	fe->frontend_priv = kmalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL);
+	if (fe->frontend_priv == NULL) {
+		up(&frontend_mutex);
+		return -ENOMEM;
+	}
+	fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	memset(fe->frontend_priv, 0, sizeof(struct dvb_frontend_private));
+
+	init_MUTEX (&fepriv->sem);
+	init_waitqueue_head (&fepriv->wait_queue);
+	init_waitqueue_head (&fepriv->events.wait_queue);
+	init_MUTEX (&fepriv->events.sem);
+	fe->dvb = dvb;
+	fepriv->inversion = INVERSION_OFF;
+
+	printk ("DVB: registering frontend %i (%s)...\n",
+		fe->dvb->num,
+		fe->ops->info.name);
+
+	dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
+			     fe, DVB_DEVICE_FRONTEND);
+
+	up (&frontend_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_register_frontend);
+
+int dvb_unregister_frontend(struct dvb_frontend* fe)
+{
+	struct dvb_frontend_private *fepriv = (struct dvb_frontend_private*) fe->frontend_priv;
+	dprintk ("%s\n", __FUNCTION__);
+
+	down (&frontend_mutex);
+	dvb_unregister_device (fepriv->dvbdev);
+	dvb_frontend_stop (fe);
+	if (fe->ops->release)
+		fe->ops->release(fe);
+	else
+		printk("dvb_frontend: Demodulator (%s) does not have a release callback!\n", fe->ops->info.name);
+	/* fe is invalid now */
+	kfree(fepriv);
+	up (&frontend_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_frontend);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
new file mode 100644
index 0000000..d2b0217
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -0,0 +1,126 @@
+/*
+ * dvb_frontend.h
+ *
+ * Copyright (C) 2001 convergence integrated media GmbH
+ * Copyright (C) 2004 convergence GmbH
+ *
+ * Written by Ralph Metzler
+ * Overhauled by Holger Waechtler
+ * Kernel I2C stuff by Michael Hunold <hunold@convergence.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DVB_FRONTEND_H_
+#define _DVB_FRONTEND_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "dvbdev.h"
+
+/* FIXME: Move to i2c-id.h */
+#define I2C_DRIVERID_DVBFE_SP8870	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_CX22700	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_AT76C651	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_CX24110	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_CX22702	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_DIB3000MB	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_DST		I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_DUMMY	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_L64781	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_MT312	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_MT352	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_NXT6000	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_SP887X	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_STV0299	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_TDA1004X	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_TDA8083	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_VES1820	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_VES1X93	I2C_DRIVERID_EXP2
+#define I2C_DRIVERID_DVBFE_TDA80XX	I2C_DRIVERID_EXP2
+
+
+struct dvb_frontend_tune_settings {
+        int min_delay_ms;
+        int step_size;
+        int max_drift;
+        struct dvb_frontend_parameters parameters;
+};
+
+struct dvb_frontend;
+
+struct dvb_frontend_ops {
+
+	struct dvb_frontend_info info;
+
+	void (*release)(struct dvb_frontend* fe);
+
+	int (*init)(struct dvb_frontend* fe);
+	int (*sleep)(struct dvb_frontend* fe);
+
+	int (*set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+	int (*get_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+	int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings);
+
+	int (*read_status)(struct dvb_frontend* fe, fe_status_t* status);
+	int (*read_ber)(struct dvb_frontend* fe, u32* ber);
+	int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength);
+	int (*read_snr)(struct dvb_frontend* fe, u16* snr);
+	int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks);
+
+	int (*diseqc_reset_overload)(struct dvb_frontend* fe);
+	int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
+	int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply);
+	int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
+	int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
+	int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+	int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, int arg);
+	int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned int cmd);
+};
+
+#define MAX_EVENT 8
+
+struct dvb_fe_events {
+	struct dvb_frontend_event events[MAX_EVENT];
+	int			  eventw;
+	int			  eventr;
+	int			  overflow;
+	wait_queue_head_t	  wait_queue;
+	struct semaphore	  sem;
+};
+
+struct dvb_frontend {
+	struct dvb_frontend_ops* ops;
+	struct dvb_adapter *dvb;
+	void* demodulator_priv;
+	void* frontend_priv;
+};
+
+extern int dvb_register_frontend(struct dvb_adapter* dvb,
+				 struct dvb_frontend* fe);
+
+extern int dvb_unregister_frontend(struct dvb_frontend* fe);
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
new file mode 100644
index 0000000..44892e7
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -0,0 +1,1381 @@
+/*
+ * dvb_net.c
+ *
+ * Copyright (C) 2001 Convergence integrated media GmbH
+ *                    Ralph Metzler <ralph@convergence.de>
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * ULE Decapsulation code:
+ * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH.
+ *                      and Department of Scientific Computing
+ *                          Paris Lodron University of Salzburg.
+ *                          Hilmar Linder <hlinder@cosy.sbg.ac.at>
+ *                      and Wolfram Stering <wstering@cosy.sbg.ac.at>
+ *
+ * ULE Decaps according to draft-ietf-ipdvb-ule-03.txt.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*
+ * ULE ChangeLog:
+ * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt
+ *
+ * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt:
+ *                       ULE Extension header handling.
+ *                     Bugreports by Moritz Vieth and Hanno Tersteegen,
+ *                       Fraunhofer Institute for Open Communication Systems
+ *                       Competence Center for Advanced Satellite Communications.
+ *                     Bugfixes and robustness improvements.
+ *                     Filtering on dest MAC addresses, if present (D-Bit = 0)
+ *                     ULE_DEBUG compile-time option.
+ */
+
+/*
+ * FIXME / TODO (dvb_net.c):
+ *
+ * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero.
+ *
+ * TS_FEED callback is called once for every single TS cell although it is
+ * registered (in dvb_net_feed_start()) for 100 TS cells (used for dvb_net_ule()).
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/dvb/net.h>
+#include <linux/uio.h>
+#include <asm/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/version.h>
+
+#include "dvb_demux.h"
+#include "dvb_net.h"
+
+static int dvb_net_debug;
+module_param(dvb_net_debug, int, 0444);
+MODULE_PARM_DESC(dvb_net_debug, "enable debug messages");
+
+#define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0)
+
+
+static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt )
+{
+	unsigned int j;
+	for (j = 0; j < cnt; j++)
+		c = crc32_be( c, iov[j].iov_base, iov[j].iov_len );
+	return c;
+}
+
+
+#define DVB_NET_MULTICAST_MAX 10
+
+#undef ULE_DEBUG
+
+#ifdef ULE_DEBUG
+
+#define isprint(c)	((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
+
+static void hexdump( const unsigned char *buf, unsigned short len )
+{
+	char str[80], octet[10];
+	int ofs, i, l;
+
+	for (ofs = 0; ofs < len; ofs += 16) {
+		sprintf( str, "%03d: ", ofs );
+
+		for (i = 0; i < 16; i++) {
+			if ((i + ofs) < len)
+				sprintf( octet, "%02x ", buf[ofs + i] );
+			else
+				strcpy( octet, "   " );
+
+			strcat( str, octet );
+		}
+		strcat( str, "  " );
+		l = strlen( str );
+
+		for (i = 0; (i < 16) && ((i + ofs) < len); i++)
+			str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.';
+
+		str[l] = '\0';
+		printk( KERN_WARNING "%s\n", str );
+	}
+}
+
+#endif
+
+struct dvb_net_priv {
+	int in_use;
+	struct net_device_stats stats;
+	u16 pid;
+	struct dvb_net *host;
+	struct dmx_demux *demux;
+	struct dmx_section_feed *secfeed;
+	struct dmx_section_filter *secfilter;
+	struct dmx_ts_feed *tsfeed;
+	int multi_num;
+	struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX];
+	unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6];
+	int rx_mode;
+#define RX_MODE_UNI 0
+#define RX_MODE_MULTI 1
+#define RX_MODE_ALL_MULTI 2
+#define RX_MODE_PROMISC 3
+	struct work_struct set_multicast_list_wq;
+	struct work_struct restart_net_feed_wq;
+	unsigned char feedtype;			/* Either FEED_TYPE_ or FEED_TYPE_ULE */
+	int need_pusi;				/* Set to 1, if synchronization on PUSI required. */
+	unsigned char tscc;			/* TS continuity counter after sync on PUSI. */
+	struct sk_buff *ule_skb;		/* ULE SNDU decodes into this buffer. */
+	unsigned char *ule_next_hdr;		/* Pointer into skb to next ULE extension header. */
+	unsigned short ule_sndu_len;		/* ULE SNDU length in bytes, w/o D-Bit. */
+	unsigned short ule_sndu_type;		/* ULE SNDU type field, complete. */
+	unsigned char ule_sndu_type_1;		/* ULE SNDU type field, if split across 2 TS cells. */
+	unsigned char ule_dbit;			/* Whether the DestMAC address present
+						 * or not (bit is set). */
+	unsigned char ule_bridged;		/* Whether the ULE_BRIDGED extension header was found. */
+	int ule_sndu_remain;			/* Nr. of bytes still required for current ULE SNDU. */
+	unsigned long ts_count;			/* Current ts cell counter. */
+};
+
+
+/**
+ *	Determine the packet's protocol ID. The rule here is that we
+ *	assume 802.3 if the type field is short enough to be a length.
+ *	This is normal practice and works for any 'now in use' protocol.
+ *
+ *  stolen from eth.c out of the linux kernel, hacked for dvb-device
+ *  by Michael Holzt <kju@debian.org>
+ */
+static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+	struct ethhdr *eth;
+	unsigned char *rawp;
+
+	skb->mac.raw=skb->data;
+	skb_pull(skb,dev->hard_header_len);
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,8)
+	eth = skb->mac.ethernet;
+#else
+	eth = eth_hdr(skb);
+#endif
+
+	if (*eth->h_dest & 1) {
+		if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
+			skb->pkt_type=PACKET_BROADCAST;
+		else
+			skb->pkt_type=PACKET_MULTICAST;
+	}
+
+	if (ntohs(eth->h_proto) >= 1536)
+		return eth->h_proto;
+
+	rawp = skb->data;
+
+	/**
+	 *	This is a magic hack to spot IPX packets. Older Novell breaks
+	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
+	 *	layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+	 *	won't work for fault tolerant netware but does for the rest.
+	 */
+	if (*(unsigned short *)rawp == 0xFFFF)
+		return htons(ETH_P_802_3);
+
+	/**
+	 *	Real 802.2 LLC
+	 */
+	return htons(ETH_P_802_2);
+}
+
+#define TS_SZ	188
+#define TS_SYNC	0x47
+#define TS_TEI	0x80
+#define TS_SC	0xC0
+#define TS_PUSI	0x40
+#define TS_AF_A	0x20
+#define TS_AF_D	0x10
+
+/* ULE Extension Header handlers. */
+
+#define ULE_TEST	0
+#define ULE_BRIDGED	1
+
+static int ule_test_sndu( struct dvb_net_priv *p )
+{
+	return -1;
+}
+
+static int ule_bridged_sndu( struct dvb_net_priv *p )
+{
+	/* BRIDGE SNDU handling sucks in draft-ietf-ipdvb-ule-03.txt.
+	 * This has to be the last extension header, otherwise it won't work.
+	 * Blame the authors!
+	 */
+	p->ule_bridged = 1;
+	return 0;
+}
+
+
+/** Handle ULE extension headers.
+ *  Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding.
+ *  Returns: >= 0: nr. of bytes consumed by next extension header
+ *	     -1:   Mandatory extension header that is not recognized or TEST SNDU; discard.
+ */
+static int handle_one_ule_extension( struct dvb_net_priv *p )
+{
+	/* Table of mandatory extension header handlers.  The header type is the index. */
+	static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) =
+		{ [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL,  };
+
+	/* Table of optional extension header handlers.  The header type is the index. */
+	static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = { NULL, };
+
+	int ext_len = 0;
+	unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8;
+	unsigned char htype = p->ule_sndu_type & 0x00FF;
+
+	/* Discriminate mandatory and optional extension headers. */
+	if (hlen == 0) {
+		/* Mandatory extension header */
+		if (ule_mandatory_ext_handlers[htype]) {
+			ext_len = ule_mandatory_ext_handlers[htype]( p );
+			p->ule_next_hdr += ext_len;
+			if (! p->ule_bridged) {
+				p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr );
+				p->ule_next_hdr += 2;
+			} else {
+				p->ule_sndu_type = ntohs( *(unsigned short *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN)) );
+				/* This assures the extension handling loop will terminate. */
+			}
+		} else
+			ext_len = -1;	/* SNDU has to be discarded. */
+	} else {
+		/* Optional extension header.  Calculate the length. */
+		ext_len = hlen << 2;
+		/* Process the optional extension header according to its type. */
+		if (ule_optional_ext_handlers[htype])
+			(void)ule_optional_ext_handlers[htype]( p );
+		p->ule_next_hdr += ext_len;
+		p->ule_sndu_type = ntohs( *(unsigned short *)p->ule_next_hdr );
+		p->ule_next_hdr += 2;
+	}
+
+	return ext_len;
+}
+
+static int handle_ule_extensions( struct dvb_net_priv *p )
+{
+	int total_ext_len = 0, l;
+
+	p->ule_next_hdr = p->ule_skb->data;
+	do {
+		l = handle_one_ule_extension( p );
+		if (l == -1) return -1;	/* Stop extension header processing and discard SNDU. */
+		total_ext_len += l;
+
+	} while (p->ule_sndu_type < 1536);
+
+	return total_ext_len;
+}
+
+
+/** Prepare for a new ULE SNDU: reset the decoder state. */
+static inline void reset_ule( struct dvb_net_priv *p )
+{
+	p->ule_skb = NULL;
+	p->ule_next_hdr = NULL;
+	p->ule_sndu_len = 0;
+	p->ule_sndu_type = 0;
+	p->ule_sndu_type_1 = 0;
+	p->ule_sndu_remain = 0;
+	p->ule_dbit = 0xFF;
+	p->ule_bridged = 0;
+}
+
+/**
+ * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of
+ * TS cells of a single PID.
+ */
+static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv *)dev->priv;
+	unsigned long skipped = 0L;
+	u8 *ts, *ts_end, *from_where = NULL, ts_remain = 0, how_much = 0, new_ts = 1;
+	struct ethhdr *ethh = NULL;
+
+#ifdef ULE_DEBUG
+	/* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */
+	static unsigned char ule_hist[100*TS_SZ];
+	static unsigned char *ule_where = ule_hist, ule_dump = 0;
+#endif
+
+	if (dev == NULL) {
+		printk( KERN_ERR "NO netdev struct!\n" );
+		return;
+	}
+
+	/* For all TS cells in current buffer.
+	 * Appearently, we are called for every single TS cell.
+	 */
+	for (ts = (char *)buf, ts_end = (char *)buf + buf_len; ts < ts_end; /* no default incr. */ ) {
+
+		if (new_ts) {
+			/* We are about to process a new TS cell. */
+
+#ifdef ULE_DEBUG
+			if (ule_where >= &ule_hist[100*TS_SZ]) ule_where = ule_hist;
+			memcpy( ule_where, ts, TS_SZ );
+			if (ule_dump) {
+				hexdump( ule_where, TS_SZ );
+				ule_dump = 0;
+			}
+			ule_where += TS_SZ;
+#endif
+
+			/* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */
+			if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) {
+				printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n",
+				       priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6);
+
+				/* Drop partly decoded SNDU, reset state, resync on PUSI. */
+				if (priv->ule_skb) {
+					dev_kfree_skb( priv->ule_skb );
+					/* Prepare for next SNDU. */
+					((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+					((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+				}
+				reset_ule(priv);
+				priv->need_pusi = 1;
+
+				/* Continue with next TS cell. */
+				ts += TS_SZ;
+				priv->ts_count++;
+				continue;
+			}
+
+			ts_remain = 184;
+			from_where = ts + 4;
+		}
+		/* Synchronize on PUSI, if required. */
+		if (priv->need_pusi) {
+			if (ts[1] & TS_PUSI) {
+				/* Find beginning of first ULE SNDU in current TS cell. */
+				/* Synchronize continuity counter. */
+				priv->tscc = ts[3] & 0x0F;
+				/* There is a pointer field here. */
+				if (ts[4] > ts_remain) {
+					printk(KERN_ERR "%lu: Invalid ULE packet "
+					       "(pointer field %d)\n", priv->ts_count, ts[4]);
+					ts += TS_SZ;
+					priv->ts_count++;
+					continue;
+				}
+				/* Skip to destination of pointer field. */
+				from_where = &ts[5] + ts[4];
+				ts_remain -= 1 + ts[4];
+				skipped = 0;
+			} else {
+				skipped++;
+				ts += TS_SZ;
+				priv->ts_count++;
+				continue;
+			}
+		}
+
+		/* Check continuity counter. */
+		if (new_ts) {
+			if ((ts[3] & 0x0F) == priv->tscc)
+				priv->tscc = (priv->tscc + 1) & 0x0F;
+			else {
+				/* TS discontinuity handling: */
+				printk(KERN_WARNING "%lu: TS discontinuity: got %#x, "
+				       "exptected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc);
+				/* Drop partly decoded SNDU, reset state, resync on PUSI. */
+				if (priv->ule_skb) {
+					dev_kfree_skb( priv->ule_skb );
+					/* Prepare for next SNDU. */
+					// reset_ule(priv);  moved to below.
+					((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+					((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+				}
+				reset_ule(priv);
+				/* skip to next PUSI. */
+				priv->need_pusi = 1;
+				ts += TS_SZ;
+				priv->ts_count++;
+				continue;
+			}
+			/* If we still have an incomplete payload, but PUSI is
+			 * set; some TS cells are missing.
+			 * This is only possible here, if we missed exactly 16 TS
+			 * cells (continuity counter wrap). */
+			if (ts[1] & TS_PUSI) {
+				if (! priv->need_pusi) {
+					if (*from_where > 181) {
+						/* Pointer field is invalid.  Drop this TS cell and any started ULE SNDU. */
+						printk(KERN_WARNING "%lu: Invalid pointer "
+						       "field: %u.\n", priv->ts_count, *from_where);
+
+						/* Drop partly decoded SNDU, reset state, resync on PUSI. */
+						if (priv->ule_skb) {
+							dev_kfree_skb( priv->ule_skb );
+							((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+							((struct dvb_net_priv *) dev->priv)->stats.rx_frame_errors++;
+						}
+						reset_ule(priv);
+						priv->need_pusi = 1;
+						ts += TS_SZ;
+						priv->ts_count++;
+						continue;
+					}
+					/* Skip pointer field (we're processing a
+					 * packed payload). */
+					from_where += 1;
+					ts_remain -= 1;
+				} else
+					priv->need_pusi = 0;
+
+				if (priv->ule_sndu_remain > 183) {
+					/* Current SNDU lacks more data than there could be available in the
+					 * current TS cell. */
+					((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+					((struct dvb_net_priv *) dev->priv)->stats.rx_length_errors++;
+					printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but "
+					       "got PUSI (pf %d, ts_remain %d).  Flushing incomplete payload.\n",
+					       priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain);
+					dev_kfree_skb(priv->ule_skb);
+					/* Prepare for next SNDU. */
+					reset_ule(priv);
+					/* Resync: go to where pointer field points to: start of next ULE SNDU. */
+					from_where += ts[4];
+					ts_remain -= ts[4];
+				}
+			}
+		}
+
+		/* Check if new payload needs to be started. */
+		if (priv->ule_skb == NULL) {
+			/* Start a new payload with skb.
+			 * Find ULE header.  It is only guaranteed that the
+			 * length field (2 bytes) is contained in the current
+			 * TS.
+			 * Check ts_remain has to be >= 2 here. */
+			if (ts_remain < 2) {
+				printk(KERN_WARNING "Invalid payload packing: only %d "
+				       "bytes left in TS.  Resyncing.\n", ts_remain);
+				priv->ule_sndu_len = 0;
+				priv->need_pusi = 1;
+				continue;
+			}
+
+			if (! priv->ule_sndu_len) {
+				/* Got at least two bytes, thus extrace the SNDU length. */
+				priv->ule_sndu_len = from_where[0] << 8 | from_where[1];
+				if (priv->ule_sndu_len & 0x8000) {
+					/* D-Bit is set: no dest mac present. */
+					priv->ule_sndu_len &= 0x7FFF;
+					priv->ule_dbit = 1;
+				} else
+					priv->ule_dbit = 0;
+
+				if (priv->ule_sndu_len > 32763) {
+					printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. "
+					       "Resyncing.\n", priv->ts_count, priv->ule_sndu_len);
+					priv->ule_sndu_len = 0;
+					priv->need_pusi = 1;
+					new_ts = 1;
+					ts += TS_SZ;
+					priv->ts_count++;
+					continue;
+				}
+				ts_remain -= 2;	/* consume the 2 bytes SNDU length. */
+				from_where += 2;
+			}
+
+			/*
+			 * State of current TS:
+			 *   ts_remain (remaining bytes in the current TS cell)
+			 *   0	ule_type is not available now, we need the next TS cell
+			 *   1	the first byte of the ule_type is present
+			 * >=2	full ULE header present, maybe some payload data as well.
+			 */
+			switch (ts_remain) {
+				case 1:
+					priv->ule_sndu_type = from_where[0] << 8;
+					priv->ule_sndu_type_1 = 1; /* first byte of ule_type is set. */
+					ts_remain -= 1; from_where += 1;
+					/* Continue w/ next TS. */
+				case 0:
+					new_ts = 1;
+					ts += TS_SZ;
+					priv->ts_count++;
+					continue;
+
+				default: /* complete ULE header is present in current TS. */
+					/* Extract ULE type field. */
+					if (priv->ule_sndu_type_1) {
+						priv->ule_sndu_type |= from_where[0];
+						from_where += 1; /* points to payload start. */
+						ts_remain -= 1;
+					} else {
+						/* Complete type is present in new TS. */
+						priv->ule_sndu_type = from_where[0] << 8 | from_where[1];
+						from_where += 2; /* points to payload start. */
+						ts_remain -= 2;
+					}
+					break;
+			}
+
+			/* Allocate the skb (decoder target buffer) with the correct size, as follows:
+			 * prepare for the largest case: bridged SNDU with MAC address (dbit = 0). */
+			priv->ule_skb = dev_alloc_skb( priv->ule_sndu_len + ETH_HLEN + ETH_ALEN );
+			if (priv->ule_skb == NULL) {
+				printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+				       dev->name);
+				((struct dvb_net_priv *)dev->priv)->stats.rx_dropped++;
+				return;
+			}
+
+			/* This includes the CRC32 _and_ dest mac, if !dbit. */
+			priv->ule_sndu_remain = priv->ule_sndu_len;
+			priv->ule_skb->dev = dev;
+			/* Leave space for Ethernet or bridged SNDU header (eth hdr plus one MAC addr). */
+			skb_reserve( priv->ule_skb, ETH_HLEN + ETH_ALEN );
+		}
+
+		/* Copy data into our current skb. */
+		how_much = min(priv->ule_sndu_remain, (int)ts_remain);
+		memcpy(skb_put(priv->ule_skb, how_much), from_where, how_much);
+		priv->ule_sndu_remain -= how_much;
+		ts_remain -= how_much;
+		from_where += how_much;
+
+		/* Check for complete payload. */
+		if (priv->ule_sndu_remain <= 0) {
+			/* Check CRC32, we've got it in our skb already. */
+			unsigned short ulen = htons(priv->ule_sndu_len);
+			unsigned short utype = htons(priv->ule_sndu_type);
+			struct kvec iov[3] = {
+				{ &ulen, sizeof ulen },
+				{ &utype, sizeof utype },
+				{ priv->ule_skb->data, priv->ule_skb->len - 4 }
+			};
+			unsigned long ule_crc = ~0L, expected_crc;
+			if (priv->ule_dbit) {
+				/* Set D-bit for CRC32 verification,
+				 * if it was set originally. */
+				ulen |= 0x0080;
+			}
+
+			ule_crc = iov_crc32(ule_crc, iov, 3);
+			expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 |
+				       *((u8 *)priv->ule_skb->tail - 3) << 16 |
+				       *((u8 *)priv->ule_skb->tail - 2) << 8 |
+				       *((u8 *)priv->ule_skb->tail - 1);
+			if (ule_crc != expected_crc) {
+				printk(KERN_WARNING "%lu: CRC32 check FAILED: %#lx / %#lx, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n",
+				       priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0);
+
+#ifdef ULE_DEBUG
+				hexdump( iov[0].iov_base, iov[0].iov_len );
+				hexdump( iov[1].iov_base, iov[1].iov_len );
+				hexdump( iov[2].iov_base, iov[2].iov_len );
+
+				if (ule_where == ule_hist) {
+					hexdump( &ule_hist[98*TS_SZ], TS_SZ );
+					hexdump( &ule_hist[99*TS_SZ], TS_SZ );
+				} else if (ule_where == &ule_hist[TS_SZ]) {
+					hexdump( &ule_hist[99*TS_SZ], TS_SZ );
+					hexdump( ule_hist, TS_SZ );
+				} else {
+					hexdump( ule_where - TS_SZ - TS_SZ, TS_SZ );
+					hexdump( ule_where - TS_SZ, TS_SZ );
+				}
+				ule_dump = 1;
+#endif
+
+				((struct dvb_net_priv *) dev->priv)->stats.rx_errors++;
+				((struct dvb_net_priv *) dev->priv)->stats.rx_crc_errors++;
+				dev_kfree_skb(priv->ule_skb);
+			} else {
+				/* CRC32 verified OK. */
+				/* Handle ULE Extension Headers. */
+				if (priv->ule_sndu_type < 1536) {
+					/* There is an extension header.  Handle it accordingly. */
+					int l = handle_ule_extensions( priv );
+					if (l < 0) {
+						/* Mandatory extension header unknown or TEST SNDU.  Drop it. */
+						// printk( KERN_WARNING "Dropping SNDU, extension headers.\n" );
+						dev_kfree_skb( priv->ule_skb );
+						goto sndu_done;
+					}
+					skb_pull( priv->ule_skb, l );
+				}
+
+				/* CRC32 was OK. Remove it from skb. */
+				priv->ule_skb->tail -= 4;
+				priv->ule_skb->len -= 4;
+
+				/* Filter on receiver's destination MAC address, if present. */
+				if (!priv->ule_dbit) {
+					/* The destination MAC address is the next data in the skb. */
+					if (memcmp( priv->ule_skb->data, dev->dev_addr, ETH_ALEN )) {
+						/* MAC addresses don't match.  Drop SNDU. */
+						// printk( KERN_WARNING "Dropping SNDU, MAC address.\n" );
+						dev_kfree_skb( priv->ule_skb );
+						goto sndu_done;
+					}
+					if (! priv->ule_bridged) {
+						skb_push( priv->ule_skb, ETH_ALEN + 2 );
+						ethh = (struct ethhdr *)priv->ule_skb->data;
+						memcpy( ethh->h_dest, ethh->h_source, ETH_ALEN );
+						memset( ethh->h_source, 0, ETH_ALEN );
+						ethh->h_proto = htons( priv->ule_sndu_type );
+					} else {
+						/* Skip the Receiver destination MAC address. */
+						skb_pull( priv->ule_skb, ETH_ALEN );
+					}
+				} else {
+					if (! priv->ule_bridged) {
+						skb_push( priv->ule_skb, ETH_HLEN );
+						ethh = (struct ethhdr *)priv->ule_skb->data;
+						memcpy( ethh->h_dest, dev->dev_addr, ETH_ALEN );
+						memset( ethh->h_source, 0, ETH_ALEN );
+						ethh->h_proto = htons( priv->ule_sndu_type );
+					} else {
+						/* skb is in correct state; nothing to do. */
+					}
+				}
+				priv->ule_bridged = 0;
+
+				/* Stuff into kernel's protocol stack. */
+				priv->ule_skb->protocol = dvb_net_eth_type_trans(priv->ule_skb, dev);
+				/* If D-bit is set (i.e. destination MAC address not present),
+				 * receive the packet anyhow. */
+				/* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST)
+					priv->ule_skb->pkt_type = PACKET_HOST; */
+				((struct dvb_net_priv *) dev->priv)->stats.rx_packets++;
+				((struct dvb_net_priv *) dev->priv)->stats.rx_bytes += priv->ule_skb->len;
+				netif_rx(priv->ule_skb);
+			}
+			sndu_done:
+			/* Prepare for next SNDU. */
+			reset_ule(priv);
+		}
+
+		/* More data in current TS (look at the bytes following the CRC32)? */
+		if (ts_remain >= 2 && *((unsigned short *)from_where) != 0xFFFF) {
+			/* Next ULE SNDU starts right there. */
+			new_ts = 0;
+			priv->ule_skb = NULL;
+			priv->ule_sndu_type_1 = 0;
+			priv->ule_sndu_len = 0;
+			// printk(KERN_WARNING "More data in current TS: [%#x %#x %#x %#x]\n",
+			//	*(from_where + 0), *(from_where + 1),
+			//	*(from_where + 2), *(from_where + 3));
+			// printk(KERN_WARNING "ts @ %p, stopped @ %p:\n", ts, from_where + 0);
+			// hexdump(ts, 188);
+		} else {
+			new_ts = 1;
+			ts += TS_SZ;
+			priv->ts_count++;
+			if (priv->ule_skb == NULL) {
+				priv->need_pusi = 1;
+				priv->ule_sndu_type_1 = 0;
+				priv->ule_sndu_len = 0;
+			}
+		}
+	}	/* for all available TS cells */
+}
+
+static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len,
+			       const u8 *buffer2, size_t buffer2_len,
+			       struct dmx_ts_feed *feed, enum dmx_success success)
+{
+	struct net_device *dev = (struct net_device *)feed->priv;
+
+	if (buffer2 != 0)
+		printk(KERN_WARNING "buffer2 not 0: %p.\n", buffer2);
+	if (buffer1_len > 32768)
+		printk(KERN_WARNING "length > 32k: %zu.\n", buffer1_len);
+	/* printk("TS callback: %u bytes, %u TS cells @ %p.\n",
+	          buffer1_len, buffer1_len / TS_SZ, buffer1); */
+	dvb_net_ule(dev, buffer1, buffer1_len);
+	return 0;
+}
+
+
+static void dvb_net_sec(struct net_device *dev, u8 *pkt, int pkt_len)
+{
+        u8 *eth;
+        struct sk_buff *skb;
+	struct net_device_stats *stats = &(((struct dvb_net_priv *) dev->priv)->stats);
+
+	/* note: pkt_len includes a 32bit checksum */
+	if (pkt_len < 16) {
+		printk("%s: IP/MPE packet length = %d too small.\n",
+			dev->name, pkt_len);
+		stats->rx_errors++;
+		stats->rx_length_errors++;
+		return;
+	}
+/* it seems some ISPs manage to screw up here, so we have to
+ * relax the error checks... */
+#if 0
+	if ((pkt[5] & 0xfd) != 0xc1) {
+		/* drop scrambled or broken packets */
+#else
+	if ((pkt[5] & 0x3c) != 0x00) {
+		/* drop scrambled */
+#endif
+		stats->rx_errors++;
+		stats->rx_crc_errors++;
+		return;
+	}
+	if (pkt[5] & 0x02) {
+		//FIXME: handle LLC/SNAP
+                stats->rx_dropped++;
+                return;
+        }
+	if (pkt[7]) {
+		/* FIXME: assemble datagram from multiple sections */
+		stats->rx_errors++;
+		stats->rx_frame_errors++;
+		return;
+	}
+
+	/* we have 14 byte ethernet header (ip header follows);
+	 * 12 byte MPE header; 4 byte checksum; + 2 byte alignment
+	 */
+	if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2))) {
+		//printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name);
+		stats->rx_dropped++;
+		return;
+	}
+	skb_reserve(skb, 2);    /* longword align L3 header */
+	skb->dev = dev;
+
+	/* copy L3 payload */
+	eth = (u8 *) skb_put(skb, pkt_len - 12 - 4 + 14);
+	memcpy(eth + 14, pkt + 12, pkt_len - 12 - 4);
+
+	/* create ethernet header: */
+        eth[0]=pkt[0x0b];
+        eth[1]=pkt[0x0a];
+        eth[2]=pkt[0x09];
+        eth[3]=pkt[0x08];
+        eth[4]=pkt[0x04];
+        eth[5]=pkt[0x03];
+
+        eth[6]=eth[7]=eth[8]=eth[9]=eth[10]=eth[11]=0;
+
+	eth[12] = 0x08;	/* ETH_P_IP */
+	eth[13] = 0x00;
+
+	skb->protocol = dvb_net_eth_type_trans(skb, dev);
+
+	stats->rx_packets++;
+	stats->rx_bytes+=skb->len;
+        netif_rx(skb);
+}
+
+static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len,
+		 const u8 *buffer2, size_t buffer2_len,
+		 struct dmx_section_filter *filter,
+		 enum dmx_success success)
+{
+        struct net_device *dev=(struct net_device *) filter->priv;
+
+	/**
+	 * we rely on the DVB API definition where exactly one complete
+	 * section is delivered in buffer1
+	 */
+	dvb_net_sec (dev, (u8*) buffer1, buffer1_len);
+	return 0;
+}
+
+static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+static u8 mask_normal[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static u8 mask_allmulti[6]={0xff, 0xff, 0xff, 0x00, 0x00, 0x00};
+static u8 mac_allmulti[6]={0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
+static u8 mask_promisc[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static int dvb_net_filter_sec_set(struct net_device *dev,
+		   struct dmx_section_filter **secfilter,
+		   u8 *mac, u8 *mac_mask)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	int ret;
+
+	*secfilter=NULL;
+	ret = priv->secfeed->allocate_filter(priv->secfeed, secfilter);
+	if (ret<0) {
+		printk("%s: could not get filter\n", dev->name);
+		return ret;
+	}
+
+	(*secfilter)->priv=(void *) dev;
+
+	memset((*secfilter)->filter_value, 0x00, DMX_MAX_FILTER_SIZE);
+	memset((*secfilter)->filter_mask,  0x00, DMX_MAX_FILTER_SIZE);
+	memset((*secfilter)->filter_mode,  0xff, DMX_MAX_FILTER_SIZE);
+
+	(*secfilter)->filter_value[0]=0x3e;
+	(*secfilter)->filter_value[3]=mac[5];
+	(*secfilter)->filter_value[4]=mac[4];
+	(*secfilter)->filter_value[8]=mac[3];
+	(*secfilter)->filter_value[9]=mac[2];
+	(*secfilter)->filter_value[10]=mac[1];
+	(*secfilter)->filter_value[11]=mac[0];
+
+	(*secfilter)->filter_mask[0] = 0xff;
+	(*secfilter)->filter_mask[3] = mac_mask[5];
+	(*secfilter)->filter_mask[4] = mac_mask[4];
+	(*secfilter)->filter_mask[8] = mac_mask[3];
+	(*secfilter)->filter_mask[9] = mac_mask[2];
+	(*secfilter)->filter_mask[10] = mac_mask[1];
+	(*secfilter)->filter_mask[11]=mac_mask[0];
+
+	dprintk("%s: filter mac=%02x %02x %02x %02x %02x %02x\n",
+	       dev->name, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	dprintk("%s: filter mask=%02x %02x %02x %02x %02x %02x\n",
+	       dev->name, mac_mask[0], mac_mask[1], mac_mask[2],
+	       mac_mask[3], mac_mask[4], mac_mask[5]);
+
+	return 0;
+}
+
+static int dvb_net_feed_start(struct net_device *dev)
+{
+	int ret, i;
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+        struct dmx_demux *demux = priv->demux;
+        unsigned char *mac = (unsigned char *) dev->dev_addr;
+
+	dprintk("%s: rx_mode %i\n", __FUNCTION__, priv->rx_mode);
+	if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0])
+		printk("%s: BUG %d\n", __FUNCTION__, __LINE__);
+
+	priv->secfeed=NULL;
+	priv->secfilter=NULL;
+	priv->tsfeed = NULL;
+
+	if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+		dprintk("%s: alloc secfeed\n", __FUNCTION__);
+		ret=demux->allocate_section_feed(demux, &priv->secfeed,
+					 dvb_net_sec_callback);
+		if (ret<0) {
+			printk("%s: could not allocate section feed\n", dev->name);
+			return ret;
+		}
+
+		ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 0, 1);
+
+		if (ret<0) {
+			printk("%s: could not set section feed\n", dev->name);
+			priv->demux->release_section_feed(priv->demux, priv->secfeed);
+			priv->secfeed=NULL;
+			return ret;
+		}
+
+		if (priv->rx_mode != RX_MODE_PROMISC) {
+			dprintk("%s: set secfilter\n", __FUNCTION__);
+			dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal);
+		}
+
+		switch (priv->rx_mode) {
+		case RX_MODE_MULTI:
+			for (i = 0; i < priv->multi_num; i++) {
+				dprintk("%s: set multi_secfilter[%d]\n", __FUNCTION__, i);
+				dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i],
+						       priv->multi_macs[i], mask_normal);
+			}
+			break;
+		case RX_MODE_ALL_MULTI:
+			priv->multi_num=1;
+			dprintk("%s: set multi_secfilter[0]\n", __FUNCTION__);
+			dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0],
+					       mac_allmulti, mask_allmulti);
+			break;
+		case RX_MODE_PROMISC:
+			priv->multi_num=0;
+			dprintk("%s: set secfilter\n", __FUNCTION__);
+			dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc);
+			break;
+		}
+
+		dprintk("%s: start filtering\n", __FUNCTION__);
+		priv->secfeed->start_filtering(priv->secfeed);
+	} else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+		struct timespec timeout = { 0, 30000000 }; // 30 msec
+
+		/* we have payloads encapsulated in TS */
+		dprintk("%s: alloc tsfeed\n", __FUNCTION__);
+		ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback);
+		if (ret < 0) {
+			printk("%s: could not allocate ts feed\n", dev->name);
+			return ret;
+		}
+
+		/* Set netdevice pointer for ts decaps callback. */
+		priv->tsfeed->priv = (void *)dev;
+		ret = priv->tsfeed->set(priv->tsfeed, priv->pid,
+					TS_PACKET, DMX_TS_PES_OTHER,
+					188 * 100, /* nr. of bytes delivered per callback */
+					32768,     /* circular buffer size */
+					0,         /* descramble */
+					timeout);
+
+		if (ret < 0) {
+			printk("%s: could not set ts feed\n", dev->name);
+			priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+			priv->tsfeed = NULL;
+			return ret;
+		}
+
+		dprintk("%s: start filtering\n", __FUNCTION__);
+		priv->tsfeed->start_filtering(priv->tsfeed);
+	} else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int dvb_net_feed_stop(struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+	if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) {
+		if (priv->secfeed) {
+			if (priv->secfeed->is_filtering) {
+				dprintk("%s: stop secfeed\n", __FUNCTION__);
+				priv->secfeed->stop_filtering(priv->secfeed);
+			}
+
+			if (priv->secfilter) {
+				dprintk("%s: release secfilter\n", __FUNCTION__);
+				priv->secfeed->release_filter(priv->secfeed,
+							      priv->secfilter);
+				priv->secfilter=NULL;
+			}
+
+			for (i=0; i<priv->multi_num; i++) {
+				if (priv->multi_secfilter[i]) {
+					dprintk("%s: release multi_filter[%d]\n",
+						__FUNCTION__, i);
+					priv->secfeed->release_filter(priv->secfeed,
+								      priv->multi_secfilter[i]);
+					priv->multi_secfilter[i] = NULL;
+				}
+			}
+
+			priv->demux->release_section_feed(priv->demux, priv->secfeed);
+			priv->secfeed = NULL;
+		} else
+			printk("%s: no feed to stop\n", dev->name);
+	} else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) {
+		if (priv->tsfeed) {
+			if (priv->tsfeed->is_filtering) {
+				dprintk("%s: stop tsfeed\n", __FUNCTION__);
+				priv->tsfeed->stop_filtering(priv->tsfeed);
+			}
+			priv->demux->release_ts_feed(priv->demux, priv->tsfeed);
+			priv->tsfeed = NULL;
+		}
+		else
+			printk("%s: no ts feed to stop\n", dev->name);
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+
+static int dvb_set_mc_filter (struct net_device *dev, struct dev_mc_list *mc)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	if (priv->multi_num == DVB_NET_MULTICAST_MAX)
+		return -ENOMEM;
+
+	memcpy(priv->multi_macs[priv->multi_num], mc->dmi_addr, 6);
+
+	priv->multi_num++;
+	return 0;
+}
+
+
+static void wq_set_multicast_list (void *data)
+{
+	struct net_device *dev = data;
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	dvb_net_feed_stop(dev);
+
+	priv->rx_mode = RX_MODE_UNI;
+
+	if (dev->flags & IFF_PROMISC) {
+		dprintk("%s: promiscuous mode\n", dev->name);
+		priv->rx_mode = RX_MODE_PROMISC;
+	} else if ((dev->flags & IFF_ALLMULTI)) {
+		dprintk("%s: allmulti mode\n", dev->name);
+		priv->rx_mode = RX_MODE_ALL_MULTI;
+	} else if (dev->mc_count) {
+		int mci;
+		struct dev_mc_list *mc;
+
+		dprintk("%s: set_mc_list, %d entries\n",
+			dev->name, dev->mc_count);
+
+		priv->rx_mode = RX_MODE_MULTI;
+		priv->multi_num = 0;
+
+		for (mci = 0, mc=dev->mc_list;
+		     mci < dev->mc_count;
+		     mc = mc->next, mci++) {
+			dvb_set_mc_filter(dev, mc);
+		}
+	}
+
+	dvb_net_feed_start(dev);
+}
+
+
+static void dvb_net_set_multicast_list (struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	schedule_work(&priv->set_multicast_list_wq);
+}
+
+
+static void wq_restart_net_feed (void *data)
+{
+	struct net_device *dev = data;
+
+	if (netif_running(dev)) {
+		dvb_net_feed_stop(dev);
+		dvb_net_feed_start(dev);
+	}
+}
+
+
+static int dvb_net_set_mac (struct net_device *dev, void *p)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+	struct sockaddr *addr=p;
+
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	if (netif_running(dev))
+		schedule_work(&priv->restart_net_feed_wq);
+
+	return 0;
+}
+
+
+static int dvb_net_open(struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	priv->in_use++;
+	dvb_net_feed_start(dev);
+	return 0;
+}
+
+
+static int dvb_net_stop(struct net_device *dev)
+{
+	struct dvb_net_priv *priv = (struct dvb_net_priv*) dev->priv;
+
+	priv->in_use--;
+        return dvb_net_feed_stop(dev);
+}
+
+static struct net_device_stats * dvb_net_get_stats(struct net_device *dev)
+{
+        return &((struct dvb_net_priv*) dev->priv)->stats;
+}
+
+static void dvb_net_setup(struct net_device *dev)
+{
+	ether_setup(dev);
+
+	dev->open		= dvb_net_open;
+	dev->stop		= dvb_net_stop;
+	dev->hard_start_xmit	= dvb_net_tx;
+	dev->get_stats		= dvb_net_get_stats;
+	dev->set_multicast_list = dvb_net_set_multicast_list;
+	dev->set_mac_address    = dvb_net_set_mac;
+	dev->mtu		= 4096;
+	dev->mc_count           = 0;
+	dev->hard_header_cache  = NULL;
+	dev->flags |= IFF_NOARP;
+}
+
+static int get_if(struct dvb_net *dvbnet)
+{
+	int i;
+
+	for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+		if (!dvbnet->state[i])
+			break;
+
+	if (i == DVB_NET_DEVICES_MAX)
+		return -1;
+
+	dvbnet->state[i]=1;
+	return i;
+}
+
+static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype)
+{
+	struct net_device *net;
+	struct dvb_net_priv *priv;
+	int result;
+	int if_num;
+
+	if (feedtype != DVB_NET_FEEDTYPE_MPE && feedtype != DVB_NET_FEEDTYPE_ULE)
+		return -EINVAL;
+	if ((if_num = get_if(dvbnet)) < 0)
+		return -EINVAL;
+
+	net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb", dvb_net_setup);
+	if (!net)
+		return -ENOMEM;
+
+	if (dvbnet->dvbdev->id)
+		snprintf(net->name, IFNAMSIZ, "dvb%d%u%d",
+			 dvbnet->dvbdev->adapter->num, dvbnet->dvbdev->id, if_num);
+	else
+		/* compatibility fix to keep dvb0_0 format */
+		snprintf(net->name, IFNAMSIZ, "dvb%d_%d",
+			 dvbnet->dvbdev->adapter->num, if_num);
+
+	net->addr_len = 6;
+	memcpy(net->dev_addr, dvbnet->dvbdev->adapter->proposed_mac, 6);
+
+	dvbnet->device[if_num] = net;
+
+	priv = net->priv;
+	priv->demux = dvbnet->demux;
+	priv->pid = pid;
+	priv->rx_mode = RX_MODE_UNI;
+	priv->need_pusi = 1;
+	priv->tscc = 0;
+	priv->feedtype = feedtype;
+	reset_ule(priv);
+
+	INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list, net);
+	INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed, net);
+
+	net->base_addr = pid;
+
+	if ((result = register_netdev(net)) < 0) {
+		dvbnet->device[if_num] = NULL;
+		free_netdev(net);
+		return result;
+	}
+	printk("dvb_net: created network interface %s\n", net->name);
+
+	return if_num;
+}
+
+static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned int num)
+{
+	struct net_device *net = dvbnet->device[num];
+	struct dvb_net_priv *priv;
+
+	if (!dvbnet->state[num])
+		return -EINVAL;
+	priv = net->priv;
+	if (priv->in_use)
+		return -EBUSY;
+
+	dvb_net_stop(net);
+	flush_scheduled_work();
+	printk("dvb_net: removed network interface %s\n", net->name);
+	unregister_netdev(net);
+	dvbnet->state[num]=0;
+	dvbnet->device[num] = NULL;
+	free_netdev(net);
+
+	return 0;
+}
+
+static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
+		  unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct dvb_net *dvbnet = (struct dvb_net *) dvbdev->priv;
+
+	if (((file->f_flags&O_ACCMODE)==O_RDONLY))
+		return -EPERM;
+
+	switch (cmd) {
+	case NET_ADD_IF:
+	{
+		struct dvb_net_if *dvbnetif=(struct dvb_net_if *)parg;
+		int result;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (!try_module_get(dvbdev->adapter->module))
+			return -EPERM;
+
+		result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype);
+		if (result<0) {
+			module_put(dvbdev->adapter->module);
+			return result;
+		}
+		dvbnetif->if_num=result;
+		break;
+	}
+	case NET_GET_IF:
+	{
+		struct net_device *netdev;
+		struct dvb_net_priv *priv_data;
+		struct dvb_net_if *dvbnetif=(struct dvb_net_if *)parg;
+
+		if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+		    !dvbnet->state[dvbnetif->if_num])
+			return -EINVAL;
+
+		netdev = dvbnet->device[dvbnetif->if_num];
+
+		priv_data=(struct dvb_net_priv*)netdev->priv;
+		dvbnetif->pid=priv_data->pid;
+		dvbnetif->feedtype=priv_data->feedtype;
+		break;
+	}
+	case NET_REMOVE_IF:
+	{
+		int ret;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if ((unsigned int) parg >= DVB_NET_DEVICES_MAX)
+			return -EINVAL;
+		ret = dvb_net_remove_if(dvbnet, (unsigned int) parg);
+		if (!ret)
+			module_put(dvbdev->adapter->module);
+		return ret;
+	}
+
+	/* binary compatiblity cruft */
+	case __NET_ADD_IF_OLD:
+	{
+		struct __dvb_net_if_old *dvbnetif=(struct __dvb_net_if_old *)parg;
+		int result;
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		if (!try_module_get(dvbdev->adapter->module))
+			return -EPERM;
+
+		result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE);
+		if (result<0) {
+			module_put(dvbdev->adapter->module);
+			return result;
+		}
+		dvbnetif->if_num=result;
+		break;
+	}
+	case __NET_GET_IF_OLD:
+	{
+		struct net_device *netdev;
+		struct dvb_net_priv *priv_data;
+		struct __dvb_net_if_old *dvbnetif=(struct __dvb_net_if_old *)parg;
+
+		if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
+		    !dvbnet->state[dvbnetif->if_num])
+			return -EINVAL;
+
+		netdev = dvbnet->device[dvbnetif->if_num];
+
+		priv_data=(struct dvb_net_priv*)netdev->priv;
+		dvbnetif->pid=priv_data->pid;
+		break;
+	}
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int dvb_net_ioctl(struct inode *inode, struct file *file,
+	      unsigned int cmd, unsigned long arg)
+{
+	return dvb_usercopy(inode, file, cmd, arg, dvb_net_do_ioctl);
+}
+
+static struct file_operations dvb_net_fops = {
+	.owner = THIS_MODULE,
+	.ioctl = dvb_net_ioctl,
+	.open =	dvb_generic_open,
+	.release = dvb_generic_release,
+};
+
+static struct dvb_device dvbdev_net = {
+        .priv = NULL,
+        .users = 1,
+        .writers = 1,
+        .fops = &dvb_net_fops,
+};
+
+
+void dvb_net_release (struct dvb_net *dvbnet)
+{
+	int i;
+
+	dvb_unregister_device(dvbnet->dvbdev);
+
+	for (i=0; i<DVB_NET_DEVICES_MAX; i++) {
+		if (!dvbnet->state[i])
+			continue;
+		dvb_net_remove_if(dvbnet, i);
+	}
+}
+EXPORT_SYMBOL(dvb_net_release);
+
+
+int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
+		  struct dmx_demux *dmx)
+{
+	int i;
+
+	dvbnet->demux = dmx;
+
+	for (i=0; i<DVB_NET_DEVICES_MAX; i++)
+		dvbnet->state[i] = 0;
+
+	dvb_register_device (adap, &dvbnet->dvbdev, &dvbdev_net,
+			     dvbnet, DVB_DEVICE_NET);
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_net_init);
diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h
new file mode 100644
index 0000000..f14e4ca
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_net.h
@@ -0,0 +1,46 @@
+/*
+ * dvb_net.h
+ *
+ * Copyright (C) 2001 Ralph Metzler for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DVB_NET_H_
+#define _DVB_NET_H_
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "dvbdev.h"
+
+#define DVB_NET_DEVICES_MAX 10
+
+struct dvb_net {
+	struct dvb_device *dvbdev;
+	struct net_device *device[DVB_NET_DEVICES_MAX];
+	int state[DVB_NET_DEVICES_MAX];
+	struct dmx_demux *demux;
+};
+
+
+void dvb_net_release(struct dvb_net *);
+int  dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *);
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
new file mode 100644
index 0000000..fb6d94a
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -0,0 +1,270 @@
+/*
+ *
+ * dvb_ringbuffer.c: ring buffer implementation for the dvb driver
+ *
+ * Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+
+#define __KERNEL_SYSCALLS__
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include "dvb_ringbuffer.h"
+
+#define PKT_READY 0
+#define PKT_DISPOSED 1
+
+
+void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
+{
+        rbuf->pread=rbuf->pwrite=0;
+        rbuf->data=data;
+        rbuf->size=len;
+
+        init_waitqueue_head(&rbuf->queue);
+
+        spin_lock_init(&(rbuf->lock));
+}
+
+
+
+int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
+{
+        return (rbuf->pread==rbuf->pwrite);
+}
+
+
+
+ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
+{
+        ssize_t free;
+
+        free = rbuf->pread - rbuf->pwrite;
+        if (free <= 0)
+                free += rbuf->size;
+        return free-1;
+}
+
+
+
+ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
+{
+        ssize_t avail;
+
+        avail = rbuf->pwrite - rbuf->pread;
+        if (avail < 0)
+                avail += rbuf->size;
+        return avail;
+}
+
+
+
+void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
+{
+        rbuf->pread = rbuf->pwrite;
+}
+
+
+
+void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
+{
+        unsigned long flags;
+
+        spin_lock_irqsave(&rbuf->lock, flags);
+        dvb_ringbuffer_flush(rbuf);
+        spin_unlock_irqrestore(&rbuf->lock, flags);
+
+        wake_up(&rbuf->queue);
+}
+
+
+
+ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem)
+{
+        size_t todo = len;
+        size_t split;
+
+        split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
+        if (split > 0) {
+                if (!usermem)
+                        memcpy(buf, rbuf->data+rbuf->pread, split);
+                else
+                        if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
+                                return -EFAULT;
+                buf += split;
+                todo -= split;
+                rbuf->pread = 0;
+        }
+        if (!usermem)
+                memcpy(buf, rbuf->data+rbuf->pread, todo);
+        else
+                if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
+                        return -EFAULT;
+
+        rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+
+        return len;
+}
+
+
+
+ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
+{
+        size_t todo = len;
+        size_t split;
+
+        split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
+
+        if (split > 0) {
+                memcpy(rbuf->data+rbuf->pwrite, buf, split);
+                buf += split;
+                todo -= split;
+                rbuf->pwrite = 0;
+        }
+        memcpy(rbuf->data+rbuf->pwrite, buf, todo);
+        rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+
+        return len;
+}
+
+ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
+{
+        int status;
+        ssize_t oldpwrite = rbuf->pwrite;
+
+        DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+        DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+        DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
+        status = dvb_ringbuffer_write(rbuf, buf, len);
+
+        if (status < 0) rbuf->pwrite = oldpwrite;
+        return status;
+}
+
+ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+                                int offset, u8* buf, size_t len, int usermem)
+{
+        size_t todo;
+        size_t split;
+        size_t pktlen;
+
+        pktlen = rbuf->data[idx] << 8;
+        pktlen |= rbuf->data[(idx + 1) % rbuf->size];
+        if (offset > pktlen) return -EINVAL;
+        if ((offset + len) > pktlen) len = pktlen - offset;
+
+        idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
+        todo = len;
+        split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
+        if (split > 0) {
+                if (!usermem)
+                        memcpy(buf, rbuf->data+idx, split);
+                else
+                        if (copy_to_user(buf, rbuf->data+idx, split))
+                                return -EFAULT;
+                buf += split;
+                todo -= split;
+                idx = 0;
+        }
+        if (!usermem)
+                memcpy(buf, rbuf->data+idx, todo);
+        else
+                if (copy_to_user(buf, rbuf->data+idx, todo))
+                        return -EFAULT;
+
+        return len;
+}
+
+void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
+{
+        size_t pktlen;
+
+        rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
+
+        // clean up disposed packets
+        while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
+                if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
+                        pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
+                        pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
+                        DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
+                } else {
+                        // first packet is not disposed, so we stop cleaning now
+                        break;
+                }
+        }
+}
+
+ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
+{
+        int consumed;
+        int curpktlen;
+        int curpktstatus;
+
+        if (idx == -1) {
+	       idx = rbuf->pread;
+	} else {
+                curpktlen = rbuf->data[idx] << 8;
+                curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+	        idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+	}
+
+        consumed = (idx - rbuf->pread) % rbuf->size;
+
+        while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
+
+                curpktlen = rbuf->data[idx] << 8;
+                curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+                curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
+
+                if (curpktstatus == PKT_READY) {
+                        *pktlen = curpktlen;
+                        return idx;
+                }
+
+                consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
+                idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+        }
+
+        // no packets available
+        return -1;
+}
+
+
+
+EXPORT_SYMBOL(dvb_ringbuffer_init);
+EXPORT_SYMBOL(dvb_ringbuffer_empty);
+EXPORT_SYMBOL(dvb_ringbuffer_free);
+EXPORT_SYMBOL(dvb_ringbuffer_avail);
+EXPORT_SYMBOL(dvb_ringbuffer_flush);
+EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
+EXPORT_SYMBOL(dvb_ringbuffer_read);
+EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
new file mode 100644
index 0000000..d18e9c4
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -0,0 +1,173 @@
+/*
+ *
+ * dvb_ringbuffer.h: ring buffer implementation for the dvb driver
+ *
+ * Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
+ *                         for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DVB_RINGBUFFER_H_
+#define _DVB_RINGBUFFER_H_
+
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+struct dvb_ringbuffer {
+        u8               *data;
+        ssize_t           size;
+        ssize_t           pread;
+        ssize_t           pwrite;
+
+        wait_queue_head_t queue;
+        spinlock_t        lock;
+};
+
+#define DVB_RINGBUFFER_PKTHDRSIZE 3
+
+
+/*
+** Notes:
+** ------
+** (1) For performance reasons read and write routines don't check buffer sizes
+**     and/or number of bytes free/available. This has to be done before these
+**     routines are called. For example:
+**
+**     *** write <buflen> bytes ***
+**     free = dvb_ringbuffer_free(rbuf);
+**     if (free >= buflen)
+**         count = dvb_ringbuffer_write(rbuf, buffer, buflen);
+**     else
+**         ...
+**
+**     *** read min. 1000, max. <bufsize> bytes ***
+**     avail = dvb_ringbuffer_avail(rbuf);
+**     if (avail >= 1000)
+**         count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize), 0);
+**     else
+**         ...
+**
+** (2) If there is exactly one reader and one writer, there is no need
+**     to lock read or write operations.
+**     Two or more readers must be locked against each other.
+**     Flushing the buffer counts as a read operation.
+**     Two or more writers must be locked against each other.
+*/
+
+/* initialize ring buffer, lock and queue */
+extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len);
+
+/* test whether buffer is empty */
+extern int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf);
+
+/* return the number of free bytes in the buffer */
+extern ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf);
+
+/* return the number of bytes waiting in the buffer */
+extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf);
+
+
+/* read routines & macros */
+/* ---------------------- */
+/* flush buffer */
+extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf);
+
+/* flush buffer protected by spinlock and wake-up waiting task(s) */
+extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf);
+
+/* peek at byte <offs> in the buffer */
+#define DVB_RINGBUFFER_PEEK(rbuf,offs)	\
+			(rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size]
+
+/* advance read ptr by <num> bytes */
+#define DVB_RINGBUFFER_SKIP(rbuf,num)	\
+			(rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size
+
+/*
+** read <len> bytes from ring buffer into <buf>
+** <usermem> specifies whether <buf> resides in user space
+** returns number of bytes transferred or -EFAULT
+*/
+extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
+                                   size_t len, int usermem);
+
+
+/* write routines & macros */
+/* ----------------------- */
+/* write single byte to ring buffer */
+#define DVB_RINGBUFFER_WRITE_BYTE(rbuf,byte)	\
+			{ (rbuf)->data[(rbuf)->pwrite]=(byte); \
+			(rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; }
+/*
+** write <len> bytes to ring buffer
+** <usermem> specifies whether <buf> resides in user space
+** returns number of bytes transferred or -EFAULT
+*/
+extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
+                                    size_t len);
+
+
+/**
+ * Write a packet into the ringbuffer.
+ *
+ * <rbuf> Ringbuffer to write to.
+ * <buf> Buffer to write.
+ * <len> Length of buffer (currently limited to 65535 bytes max).
+ * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL.
+ */
+extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf,
+                                        size_t len);
+
+/**
+ * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this
+ * does NOT update the read pointer in the ringbuffer. You must use
+ * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ * <offset> Offset into packet to read from.
+ * <buf> Destination buffer for data.
+ * <len> Size of destination buffer.
+ * <usermem> Set to 1 if <buf> is in userspace.
+ * returns Number of bytes read, or -EFAULT.
+ */
+extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+                                       int offset, u8* buf, size_t len, int usermem);
+
+/**
+ * Dispose of a packet in the ring buffer.
+ *
+ * <rbuf> Ring buffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ */
+extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx);
+
+/**
+ * Get the index of the next packet in a ringbuffer.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Previous packet index, or -1 to return the first packet index.
+ * <pktlen> On success, will be updated to contain the length of the packet in bytes.
+ * returns Packet index (if >=0), or -1 if no packets available.
+ */
+extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
+
+
+#endif /* _DVB_RINGBUFFER_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
new file mode 100644
index 0000000..cf4ffe3
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -0,0 +1,449 @@
+/*
+ * dvbdev.c
+ *
+ * Copyright (C) 2000 Ralph  Metzler <ralph@convergence.de>
+ *                  & Marcus Metzler <marcus@convergence.de>
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+
+#include "dvbdev.h"
+
+static int dvbdev_debug;
+
+module_param(dvbdev_debug, int, 0644);
+MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
+
+#define dprintk if (dvbdev_debug) printk
+
+static LIST_HEAD(dvb_adapter_list);
+static DECLARE_MUTEX(dvbdev_register_lock);
+
+static const char * const dnames[] = {
+        "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
+	"net", "osd"
+};
+
+#define DVB_MAX_ADAPTERS	8
+#define DVB_MAX_IDS		4
+#define nums2minor(num,type,id)	((num << 6) | (id << 4) | type)
+#define MAX_DVB_MINORS		(DVB_MAX_ADAPTERS*64)
+
+struct class_simple *dvb_class;
+EXPORT_SYMBOL(dvb_class);
+
+static struct dvb_device* dvbdev_find_device (int minor)
+{
+	struct list_head *entry;
+
+	list_for_each (entry, &dvb_adapter_list) {
+		struct list_head *entry0;
+		struct dvb_adapter *adap;
+		adap = list_entry (entry, struct dvb_adapter, list_head);
+		list_for_each (entry0, &adap->device_list) {
+			struct dvb_device *dev;
+			dev = list_entry (entry0, struct dvb_device, list_head);
+			if (nums2minor(adap->num, dev->type, dev->id) == minor)
+				return dev;
+		}
+	}
+
+	return NULL;
+}
+
+
+static int dvb_device_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev;
+
+	dvbdev = dvbdev_find_device (iminor(inode));
+
+	if (dvbdev && dvbdev->fops) {
+		int err = 0;
+		struct file_operations *old_fops;
+
+		file->private_data = dvbdev;
+		old_fops = file->f_op;
+                file->f_op = fops_get(dvbdev->fops);
+                if(file->f_op->open)
+                        err = file->f_op->open(inode,file);
+                if (err) {
+                        fops_put(file->f_op);
+                        file->f_op = fops_get(old_fops);
+                }
+                fops_put(old_fops);
+                return err;
+	}
+	return -ENODEV;
+}
+
+
+static struct file_operations dvb_device_fops =
+{
+	.owner =	THIS_MODULE,
+	.open =		dvb_device_open,
+};
+
+static struct cdev dvb_device_cdev = {
+	.kobj   = {.name = "dvb", },
+	.owner  =       THIS_MODULE,
+};
+
+int dvb_generic_open(struct inode *inode, struct file *file)
+{
+        struct dvb_device *dvbdev = file->private_data;
+
+        if (!dvbdev)
+                return -ENODEV;
+
+	if (!dvbdev->users)
+                return -EBUSY;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+                if (!dvbdev->readers)
+		        return -EBUSY;
+		dvbdev->readers--;
+	} else {
+                if (!dvbdev->writers)
+		        return -EBUSY;
+		dvbdev->writers--;
+	}
+
+	dvbdev->users--;
+	return 0;
+}
+EXPORT_SYMBOL(dvb_generic_open);
+
+
+int dvb_generic_release(struct inode *inode, struct file *file)
+{
+        struct dvb_device *dvbdev = file->private_data;
+
+	if (!dvbdev)
+                return -ENODEV;
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+		dvbdev->readers++;
+	} else {
+		dvbdev->writers++;
+	}
+
+	dvbdev->users++;
+	return 0;
+}
+EXPORT_SYMBOL(dvb_generic_release);
+
+
+int dvb_generic_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg)
+{
+        struct dvb_device *dvbdev = file->private_data;
+
+        if (!dvbdev)
+	        return -ENODEV;
+
+	if (!dvbdev->kernel_ioctl)
+		return -EINVAL;
+
+	return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl);
+}
+EXPORT_SYMBOL(dvb_generic_ioctl);
+
+
+static int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
+{
+	u32 id = 0;
+
+	while (id < DVB_MAX_IDS) {
+		struct list_head *entry;
+		list_for_each (entry, &adap->device_list) {
+			struct dvb_device *dev;
+			dev = list_entry (entry, struct dvb_device, list_head);
+			if (dev->type == type && dev->id == id)
+				goto skip;
+		}
+		return id;
+skip:
+		id++;
+	}
+	return -ENFILE;
+}
+
+
+int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
+			const struct dvb_device *template, void *priv, int type)
+{
+	struct dvb_device *dvbdev;
+	int id;
+
+	if (down_interruptible (&dvbdev_register_lock))
+		return -ERESTARTSYS;
+
+	if ((id = dvbdev_get_free_id (adap, type)) < 0) {
+		up (&dvbdev_register_lock);
+		*pdvbdev = NULL;
+		printk ("%s: could get find free device id...\n", __FUNCTION__);
+		return -ENFILE;
+	}
+
+	*pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
+
+	if (!dvbdev) {
+		up(&dvbdev_register_lock);
+		return -ENOMEM;
+	}
+
+	up (&dvbdev_register_lock);
+
+	memcpy(dvbdev, template, sizeof(struct dvb_device));
+	dvbdev->type = type;
+	dvbdev->id = id;
+	dvbdev->adapter = adap;
+	dvbdev->priv = priv;
+
+	dvbdev->fops->owner = adap->module;
+
+	list_add_tail (&dvbdev->list_head, &adap->device_list);
+
+	devfs_mk_cdev(MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
+			S_IFCHR | S_IRUSR | S_IWUSR,
+			"dvb/adapter%d/%s%d", adap->num, dnames[type], id);
+
+	class_simple_device_add(dvb_class, MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
+				NULL, "dvb%d.%s%d", adap->num, dnames[type], id);
+
+	dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
+		adap->num, dnames[type], id, nums2minor(adap->num, type, id),
+		nums2minor(adap->num, type, id));
+
+	return 0;
+}
+EXPORT_SYMBOL(dvb_register_device);
+
+
+void dvb_unregister_device(struct dvb_device *dvbdev)
+{
+	if (!dvbdev)
+		return;
+
+	devfs_remove("dvb/adapter%d/%s%d", dvbdev->adapter->num,
+			dnames[dvbdev->type], dvbdev->id);
+
+	class_simple_device_remove(MKDEV(DVB_MAJOR, nums2minor(dvbdev->adapter->num,
+					dvbdev->type, dvbdev->id)));
+
+	list_del (&dvbdev->list_head);
+	kfree (dvbdev);
+}
+EXPORT_SYMBOL(dvb_unregister_device);
+
+
+static int dvbdev_get_free_adapter_num (void)
+{
+	int num = 0;
+
+	while (num < DVB_MAX_ADAPTERS) {
+		struct list_head *entry;
+		list_for_each (entry, &dvb_adapter_list) {
+			struct dvb_adapter *adap;
+			adap = list_entry (entry, struct dvb_adapter, list_head);
+			if (adap->num == num)
+				goto skip;
+		}
+		return num;
+skip:
+		num++;
+	}
+
+	return -ENFILE;
+}
+
+
+int dvb_register_adapter(struct dvb_adapter **padap, const char *name, struct module *module)
+{
+	struct dvb_adapter *adap;
+	int num;
+
+	if (down_interruptible (&dvbdev_register_lock))
+		return -ERESTARTSYS;
+
+	if ((num = dvbdev_get_free_adapter_num ()) < 0) {
+		up (&dvbdev_register_lock);
+		return -ENFILE;
+	}
+
+	if (!(*padap = adap = kmalloc(sizeof(struct dvb_adapter), GFP_KERNEL))) {
+		up(&dvbdev_register_lock);
+		return -ENOMEM;
+	}
+
+	memset (adap, 0, sizeof(struct dvb_adapter));
+	INIT_LIST_HEAD (&adap->device_list);
+
+	printk ("DVB: registering new adapter (%s).\n", name);
+
+	devfs_mk_dir("dvb/adapter%d", num);
+	adap->num = num;
+	adap->name = name;
+	adap->module = module;
+
+	list_add_tail (&adap->list_head, &dvb_adapter_list);
+
+	up (&dvbdev_register_lock);
+
+	return num;
+}
+EXPORT_SYMBOL(dvb_register_adapter);
+
+
+int dvb_unregister_adapter(struct dvb_adapter *adap)
+{
+	devfs_remove("dvb/adapter%d", adap->num);
+
+	if (down_interruptible (&dvbdev_register_lock))
+		return -ERESTARTSYS;
+	list_del (&adap->list_head);
+	up (&dvbdev_register_lock);
+	kfree (adap);
+	return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_adapter);
+
+/* if the miracle happens and "generic_usercopy()" is included into
+   the kernel, then this can vanish. please don't make the mistake and
+   define this as video_usercopy(). this will introduce a dependecy
+   to the v4l "videodev.o" module, which is unnecessary for some
+   cards (ie. the budget dvb-cards don't need the v4l module...) */
+int dvb_usercopy(struct inode *inode, struct file *file,
+	             unsigned int cmd, unsigned long arg,
+		     int (*func)(struct inode *inode, struct file *file,
+		     unsigned int cmd, void *arg))
+{
+        char    sbuf[128];
+        void    *mbuf = NULL;
+        void    *parg = NULL;
+        int     err  = -EINVAL;
+
+        /*  Copy arguments into temp kernel buffer  */
+        switch (_IOC_DIR(cmd)) {
+        case _IOC_NONE:
+		/*
+		 * For this command, the pointer is actually an integer
+		 * argument.
+		 */
+		parg = (void *) arg;
+		break;
+        case _IOC_READ: /* some v4l ioctls are marked wrong ... */
+        case _IOC_WRITE:
+        case (_IOC_WRITE | _IOC_READ):
+                if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+                        parg = sbuf;
+                } else {
+                        /* too big to allocate from stack */
+                        mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
+                        if (NULL == mbuf)
+                                return -ENOMEM;
+                        parg = mbuf;
+                }
+
+                err = -EFAULT;
+                if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
+                        goto out;
+                break;
+        }
+
+        /* call driver */
+        if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
+                err = -EINVAL;
+
+        if (err < 0)
+                goto out;
+
+        /*  Copy results into user buffer  */
+        switch (_IOC_DIR(cmd))
+        {
+        case _IOC_READ:
+        case (_IOC_WRITE | _IOC_READ):
+                if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+                        err = -EFAULT;
+                break;
+        }
+
+out:
+        kfree(mbuf);
+        return err;
+}
+
+static int __init init_dvbdev(void)
+{
+	int retval;
+	dev_t dev = MKDEV(DVB_MAJOR, 0);
+
+	if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
+		printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
+		return retval;
+	}
+
+	cdev_init(&dvb_device_cdev, &dvb_device_fops);
+	if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
+		printk("dvb-core: unable to get major %d\n", DVB_MAJOR);
+		goto error;
+	}
+
+	devfs_mk_dir("dvb");
+
+	dvb_class = class_simple_create(THIS_MODULE, "dvb");
+	if (IS_ERR(dvb_class)) {
+		retval = PTR_ERR(dvb_class);
+		goto error;
+	}
+	return 0;
+
+error:
+	cdev_del(&dvb_device_cdev);
+	unregister_chrdev_region(dev, MAX_DVB_MINORS);
+	return retval;
+}
+
+
+static void __exit exit_dvbdev(void)
+{
+        devfs_remove("dvb");
+	class_simple_destroy(dvb_class);
+	cdev_del(&dvb_device_cdev);
+        unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
+}
+
+module_init(init_dvbdev);
+module_exit(exit_dvbdev);
+
+MODULE_DESCRIPTION("DVB Core Driver");
+MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
new file mode 100644
index 0000000..184edba
--- /dev/null
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -0,0 +1,104 @@
+/*
+ * dvbdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ *                    for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Lesser Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DVBDEV_H_
+#define _DVBDEV_H_
+
+#include <linux/types.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/smp_lock.h>
+
+#define DVB_MAJOR 212
+
+#define DVB_DEVICE_VIDEO      0
+#define DVB_DEVICE_AUDIO      1
+#define DVB_DEVICE_SEC        2
+#define DVB_DEVICE_FRONTEND   3
+#define DVB_DEVICE_DEMUX      4
+#define DVB_DEVICE_DVR        5
+#define DVB_DEVICE_CA         6
+#define DVB_DEVICE_NET        7
+#define DVB_DEVICE_OSD        8
+
+
+struct dvb_adapter {
+	int num;
+	struct list_head list_head;
+	struct list_head device_list;
+	const char *name;
+	u8 proposed_mac [6];
+	void* priv;
+
+	struct module *module;
+};
+
+
+struct dvb_device {
+	struct list_head list_head;
+	struct file_operations *fops;
+	struct dvb_adapter *adapter;
+	int type;
+	u32 id;
+
+	/* in theory, 'users' can vanish now,
+	   but I don't want to change too much now... */
+	int readers;
+	int writers;
+	int users;
+
+        /* don't really need those !? -- FIXME: use video_usercopy  */
+        int (*kernel_ioctl)(struct inode *inode, struct file *file,
+			    unsigned int cmd, void *arg);
+
+	void *priv;
+};
+
+
+extern int dvb_register_adapter (struct dvb_adapter **padap, const char *name, struct module *module);
+extern int dvb_unregister_adapter (struct dvb_adapter *adap);
+
+extern int dvb_register_device (struct dvb_adapter *adap,
+				struct dvb_device **pdvbdev,
+				const struct dvb_device *template,
+				void *priv,
+				int type);
+
+extern void dvb_unregister_device (struct dvb_device *dvbdev);
+
+extern int dvb_generic_open (struct inode *inode, struct file *file);
+extern int dvb_generic_release (struct inode *inode, struct file *file);
+extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
+			      unsigned int cmd, unsigned long arg);
+
+/* we don't mess with video_usercopy() any more,
+we simply define out own dvb_usercopy(), which will hopefully become
+generic_usercopy()  someday... */
+
+extern int dvb_usercopy(struct inode *inode, struct file *file,
+	                    unsigned int cmd, unsigned long arg,
+			    int (*func)(struct inode *inode, struct file *file,
+			    unsigned int cmd, void *arg));
+
+#endif /* #ifndef _DVBDEV_H_ */
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
new file mode 100644
index 0000000..0bfd4df
--- /dev/null
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -0,0 +1,172 @@
+menu "Customise DVB Frontends"
+	depends on DVB_CORE
+
+comment "DVB-S (satellite) frontends"
+	depends on DVB_CORE
+
+config DVB_STV0299
+	tristate "ST STV0299 based"
+	depends on DVB_CORE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX24110
+	tristate "Conexant CX24110 based"
+ 	depends on DVB_CORE
+ 	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+ 
+config DVB_TDA8083
+	tristate "Philips TDA8083 based"
+	depends on DVB_CORE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA80XX
+	tristate "Philips TDA8044 or TDA8083 based"
+	depends on DVB_CORE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_MT312
+	tristate "Zarlink MT312 based"
+	depends on DVB_CORE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_VES1X93
+	tristate "VLSI VES1893 or VES1993 based"
+	depends on DVB_CORE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+comment "DVB-T (terrestrial) frontends"
+	depends on DVB_CORE
+
+config DVB_SP8870
+ 	tristate "Spase sp8870 based"
+	depends on DVB_CORE
+	select FW_LOADER
+	help
+ 	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+	  This driver needs external firmware. Please use the command
+	  "<kerneldir>/Documentation/dvb/get_dvb_firmware sp8870" to
+	  download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+
+config DVB_SP887X
+ 	tristate "Spase sp887x based"
+	depends on DVB_CORE
+	select FW_LOADER
+	help
+	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+	  This driver needs external firmware. Please use the command
+	  "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+	  download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+
+config DVB_CX22700
+	tristate "Conexant CX22700 based"
+	depends on DVB_CORE
+	help
+	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX22702
+ 	tristate "Conexant cx22702 demodulator (OFDM)"
+ 	depends on DVB_CORE
+ 	help
+ 	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_L64781
+	tristate "LSI L64781"
+	depends on DVB_CORE
+	help
+	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA1004X
+	tristate "Philips TDA10045H/TDA10046H based"
+	depends on DVB_CORE
+	select FW_LOADER
+	help
+	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+	  This driver needs external firmware. Please use the commands
+	  "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
+  	  "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+	  download/extract them, and then copy them to /usr/lib/hotplug/firmware.
+
+config DVB_NXT6000
+	tristate "NxtWave Communications NXT6000 based"
+	depends on DVB_CORE
+	help
+	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_MT352
+	tristate "Zarlink MT352 based"
+	depends on DVB_CORE
+	help
+	  A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_DIB3000MB
+	tristate "DiBcom 3000M-B"
+	depends on DVB_CORE
+	help
+	  A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+	  to support this frontend.
+
+config DVB_DIB3000MC
+	tristate "DiBcom 3000P/M-C"
+	depends on DVB_CORE
+	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
+
+config DVB_ATMEL_AT76C651
+	tristate "Atmel AT76C651 based"
+	depends on DVB_CORE
+        help
+ 	  A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_VES1820
+	tristate "VLSI VES1820 based"
+	depends on DVB_CORE
+	help
+ 	  A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10021
+	tristate "Philips TDA10021 based"
+	depends on DVB_CORE
+	help
+ 	  A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_STV0297
+	tristate "ST STV0297 based"
+	depends on DVB_CORE
+	help
+	  A DVB-C tuner module. Say Y when you want to support this frontend.
+
+comment "ATSC (North American/Korean Terresterial DTV) frontends"
+	depends on DVB_CORE
+
+config DVB_NXT2002
+	tristate "Nxt2002 based"
+	depends on DVB_CORE
+	select FW_LOADER
+	help
+	  An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
+
+config DVB_OR51132
+	tristate "OR51132 based (pcHDTV)"
+	depends on DVB_CORE
+
+config DVB_OR51211
+	tristate "or51211 based (pcHDTV HD2000 card)"
+	depends on DVB_CORE
+	select FW_LOADER
+	help
+	  An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
+
+endmenu
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
new file mode 100644
index 0000000..7f87848
--- /dev/null
+++ b/drivers/media/dvb/frontends/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the kernel DVB frontend device drivers.
+#
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
+
+obj-$(CONFIG_DVB_CORE) += dvb-pll.o
+obj-$(CONFIG_DVB_STV0299) += stv0299.o
+obj-$(CONFIG_DVB_SP8870) += sp8870.o
+obj-$(CONFIG_DVB_CX22700) += cx22700.o
+obj-$(CONFIG_DVB_ATMEL_AT76C651) += at76c651.o
+obj-$(CONFIG_DVB_CX24110) += cx24110.o
+obj-$(CONFIG_DVB_TDA8083) += tda8083.o
+obj-$(CONFIG_DVB_L64781) += l64781.o
+obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o dib3000-common.o
+obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dib3000-common.o
+obj-$(CONFIG_DVB_MT312) += mt312.o
+obj-$(CONFIG_DVB_VES1820) += ves1820.o
+obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
+obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o
+obj-$(CONFIG_DVB_SP887X) += sp887x.o
+obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
+obj-$(CONFIG_DVB_MT352) += mt352.o
+obj-$(CONFIG_DVB_CX22702) += cx22702.o
+obj-$(CONFIG_DVB_TDA80XX) += tda80xx.o
+obj-$(CONFIG_DVB_TDA10021) += tda10021.o
+obj-$(CONFIG_DVB_STV0297) += stv0297.o
+obj-$(CONFIG_DVB_NXT2002) += nxt2002.o
+obj-$(CONFIG_DVB_OR51211) += or51211.o
+obj-$(CONFIG_DVB_OR51132) += or51132.o
diff --git a/drivers/media/dvb/frontends/at76c651.c b/drivers/media/dvb/frontends/at76c651.c
new file mode 100644
index 0000000..ce2eaa1
--- /dev/null
+++ b/drivers/media/dvb/frontends/at76c651.c
@@ -0,0 +1,450 @@
+/*
+ * at76c651.c
+ *
+ * Atmel DVB-C Frontend Driver (at76c651/tua6010xs)
+ *
+ * Copyright (C) 2001 fnbrd <fnbrd@gmx.de>
+ *             & 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *             & 2003 Wolfram Joost <dbox2@frokaschwei.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; 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.
+ *
+ * AT76C651
+ * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf
+ * http://www.atmel.com/atmel/acrobat/doc1320.pdf
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include "dvb_frontend.h"
+#include "at76c651.h"
+
+
+struct at76c651_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct at76c651_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* revision of the chip */
+	u8 revision;
+
+	/* last QAM value set */
+	u8 qam;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "at76c651: " args); \
+	} while (0)
+
+
+#if ! defined(__powerpc__)
+static __inline__ int __ilog2(unsigned long x)
+{
+	int i;
+
+	if (x == 0)
+		return -1;
+
+	for (i = 0; x != 0; i++)
+		x >>= 1;
+
+	return i - 1;
+}
+#endif
+
+static int at76c651_writereg(struct at76c651_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg =
+		{ .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: writereg error "
+			"(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+			__FUNCTION__, reg, data, ret);
+
+	msleep(10);
+
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static u8 at76c651_readreg(struct at76c651_state* state, u8 reg)
+{
+	int ret;
+	u8 val;
+	struct i2c_msg msg[] = {
+		{ .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },
+		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = &val, .len = 1 }
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+	return val;
+}
+
+static int at76c651_reset(struct at76c651_state* state)
+{
+	return at76c651_writereg(state, 0x07, 0x01);
+}
+
+static void at76c651_disable_interrupts(struct at76c651_state* state)
+{
+	at76c651_writereg(state, 0x0b, 0x00);
+}
+
+static int at76c651_set_auto_config(struct at76c651_state *state)
+{
+	/*
+	 * Autoconfig
+	 */
+
+	at76c651_writereg(state, 0x06, 0x01);
+
+	/*
+	 * Performance optimizations, should be done after autoconfig
+	 */
+
+	at76c651_writereg(state, 0x10, 0x06);
+	at76c651_writereg(state, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10);
+	at76c651_writereg(state, 0x15, 0x28);
+	at76c651_writereg(state, 0x20, 0x09);
+	at76c651_writereg(state, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90);
+	at76c651_writereg(state, 0x30, 0x90);
+	if (state->qam == 5)
+		at76c651_writereg(state, 0x35, 0x2A);
+
+	/*
+	 * Initialize A/D-converter
+	 */
+
+	if (state->revision == 0x11) {
+		at76c651_writereg(state, 0x2E, 0x38);
+		at76c651_writereg(state, 0x2F, 0x13);
+	}
+
+	at76c651_disable_interrupts(state);
+
+	/*
+	 * Restart operation
+	 */
+
+	at76c651_reset(state);
+
+	return 0;
+}
+
+static void at76c651_set_bbfreq(struct at76c651_state* state)
+{
+	at76c651_writereg(state, 0x04, 0x3f);
+	at76c651_writereg(state, 0x05, 0xee);
+}
+
+static int at76c651_set_symbol_rate(struct at76c651_state* state, u32 symbol_rate)
+{
+	u8 exponent;
+	u32 mantissa;
+
+	if (symbol_rate > 9360000)
+		return -EINVAL;
+
+	/*
+	 * FREF = 57800 kHz
+	 * exponent = 10 + floor (log2(symbol_rate / FREF))
+	 * mantissa = (symbol_rate / FREF) * (1 << (30 - exponent))
+	 */
+
+	exponent = __ilog2((symbol_rate << 4) / 903125);
+	mantissa = ((symbol_rate / 3125) * (1 << (24 - exponent))) / 289;
+
+	at76c651_writereg(state, 0x00, mantissa >> 13);
+	at76c651_writereg(state, 0x01, mantissa >> 5);
+	at76c651_writereg(state, 0x02, (mantissa << 3) | exponent);
+
+	return 0;
+}
+
+static int at76c651_set_qam(struct at76c651_state *state, fe_modulation_t qam)
+{
+	switch (qam) {
+	case QPSK:
+		state->qam = 0x02;
+		break;
+	case QAM_16:
+		state->qam = 0x04;
+		break;
+	case QAM_32:
+		state->qam = 0x05;
+		break;
+	case QAM_64:
+		state->qam = 0x06;
+		break;
+	case QAM_128:
+		state->qam = 0x07;
+		break;
+	case QAM_256:
+		state->qam = 0x08;
+		break;
+#if 0
+	case QAM_512:
+		state->qam = 0x09;
+		break;
+	case QAM_1024:
+		state->qam = 0x0A;
+		break;
+#endif
+	default:
+		return -EINVAL;
+
+	}
+
+	return at76c651_writereg(state, 0x03, state->qam);
+}
+
+static int at76c651_set_inversion(struct at76c651_state* state, fe_spectral_inversion_t inversion)
+{
+	u8 feciqinv = at76c651_readreg(state, 0x60);
+
+	switch (inversion) {
+	case INVERSION_OFF:
+		feciqinv |= 0x02;
+		feciqinv &= 0xFE;
+		break;
+
+	case INVERSION_ON:
+		feciqinv |= 0x03;
+		break;
+
+	case INVERSION_AUTO:
+		feciqinv &= 0xFC;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return at76c651_writereg(state, 0x60, feciqinv);
+}
+
+static int at76c651_set_parameters(struct dvb_frontend* fe,
+				   struct dvb_frontend_parameters *p)
+{
+	int ret;
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+	at76c651_writereg(state, 0x0c, 0xc3);
+	state->config->pll_set(fe, p);
+	at76c651_writereg(state, 0x0c, 0xc2);
+
+	if ((ret = at76c651_set_symbol_rate(state, p->u.qam.symbol_rate)))
+		return ret;
+
+	if ((ret = at76c651_set_inversion(state, p->inversion)))
+		return ret;
+
+	return at76c651_set_auto_config(state);
+}
+
+static int at76c651_set_defaults(struct dvb_frontend* fe)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+	at76c651_set_symbol_rate(state, 6900000);
+	at76c651_set_qam(state, QAM_64);
+	at76c651_set_bbfreq(state);
+	at76c651_set_auto_config(state);
+
+	if (state->config->pll_init) {
+		at76c651_writereg(state, 0x0c, 0xc3);
+		state->config->pll_init(fe);
+		at76c651_writereg(state, 0x0c, 0xc2);
+	}
+
+	return 0;
+}
+
+static int at76c651_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+	u8 sync;
+
+	/*
+	 * Bits: FEC, CAR, EQU, TIM, AGC2, AGC1, ADC, PLL (PLL=0)
+	 */
+	sync = at76c651_readreg(state, 0x80);
+	*status = 0;
+
+	if (sync & (0x04 | 0x10))	/* AGC1 || TIM */
+		*status |= FE_HAS_SIGNAL;
+	if (sync & 0x10)		/* TIM */
+		*status |= FE_HAS_CARRIER;
+	if (sync & 0x80)		/* FEC */
+		*status |= FE_HAS_VITERBI;
+	if (sync & 0x40)		/* CAR */
+		*status |= FE_HAS_SYNC;
+	if ((sync & 0xF0) == 0xF0)	/* TIM && EQU && CAR && FEC */
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int at76c651_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+	*ber = (at76c651_readreg(state, 0x81) & 0x0F) << 16;
+	*ber |= at76c651_readreg(state, 0x82) << 8;
+	*ber |= at76c651_readreg(state, 0x83);
+	*ber *= 10;
+
+	return 0;
+}
+
+static int at76c651_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+	u8 gain = ~at76c651_readreg(state, 0x91);
+	*strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int at76c651_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+	*snr = 0xFFFF -
+	    ((at76c651_readreg(state, 0x8F) << 8) |
+	     at76c651_readreg(state, 0x90));
+
+	return 0;
+}
+
+static int at76c651_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+	*ucblocks = at76c651_readreg(state, 0x82);
+
+	return 0;
+}
+
+static int at76c651_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *fesettings)
+{
+        fesettings->min_delay_ms = 50;
+        fesettings->step_size = 0;
+        fesettings->max_drift = 0;
+	return 0;
+}
+
+static void at76c651_release(struct dvb_frontend* fe)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops at76c651_ops;
+
+struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
+				     struct i2c_adapter* i2c)
+{
+	struct at76c651_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct at76c651_state*) kmalloc(sizeof(struct at76c651_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->qam = 0;
+
+	/* check if the demod is there */
+	if (at76c651_readreg(state, 0x0e) != 0x65) goto error;
+
+	/* finalise state setup */
+	state->i2c = i2c;
+	state->revision = at76c651_readreg(state, 0x0f) & 0xfe;
+	memcpy(&state->ops, &at76c651_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops at76c651_ops = {
+
+	.info = {
+		.name = "Atmel AT76C651B DVB-C",
+		.type = FE_QAM,
+		.frequency_min = 48250000,
+		.frequency_max = 863250000,
+		.frequency_stepsize = 62500,
+		/*.frequency_tolerance = */	/* FIXME: 12% of SR */
+		.symbol_rate_min = 0,		/* FIXME */
+		.symbol_rate_max = 9360000,	/* FIXME */
+		.symbol_rate_tolerance = 4000,
+		.caps = FE_CAN_INVERSION_AUTO |
+		    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		    FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+		    FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+		    FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 |
+		    FE_CAN_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER
+	},
+
+	.release = at76c651_release,
+
+	.init = at76c651_set_defaults,
+
+	.set_frontend = at76c651_set_parameters,
+	.get_tune_settings = at76c651_get_tune_settings,
+
+	.read_status = at76c651_read_status,
+	.read_ber = at76c651_read_ber,
+	.read_signal_strength = at76c651_read_signal_strength,
+	.read_snr = at76c651_read_snr,
+	.read_ucblocks = at76c651_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Atmel AT76C651 DVB-C Demodulator Driver");
+MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(at76c651_attach);
diff --git a/drivers/media/dvb/frontends/at76c651.h b/drivers/media/dvb/frontends/at76c651.h
new file mode 100644
index 0000000..34054df
--- /dev/null
+++ b/drivers/media/dvb/frontends/at76c651.h
@@ -0,0 +1,47 @@
+/*
+ * at76c651.c
+ *
+ * Atmel DVB-C Frontend Driver (at76c651)
+ *
+ * Copyright (C) 2001 fnbrd <fnbrd@gmx.de>
+ *             & 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *             & 2003 Wolfram Joost <dbox2@frokaschwei.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; 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.
+ *
+ * AT76C651
+ * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf
+ * http://www.atmel.com/atmel/acrobat/doc1320.pdf
+ */
+
+#ifndef AT76C651_H
+#define AT76C651_H
+
+#include <linux/dvb/frontend.h>
+
+struct at76c651_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
+					    struct i2c_adapter* i2c);
+
+#endif // AT76C651_H
diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c
new file mode 100644
index 0000000..a212279
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22700.c
@@ -0,0 +1,435 @@
+/*
+    Conexant cx22700 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	Holger Waechtler <holger@convergence.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; 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/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "cx22700.h"
+
+
+struct cx22700_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct cx22700_config* config;
+
+	struct dvb_frontend frontend;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "cx22700: " args); \
+	} while (0)
+
+static u8 init_tab [] = {
+	0x04, 0x10,
+	0x05, 0x09,
+	0x06, 0x00,
+	0x08, 0x04,
+	0x09, 0x00,
+	0x0a, 0x01,
+	0x15, 0x40,
+	0x16, 0x10,
+	0x17, 0x87,
+	0x18, 0x17,
+	0x1a, 0x10,
+	0x25, 0x04,
+	0x2e, 0x00,
+	0x39, 0x00,
+	0x3a, 0x04,
+	0x45, 0x08,
+	0x46, 0x02,
+	0x47, 0x05,
+};
+
+
+static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	ret = i2c_transfer (state->i2c, &msg, 1);
+
+	if (ret != 1)
+		printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+			__FUNCTION__, reg, data, ret);
+
+	return (ret != 1) ? -1 : 0;
+}
+
+static int cx22700_readreg (struct cx22700_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+
+	if (ret != 2) return -EIO;
+
+	return b1[0];
+}
+
+static int cx22700_set_inversion (struct cx22700_state* state, int inversion)
+{
+	u8 val;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	switch (inversion) {
+	case INVERSION_AUTO:
+		return -EOPNOTSUPP;
+	case INVERSION_ON:
+		val = cx22700_readreg (state, 0x09);
+		return cx22700_writereg (state, 0x09, val | 0x01);
+	case INVERSION_OFF:
+		val = cx22700_readreg (state, 0x09);
+		return cx22700_writereg (state, 0x09, val & 0xfe);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_parameters *p)
+{
+	static const u8 qam_tab [4] = { 0, 1, 0, 2 };
+	static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 };
+	u8 val;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8)
+		return -EINVAL;
+
+	if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8)
+
+	if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
+		return -EINVAL;
+
+	if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+	    p->guard_interval > GUARD_INTERVAL_1_4)
+		return -EINVAL;
+
+	if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+	    p->transmission_mode != TRANSMISSION_MODE_8K)
+		return -EINVAL;
+
+	if (p->constellation != QPSK &&
+	    p->constellation != QAM_16 &&
+	    p->constellation != QAM_64)
+		return -EINVAL;
+
+	if (p->hierarchy_information < HIERARCHY_NONE ||
+	    p->hierarchy_information > HIERARCHY_4)
+		return -EINVAL;
+
+	if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ)
+		return -EINVAL;
+
+	if (p->bandwidth == BANDWIDTH_7_MHZ)
+		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10));
+	else
+		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10));
+
+	val = qam_tab[p->constellation - QPSK];
+	val |= p->hierarchy_information - HIERARCHY_NONE;
+
+	cx22700_writereg (state, 0x04, val);
+
+	val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
+	val |= fec_tab[p->code_rate_LP - FEC_1_2];
+
+	cx22700_writereg (state, 0x05, val);
+
+	val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2;
+	val |= p->transmission_mode - TRANSMISSION_MODE_2K;
+
+	cx22700_writereg (state, 0x06, val);
+
+	cx22700_writereg (state, 0x08, 0x04 | 0x02);  /* use user tps parameters */
+	cx22700_writereg (state, 0x08, 0x04);         /* restart aquisition */
+
+	return 0;
+}
+
+static int cx22700_get_tps (struct cx22700_state* state, struct dvb_ofdm_parameters *p)
+{
+	static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 };
+	static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4,
+						    FEC_5_6, FEC_7_8 };
+	u8 val;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (!(cx22700_readreg(state, 0x07) & 0x20))  /*  tps valid? */
+		return -EAGAIN;
+
+	val = cx22700_readreg (state, 0x01);
+
+	if ((val & 0x7) > 4)
+		p->hierarchy_information = HIERARCHY_AUTO;
+	else
+		p->hierarchy_information = HIERARCHY_NONE + (val & 0x7);
+
+	if (((val >> 3) & 0x3) > 2)
+		p->constellation = QAM_AUTO;
+	else
+		p->constellation = qam_tab[(val >> 3) & 0x3];
+
+	val = cx22700_readreg (state, 0x02);
+
+	if (((val >> 3) & 0x07) > 4)
+		p->code_rate_HP = FEC_AUTO;
+	else
+		p->code_rate_HP = fec_tab[(val >> 3) & 0x07];
+
+	if ((val & 0x07) > 4)
+		p->code_rate_LP = FEC_AUTO;
+	else
+		p->code_rate_LP = fec_tab[val & 0x07];
+
+	val = cx22700_readreg (state, 0x03);
+
+	p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3);
+	p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1);
+
+	return 0;
+}
+
+static int cx22700_init (struct dvb_frontend* fe)
+
+{	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+	int i;
+
+	dprintk("cx22700_init: init chip\n");
+
+	cx22700_writereg (state, 0x00, 0x02);   /*  soft reset */
+	cx22700_writereg (state, 0x00, 0x00);
+
+	msleep(10);
+
+	for (i=0; i<sizeof(init_tab); i+=2)
+		cx22700_writereg (state, init_tab[i], init_tab[i+1]);
+
+	cx22700_writereg (state, 0x00, 0x01);
+
+	if (state->config->pll_init) {
+		cx22700_writereg (state, 0x0a, 0x00);  /* open i2c bus switch */
+		state->config->pll_init(fe);
+		cx22700_writereg (state, 0x0a, 0x01);  /* close i2c bus switch */
+	}
+
+	return 0;
+}
+
+static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+		   | (cx22700_readreg (state, 0x0e) << 1);
+	u8 sync = cx22700_readreg (state, 0x07);
+
+	*status = 0;
+
+	if (rs_ber < 0xff00)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x20)
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_SYNC;
+
+	if (*status == 0x0f)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	*ber = cx22700_readreg (state, 0x0c) & 0x7f;
+	cx22700_writereg (state, 0x0c, 0x00);
+
+	return 0;
+}
+
+static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+		   | (cx22700_readreg (state, 0x0e) << 1);
+	*signal_strength = ~rs_ber;
+
+	return 0;
+}
+
+static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+		   | (cx22700_readreg (state, 0x0e) << 1);
+	*snr = ~rs_ber;
+
+	return 0;
+}
+
+static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	*ucblocks = cx22700_readreg (state, 0x0f);
+	cx22700_writereg (state, 0x0f, 0x00);
+
+	return 0;
+}
+
+static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/
+	cx22700_writereg (state, 0x00, 0x00);
+
+	cx22700_writereg (state, 0x0a, 0x00);  /* open i2c bus switch */
+	state->config->pll_set(fe, p);
+	cx22700_writereg (state, 0x0a, 0x01);  /* close i2c bus switch */
+	cx22700_set_inversion (state, p->inversion);
+	cx22700_set_tps (state, &p->u.ofdm);
+	cx22700_writereg (state, 0x37, 0x01);  /* PAL loop filter off */
+	cx22700_writereg (state, 0x00, 0x01);  /* restart acquire */
+
+	return 0;
+}
+
+static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+	u8 reg09 = cx22700_readreg (state, 0x09);
+
+	p->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF;
+	return cx22700_get_tps (state, &p->u.ofdm);
+}
+
+static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 150;
+        fesettings->step_size = 166667;
+        fesettings->max_drift = 166667*2;
+        return 0;
+}
+
+static void cx22700_release(struct dvb_frontend* fe)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops cx22700_ops;
+
+struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct cx22700_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct cx22700_state*) kmalloc(sizeof(struct cx22700_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &cx22700_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if (cx22700_readreg(state, 0x07) < 0) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops cx22700_ops = {
+
+	.info = {
+		.name			= "Conexant CX22700 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 470000000,
+		.frequency_max		= 860000000,
+		.frequency_stepsize	= 166667,
+		.caps = 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_RECOVER
+	},
+
+	.release = cx22700_release,
+
+	.init = cx22700_init,
+
+	.set_frontend = cx22700_set_frontend,
+	.get_frontend = cx22700_get_frontend,
+	.get_tune_settings = cx22700_get_tune_settings,
+
+	.read_status = cx22700_read_status,
+	.read_ber = cx22700_read_ber,
+	.read_signal_strength = cx22700_read_signal_strength,
+	.read_snr = cx22700_read_snr,
+	.read_ucblocks = cx22700_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx22700_attach);
diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h
new file mode 100644
index 0000000..c9145b4
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22700.h
@@ -0,0 +1,41 @@
+/*
+    Conexant CX22700 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	Holger Waechtler <holger@convergence.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; 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 CX22700_H
+#define CX22700_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22700_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // CX22700_H
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
new file mode 100644
index 0000000..1930b51
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -0,0 +1,519 @@
+/*
+    Conexant 22702 DVB OFDM demodulator driver
+
+    based on:
+        Alps TDMB7 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	  Holger Waechtler <holger@convergence.de>
+
+    Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk>
+
+    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/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "cx22702.h"
+
+
+struct cx22702_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct cx22702_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* previous uncorrected block counter */
+	u8 prevUCBlocks;
+};
+
+static int debug = 0;
+#define dprintk	if (debug) printk
+
+/* Register values to initialise the demod */
+static u8 init_tab [] = {
+	0x00, 0x00, /* Stop aquisition */
+	0x0B, 0x06,
+	0x09, 0x01,
+	0x0D, 0x41,
+	0x16, 0x32,
+	0x20, 0x0A,
+	0x21, 0x17,
+	0x24, 0x3e,
+	0x26, 0xff,
+	0x27, 0x10,
+	0x28, 0x00,
+	0x29, 0x00,
+	0x2a, 0x10,
+	0x2b, 0x00,
+	0x2c, 0x10,
+	0x2d, 0x00,
+	0x48, 0xd4,
+	0x49, 0x56,
+	0x6b, 0x1e,
+	0xc8, 0x02,
+	0xf8, 0x02,
+	0xf9, 0x00,
+	0xfa, 0x00,
+	0xfb, 0x00,
+	0xfc, 0x00,
+	0xfd, 0x00,
+};
+
+static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+			__FUNCTION__, reg, data, ret);
+
+	return (ret != 1) ? -1 : 0;
+}
+
+static u8 cx22702_readreg (struct cx22702_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+
+	struct i2c_msg msg [] = {
+		{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+	return b1[0];
+}
+
+static int cx22702_set_inversion (struct cx22702_state *state, int inversion)
+{
+	u8 val;
+
+	switch (inversion) {
+
+		case INVERSION_AUTO:
+			return -EOPNOTSUPP;
+
+		case INVERSION_ON:
+			val = cx22702_readreg (state, 0x0C);
+			return cx22702_writereg (state, 0x0C, val | 0x01);
+
+		case INVERSION_OFF:
+			val = cx22702_readreg (state, 0x0C);
+			return cx22702_writereg (state, 0x0C, val & 0xfe);
+
+		default:
+			return -EINVAL;
+
+	}
+
+}
+
+/* Retrieve the demod settings */
+static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p)
+{
+	u8 val;
+
+	/* Make sure the TPS regs are valid */
+	if (!(cx22702_readreg(state, 0x0A) & 0x20))
+		return -EAGAIN;
+
+	val = cx22702_readreg (state, 0x01);
+	switch( (val&0x18)>>3) {
+		case 0: p->constellation =   QPSK; break;
+		case 1: p->constellation = QAM_16; break;
+		case 2: p->constellation = QAM_64; break;
+	}
+	switch( val&0x07 ) {
+		case 0: p->hierarchy_information = HIERARCHY_NONE; break;
+		case 1: p->hierarchy_information =    HIERARCHY_1; break;
+		case 2: p->hierarchy_information =    HIERARCHY_2; break;
+		case 3: p->hierarchy_information =    HIERARCHY_4; break;
+	}
+
+
+	val = cx22702_readreg (state, 0x02);
+	switch( (val&0x38)>>3 ) {
+		case 0: p->code_rate_HP = FEC_1_2; break;
+		case 1: p->code_rate_HP = FEC_2_3; break;
+		case 2: p->code_rate_HP = FEC_3_4; break;
+		case 3: p->code_rate_HP = FEC_5_6; break;
+		case 4: p->code_rate_HP = FEC_7_8; break;
+	}
+	switch( val&0x07 ) {
+		case 0: p->code_rate_LP = FEC_1_2; break;
+		case 1: p->code_rate_LP = FEC_2_3; break;
+		case 2: p->code_rate_LP = FEC_3_4; break;
+		case 3: p->code_rate_LP = FEC_5_6; break;
+		case 4: p->code_rate_LP = FEC_7_8; break;
+	}
+
+
+	val = cx22702_readreg (state, 0x03);
+	switch( (val&0x0c)>>2 ) {
+		case 0: p->guard_interval = GUARD_INTERVAL_1_32; break;
+		case 1: p->guard_interval = GUARD_INTERVAL_1_16; break;
+		case 2: p->guard_interval =  GUARD_INTERVAL_1_8; break;
+		case 3: p->guard_interval =  GUARD_INTERVAL_1_4; break;
+	}
+	switch( val&0x03 ) {
+		case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break;
+		case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break;
+	}
+
+	return 0;
+}
+
+/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
+static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	u8 val;
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+	/* set PLL */
+        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+	state->config->pll_set(fe, p);
+        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
+
+	/* set inversion */
+	cx22702_set_inversion (state, p->inversion);
+
+	/* set bandwidth */
+	switch(p->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 );
+		break;
+	case BANDWIDTH_7_MHZ:
+		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 );
+		break;
+	case BANDWIDTH_8_MHZ:
+		cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf );
+		break;
+	default:
+		dprintk ("%s: invalid bandwidth\n",__FUNCTION__);
+		return -EINVAL;
+	}
+
+
+	p->u.ofdm.code_rate_LP = FEC_AUTO; //temp hack as manual not working
+
+	/* use auto configuration? */
+	if((p->u.ofdm.hierarchy_information==HIERARCHY_AUTO) ||
+	   (p->u.ofdm.constellation==QAM_AUTO) ||
+	   (p->u.ofdm.code_rate_HP==FEC_AUTO) ||
+	   (p->u.ofdm.code_rate_LP==FEC_AUTO) ||
+	   (p->u.ofdm.guard_interval==GUARD_INTERVAL_AUTO) ||
+	   (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) {
+
+		/* TPS Source - use hardware driven values */
+		cx22702_writereg(state, 0x06, 0x10);
+		cx22702_writereg(state, 0x07, 0x9);
+		cx22702_writereg(state, 0x08, 0xC1);
+		cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc );
+		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
+		cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */
+		printk("%s: Autodetecting\n",__FUNCTION__);
+		return 0;
+	}
+
+	/* manually programmed values */
+	val=0;
+	switch(p->u.ofdm.constellation) {
+		case   QPSK: val = (val&0xe7); break;
+		case QAM_16: val = (val&0xe7)|0x08; break;
+		case QAM_64: val = (val&0xe7)|0x10; break;
+		default:
+			dprintk ("%s: invalid constellation\n",__FUNCTION__);
+			return -EINVAL;
+	}
+	switch(p->u.ofdm.hierarchy_information) {
+		case HIERARCHY_NONE: val = (val&0xf8); break;
+		case    HIERARCHY_1: val = (val&0xf8)|1; break;
+		case    HIERARCHY_2: val = (val&0xf8)|2; break;
+		case    HIERARCHY_4: val = (val&0xf8)|3; break;
+		default:
+			dprintk ("%s: invalid hierarchy\n",__FUNCTION__);
+			return -EINVAL;
+	}
+	cx22702_writereg (state, 0x06, val);
+
+	val=0;
+	switch(p->u.ofdm.code_rate_HP) {
+		case FEC_NONE:
+		case FEC_1_2: val = (val&0xc7); break;
+		case FEC_2_3: val = (val&0xc7)|0x08; break;
+		case FEC_3_4: val = (val&0xc7)|0x10; break;
+		case FEC_5_6: val = (val&0xc7)|0x18; break;
+		case FEC_7_8: val = (val&0xc7)|0x20; break;
+		default:
+			dprintk ("%s: invalid code_rate_HP\n",__FUNCTION__);
+			return -EINVAL;
+	}
+	switch(p->u.ofdm.code_rate_LP) {
+		case FEC_NONE:
+		case FEC_1_2: val = (val&0xf8); break;
+		case FEC_2_3: val = (val&0xf8)|1; break;
+		case FEC_3_4: val = (val&0xf8)|2; break;
+		case FEC_5_6: val = (val&0xf8)|3; break;
+		case FEC_7_8: val = (val&0xf8)|4; break;
+		default:
+			dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__);
+			return -EINVAL;
+	}
+	cx22702_writereg (state, 0x07, val);
+
+	val=0;
+	switch(p->u.ofdm.guard_interval) {
+		case GUARD_INTERVAL_1_32: val = (val&0xf3); break;
+		case GUARD_INTERVAL_1_16: val = (val&0xf3)|0x04; break;
+		case  GUARD_INTERVAL_1_8: val = (val&0xf3)|0x08; break;
+		case  GUARD_INTERVAL_1_4: val = (val&0xf3)|0x0c; break;
+		default:
+			dprintk ("%s: invalid guard_interval\n",__FUNCTION__);
+			return -EINVAL;
+	}
+	switch(p->u.ofdm.transmission_mode) {
+		case TRANSMISSION_MODE_2K: val = (val&0xfc); break;
+		case TRANSMISSION_MODE_8K: val = (val&0xfc)|1; break;
+		default:
+			dprintk ("%s: invalid transmission_mode\n",__FUNCTION__);
+			return -EINVAL;
+	}
+	cx22702_writereg(state, 0x08, val);
+	cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 );
+	cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
+
+	/* Begin channel aquisition */
+	cx22702_writereg(state, 0x00, 0x01);
+
+	return 0;
+}
+
+/* Reset the demod hardware and reset all of the configuration registers
+   to a default state. */
+static int cx22702_init (struct dvb_frontend* fe)
+{
+	int i;
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+	cx22702_writereg (state, 0x00, 0x02);
+
+	msleep(10);
+
+	for (i=0; i<sizeof(init_tab); i+=2)
+		cx22702_writereg (state, init_tab[i], init_tab[i+1]);
+
+
+	/* init PLL */
+	if (state->config->pll_init) {
+	        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+		state->config->pll_init(fe);
+		cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
+	}
+
+	return 0;
+}
+
+static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+	u8 reg0A;
+	u8 reg23;
+
+	*status = 0;
+
+	reg0A = cx22702_readreg (state, 0x0A);
+	reg23 = cx22702_readreg (state, 0x23);
+
+	dprintk ("%s: status demod=0x%02x agc=0x%02x\n"
+		,__FUNCTION__,reg0A,reg23);
+
+	if(reg0A & 0x10) {
+		*status |= FE_HAS_LOCK;
+		*status |= FE_HAS_VITERBI;
+		*status |= FE_HAS_SYNC;
+	}
+
+	if(reg0A & 0x20)
+		*status |= FE_HAS_CARRIER;
+
+	if(reg23 < 0xf0)
+		*status |= FE_HAS_SIGNAL;
+
+	return 0;
+}
+
+static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+	if(cx22702_readreg (state, 0xE4) & 0x02) {
+		/* Realtime statistics */
+		*ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+			| (cx22702_readreg (state, 0xDF)&0x7F);
+	} else {
+		/* Averagtine statistics */
+		*ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+			| cx22702_readreg (state, 0xDF);
+	}
+
+	return 0;
+}
+
+static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+	*signal_strength = cx22702_readreg (state, 0x23);
+
+	return 0;
+}
+
+static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+	u16 rs_ber=0;
+	if(cx22702_readreg (state, 0xE4) & 0x02) {
+		/* Realtime statistics */
+		rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+			| (cx22702_readreg (state, 0xDF)& 0x7F);
+	} else {
+		/* Averagine statistics */
+		rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8
+			| cx22702_readreg (state, 0xDF);
+	}
+	*snr = ~rs_ber;
+
+	return 0;
+}
+
+static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+	u8 _ucblocks;
+
+	/* RS Uncorrectable Packet Count then reset */
+	_ucblocks = cx22702_readreg (state, 0xE3);
+	if (state->prevUCBlocks < _ucblocks) *ucblocks = (_ucblocks - state->prevUCBlocks);
+	else *ucblocks = state->prevUCBlocks - _ucblocks;
+	state->prevUCBlocks = _ucblocks;
+
+	return 0;
+}
+
+static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+	u8 reg0C = cx22702_readreg (state, 0x0C);
+
+	p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
+	return cx22702_get_tps (state, &p->u.ofdm);
+}
+
+static void cx22702_release(struct dvb_frontend* fe)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops cx22702_ops;
+
+struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct cx22702_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct cx22702_state*) kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &cx22702_ops, sizeof(struct dvb_frontend_ops));
+	state->prevUCBlocks = 0;
+
+	/* check if the demod is there */
+	if (cx22702_readreg(state, 0x1f) != 0x3) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops cx22702_ops = {
+
+	.info = {
+		.name			= "Conexant CX22702 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 177000000,
+		.frequency_max		= 858000000,
+		.frequency_stepsize	= 166666,
+		.caps = 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_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+		FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
+	},
+
+	.release = cx22702_release,
+
+	.init = cx22702_init,
+
+	.set_frontend = cx22702_set_tps,
+	.get_frontend = cx22702_get_frontend,
+
+	.read_status = cx22702_read_status,
+	.read_ber = cx22702_read_ber,
+	.read_signal_strength = cx22702_read_signal_strength,
+	.read_snr = cx22702_read_snr,
+	.read_ucblocks = cx22702_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx22702_attach);
diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h
new file mode 100644
index 0000000..6e34f99
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22702.h
@@ -0,0 +1,46 @@
+/*
+    Conexant 22702 DVB OFDM demodulator driver
+
+    based on:
+        Alps TDMB7 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	  Holger Waechtler <holger@convergence.de>
+
+    Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk>
+
+    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 CX22702_H
+#define CX22702_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22702_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // CX22702_H
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
new file mode 100644
index 0000000..ae16112
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -0,0 +1,657 @@
+/*
+    cx24110 - Single Chip Satellite Channel Receiver driver module
+
+    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
+    work
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.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; 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include "dvb_frontend.h"
+#include "cx24110.h"
+
+
+struct cx24110_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct cx24110_config* config;
+
+	struct dvb_frontend frontend;
+
+	u32 lastber;
+	u32 lastbler;
+	u32 lastesn0;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "cx24110: " args); \
+	} while (0)
+
+static struct {u8 reg; u8 data;} cx24110_regdata[]=
+                      /* Comments beginning with @ denote this value should
+                         be the default */
+        {{0x09,0x01}, /* SoftResetAll */
+         {0x09,0x00}, /* release reset */
+         {0x01,0xe8}, /* MSB of code rate 27.5MS/s */
+         {0x02,0x17}, /* middle byte " */
+         {0x03,0x29}, /* LSB         " */
+         {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */
+         {0x06,0xa5}, /* @ PLL 60MHz */
+         {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */
+         {0x0a,0x00}, /* @ partial chip disables, do not set */
+         {0x0b,0x01}, /* set output clock in gapped mode, start signal low
+                         active for first byte */
+         {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */
+         {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */
+         {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1
+                         to avoid starting the BER counter. Reset the
+                         CRC test bit. Finite counting selected */
+         {0x15,0xff}, /* @ size of the limited time window for RS BER
+                         estimation. It is <value>*256 RS blocks, this
+                         gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */
+         {0x16,0x00}, /* @ enable all RS output ports */
+         {0x17,0x04}, /* @ time window allowed for the RS to sync */
+         {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned
+                         for automatically */
+                      /* leave the current code rate and normalization
+                         registers as they are after reset... */
+         {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting
+                         only once */
+         {0x23,0x18}, /* @ size of the limited time window for Viterbi BER
+                         estimation. It is <value>*65536 channel bits, i.e.
+                         approx. 38ms at 27.5MS/s, rate 3/4 */
+         {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */
+                      /* leave front-end AGC parameters at default values */
+                      /* leave decimation AGC parameters at default values */
+         {0x35,0x40}, /* disable all interrupts. They are not connected anyway */
+         {0x36,0xff}, /* clear all interrupt pending flags */
+         {0x37,0x00}, /* @ fully enable AutoAcqq state machine */
+         {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */
+                      /* leave the equalizer parameters on their default values */
+                      /* leave the final AGC parameters on their default values */
+         {0x41,0x00}, /* @ MSB of front-end derotator frequency */
+         {0x42,0x00}, /* @ middle bytes " */
+         {0x43,0x00}, /* @ LSB          " */
+                      /* leave the carrier tracking loop parameters on default */
+                      /* leave the bit timing loop parameters at gefault */
+         {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */
+                      /* the cx24108 data sheet for symbol rates above 15MS/s */
+         {0x57,0x00}, /* @ Filter sigma delta enabled, positive */
+         {0x61,0x95}, /* GPIO pins 1-4 have special function */
+         {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */
+         {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */
+         {0x64,0x20}, /* GPIO 6 is input, all others are outputs */
+         {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */
+         {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */
+         {0x73,0x00}, /* @ disable several demod bypasses */
+         {0x74,0x00}, /* @  " */
+         {0x75,0x00}  /* @  " */
+                      /* the remaining registers are for SEC */
+	};
+
+
+static int cx24110_writereg (struct cx24110_state* state, int reg, int data)
+{
+        u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+	int err;
+
+        if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		dprintk ("%s: writereg error (err == %i, reg == 0x%02x,"
+			 " data == 0x%02x)\n", __FUNCTION__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+        return 0;
+}
+
+static int cx24110_readreg (struct cx24110_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) return ret;
+
+	return b1[0];
+}
+
+static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion)
+{
+/* fixme (low): error handling */
+
+	switch (inversion) {
+	case INVERSION_OFF:
+                cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
+                /* AcqSpectrInvDis on. No idea why someone should want this */
+                cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7);
+                /* Initial value 0 at start of acq */
+                cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef);
+                /* current value 0 */
+                /* The cx24110 manual tells us this reg is read-only.
+                   But what the heck... set it ayways */
+                break;
+	case INVERSION_ON:
+                cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
+                /* AcqSpectrInvDis on. No idea why someone should want this */
+                cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08);
+                /* Initial value 1 at start of acq */
+                cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10);
+                /* current value 1 */
+                break;
+	case INVERSION_AUTO:
+                cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe);
+                /* AcqSpectrInvDis off. Leave initial & current states as is */
+                break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec)
+{
+/* fixme (low): error handling */
+
+        static const int rate[]={-1,1,2,3,5,7,-1};
+        static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1};
+        static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1};
+
+        /* Well, the AutoAcq engine of the cx24106 and 24110 automatically
+           searches all enabled viterbi rates, and can handle non-standard
+           rates as well. */
+
+        if (fec>FEC_AUTO)
+                fec=FEC_AUTO;
+
+        if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
+		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf);
+		/* clear AcqVitDis bit */
+		cx24110_writereg(state,0x18,0xae);
+		/* allow all DVB standard code rates */
+		cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3);
+		/* set nominal Viterbi rate 3/4 */
+		cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3);
+		/* set current Viterbi rate 3/4 */
+		cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06);
+		/* set the puncture registers for code rate 3/4 */
+		return 0;
+        } else {
+		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20);
+		/* set AcqVitDis bit */
+		if(rate[fec]>0) {
+			cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]);
+			/* set nominal Viterbi rate */
+			cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]);
+			/* set current Viterbi rate */
+			cx24110_writereg(state,0x1a,g1[fec]);
+			cx24110_writereg(state,0x1b,g2[fec]);
+			/* not sure if this is the right way: I always used AutoAcq mode */
+           } else
+		   return -EOPNOTSUPP;
+/* fixme (low): which is the correct return code? */
+        };
+	return 0;
+}
+
+static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state)
+{
+	int i;
+
+	i=cx24110_readreg(state,0x22)&0x0f;
+	if(!(i&0x08)) {
+		return FEC_1_2 + i - 1;
+	} else {
+/* fixme (low): a special code rate has been selected. In theory, we need to
+   return a denominator value, a numerator value, and a pair of puncture
+   maps to correctly describe this mode. But this should never happen in
+   practice, because it cannot be set by cx24110_get_fec. */
+	   return FEC_NONE;
+	}
+}
+
+static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
+{
+/* fixme (low): add error handling */
+        u32 ratio;
+        u32 tmp, fclk, BDRI;
+
+        static const u32 bands[]={5000000UL,15000000UL,90999000UL/2};
+        int i;
+
+dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate);
+        if (srate>90999000UL/2)
+                srate=90999000UL/2;
+        if (srate<500000)
+                srate=500000;
+
+        for(i=0;(i<sizeof(bands)/sizeof(bands[0]))&&(srate>bands[i]);i++)
+		;
+        /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz,
+           and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult,
+           R06[3:0] PLLphaseDetGain */
+        tmp=cx24110_readreg(state,0x07)&0xfc;
+        if(srate<90999000UL/4) { /* sample rate 45MHz*/
+		cx24110_writereg(state,0x07,tmp);
+		cx24110_writereg(state,0x06,0x78);
+		fclk=90999000UL/2;
+        } else if(srate<60666000UL/2) { /* sample rate 60MHz */
+		cx24110_writereg(state,0x07,tmp|0x1);
+		cx24110_writereg(state,0x06,0xa5);
+		fclk=60666000UL;
+        } else if(srate<80888000UL/2) { /* sample rate 80MHz */
+		cx24110_writereg(state,0x07,tmp|0x2);
+		cx24110_writereg(state,0x06,0x87);
+		fclk=80888000UL;
+        } else { /* sample rate 90MHz */
+		cx24110_writereg(state,0x07,tmp|0x3);
+		cx24110_writereg(state,0x06,0x78);
+		fclk=90999000UL;
+        };
+        dprintk("cx24110 debug: fclk %d Hz\n",fclk);
+        /* we need to divide two integers with approx. 27 bits in 32 bit
+           arithmetic giving a 25 bit result */
+        /* the maximum dividend is 90999000/2, 0x02b6446c, this number is
+           also the most complex divisor. Hence, the dividend has,
+           assuming 32bit unsigned arithmetic, 6 clear bits on top, the
+           divisor 2 unused bits at the bottom. Also, the quotient is
+           always less than 1/2. Borrowed from VES1893.c, of course */
+
+        tmp=srate<<6;
+        BDRI=fclk>>2;
+        ratio=(tmp/BDRI);
+
+        tmp=(tmp%BDRI)<<8;
+        ratio=(ratio<<8)+(tmp/BDRI);
+
+        tmp=(tmp%BDRI)<<8;
+        ratio=(ratio<<8)+(tmp/BDRI);
+
+        tmp=(tmp%BDRI)<<1;
+        ratio=(ratio<<1)+(tmp/BDRI);
+
+        dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]);
+        dprintk("fclk = %d\n", fclk);
+        dprintk("ratio= %08x\n", ratio);
+
+        cx24110_writereg(state, 0x1, (ratio>>16)&0xff);
+        cx24110_writereg(state, 0x2, (ratio>>8)&0xff);
+        cx24110_writereg(state, 0x3, (ratio)&0xff);
+
+        return 0;
+
+}
+
+int cx24110_pll_write (struct dvb_frontend* fe, u32 data)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+/* tuner data is 21 bits long, must be left-aligned in data */
+/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */
+/* FIXME (low): add error handling, avoid infinite loops if HW fails... */
+
+	dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data);
+
+        cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */
+        cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */
+
+        /* if the auto tuner writer is still busy, clear it out */
+        while (cx24110_readreg(state,0x6d)&0x80)
+		cx24110_writereg(state,0x72,0);
+
+        /* write the topmost 8 bits */
+        cx24110_writereg(state,0x72,(data>>24)&0xff);
+
+        /* wait for the send to be completed */
+        while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+		;
+
+        /* send another 8 bytes */
+        cx24110_writereg(state,0x72,(data>>16)&0xff);
+        while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+		;
+
+        /* and the topmost 5 bits of this byte */
+        cx24110_writereg(state,0x72,(data>>8)&0xff);
+        while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+		;
+
+        /* now strobe the enable line once */
+        cx24110_writereg(state,0x6d,0x32);
+        cx24110_writereg(state,0x6d,0x30);
+
+        return 0;
+}
+
+static int cx24110_initfe(struct dvb_frontend* fe)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+/* fixme (low): error handling */
+        int i;
+
+	dprintk("%s: init chip\n", __FUNCTION__);
+
+        for(i=0;i<sizeof(cx24110_regdata)/sizeof(cx24110_regdata[0]);i++) {
+		cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data);
+        };
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	return 0;
+}
+
+static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0);
+	case SEC_VOLTAGE_18:
+		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40);
+	default:
+		return -EINVAL;
+	};
+}
+
+static int cx24110_diseqc_send_burst(struct dvb_frontend* fe,
+			fe_sec_mini_cmd_t burst)
+{
+	int rv, bit, i;
+	struct cx24110_state *state = fe->demodulator_priv;
+
+	if (burst == SEC_MINI_A)
+		bit = 0x00;
+	else if (burst == SEC_MINI_B)
+		bit = 0x08;
+	else
+		return -EINVAL;
+
+	rv = cx24110_readreg(state, 0x77);
+	cx24110_writereg(state, 0x77, rv|0x04);
+
+	rv = cx24110_readreg(state, 0x76);
+	cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit));
+	for (i = 500; i-- > 0 && !(cx24110_readreg(state,0x76)&0x40) ; )
+              ; /* wait for LNB ready */
+
+	return 0;
+}
+
+static int cx24110_send_diseqc_msg(struct dvb_frontend* fe,
+				   struct dvb_diseqc_master_cmd *cmd)
+{
+	int i, rv;
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+	for (i = 0; i < cmd->msg_len; i++)
+		cx24110_writereg(state, 0x79 + i, cmd->msg[i]);
+
+	rv = cx24110_readreg(state, 0x77);
+	cx24110_writereg(state, 0x77, rv|0x04);
+
+	rv = cx24110_readreg(state, 0x76);
+
+	cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3));
+	for (i=500; i-- > 0 && !(cx24110_readreg(state,0x76)&0x40);)
+		; /* wait for LNB ready */
+
+	return 0;
+}
+
+static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+	int sync = cx24110_readreg (state, 0x55);
+
+	*status = 0;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x08)
+		*status |= FE_HAS_CARRIER;
+
+	sync = cx24110_readreg (state, 0x08);
+
+	if (sync & 0x40)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x20)
+		*status |= FE_HAS_SYNC;
+
+	if ((sync & 0x60) == 0x60)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+	/* fixme (maybe): value range is 16 bit. Scale? */
+	if(cx24110_readreg(state,0x24)&0x10) {
+		/* the Viterbi error counter has finished one counting window */
+		cx24110_writereg(state,0x24,0x04); /* select the ber reg */
+		state->lastber=cx24110_readreg(state,0x25)|
+			(cx24110_readreg(state,0x26)<<8);
+		cx24110_writereg(state,0x24,0x04); /* start new count window */
+		cx24110_writereg(state,0x24,0x14);
+	}
+	*ber = state->lastber;
+
+	return 0;
+}
+
+static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+/* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */
+	u8 signal = cx24110_readreg (state, 0x27)+128;
+	*signal_strength = (signal << 8) | signal;
+
+	return 0;
+}
+
+static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+	/* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */
+	if(cx24110_readreg(state,0x6a)&0x80) {
+		/* the Es/N0 error counter has finished one counting window */
+		state->lastesn0=cx24110_readreg(state,0x69)|
+			(cx24110_readreg(state,0x68)<<8);
+		cx24110_writereg(state,0x6a,0x84); /* start new count window */
+	}
+	*snr = state->lastesn0;
+
+	return 0;
+}
+
+static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+	u32 lastbyer;
+
+	if(cx24110_readreg(state,0x10)&0x40) {
+		/* the RS error counter has finished one counting window */
+		cx24110_writereg(state,0x10,0x60); /* select the byer reg */
+		lastbyer=cx24110_readreg(state,0x12)|
+			(cx24110_readreg(state,0x13)<<8)|
+			(cx24110_readreg(state,0x14)<<16);
+		cx24110_writereg(state,0x10,0x70); /* select the bler reg */
+		state->lastbler=cx24110_readreg(state,0x12)|
+			(cx24110_readreg(state,0x13)<<8)|
+			(cx24110_readreg(state,0x14)<<16);
+		cx24110_writereg(state,0x10,0x20); /* start new count window */
+	}
+	*ucblocks = state->lastbler;
+
+	return 0;
+}
+
+static int cx24110_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+	state->config->pll_set(fe, p);
+	cx24110_set_inversion (state, p->inversion);
+	cx24110_set_fec (state, p->u.qpsk.fec_inner);
+	cx24110_set_symbolrate (state, p->u.qpsk.symbol_rate);
+	cx24110_writereg(state,0x04,0x05); /* start aquisition */
+
+	return 0;
+}
+
+static int cx24110_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+	s32 afc; unsigned sclk;
+
+/* cannot read back tuner settings (freq). Need to have some private storage */
+
+	sclk = cx24110_readreg (state, 0x07) & 0x03;
+/* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz.
+ * Need 64 bit arithmetic. Is thiss possible in the kernel? */
+	if (sclk==0) sclk=90999000L/2L;
+	else if (sclk==1) sclk=60666000L;
+	else if (sclk==2) sclk=80888000L;
+	else sclk=90999000L;
+	sclk>>=8;
+	afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+
+	      ((sclk*cx24110_readreg (state, 0x45))>>8)+
+	      ((sclk*cx24110_readreg (state, 0x46))>>16);
+
+	p->frequency += afc;
+	p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ?
+				INVERSION_ON : INVERSION_OFF;
+	p->u.qpsk.fec_inner = cx24110_get_fec (state);
+
+	return 0;
+}
+
+static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+	return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0));
+}
+
+static void cx24110_release(struct dvb_frontend* fe)
+{
+	struct cx24110_state* state = (struct cx24110_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops cx24110_ops;
+
+struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct cx24110_state* state = NULL;
+	int ret;
+
+	/* allocate memory for the internal state */
+	state = (struct cx24110_state*) kmalloc(sizeof(struct cx24110_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &cx24110_ops, sizeof(struct dvb_frontend_ops));
+	state->lastber = 0;
+	state->lastbler = 0;
+	state->lastesn0 = 0;
+
+	/* check if the demod is there */
+	ret = cx24110_readreg(state, 0x00);
+	if ((ret != 0x5a) && (ret != 0x69)) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops cx24110_ops = {
+
+	.info = {
+		.name = "Conexant CX24110 DVB-S",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1011,  /* kHz for QPSK frontends */
+		.frequency_tolerance = 29500,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.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_RECOVER
+	},
+
+	.release = cx24110_release,
+
+	.init = cx24110_initfe,
+	.set_frontend = cx24110_set_frontend,
+	.get_frontend = cx24110_get_frontend,
+	.read_status = cx24110_read_status,
+	.read_ber = cx24110_read_ber,
+	.read_signal_strength = cx24110_read_signal_strength,
+	.read_snr = cx24110_read_snr,
+	.read_ucblocks = cx24110_read_ucblocks,
+
+	.diseqc_send_master_cmd = cx24110_send_diseqc_msg,
+	.set_tone = cx24110_set_tone,
+	.set_voltage = cx24110_set_voltage,
+	.diseqc_send_burst = cx24110_diseqc_send_burst,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver");
+MODULE_AUTHOR("Peter Hettkamp");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx24110_attach);
+EXPORT_SYMBOL(cx24110_pll_write);
diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h
new file mode 100644
index 0000000..6b663f4
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24110.h
@@ -0,0 +1,45 @@
+/*
+    cx24110 - Single Chip Satellite Channel Receiver driver module
+
+    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
+    work
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.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; 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 CX24110_H
+#define CX24110_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24110_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+					   struct i2c_adapter* i2c);
+
+extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data);
+
+#endif // CX24110_H
diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c
new file mode 100644
index 0000000..47ab02e
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000-common.c
@@ -0,0 +1,83 @@
+#include "dib3000-common.h"
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able)).");
+#endif
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_i2c(args...) dprintk(0x02,args)
+#define deb_srch(args...) dprintk(0x04,args)
+
+
+int dib3000_read_reg(struct dib3000_state *state, u16 reg)
+{
+	u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
+	u8 rb[2];
+	struct i2c_msg msg[] = {
+		{ .addr = state->config.demod_address, .flags = 0,        .buf = wb, .len = 2 },
+		{ .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+	};
+
+	if (i2c_transfer(state->i2c, msg, 2) != 2)
+		deb_i2c("i2c read error\n");
+
+	deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,
+			(rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]);
+
+	return (rb[0] << 8) | rb[1];
+}
+
+int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val)
+{
+	u8 b[] = {
+		(reg >> 8) & 0xff, reg & 0xff,
+		(val >> 8) & 0xff, val & 0xff,
+	};
+	struct i2c_msg msg[] = {
+		{ .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 }
+	};
+	deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val);
+
+	return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+int dib3000_search_status(u16 irq,u16 lock)
+{
+	if (irq & 0x02) {
+		if (lock & 0x01) {
+			deb_srch("auto search succeeded\n");
+			return 1; // auto search succeeded
+		} else {
+			deb_srch("auto search not successful\n");
+			return 0; // auto search failed
+		}
+	} else if (irq & 0x01)  {
+		deb_srch("auto search failed\n");
+		return 0; // auto search failed
+	}
+	return -1; // try again
+}
+
+/* for auto search */
+u16 dib3000_seq[2][2][2] =     /* fft,gua,   inv   */
+	{ /* fft */
+		{ /* gua */
+			{ 0, 1 },                   /*  0   0   { 0,1 } */
+			{ 3, 9 },                   /*  0   1   { 0,1 } */
+		},
+		{
+			{ 2, 5 },                   /*  1   0   { 0,1 } */
+			{ 6, 11 },                  /*  1   1   { 0,1 } */
+		}
+	};
+
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de");
+MODULE_DESCRIPTION("Common functions for the dib3000mb/dib3000mc dvb-frontend drivers");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000_seq);
+
+EXPORT_SYMBOL(dib3000_read_reg);
+EXPORT_SYMBOL(dib3000_write_reg);
+EXPORT_SYMBOL(dib3000_search_status);
diff --git a/drivers/media/dvb/frontends/dib3000-common.h b/drivers/media/dvb/frontends/dib3000-common.h
new file mode 100644
index 0000000..c31d6df
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000-common.h
@@ -0,0 +1,137 @@
+/*
+ * .h-files for the common use of the frontend drivers made by DiBcom
+ * DiBcom 3000M-B/C, 3000P
+ *
+ * DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@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.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#ifndef DIB3000_COMMON_H
+#define DIB3000_COMMON_H
+
+#include "dvb_frontend.h"
+#include "dib3000.h"
+
+/* info and err, taken from usb.h, if there is anything available like by default. */
+#define err(format, arg...)  printk(KERN_ERR     "dib3000: " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO    "dib3000: " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg)
+
+/* frontend state */
+struct dib3000_state {
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+/* configuration settings */
+	struct dib3000_config config;
+
+	struct dvb_frontend frontend;
+	int timing_offset;
+	int timing_offset_comp_done;
+
+	fe_bandwidth_t last_tuned_bw;
+	u32 last_tuned_freq;
+};
+
+/* commonly used methods by the dib3000mb/mc/p frontend */
+extern int dib3000_read_reg(struct dib3000_state *state, u16 reg);
+extern int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val);
+
+extern int dib3000_search_status(u16 irq,u16 lock);
+
+/* handy shortcuts */
+#define rd(reg) dib3000_read_reg(state,reg)
+
+#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \
+	{ err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
+
+#define wr_foreach(a,v) { int i; \
+	if (sizeof(a) != sizeof(v)) \
+		err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\
+	for (i=0; i < sizeof(a)/sizeof(u16); i++) \
+		wr(a[i],v[i]); \
+	}
+
+#define set_or(reg,val) wr(reg,rd(reg) | val)
+
+#define set_and(reg,val) wr(reg,rd(reg) & val)
+
+
+/* debug */
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define dprintk(level,args...) \
+    do { if ((debug & level)) { printk(args); } } while (0)
+#else
+#define dprintk(args...) do { } while (0)
+#endif
+
+/* mask for enabling a specific pid for the pid_filter */
+#define DIB3000_ACTIVATE_PID_FILTERING	(0x2000)
+
+/* common values for tuning */
+#define DIB3000_ALPHA_0					(     0)
+#define DIB3000_ALPHA_1					(     1)
+#define DIB3000_ALPHA_2					(     2)
+#define DIB3000_ALPHA_4					(     4)
+
+#define DIB3000_CONSTELLATION_QPSK		(     0)
+#define DIB3000_CONSTELLATION_16QAM		(     1)
+#define DIB3000_CONSTELLATION_64QAM		(     2)
+
+#define DIB3000_GUARD_TIME_1_32			(     0)
+#define DIB3000_GUARD_TIME_1_16			(     1)
+#define DIB3000_GUARD_TIME_1_8			(     2)
+#define DIB3000_GUARD_TIME_1_4			(     3)
+
+#define DIB3000_TRANSMISSION_MODE_2K	(     0)
+#define DIB3000_TRANSMISSION_MODE_8K	(     1)
+
+#define DIB3000_SELECT_LP				(     0)
+#define DIB3000_SELECT_HP				(     1)
+
+#define DIB3000_FEC_1_2					(     1)
+#define DIB3000_FEC_2_3					(     2)
+#define DIB3000_FEC_3_4					(     3)
+#define DIB3000_FEC_5_6					(     5)
+#define DIB3000_FEC_7_8					(     7)
+
+#define DIB3000_HRCH_OFF				(     0)
+#define DIB3000_HRCH_ON					(     1)
+
+#define DIB3000_DDS_INVERSION_OFF		(     0)
+#define DIB3000_DDS_INVERSION_ON		(     1)
+
+#define DIB3000_TUNER_WRITE_ENABLE(a)	(0xffff & (a << 8))
+#define DIB3000_TUNER_WRITE_DISABLE(a)	(0xffff & ((a << 8) | (1 << 7)))
+
+/* for auto search */
+extern u16 dib3000_seq[2][2][2];
+
+#define DIB3000_REG_MANUFACTOR_ID		(  1025)
+#define DIB3000_I2C_ID_DIBCOM			(0x01b3)
+
+#define DIB3000_REG_DEVICE_ID			(  1026)
+#define DIB3000MB_DEVICE_ID				(0x3000)
+#define DIB3000MC_DEVICE_ID				(0x3001)
+#define DIB3000P_DEVICE_ID				(0x3002)
+
+#endif // DIB3000_COMMON_H
diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h
new file mode 100644
index 0000000..80687c1
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000.h
@@ -0,0 +1,54 @@
+/*
+ * public header file of the frontend drivers for mobile DVB-T demodulators
+ * DiBcom 3000M-B and DiBcom 3000P/M-C (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@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.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#ifndef DIB3000_H
+#define DIB3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct dib3000_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance and the i2c address of the PLL */
+	u8 (*pll_addr)(struct dvb_frontend *fe);
+	int (*pll_init)(struct dvb_frontend *fe, u8 pll_buf[5]);
+	int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params, u8 pll_buf[5]);
+};
+
+struct dib_fe_xfer_ops
+{
+	/* pid and transfer handling is done in the demodulator */
+	int (*pid_parse)(struct dvb_frontend *fe, int onoff);
+	int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff);
+	int (*pid_ctrl)(struct dvb_frontend *fe, int index, int pid, int onoff);
+	int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl);
+};
+
+extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+					     struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
+
+extern struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
+					     struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
+#endif // DIB3000_H
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c
new file mode 100644
index 0000000..a853d12
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mb.c
@@ -0,0 +1,784 @@
+/*
+ * Frontend driver for mobile DVB-T demodulator DiBcom 3000M-B
+ * DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@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.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dib3000-common.h"
+#include "dib3000mb_priv.h"
+#include "dib3000.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.1"
+#define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able)).");
+#endif
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_xfer(args...) dprintk(0x02,args)
+#define deb_setf(args...) dprintk(0x04,args)
+#define deb_getf(args...) dprintk(0x08,args)
+
+static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr);
+
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep);
+
+static int dib3000mb_set_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep, int tuner)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+	fe_code_rate_t fe_cr = FEC_NONE;
+	int search_state, seq;
+
+	if (tuner) {
+		dib3000mb_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+		state->config.pll_set(fe, fep, NULL);
+		dib3000mb_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+
+		deb_setf("bandwidth: ");
+		switch (ofdm->bandwidth) {
+			case BANDWIDTH_8_MHZ:
+				deb_setf("8 MHz\n");
+				wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]);
+				wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz);
+				break;
+			case BANDWIDTH_7_MHZ:
+				deb_setf("7 MHz\n");
+				wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[1]);
+				wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_7mhz);
+				break;
+			case BANDWIDTH_6_MHZ:
+				deb_setf("6 MHz\n");
+				wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[0]);
+				wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_6mhz);
+				break;
+			case BANDWIDTH_AUTO:
+				return -EOPNOTSUPP;
+			default:
+				err("unkown bandwidth value.");
+				return -EINVAL;
+		}
+	}
+	wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4);
+
+	deb_setf("transmission mode: ");
+	switch (ofdm->transmission_mode) {
+		case TRANSMISSION_MODE_2K:
+			deb_setf("2k\n");
+			wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K);
+			break;
+		case TRANSMISSION_MODE_8K:
+			deb_setf("8k\n");
+			wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K);
+			break;
+		case TRANSMISSION_MODE_AUTO:
+			deb_setf("auto\n");
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	deb_setf("guard: ");
+	switch (ofdm->guard_interval) {
+		case GUARD_INTERVAL_1_32:
+			deb_setf("1_32\n");
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32);
+			break;
+		case GUARD_INTERVAL_1_16:
+			deb_setf("1_16\n");
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16);
+			break;
+		case GUARD_INTERVAL_1_8:
+			deb_setf("1_8\n");
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8);
+			break;
+		case GUARD_INTERVAL_1_4:
+			deb_setf("1_4\n");
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4);
+			break;
+		case GUARD_INTERVAL_AUTO:
+			deb_setf("auto\n");
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	deb_setf("inversion: ");
+	switch (fep->inversion) {
+		case INVERSION_OFF:
+			deb_setf("off\n");
+			wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF);
+			break;
+		case INVERSION_AUTO:
+			deb_setf("auto ");
+			break;
+		case INVERSION_ON:
+			deb_setf("on\n");
+			wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	deb_setf("constellation: ");
+	switch (ofdm->constellation) {
+		case QPSK:
+			deb_setf("qpsk\n");
+			wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK);
+			break;
+		case QAM_16:
+			deb_setf("qam16\n");
+			wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM);
+			break;
+		case QAM_64:
+			deb_setf("qam64\n");
+			wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM);
+			break;
+		case QAM_AUTO:
+			break;
+		default:
+			return -EINVAL;
+	}
+	deb_setf("hierachy: ");
+	switch (ofdm->hierarchy_information) {
+		case HIERARCHY_NONE:
+			deb_setf("none ");
+			/* fall through */
+		case HIERARCHY_1:
+			deb_setf("alpha=1\n");
+			wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1);
+			break;
+		case HIERARCHY_2:
+			deb_setf("alpha=2\n");
+			wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2);
+			break;
+		case HIERARCHY_4:
+			deb_setf("alpha=4\n");
+			wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4);
+			break;
+		case HIERARCHY_AUTO:
+			deb_setf("alpha=auto\n");
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	deb_setf("hierarchy: ");
+	if (ofdm->hierarchy_information == HIERARCHY_NONE) {
+		deb_setf("none\n");
+		wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF);
+		wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP);
+		fe_cr = ofdm->code_rate_HP;
+	} else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
+		deb_setf("on\n");
+		wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON);
+		wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP);
+		fe_cr = ofdm->code_rate_LP;
+	}
+	deb_setf("fec: ");
+	switch (fe_cr) {
+		case FEC_1_2:
+			deb_setf("1_2\n");
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2);
+			break;
+		case FEC_2_3:
+			deb_setf("2_3\n");
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3);
+			break;
+		case FEC_3_4:
+			deb_setf("3_4\n");
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4);
+			break;
+		case FEC_5_6:
+			deb_setf("5_6\n");
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6);
+			break;
+		case FEC_7_8:
+			deb_setf("7_8\n");
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8);
+			break;
+		case FEC_NONE:
+			deb_setf("none ");
+			break;
+		case FEC_AUTO:
+			deb_setf("auto\n");
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	seq = dib3000_seq
+		[ofdm->transmission_mode == TRANSMISSION_MODE_AUTO]
+		[ofdm->guard_interval == GUARD_INTERVAL_AUTO]
+		[fep->inversion == INVERSION_AUTO];
+
+	deb_setf("seq? %d\n", seq);
+
+	wr(DIB3000MB_REG_SEQ, seq);
+
+	wr(DIB3000MB_REG_ISI, seq ? DIB3000MB_ISI_INHIBIT : DIB3000MB_ISI_ACTIVATE);
+
+	if (ofdm->transmission_mode == TRANSMISSION_MODE_2K) {
+		if (ofdm->guard_interval == GUARD_INTERVAL_1_8) {
+			wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_2K_1_8);
+		} else {
+			wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_DEFAULT);
+		}
+
+		wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_2K);
+	} else {
+		wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_DEFAULT);
+	}
+
+	wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_OFF);
+	wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF);
+	wr(DIB3000MB_REG_MOBILE_MODE, DIB3000MB_MOBILE_MODE_OFF);
+
+	wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_high);
+
+	wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_ACTIVATE);
+
+	wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC + DIB3000MB_RESTART_CTRL);
+	wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF);
+
+	/* wait for AGC lock */
+	msleep(70);
+
+	wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low);
+
+	/* something has to be auto searched */
+	if (ofdm->constellation == QAM_AUTO ||
+		ofdm->hierarchy_information == HIERARCHY_AUTO ||
+		fe_cr == FEC_AUTO ||
+		fep->inversion == INVERSION_AUTO) {
+		int as_count=0;
+
+		deb_setf("autosearch enabled.\n");
+
+		wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT);
+
+		wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AUTO_SEARCH);
+		wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF);
+
+		while ((search_state =
+				dib3000_search_status(
+					rd(DIB3000MB_REG_AS_IRQ_PENDING),
+					rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100)
+			msleep(1);
+
+		deb_setf("search_state after autosearch %d after %d checks\n",search_state,as_count);
+
+		if (search_state == 1) {
+			struct dvb_frontend_parameters feps;
+			if (dib3000mb_get_frontend(fe, &feps) == 0) {
+				deb_setf("reading tuning data from frontend succeeded.\n");
+				return dib3000mb_set_frontend(fe, &feps, 0);
+			}
+		}
+
+	} else {
+		wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_CTRL);
+		wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF);
+	}
+
+	return 0;
+}
+
+static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+	deb_info("dib3000mb is getting up.\n");
+	wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_UP);
+
+	wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC);
+
+	wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE);
+	wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE_RST);
+
+	wr(DIB3000MB_REG_CLOCK, DIB3000MB_CLOCK_DEFAULT);
+
+	wr(DIB3000MB_REG_ELECT_OUT_MODE, DIB3000MB_ELECT_OUT_MODE_ON);
+
+	wr(DIB3000MB_REG_DDS_FREQ_MSB, DIB3000MB_DDS_FREQ_MSB);
+	wr(DIB3000MB_REG_DDS_FREQ_LSB, DIB3000MB_DDS_FREQ_LSB);
+
+	wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]);
+
+	wr_foreach(dib3000mb_reg_impulse_noise,
+			dib3000mb_impulse_noise_values[DIB3000MB_IMPNOISE_OFF]);
+
+	wr_foreach(dib3000mb_reg_agc_gain, dib3000mb_default_agc_gain);
+
+	wr(DIB3000MB_REG_PHASE_NOISE, DIB3000MB_PHASE_NOISE_DEFAULT);
+
+	wr_foreach(dib3000mb_reg_phase_noise, dib3000mb_default_noise_phase);
+
+	wr_foreach(dib3000mb_reg_lock_duration, dib3000mb_default_lock_duration);
+
+	wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low);
+
+	wr(DIB3000MB_REG_LOCK0_MASK, DIB3000MB_LOCK0_DEFAULT);
+	wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4);
+	wr(DIB3000MB_REG_LOCK2_MASK, DIB3000MB_LOCK2_DEFAULT);
+	wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]);
+
+	wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz);
+
+	wr(DIB3000MB_REG_UNK_68, DIB3000MB_UNK_68);
+	wr(DIB3000MB_REG_UNK_69, DIB3000MB_UNK_69);
+	wr(DIB3000MB_REG_UNK_71, DIB3000MB_UNK_71);
+	wr(DIB3000MB_REG_UNK_77, DIB3000MB_UNK_77);
+	wr(DIB3000MB_REG_UNK_78, DIB3000MB_UNK_78);
+	wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT);
+	wr(DIB3000MB_REG_UNK_92, DIB3000MB_UNK_92);
+	wr(DIB3000MB_REG_UNK_96, DIB3000MB_UNK_96);
+	wr(DIB3000MB_REG_UNK_97, DIB3000MB_UNK_97);
+	wr(DIB3000MB_REG_UNK_106, DIB3000MB_UNK_106);
+	wr(DIB3000MB_REG_UNK_107, DIB3000MB_UNK_107);
+	wr(DIB3000MB_REG_UNK_108, DIB3000MB_UNK_108);
+	wr(DIB3000MB_REG_UNK_122, DIB3000MB_UNK_122);
+	wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF);
+	wr(DIB3000MB_REG_BERLEN, DIB3000MB_BERLEN_DEFAULT);
+
+	wr_foreach(dib3000mb_reg_filter_coeffs, dib3000mb_filter_coeffs);
+
+	wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_ON);
+	wr(DIB3000MB_REG_MULTI_DEMOD_MSB, DIB3000MB_MULTI_DEMOD_MSB);
+	wr(DIB3000MB_REG_MULTI_DEMOD_LSB, DIB3000MB_MULTI_DEMOD_LSB);
+
+	wr(DIB3000MB_REG_OUTPUT_MODE, DIB3000MB_OUTPUT_MODE_SLAVE);
+
+	wr(DIB3000MB_REG_FIFO_142, DIB3000MB_FIFO_142);
+	wr(DIB3000MB_REG_MPEG2_OUT_MODE, DIB3000MB_MPEG2_OUT_MODE_188);
+	wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE);
+	wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT);
+	wr(DIB3000MB_REG_FIFO_146, DIB3000MB_FIFO_146);
+	wr(DIB3000MB_REG_FIFO_147, DIB3000MB_FIFO_147);
+
+	wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF);
+
+	if (state->config.pll_init) {
+		dib3000mb_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+		state->config.pll_init(fe,NULL);
+		dib3000mb_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+	}
+
+	return 0;
+}
+
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+	fe_code_rate_t *cr;
+	u16 tps_val;
+	int inv_test1,inv_test2;
+	u32 dds_val, threshold = 0x800000;
+
+	if (!rd(DIB3000MB_REG_TPS_LOCK))
+		return 0;
+
+	dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB);
+	deb_getf("DDS_VAL: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_VALUE_MSB), rd(DIB3000MB_REG_DDS_VALUE_LSB));
+	if (dds_val < threshold)
+		inv_test1 = 0;
+	else if (dds_val == threshold)
+		inv_test1 = 1;
+	else
+		inv_test1 = 2;
+
+	dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB);
+	deb_getf("DDS_FREQ: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_FREQ_MSB), rd(DIB3000MB_REG_DDS_FREQ_LSB));
+	if (dds_val < threshold)
+		inv_test2 = 0;
+	else if (dds_val == threshold)
+		inv_test2 = 1;
+	else
+		inv_test2 = 2;
+
+	fep->inversion =
+		((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
+		((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
+		INVERSION_ON : INVERSION_OFF;
+
+	deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+
+	switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) {
+		case DIB3000_CONSTELLATION_QPSK:
+			deb_getf("QPSK ");
+			ofdm->constellation = QPSK;
+			break;
+		case DIB3000_CONSTELLATION_16QAM:
+			deb_getf("QAM16 ");
+			ofdm->constellation = QAM_16;
+			break;
+		case DIB3000_CONSTELLATION_64QAM:
+			deb_getf("QAM64 ");
+			ofdm->constellation = QAM_64;
+			break;
+		default:
+			err("Unexpected constellation returned by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("TPS: %d\n", tps_val);
+
+	if (rd(DIB3000MB_REG_TPS_HRCH)) {
+		deb_getf("HRCH ON\n");
+		cr = &ofdm->code_rate_LP;
+		ofdm->code_rate_HP = FEC_NONE;
+		switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) {
+			case DIB3000_ALPHA_0:
+				deb_getf("HIERARCHY_NONE ");
+				ofdm->hierarchy_information = HIERARCHY_NONE;
+				break;
+			case DIB3000_ALPHA_1:
+				deb_getf("HIERARCHY_1 ");
+				ofdm->hierarchy_information = HIERARCHY_1;
+				break;
+			case DIB3000_ALPHA_2:
+				deb_getf("HIERARCHY_2 ");
+				ofdm->hierarchy_information = HIERARCHY_2;
+				break;
+			case DIB3000_ALPHA_4:
+				deb_getf("HIERARCHY_4 ");
+				ofdm->hierarchy_information = HIERARCHY_4;
+				break;
+			default:
+				err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
+				break;
+		}
+		deb_getf("TPS: %d\n", tps_val);
+
+		tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP);
+	} else {
+		deb_getf("HRCH OFF\n");
+		cr = &ofdm->code_rate_HP;
+		ofdm->code_rate_LP = FEC_NONE;
+		ofdm->hierarchy_information = HIERARCHY_NONE;
+
+		tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP);
+	}
+
+	switch (tps_val) {
+		case DIB3000_FEC_1_2:
+			deb_getf("FEC_1_2 ");
+			*cr = FEC_1_2;
+			break;
+		case DIB3000_FEC_2_3:
+			deb_getf("FEC_2_3 ");
+			*cr = FEC_2_3;
+			break;
+		case DIB3000_FEC_3_4:
+			deb_getf("FEC_3_4 ");
+			*cr = FEC_3_4;
+			break;
+		case DIB3000_FEC_5_6:
+			deb_getf("FEC_5_6 ");
+			*cr = FEC_4_5;
+			break;
+		case DIB3000_FEC_7_8:
+			deb_getf("FEC_7_8 ");
+			*cr = FEC_7_8;
+			break;
+		default:
+			err("Unexpected FEC returned by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("TPS: %d\n",tps_val);
+
+	switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) {
+		case DIB3000_GUARD_TIME_1_32:
+			deb_getf("GUARD_INTERVAL_1_32 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case DIB3000_GUARD_TIME_1_16:
+			deb_getf("GUARD_INTERVAL_1_16 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case DIB3000_GUARD_TIME_1_8:
+			deb_getf("GUARD_INTERVAL_1_8 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case DIB3000_GUARD_TIME_1_4:
+			deb_getf("GUARD_INTERVAL_1_4 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		default:
+			err("Unexpected Guard Time returned by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("TPS: %d\n", tps_val);
+
+	switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) {
+		case DIB3000_TRANSMISSION_MODE_2K:
+			deb_getf("TRANSMISSION_MODE_2K ");
+			ofdm->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case DIB3000_TRANSMISSION_MODE_8K:
+			deb_getf("TRANSMISSION_MODE_8K ");
+			ofdm->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		default:
+			err("unexpected transmission mode return by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("TPS: %d\n", tps_val);
+
+	return 0;
+}
+
+static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+	*stat = 0;
+
+	if (rd(DIB3000MB_REG_AGC_LOCK))
+		*stat |= FE_HAS_SIGNAL;
+	if (rd(DIB3000MB_REG_CARRIER_LOCK))
+		*stat |= FE_HAS_CARRIER;
+	if (rd(DIB3000MB_REG_VIT_LCK))
+		*stat |= FE_HAS_VITERBI;
+	if (rd(DIB3000MB_REG_TS_SYNC_LOCK))
+		*stat |= (FE_HAS_SYNC | FE_HAS_LOCK);
+
+	deb_getf("actual status is %2x\n",*stat);
+
+	deb_getf("autoval: tps: %d, qam: %d, hrch: %d, alpha: %d, hp: %d, lp: %d, guard: %d, fft: %d cell: %d\n",
+			rd(DIB3000MB_REG_TPS_LOCK),
+			rd(DIB3000MB_REG_TPS_QAM),
+			rd(DIB3000MB_REG_TPS_HRCH),
+			rd(DIB3000MB_REG_TPS_VIT_ALPHA),
+			rd(DIB3000MB_REG_TPS_CODE_RATE_HP),
+			rd(DIB3000MB_REG_TPS_CODE_RATE_LP),
+			rd(DIB3000MB_REG_TPS_GUARD_TIME),
+			rd(DIB3000MB_REG_TPS_FFT),
+			rd(DIB3000MB_REG_TPS_CELL_ID));
+
+	//*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+	return 0;
+}
+
+static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+	*ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB));
+	return 0;
+}
+
+/* see dib3000-watch dvb-apps for exact calcuations of signal_strength and snr */
+static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+	*strength = rd(DIB3000MB_REG_SIGNAL_POWER) * 0xffff / 0x170;
+	return 0;
+}
+
+static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER);
+	int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) |
+		rd(DIB3000MB_REG_NOISE_POWER_LSB);
+	*snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1);
+	return 0;
+}
+
+static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+	*unc = rd(DIB3000MB_REG_UNC);
+	return 0;
+}
+
+static int dib3000mb_sleep(struct dvb_frontend* fe)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	deb_info("dib3000mb is going to bed.\n");
+	wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_DOWN);
+	return 0;
+}
+
+static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+	tune->min_delay_ms = 800;
+	tune->step_size = 166667;
+	tune->max_drift = 166667 * 2;
+
+	return 0;
+}
+
+static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe)
+{
+	return dib3000mb_fe_init(fe, 0);
+}
+
+static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+{
+	return dib3000mb_set_frontend(fe, fep, 1);
+}
+
+static void dib3000mb_release(struct dvb_frontend* fe)
+{
+	struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+/* pid filter and transfer stuff */
+static int dib3000mb_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+{
+	struct dib3000_state *state = fe->demodulator_priv;
+	pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
+	wr(index+DIB3000MB_REG_FIRST_PID,pid);
+	return 0;
+}
+
+static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+	struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+
+	deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling");
+	if (onoff) {
+		wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE);
+	} else {
+		wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT);
+	}
+	return 0;
+}
+
+static int dib3000mb_pid_parse(struct dvb_frontend *fe, int onoff)
+{
+	struct dib3000_state *state = fe->demodulator_priv;
+	deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling");
+	wr(DIB3000MB_REG_PID_PARSE,onoff);
+	return 0;
+}
+
+static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr)
+{
+	struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+	if (onoff) {
+		wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr));
+	} else {
+		wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr));
+	}
+	return 0;
+}
+
+static struct dvb_frontend_ops dib3000mb_ops;
+
+struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+				      struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+{
+	struct dib3000_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+	memset(state,0,sizeof(struct dib3000_state));
+
+	/* setup the state */
+	state->i2c = i2c;
+	memcpy(&state->config,config,sizeof(struct dib3000_config));
+	memcpy(&state->ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check for the correct demod */
+	if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+		goto error;
+
+	if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID)
+		goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+
+	/* set the xfer operations */
+	xfer_ops->pid_parse = dib3000mb_pid_parse;
+	xfer_ops->fifo_ctrl = dib3000mb_fifo_control;
+	xfer_ops->pid_ctrl = dib3000mb_pid_control;
+	xfer_ops->tuner_pass_ctrl = dib3000mb_tuner_pass_ctrl;
+
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops dib3000mb_ops = {
+
+	.info = {
+		.name			= "DiBcom 3000M-B DVB-T",
+		.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 = dib3000mb_release,
+
+	.init = dib3000mb_fe_init_nonmobile,
+	.sleep = dib3000mb_sleep,
+
+	.set_frontend = dib3000mb_set_frontend_and_tuner,
+	.get_frontend = dib3000mb_get_frontend,
+	.get_tune_settings = dib3000mb_fe_get_tune_settings,
+
+	.read_status = dib3000mb_read_status,
+	.read_ber = dib3000mb_read_ber,
+	.read_signal_strength = dib3000mb_read_signal_strength,
+	.read_snr = dib3000mb_read_snr,
+	.read_ucblocks = dib3000mb_read_unc_blocks,
+};
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000mb_attach);
diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h
new file mode 100644
index 0000000..57e61aa
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mb_priv.h
@@ -0,0 +1,467 @@
+/*
+ * dib3000mb_priv.h
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.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.
+ *
+ * for more information see dib3000mb.c .
+ */
+
+#ifndef __DIB3000MB_PRIV_H_INCLUDED__
+#define __DIB3000MB_PRIV_H_INCLUDED__
+
+/* register addresses and some of their default values */
+
+/* restart subsystems */
+#define DIB3000MB_REG_RESTART			(     0)
+
+#define DIB3000MB_RESTART_OFF			(     0)
+#define DIB3000MB_RESTART_AUTO_SEARCH		(1 << 1)
+#define DIB3000MB_RESTART_CTRL				(1 << 2)
+#define DIB3000MB_RESTART_AGC				(1 << 3)
+
+/* FFT size */
+#define DIB3000MB_REG_FFT				(     1)
+
+/* Guard time */
+#define DIB3000MB_REG_GUARD_TIME		(     2)
+
+/* QAM */
+#define DIB3000MB_REG_QAM				(     3)
+
+/* Alpha coefficient high priority Viterbi algorithm */
+#define DIB3000MB_REG_VIT_ALPHA			(     4)
+
+/* spectrum inversion */
+#define DIB3000MB_REG_DDS_INV			(     5)
+
+/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */
+#define DIB3000MB_REG_DDS_FREQ_MSB		(     6)
+#define DIB3000MB_REG_DDS_FREQ_LSB		(     7)
+#define DIB3000MB_DDS_FREQ_MSB				(   178)
+#define DIB3000MB_DDS_FREQ_LSB				(  8990)
+
+/* timing frequency (carrier spacing) */
+static u16 dib3000mb_reg_timing_freq[] = { 8,9 };
+static u16 dib3000mb_timing_freq[][2] = {
+	{ 126 , 48873 }, /* 6 MHz */
+	{ 147 , 57019 }, /* 7 MHz */
+	{ 168 , 65164 }, /* 8 MHz */
+};
+
+/* impulse noise parameter */
+/* 36 ??? */
+
+static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 };
+
+enum dib3000mb_impulse_noise_type {
+	DIB3000MB_IMPNOISE_OFF,
+	DIB3000MB_IMPNOISE_MOBILE,
+	DIB3000MB_IMPNOISE_FIXED,
+	DIB3000MB_IMPNOISE_DEFAULT
+};
+
+static u16 dib3000mb_impulse_noise_values[][5] = {
+	{ 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */
+	{ 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */
+	{ 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */
+	{ 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */
+};
+
+/*
+ * Dual Automatic-Gain-Control
+ * - gains RF in tuner (AGC1)
+ * - gains IF after filtering (AGC2)
+ */
+
+/* also from 16 to 18 */
+static u16 dib3000mb_reg_agc_gain[] = {
+	19,20,21,22,23,24,25,26,27,28,29,30,31,32
+};
+
+static u16 dib3000mb_default_agc_gain[] =
+	{ 0x0001, 52429,   623, 128, 166, 195, 61,   /* RF ??? */
+	  0x0001, 53766, 38011,   0,  90,  33, 23 }; /* IF ??? */
+
+/* phase noise */
+/* 36 is set when setting the impulse noise */
+static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 };
+
+static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 };
+
+/* lock duration */
+static u16 dib3000mb_reg_lock_duration[] = { 39,40 };
+static u16 dib3000mb_default_lock_duration[] = { 135, 135 };
+
+/* AGC loop bandwidth */
+static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 };
+
+static u16 dib3000mb_agc_bandwidth_low[]  =
+	{ 2088, 10, 2088, 10, 3448, 5, 3448, 5 };
+static u16 dib3000mb_agc_bandwidth_high[] =
+	{ 2349,  5, 2349,  5, 2586, 2, 2586, 2 };
+
+/*
+ * lock0 definition (coff_lock)
+ */
+#define DIB3000MB_REG_LOCK0_MASK		(    51)
+#define DIB3000MB_LOCK0_DEFAULT				(     4)
+
+/*
+ * lock1 definition (cpil_lock)
+ * for auto search
+ * which values hide behind the lock masks
+ */
+#define DIB3000MB_REG_LOCK1_MASK		(    52)
+#define DIB3000MB_LOCK1_SEARCH_4			(0x0004)
+#define DIB3000MB_LOCK1_SEARCH_2048			(0x0800)
+#define DIB3000MB_LOCK1_DEFAULT				(0x0001)
+
+/*
+ * lock2 definition (fec_lock) */
+#define DIB3000MB_REG_LOCK2_MASK		(    53)
+#define DIB3000MB_LOCK2_DEFAULT				(0x0080)
+
+/*
+ * SEQ ? what was that again ... :)
+ * changes when, inversion, guard time and fft is
+ * either automatically detected or not
+ */
+#define DIB3000MB_REG_SEQ				(    54)
+
+/* bandwidth */
+static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 };
+static u16 dib3000mb_bandwidth_6mhz[] =
+	{ 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_7mhz[] =
+	{ 0, 28, 64421,  96, 39973, 483,  3255, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_8mhz[] =
+	{ 0, 25, 23600,  84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 };
+
+#define DIB3000MB_REG_UNK_68				(    68)
+#define DIB3000MB_UNK_68						(     0)
+
+#define DIB3000MB_REG_UNK_69				(    69)
+#define DIB3000MB_UNK_69						(     0)
+
+#define DIB3000MB_REG_UNK_71				(    71)
+#define DIB3000MB_UNK_71						(     0)
+
+#define DIB3000MB_REG_UNK_77				(    77)
+#define DIB3000MB_UNK_77						(     6)
+
+#define DIB3000MB_REG_UNK_78				(    78)
+#define DIB3000MB_UNK_78						(0x0080)
+
+/* isi */
+#define DIB3000MB_REG_ISI				(    79)
+#define DIB3000MB_ISI_ACTIVATE				(     0)
+#define DIB3000MB_ISI_INHIBIT				(     1)
+
+/* sync impovement */
+#define DIB3000MB_REG_SYNC_IMPROVEMENT	(    84)
+#define DIB3000MB_SYNC_IMPROVE_2K_1_8		(     3)
+#define DIB3000MB_SYNC_IMPROVE_DEFAULT		(     0)
+
+/* phase noise compensation inhibition */
+#define DIB3000MB_REG_PHASE_NOISE		(    87)
+#define DIB3000MB_PHASE_NOISE_DEFAULT	(     0)
+
+#define DIB3000MB_REG_UNK_92				(    92)
+#define DIB3000MB_UNK_92						(0x0080)
+
+#define DIB3000MB_REG_UNK_96				(    96)
+#define DIB3000MB_UNK_96						(0x0010)
+
+#define DIB3000MB_REG_UNK_97				(    97)
+#define DIB3000MB_UNK_97						(0x0009)
+
+/* mobile mode ??? */
+#define DIB3000MB_REG_MOBILE_MODE		(   101)
+#define DIB3000MB_MOBILE_MODE_ON			(     1)
+#define DIB3000MB_MOBILE_MODE_OFF			(     0)
+
+#define DIB3000MB_REG_UNK_106			(   106)
+#define DIB3000MB_UNK_106					(0x0080)
+
+#define DIB3000MB_REG_UNK_107			(   107)
+#define DIB3000MB_UNK_107					(0x0080)
+
+#define DIB3000MB_REG_UNK_108			(   108)
+#define DIB3000MB_UNK_108					(0x0080)
+
+/* fft */
+#define DIB3000MB_REG_UNK_121			(   121)
+#define DIB3000MB_UNK_121_2K				(     7)
+#define DIB3000MB_UNK_121_DEFAULT			(     5)
+
+#define DIB3000MB_REG_UNK_122			(   122)
+#define DIB3000MB_UNK_122					(  2867)
+
+/* QAM for mobile mode */
+#define DIB3000MB_REG_MOBILE_MODE_QAM	(   126)
+#define DIB3000MB_MOBILE_MODE_QAM_64		(     3)
+#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16	(     1)
+#define DIB3000MB_MOBILE_MODE_QAM_OFF		(     0)
+
+/*
+ * data diversity when having more than one chip on-board
+ * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY
+ */
+#define DIB3000MB_REG_DATA_IN_DIVERSITY		(   127)
+#define DIB3000MB_DATA_DIVERSITY_IN_OFF			(     0)
+#define DIB3000MB_DATA_DIVERSITY_IN_ON			(     2)
+
+/* vit hrch */
+#define DIB3000MB_REG_VIT_HRCH			(   128)
+
+/* vit code rate */
+#define DIB3000MB_REG_VIT_CODE_RATE		(   129)
+
+/* vit select hp */
+#define DIB3000MB_REG_VIT_HP			(   130)
+
+/* time frame for Bit-Error-Rate calculation */
+#define DIB3000MB_REG_BERLEN			(   135)
+#define DIB3000MB_BERLEN_LONG				(     0)
+#define DIB3000MB_BERLEN_DEFAULT			(     1)
+#define DIB3000MB_BERLEN_MEDIUM				(     2)
+#define DIB3000MB_BERLEN_SHORT				(     3)
+
+/* 142 - 152 FIFO parameters
+ * which is what ?
+ */
+
+#define DIB3000MB_REG_FIFO_142			(   142)
+#define DIB3000MB_FIFO_142					(     0)
+
+/* MPEG2 TS output mode */
+#define DIB3000MB_REG_MPEG2_OUT_MODE	(   143)
+#define DIB3000MB_MPEG2_OUT_MODE_204		(     0)
+#define DIB3000MB_MPEG2_OUT_MODE_188		(     1)
+
+#define DIB3000MB_REG_PID_PARSE			(   144)
+#define DIB3000MB_PID_PARSE_INHIBIT		(     0)
+#define DIB3000MB_PID_PARSE_ACTIVATE	(     1)
+
+#define DIB3000MB_REG_FIFO				(   145)
+#define DIB3000MB_FIFO_INHIBIT				(     1)
+#define DIB3000MB_FIFO_ACTIVATE				(     0)
+
+#define DIB3000MB_REG_FIFO_146			(   146)
+#define DIB3000MB_FIFO_146					(     3)
+
+#define DIB3000MB_REG_FIFO_147			(   147)
+#define DIB3000MB_FIFO_147					(0x0100)
+
+/*
+ * pidfilter
+ * it is not a hardware pidfilter but a filter which drops all pids
+ * except the ones set. Necessary because of the limited USB1.1 bandwidth.
+ * regs 153-168
+ */
+
+#define DIB3000MB_REG_FIRST_PID			(   153)
+#define DIB3000MB_NUM_PIDS				(    16)
+
+/*
+ * output mode
+ * USB devices have to use 'slave'-mode
+ * see also DIB3000MB_REG_ELECT_OUT_MODE
+ */
+#define DIB3000MB_REG_OUTPUT_MODE		(   169)
+#define DIB3000MB_OUTPUT_MODE_GATED_CLK		(     0)
+#define DIB3000MB_OUTPUT_MODE_CONT_CLK		(     1)
+#define DIB3000MB_OUTPUT_MODE_SERIAL		(     2)
+#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY	(     5)
+#define DIB3000MB_OUTPUT_MODE_SLAVE			(     6)
+
+/* irq event mask */
+#define DIB3000MB_REG_IRQ_EVENT_MASK		(   170)
+#define DIB3000MB_IRQ_EVENT_MASK				(     0)
+
+/* filter coefficients */
+static u16 dib3000mb_reg_filter_coeffs[] = {
+	171, 172, 173, 174, 175, 176, 177, 178,
+	179, 180, 181, 182, 183, 184, 185, 186,
+	188, 189, 190, 191, 192, 194
+};
+
+static u16 dib3000mb_filter_coeffs[] = {
+	 226,  160,   29,
+ 	 979,  998,   19,
+	  22, 1019, 1006,
+	1022,   12,    6,
+	1017, 1017,    3,
+	   6,       1019,
+	1021,    2,    3,
+	   1,          0,
+};
+
+/*
+ * mobile algorithm (when you are moving with your device)
+ * but not faster than 90 km/h
+ */
+#define DIB3000MB_REG_MOBILE_ALGO		(   195)
+#define DIB3000MB_MOBILE_ALGO_ON			(     0)
+#define DIB3000MB_MOBILE_ALGO_OFF			(     1)
+
+/* multiple demodulators algorithm */
+#define DIB3000MB_REG_MULTI_DEMOD_MSB	(   206)
+#define DIB3000MB_REG_MULTI_DEMOD_LSB	(   207)
+
+/* terminator, no more demods */
+#define DIB3000MB_MULTI_DEMOD_MSB			( 32767)
+#define DIB3000MB_MULTI_DEMOD_LSB			(  4095)
+
+/* bring the device into a known  */
+#define DIB3000MB_REG_RESET_DEVICE		(  1024)
+#define DIB3000MB_RESET_DEVICE				(0x812c)
+#define DIB3000MB_RESET_DEVICE_RST			(     0)
+
+/* hardware clock configuration */
+#define DIB3000MB_REG_CLOCK				(  1027)
+#define DIB3000MB_CLOCK_DEFAULT				(0x9000)
+#define DIB3000MB_CLOCK_DIVERSITY			(0x92b0)
+
+/* power down config */
+#define DIB3000MB_REG_POWER_CONTROL		(  1028)
+#define DIB3000MB_POWER_DOWN				(     1)
+#define DIB3000MB_POWER_UP					(     0)
+
+/* electrical output mode */
+#define DIB3000MB_REG_ELECT_OUT_MODE	(  1029)
+#define DIB3000MB_ELECT_OUT_MODE_OFF		(     0)
+#define DIB3000MB_ELECT_OUT_MODE_ON			(     1)
+
+/* set the tuner i2c address */
+#define DIB3000MB_REG_TUNER				(  1089)
+
+/* monitoring registers (read only) */
+
+/* agc loop locked (size: 1) */
+#define DIB3000MB_REG_AGC_LOCK			(   324)
+
+/* agc power (size: 16) */
+#define DIB3000MB_REG_AGC_POWER			(   325)
+
+/* agc1 value (16) */
+#define DIB3000MB_REG_AGC1_VALUE		(   326)
+
+/* agc2 value (16) */
+#define DIB3000MB_REG_AGC2_VALUE		(   327)
+
+/* total RF power (16), can be used for signal strength */
+#define DIB3000MB_REG_RF_POWER			(   328)
+
+/* dds_frequency with offset (24) */
+#define DIB3000MB_REG_DDS_VALUE_MSB		(   339)
+#define DIB3000MB_REG_DDS_VALUE_LSB		(   340)
+
+/* timing offset signed (24) */
+#define DIB3000MB_REG_TIMING_OFFSET_MSB	(   341)
+#define DIB3000MB_REG_TIMING_OFFSET_LSB	(   342)
+
+/* fft start position (13) */
+#define DIB3000MB_REG_FFT_WINDOW_POS	(   353)
+
+/* carriers locked (1) */
+#define DIB3000MB_REG_CARRIER_LOCK		(   355)
+
+/* noise power (24) */
+#define DIB3000MB_REG_NOISE_POWER_MSB	(   372)
+#define DIB3000MB_REG_NOISE_POWER_LSB	(   373)
+
+#define DIB3000MB_REG_MOBILE_NOISE_MSB	(   374)
+#define DIB3000MB_REG_MOBILE_NOISE_LSB	(   375)
+
+/*
+ * signal power (16), this and the above can be
+ * used to calculate the signal/noise - ratio
+ */
+#define DIB3000MB_REG_SIGNAL_POWER		(   380)
+
+/* mer (24) */
+#define DIB3000MB_REG_MER_MSB			(   381)
+#define DIB3000MB_REG_MER_LSB			(   382)
+
+/*
+ * Transmission Parameter Signalling (TPS)
+ * the following registers can be used to get TPS-information.
+ * The values are according to the DVB-T standard.
+ */
+
+/* TPS locked (1) */
+#define DIB3000MB_REG_TPS_LOCK			(   394)
+
+/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */
+#define DIB3000MB_REG_TPS_QAM			(   398)
+
+/* hierarchy from TPS (1) */
+#define DIB3000MB_REG_TPS_HRCH			(   399)
+
+/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */
+#define DIB3000MB_REG_TPS_VIT_ALPHA		(   400)
+
+/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */
+#define DIB3000MB_REG_TPS_CODE_RATE_HP	(   401)
+
+/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */
+#define DIB3000MB_REG_TPS_CODE_RATE_LP	(   402)
+
+/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */
+#define DIB3000MB_REG_TPS_GUARD_TIME	(   403)
+
+/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */
+#define DIB3000MB_REG_TPS_FFT			(   404)
+
+/* cell id from TPS (16) */
+#define DIB3000MB_REG_TPS_CELL_ID		(   406)
+
+/* TPS (68) */
+#define DIB3000MB_REG_TPS_1				(   408)
+#define DIB3000MB_REG_TPS_2				(   409)
+#define DIB3000MB_REG_TPS_3				(   410)
+#define DIB3000MB_REG_TPS_4				(   411)
+#define DIB3000MB_REG_TPS_5				(   412)
+
+/* bit error rate (before RS correction) (21) */
+#define DIB3000MB_REG_BER_MSB			(   414)
+#define DIB3000MB_REG_BER_LSB			(   415)
+
+/* packet error rate (uncorrected TS packets) (16) */
+#define DIB3000MB_REG_PACKET_ERROR_RATE	(   417)
+
+/* uncorrected packet count (16) */
+#define DIB3000MB_REG_UNC				(   420)
+
+/* viterbi locked (1) */
+#define DIB3000MB_REG_VIT_LCK			(   421)
+
+/* viterbi inidcator (16) */
+#define DIB3000MB_REG_VIT_INDICATOR		(   422)
+
+/* transport stream sync lock (1) */
+#define DIB3000MB_REG_TS_SYNC_LOCK		(   423)
+
+/* transport stream RS lock (1) */
+#define DIB3000MB_REG_TS_RS_LOCK		(   424)
+
+/* lock mask 0 value (1) */
+#define DIB3000MB_REG_LOCK0_VALUE		(   425)
+
+/* lock mask 1 value (1) */
+#define DIB3000MB_REG_LOCK1_VALUE		(   426)
+
+/* lock mask 2 value (1) */
+#define DIB3000MB_REG_LOCK2_VALUE		(   427)
+
+/* interrupt pending for auto search */
+#define DIB3000MB_REG_AS_IRQ_PENDING	(   434)
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
new file mode 100644
index 0000000..4a31c05
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mc.c
@@ -0,0 +1,931 @@
+/*
+ * Frontend driver for mobile DVB-T demodulator DiBcom 3000P/M-C
+ * DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DiBCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@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.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dib3000-common.h"
+#include "dib3000mc_priv.h"
+#include "dib3000.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.1"
+#define DRIVER_DESC "DiBcom 3000M-C DVB-T demodulator"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe,16=stat (|-able)).");
+#endif
+#define deb_info(args...) dprintk(0x01,args)
+#define deb_xfer(args...) dprintk(0x02,args)
+#define deb_setf(args...) dprintk(0x04,args)
+#define deb_getf(args...) dprintk(0x08,args)
+#define deb_stat(args...) dprintk(0x10,args)
+
+static int dib3000mc_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr);
+
+static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode,
+	fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth)
+{
+	switch (transmission_mode) {
+		case TRANSMISSION_MODE_2K:
+			wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[0]);
+			break;
+		case TRANSMISSION_MODE_8K:
+			wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[1]);
+			break;
+		default:
+			break;
+	}
+
+	switch (bandwidth) {
+/*		case BANDWIDTH_5_MHZ:
+			wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[0]);
+			break; */
+		case BANDWIDTH_6_MHZ:
+			wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[1]);
+			break;
+		case BANDWIDTH_7_MHZ:
+			wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[2]);
+			break;
+		case BANDWIDTH_8_MHZ:
+			wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[3]);
+			break;
+		default:
+			break;
+	}
+
+	switch (mode) {
+		case 0: /* no impulse */ /* fall through */
+			wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[0]);
+			break;
+		case 1: /* new algo */
+			wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[1]);
+			set_or(DIB3000MC_REG_IMP_NOISE_55,DIB3000MC_IMP_NEW_ALGO(0)); /* gives 1<<10 */
+			break;
+		default: /* old algo */
+			wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[3]);
+			break;
+	}
+	return 0;
+}
+
+static int dib3000mc_set_timing(struct dib3000_state *state, int upd_offset,
+		fe_transmit_mode_t fft, fe_bandwidth_t bw)
+{
+	u16 timf_msb,timf_lsb;
+	s32 tim_offset,tim_sgn;
+	u64 comp1,comp2,comp=0;
+
+	switch (bw) {
+		case BANDWIDTH_8_MHZ: comp = DIB3000MC_CLOCK_REF*8; break;
+		case BANDWIDTH_7_MHZ: comp = DIB3000MC_CLOCK_REF*7; break;
+		case BANDWIDTH_6_MHZ: comp = DIB3000MC_CLOCK_REF*6; break;
+		default: err("unknown bandwidth (%d)",bw); break;
+	}
+	timf_msb = (comp >> 16) & 0xff;
+	timf_lsb = (comp & 0xffff);
+
+	// Update the timing offset ;
+	if (upd_offset > 0) {
+		if (!state->timing_offset_comp_done) {
+			msleep(200);
+			state->timing_offset_comp_done = 1;
+		}
+		tim_offset = rd(DIB3000MC_REG_TIMING_OFFS_MSB);
+		if ((tim_offset & 0x2000) == 0x2000)
+			tim_offset |= 0xC000;
+		if (fft == TRANSMISSION_MODE_2K)
+			tim_offset <<= 2;
+		state->timing_offset += tim_offset;
+	}
+
+	tim_offset = state->timing_offset;
+	if (tim_offset < 0) {
+		tim_sgn = 1;
+		tim_offset = -tim_offset;
+	} else
+		tim_sgn = 0;
+
+	comp1 =  (u32)tim_offset * (u32)timf_lsb ;
+	comp2 =  (u32)tim_offset * (u32)timf_msb ;
+	comp  = ((comp1 >> 16) + comp2) >> 7;
+
+	if (tim_sgn == 0)
+		comp = (u32)(timf_msb << 16) + (u32) timf_lsb + comp;
+	else
+		comp = (u32)(timf_msb << 16) + (u32) timf_lsb - comp ;
+
+	timf_msb = (comp >> 16) & 0xff;
+	timf_lsb = comp & 0xffff;
+
+	wr(DIB3000MC_REG_TIMING_FREQ_MSB,timf_msb);
+	wr(DIB3000MC_REG_TIMING_FREQ_LSB,timf_lsb);
+	return 0;
+}
+
+static int dib3000mc_init_auto_scan(struct dib3000_state *state, fe_bandwidth_t bw, int boost)
+{
+	if (boost) {
+		wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_ON);
+	} else {
+		wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_OFF);
+	}
+	switch (bw) {
+		case BANDWIDTH_8_MHZ:
+			wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
+			break;
+		case BANDWIDTH_7_MHZ:
+			wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_7mhz);
+			break;
+		case BANDWIDTH_6_MHZ:
+			wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_6mhz);
+			break;
+/*		case BANDWIDTH_5_MHZ:
+			wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_5mhz);
+			break;*/
+		case BANDWIDTH_AUTO:
+			return -EOPNOTSUPP;
+		default:
+			err("unknown bandwidth value (%d).",bw);
+			return -EINVAL;
+	}
+	if (boost) {
+		u32 timeout = (rd(DIB3000MC_REG_BW_TIMOUT_MSB) << 16) +
+			rd(DIB3000MC_REG_BW_TIMOUT_LSB);
+		timeout *= 85; timeout >>= 7;
+		wr(DIB3000MC_REG_BW_TIMOUT_MSB,(timeout >> 16) & 0xffff);
+		wr(DIB3000MC_REG_BW_TIMOUT_LSB,timeout & 0xffff);
+	}
+	return 0;
+}
+
+static int dib3000mc_set_adp_cfg(struct dib3000_state *state, fe_modulation_t con)
+{
+	switch (con) {
+		case QAM_64:
+			wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[2]);
+			break;
+		case QAM_16:
+			wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[1]);
+			break;
+		case QPSK:
+			wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[0]);
+			break;
+		case QAM_AUTO:
+			break;
+		default:
+			warn("unkown constellation.");
+			break;
+	}
+	return 0;
+}
+
+static int dib3000mc_set_general_cfg(struct dib3000_state *state, struct dvb_frontend_parameters *fep, int *auto_val)
+{
+	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+	fe_code_rate_t fe_cr = FEC_NONE;
+	u8 fft=0, guard=0, qam=0, alpha=0, sel_hp=0, cr=0, hrch=0;
+	int seq;
+
+	switch (ofdm->transmission_mode) {
+		case TRANSMISSION_MODE_2K: fft = DIB3000_TRANSMISSION_MODE_2K; break;
+		case TRANSMISSION_MODE_8K: fft = DIB3000_TRANSMISSION_MODE_8K; break;
+		case TRANSMISSION_MODE_AUTO: break;
+		default: return -EINVAL;
+	}
+	switch (ofdm->guard_interval) {
+		case GUARD_INTERVAL_1_32: guard = DIB3000_GUARD_TIME_1_32; break;
+		case GUARD_INTERVAL_1_16: guard = DIB3000_GUARD_TIME_1_16; break;
+		case GUARD_INTERVAL_1_8:  guard = DIB3000_GUARD_TIME_1_8; break;
+		case GUARD_INTERVAL_1_4:  guard = DIB3000_GUARD_TIME_1_4; break;
+		case GUARD_INTERVAL_AUTO: break;
+		default: return -EINVAL;
+	}
+	switch (ofdm->constellation) {
+		case QPSK:   qam = DIB3000_CONSTELLATION_QPSK; break;
+		case QAM_16: qam = DIB3000_CONSTELLATION_16QAM; break;
+		case QAM_64: qam = DIB3000_CONSTELLATION_64QAM; break;
+		case QAM_AUTO: break;
+		default: return -EINVAL;
+	}
+	switch (ofdm->hierarchy_information) {
+		case HIERARCHY_NONE: /* fall through */
+		case HIERARCHY_1: alpha = DIB3000_ALPHA_1; break;
+		case HIERARCHY_2: alpha = DIB3000_ALPHA_2; break;
+		case HIERARCHY_4: alpha = DIB3000_ALPHA_4; break;
+		case HIERARCHY_AUTO: break;
+		default: return -EINVAL;
+	}
+	if (ofdm->hierarchy_information == HIERARCHY_NONE) {
+		hrch   = DIB3000_HRCH_OFF;
+		sel_hp = DIB3000_SELECT_HP;
+		fe_cr  = ofdm->code_rate_HP;
+	} else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
+		hrch   = DIB3000_HRCH_ON;
+		sel_hp = DIB3000_SELECT_LP;
+		fe_cr  = ofdm->code_rate_LP;
+	}
+	switch (fe_cr) {
+		case FEC_1_2: cr = DIB3000_FEC_1_2; break;
+		case FEC_2_3: cr = DIB3000_FEC_2_3; break;
+		case FEC_3_4: cr = DIB3000_FEC_3_4; break;
+		case FEC_5_6: cr = DIB3000_FEC_5_6; break;
+		case FEC_7_8: cr = DIB3000_FEC_7_8; break;
+		case FEC_NONE: break;
+		case FEC_AUTO: break;
+		default: return -EINVAL;
+	}
+
+	wr(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_PARM(alpha,qam,guard,fft));
+	wr(DIB3000MC_REG_HRCH_PARM,DIB3000MC_HRCH_PARM(sel_hp,cr,hrch));
+
+	switch (fep->inversion) {
+		case INVERSION_OFF:
+			wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+			break;
+		case INVERSION_AUTO: /* fall through */
+		case INVERSION_ON:
+			wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_ON);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	seq = dib3000_seq
+		[ofdm->transmission_mode == TRANSMISSION_MODE_AUTO]
+		[ofdm->guard_interval == GUARD_INTERVAL_AUTO]
+		[fep->inversion == INVERSION_AUTO];
+
+	deb_setf("seq? %d\n", seq);
+	wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS(seq,1));
+	*auto_val = ofdm->constellation == QAM_AUTO ||
+			ofdm->hierarchy_information == HIERARCHY_AUTO ||
+			ofdm->guard_interval == GUARD_INTERVAL_AUTO ||
+			ofdm->transmission_mode == TRANSMISSION_MODE_AUTO ||
+			fe_cr == FEC_AUTO ||
+			fep->inversion == INVERSION_AUTO;
+	return 0;
+}
+
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+	fe_code_rate_t *cr;
+	u16 tps_val,cr_val;
+	int inv_test1,inv_test2;
+	u32 dds_val, threshold = 0x1000000;
+
+	if (!(rd(DIB3000MC_REG_LOCK_507) & DIB3000MC_LOCK_507))
+		return 0;
+
+	dds_val = (rd(DIB3000MC_REG_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_DDS_FREQ_LSB);
+	deb_getf("DDS_FREQ: %6x\n",dds_val);
+	if (dds_val < threshold)
+		inv_test1 = 0;
+	else if (dds_val == threshold)
+		inv_test1 = 1;
+	else
+		inv_test1 = 2;
+
+	dds_val = (rd(DIB3000MC_REG_SET_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_SET_DDS_FREQ_LSB);
+	deb_getf("DDS_SET_FREQ: %6x\n",dds_val);
+	if (dds_val < threshold)
+		inv_test2 = 0;
+	else if (dds_val == threshold)
+		inv_test2 = 1;
+	else
+		inv_test2 = 2;
+
+	fep->inversion =
+		((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
+		((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
+		INVERSION_ON : INVERSION_OFF;
+
+	deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+
+	fep->frequency = state->last_tuned_freq;
+	fep->u.ofdm.bandwidth= state->last_tuned_bw;
+
+	tps_val = rd(DIB3000MC_REG_TUNING_PARM);
+
+	switch (DIB3000MC_TP_QAM(tps_val)) {
+		case DIB3000_CONSTELLATION_QPSK:
+			deb_getf("QPSK ");
+			ofdm->constellation = QPSK;
+			break;
+		case DIB3000_CONSTELLATION_16QAM:
+			deb_getf("QAM16 ");
+			ofdm->constellation = QAM_16;
+			break;
+		case DIB3000_CONSTELLATION_64QAM:
+			deb_getf("QAM64 ");
+			ofdm->constellation = QAM_64;
+			break;
+		default:
+			err("Unexpected constellation returned by TPS (%d)", tps_val);
+			break;
+	}
+
+	if (DIB3000MC_TP_HRCH(tps_val)) {
+		deb_getf("HRCH ON ");
+		cr = &ofdm->code_rate_LP;
+		ofdm->code_rate_HP = FEC_NONE;
+		switch (DIB3000MC_TP_ALPHA(tps_val)) {
+			case DIB3000_ALPHA_0:
+				deb_getf("HIERARCHY_NONE ");
+				ofdm->hierarchy_information = HIERARCHY_NONE;
+				break;
+			case DIB3000_ALPHA_1:
+				deb_getf("HIERARCHY_1 ");
+				ofdm->hierarchy_information = HIERARCHY_1;
+				break;
+			case DIB3000_ALPHA_2:
+				deb_getf("HIERARCHY_2 ");
+				ofdm->hierarchy_information = HIERARCHY_2;
+				break;
+			case DIB3000_ALPHA_4:
+				deb_getf("HIERARCHY_4 ");
+				ofdm->hierarchy_information = HIERARCHY_4;
+				break;
+			default:
+				err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
+				break;
+		}
+		cr_val = DIB3000MC_TP_FEC_CR_LP(tps_val);
+	} else {
+		deb_getf("HRCH OFF ");
+		cr = &ofdm->code_rate_HP;
+		ofdm->code_rate_LP = FEC_NONE;
+		ofdm->hierarchy_information = HIERARCHY_NONE;
+		cr_val = DIB3000MC_TP_FEC_CR_HP(tps_val);
+	}
+
+	switch (cr_val) {
+		case DIB3000_FEC_1_2:
+			deb_getf("FEC_1_2 ");
+			*cr = FEC_1_2;
+			break;
+		case DIB3000_FEC_2_3:
+			deb_getf("FEC_2_3 ");
+			*cr = FEC_2_3;
+			break;
+		case DIB3000_FEC_3_4:
+			deb_getf("FEC_3_4 ");
+			*cr = FEC_3_4;
+			break;
+		case DIB3000_FEC_5_6:
+			deb_getf("FEC_5_6 ");
+			*cr = FEC_4_5;
+			break;
+		case DIB3000_FEC_7_8:
+			deb_getf("FEC_7_8 ");
+			*cr = FEC_7_8;
+			break;
+		default:
+			err("Unexpected FEC returned by TPS (%d)", tps_val);
+			break;
+	}
+
+	switch (DIB3000MC_TP_GUARD(tps_val)) {
+		case DIB3000_GUARD_TIME_1_32:
+			deb_getf("GUARD_INTERVAL_1_32 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case DIB3000_GUARD_TIME_1_16:
+			deb_getf("GUARD_INTERVAL_1_16 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case DIB3000_GUARD_TIME_1_8:
+			deb_getf("GUARD_INTERVAL_1_8 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case DIB3000_GUARD_TIME_1_4:
+			deb_getf("GUARD_INTERVAL_1_4 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		default:
+			err("Unexpected Guard Time returned by TPS (%d)", tps_val);
+			break;
+	}
+
+	switch (DIB3000MC_TP_FFT(tps_val)) {
+		case DIB3000_TRANSMISSION_MODE_2K:
+			deb_getf("TRANSMISSION_MODE_2K ");
+			ofdm->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case DIB3000_TRANSMISSION_MODE_8K:
+			deb_getf("TRANSMISSION_MODE_8K ");
+			ofdm->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		default:
+			err("unexpected transmission mode return by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("\n");
+
+	return 0;
+}
+
+static int dib3000mc_set_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep, int tuner)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+	int search_state,auto_val;
+	u16 val;
+
+	if (tuner) { /* initial call from dvb */
+		dib3000mc_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+		state->config.pll_set(fe,fep,NULL);
+		dib3000mc_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+
+		state->last_tuned_freq = fep->frequency;
+	//	if (!scanboost) {
+			dib3000mc_set_timing(state,0,ofdm->transmission_mode,ofdm->bandwidth);
+			dib3000mc_init_auto_scan(state, ofdm->bandwidth, 0);
+			state->last_tuned_bw = ofdm->bandwidth;
+
+			wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth);
+			wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_AGC);
+			wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
+
+			/* Default cfg isi offset adp */
+			wr_foreach(dib3000mc_reg_offset,dib3000mc_offset[0]);
+
+			wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT | DIB3000MC_ISI_INHIBIT);
+			dib3000mc_set_adp_cfg(state,ofdm->constellation);
+			wr(DIB3000MC_REG_UNK_133,DIB3000MC_UNK_133);
+
+			wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
+			/* power smoothing */
+			if (ofdm->bandwidth != BANDWIDTH_8_MHZ) {
+				wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[0]);
+			} else {
+				wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[3]);
+			}
+			auto_val = 0;
+			dib3000mc_set_general_cfg(state,fep,&auto_val);
+			dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth);
+
+			val = rd(DIB3000MC_REG_DEMOD_PARM);
+			wr(DIB3000MC_REG_DEMOD_PARM,val|DIB3000MC_DEMOD_RST_DEMOD_ON);
+			wr(DIB3000MC_REG_DEMOD_PARM,val);
+	//	}
+		msleep(70);
+
+		/* something has to be auto searched */
+		if (auto_val) {
+			int as_count=0;
+
+			deb_setf("autosearch enabled.\n");
+
+			val = rd(DIB3000MC_REG_DEMOD_PARM);
+			wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
+			wr(DIB3000MC_REG_DEMOD_PARM,val);
+
+			while ((search_state = dib3000_search_status(
+						rd(DIB3000MC_REG_AS_IRQ),1)) < 0 && as_count++ < 100)
+				msleep(10);
+
+			deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count);
+
+			if (search_state == 1) {
+				struct dvb_frontend_parameters feps;
+				if (dib3000mc_get_frontend(fe, &feps) == 0) {
+					deb_setf("reading tuning data from frontend succeeded.\n");
+					return dib3000mc_set_frontend(fe, &feps, 0);
+				}
+			}
+		} else {
+			dib3000mc_set_impulse_noise(state,0,ofdm->transmission_mode,ofdm->bandwidth);
+			wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE);
+			dib3000mc_set_adp_cfg(state,ofdm->constellation);
+
+			/* set_offset_cfg */
+			wr_foreach(dib3000mc_reg_offset,
+					dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]);
+		}
+	} else { /* second call, after autosearch (fka: set_WithKnownParams) */
+//		dib3000mc_set_timing(state,1,ofdm->transmission_mode,ofdm->bandwidth);
+
+		auto_val = 0;
+		dib3000mc_set_general_cfg(state,fep,&auto_val);
+		if (auto_val)
+			deb_info("auto_val is true, even though an auto search was already performed.\n");
+
+		dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth);
+
+		val = rd(DIB3000MC_REG_DEMOD_PARM);
+		wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
+		wr(DIB3000MC_REG_DEMOD_PARM,val);
+
+		msleep(30);
+
+		wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE);
+			dib3000mc_set_adp_cfg(state,ofdm->constellation);
+		wr_foreach(dib3000mc_reg_offset,
+				dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]);
+
+
+	}
+	return 0;
+}
+
+static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode)
+{
+	struct dib3000_state *state;
+
+	deb_info("init start\n");
+
+	state = fe->demodulator_priv;
+	state->timing_offset = 0;
+	state->timing_offset_comp_done = 0;
+
+	wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_CONFIG);
+	wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
+	wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_UP);
+	wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_PUP_MOBILE);
+	wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_UP);
+	wr(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_INIT);
+
+	wr(DIB3000MC_REG_RST_UNC,DIB3000MC_RST_UNC_OFF);
+	wr(DIB3000MC_REG_UNK_19,DIB3000MC_UNK_19);
+
+	wr(33,5);
+	wr(36,81);
+	wr(DIB3000MC_REG_UNK_88,DIB3000MC_UNK_88);
+
+	wr(DIB3000MC_REG_UNK_99,DIB3000MC_UNK_99);
+	wr(DIB3000MC_REG_UNK_111,DIB3000MC_UNK_111_PH_N_MODE_0); /* phase noise algo off */
+
+	/* mobile mode - portable reception */
+	wr_foreach(dib3000mc_reg_mobile_mode,dib3000mc_mobile_mode[1]);
+
+/* TUNER_PANASONIC_ENV57H12D5: */
+	wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth);
+	wr_foreach(dib3000mc_reg_agc_bandwidth_general,dib3000mc_agc_bandwidth_general);
+	wr_foreach(dib3000mc_reg_agc,dib3000mc_agc_tuner[1]);
+
+	wr(DIB3000MC_REG_UNK_110,DIB3000MC_UNK_110);
+	wr(26,0x6680);
+	wr(DIB3000MC_REG_UNK_1,DIB3000MC_UNK_1);
+	wr(DIB3000MC_REG_UNK_2,DIB3000MC_UNK_2);
+	wr(DIB3000MC_REG_UNK_3,DIB3000MC_UNK_3);
+	wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS_DEFAULT);
+
+	wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
+	wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
+
+	wr(DIB3000MC_REG_UNK_4,DIB3000MC_UNK_4);
+
+	wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+	wr(DIB3000MC_REG_SET_DDS_FREQ_LSB,DIB3000MC_DDS_FREQ_LSB);
+
+	dib3000mc_set_timing(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
+//	wr_foreach(dib3000mc_reg_timing_freq,dib3000mc_timing_freq[3]);
+
+	wr(DIB3000MC_REG_UNK_120,DIB3000MC_UNK_120);
+	wr(DIB3000MC_REG_UNK_134,DIB3000MC_UNK_134);
+	wr(DIB3000MC_REG_FEC_CFG,DIB3000MC_FEC_CFG);
+
+	wr(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+
+	dib3000mc_set_impulse_noise(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
+
+/* output mode control, just the MPEG2_SLAVE */
+//	set_or(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE);
+	wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE);
+	wr(DIB3000MC_REG_SMO_MODE,DIB3000MC_SMO_MODE_SLAVE);
+	wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_SLAVE);
+	wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_SLAVE);
+
+/* MPEG2_PARALLEL_CONTINUOUS_CLOCK
+	wr(DIB3000MC_REG_OUTMODE,
+		DIB3000MC_SET_OUTMODE(DIB3000MC_OM_PAR_CONT_CLK,
+			rd(DIB3000MC_REG_OUTMODE)));
+
+	wr(DIB3000MC_REG_SMO_MODE,
+			DIB3000MC_SMO_MODE_DEFAULT |
+			DIB3000MC_SMO_MODE_188);
+
+	wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_DEFAULT);
+	wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
+*/
+
+/* diversity */
+	wr(DIB3000MC_REG_DIVERSITY1,DIB3000MC_DIVERSITY1_DEFAULT);
+	wr(DIB3000MC_REG_DIVERSITY2,DIB3000MC_DIVERSITY2_DEFAULT);
+
+	set_and(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+
+	set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF);
+
+/*	if (state->config->pll_init) {
+		dib3000mc_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+		state->config->pll_init(fe,NULL);
+		dib3000mc_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+	}*/
+	deb_info("init end\n");
+	return 0;
+}
+static int dib3000mc_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	u16 lock = rd(DIB3000MC_REG_LOCKING);
+
+	*stat = 0;
+	if (DIB3000MC_AGC_LOCK(lock))
+		*stat |= FE_HAS_SIGNAL;
+	if (DIB3000MC_CARRIER_LOCK(lock))
+		*stat |= FE_HAS_CARRIER;
+	if (DIB3000MC_TPS_LOCK(lock))
+		*stat |= FE_HAS_VITERBI;
+	if (DIB3000MC_MPEG_SYNC_LOCK(lock))
+		*stat |= (FE_HAS_SYNC | FE_HAS_LOCK);
+
+	deb_stat("actual status is %2x fifo_level: %x,244: %x, 206: %x, 207: %x, 1040: %x\n",*stat,rd(510),rd(244),rd(206),rd(207),rd(1040));
+
+	return 0;
+}
+
+static int dib3000mc_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	*ber = ((rd(DIB3000MC_REG_BER_MSB) << 16) | rd(DIB3000MC_REG_BER_LSB));
+	return 0;
+}
+
+static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+	*unc = rd(DIB3000MC_REG_PACKET_ERROR_COUNT);
+	return 0;
+}
+
+/* see dib3000mb.c for calculation comments */
+static int dib3000mc_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB);
+	*strength = (((val >> 6) & 0xff) << 8) + (val & 0x3f);
+
+	deb_stat("signal: mantisse = %d, exponent = %d\n",(*strength >> 8) & 0xff, *strength & 0xff);
+	return 0;
+}
+
+/* see dib3000mb.c for calculation comments */
+static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+	u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB),
+		val2 = rd(DIB3000MC_REG_SIGNAL_NOISE_MSB);
+	u16 sig,noise;
+
+	sig =   (((val >> 6) & 0xff) << 8) + (val & 0x3f);
+	noise = (((val >> 4) & 0xff) << 8) + ((val & 0xf) << 2) + ((val2 >> 14) & 0x3);
+	if (noise == 0)
+		*snr = 0xffff;
+	else
+		*snr = (u16) sig/noise;
+
+	deb_stat("signal: mantisse = %d, exponent = %d\n",(sig >> 8) & 0xff, sig & 0xff);
+	deb_stat("noise:  mantisse = %d, exponent = %d\n",(noise >> 8) & 0xff, noise & 0xff);
+	deb_stat("snr: %d\n",*snr);
+	return 0;
+}
+
+static int dib3000mc_sleep(struct dvb_frontend* fe)
+{
+	struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+	set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_PWR_DOWN);
+	wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_DOWN);
+	wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_POWER_DOWN);
+	wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_DOWN);
+	return 0;
+}
+
+static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+	tune->min_delay_ms = 2000;
+	tune->step_size = 166667;
+	tune->max_drift = 166667 * 2;
+
+	return 0;
+}
+
+static int dib3000mc_fe_init_nonmobile(struct dvb_frontend* fe)
+{
+	return dib3000mc_fe_init(fe, 0);
+}
+
+static int dib3000mc_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+{
+	return dib3000mc_set_frontend(fe, fep, 1);
+}
+
+static void dib3000mc_release(struct dvb_frontend* fe)
+{
+	struct dib3000_state *state = (struct dib3000_state *) fe->demodulator_priv;
+	kfree(state);
+}
+
+/* pid filter and transfer stuff */
+static int dib3000mc_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+{
+	struct dib3000_state *state = fe->demodulator_priv;
+	pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
+	wr(index+DIB3000MC_REG_FIRST_PID,pid);
+	return 0;
+}
+
+static int dib3000mc_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+	struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+	u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
+
+	deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling");
+
+	if (onoff) {
+		deb_xfer("%d %x\n",tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH);
+		wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH);
+	} else {
+		deb_xfer("%d %x\n",tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH);
+		wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH);
+	}
+	return 0;
+}
+
+static int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
+{
+	struct dib3000_state *state = fe->demodulator_priv;
+	u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
+
+	deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling");
+
+	if (onoff) {
+		wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_PID_PARSE);
+	} else {
+		wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_NO_PID_PARSE);
+	}
+	return 0;
+}
+
+static int dib3000mc_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr)
+{
+	struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+	if (onoff) {
+		wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr));
+	} else {
+		wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr));
+	}
+	return 0;
+}
+
+static int dib3000mc_demod_init(struct dib3000_state *state)
+{
+	u16 default_addr = 0x0a;
+	/* first init */
+	if (state->config.demod_address != default_addr) {
+		deb_info("initializing the demod the first time. Setting demod addr to 0x%x\n",default_addr);
+		wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
+		wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_PAR_CONT_CLK);
+
+		wr(DIB3000MC_REG_RST_I2C_ADDR,
+			DIB3000MC_DEMOD_ADDR(default_addr) |
+			DIB3000MC_DEMOD_ADDR_ON);
+
+		state->config.demod_address = default_addr;
+
+		wr(DIB3000MC_REG_RST_I2C_ADDR,
+			DIB3000MC_DEMOD_ADDR(default_addr));
+	} else
+		deb_info("demod is already initialized. Demod addr: 0x%x\n",state->config.demod_address);
+	return 0;
+}
+
+
+static struct dvb_frontend_ops dib3000mc_ops;
+
+struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
+				      struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+{
+	struct dib3000_state* state = NULL;
+	u16 devid;
+
+	/* allocate memory for the internal state */
+	state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+	memset(state,0,sizeof(struct dib3000_state));
+
+	/* setup the state */
+	state->i2c = i2c;
+	memcpy(&state->config,config,sizeof(struct dib3000_config));
+	memcpy(&state->ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check for the correct demod */
+	if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+		goto error;
+
+	devid = rd(DIB3000_REG_DEVICE_ID);
+	if (devid != DIB3000MC_DEVICE_ID && devid != DIB3000P_DEVICE_ID)
+		goto error;
+
+	switch (devid) {
+		case DIB3000MC_DEVICE_ID:
+			info("Found a DiBcom 3000M-C, interesting...");
+			break;
+		case DIB3000P_DEVICE_ID:
+			info("Found a DiBcom 3000P.");
+			break;
+	}
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+
+	/* set the xfer operations */
+	xfer_ops->pid_parse = dib3000mc_pid_parse;
+	xfer_ops->fifo_ctrl = dib3000mc_fifo_control;
+	xfer_ops->pid_ctrl = dib3000mc_pid_control;
+	xfer_ops->tuner_pass_ctrl = dib3000mc_tuner_pass_ctrl;
+
+	dib3000mc_demod_init(state);
+
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops dib3000mc_ops = {
+
+	.info = {
+		.name			= "DiBcom 3000P/M-C DVB-T",
+		.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 = dib3000mc_release,
+
+	.init = dib3000mc_fe_init_nonmobile,
+	.sleep = dib3000mc_sleep,
+
+	.set_frontend = dib3000mc_set_frontend_and_tuner,
+	.get_frontend = dib3000mc_get_frontend,
+	.get_tune_settings = dib3000mc_fe_get_tune_settings,
+
+	.read_status = dib3000mc_read_status,
+	.read_ber = dib3000mc_read_ber,
+	.read_signal_strength = dib3000mc_read_signal_strength,
+	.read_snr = dib3000mc_read_snr,
+	.read_ucblocks = dib3000mc_read_unc_blocks,
+};
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000mc_attach);
diff --git a/drivers/media/dvb/frontends/dib3000mc_priv.h b/drivers/media/dvb/frontends/dib3000mc_priv.h
new file mode 100644
index 0000000..2930aac
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mc_priv.h
@@ -0,0 +1,428 @@
+/*
+ * dib3000mc_priv.h
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.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.
+ *
+ * for more information see dib3000mc.c .
+ */
+
+#ifndef __DIB3000MC_PRIV_H__
+#define __DIB3000MC_PRIV_H__
+
+/*
+ * Demodulator parameters
+ * reg: 0  1 1  1 11 11 111
+ *         | |  |  |  |  |
+ *         | |  |  |  |  +-- alpha (000=0, 001=1, 010=2, 100=4)
+ *         | |  |  |  +----- constellation (00=QPSK, 01=16QAM, 10=64QAM)
+ *         | |  |  +-------- guard (00=1/32, 01=1/16, 10=1/8, 11=1/4)
+ *         | |  +----------- transmission mode (0=2k, 1=8k)
+ *         | |
+ *         | +-------------- restart autosearch for parameters
+ *         +---------------- restart the demodulator
+ * reg: 181      1 111 1
+ *               |  |  |
+ *               |  |  +- FEC applies for HP or LP (0=LP, 1=HP)
+ *               |  +---- FEC rate (001=1/2, 010=2/3, 011=3/4, 101=5/6, 111=7/8)
+ *               +------- hierarchy on (0=no, 1=yes)
+ */
+
+/* demodulator tuning parameter and restart options */
+#define DIB3000MC_REG_DEMOD_PARM		(     0)
+#define DIB3000MC_DEMOD_PARM(a,c,g,t)	( \
+		 (0x7 & a) | \
+		((0x3 & c) << 3) | \
+		((0x3 & g) << 5) | \
+		((0x1 & t) << 7) )
+#define DIB3000MC_DEMOD_RST_AUTO_SRCH_ON	(1 << 8)
+#define DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF	(0 << 8)
+#define DIB3000MC_DEMOD_RST_DEMOD_ON		(1 << 9)
+#define DIB3000MC_DEMOD_RST_DEMOD_OFF		(0 << 9)
+
+/* register for hierarchy parameters */
+#define DIB3000MC_REG_HRCH_PARM			(   181)
+#define DIB3000MC_HRCH_PARM(s,f,h)		( \
+		 (0x1 & s) | \
+		((0x7 & f) << 1) | \
+		((0x1 & h) << 4) )
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_1				(     1)
+#define DIB3000MC_UNK_1					(  0x04)
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_2				(     2)
+#define DIB3000MC_UNK_2					(  0x04)
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_3				(     3)
+#define DIB3000MC_UNK_3					(0x1000)
+
+#define DIB3000MC_REG_UNK_4				(     4)
+#define DIB3000MC_UNK_4					(0x0814)
+
+/* timeout ??? */
+#define DIB3000MC_REG_SEQ_TPS			(     5)
+#define DIB3000MC_SEQ_TPS_DEFAULT		(     1)
+#define DIB3000MC_SEQ_TPS(s,t)			( \
+		((s & 0x0f) << 4) | \
+		((t & 0x01) << 8) )
+#define DIB3000MC_IS_TPS(v)				((v << 8) & 0x1)
+#define DIB3000MC_IS_AS(v)				((v >> 4) & 0xf)
+
+/* parameters for the bandwidth */
+#define DIB3000MC_REG_BW_TIMOUT_MSB		(     6)
+#define DIB3000MC_REG_BW_TIMOUT_LSB		(     7)
+
+static u16 dib3000mc_reg_bandwidth[] = { 6,7,8,9,10,11,16,17 };
+
+/*static u16 dib3000mc_bandwidth_5mhz[] =
+	{ 0x28, 0x9380, 0x87, 0x4100, 0x2a4, 0x4500, 0x1, 0xb0d0 };*/
+
+static u16 dib3000mc_bandwidth_6mhz[] =
+	{ 0x21, 0xd040, 0x70, 0xb62b, 0x233, 0x8ed5, 0x1, 0xb0d0 };
+
+static u16 dib3000mc_bandwidth_7mhz[] =
+	{ 0x1c, 0xfba5, 0x60, 0x9c25, 0x1e3, 0x0cb7, 0x1, 0xb0d0 };
+
+static u16 dib3000mc_bandwidth_8mhz[] =
+	{ 0x19, 0x5c30, 0x54, 0x88a0, 0x1a6, 0xab20, 0x1, 0xb0d0 };
+
+static u16 dib3000mc_reg_bandwidth_general[] = { 12,13,14,15 };
+static u16 dib3000mc_bandwidth_general[] = { 0x0000, 0x03e8, 0x0000, 0x03f2 };
+
+/* lock mask */
+#define DIB3000MC_REG_LOCK_MASK			(    15)
+#define DIB3000MC_ACTIVATE_LOCK_MASK	(0x0800)
+
+/* reset the uncorrected packet count (??? do it 5 times) */
+#define DIB3000MC_REG_RST_UNC			(    18)
+#define DIB3000MC_RST_UNC_ON			(     1)
+#define DIB3000MC_RST_UNC_OFF			(     0)
+
+#define DIB3000MC_REG_UNK_19			(    19)
+#define DIB3000MC_UNK_19				(     0)
+
+/* DDS frequency value (IF position) and inversion bit */
+#define DIB3000MC_REG_INVERSION			(    21)
+#define DIB3000MC_REG_SET_DDS_FREQ_MSB	(    21)
+#define DIB3000MC_DDS_FREQ_MSB_INV_OFF	(0x0164)
+#define DIB3000MC_DDS_FREQ_MSB_INV_ON	(0x0364)
+
+#define DIB3000MC_REG_SET_DDS_FREQ_LSB	(    22)
+#define DIB3000MC_DDS_FREQ_LSB			(0x463d)
+
+/* timing frequencies setting */
+#define DIB3000MC_REG_TIMING_FREQ_MSB	(    23)
+#define DIB3000MC_REG_TIMING_FREQ_LSB	(    24)
+#define DIB3000MC_CLOCK_REF				(0x151fd1)
+
+//static u16 dib3000mc_reg_timing_freq[] = { 23,24 };
+
+//static u16 dib3000mc_timing_freq[][2] = {
+//	{ 0x69, 0x9f18 }, /* 5 MHz */
+//	{ 0x7e ,0xbee9 }, /* 6 MHz */
+//	{ 0x93 ,0xdebb }, /* 7 MHz */
+//	{ 0xa8 ,0xfe8c }, /* 8 MHz */
+//};
+
+/* timeout ??? */
+static u16 dib3000mc_reg_offset[] = { 26,33 };
+
+static u16 dib3000mc_offset[][2] = {
+	{ 26240, 5 }, /* default */
+	{ 30336, 6 }, /* 8K */
+	{ 38528, 8 }, /* 2K */
+};
+
+#define DIB3000MC_REG_ISI				(    29)
+#define DIB3000MC_ISI_DEFAULT			(0x1073)
+#define DIB3000MC_ISI_ACTIVATE			(0x0000)
+#define DIB3000MC_ISI_INHIBIT			(0x0200)
+
+/* impulse noise control */
+static u16 dib3000mc_reg_imp_noise_ctl[] = { 34,35 };
+
+static u16 dib3000mc_imp_noise_ctl[][2] = {
+	{ 0x1294, 0x1ff8 }, /* mode 0 */
+	{ 0x1294, 0x1ff8 }, /* mode 1 */
+	{ 0x1294, 0x1ff8 }, /* mode 2 */
+	{ 0x1294, 0x1ff8 }, /* mode 3 */
+	{ 0x1294, 0x1ff8 }, /* mode 4 */
+};
+
+/* AGC registers */
+static u16 dib3000mc_reg_agc[] = {
+	36,37,38,39,42,43,44,45,46,47,48,49
+};
+
+static u16 dib3000mc_agc_tuner[][12] = {
+	{	0x0051, 0x301d, 0x0000, 0x1cc7, 0xcf5c, 0x6666,
+		0xbae1, 0xa148, 0x3b5e, 0x3c1c, 0x001a, 0x2019
+	}, /* TUNER_PANASONIC_ENV77H04D5, */
+
+	{	0x0051, 0x301d, 0x0000, 0x1cc7, 0xdc29, 0x570a,
+		0xbae1, 0x8ccd, 0x3b6d, 0x551d, 0x000a, 0x951e
+	}, /* TUNER_PANASONIC_ENV57H13D5, TUNER_PANASONIC_ENV57H12D5 */
+
+	{	0x0051, 0x301d, 0x0000, 0x1cc7, 0xffff, 0xffff,
+		0xffff, 0x0000, 0xfdfd, 0x4040, 0x00fd, 0x4040
+	}, /* TUNER_SAMSUNG_DTOS333IH102, TUNER_RFAGCIN_UNKNOWN */
+
+	{	0x0196, 0x301d, 0x0000, 0x1cc7, 0xbd71, 0x5c29,
+		0xb5c3, 0x6148, 0x6569, 0x5127, 0x0033, 0x3537
+	}, /* TUNER_PROVIDER_X */
+	/* TODO TUNER_PANASONIC_ENV57H10D8, TUNER_PANASONIC_ENV57H11D8 */
+};
+
+/* AGC loop bandwidth */
+static u16 dib3000mc_reg_agc_bandwidth[] = { 40,41 };
+static u16 dib3000mc_agc_bandwidth[]  = { 0x119,0x330 };
+
+static u16 dib3000mc_reg_agc_bandwidth_general[] = { 50,51,52,53,54 };
+static u16 dib3000mc_agc_bandwidth_general[] =
+	{ 0x8000, 0x91ca, 0x01ba, 0x0087, 0x0087 };
+
+#define DIB3000MC_REG_IMP_NOISE_55		(    55)
+#define DIB3000MC_IMP_NEW_ALGO(w)		(w | (1<<10))
+
+/* Impulse noise params */
+static u16 dib3000mc_reg_impulse_noise[] = { 55,56,57 };
+static u16 dib3000mc_impluse_noise[][3] = {
+	{ 0x489, 0x89, 0x72 }, /* 5 MHz */
+	{ 0x4a5, 0xa5, 0x89 }, /* 6 MHz */
+	{ 0x4c0, 0xc0, 0xa0 }, /* 7 MHz */
+	{ 0x4db, 0xdb, 0xb7 }, /* 8 Mhz */
+};
+
+static u16 dib3000mc_reg_fft[] = {
+	58,59,60,61,62,63,64,65,66,67,68,69,
+	70,71,72,73,74,75,76,77,78,79,80,81,
+	82,83,84,85,86
+};
+
+static u16 dib3000mc_fft_modes[][29] = {
+	{	0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
+		0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
+		0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
+		0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
+		0x3ffe, 0x5b3, 0x3feb, 0x76,   0x0, 0xd
+	}, /* fft mode 0 */
+	{	0x3b, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
+		0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
+		0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
+		0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
+		0x3ffe, 0x5b3, 0x3feb, 0x0,  0x8200, 0xd
+	}, /* fft mode 1 */
+};
+
+#define DIB3000MC_REG_UNK_88			(    88)
+#define DIB3000MC_UNK_88				(0x0410)
+
+static u16 dib3000mc_reg_bw[] = { 93,94,95,96,97,98 };
+static u16 dib3000mc_bw[][6] = {
+	{ 0,0,0,0,0,0 }, /* 5 MHz */
+	{ 0,0,0,0,0,0 }, /* 6 MHz */
+	{ 0,0,0,0,0,0 }, /* 7 MHz */
+	{ 0x20, 0x21, 0x20, 0x23, 0x20, 0x27 }, /* 8 MHz */
+};
+
+
+/* phase noise control */
+#define DIB3000MC_REG_UNK_99			(    99)
+#define DIB3000MC_UNK_99				(0x0220)
+
+#define DIB3000MC_REG_SCAN_BOOST		(   100)
+#define DIB3000MC_SCAN_BOOST_ON			((11 << 6) + 6)
+#define DIB3000MC_SCAN_BOOST_OFF		((16 << 6) + 9)
+
+/* timeout ??? */
+#define DIB3000MC_REG_UNK_110			(   110)
+#define DIB3000MC_UNK_110				(  3277)
+
+#define DIB3000MC_REG_UNK_111			(   111)
+#define DIB3000MC_UNK_111_PH_N_MODE_0	(     0)
+#define DIB3000MC_UNK_111_PH_N_MODE_1	(1 << 1)
+
+/* superious rm config */
+#define DIB3000MC_REG_UNK_120			(   120)
+#define DIB3000MC_UNK_120				(  8207)
+
+#define DIB3000MC_REG_UNK_133			(   133)
+#define DIB3000MC_UNK_133				( 15564)
+
+#define DIB3000MC_REG_UNK_134			(   134)
+#define DIB3000MC_UNK_134				(     0)
+
+/* adapter config for constellation */
+static u16 dib3000mc_reg_adp_cfg[] = { 129, 130, 131, 132 };
+
+static u16 dib3000mc_adp_cfg[][4] = {
+	{ 0x99a, 0x7fae, 0x333, 0x7ff0 }, /* QPSK  */
+	{ 0x23d, 0x7fdf, 0x0a4, 0x7ff0 }, /* 16-QAM */
+	{ 0x148, 0x7ff0, 0x0a4, 0x7ff8 }, /* 64-QAM */
+};
+
+static u16 dib3000mc_reg_mobile_mode[] = { 139, 140, 141, 175, 1032 };
+
+static u16 dib3000mc_mobile_mode[][5] = {
+	{ 0x01, 0x0, 0x0, 0x00, 0x12c }, /* fixed */
+	{ 0x01, 0x0, 0x0, 0x00, 0x12c }, /* portable */
+	{ 0x00, 0x0, 0x0, 0x02, 0x000 }, /* mobile */
+	{ 0x00, 0x0, 0x0, 0x02, 0x000 }, /* auto */
+};
+
+#define DIB3000MC_REG_DIVERSITY1		(   177)
+#define DIB3000MC_DIVERSITY1_DEFAULT	(     1)
+
+#define DIB3000MC_REG_DIVERSITY2		(   178)
+#define DIB3000MC_DIVERSITY2_DEFAULT	(     1)
+
+#define DIB3000MC_REG_DIVERSITY3		(   180)
+#define DIB3000MC_DIVERSITY3_IN_OFF		(0xfff0)
+#define DIB3000MC_DIVERSITY3_IN_ON		(0xfff6)
+
+#define DIB3000MC_REG_FEC_CFG			(   195)
+#define DIB3000MC_FEC_CFG				(  0x10)
+
+/*
+ * reg 206, output mode
+ *              1111 1111
+ *              |||| ||||
+ *              |||| |||+- unk
+ *              |||| ||+-- unk
+ *              |||| |+--- unk (on by default)
+ *              |||| +---- fifo_ctrl (1 = inhibit (flushed), 0 = active (unflushed))
+ *              |||+------ pid_parse (1 = enabled, 0 = disabled)
+ *              ||+------- outp_188  (1 = TS packet size 188, 0 = packet size 204)
+ *              |+-------- unk
+ *              +--------- unk
+ */
+
+#define DIB3000MC_REG_SMO_MODE			(   206)
+#define DIB3000MC_SMO_MODE_DEFAULT		(1 << 2)
+#define DIB3000MC_SMO_MODE_FIFO_FLUSH	(1 << 3)
+#define DIB3000MC_SMO_MODE_FIFO_UNFLUSH	(0xfff7)
+#define DIB3000MC_SMO_MODE_PID_PARSE	(1 << 4)
+#define DIB3000MC_SMO_MODE_NO_PID_PARSE	(0xffef)
+#define DIB3000MC_SMO_MODE_188			(1 << 5)
+#define DIB3000MC_SMO_MODE_SLAVE		(DIB3000MC_SMO_MODE_DEFAULT | \
+			DIB3000MC_SMO_MODE_188 | DIB3000MC_SMO_MODE_PID_PARSE | (1<<1))
+
+#define DIB3000MC_REG_FIFO_THRESHOLD	(   207)
+#define DIB3000MC_FIFO_THRESHOLD_DEFAULT	(  1792)
+#define DIB3000MC_FIFO_THRESHOLD_SLAVE	(   512)
+/*
+ * pidfilter
+ * it is not a hardware pidfilter but a filter which drops all pids
+ * except the ones set. When connected to USB1.1 bandwidth this is important.
+ * DiB3000P/M-C can filter up to 32 PIDs
+ */
+#define DIB3000MC_REG_FIRST_PID			(   212)
+#define DIB3000MC_NUM_PIDS				(    32)
+
+#define DIB3000MC_REG_OUTMODE			(   244)
+#define DIB3000MC_OM_PARALLEL_GATED_CLK	(     0)
+#define DIB3000MC_OM_PAR_CONT_CLK		(1 << 11)
+#define DIB3000MC_OM_SERIAL				(2 << 11)
+#define DIB3000MC_OM_DIVOUT_ON			(4 << 11)
+#define DIB3000MC_OM_SLAVE				(DIB3000MC_OM_DIVOUT_ON | DIB3000MC_OM_PAR_CONT_CLK)
+
+#define DIB3000MC_REG_RF_POWER			(   392)
+
+#define DIB3000MC_REG_FFT_POSITION		(   407)
+
+#define DIB3000MC_REG_DDS_FREQ_MSB		(   414)
+#define DIB3000MC_REG_DDS_FREQ_LSB		(   415)
+
+#define DIB3000MC_REG_TIMING_OFFS_MSB	(   416)
+#define DIB3000MC_REG_TIMING_OFFS_LSB	(   417)
+
+#define DIB3000MC_REG_TUNING_PARM		(   458)
+#define DIB3000MC_TP_QAM(v)				((v >> 13) & 0x03)
+#define DIB3000MC_TP_HRCH(v)			((v >> 12) & 0x01)
+#define DIB3000MC_TP_ALPHA(v)			((v >> 9) & 0x07)
+#define DIB3000MC_TP_FFT(v)				((v >> 8) & 0x01)
+#define DIB3000MC_TP_FEC_CR_HP(v)		((v >> 5) & 0x07)
+#define DIB3000MC_TP_FEC_CR_LP(v)		((v >> 2) & 0x07)
+#define DIB3000MC_TP_GUARD(v)			(v & 0x03)
+
+#define DIB3000MC_REG_SIGNAL_NOISE_MSB	(   483)
+#define DIB3000MC_REG_SIGNAL_NOISE_LSB	(   484)
+
+#define DIB3000MC_REG_MER				(   485)
+
+#define DIB3000MC_REG_BER_MSB			(   500)
+#define DIB3000MC_REG_BER_LSB			(   501)
+
+#define DIB3000MC_REG_PACKET_ERRORS		(   503)
+
+#define DIB3000MC_REG_PACKET_ERROR_COUNT	(   506)
+
+#define DIB3000MC_REG_LOCK_507			(   507)
+#define DIB3000MC_LOCK_507				(0x0002) // ? name correct ?
+
+#define DIB3000MC_REG_LOCKING			(   509)
+#define DIB3000MC_AGC_LOCK(v)			(v & 0x8000)
+#define DIB3000MC_CARRIER_LOCK(v)		(v & 0x2000)
+#define DIB3000MC_MPEG_SYNC_LOCK(v)		(v & 0x0080)
+#define DIB3000MC_MPEG_DATA_LOCK(v)		(v & 0x0040)
+#define DIB3000MC_TPS_LOCK(v)			(v & 0x0004)
+
+#define DIB3000MC_REG_AS_IRQ			(   511)
+#define DIB3000MC_AS_IRQ_SUCCESS		(1 << 1)
+#define DIB3000MC_AS_IRQ_FAIL			(     1)
+
+#define DIB3000MC_REG_TUNER				(   769)
+
+#define DIB3000MC_REG_RST_I2C_ADDR		(  1024)
+#define DIB3000MC_DEMOD_ADDR_ON			(     1)
+#define DIB3000MC_DEMOD_ADDR(a)			((a << 4) & 0x03F0)
+
+#define DIB3000MC_REG_RESTART			(  1027)
+#define DIB3000MC_RESTART_OFF			(0x0000)
+#define DIB3000MC_RESTART_AGC			(0x0800)
+#define DIB3000MC_RESTART_CONFIG		(0x8000)
+
+#define DIB3000MC_REG_RESTART_VIT		(  1028)
+#define DIB3000MC_RESTART_VIT_OFF		(     0)
+#define DIB3000MC_RESTART_VIT_ON		(     1)
+
+#define DIB3000MC_REG_CLK_CFG_1			(  1031)
+#define DIB3000MC_CLK_CFG_1_POWER_UP	(     0)
+#define DIB3000MC_CLK_CFG_1_POWER_DOWN	(0xffff)
+
+#define DIB3000MC_REG_CLK_CFG_2			(  1032)
+#define DIB3000MC_CLK_CFG_2_PUP_FIXED	(0x012c)
+#define DIB3000MC_CLK_CFG_2_PUP_PORT	(0x0104)
+#define DIB3000MC_CLK_CFG_2_PUP_MOBILE  (0x0000)
+#define DIB3000MC_CLK_CFG_2_POWER_DOWN	(0xffff)
+
+#define DIB3000MC_REG_CLK_CFG_3			(  1033)
+#define DIB3000MC_CLK_CFG_3_POWER_UP	(     0)
+#define DIB3000MC_CLK_CFG_3_POWER_DOWN	(0xfff5)
+
+#define DIB3000MC_REG_CLK_CFG_7			(  1037)
+#define DIB3000MC_CLK_CFG_7_INIT		( 12592)
+#define DIB3000MC_CLK_CFG_7_POWER_UP	(~0x0003)
+#define DIB3000MC_CLK_CFG_7_PWR_DOWN	(0x0003)
+#define DIB3000MC_CLK_CFG_7_DIV_IN_OFF	(1 << 8)
+
+/* was commented out ??? */
+#define DIB3000MC_REG_CLK_CFG_8			(  1038)
+#define DIB3000MC_CLK_CFG_8_POWER_UP	(0x160c)
+
+#define DIB3000MC_REG_CLK_CFG_9			(  1039)
+#define DIB3000MC_CLK_CFG_9_POWER_UP	(     0)
+
+/* also clock ??? */
+#define DIB3000MC_REG_ELEC_OUT			(  1040)
+#define DIB3000MC_ELEC_OUT_HIGH_Z		(     0)
+#define DIB3000MC_ELEC_OUT_DIV_OUT_ON	(     1)
+#define DIB3000MC_ELEC_OUT_SLAVE		(     3)
+
+#endif
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
new file mode 100644
index 0000000..2a3c2ce
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -0,0 +1,168 @@
+/*
+ * $Id: dvb-pll.c,v 1.7 2005/02/10 11:52:02 kraxel Exp $
+ *
+ * descriptions + helper functions for simple dvb plls.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "dvb-pll.h"
+
+/* ----------------------------------------------------------- */
+/* descriptions                                                */
+
+struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
+	.name  = "Thomson dtt7579",
+	.min   = 177000000,
+	.max   = 858000000,
+	.count = 5,
+	.entries = {
+		{          0, 36166667, 166666, 0xb4, 0x03 }, /* go sleep */
+		{  443250000, 36166667, 166666, 0xb4, 0x02 },
+		{  542000000, 36166667, 166666, 0xb4, 0x08 },
+		{  771000000, 36166667, 166666, 0xbc, 0x08 },
+		{  999999999, 36166667, 166666, 0xf4, 0x08 },
+	},
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt7579);
+
+struct dvb_pll_desc dvb_pll_thomson_dtt7610 = {
+	.name  = "Thomson dtt7610",
+	.min   =  44000000,
+	.max   = 958000000,
+	.count = 3,
+	.entries = {
+		{ 157250000, 44000000, 62500, 0x8e, 0x39 },
+		{ 454000000, 44000000, 62500, 0x8e, 0x3a },
+		{ 999999999, 44000000, 62500, 0x8e, 0x3c },
+	},
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt7610);
+
+static void thomson_dtt759x_bw(u8 *buf, int bandwidth)
+{
+	if (BANDWIDTH_7_MHZ == bandwidth)
+		buf[3] |= 0x10;
+}
+
+struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
+	.name  = "Thomson dtt759x",
+	.min   = 177000000,
+	.max   = 896000000,
+	.setbw = thomson_dtt759x_bw,
+	.count = 6,
+	.entries = {
+		{          0, 36166667, 166666, 0x84, 0x03 },
+		{  264000000, 36166667, 166666, 0xb4, 0x02 },
+		{  470000000, 36166667, 166666, 0xbc, 0x02 },
+		{  735000000, 36166667, 166666, 0xbc, 0x08 },
+		{  835000000, 36166667, 166666, 0xf4, 0x08 },
+		{  999999999, 36166667, 166666, 0xfc, 0x08 },
+	},
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt759x);
+
+struct dvb_pll_desc dvb_pll_lg_z201 = {
+	.name  = "LG z201",
+	.min   = 174000000,
+	.max   = 862000000,
+	.count = 5,
+	.entries = {
+		{          0, 36166667, 166666, 0xbc, 0x03 },
+		{  443250000, 36166667, 166666, 0xbc, 0x01 },
+		{  542000000, 36166667, 166666, 0xbc, 0x02 },
+		{  830000000, 36166667, 166666, 0xf4, 0x02 },
+		{  999999999, 36166667, 166666, 0xfc, 0x02 },
+	},
+};
+EXPORT_SYMBOL(dvb_pll_lg_z201);
+
+struct dvb_pll_desc dvb_pll_unknown_1 = {
+	.name  = "unknown 1", /* used by dntv live dvb-t */
+	.min   = 174000000,
+	.max   = 862000000,
+	.count = 9,
+	.entries = {
+		{  150000000, 36166667, 166666, 0xb4, 0x01 },
+		{  173000000, 36166667, 166666, 0xbc, 0x01 },
+		{  250000000, 36166667, 166666, 0xb4, 0x02 },
+		{  400000000, 36166667, 166666, 0xbc, 0x02 },
+		{  420000000, 36166667, 166666, 0xf4, 0x02 },
+		{  470000000, 36166667, 166666, 0xfc, 0x02 },
+		{  600000000, 36166667, 166666, 0xbc, 0x08 },
+		{  730000000, 36166667, 166666, 0xf4, 0x08 },
+		{  999999999, 36166667, 166666, 0xfc, 0x08 },
+	},
+};
+EXPORT_SYMBOL(dvb_pll_unknown_1);
+
+/* ----------------------------------------------------------- */
+/* code                                                        */
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
+		      u32 freq, int bandwidth)
+{
+	u32 div;
+	int i;
+
+	if (freq != 0 && (freq < desc->min || freq > desc->max))
+	    return -EINVAL;
+
+	for (i = 0; i < desc->count; i++) {
+		if (freq > desc->entries[i].limit)
+			continue;
+		break;
+	}
+	if (debug)
+		printk("pll: %s: freq=%d bw=%d | i=%d/%d\n",
+		       desc->name, freq, bandwidth, i, desc->count);
+	BUG_ON(i == desc->count);
+
+	div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize;
+	buf[0] = div >> 8;
+	buf[1] = div & 0xff;
+	buf[2] = desc->entries[i].cb1;
+	buf[3] = desc->entries[i].cb2;
+
+	if (desc->setbw)
+		desc->setbw(buf, bandwidth);
+
+	if (debug)
+		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;
+}
+EXPORT_SYMBOL(dvb_pll_configure);
+
+MODULE_DESCRIPTION("dvb pll library");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
new file mode 100644
index 0000000..016c794
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -0,0 +1,34 @@
+/*
+ * $Id: dvb-pll.h,v 1.2 2005/02/10 11:43:41 kraxel Exp $
+ */
+
+struct dvb_pll_desc {
+	char *name;
+	u32  min;
+	u32  max;
+	void (*setbw)(u8 *buf, int bandwidth);
+	int  count;
+	struct {
+		u32 limit;
+		u32 offset;
+		u32 stepsize;
+		u8  cb1;
+		u8  cb2;
+	} entries[9];
+};
+
+extern struct dvb_pll_desc dvb_pll_thomson_dtt7579;
+extern struct dvb_pll_desc dvb_pll_thomson_dtt759x;
+extern struct dvb_pll_desc dvb_pll_thomson_dtt7610;
+extern struct dvb_pll_desc dvb_pll_lg_z201;
+extern struct dvb_pll_desc dvb_pll_unknown_1;
+
+int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
+		      u32 freq, int bandwidth);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c
new file mode 100644
index 0000000..c05a9b0
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c
@@ -0,0 +1,279 @@
+/*
+ *  Driver for Dummy Frontend
+ *
+ *  Written by Emard <emard@softhome.net>
+ *
+ *  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/moduleparam.h>
+#include <linux/init.h>
+
+#include "dvb_frontend.h"
+#include "dvb_dummy_fe.h"
+
+
+struct dvb_dummy_fe_state {
+	struct dvb_frontend_ops ops;
+	struct dvb_frontend frontend;
+};
+
+
+static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	*status = FE_HAS_SIGNAL
+		| FE_HAS_CARRIER
+		| FE_HAS_VITERBI
+		| FE_HAS_SYNC
+		| FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int dvb_dummy_fe_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	*ber = 0;
+	return 0;
+}
+
+static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	*strength = 0;
+	return 0;
+}
+
+static int dvb_dummy_fe_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	*snr = 0;
+	return 0;
+}
+
+static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	*ucblocks = 0;
+	return 0;
+}
+
+static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	return 0;
+}
+
+static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	return 0;
+}
+
+static int dvb_dummy_fe_sleep(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int dvb_dummy_fe_init(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	return 0;
+}
+
+static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	return 0;
+}
+
+static void dvb_dummy_fe_release(struct dvb_frontend* fe)
+{
+	struct dvb_dummy_fe_state* state = (struct dvb_dummy_fe_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops;
+
+struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void)
+{
+	struct dvb_dummy_fe_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	memcpy(&state->ops, &dvb_dummy_fe_ofdm_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops;
+
+struct dvb_frontend* dvb_dummy_fe_qpsk_attach()
+{
+	struct dvb_dummy_fe_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	memcpy(&state->ops, &dvb_dummy_fe_qpsk_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops dvb_dummy_fe_qam_ops;
+
+struct dvb_frontend* dvb_dummy_fe_qam_attach()
+{
+	struct dvb_dummy_fe_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	memcpy(&state->ops, &dvb_dummy_fe_qam_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = {
+
+	.info = {
+		.name			= "Dummy DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 0,
+		.frequency_max		= 863250000,
+		.frequency_stepsize	= 62500,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+				FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+				FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = dvb_dummy_fe_release,
+
+	.init = dvb_dummy_fe_init,
+	.sleep = dvb_dummy_fe_sleep,
+
+	.set_frontend = dvb_dummy_fe_set_frontend,
+	.get_frontend = dvb_dummy_fe_get_frontend,
+
+	.read_status = dvb_dummy_fe_read_status,
+	.read_ber = dvb_dummy_fe_read_ber,
+	.read_signal_strength = dvb_dummy_fe_read_signal_strength,
+	.read_snr = dvb_dummy_fe_read_snr,
+	.read_ucblocks = dvb_dummy_fe_read_ucblocks,
+};
+
+static struct dvb_frontend_ops dvb_dummy_fe_qam_ops = {
+
+	.info = {
+		.name			= "Dummy DVB-C",
+		.type			= FE_QAM,
+		.frequency_stepsize	= 62500,
+		.frequency_min		= 51000000,
+		.frequency_max		= 858000000,
+		.symbol_rate_min	= (57840000/2)/64,     /* SACLK/64 == (XIN/2)/64 */
+		.symbol_rate_max	= (57840000/2)/4,      /* SACLK/4 */
+		.caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+			FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO
+	},
+
+	.release = dvb_dummy_fe_release,
+
+	.init = dvb_dummy_fe_init,
+	.sleep = dvb_dummy_fe_sleep,
+
+	.set_frontend = dvb_dummy_fe_set_frontend,
+	.get_frontend = dvb_dummy_fe_get_frontend,
+
+	.read_status = dvb_dummy_fe_read_status,
+	.read_ber = dvb_dummy_fe_read_ber,
+	.read_signal_strength = dvb_dummy_fe_read_signal_strength,
+	.read_snr = dvb_dummy_fe_read_snr,
+	.read_ucblocks = dvb_dummy_fe_read_ucblocks,
+};
+
+static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = {
+
+	.info = {
+		.name			= "Dummy DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 250,           /* kHz for QPSK frontends */
+		.frequency_tolerance	= 29500,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+		.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
+	},
+
+	.release = dvb_dummy_fe_release,
+
+	.init = dvb_dummy_fe_init,
+	.sleep = dvb_dummy_fe_sleep,
+
+	.set_frontend = dvb_dummy_fe_set_frontend,
+	.get_frontend = dvb_dummy_fe_get_frontend,
+
+	.read_status = dvb_dummy_fe_read_status,
+	.read_ber = dvb_dummy_fe_read_ber,
+	.read_signal_strength = dvb_dummy_fe_read_signal_strength,
+	.read_snr = dvb_dummy_fe_read_snr,
+	.read_ucblocks = dvb_dummy_fe_read_ucblocks,
+
+	.set_voltage = dvb_dummy_fe_set_voltage,
+	.set_tone = dvb_dummy_fe_set_tone,
+};
+
+MODULE_DESCRIPTION("DVB DUMMY Frontend");
+MODULE_AUTHOR("Emard");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach);
+EXPORT_SYMBOL(dvb_dummy_fe_qam_attach);
+EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach);
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.h b/drivers/media/dvb/frontends/dvb_dummy_fe.h
new file mode 100644
index 0000000..8210f19
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb_dummy_fe.h
@@ -0,0 +1,32 @@
+/*
+ *  Driver for Dummy Frontend
+ *
+ *  Written by Emard <emard@softhome.net>
+ *
+ *  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 DVB_DUMMY_FE_H
+#define DVB_DUMMY_FE_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void);
+
+#endif // DVB_DUMMY_FE_H
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
new file mode 100644
index 0000000..9ac95de
--- /dev/null
+++ b/drivers/media/dvb/frontends/l64781.c
@@ -0,0 +1,602 @@
+/*
+    driver for LSI L64781 COFDM demodulator
+
+    Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH
+                       Marko Kohtala <marko.kohtala@luukku.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "l64781.h"
+
+
+struct l64781_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	const struct l64781_config* config;
+	struct dvb_frontend frontend;
+
+	/* private demodulator data */
+	int first:1;
+};
+
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "l64781: " args); \
+	} while (0)
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+
+static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
+		dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
+			 __FUNCTION__, reg, ret);
+
+	return (ret != 1) ? -1 : 0;
+}
+
+static int l64781_readreg (struct l64781_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) return ret;
+
+	return b1[0];
+}
+
+static void apply_tps (struct l64781_state* state)
+{
+	l64781_writereg (state, 0x2a, 0x00);
+	l64781_writereg (state, 0x2a, 0x01);
+
+	/* This here is a little bit questionable because it enables
+	   the automatic update of TPS registers. I think we'd need to
+	   handle the IRQ from FE to update some other registers as
+	   well, or at least implement some magic to tuning to correct
+	   to the TPS received from transmission. */
+	l64781_writereg (state, 0x2a, 0x02);
+}
+
+
+static void reset_afc (struct l64781_state* state)
+{
+	/* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
+	   timing offset */
+	l64781_writereg (state, 0x07, 0x9e); /* stall AFC */
+	l64781_writereg (state, 0x08, 0);    /* AFC INIT FREQ */
+	l64781_writereg (state, 0x09, 0);
+	l64781_writereg (state, 0x0a, 0);
+	l64781_writereg (state, 0x07, 0x8e);
+	l64781_writereg (state, 0x0e, 0);    /* AGC gain to zero in beginning */
+	l64781_writereg (state, 0x11, 0x80); /* stall TIM */
+	l64781_writereg (state, 0x10, 0);    /* TIM_OFFSET_LSB */
+	l64781_writereg (state, 0x12, 0);
+	l64781_writereg (state, 0x13, 0);
+	l64781_writereg (state, 0x11, 0x00);
+}
+
+static int reset_and_configure (struct l64781_state* state)
+{
+	u8 buf [] = { 0x06 };
+	struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 };
+	// NOTE: this is correct in writing to address 0x00
+
+	return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV;
+}
+
+static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_parameters *param)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	/* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */
+	static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 };
+	/* QPSK, QAM_16, QAM_64 */
+	static const u8 qam_tab [] = { 2, 4, 0, 6 };
+	static const u8 bw_tab [] = { 8, 7, 6 };  /* 8Mhz, 7MHz, 6MHz */
+	static const u8 guard_tab [] = { 1, 2, 4, 8 };
+	/* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */
+	static const u32 ppm = 8000;
+	struct dvb_ofdm_parameters *p = &param->u.ofdm;
+	u32 ddfs_offset_fixed;
+/*	u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */
+/*			bw_tab[p->bandWidth]<<10)/15625; */
+	u32 init_freq;
+	u32 spi_bias;
+	u8 val0x04;
+	u8 val0x05;
+	u8 val0x06;
+	int bw = p->bandwidth - BANDWIDTH_8_MHZ;
+
+	state->config->pll_set(fe, param);
+
+	if (param->inversion != INVERSION_ON &&
+	    param->inversion != INVERSION_OFF)
+		return -EINVAL;
+
+	if (bw < 0 || bw > 2)
+		return -EINVAL;
+
+	if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 &&
+	    p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 &&
+	    p->code_rate_HP != FEC_7_8)
+		return -EINVAL;
+
+	if (p->hierarchy_information != HIERARCHY_NONE &&
+	    (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 &&
+	     p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 &&
+	     p->code_rate_LP != FEC_7_8))
+		return -EINVAL;
+
+	if (p->constellation != QPSK && p->constellation != QAM_16 &&
+	    p->constellation != QAM_64)
+		return -EINVAL;
+
+	if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+	    p->transmission_mode != TRANSMISSION_MODE_8K)
+		return -EINVAL;
+
+	if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+	    p->guard_interval > GUARD_INTERVAL_1_4)
+		return -EINVAL;
+
+	if (p->hierarchy_information < HIERARCHY_NONE ||
+	    p->hierarchy_information > HIERARCHY_4)
+		return -EINVAL;
+
+	ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000;
+
+	/* This works up to 20000 ppm, it overflows if too large ppm! */
+	init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) /
+			bw_tab[p->bandwidth] & 0xFFFFFF);
+
+	/* SPI bias calculation is slightly modified to fit in 32bit */
+	/* will work for high ppm only... */
+	spi_bias = 378 * (1 << 10);
+	spi_bias *= 16;
+	spi_bias *= bw_tab[p->bandwidth];
+	spi_bias *= qam_tab[p->constellation];
+	spi_bias /= p->code_rate_HP + 1;
+	spi_bias /= (guard_tab[p->guard_interval] + 32);
+	spi_bias *= 1000ULL;
+	spi_bias /= 1000ULL + ppm/1000;
+	spi_bias *= p->code_rate_HP;
+
+	val0x04 = (p->transmission_mode << 2) | p->guard_interval;
+	val0x05 = fec_tab[p->code_rate_HP];
+
+	if (p->hierarchy_information != HIERARCHY_NONE)
+		val0x05 |= (p->code_rate_LP - FEC_1_2) << 3;
+
+	val0x06 = (p->hierarchy_information << 2) | p->constellation;
+
+	l64781_writereg (state, 0x04, val0x04);
+	l64781_writereg (state, 0x05, val0x05);
+	l64781_writereg (state, 0x06, val0x06);
+
+	reset_afc (state);
+
+	/* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */
+	l64781_writereg (state, 0x15,
+			 p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3);
+	l64781_writereg (state, 0x16, init_freq & 0xff);
+	l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff);
+	l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff);
+
+	l64781_writereg (state, 0x1b, spi_bias & 0xff);
+	l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff);
+	l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) |
+		(param->inversion == INVERSION_ON ? 0x80 : 0x00));
+
+	l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff);
+	l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f);
+
+	l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
+	l64781_readreg (state, 0x01);  /*  dto. */
+
+	apply_tps (state);
+
+	return 0;
+}
+
+static int get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* param)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	int tmp;
+
+
+	tmp = l64781_readreg(state, 0x04);
+	switch(tmp & 3) {
+	case 0:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+		break;
+	case 1:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+		break;
+	case 2:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+		break;
+	case 3:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+		break;
+	}
+	switch((tmp >> 2) & 3) {
+	case 0:
+		param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+		break;
+	case 1:
+		param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+		break;
+	default:
+		printk("Unexpected value for transmission_mode\n");
+	}
+
+
+
+	tmp = l64781_readreg(state, 0x05);
+	switch(tmp & 7) {
+	case 0:
+		param->u.ofdm.code_rate_HP = FEC_1_2;
+		break;
+	case 1:
+		param->u.ofdm.code_rate_HP = FEC_2_3;
+		break;
+	case 2:
+		param->u.ofdm.code_rate_HP = FEC_3_4;
+		break;
+	case 3:
+		param->u.ofdm.code_rate_HP = FEC_5_6;
+		break;
+	case 4:
+		param->u.ofdm.code_rate_HP = FEC_7_8;
+		break;
+	default:
+		printk("Unexpected value for code_rate_HP\n");
+	}
+	switch((tmp >> 3) & 7) {
+	case 0:
+		param->u.ofdm.code_rate_LP = FEC_1_2;
+		break;
+	case 1:
+		param->u.ofdm.code_rate_LP = FEC_2_3;
+		break;
+	case 2:
+		param->u.ofdm.code_rate_LP = FEC_3_4;
+		break;
+	case 3:
+		param->u.ofdm.code_rate_LP = FEC_5_6;
+		break;
+	case 4:
+		param->u.ofdm.code_rate_LP = FEC_7_8;
+		break;
+	default:
+		printk("Unexpected value for code_rate_LP\n");
+	}
+
+
+	tmp = l64781_readreg(state, 0x06);
+	switch(tmp & 3) {
+	case 0:
+		param->u.ofdm.constellation = QPSK;
+		break;
+	case 1:
+		param->u.ofdm.constellation = QAM_16;
+		break;
+	case 2:
+		param->u.ofdm.constellation = QAM_64;
+		break;
+	default:
+		printk("Unexpected value for constellation\n");
+	}
+	switch((tmp >> 2) & 7) {
+	case 0:
+		param->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+		break;
+	case 1:
+		param->u.ofdm.hierarchy_information = HIERARCHY_1;
+		break;
+	case 2:
+		param->u.ofdm.hierarchy_information = HIERARCHY_2;
+		break;
+	case 3:
+		param->u.ofdm.hierarchy_information = HIERARCHY_4;
+		break;
+	default:
+		printk("Unexpected value for hierarchy\n");
+	}
+
+
+	tmp = l64781_readreg (state, 0x1d);
+	param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF;
+
+	tmp = (int) (l64781_readreg (state, 0x08) |
+		     (l64781_readreg (state, 0x09) << 8) |
+		     (l64781_readreg (state, 0x0a) << 16));
+	param->frequency += tmp;
+
+	return 0;
+}
+
+static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	int sync = l64781_readreg (state, 0x32);
+	int gain = l64781_readreg (state, 0x0e);
+
+	l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
+	l64781_readreg (state, 0x01);  /*  dto. */
+
+	*status = 0;
+
+	if (gain > 5)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x02) /* VCXO locked, this criteria should be ok */
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 0x20)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x40)
+		*status |= FE_HAS_SYNC;
+
+	if (sync == 0x7f)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int l64781_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	/*   XXX FIXME: set up counting period (reg 0x26...0x28)
+	 */
+	*ber = l64781_readreg (state, 0x39)
+	    | (l64781_readreg (state, 0x3a) << 8);
+
+	return 0;
+}
+
+static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	u8 gain = l64781_readreg (state, 0x0e);
+	*signal_strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int l64781_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	u8 avg_quality = 0xff - l64781_readreg (state, 0x33);
+	*snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/
+
+	return 0;
+}
+
+static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	*ucblocks = l64781_readreg (state, 0x37)
+	   | (l64781_readreg (state, 0x38) << 8);
+
+	return 0;
+}
+
+static int l64781_sleep(struct dvb_frontend* fe)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	/* Power down */
+	return l64781_writereg (state, 0x3e, 0x5a);
+}
+
+static int l64781_init(struct dvb_frontend* fe)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+        reset_and_configure (state);
+
+	/* Power up */
+	l64781_writereg (state, 0x3e, 0xa5);
+
+	/* Reset hard */
+	l64781_writereg (state, 0x2a, 0x04);
+	l64781_writereg (state, 0x2a, 0x00);
+
+	/* Set tuner specific things */
+	/* AFC_POL, set also in reset_afc */
+	l64781_writereg (state, 0x07, 0x8e);
+
+	/* Use internal ADC */
+	l64781_writereg (state, 0x0b, 0x81);
+
+	/* AGC loop gain, and polarity is positive */
+	l64781_writereg (state, 0x0c, 0x84);
+
+	/* Internal ADC outputs two's complement */
+	l64781_writereg (state, 0x0d, 0x8c);
+
+	/* With ppm=8000, it seems the DTR_SENSITIVITY will result in
+           value of 2 with all possible bandwidths and guard
+           intervals, which is the initial value anyway. */
+        /*l64781_writereg (state, 0x19, 0x92);*/
+
+	/* Everything is two's complement, soft bit and CSI_OUT too */
+	l64781_writereg (state, 0x1e, 0x09);
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	/* delay a bit after first init attempt */
+	if (state->first) {
+		state->first = 0;
+		msleep(200);
+	}
+
+	return 0;
+}
+
+static int l64781_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 200;
+        fesettings->step_size = 166667;
+        fesettings->max_drift = 166667*2;
+        return 0;
+}
+
+static void l64781_release(struct dvb_frontend* fe)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops l64781_ops;
+
+struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+				   struct i2c_adapter* i2c)
+{
+	struct l64781_state* state = NULL;
+	int reg0x3e = -1;
+	u8 b0 [] = { 0x1a };
+	u8 b1 [] = { 0x00 };
+	struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	/* allocate memory for the internal state */
+	state = (struct l64781_state*) kmalloc(sizeof(struct l64781_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &l64781_ops, sizeof(struct dvb_frontend_ops));
+	state->first = 1;
+
+	/**
+	 *  the L64781 won't show up before we send the reset_and_configure()
+	 *  broadcast. If nothing responds there is no L64781 on the bus...
+	 */
+	if (reset_and_configure(state) < 0) {
+		dprintk("No response to reset and configure broadcast...\n");
+		goto error;
+	}
+
+	/* The chip always responds to reads */
+	if (i2c_transfer(state->i2c, msg, 2) != 2) {
+	        dprintk("No response to read on I2C bus\n");
+		goto error;
+	}
+
+	/* Save current register contents for bailout */
+	reg0x3e = l64781_readreg(state, 0x3e);
+
+	/* Reading the POWER_DOWN register always returns 0 */
+	if (reg0x3e != 0) {
+	        dprintk("Device doesn't look like L64781\n");
+		goto error;
+	}
+
+	/* Turn the chip off */
+	l64781_writereg (state, 0x3e, 0x5a);
+
+	/* Responds to all reads with 0 */
+	if (l64781_readreg(state, 0x1a) != 0) {
+	        dprintk("Read 1 returned unexpcted value\n");
+		goto error;
+	}
+
+	/* Turn the chip on */
+	l64781_writereg (state, 0x3e, 0xa5);
+
+	/* Responds with register default value */
+	if (l64781_readreg(state, 0x1a) != 0xa1) {
+	        dprintk("Read 2 returned unexpcted value\n");
+		goto error;
+	}
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (reg0x3e >= 0) l64781_writereg (state, 0x3e, reg0x3e);  /* restore reg 0x3e */
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops l64781_ops = {
+
+	.info = {
+		.name = "LSI L64781 DVB-T",
+		.type = FE_OFDM,
+	/*	.frequency_min = ???,*/
+	/*	.frequency_max = ???,*/
+		.frequency_stepsize = 166666,
+	/*      .frequency_tolerance = ???,*/
+	/*      .symbol_rate_tolerance = ???,*/
+		.caps = 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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+		      FE_CAN_MUTE_TS
+	},
+
+	.release = l64781_release,
+
+	.init = l64781_init,
+	.sleep = l64781_sleep,
+
+	.set_frontend = apply_frontend_param,
+	.get_frontend = get_frontend,
+	.get_tune_settings = l64781_get_tune_settings,
+
+	.read_status = l64781_read_status,
+	.read_ber = l64781_read_ber,
+	.read_signal_strength = l64781_read_signal_strength,
+	.read_snr = l64781_read_snr,
+	.read_ucblocks = l64781_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(l64781_attach);
diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h
new file mode 100644
index 0000000..7e30fb0
--- /dev/null
+++ b/drivers/media/dvb/frontends/l64781.h
@@ -0,0 +1,42 @@
+/*
+    driver for LSI L64781 COFDM demodulator
+
+    Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH
+                       Marko Kohtala <marko.kohtala@luukku.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef L64781_H
+#define L64781_H
+
+#include <linux/dvb/frontend.h>
+
+struct l64781_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+
+extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+					  struct i2c_adapter* i2c);
+
+#endif // L64781_H
diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c
new file mode 100644
index 0000000..176a22e
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt312.c
@@ -0,0 +1,729 @@
+/*
+    Driver for Zarlink VP310/MT312 Satellite Channel Decoder
+
+    Copyright (C) 2003 Andreas Oberritter <obi@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; 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.
+
+    References:
+    http://products.zarlink.com/product_profiles/MT312.htm
+    http://products.zarlink.com/product_profiles/SL1935.htm
+*/
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include "dvb_frontend.h"
+#include "mt312_priv.h"
+#include "mt312.h"
+
+
+struct mt312_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	/* configuration settings */
+	const struct mt312_config* config;
+	struct dvb_frontend frontend;
+
+	u8 id;
+	u8 frequency;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "mt312: " args); \
+	} while (0)
+
+#define MT312_SYS_CLK		90000000UL	/* 90 MHz */
+#define MT312_LPOWER_SYS_CLK	60000000UL	/* 60 MHz */
+#define MT312_PLL_CLK		10000000UL	/* 10 MHz */
+
+static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
+		      void *buf, const size_t count)
+{
+	int ret;
+	struct i2c_msg msg[2];
+	u8 regbuf[1] = { reg };
+
+	msg[0].addr = state->config->demod_address;
+	msg[0].flags = 0;
+	msg[0].buf = regbuf;
+	msg[0].len = 1;
+	msg[1].addr = state->config->demod_address;
+	msg[1].flags = I2C_M_RD;
+	msg[1].buf = buf;
+	msg[1].len = count;
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		printk(KERN_ERR "%s: ret == %d\n", __FUNCTION__, ret);
+		return -EREMOTEIO;
+	}
+
+	if(debug) {
+		int i;
+		dprintk("R(%d):", reg & 0x7f);
+		for (i = 0; i < count; i++)
+			printk(" %02x", ((const u8 *) buf)[i]);
+		printk("\n");
+	}
+
+	return 0;
+}
+
+static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg,
+		       const void *src, const size_t count)
+{
+	int ret;
+	u8 buf[count + 1];
+	struct i2c_msg msg;
+
+	if(debug) {
+		int i;
+		dprintk("W(%d):", reg & 0x7f);
+		for (i = 0; i < count; i++)
+			printk(" %02x", ((const u8 *) src)[i]);
+		printk("\n");
+	}
+
+	buf[0] = reg;
+	memcpy(&buf[1], src, count);
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = count + 1;
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1) {
+		dprintk("%s: ret == %d\n", __FUNCTION__, ret);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static inline int mt312_readreg(struct mt312_state* state,
+				const enum mt312_reg_addr reg, u8 *val)
+{
+	return mt312_read(state, reg, val, 1);
+}
+
+static inline int mt312_writereg(struct mt312_state* state,
+				 const enum mt312_reg_addr reg, const u8 val)
+{
+	return mt312_write(state, reg, &val, 1);
+}
+
+static inline u32 mt312_div(u32 a, u32 b)
+{
+	return (a + (b / 2)) / b;
+}
+
+static int mt312_reset(struct mt312_state* state, const u8 full)
+{
+	return mt312_writereg(state, RESET, full ? 0x80 : 0x40);
+}
+
+static int mt312_get_inversion(struct mt312_state* state,
+			       fe_spectral_inversion_t *i)
+{
+	int ret;
+	u8 vit_mode;
+
+	if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0)
+		return ret;
+
+	if (vit_mode & 0x80)	/* auto inversion was used */
+		*i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF;
+
+	return 0;
+}
+
+static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
+{
+	int ret;
+	u8 sym_rate_h;
+	u8 dec_ratio;
+	u16 sym_rat_op;
+	u16 monitor;
+	u8 buf[2];
+
+	if ((ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h)) < 0)
+		return ret;
+
+	if (sym_rate_h & 0x80) {	/* symbol rate search was used */
+		if ((ret = mt312_writereg(state, MON_CTRL, 0x03)) < 0)
+			return ret;
+
+		if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+			return ret;
+
+		monitor = (buf[0] << 8) | buf[1];
+
+		dprintk(KERN_DEBUG "sr(auto) = %u\n",
+		       mt312_div(monitor * 15625, 4));
+	} else {
+		if ((ret = mt312_writereg(state, MON_CTRL, 0x05)) < 0)
+			return ret;
+
+		if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+			return ret;
+
+		dec_ratio = ((buf[0] >> 5) & 0x07) * 32;
+
+		if ((ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf))) < 0)
+			return ret;
+
+		sym_rat_op = (buf[0] << 8) | buf[1];
+
+		dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n",
+		       sym_rat_op, dec_ratio);
+		dprintk(KERN_DEBUG "*sr(manual) = %lu\n",
+		       (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) *
+			2) - dec_ratio);
+	}
+
+	return 0;
+}
+
+static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
+{
+	const fe_code_rate_t fec_tab[8] =
+	    { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8,
+		FEC_AUTO, FEC_AUTO };
+
+	int ret;
+	u8 fec_status;
+
+	if ((ret = mt312_readreg(state, FEC_STATUS, &fec_status)) < 0)
+		return ret;
+
+	*cr = fec_tab[(fec_status >> 4) & 0x07];
+
+	return 0;
+}
+
+static int mt312_initfe(struct dvb_frontend* fe)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[2];
+
+	/* wake up */
+	if ((ret = mt312_writereg(state, CONFIG, (state->frequency == 60 ? 0x88 : 0x8c))) < 0)
+		return ret;
+
+	/* wait at least 150 usec */
+	udelay(150);
+
+	/* full reset */
+	if ((ret = mt312_reset(state, 1)) < 0)
+		return ret;
+
+// Per datasheet, write correct values. 09/28/03 ACCJr.
+// If we don't do this, we won't get FE_HAS_VITERBI in the VP310.
+	{
+		u8 buf_def[8]={0x14, 0x12, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00};
+
+		if ((ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def))) < 0)
+			return ret;
+	}
+
+	/* SYS_CLK */
+	buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000);
+
+	/* DISEQC_RATIO */
+	buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4);
+
+	if ((ret = mt312_write(state, SYS_CLK, buf, sizeof(buf))) < 0)
+		return ret;
+
+	if ((ret = mt312_writereg(state, SNR_THS_HIGH, 0x32)) < 0)
+		return ret;
+
+	if ((ret = mt312_writereg(state, OP_CTRL, 0x53)) < 0)
+		return ret;
+
+	/* TS_SW_LIM */
+	buf[0] = 0x8c;
+	buf[1] = 0x98;
+
+	if ((ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf))) < 0)
+		return ret;
+
+	if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0)
+		return ret;
+
+	if (state->config->pll_init) {
+		mt312_writereg(state, GPP_CTRL, 0x40);
+		state->config->pll_init(fe);
+		mt312_writereg(state, GPP_CTRL, 0x00);
+	}
+
+	return 0;
+}
+
+static int mt312_send_master_cmd(struct dvb_frontend* fe,
+				 struct dvb_diseqc_master_cmd *c)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 diseqc_mode;
+
+	if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg)))
+		return -EINVAL;
+
+	if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+		return ret;
+
+	if ((ret =
+	     mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0)
+		return ret;
+
+	if ((ret =
+	     mt312_writereg(state, DISEQC_MODE,
+			    (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3)
+			    | 0x04)) < 0)
+		return ret;
+
+	/* set DISEQC_MODE[2:0] to zero if a return message is expected */
+	if (c->msg[0] & 0x02)
+		if ((ret =
+		     mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40))) < 0)
+			return ret;
+
+	return 0;
+}
+
+static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	const u8 mini_tab[2] = { 0x02, 0x03 };
+
+	int ret;
+	u8 diseqc_mode;
+
+	if (c > SEC_MINI_B)
+		return -EINVAL;
+
+	if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+		return ret;
+
+	if ((ret =
+	     mt312_writereg(state, DISEQC_MODE,
+			    (diseqc_mode & 0x40) | mini_tab[c])) < 0)
+		return ret;
+
+	return 0;
+}
+
+static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	const u8 tone_tab[2] = { 0x01, 0x00 };
+
+	int ret;
+	u8 diseqc_mode;
+
+	if (t > SEC_TONE_OFF)
+		return -EINVAL;
+
+	if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+		return ret;
+
+	if ((ret =
+	     mt312_writereg(state, DISEQC_MODE,
+			    (diseqc_mode & 0x40) | tone_tab[t])) < 0)
+		return ret;
+
+	return 0;
+}
+
+static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	const u8 volt_tab[3] = { 0x00, 0x40, 0x00 };
+
+	if (v > SEC_VOLTAGE_OFF)
+		return -EINVAL;
+
+	return mt312_writereg(state, DISEQC_MODE, volt_tab[v]);
+}
+
+static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 status[3];
+
+	*s = 0;
+
+	if ((ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status))) < 0)
+		return ret;
+
+	dprintk(KERN_DEBUG "QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
+
+	if (status[0] & 0xc0)
+		*s |= FE_HAS_SIGNAL;	/* signal noise ratio */
+	if (status[0] & 0x04)
+		*s |= FE_HAS_CARRIER;	/* qpsk carrier lock */
+	if (status[2] & 0x02)
+		*s |= FE_HAS_VITERBI;	/* viterbi lock */
+	if (status[2] & 0x04)
+		*s |= FE_HAS_SYNC;	/* byte align lock */
+	if (status[0] & 0x01)
+		*s |= FE_HAS_LOCK;	/* qpsk lock */
+
+	return 0;
+}
+
+static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[3];
+
+	if ((ret = mt312_read(state, RS_BERCNT_H, buf, 3)) < 0)
+		return ret;
+
+	*ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64;
+
+	return 0;
+}
+
+static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_strength)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[3];
+	u16 agc;
+	s16 err_db;
+
+	if ((ret = mt312_read(state, AGC_H, buf, sizeof(buf))) < 0)
+		return ret;
+
+	agc = (buf[0] << 6) | (buf[1] >> 2);
+	err_db = (s16) (((buf[1] & 0x03) << 14) | buf[2] << 6) >> 6;
+
+	*signal_strength = agc;
+
+	dprintk(KERN_DEBUG "agc=%08x err_db=%hd\n", agc, err_db);
+
+	return 0;
+}
+
+static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[2];
+
+	if ((ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf))) < 0)
+		return ret;
+
+	*snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1);
+
+	return 0;
+}
+
+static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[2];
+
+	if ((ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf))) < 0)
+		return ret;
+
+	*ubc = (buf[0] << 8) | buf[1];
+
+	return 0;
+}
+
+static int mt312_set_frontend(struct dvb_frontend* fe,
+			      struct dvb_frontend_parameters *p)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[5], config_val;
+	u16 sr;
+
+	const u8 fec_tab[10] =
+	    { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f };
+	const u8 inv_tab[3] = { 0x00, 0x40, 0x80 };
+
+	dprintk("%s: Freq %d\n", __FUNCTION__, p->frequency);
+
+	if ((p->frequency < fe->ops->info.frequency_min)
+	    || (p->frequency > fe->ops->info.frequency_max))
+		return -EINVAL;
+
+	if ((p->inversion < INVERSION_OFF)
+	    || (p->inversion > INVERSION_ON))
+		return -EINVAL;
+
+	if ((p->u.qpsk.symbol_rate < fe->ops->info.symbol_rate_min)
+	    || (p->u.qpsk.symbol_rate > fe->ops->info.symbol_rate_max))
+		return -EINVAL;
+
+	if ((p->u.qpsk.fec_inner < FEC_NONE)
+	    || (p->u.qpsk.fec_inner > FEC_AUTO))
+		return -EINVAL;
+
+	if ((p->u.qpsk.fec_inner == FEC_4_5)
+	    || (p->u.qpsk.fec_inner == FEC_8_9))
+		return -EINVAL;
+
+	switch (state->id) {
+	case ID_VP310:
+	// For now we will do this only for the VP310.
+	// It should be better for the mt312 as well, but tunning will be slower. ACCJr 09/29/03
+		if ((ret = mt312_readreg(state, CONFIG, &config_val) < 0))
+			return ret;
+		if (p->u.qpsk.symbol_rate >= 30000000) //Note that 30MS/s should use 90MHz
+		{
+			if ((config_val & 0x0c) == 0x08) { //We are running 60MHz
+				state->frequency = 90;
+				if ((ret = mt312_initfe(fe)) < 0)
+					return ret;
+			}
+		}
+		else
+		{
+			if ((config_val & 0x0c) == 0x0C) { //We are running 90MHz
+				state->frequency = 60;
+				if ((ret = mt312_initfe(fe)) < 0)
+					return ret;
+			}
+		}
+		break;
+
+	case ID_MT312:
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	mt312_writereg(state, GPP_CTRL, 0x40);
+	state->config->pll_set(fe, p);
+	mt312_writereg(state, GPP_CTRL, 0x00);
+
+	/* sr = (u16)(sr * 256.0 / 1000000.0) */
+	sr = mt312_div(p->u.qpsk.symbol_rate * 4, 15625);
+
+	/* SYM_RATE */
+	buf[0] = (sr >> 8) & 0x3f;
+	buf[1] = (sr >> 0) & 0xff;
+
+	/* VIT_MODE */
+	buf[2] = inv_tab[p->inversion] | fec_tab[p->u.qpsk.fec_inner];
+
+	/* QPSK_CTRL */
+	buf[3] = 0x40;		/* swap I and Q before QPSK demodulation */
+
+	if (p->u.qpsk.symbol_rate < 10000000)
+		buf[3] |= 0x04;	/* use afc mode */
+
+	/* GO */
+	buf[4] = 0x01;
+
+	if ((ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf))) < 0)
+		return ret;
+
+        mt312_reset(state, 0);
+
+	return 0;
+}
+
+static int mt312_get_frontend(struct dvb_frontend* fe,
+			      struct dvb_frontend_parameters *p)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+
+	if ((ret = mt312_get_inversion(state, &p->inversion)) < 0)
+		return ret;
+
+	if ((ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate)) < 0)
+		return ret;
+
+	if ((ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner)) < 0)
+		return ret;
+
+	return 0;
+}
+
+static int mt312_sleep(struct dvb_frontend* fe)
+{
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+	int ret;
+	u8 config;
+
+	/* reset all registers to defaults */
+	if ((ret = mt312_reset(state, 1)) < 0)
+		return ret;
+
+	if ((ret = mt312_readreg(state, CONFIG, &config)) < 0)
+		return ret;
+
+	/* enter standby */
+	if ((ret = mt312_writereg(state, CONFIG, config & 0x7f)) < 0)
+		return ret;
+
+	return 0;
+}
+
+static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+	fesettings->min_delay_ms = 50;
+	fesettings->step_size = 0;
+	fesettings->max_drift = 0;
+	return 0;
+}
+
+static void mt312_release(struct dvb_frontend* fe)
+{
+	struct mt312_state* state = (struct mt312_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops vp310_mt312_ops;
+
+struct dvb_frontend* vp310_attach(const struct mt312_config* config,
+				  struct i2c_adapter* i2c)
+{
+	struct mt312_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops));
+	strcpy(state->ops.info.name, "Zarlink VP310 DVB-S");
+
+	/* check if the demod is there */
+	if (mt312_readreg(state, ID, &state->id) < 0)
+		goto error;
+	if (state->id != ID_VP310) {
+		goto error;
+	}
+
+	/* create dvb_frontend */
+	state->frequency = 90;
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+struct dvb_frontend* mt312_attach(const struct mt312_config* config,
+				  struct i2c_adapter* i2c)
+{
+	struct mt312_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops));
+	strcpy(state->ops.info.name, "Zarlink MT312 DVB-S");
+
+	/* check if the demod is there */
+	if (mt312_readreg(state, ID, &state->id) < 0)
+		goto error;
+	if (state->id != ID_MT312) {
+		goto error;
+	}
+
+	/* create dvb_frontend */
+	state->frequency = 60;
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state)
+		kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops vp310_mt312_ops = {
+
+	.info = {
+		.name = "Zarlink ???? DVB-S",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = (MT312_PLL_CLK / 1000) / 128,
+		.symbol_rate_min = MT312_SYS_CLK / 128,
+		.symbol_rate_max = MT312_SYS_CLK / 2,
+		.caps =
+		    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_MUTE_TS |
+	            FE_CAN_RECOVER
+	},
+
+	.release = mt312_release,
+
+	.init = mt312_initfe,
+	.sleep = mt312_sleep,
+
+	.set_frontend = mt312_set_frontend,
+	.get_frontend = mt312_get_frontend,
+	.get_tune_settings = mt312_get_tune_settings,
+
+	.read_status = mt312_read_status,
+	.read_ber = mt312_read_ber,
+	.read_signal_strength = mt312_read_signal_strength,
+	.read_snr = mt312_read_snr,
+	.read_ucblocks = mt312_read_ucblocks,
+
+	.diseqc_send_master_cmd = mt312_send_master_cmd,
+	.diseqc_send_burst = mt312_send_burst,
+	.set_tone = mt312_set_tone,
+	.set_voltage = mt312_set_voltage,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver");
+MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(mt312_attach);
+EXPORT_SYMBOL(vp310_attach);
diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h
new file mode 100644
index 0000000..b3a53a7
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt312.h
@@ -0,0 +1,47 @@
+/*
+    Driver for Zarlink MT312 Satellite Channel Decoder
+
+    Copyright (C) 2003 Andreas Oberritter <obi@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; 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.
+
+    References:
+    http://products.zarlink.com/product_profiles/MT312.htm
+    http://products.zarlink.com/product_profiles/SL1935.htm
+*/
+
+#ifndef MT312_H
+#define MT312_H
+
+#include <linux/dvb/frontend.h>
+
+struct mt312_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* mt312_attach(const struct mt312_config* config,
+					 struct i2c_adapter* i2c);
+
+extern struct dvb_frontend* vp310_attach(const struct mt312_config* config,
+					 struct i2c_adapter* i2c);
+
+#endif // MT312_H
diff --git a/drivers/media/dvb/frontends/mt312_priv.h b/drivers/media/dvb/frontends/mt312_priv.h
new file mode 100644
index 0000000..5e0b95b
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt312_priv.h
@@ -0,0 +1,162 @@
+/*
+    Driver for Zarlink MT312 QPSK Frontend
+
+    Copyright (C) 2003 Andreas Oberritter <obi@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; 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 _DVB_FRONTENDS_MT312_PRIV
+#define _DVB_FRONTENDS_MT312_PRIV
+
+enum mt312_reg_addr {
+	QPSK_INT_H = 0,
+	QPSK_INT_M = 1,
+	QPSK_INT_L = 2,
+	FEC_INT = 3,
+	QPSK_STAT_H = 4,
+	QPSK_STAT_L = 5,
+	FEC_STATUS = 6,
+	LNB_FREQ_H = 7,
+	LNB_FREQ_L = 8,
+	M_SNR_H = 9,
+	M_SNR_L = 10,
+	VIT_ERRCNT_H = 11,
+	VIT_ERRCNT_M = 12,
+	VIT_ERRCNT_L = 13,
+	RS_BERCNT_H = 14,
+	RS_BERCNT_M = 15,
+	RS_BERCNT_L = 16,
+	RS_UBC_H = 17,
+	RS_UBC_L = 18,
+	SIG_LEVEL = 19,
+	GPP_CTRL = 20,
+	RESET = 21,
+	DISEQC_MODE = 22,
+	SYM_RATE_H = 23,
+	SYM_RATE_L = 24,
+	VIT_MODE = 25,
+	QPSK_CTRL = 26,
+	GO = 27,
+	IE_QPSK_H = 28,
+	IE_QPSK_M = 29,
+	IE_QPSK_L = 30,
+	IE_FEC = 31,
+	QPSK_STAT_EN = 32,
+	FEC_STAT_EN = 33,
+	SYS_CLK = 34,
+	DISEQC_RATIO = 35,
+	DISEQC_INSTR = 36,
+	FR_LIM = 37,
+	FR_OFF = 38,
+	AGC_CTRL = 39,
+	AGC_INIT = 40,
+	AGC_REF = 41,
+	AGC_MAX = 42,
+	AGC_MIN = 43,
+	AGC_LK_TH = 44,
+	TS_AGC_LK_TH = 45,
+	AGC_PWR_SET = 46,
+	QPSK_MISC = 47,
+	SNR_THS_LOW = 48,
+	SNR_THS_HIGH = 49,
+	TS_SW_RATE = 50,
+	TS_SW_LIM_L = 51,
+	TS_SW_LIM_H = 52,
+	CS_SW_RATE_1 = 53,
+	CS_SW_RATE_2 = 54,
+	CS_SW_RATE_3 = 55,
+	CS_SW_RATE_4 = 56,
+	CS_SW_LIM = 57,
+	TS_LPK = 58,
+	TS_LPK_M = 59,
+	TS_LPK_L = 60,
+	CS_KPROP_H = 61,
+	CS_KPROP_L = 62,
+	CS_KINT_H = 63,
+	CS_KINT_L = 64,
+	QPSK_SCALE = 65,
+	TLD_OUTCLK_TH = 66,
+	TLD_INCLK_TH = 67,
+	FLD_TH = 68,
+	PLD_OUTLK3 = 69,
+	PLD_OUTLK2 = 70,
+	PLD_OUTLK1 = 71,
+	PLD_OUTLK0 = 72,
+	PLD_INLK3 = 73,
+	PLD_INLK2 = 74,
+	PLD_INLK1 = 75,
+	PLD_INLK0 = 76,
+	PLD_ACC_TIME = 77,
+	SWEEP_PAR = 78,
+	STARTUP_TIME = 79,
+	LOSSLOCK_TH = 80,
+	FEC_LOCK_TM = 81,
+	LOSSLOCK_TM = 82,
+	VIT_ERRPER_H = 83,
+	VIT_ERRPER_M = 84,
+	VIT_ERRPER_L = 85,
+	VIT_SETUP = 86,
+	VIT_REF0 = 87,
+	VIT_REF1 = 88,
+	VIT_REF2 = 89,
+	VIT_REF3 = 90,
+	VIT_REF4 = 91,
+	VIT_REF5 = 92,
+	VIT_REF6 = 93,
+	VIT_MAXERR = 94,
+	BA_SETUPT = 95,
+	OP_CTRL = 96,
+	FEC_SETUP = 97,
+	PROG_SYNC = 98,
+	AFC_SEAR_TH = 99,
+	CSACC_DIF_TH = 100,
+	QPSK_LK_CT = 101,
+	QPSK_ST_CT = 102,
+	MON_CTRL = 103,
+	QPSK_RESET = 104,
+	QPSK_TST_CT = 105,
+	QPSK_TST_ST = 106,
+	TEST_R = 107,
+	AGC_H = 108,
+	AGC_M = 109,
+	AGC_L = 110,
+	FREQ_ERR1_H = 111,
+	FREQ_ERR1_M = 112,
+	FREQ_ERR1_L = 113,
+	FREQ_ERR2_H = 114,
+	FREQ_ERR2_L = 115,
+	SYM_RAT_OP_H = 116,
+	SYM_RAT_OP_L = 117,
+	DESEQC2_INT = 118,
+	DISEQC2_STAT = 119,
+	DISEQC2_FIFO = 120,
+	DISEQC2_CTRL1 = 121,
+	DISEQC2_CTRL2 = 122,
+	MONITOR_H = 123,
+	MONITOR_L = 124,
+	TEST_MODE = 125,
+	ID = 126,
+	CONFIG = 127
+};
+
+enum mt312_model_id {
+	ID_VP310 = 1,
+	ID_MT312 = 3
+};
+
+#endif				/* DVB_FRONTENDS_MT312_PRIV */
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c
new file mode 100644
index 0000000..50326c7
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt352.c
@@ -0,0 +1,610 @@
+/*
+ *  Driver for Zarlink DVB-T MT352 demodulator
+ *
+ *  Written by Holger Waechtler <holger@qanu.de>
+ *	 and Daniel Mack <daniel@qanu.de>
+ *
+ *  AVerMedia AVerTV DVB-T 771 support by
+ *       Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ *  Support for Samsung TDTC9251DH01C(M) tuner
+ *  Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ *                     Amauri  Celani  <acelani@essegi.net>
+ *
+ *  DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ *       Christopher 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; 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "mt352_priv.h"
+#include "mt352.h"
+
+struct mt352_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend frontend;
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct mt352_config* config;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "mt352: " args); \
+	} while (0)
+
+static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+	u8 buf[2] = { reg, val };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0,
+			       .buf = buf, .len = 2 };
+	int err = i2c_transfer(state->i2c, &msg, 1);
+	if (err != 1) {
+		printk("mt352_write() to reg %x failed (err = %d)!\n", reg, err);
+		return err;
+	}
+	return 0;
+}
+
+int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
+{
+	int err,i;
+	for (i=0; i < ilen-1; i++)
+		if ((err = mt352_single_write(fe,ibuf[0]+i,ibuf[i+1])))
+			return err;
+
+	return 0;
+}
+
+static int mt352_read_register(struct mt352_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address,
+				    .flags = 0,
+				    .buf = b0, .len = 1 },
+				  { .addr = state->config->demod_address,
+				    .flags = I2C_M_RD,
+				    .buf = b1, .len = 1 } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		printk("%s: readreg error (reg=%d, ret==%i)\n",
+		       __FUNCTION__, reg, ret);
+		return ret;
+	}
+
+	return b1[0];
+}
+
+int mt352_read(struct dvb_frontend *fe, u8 reg)
+{
+	return mt352_read_register(fe->demodulator_priv,reg);
+}
+
+static int mt352_sleep(struct dvb_frontend* fe)
+{
+	static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 };
+
+	mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
+	return 0;
+}
+
+static void mt352_calc_nominal_rate(struct mt352_state* state,
+				    enum fe_bandwidth bandwidth,
+				    unsigned char *buf)
+{
+	u32 adc_clock = 20480; /* 20.340 MHz */
+	u32 bw,value;
+
+	switch (bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		bw = 6;
+		break;
+	case BANDWIDTH_7_MHZ:
+		bw = 7;
+		break;
+	case BANDWIDTH_8_MHZ:
+	default:
+		bw = 8;
+		break;
+	}
+	if (state->config->adc_clock)
+		adc_clock = state->config->adc_clock;
+
+	value = 64 * bw * (1<<16) / (7 * 8);
+	value = value * 1000 / adc_clock;
+	dprintk("%s: bw %d, adc_clock %d => 0x%x\n",
+		__FUNCTION__, bw, adc_clock, value);
+	buf[0] = msb(value);
+	buf[1] = lsb(value);
+}
+
+static void mt352_calc_input_freq(struct mt352_state* state,
+				  unsigned char *buf)
+{
+	int adc_clock = 20480; /* 20.480000 MHz */
+	int if2       = 36167; /* 36.166667 MHz */
+	int ife,value;
+
+	if (state->config->adc_clock)
+		adc_clock = state->config->adc_clock;
+	if (state->config->if2)
+		if2 = state->config->if2;
+
+	ife = (2*adc_clock - if2);
+	value = -16374 * ife / adc_clock;
+	dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n",
+		__FUNCTION__, if2, ife, adc_clock, value, value & 0x3fff);
+	buf[0] = msb(value);
+	buf[1] = lsb(value);
+}
+
+static int mt352_set_parameters(struct dvb_frontend* fe,
+				struct dvb_frontend_parameters *param)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+	unsigned char buf[13];
+	static unsigned char tuner_go[] = { 0x5d, 0x01 };
+	static unsigned char fsm_go[]   = { 0x5e, 0x01 };
+	unsigned int tps = 0;
+	struct dvb_ofdm_parameters *op = &param->u.ofdm;
+
+	switch (op->code_rate_HP) {
+		case FEC_2_3:
+			tps |= (1 << 7);
+			break;
+		case FEC_3_4:
+			tps |= (2 << 7);
+			break;
+		case FEC_5_6:
+			tps |= (3 << 7);
+			break;
+		case FEC_7_8:
+			tps |= (4 << 7);
+			break;
+		case FEC_1_2:
+		case FEC_AUTO:
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (op->code_rate_LP) {
+		case FEC_2_3:
+			tps |= (1 << 4);
+			break;
+		case FEC_3_4:
+			tps |= (2 << 4);
+			break;
+		case FEC_5_6:
+			tps |= (3 << 4);
+			break;
+		case FEC_7_8:
+			tps |= (4 << 4);
+			break;
+		case FEC_1_2:
+		case FEC_AUTO:
+			break;
+		case FEC_NONE:
+			if (op->hierarchy_information == HIERARCHY_AUTO ||
+			    op->hierarchy_information == HIERARCHY_NONE)
+				break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (op->constellation) {
+		case QPSK:
+			break;
+		case QAM_AUTO:
+		case QAM_16:
+			tps |= (1 << 13);
+			break;
+		case QAM_64:
+			tps |= (2 << 13);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (op->transmission_mode) {
+		case TRANSMISSION_MODE_2K:
+		case TRANSMISSION_MODE_AUTO:
+			break;
+		case TRANSMISSION_MODE_8K:
+			tps |= (1 << 0);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (op->guard_interval) {
+		case GUARD_INTERVAL_1_32:
+		case GUARD_INTERVAL_AUTO:
+			break;
+		case GUARD_INTERVAL_1_16:
+			tps |= (1 << 2);
+			break;
+		case GUARD_INTERVAL_1_8:
+			tps |= (2 << 2);
+			break;
+		case GUARD_INTERVAL_1_4:
+			tps |= (3 << 2);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (op->hierarchy_information) {
+		case HIERARCHY_AUTO:
+		case HIERARCHY_NONE:
+			break;
+		case HIERARCHY_1:
+			tps |= (1 << 10);
+			break;
+		case HIERARCHY_2:
+			tps |= (2 << 10);
+			break;
+		case HIERARCHY_4:
+			tps |= (3 << 10);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+
+	buf[0] = TPS_GIVEN_1; /* TPS_GIVEN_1 and following registers */
+
+	buf[1] = msb(tps);      /* TPS_GIVEN_(1|0) */
+	buf[2] = lsb(tps);
+
+	buf[3] = 0x50;  // old
+//	buf[3] = 0xf4;  // pinnacle
+
+	mt352_calc_nominal_rate(state, op->bandwidth, buf+4);
+	mt352_calc_input_freq(state, buf+6);
+	state->config->pll_set(fe, param, buf+8);
+
+	mt352_write(fe, buf, sizeof(buf));
+	if (state->config->no_tuner) {
+		/* start decoding */
+		mt352_write(fe, fsm_go, 2);
+	} else {
+		/* start tuning */
+		mt352_write(fe, tuner_go, 2);
+	}
+	return 0;
+}
+
+static int mt352_get_parameters(struct dvb_frontend* fe,
+				struct dvb_frontend_parameters *param)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+	u16 tps;
+	u16 div;
+	u8 trl;
+	struct dvb_ofdm_parameters *op = &param->u.ofdm;
+	static const u8 tps_fec_to_api[8] =
+	{
+		FEC_1_2,
+		FEC_2_3,
+		FEC_3_4,
+		FEC_5_6,
+		FEC_7_8,
+		FEC_AUTO,
+		FEC_AUTO,
+		FEC_AUTO
+	};
+
+	if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 )
+		return -EINVAL;
+
+	/* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because
+	 * the mt352 sometimes works with the wrong parameters
+	 */
+	tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0);
+	div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0);
+	trl = mt352_read_register(state, TRL_NOMINAL_RATE_1);
+
+	op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7];
+	op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7];
+
+	switch ( (tps >> 13) & 3)
+	{
+		case 0:
+			op->constellation = QPSK;
+			break;
+		case 1:
+			op->constellation = QAM_16;
+			break;
+		case 2:
+			op->constellation = QAM_64;
+			break;
+		default:
+			op->constellation = QAM_AUTO;
+			break;
+	}
+
+	op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K;
+
+	switch ( (tps >> 2) & 3)
+	{
+		case 0:
+			op->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case 1:
+			op->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case 2:
+			op->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case 3:
+			op->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		default:
+			op->guard_interval = GUARD_INTERVAL_AUTO;
+			break;
+	}
+
+	switch ( (tps >> 10) & 7)
+	{
+		case 0:
+			op->hierarchy_information = HIERARCHY_NONE;
+			break;
+		case 1:
+			op->hierarchy_information = HIERARCHY_1;
+			break;
+		case 2:
+			op->hierarchy_information = HIERARCHY_2;
+			break;
+		case 3:
+			op->hierarchy_information = HIERARCHY_4;
+			break;
+		default:
+			op->hierarchy_information = HIERARCHY_AUTO;
+			break;
+	}
+
+	param->frequency = ( 500 * (div - IF_FREQUENCYx6) ) / 3 * 1000;
+
+	if (trl == 0x72)
+		op->bandwidth = BANDWIDTH_8_MHZ;
+	else if (trl == 0x64)
+		op->bandwidth = BANDWIDTH_7_MHZ;
+	else
+		op->bandwidth = BANDWIDTH_6_MHZ;
+
+
+	if (mt352_read_register(state, STATUS_2) & 0x02)
+		param->inversion = INVERSION_OFF;
+	else
+		param->inversion = INVERSION_ON;
+
+	return 0;
+}
+
+static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+	int s0, s1, s3;
+
+	/* FIXME:
+	 *
+	 * The MT352 design manual from Zarlink states (page 46-47):
+	 *
+	 * Notes about the TUNER_GO register:
+	 *
+	 * If the Read_Tuner_Byte (bit-1) is activated, then the tuner status
+	 * byte is copied from the tuner to the STATUS_3 register and
+	 * completion of the read operation is indicated by bit-5 of the
+	 * INTERRUPT_3 register.
+	 */
+
+	if ((s0 = mt352_read_register(state, STATUS_0)) < 0)
+		return -EREMOTEIO;
+	if ((s1 = mt352_read_register(state, STATUS_1)) < 0)
+		return -EREMOTEIO;
+	if ((s3 = mt352_read_register(state, STATUS_3)) < 0)
+		return -EREMOTEIO;
+
+	*status = 0;
+	if (s0 & (1 << 4))
+		*status |= FE_HAS_CARRIER;
+	if (s0 & (1 << 1))
+		*status |= FE_HAS_VITERBI;
+	if (s0 & (1 << 5))
+		*status |= FE_HAS_LOCK;
+	if (s1 & (1 << 1))
+		*status |= FE_HAS_SYNC;
+	if (s3 & (1 << 6))
+		*status |= FE_HAS_SIGNAL;
+
+	if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
+		      (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
+		*status &= ~FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int mt352_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+
+	*ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) |
+	       (mt352_read_register (state, RS_ERR_CNT_1) << 8) |
+	       (mt352_read_register (state, RS_ERR_CNT_0));
+
+	return 0;
+}
+
+static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+
+	u16 signal = ((mt352_read_register(state, AGC_GAIN_1) << 8) & 0x0f) |
+		      (mt352_read_register(state, AGC_GAIN_0));
+
+	*strength = ~signal;
+	return 0;
+}
+
+static int mt352_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+
+	u8 _snr = mt352_read_register (state, SNR);
+	*snr = (_snr << 8) | _snr;
+
+	return 0;
+}
+
+static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+
+	*ucblocks = (mt352_read_register (state,  RS_UBC_1) << 8) |
+		    (mt352_read_register (state,  RS_UBC_0));
+
+	return 0;
+}
+
+static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+	fe_tune_settings->min_delay_ms = 800;
+	fe_tune_settings->step_size = 0;
+	fe_tune_settings->max_drift = 0;
+
+	return 0;
+}
+
+static int mt352_init(struct dvb_frontend* fe)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+
+	static u8 mt352_reset_attach [] = { RESET, 0xC0 };
+
+	dprintk("%s: hello\n",__FUNCTION__);
+
+	if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 ||
+	    (mt352_read_register(state, CONFIG) & 0x20) == 0) {
+
+		/* Do a "hard" reset */
+		mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
+		return state->config->demod_init(fe);
+	}
+
+	return 0;
+}
+
+static void mt352_release(struct dvb_frontend* fe)
+{
+	struct mt352_state* state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops mt352_ops;
+
+struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+				  struct i2c_adapter* i2c)
+{
+	struct mt352_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = kmalloc(sizeof(struct mt352_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+	memset(state,0,sizeof(*state));
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &mt352_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops mt352_ops = {
+
+	.info = {
+		.name			= "Zarlink MT352 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 174000000,
+		.frequency_max		= 862000000,
+		.frequency_stepsize	= 166667,
+		.frequency_tolerance	= 0,
+		.caps = 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_HIERARCHY_AUTO | FE_CAN_RECOVER |
+			FE_CAN_MUTE_TS
+	},
+
+	.release = mt352_release,
+
+	.init = mt352_init,
+	.sleep = mt352_sleep,
+
+	.set_frontend = mt352_set_parameters,
+	.get_frontend = mt352_get_parameters,
+	.get_tune_settings = mt352_get_tune_settings,
+
+	.read_status = mt352_read_status,
+	.read_ber = mt352_read_ber,
+	.read_signal_strength = mt352_read_signal_strength,
+	.read_snr = mt352_read_snr,
+	.read_ucblocks = mt352_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(mt352_attach);
+EXPORT_SYMBOL(mt352_write);
+EXPORT_SYMBOL(mt352_read);
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h
new file mode 100644
index 0000000..f5d8a5a
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt352.h
@@ -0,0 +1,72 @@
+/*
+ *  Driver for Zarlink DVB-T MT352 demodulator
+ *
+ *  Written by Holger Waechtler <holger@qanu.de>
+ *	 and Daniel Mack <daniel@qanu.de>
+ *
+ *  AVerMedia AVerTV DVB-T 771 support by
+ *       Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ *  Support for Samsung TDTC9251DH01C(M) tuner
+ *  Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ *                     Amauri  Celani  <acelani@essegi.net>
+ *
+ *  DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ *       Christopher 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; 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 MT352_H
+#define MT352_H
+
+#include <linux/dvb/frontend.h>
+
+struct mt352_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* frequencies in kHz */
+	int adc_clock;  // default: 20480
+	int if2;        // default: 36166
+
+	/* set if no pll is connected to the secondary i2c bus */
+	int no_tuner;
+
+	/* Initialise the demodulator and PLL. Cannot be NULL */
+	int (*demod_init)(struct dvb_frontend* fe);
+
+	/* PLL setup - fill out the supplied 5 byte buffer with your PLL settings.
+	 * byte0: Set to pll i2c address (nonlinux; left shifted by 1)
+	 * byte1-4: PLL configuration.
+	 */
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf);
+};
+
+extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+					 struct i2c_adapter* i2c);
+
+extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen);
+extern int mt352_read(struct dvb_frontend *fe, u8 reg);
+
+#endif // MT352_H
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/mt352_priv.h b/drivers/media/dvb/frontends/mt352_priv.h
new file mode 100644
index 0000000..44ad0d4
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt352_priv.h
@@ -0,0 +1,127 @@
+/*
+ *  Driver for Zarlink DVB-T MT352 demodulator
+ *
+ *  Written by Holger Waechtler <holger@qanu.de>
+ *	 and Daniel Mack <daniel@qanu.de>
+ *
+ *  AVerMedia AVerTV DVB-T 771 support by
+ *       Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ *  Support for Samsung TDTC9251DH01C(M) tuner
+ *  Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ *                     Amauri  Celani  <acelani@essegi.net>
+ *
+ *  DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ *       Christopher 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; 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 _MT352_PRIV_
+#define _MT352_PRIV_
+
+#define ID_MT352        0x13
+
+#define msb(x) (((x) >> 8) & 0xff)
+#define lsb(x) ((x) & 0xff)
+
+enum mt352_reg_addr {
+	STATUS_0           = 0x00,
+	STATUS_1           = 0x01,
+	STATUS_2           = 0x02,
+	STATUS_3           = 0x03,
+	STATUS_4           = 0x04,
+	INTERRUPT_0        = 0x05,
+	INTERRUPT_1        = 0x06,
+	INTERRUPT_2        = 0x07,
+	INTERRUPT_3        = 0x08,
+	SNR                = 0x09,
+	VIT_ERR_CNT_2      = 0x0A,
+	VIT_ERR_CNT_1      = 0x0B,
+	VIT_ERR_CNT_0      = 0x0C,
+	RS_ERR_CNT_2       = 0x0D,
+	RS_ERR_CNT_1       = 0x0E,
+	RS_ERR_CNT_0       = 0x0F,
+	RS_UBC_1           = 0x10,
+	RS_UBC_0           = 0x11,
+	AGC_GAIN_3         = 0x12,
+	AGC_GAIN_2         = 0x13,
+	AGC_GAIN_1         = 0x14,
+	AGC_GAIN_0         = 0x15,
+	FREQ_OFFSET_2      = 0x17,
+	FREQ_OFFSET_1      = 0x18,
+	FREQ_OFFSET_0      = 0x19,
+	TIMING_OFFSET_1    = 0x1A,
+	TIMING_OFFSET_0    = 0x1B,
+	CHAN_FREQ_1        = 0x1C,
+	CHAN_FREQ_0        = 0x1D,
+	TPS_RECEIVED_1     = 0x1E,
+	TPS_RECEIVED_0     = 0x1F,
+	TPS_CURRENT_1      = 0x20,
+	TPS_CURRENT_0      = 0x21,
+	TPS_CELL_ID_1      = 0x22,
+	TPS_CELL_ID_0      = 0x23,
+	TPS_MISC_DATA_2    = 0x24,
+	TPS_MISC_DATA_1    = 0x25,
+	TPS_MISC_DATA_0    = 0x26,
+	RESET              = 0x50,
+	TPS_GIVEN_1        = 0x51,
+	TPS_GIVEN_0        = 0x52,
+	ACQ_CTL            = 0x53,
+	TRL_NOMINAL_RATE_1 = 0x54,
+	TRL_NOMINAL_RATE_0 = 0x55,
+	INPUT_FREQ_1       = 0x56,
+	INPUT_FREQ_0       = 0x57,
+	TUNER_ADDR         = 0x58,
+	CHAN_START_1       = 0x59,
+	CHAN_START_0       = 0x5A,
+	CONT_1             = 0x5B,
+	CONT_0             = 0x5C,
+	TUNER_GO           = 0x5D,
+	STATUS_EN_0        = 0x5F,
+	STATUS_EN_1        = 0x60,
+	INTERRUPT_EN_0     = 0x61,
+	INTERRUPT_EN_1     = 0x62,
+	INTERRUPT_EN_2     = 0x63,
+	INTERRUPT_EN_3     = 0x64,
+	AGC_TARGET         = 0x67,
+	AGC_CTL            = 0x68,
+	CAPT_RANGE         = 0x75,
+	SNR_SELECT_1       = 0x79,
+	SNR_SELECT_0       = 0x7A,
+	RS_ERR_PER_1       = 0x7C,
+	RS_ERR_PER_0       = 0x7D,
+	CHIP_ID            = 0x7F,
+	CHAN_STOP_1        = 0x80,
+	CHAN_STOP_0        = 0x81,
+	CHAN_STEP_1        = 0x82,
+	CHAN_STEP_0        = 0x83,
+	FEC_LOCK_TIME      = 0x85,
+	OFDM_LOCK_TIME     = 0x86,
+	ACQ_DELAY          = 0x87,
+	SCAN_CTL           = 0x88,
+	CLOCK_CTL          = 0x89,
+	CONFIG             = 0x8A,
+	MCLK_RATIO         = 0x8B,
+	GPP_CTL            = 0x8C,
+	ADC_CTL_1          = 0x8E,
+	ADC_CTL_0          = 0x8F
+};
+
+/* here we assume 1/6MHz == 166.66kHz stepsize */
+#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+
+#endif                          /* _MT352_PRIV_ */
diff --git a/drivers/media/dvb/frontends/nxt2002.c b/drivers/media/dvb/frontends/nxt2002.c
new file mode 100644
index 0000000..4743aa1
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt2002.c
@@ -0,0 +1,705 @@
+/*
+    Support for B2C2/BBTI Technisat Air2PC - ATSC
+
+    Copyright (C) 2004 Taylor Jacob <rtjacob@earthlink.net>
+
+    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.
+
+*/
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
+#define CRC_CCIT_MASK 0x1021
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "nxt2002.h"
+
+struct nxt2002_state {
+
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	const struct nxt2002_config* config;
+	struct dvb_frontend frontend;
+
+	/* demodulator private data */
+	u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "nxt2002: " args); \
+	} while (0)
+
+static int i2c_writebytes (struct nxt2002_state* state, u8 reg, u8 *buf, u8 len)
+{
+	/* probbably a much better way or doing this */
+	u8 buf2 [256],x;
+	int err;
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 };
+
+	buf2[0] = reg;
+	for (x = 0 ; x < len ; x++)
+		buf2[x+1] = buf[x];
+
+	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+		printk ("%s: i2c write error (addr %02x, err == %i)\n",
+			__FUNCTION__, state->config->demod_address, err);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static u8 i2c_readbytes (struct nxt2002_state* state, u8 reg, u8* buf, u8 len)
+{
+	u8 reg2 [] = { reg };
+
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 },
+			{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+	int err;
+
+	if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) {
+		printk ("%s: i2c read error (addr %02x, err == %i)\n",
+			__FUNCTION__, state->config->demod_address, err);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static u16 nxt2002_crc(u16 crc, u8 c)
+{
+
+	u8 i;
+	u16 input = (u16) c & 0xFF;
+
+	input<<=8;
+	for(i=0 ;i<8 ;i++) {
+		if((crc ^ input) & 0x8000)
+			crc=(crc<<1)^CRC_CCIT_MASK;
+		else
+			crc<<=1;
+	input<<=1;
+	}
+	return crc;
+}
+
+static int nxt2002_writereg_multibyte (struct nxt2002_state* state, u8 reg, u8* data, u8 len)
+{
+	u8 buf;
+	dprintk("%s\n", __FUNCTION__);
+
+	/* set multi register length */
+	i2c_writebytes(state,0x34,&len,1);
+
+	/* set mutli register register */
+	i2c_writebytes(state,0x35,&reg,1);
+
+	/* send the actual data */
+	i2c_writebytes(state,0x36,data,len);
+
+	/* toggle the multireg write bit*/
+	buf = 0x02;
+	i2c_writebytes(state,0x21,&buf,1);
+
+	i2c_readbytes(state,0x21,&buf,1);
+
+	if ((buf & 0x02) == 0)
+		return 0;
+
+	dprintk("Error writing multireg register %02X\n",reg);
+
+	return 0;
+}
+
+static int nxt2002_readreg_multibyte (struct nxt2002_state* state, u8 reg, u8* data, u8 len)
+{
+	u8 len2;
+	dprintk("%s\n", __FUNCTION__);
+
+	/* set multi register length */
+	len2 = len & 0x80;
+	i2c_writebytes(state,0x34,&len2,1);
+
+	/* set mutli register register */
+	i2c_writebytes(state,0x35,&reg,1);
+
+	/* send the actual data */
+	i2c_readbytes(state,reg,data,len);
+
+	return 0;
+}
+
+static void nxt2002_microcontroller_stop (struct nxt2002_state* state)
+{
+	u8 buf[2],counter = 0;
+	dprintk("%s\n", __FUNCTION__);
+
+	buf[0] = 0x80;
+	i2c_writebytes(state,0x22,buf,1);
+
+	while (counter < 20) {
+		i2c_readbytes(state,0x31,buf,1);
+		if (buf[0] & 0x40)
+			return;
+		msleep(10);
+		counter++;
+	}
+
+	dprintk("Timeout waiting for micro to stop.. This is ok after firmware upload\n");
+	return;
+}
+
+static void nxt2002_microcontroller_start (struct nxt2002_state* state)
+{
+	u8 buf;
+	dprintk("%s\n", __FUNCTION__);
+
+	buf = 0x00;
+	i2c_writebytes(state,0x22,&buf,1);
+}
+
+static int nxt2002_writetuner (struct nxt2002_state* state, u8* data)
+{
+	u8 buf,count = 0;
+
+	dprintk("Tuner Bytes: %02X %02X %02X %02X\n",data[0],data[1],data[2],data[3]);
+
+	dprintk("%s\n", __FUNCTION__);
+	/* stop the micro first */
+	nxt2002_microcontroller_stop(state);
+
+	/* set the i2c transfer speed to the tuner */
+	buf = 0x03;
+	i2c_writebytes(state,0x20,&buf,1);
+
+	/* setup to transfer 4 bytes via i2c */
+	buf = 0x04;
+	i2c_writebytes(state,0x34,&buf,1);
+
+	/* write actual tuner bytes */
+	i2c_writebytes(state,0x36,data,4);
+
+	/* set tuner i2c address */
+	buf = 0xC2;
+	i2c_writebytes(state,0x35,&buf,1);
+
+	/* write UC Opmode to begin transfer */
+	buf = 0x80;
+	i2c_writebytes(state,0x21,&buf,1);
+
+	while (count < 20) {
+		i2c_readbytes(state,0x21,&buf,1);
+		if ((buf & 0x80)== 0x00)
+			return 0;
+		msleep(100);
+		count++;
+	}
+
+	printk("nxt2002: timeout error writing tuner\n");
+	return 0;
+}
+
+static void nxt2002_agc_reset(struct nxt2002_state* state)
+{
+	u8 buf;
+	dprintk("%s\n", __FUNCTION__);
+
+	buf = 0x08;
+	i2c_writebytes(state,0x08,&buf,1);
+
+	buf = 0x00;
+	i2c_writebytes(state,0x08,&buf,1);
+
+	return;
+}
+
+static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	u8 buf[256],written = 0,chunkpos = 0;
+	u16 rambase,position,crc = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+	dprintk("Firmware is %zu bytes\n",fw->size);
+
+	/* Get the RAM base for this nxt2002 */
+	i2c_readbytes(state,0x10,buf,1);
+
+	if (buf[0] & 0x10)
+		rambase = 0x1000;
+	else
+		rambase = 0x0000;
+
+	dprintk("rambase on this nxt2002 is %04X\n",rambase);
+
+	/* Hold the micro in reset while loading firmware */
+	buf[0] = 0x80;
+	i2c_writebytes(state,0x2B,buf,1);
+
+	for (position = 0; position < fw->size ; position++) {
+		if (written == 0) {
+			crc = 0;
+			chunkpos = 0x28;
+			buf[0] = ((rambase + position) >> 8);
+			buf[1] = (rambase + position) & 0xFF;
+			buf[2] = 0x81;
+			/* write starting address */
+			i2c_writebytes(state,0x29,buf,3);
+		}
+		written++;
+		chunkpos++;
+
+		if ((written % 4) == 0)
+			i2c_writebytes(state,chunkpos,&fw->data[position-3],4);
+
+		crc = nxt2002_crc(crc,fw->data[position]);
+
+		if ((written == 255) || (position+1 == fw->size)) {
+			/* write remaining bytes of firmware */
+			i2c_writebytes(state, chunkpos+4-(written %4),
+				&fw->data[position-(written %4) + 1],
+				written %4);
+			buf[0] = crc << 8;
+			buf[1] = crc & 0xFF;
+
+			/* write crc */
+			i2c_writebytes(state,0x2C,buf,2);
+
+			/* do a read to stop things */
+			i2c_readbytes(state,0x2A,buf,1);
+
+			/* set transfer mode to complete */
+			buf[0] = 0x80;
+			i2c_writebytes(state,0x2B,buf,1);
+
+			written = 0;
+		}
+	}
+
+	printk ("done.\n");
+	return 0;
+};
+
+static int nxt2002_setup_frontend_parameters (struct dvb_frontend* fe,
+					     struct dvb_frontend_parameters *p)
+{
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	u32 freq = 0;
+	u16 tunerfreq = 0;
+	u8 buf[4];
+
+	freq = 44000 + ( p->frequency / 1000 );
+
+	dprintk("freq = %d      p->frequency = %d\n",freq,p->frequency);
+
+	tunerfreq = freq * 24/4000;
+
+	buf[0] = (tunerfreq >> 8) & 0x7F;
+	buf[1] = (tunerfreq & 0xFF);
+
+	if (p->frequency <= 214000000) {
+		buf[2] = 0x84 + (0x06 << 3);
+		buf[3] = (p->frequency <= 172000000) ? 0x01 : 0x02;
+	} else if (p->frequency <= 721000000) {
+		buf[2] = 0x84 + (0x07 << 3);
+		buf[3] = (p->frequency <= 467000000) ? 0x02 : 0x08;
+	} else if (p->frequency <= 841000000) {
+		buf[2] = 0x84 + (0x0E << 3);
+		buf[3] = 0x08;
+	} else {
+		buf[2] = 0x84 + (0x0F << 3);
+		buf[3] = 0x02;
+	}
+
+	/* write frequency information */
+	nxt2002_writetuner(state,buf);
+
+	/* reset the agc now that tuning has been completed */
+	nxt2002_agc_reset(state);
+
+
+
+	/* set target power level */
+	switch (p->u.vsb.modulation) {
+		case QAM_64:
+		case QAM_256:
+				buf[0] = 0x74;
+				break;
+		case VSB_8:
+				buf[0] = 0x70;
+				break;
+		default:
+				return -EINVAL;
+				break;
+	}
+	i2c_writebytes(state,0x42,buf,1);
+
+	/* configure sdm */
+	buf[0] = 0x87;
+	i2c_writebytes(state,0x57,buf,1);
+
+	/* write sdm1 input */
+	buf[0] = 0x10;
+	buf[1] = 0x00;
+	nxt2002_writereg_multibyte(state,0x58,buf,2);
+
+	/* write sdmx input */
+	switch (p->u.vsb.modulation) {
+		case QAM_64:
+				buf[0] = 0x68;
+				break;
+		case QAM_256:
+				buf[0] = 0x64;
+				break;
+		case VSB_8:
+				buf[0] = 0x60;
+				break;
+		default:
+				return -EINVAL;
+				break;
+	}
+	buf[1] = 0x00;
+	nxt2002_writereg_multibyte(state,0x5C,buf,2);
+
+	/* write adc power lpf fc */
+	buf[0] = 0x05;
+	i2c_writebytes(state,0x43,buf,1);
+
+	/* write adc power lpf fc */
+	buf[0] = 0x05;
+	i2c_writebytes(state,0x43,buf,1);
+
+	/* write accumulator2 input */
+	buf[0] = 0x80;
+	buf[1] = 0x00;
+	nxt2002_writereg_multibyte(state,0x4B,buf,2);
+
+	/* write kg1 */
+	buf[0] = 0x00;
+	i2c_writebytes(state,0x4D,buf,1);
+
+	/* write sdm12 lpf fc */
+	buf[0] = 0x44;
+	i2c_writebytes(state,0x55,buf,1);
+
+	/* write agc control reg */
+	buf[0] = 0x04;
+	i2c_writebytes(state,0x41,buf,1);
+
+	/* write agc ucgp0 */
+	switch (p->u.vsb.modulation) {
+		case QAM_64:
+				buf[0] = 0x02;
+				break;
+		case QAM_256:
+				buf[0] = 0x03;
+				break;
+		case VSB_8:
+				buf[0] = 0x00;
+				break;
+		default:
+				return -EINVAL;
+				break;
+	}
+	i2c_writebytes(state,0x30,buf,1);
+
+	/* write agc control reg */
+	buf[0] = 0x00;
+	i2c_writebytes(state,0x41,buf,1);
+
+	/* write accumulator2 input */
+	buf[0] = 0x80;
+	buf[1] = 0x00;
+	nxt2002_writereg_multibyte(state,0x49,buf,2);
+	nxt2002_writereg_multibyte(state,0x4B,buf,2);
+
+	/* write agc control reg */
+	buf[0] = 0x04;
+	i2c_writebytes(state,0x41,buf,1);
+
+	nxt2002_microcontroller_start(state);
+
+	/* adjacent channel detection should be done here, but I don't
+	have any stations with this need so I cannot test it */
+
+	return 0;
+}
+
+static int nxt2002_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	u8 lock;
+	i2c_readbytes(state,0x31,&lock,1);
+
+	*status = 0;
+	if (lock & 0x20) {
+		*status |= FE_HAS_SIGNAL;
+		*status |= FE_HAS_CARRIER;
+		*status |= FE_HAS_VITERBI;
+		*status |= FE_HAS_SYNC;
+		*status |= FE_HAS_LOCK;
+	}
+	return 0;
+}
+
+static int nxt2002_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	u8 b[3];
+
+	nxt2002_readreg_multibyte(state,0xE6,b,3);
+
+	*ber = ((b[0] << 8) + b[1]) * 8;
+
+	return 0;
+}
+
+static int nxt2002_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	u8 b[2];
+	u16 temp = 0;
+
+	/* setup to read cluster variance */
+	b[0] = 0x00;
+	i2c_writebytes(state,0xA1,b,1);
+
+	/* get multreg val */
+	nxt2002_readreg_multibyte(state,0xA6,b,2);
+
+	temp = (b[0] << 8) | b[1];
+	*strength = ((0x7FFF - temp) & 0x0FFF) * 16;
+
+	return 0;
+}
+
+static int nxt2002_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	u8 b[2];
+	u16 temp = 0, temp2;
+	u32 snrdb = 0;
+
+	/* setup to read cluster variance */
+	b[0] = 0x00;
+	i2c_writebytes(state,0xA1,b,1);
+
+	/* get multreg val from 0xA6 */
+	nxt2002_readreg_multibyte(state,0xA6,b,2);
+
+	temp = (b[0] << 8) | b[1];
+	temp2 = 0x7FFF - temp;
+
+	/* snr will be in db */
+	if (temp2 > 0x7F00)
+		snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) );
+	else if (temp2 > 0x7EC0)
+		snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) );
+	else if (temp2 > 0x7C00)
+		snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) );
+	else
+		snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) );
+
+        /* the value reported back from the frontend will be FFFF=32db 0000=0db */
+
+	*snr = snrdb * (0xFFFF/32000);
+
+	return 0;
+}
+
+static int nxt2002_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	u8 b[3];
+
+	nxt2002_readreg_multibyte(state,0xE6,b,3);
+	*ucblocks = b[2];
+
+	return 0;
+}
+
+static int nxt2002_sleep(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int nxt2002_init(struct dvb_frontend* fe)
+{
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	const struct firmware *fw;
+	int ret;
+	u8 buf[2];
+
+	if (!state->initialised) {
+		/* request the firmware, this will block until someone uploads it */
+		printk("nxt2002: Waiting for firmware upload (%s)...\n", NXT2002_DEFAULT_FIRMWARE);
+		ret = state->config->request_firmware(fe, &fw, NXT2002_DEFAULT_FIRMWARE);
+		printk("nxt2002: Waiting for firmware upload(2)...\n");
+		if (ret) {
+			printk("nxt2002: no firmware upload (timeout or file not found?)\n");
+			return ret;
+		}
+
+		ret = nxt2002_load_firmware(fe, fw);
+		if (ret) {
+			printk("nxt2002: writing firmware to device failed\n");
+			release_firmware(fw);
+			return ret;
+		}
+		printk("nxt2002: firmware upload complete\n");
+
+		/* Put the micro into reset */
+		nxt2002_microcontroller_stop(state);
+
+		/* ensure transfer is complete */
+		buf[0]=0;
+		i2c_writebytes(state,0x2B,buf,1);
+
+		/* Put the micro into reset for real this time */
+		nxt2002_microcontroller_stop(state);
+
+		/* soft reset everything (agc,frontend,eq,fec)*/
+		buf[0] = 0x0F;
+		i2c_writebytes(state,0x08,buf,1);
+		buf[0] = 0x00;
+		i2c_writebytes(state,0x08,buf,1);
+
+		/* write agc sdm configure */
+		buf[0] = 0xF1;
+		i2c_writebytes(state,0x57,buf,1);
+
+		/* write mod output format */
+		buf[0] = 0x20;
+		i2c_writebytes(state,0x09,buf,1);
+
+		/* write fec mpeg mode */
+		buf[0] = 0x7E;
+		buf[1] = 0x00;
+		i2c_writebytes(state,0xE9,buf,2);
+
+		/* write mux selection */
+		buf[0] = 0x00;
+		i2c_writebytes(state,0xCC,buf,1);
+
+		state->initialised = 1;
+	}
+
+	return 0;
+}
+
+static int nxt2002_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+	fesettings->min_delay_ms = 500;
+	fesettings->step_size = 0;
+	fesettings->max_drift = 0;
+	return 0;
+}
+
+static void nxt2002_release(struct dvb_frontend* fe)
+{
+	struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops nxt2002_ops;
+
+struct dvb_frontend* nxt2002_attach(const struct nxt2002_config* config,
+				   struct i2c_adapter* i2c)
+{
+	struct nxt2002_state* state = NULL;
+	u8 buf [] = {0,0,0,0,0};
+
+	/* allocate memory for the internal state */
+	state = (struct nxt2002_state*) kmalloc(sizeof(struct nxt2002_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &nxt2002_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+
+        /* Check the first 5 registers to ensure this a revision we can handle */
+
+	i2c_readbytes(state, 0x00, buf, 5);
+	if (buf[0] != 0x04) goto error;		/* device id */
+	if (buf[1] != 0x02) goto error;		/* fab id */
+	if (buf[2] != 0x11) goto error;		/* month */
+	if (buf[3] != 0x20) goto error;		/* year msb */
+	if (buf[4] != 0x00) goto error;		/* year lsb */
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops nxt2002_ops = {
+
+	.info = {
+		.name = "Nextwave nxt2002 VSB/QAM frontend",
+		.type = FE_ATSC,
+		.frequency_min =  54000000,
+		.frequency_max = 860000000,
+                /* stepsize is just a guess */
+		.frequency_stepsize = 166666,
+		.caps = 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_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256
+	},
+
+	.release = nxt2002_release,
+
+	.init = nxt2002_init,
+	.sleep = nxt2002_sleep,
+
+	.set_frontend = nxt2002_setup_frontend_parameters,
+	.get_tune_settings = nxt2002_get_tune_settings,
+
+	.read_status = nxt2002_read_status,
+	.read_ber = nxt2002_read_ber,
+	.read_signal_strength = nxt2002_read_signal_strength,
+	.read_snr = nxt2002_read_snr,
+	.read_ucblocks = nxt2002_read_ucblocks,
+
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("NXT2002 ATSC (8VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver");
+MODULE_AUTHOR("Taylor Jacob");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nxt2002_attach);
diff --git a/drivers/media/dvb/frontends/nxt2002.h b/drivers/media/dvb/frontends/nxt2002.h
new file mode 100644
index 0000000..462301f
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt2002.h
@@ -0,0 +1,23 @@
+/*
+   Driver for the Nxt2002 demodulator
+*/
+
+#ifndef NXT2002_H
+#define NXT2002_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct nxt2002_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* nxt2002_attach(const struct nxt2002_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // NXT2002_H
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c
new file mode 100644
index 0000000..a41f7da
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt6000.c
@@ -0,0 +1,554 @@
+/*
+	NxtWave Communications - NXT6000 demodulator driver
+
+    Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
+    Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.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; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "nxt6000_priv.h"
+#include "nxt6000.h"
+
+
+
+struct nxt6000_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	/* configuration settings */
+	const struct nxt6000_config* config;
+	struct dvb_frontend frontend;
+};
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 };
+	int ret;
+
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
+		dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret);
+
+	return (ret != 1) ? -EFAULT : 0;
+}
+
+static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msgs[] = {
+		{.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1},
+		{.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
+	};
+
+	ret = i2c_transfer(state->i2c, msgs, 2);
+
+	if (ret != 2)
+		dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret);
+
+	return b1[0];
+}
+
+static void nxt6000_reset(struct nxt6000_state* state)
+{
+	u8 val;
+
+	val = nxt6000_readreg(state, OFDM_COR_CTL);
+
+	nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT);
+	nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT);
+}
+
+static int nxt6000_set_bandwidth(struct nxt6000_state* state, fe_bandwidth_t bandwidth)
+{
+	u16 nominal_rate;
+	int result;
+
+	switch (bandwidth) {
+
+	case BANDWIDTH_6_MHZ:
+		nominal_rate = 0x55B7;
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		nominal_rate = 0x6400;
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		nominal_rate = 0x7249;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0)
+		return result;
+
+	return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF);
+}
+
+static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval)
+{
+	switch (guard_interval) {
+
+	case GUARD_INTERVAL_1_32:
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+	case GUARD_INTERVAL_1_16:
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+	case GUARD_INTERVAL_AUTO:
+	case GUARD_INTERVAL_1_8:
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+	case GUARD_INTERVAL_1_4:
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion)
+{
+	switch (inversion) {
+
+	case INVERSION_OFF:
+		return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00);
+
+	case INVERSION_ON:
+		return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV);
+
+	default:
+		return -EINVAL;
+
+	}
+}
+
+static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode)
+{
+	int result;
+
+	switch (transmission_mode) {
+
+	case TRANSMISSION_MODE_2K:
+		if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0)
+			return result;
+
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04));
+
+	case TRANSMISSION_MODE_8K:
+	case TRANSMISSION_MODE_AUTO:
+		if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0)
+			return result;
+
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04));
+
+	default:
+		return -EINVAL;
+
+	}
+}
+
+static void nxt6000_setup(struct dvb_frontend* fe)
+{
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+
+	nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM);
+	nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01);
+	nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC);
+	nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F));
+	nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02);
+	nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW);
+	nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06);
+	nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31);
+	nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04);
+	nxt6000_writereg(state, CAS_FREQ, 0xBB);	/* CHECKME */
+	nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2);
+	nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256);
+	nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49);
+	nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72);
+	nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5);
+	nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2);
+	nxt6000_writereg(state, DIAG_CONFIG, TB_SET);
+
+	if (state->config->clock_inversion)
+		nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION);
+	else
+		nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0);
+
+	nxt6000_writereg(state, TS_FORMAT, 0);
+
+	if (state->config->pll_init) {
+		nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01);	/* open i2c bus switch */
+		state->config->pll_init(fe);
+		nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00);	/* close i2c bus switch */
+	}
+}
+
+static void nxt6000_dump_status(struct nxt6000_state *state)
+{
+	u8 val;
+
+/*
+	printk("RS_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, RS_COR_STAT));
+	printk("VIT_SYNC_STATUS: 0x%02X\n", nxt6000_readreg(fe, VIT_SYNC_STATUS));
+	printk("OFDM_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_COR_STAT));
+	printk("OFDM_SYR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_SYR_STAT));
+	printk("OFDM_TPS_RCVD_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_1));
+	printk("OFDM_TPS_RCVD_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_2));
+	printk("OFDM_TPS_RCVD_3: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_3));
+	printk("OFDM_TPS_RCVD_4: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_4));
+	printk("OFDM_TPS_RESERVED_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_1));
+	printk("OFDM_TPS_RESERVED_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_2));
+*/
+	printk("NXT6000 status:");
+
+	val = nxt6000_readreg(state, RS_COR_STAT);
+
+	printk(" DATA DESCR LOCK: %d,", val & 0x01);
+	printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01);
+
+	val = nxt6000_readreg(state, VIT_SYNC_STATUS);
+
+	printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01);
+
+	switch ((val >> 4) & 0x07) {
+
+	case 0x00:
+		printk(" VITERBI CODERATE: 1/2,");
+		break;
+
+	case 0x01:
+		printk(" VITERBI CODERATE: 2/3,");
+		break;
+
+	case 0x02:
+		printk(" VITERBI CODERATE: 3/4,");
+		break;
+
+	case 0x03:
+		printk(" VITERBI CODERATE: 5/6,");
+		break;
+
+	case 0x04:
+		printk(" VITERBI CODERATE: 7/8,");
+		break;
+
+	default:
+		printk(" VITERBI CODERATE: Reserved,");
+
+	}
+
+	val = nxt6000_readreg(state, OFDM_COR_STAT);
+
+	printk(" CHCTrack: %d,", (val >> 7) & 0x01);
+	printk(" TPSLock: %d,", (val >> 6) & 0x01);
+	printk(" SYRLock: %d,", (val >> 5) & 0x01);
+	printk(" AGCLock: %d,", (val >> 4) & 0x01);
+
+	switch (val & 0x0F) {
+
+	case 0x00:
+		printk(" CoreState: IDLE,");
+		break;
+
+	case 0x02:
+		printk(" CoreState: WAIT_AGC,");
+		break;
+
+	case 0x03:
+		printk(" CoreState: WAIT_SYR,");
+		break;
+
+	case 0x04:
+		printk(" CoreState: WAIT_PPM,");
+		break;
+
+	case 0x01:
+		printk(" CoreState: WAIT_TRL,");
+		break;
+
+	case 0x05:
+		printk(" CoreState: WAIT_TPS,");
+		break;
+
+	case 0x06:
+		printk(" CoreState: MONITOR_TPS,");
+		break;
+
+	default:
+		printk(" CoreState: Reserved,");
+
+	}
+
+	val = nxt6000_readreg(state, OFDM_SYR_STAT);
+
+	printk(" SYRLock: %d,", (val >> 4) & 0x01);
+	printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K");
+
+	switch ((val >> 4) & 0x03) {
+
+	case 0x00:
+		printk(" SYRGuard: 1/32,");
+		break;
+
+	case 0x01:
+		printk(" SYRGuard: 1/16,");
+		break;
+
+	case 0x02:
+		printk(" SYRGuard: 1/8,");
+		break;
+
+	case 0x03:
+		printk(" SYRGuard: 1/4,");
+		break;
+	}
+
+	val = nxt6000_readreg(state, OFDM_TPS_RCVD_3);
+
+	switch ((val >> 4) & 0x07) {
+
+	case 0x00:
+		printk(" TPSLP: 1/2,");
+		break;
+
+	case 0x01:
+		printk(" TPSLP: 2/3,");
+		break;
+
+	case 0x02:
+		printk(" TPSLP: 3/4,");
+		break;
+
+	case 0x03:
+		printk(" TPSLP: 5/6,");
+		break;
+
+	case 0x04:
+		printk(" TPSLP: 7/8,");
+		break;
+
+	default:
+		printk(" TPSLP: Reserved,");
+
+	}
+
+	switch (val & 0x07) {
+
+	case 0x00:
+		printk(" TPSHP: 1/2,");
+		break;
+
+	case 0x01:
+		printk(" TPSHP: 2/3,");
+		break;
+
+	case 0x02:
+		printk(" TPSHP: 3/4,");
+		break;
+
+	case 0x03:
+		printk(" TPSHP: 5/6,");
+		break;
+
+	case 0x04:
+		printk(" TPSHP: 7/8,");
+		break;
+
+	default:
+		printk(" TPSHP: Reserved,");
+
+	}
+
+	val = nxt6000_readreg(state, OFDM_TPS_RCVD_4);
+
+	printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K");
+
+	switch ((val >> 4) & 0x03) {
+
+	case 0x00:
+		printk(" TPSGuard: 1/32,");
+		break;
+
+	case 0x01:
+		printk(" TPSGuard: 1/16,");
+		break;
+
+	case 0x02:
+		printk(" TPSGuard: 1/8,");
+		break;
+
+	case 0x03:
+		printk(" TPSGuard: 1/4,");
+		break;
+
+	}
+
+	/* Strange magic required to gain access to RF_AGC_STATUS */
+	nxt6000_readreg(state, RF_AGC_VAL_1);
+	val = nxt6000_readreg(state, RF_AGC_STATUS);
+	val = nxt6000_readreg(state, RF_AGC_STATUS);
+
+	printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01);
+	printk("\n");
+}
+
+static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	u8 core_status;
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+
+	*status = 0;
+
+	core_status = nxt6000_readreg(state, OFDM_COR_STAT);
+
+	if (core_status & AGCLOCKED)
+		*status |= FE_HAS_SIGNAL;
+
+	if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK)
+		*status |= FE_HAS_CARRIER;
+
+	if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC)
+		*status |= FE_HAS_VITERBI;
+
+	if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS)
+		*status |= FE_HAS_SYNC;
+
+	if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)))
+		*status |= FE_HAS_LOCK;
+
+	if (debug)
+		nxt6000_dump_status(state);
+
+	return 0;
+}
+
+static int nxt6000_init(struct dvb_frontend* fe)
+{
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+
+	nxt6000_reset(state);
+	nxt6000_setup(fe);
+
+	return 0;
+}
+
+static int nxt6000_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *param)
+{
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+	int result;
+
+	nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01);	/* open i2c bus switch */
+	state->config->pll_set(fe, param);
+	nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00);	/* close i2c bus switch */
+
+	if ((result = nxt6000_set_bandwidth(state, param->u.ofdm.bandwidth)) < 0)
+		return result;
+	if ((result = nxt6000_set_guard_interval(state, param->u.ofdm.guard_interval)) < 0)
+		return result;
+	if ((result = nxt6000_set_transmission_mode(state, param->u.ofdm.transmission_mode)) < 0)
+		return result;
+	if ((result = nxt6000_set_inversion(state, param->inversion)) < 0)
+		return result;
+
+	return 0;
+}
+
+static void nxt6000_release(struct dvb_frontend* fe)
+{
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops nxt6000_ops;
+
+struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct nxt6000_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct nxt6000_state*) kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops nxt6000_ops = {
+
+	.info = {
+		.name = "NxtWave NXT6000 DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 0,
+		.frequency_max = 863250000,
+		.frequency_stepsize = 62500,
+		/*.frequency_tolerance = *//* FIXME: 12% of SR */
+		.symbol_rate_min = 0,	/* FIXME */
+		.symbol_rate_max = 9360000,	/* FIXME */
+		.symbol_rate_tolerance = 4000,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+	                FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+	                FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+	                FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+	                FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+	                FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = nxt6000_release,
+
+	.init = nxt6000_init,
+
+	.set_frontend = nxt6000_set_frontend,
+
+	.read_status = nxt6000_read_status,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver");
+MODULE_AUTHOR("Florian Schirmer");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nxt6000_attach);
diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb/frontends/nxt6000.h
new file mode 100644
index 0000000..b7d9bea
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt6000.h
@@ -0,0 +1,43 @@
+/*
+	NxtWave Communications - NXT6000 demodulator driver
+
+    Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
+    Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.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; 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 NXT6000_H
+#define NXT6000_H
+
+#include <linux/dvb/frontend.h>
+
+struct nxt6000_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* should clock inversion be used? */
+	u8 clock_inversion:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // NXT6000_H
diff --git a/drivers/media/dvb/frontends/nxt6000_priv.h b/drivers/media/dvb/frontends/nxt6000_priv.h
new file mode 100644
index 0000000..64b1a89
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt6000_priv.h
@@ -0,0 +1,265 @@
+/*
+ * Public Include File for DRV6000 users
+ * (ie. NxtWave Communications - NXT6000 demodulator driver)
+ *
+ * Copyright (C) 2001 NxtWave Communications, Inc.
+ *
+ */
+
+/*  Nxt6000 Register Addresses and Bit Masks */
+
+/* Maximum Register Number */
+#define MAXNXT6000REG          (0x9A)
+
+/* 0x1B A_VIT_BER_0  aka 0x3A */
+#define A_VIT_BER_0            (0x1B)
+
+/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */
+#define A_VIT_BER_TIMER_0      (0x1D)
+
+/* 0x21 RS_COR_STAT */
+#define RS_COR_STAT            (0x21)
+#define RSCORESTATUS           (0x03)
+
+/* 0x22 RS_COR_INTEN */
+#define RS_COR_INTEN           (0x22)
+
+/* 0x23 RS_COR_INSTAT */
+#define RS_COR_INSTAT          (0x23)
+#define INSTAT_ERROR           (0x04)
+#define LOCK_LOSS_BITS         (0x03)
+
+/* 0x24 RS_COR_SYNC_PARAM */
+#define RS_COR_SYNC_PARAM      (0x24)
+#define SYNC_PARAM             (0x03)
+
+/* 0x25 BER_CTRL */
+#define BER_CTRL               (0x25)
+#define BER_ENABLE             (0x02)
+#define BER_RESET              (0x01)
+
+/* 0x26 BER_PAY */
+#define BER_PAY                (0x26)
+
+/* 0x27 BER_PKT_L */
+#define BER_PKT_L              (0x27)
+#define BER_PKTOVERFLOW        (0x80)
+
+/* 0x30 VIT_COR_CTL */
+#define VIT_COR_CTL            (0x30)
+#define BER_CONTROL            (0x02)
+#define VIT_COR_MASK           (0x82)
+#define VIT_COR_RESYNC         (0x80)
+
+
+/* 0x32 VIT_SYNC_STATUS */
+#define VIT_SYNC_STATUS        (0x32)
+#define VITINSYNC              (0x80)
+
+/* 0x33 VIT_COR_INTEN */
+#define VIT_COR_INTEN          (0x33)
+#define GLOBAL_ENABLE          (0x80)
+
+/* 0x34 VIT_COR_INTSTAT */
+#define VIT_COR_INTSTAT        (0x34)
+#define BER_DONE               (0x08)
+#define BER_OVERFLOW           (0x10)
+
+			     /* 0x38 OFDM_BERTimer *//* Use the alias registers */
+#define A_VIT_BER_TIMER_0      (0x1D)
+
+			     /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */
+#define A_VIT_BER_0            (0x1B)
+
+/* 0x40 OFDM_COR_CTL */
+#define OFDM_COR_CTL           (0x40)
+#define COREACT                (0x20)
+#define HOLDSM                 (0x10)
+#define WAIT_AGC               (0x02)
+#define WAIT_SYR               (0x03)
+
+/* 0x41 OFDM_COR_STAT */
+#define OFDM_COR_STAT          (0x41)
+#define COR_STATUS             (0x0F)
+#define MONITOR_TPS            (0x06)
+#define TPSLOCKED              (0x40)
+#define AGCLOCKED              (0x10)
+
+/* 0x42 OFDM_COR_INTEN */
+#define OFDM_COR_INTEN         (0x42)
+#define TPSRCVBAD              (0x04)
+#define TPSRCVCHANGED         (0x02)
+#define TPSRCVUPDATE           (0x01)
+
+/* 0x43 OFDM_COR_INSTAT */
+#define OFDM_COR_INSTAT        (0x43)
+
+/* 0x44 OFDM_COR_MODEGUARD */
+#define OFDM_COR_MODEGUARD     (0x44)
+#define FORCEMODE              (0x08)
+#define FORCEMODE8K			   (0x04)
+
+/* 0x45 OFDM_AGC_CTL */
+#define OFDM_AGC_CTL           (0x45)
+#define INITIAL_AGC_BW		   (0x08)
+#define AGCNEG                 (0x02)
+#define AGCLAST				   (0x10)
+
+/* 0x48 OFDM_AGC_TARGET */
+#define OFDM_AGC_TARGET		   (0x48)
+#define OFDM_AGC_TARGET_DEFAULT (0x28)
+#define OFDM_AGC_TARGET_IMPULSE (0x38)
+
+/* 0x49 OFDM_AGC_GAIN_1 */
+#define OFDM_AGC_GAIN_1        (0x49)
+
+/* 0x4B OFDM_ITB_CTL */
+#define OFDM_ITB_CTL           (0x4B)
+#define ITBINV                 (0x01)
+
+/* 0x4C OFDM_ITB_FREQ_1 */
+#define OFDM_ITB_FREQ_1        (0x4C)
+
+/* 0x4D OFDM_ITB_FREQ_2 */
+#define OFDM_ITB_FREQ_2        (0x4D)
+
+/* 0x4E  OFDM_CAS_CTL */
+#define OFDM_CAS_CTL           (0x4E)
+#define ACSDIS                 (0x40)
+#define CCSEN                  (0x80)
+
+/* 0x4F CAS_FREQ */
+#define CAS_FREQ               (0x4F)
+
+/* 0x51 OFDM_SYR_CTL */
+#define OFDM_SYR_CTL           (0x51)
+#define SIXTH_ENABLE           (0x80)
+#define SYR_TRACKING_DISABLE   (0x01)
+
+/* 0x52 OFDM_SYR_STAT */
+#define OFDM_SYR_STAT		   (0x52)
+#define GI14_2K_SYR_LOCK	   (0x13)
+#define GI14_8K_SYR_LOCK	   (0x17)
+#define GI14_SYR_LOCK		   (0x10)
+
+/* 0x55 OFDM_SYR_OFFSET_1 */
+#define OFDM_SYR_OFFSET_1      (0x55)
+
+/* 0x56 OFDM_SYR_OFFSET_2 */
+#define OFDM_SYR_OFFSET_2      (0x56)
+
+/* 0x58 OFDM_SCR_CTL */
+#define OFDM_SCR_CTL           (0x58)
+#define SYR_ADJ_DECAY_MASK     (0x70)
+#define SYR_ADJ_DECAY          (0x30)
+
+/* 0x59 OFDM_PPM_CTL_1 */
+#define OFDM_PPM_CTL_1         (0x59)
+#define PPMMAX_MASK            (0x30)
+#define PPM256				   (0x30)
+
+/* 0x5B OFDM_TRL_NOMINALRATE_1 */
+#define OFDM_TRL_NOMINALRATE_1 (0x5B)
+
+/* 0x5C OFDM_TRL_NOMINALRATE_2 */
+#define OFDM_TRL_NOMINALRATE_2 (0x5C)
+
+/* 0x5D OFDM_TRL_TIME_1 */
+#define OFDM_TRL_TIME_1        (0x5D)
+
+/* 0x60 OFDM_CRL_FREQ_1 */
+#define OFDM_CRL_FREQ_1        (0x60)
+
+/* 0x63 OFDM_CHC_CTL_1 */
+#define OFDM_CHC_CTL_1         (0x63)
+#define MANMEAN1               (0xF0);
+#define CHCFIR                 (0x01)
+
+/* 0x64 OFDM_CHC_SNR */
+#define OFDM_CHC_SNR           (0x64)
+
+/* 0x65 OFDM_BDI_CTL */
+#define OFDM_BDI_CTL           (0x65)
+#define LP_SELECT              (0x02)
+
+/* 0x67 OFDM_TPS_RCVD_1 */
+#define OFDM_TPS_RCVD_1        (0x67)
+#define TPSFRAME               (0x03)
+
+/* 0x68 OFDM_TPS_RCVD_2 */
+#define OFDM_TPS_RCVD_2        (0x68)
+
+/* 0x69 OFDM_TPS_RCVD_3 */
+#define OFDM_TPS_RCVD_3        (0x69)
+
+/* 0x6A OFDM_TPS_RCVD_4 */
+#define OFDM_TPS_RCVD_4        (0x6A)
+
+/* 0x6B OFDM_TPS_RESERVED_1 */
+#define OFDM_TPS_RESERVED_1    (0x6B)
+
+/* 0x6C OFDM_TPS_RESERVED_2 */
+#define OFDM_TPS_RESERVED_2    (0x6C)
+
+/* 0x73 OFDM_MSC_REV */
+#define OFDM_MSC_REV           (0x73)
+
+/* 0x76 OFDM_SNR_CARRIER_2 */
+#define OFDM_SNR_CARRIER_2     (0x76)
+#define MEAN_MASK              (0x80)
+#define MEANBIT                (0x80)
+
+/* 0x80 ANALOG_CONTROL_0 */
+#define ANALOG_CONTROL_0       (0x80)
+#define POWER_DOWN_ADC         (0x40)
+
+/* 0x81 ENABLE_TUNER_IIC */
+#define ENABLE_TUNER_IIC       (0x81)
+#define ENABLE_TUNER_BIT       (0x01)
+
+/* 0x82 EN_DMD_RACQ */
+#define EN_DMD_RACQ            (0x82)
+#define EN_DMD_RACQ_REG_VAL    (0x81)
+#define EN_DMD_RACQ_REG_VAL_14 (0x01)
+
+/* 0x84 SNR_COMMAND */
+#define SNR_COMMAND            (0x84)
+#define SNRStat                (0x80)
+
+/* 0x85 SNRCARRIERNUMBER_LSB */
+#define SNRCARRIERNUMBER_LSB   (0x85)
+
+/* 0x87 SNRMINTHRESHOLD_LSB */
+#define SNRMINTHRESHOLD_LSB    (0x87)
+
+/* 0x89 SNR_PER_CARRIER_LSB */
+#define SNR_PER_CARRIER_LSB    (0x89)
+
+/* 0x8B SNRBELOWTHRESHOLD_LSB */
+#define SNRBELOWTHRESHOLD_LSB  (0x8B)
+
+/* 0x91 RF_AGC_VAL_1 */
+#define RF_AGC_VAL_1           (0x91)
+
+/* 0x92 RF_AGC_STATUS */
+#define RF_AGC_STATUS          (0x92)
+
+/* 0x98 DIAG_CONFIG */
+#define DIAG_CONFIG            (0x98)
+#define DIAG_MASK              (0x70)
+#define TB_SET                 (0x10)
+#define TRAN_SELECT            (0x07)
+#define SERIAL_SELECT          (0x01)
+
+/* 0x99 SUB_DIAG_MODE_SEL */
+#define SUB_DIAG_MODE_SEL      (0x99)
+#define CLKINVERSION           (0x01)
+
+/* 0x9A TS_FORMAT */
+#define TS_FORMAT              (0x9A)
+#define ERROR_SENSE            (0x08)
+#define VALID_SENSE            (0x04)
+#define SYNC_SENSE             (0x02)
+#define GATED_CLOCK            (0x01)
+
+#define NXT6000ASICDEVICE      (0x0b)
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
new file mode 100644
index 0000000..df5dee7
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -0,0 +1,628 @@
+/*
+ *    Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ *    Based on code from Jack Kelliher (kelliher@xmission.com)
+ *                           Copyright (C) 2002 & pcHDTV, inc.
+ *
+ *    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.
+ *
+*/
+
+/*
+ * This driver needs two external firmware files. Please copy
+ * "dvb-fe-or51132-vsb.fw" and "dvb-fe-or51132-qam.fw" to
+ * /usr/lib/hotplug/firmware/ or /lib/firmware/
+ * (depending on configuration of firmware hotplug).
+ */
+#define OR51132_VSB_FIRMWARE "dvb-fe-or51132-vsb.fw"
+#define OR51132_QAM_FIRMWARE "dvb-fe-or51132-qam.fw"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+
+#include "dvb_frontend.h"
+#include "dvb-pll.h"
+#include "or51132.h"
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "or51132: " args); \
+	} while (0)
+
+
+struct or51132_state
+{
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+
+	/* Configuration settings */
+	const struct or51132_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* Demodulator private data */
+	fe_modulation_t current_modulation;
+
+	/* Tuner private data */
+	u32 current_frequency;
+};
+
+static int i2c_writebytes (struct or51132_state* state, u8 reg, u8 *buf, int len)
+{
+	int err;
+	struct i2c_msg msg;
+	msg.addr  = reg;
+	msg.flags = 0;
+	msg.len   = len;
+	msg.buf   = buf;
+
+	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		printk(KERN_WARNING "or51132: i2c_writebytes error (addr %02x, err == %i)\n", reg, err);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static u8 i2c_readbytes (struct or51132_state* state, u8 reg, u8* buf, int len)
+{
+	int err;
+	struct i2c_msg msg;
+	msg.addr   = reg;
+	msg.flags = I2C_M_RD;
+	msg.len = len;
+	msg.buf = buf;
+
+	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		printk(KERN_WARNING "or51132: i2c_readbytes error (addr %02x, err == %i)\n", reg, err);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+	struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	static u8 run_buf[] = {0x7F,0x01};
+	static u8 get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
+	u8 rec_buf[14];
+	u8 cmd_buf[14];
+	u32 firmwareAsize, firmwareBsize;
+	int i,ret;
+
+	dprintk("Firmware is %Zd bytes\n",fw->size);
+
+	/* Get size of firmware A and B */
+	firmwareAsize = le32_to_cpu(*((u32*)fw->data));
+	dprintk("FirmwareA is %i bytes\n",firmwareAsize);
+	firmwareBsize = le32_to_cpu(*((u32*)(fw->data+4)));
+	dprintk("FirmwareB is %i bytes\n",firmwareBsize);
+
+	/* Upload firmware */
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 &fw->data[8],firmwareAsize))) {
+		printk(KERN_WARNING "or51132: load_firmware error 1\n");
+		return ret;
+	}
+	msleep(1); /* 1ms */
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 &fw->data[8+firmwareAsize],firmwareBsize))) {
+		printk(KERN_WARNING "or51132: load_firmware error 2\n");
+		return ret;
+	}
+	msleep(1); /* 1ms */
+
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 run_buf,2))) {
+		printk(KERN_WARNING "or51132: load_firmware error 3\n");
+		return ret;
+	}
+
+	/* Wait at least 5 msec */
+	msleep(20); /* 10ms */
+
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 run_buf,2))) {
+		printk(KERN_WARNING "or51132: load_firmware error 4\n");
+		return ret;
+	}
+
+	/* 50ms for operation to begin */
+	msleep(50);
+
+	/* Read back ucode version to besure we loaded correctly and are really up and running */
+	/* Get uCode version */
+	cmd_buf[0] = 0x10;
+	cmd_buf[1] = 0x10;
+	cmd_buf[2] = 0x00;
+	cmd_buf[3] = 0x00;
+	msleep(20); /* 20ms */
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 cmd_buf,3))) {
+		printk(KERN_WARNING "or51132: load_firmware error a\n");
+		return ret;
+	}
+
+	cmd_buf[0] = 0x04;
+	cmd_buf[1] = 0x17;
+	cmd_buf[2] = 0x00;
+	cmd_buf[3] = 0x00;
+	msleep(20); /* 20ms */
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 cmd_buf,2))) {
+		printk(KERN_WARNING "or51132: load_firmware error b\n");
+		return ret;
+	}
+
+	cmd_buf[0] = 0x00;
+	cmd_buf[1] = 0x00;
+	cmd_buf[2] = 0x00;
+	cmd_buf[3] = 0x00;
+	msleep(20); /* 20ms */
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 cmd_buf,2))) {
+		printk(KERN_WARNING "or51132: load_firmware error c\n");
+		return ret;
+	}
+
+	for(i=0;i<4;i++) {
+		msleep(20); /* 20ms */
+		get_ver_buf[4] = i+1;
+		if ((ret = i2c_readbytes(state,state->config->demod_address,
+					&rec_buf[i*2],2))) {
+			printk(KERN_WARNING
+			       "or51132: load_firmware error d - %d\n",i);
+			return ret;
+		}
+	}
+
+	printk(KERN_WARNING
+	       "or51132: Version: %02X%02X%02X%02X-%02X%02X%02X%02X (%02X%01X-%01X-%02X%01X-%01X)\n",
+	       rec_buf[1],rec_buf[0],rec_buf[3],rec_buf[2],
+	       rec_buf[5],rec_buf[4],rec_buf[7],rec_buf[6],
+	       rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f,
+	       rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f);
+
+	cmd_buf[0] = 0x10;
+	cmd_buf[1] = 0x00;
+	cmd_buf[2] = 0x00;
+	cmd_buf[3] = 0x00;
+	msleep(20); /* 20ms */
+	if ((ret = i2c_writebytes(state,state->config->demod_address,
+				 cmd_buf,3))) {
+		printk(KERN_WARNING "or51132: load_firmware error e\n");
+		return ret;
+	}
+	return 0;
+};
+
+static int or51132_init(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int or51132_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	*ber = 0;
+	return 0;
+}
+
+static int or51132_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	*ucblocks = 0;
+	return 0;
+}
+
+static int or51132_sleep(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int or51132_setmode(struct dvb_frontend* fe)
+{
+	struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	unsigned char cmd_buf[4];
+
+	dprintk("setmode %d\n",(int)state->current_modulation);
+	/* set operation mode in Receiver 1 register; */
+	cmd_buf[0] = 0x04;
+	cmd_buf[1] = 0x01;
+	switch (state->current_modulation) {
+	case QAM_256:
+	case QAM_64:
+	case QAM_AUTO:
+		/* Auto-deinterleave; MPEG ser, MPEG2tr, phase noise-high*/
+		cmd_buf[2] = 0x5F;
+		break;
+	case VSB_8:
+		/* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high*/
+		cmd_buf[2] = 0x50;
+		break;
+	default:
+		printk("setmode:Modulation set to unsupported value\n");
+	};
+	cmd_buf[3] = 0x00;
+	if (i2c_writebytes(state,state->config->demod_address,
+			   cmd_buf,3)) {
+		printk(KERN_WARNING "or51132: set_mode error 1\n");
+		return -1;
+	}
+	dprintk("or51132: set #1 to %02x\n", cmd_buf[2]);
+
+	/* Set operation mode in Receiver 6 register */
+	cmd_buf[0] = 0x1C;
+	switch (state->current_modulation) {
+	case QAM_AUTO:
+		/* REC MODE Normal Carrier Lock */
+		cmd_buf[1] = 0x00;
+		/* Channel MODE Auto QAM64/256 */
+		cmd_buf[2] = 0x4f;
+		break;
+	case QAM_256:
+		/* REC MODE Normal Carrier Lock */
+		cmd_buf[1] = 0x00;
+		/* Channel MODE QAM256 */
+		cmd_buf[2] = 0x45;
+		break;
+	case QAM_64:
+		/* REC MODE Normal Carrier Lock */
+		cmd_buf[1] = 0x00;
+		/* Channel MODE QAM64 */
+		cmd_buf[2] = 0x43;
+		break;
+	case VSB_8:
+		 /* REC MODE inv IF spectrum, Normal */
+		cmd_buf[1] = 0x03;
+		/* Channel MODE ATSC/VSB8 */
+		cmd_buf[2] = 0x06;
+		break;
+	default:
+		printk("setmode: Modulation set to unsupported value\n");
+	};
+	cmd_buf[3] = 0x00;
+	msleep(20); /* 20ms */
+	if (i2c_writebytes(state,state->config->demod_address,
+			   cmd_buf,3)) {
+		printk(KERN_WARNING "or51132: set_mode error 2\n");
+		return -1;
+	}
+	dprintk("or51132: set #6 to 0x%02x%02x\n", cmd_buf[1], cmd_buf[2]);
+
+	return 0;
+}
+
+static int or51132_set_parameters(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *param)
+{
+	int ret;
+	u8 buf[4];
+	struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	const struct firmware *fw;
+
+	/* Change only if we are actually changing the modulation */
+	if (state->current_modulation != param->u.vsb.modulation) {
+		switch(param->u.vsb.modulation) {
+		case VSB_8:
+			dprintk("set_parameters VSB MODE\n");
+			printk("or51132: Waiting for firmware upload(%s)...\n",
+			       OR51132_VSB_FIRMWARE);
+			ret = request_firmware(&fw, OR51132_VSB_FIRMWARE,
+					       &state->i2c->dev);
+			if (ret){
+				printk(KERN_WARNING "or51132: No firmware up"
+				       "loaded(timeout or file not found?)\n");
+				return ret;
+			}
+			/* Set non-punctured clock for VSB */
+			state->config->set_ts_params(fe, 0);
+			break;
+		case QAM_AUTO:
+		case QAM_64:
+		case QAM_256:
+			dprintk("set_parameters QAM MODE\n");
+			printk("or51132: Waiting for firmware upload(%s)...\n",
+			       OR51132_QAM_FIRMWARE);
+			ret = request_firmware(&fw, OR51132_QAM_FIRMWARE,
+					       &state->i2c->dev);
+			if (ret){
+				printk(KERN_WARNING "or51132: No firmware up"
+				       "loaded(timeout or file not found?)\n");
+				return ret;
+			}
+			/* Set punctured clock for QAM */
+			state->config->set_ts_params(fe, 1);
+			break;
+		default:
+			printk("or51132:Modulation type(%d) UNSUPPORTED\n",
+			       param->u.vsb.modulation);
+			return -1;
+		};
+		ret = or51132_load_firmware(fe, fw);
+		release_firmware(fw);
+		if (ret) {
+			printk(KERN_WARNING "or51132: Writing firmware to "
+			       "device failed!\n");
+			return ret;
+		}
+		printk("or51132: Firmware upload complete.\n");
+
+		state->current_modulation = param->u.vsb.modulation;
+		or51132_setmode(fe);
+	}
+
+	/* Change only if we are actually changing the channel */
+	if (state->current_frequency != param->frequency) {
+		dvb_pll_configure(state->config->pll_desc, buf,
+				  param->frequency, 0);
+		dprintk("set_parameters tuner bytes: 0x%02x 0x%02x "
+			"0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]);
+		if (i2c_writebytes(state, state->config->pll_address ,buf, 4))
+			printk(KERN_WARNING "or51132: set_parameters error "
+			       "writing to tuner\n");
+
+		/* Set to current mode */
+		or51132_setmode(fe);
+
+		/* Update current frequency */
+		state->current_frequency = param->frequency;
+	}
+	return 0;
+}
+
+static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	unsigned char rec_buf[2];
+	unsigned char snd_buf[2];
+	*status = 0;
+
+	/* 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_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_status read error\n");
+		return -1;
+	}
+	dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
+
+	if (rec_buf[1] & 0x01) { /* Receiver Lock */
+		*status |= FE_HAS_SIGNAL;
+		*status |= FE_HAS_CARRIER;
+		*status |= FE_HAS_VITERBI;
+		*status |= FE_HAS_SYNC;
+		*status |= FE_HAS_LOCK;
+	}
+	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,
+};
+
+static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+
+static unsigned int i20Log10(unsigned short val)
+{
+	unsigned int rntval = 100;
+	unsigned int tmp = val;
+	unsigned int exp = 1;
+
+	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 = (struct or51132_state*) fe->demodulator_priv;
+	unsigned char rec_buf[2];
+	unsigned char snd_buf[2];
+	u8 rcvr_stat;
+	u16 snr_equ;
+	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;
+	}
+	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% */
+	*strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000;
+	dprintk("read_signal_strength %i\n",*strength);
+
+	return 0;
+}
+
+static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	unsigned char rec_buf[2];
+	unsigned char snd_buf[2];
+	u16 snr_equ;
+
+	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;
+	}
+	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;
+	}
+	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);
+
+	*snr = 0xFFFF - snr_equ;
+	dprintk("read_snr %i\n",*snr);
+
+	return 0;
+}
+
+static int or51132_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+	fe_tune_settings->min_delay_ms = 500;
+	fe_tune_settings->step_size = 0;
+	fe_tune_settings->max_drift = 0;
+
+	return 0;
+}
+
+static void or51132_release(struct dvb_frontend* fe)
+{
+	struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops or51132_ops;
+
+struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct or51132_state* state = NULL;
+
+	/* Allocate memory for the internal state */
+	state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* Setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &or51132_ops, sizeof(struct dvb_frontend_ops));
+	state->current_frequency = -1;
+	state->current_modulation = -1;
+
+	/* Create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state)
+		kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops or51132_ops = {
+
+	.info = {
+		.name			= "Oren OR51132 VSB/QAM Frontend",
+		.type 			= FE_ATSC,
+		.frequency_min		= 44000000,
+		.frequency_max		= 958000000,
+		.frequency_stepsize	= 166666,
+		.caps = 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_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO |
+			FE_CAN_8VSB
+	},
+
+	.release = or51132_release,
+
+	.init = or51132_init,
+	.sleep = or51132_sleep,
+
+	.set_frontend = or51132_set_parameters,
+	.get_tune_settings = or51132_get_tune_settings,
+
+	.read_status = or51132_read_status,
+	.read_ber = or51132_read_ber,
+	.read_signal_strength = or51132_read_signal_strength,
+	.read_snr = or51132_read_snr,
+	.read_ucblocks = or51132_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(or51132_attach);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb/frontends/or51132.h
new file mode 100644
index 0000000..622cdd1
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51132.h
@@ -0,0 +1,48 @@
+/*
+ *    Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+*/
+
+#ifndef OR51132_H
+#define OR51132_H
+
+#include <linux/firmware.h>
+#include <linux/dvb/frontend.h>
+
+struct or51132_config
+{
+	/* The demodulator's i2c address */
+	u8 demod_address;
+	u8 pll_address;
+	struct dvb_pll_desc *pll_desc;
+
+	/* Need to set device param for start_dma */
+	int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+};
+
+extern struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // OR51132_H
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
new file mode 100644
index 0000000..ad56a99
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -0,0 +1,631 @@
+/*
+ *    Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ *    Based on code from Jack Kelliher (kelliher@xmission.com)
+ *                           Copyright (C) 2002 & pcHDTV, inc.
+ *
+ *    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.
+ *
+*/
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <asm/byteorder.h>
+
+#include "dvb_frontend.h"
+#include "or51211.h"
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "or51211: " args); \
+	} while (0)
+
+static u8 run_buf[] = {0x7f,0x01};
+static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC
+
+struct or51211_state {
+
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+
+	/* Configuration settings */
+	const struct or51211_config* config;
+
+	struct dvb_frontend frontend;
+	struct bt878* bt;
+
+	/* Demodulator private data */
+	u8 initialized:1;
+
+	/* Tuner private data */
+	u32 current_frequency;
+};
+
+static int i2c_writebytes (struct or51211_state* state, u8 reg, u8 *buf,
+			   int len)
+{
+	int err;
+	struct i2c_msg msg;
+	msg.addr	= reg;
+	msg.flags	= 0;
+	msg.len		= len;
+	msg.buf		= buf;
+
+	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+		printk(KERN_WARNING "or51211: i2c_writebytes error "
+		       "(addr %02x, err == %i)\n", reg, err);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static u8 i2c_readbytes (struct or51211_state* state, u8 reg, u8* buf, int len)
+{
+	int err;
+	struct i2c_msg msg;
+	msg.addr	= reg;
+	msg.flags	= I2C_M_RD;
+	msg.len		= len;
+	msg.buf		= buf;
+
+	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+		printk(KERN_WARNING "or51211: i2c_readbytes error "
+		       "(addr %02x, err == %i)\n", reg, err);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int or51211_load_firmware (struct dvb_frontend* fe,
+				  const struct firmware *fw)
+{
+	struct or51211_state* state = fe->demodulator_priv;
+	u8 tudata[585];
+	int i;
+
+	dprintk("Firmware is %d bytes\n",fw->size);
+
+	/* Get eprom data */
+	tudata[0] = 17;
+	if (i2c_writebytes(state,0x50,tudata,1)) {
+		printk(KERN_WARNING "or51211:load_firmware error eprom addr\n");
+		return -1;
+	}
+	if (i2c_readbytes(state,0x50,&tudata[145],192)) {
+		printk(KERN_WARNING "or51211: load_firmware error eprom\n");
+		return -1;
+	}
+
+	/* Create firmware buffer */
+	for (i = 0; i < 145; i++)
+		tudata[i] = fw->data[i];
+
+	for (i = 0; i < 248; i++)
+		tudata[i+337] = fw->data[145+i];
+
+	state->config->reset(fe);
+
+	if (i2c_writebytes(state,state->config->demod_address,tudata,585)) {
+		printk(KERN_WARNING "or51211: load_firmware error 1\n");
+		return -1;
+	}
+	msleep(1);
+
+	if (i2c_writebytes(state,state->config->demod_address,
+			   &fw->data[393],8125)) {
+		printk(KERN_WARNING "or51211: load_firmware error 2\n");
+		return -1;
+	}
+	msleep(1);
+
+	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+		printk(KERN_WARNING "or51211: load_firmware error 3\n");
+		return -1;
+	}
+
+	/* Wait at least 5 msec */
+	msleep(10);
+	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+		printk(KERN_WARNING "or51211: load_firmware error 4\n");
+		return -1;
+	}
+	msleep(10);
+
+	printk("or51211: Done.\n");
+	return 0;
+};
+
+static int or51211_setmode(struct dvb_frontend* fe, int mode)
+{
+	struct or51211_state* state = fe->demodulator_priv;
+	u8 rec_buf[14];
+
+	state->config->setmode(fe, mode);
+
+	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+		printk(KERN_WARNING "or51211: setmode error 1\n");
+		return -1;
+	}
+
+	/* Wait at least 5 msec */
+	msleep(10);
+	if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+		printk(KERN_WARNING "or51211: setmode error 2\n");
+		return -1;
+	}
+
+	msleep(10);
+
+	/* Set operation mode in Receiver 1 register;
+	 * type 1:
+	 * data 0x50h  Automatic sets receiver channel conditions
+	 *             Automatic NTSC rejection filter
+	 *             Enable  MPEG serial data output
+	 *             MPEG2tr
+	 *             High tuner phase noise
+	 *             normal +/-150kHz Carrier acquisition range
+	 */
+	if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) {
+		printk(KERN_WARNING "or51211: setmode error 3\n");
+		return -1;
+	}
+
+	rec_buf[0] = 0x04;
+	rec_buf[1] = 0x00;
+	rec_buf[2] = 0x03;
+	rec_buf[3] = 0x00;
+	msleep(20);
+	if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) {
+		printk(KERN_WARNING "or51211: setmode error 5\n");
+	}
+	msleep(3);
+	if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) {
+		printk(KERN_WARNING "or51211: setmode error 6");
+		return -1;
+	}
+	dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]);
+
+	return 0;
+}
+
+static int or51211_set_parameters(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *param)
+{
+	struct or51211_state* state = fe->demodulator_priv;
+	u32 freq = 0;
+	u16 tunerfreq = 0;
+	u8 buf[4];
+
+	/* Change only if we are actually changing the channel */
+	if (state->current_frequency != param->frequency) {
+		freq = 44000 + (param->frequency/1000);
+		tunerfreq = freq * 16/1000;
+
+		dprintk("set_parameters frequency = %d (tunerfreq = %d)\n",
+			param->frequency,tunerfreq);
+
+		buf[0] = (tunerfreq >> 8) & 0x7F;
+		buf[1] = (tunerfreq & 0xFF);
+		buf[2] = 0x8E;
+
+		if (param->frequency < 157250000) {
+			buf[3] = 0xA0;
+			dprintk("set_parameters VHF low range\n");
+		} else if (param->frequency < 454000000) {
+			buf[3] = 0x90;
+			dprintk("set_parameters VHF high range\n");
+		} else {
+			buf[3] = 0x30;
+			dprintk("set_parameters UHF range\n");
+		}
+		dprintk("set_parameters tuner bytes: 0x%02x 0x%02x "
+			"0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]);
+
+		if (i2c_writebytes(state,0xC2>>1,buf,4))
+			printk(KERN_WARNING "or51211:set_parameters error "
+			       "writing to tuner\n");
+
+		/* Set to ATSC mode */
+		or51211_setmode(fe,0);
+
+		/* Update current frequency */
+		state->current_frequency = param->frequency;
+	}
+	return 0;
+}
+
+static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct or51211_state* state = fe->demodulator_priv;
+	unsigned char rec_buf[2];
+	unsigned char snd_buf[] = {0x04,0x00,0x03,0x00};
+	*status = 0;
+
+	/* Receiver Status */
+	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
+		printk(KERN_WARNING "or51132: read_status write error\n");
+		return -1;
+	}
+	msleep(3);
+	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+		printk(KERN_WARNING "or51132: read_status read error\n");
+		return -1;
+	}
+	dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
+
+	if (rec_buf[0] &  0x01) { /* Receiver Lock */
+		*status |= FE_HAS_SIGNAL;
+		*status |= FE_HAS_CARRIER;
+		*status |= FE_HAS_VITERBI;
+		*status |= FE_HAS_SYNC;
+		*status |= FE_HAS_LOCK;
+	}
+	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,
+};
+
+static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+
+static unsigned int i20Log10(unsigned short val)
+{
+	unsigned int rntval = 100;
+	unsigned int tmp = val;
+	unsigned int exp = 1;
+
+	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;
+
+	/* 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;
+	}
+	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% */
+	*strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000;
+
+	dprintk("read_signal_strength %i\n",*strength);
+
+	return 0;
+}
+
+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];
+
+	/* 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;
+	}
+	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 = rec_buf[0] & 0xff;
+
+	dprintk("read_snr %i\n",*snr);
+
+	return 0;
+}
+
+static int or51211_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	*ber = -ENOSYS;
+	return 0;
+}
+
+static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	*ucblocks = -ENOSYS;
+	return 0;
+}
+
+static int or51211_sleep(struct dvb_frontend* fe)
+{
+	return 0;
+}
+
+static int or51211_init(struct dvb_frontend* fe)
+{
+	struct or51211_state* state = fe->demodulator_priv;
+	const struct or51211_config* config = state->config;
+	const struct firmware* fw;
+	unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
+	unsigned char rec_buf[14];
+	int ret,i;
+
+	if (!state->initialized) {
+		/* Request the firmware, this will block until it uploads */
+		printk(KERN_INFO "or51211: Waiting for firmware upload "
+		       "(%s)...\n", OR51211_DEFAULT_FIRMWARE);
+		ret = config->request_firmware(fe, &fw,
+					       OR51211_DEFAULT_FIRMWARE);
+		printk(KERN_INFO "or51211:Got Hotplug firmware\n");
+		if (ret) {
+			printk(KERN_WARNING "or51211: No firmware uploaded "
+			       "(timeout or file not found?)\n");
+			return ret;
+		}
+
+		ret = or51211_load_firmware(fe, fw);
+		if (ret) {
+			printk(KERN_WARNING "or51211: Writing firmware to "
+			       "device failed!\n");
+			release_firmware(fw);
+			return ret;
+		}
+		printk(KERN_INFO "or51211: Firmware upload complete.\n");
+
+		/* Set operation mode in Receiver 1 register;
+		 * type 1:
+		 * data 0x50h  Automatic sets receiver channel conditions
+		 *             Automatic NTSC rejection filter
+		 *             Enable  MPEG serial data output
+		 *             MPEG2tr
+		 *             High tuner phase noise
+		 *             normal +/-150kHz Carrier acquisition range
+		 */
+		if (i2c_writebytes(state,state->config->demod_address,
+				   cmd_buf,3)) {
+			printk(KERN_WARNING "or51211: Load DVR Error 5\n");
+			return -1;
+		}
+
+		/* Read back ucode version to besure we loaded correctly */
+		/* and are really up and running */
+		rec_buf[0] = 0x04;
+		rec_buf[1] = 0x00;
+		rec_buf[2] = 0x03;
+		rec_buf[3] = 0x00;
+		msleep(30);
+		if (i2c_writebytes(state,state->config->demod_address,
+				   rec_buf,3)) {
+			printk(KERN_WARNING "or51211: Load DVR Error A\n");
+			return -1;
+		}
+		msleep(3);
+		if (i2c_readbytes(state,state->config->demod_address,
+				  &rec_buf[10],2)) {
+			printk(KERN_WARNING "or51211: Load DVR Error B\n");
+			return -1;
+		}
+
+		rec_buf[0] = 0x04;
+		rec_buf[1] = 0x00;
+		rec_buf[2] = 0x01;
+		rec_buf[3] = 0x00;
+		msleep(20);
+		if (i2c_writebytes(state,state->config->demod_address,
+				   rec_buf,3)) {
+			printk(KERN_WARNING "or51211: Load DVR Error C\n");
+			return -1;
+		}
+		msleep(3);
+		if (i2c_readbytes(state,state->config->demod_address,
+				  &rec_buf[12],2)) {
+			printk(KERN_WARNING "or51211: Load DVR Error D\n");
+			return -1;
+		}
+
+		for (i = 0; i < 8; i++)
+			rec_buf[i]=0xed;
+
+		for (i = 0; i < 5; i++) {
+			msleep(30);
+			get_ver_buf[4] = i+1;
+			if (i2c_writebytes(state,state->config->demod_address,
+					   get_ver_buf,5)) {
+				printk(KERN_WARNING "or51211:Load DVR Error 6"
+				       " - %d\n",i);
+				return -1;
+			}
+			msleep(3);
+
+			if (i2c_readbytes(state,state->config->demod_address,
+					  &rec_buf[i*2],2)) {
+				printk(KERN_WARNING "or51211:Load DVR Error 7"
+				       " - %d\n",i);
+				return -1;
+			}
+			/* If we didn't receive the right index, try again */
+			if ((int)rec_buf[i*2+1]!=i+1){
+			  i--;
+			}
+		}
+		dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n",
+			rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3],
+			rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7],
+			rec_buf[8], rec_buf[9]);
+
+		printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x"
+		       " Status %02x\n",
+		       rec_buf[2], rec_buf[4],rec_buf[6],
+		       rec_buf[12],rec_buf[10]);
+
+		rec_buf[0] = 0x04;
+		rec_buf[1] = 0x00;
+		rec_buf[2] = 0x03;
+		rec_buf[3] = 0x00;
+		msleep(20);
+		if (i2c_writebytes(state,state->config->demod_address,
+				   rec_buf,3)) {
+			printk(KERN_WARNING "or51211: Load DVR Error 8\n");
+			return -1;
+		}
+		msleep(20);
+		if (i2c_readbytes(state,state->config->demod_address,
+				  &rec_buf[8],2)) {
+			printk(KERN_WARNING "or51211: Load DVR Error 9\n");
+			return -1;
+		}
+		state->initialized = 1;
+	}
+
+	return 0;
+}
+
+static int or51211_get_tune_settings(struct dvb_frontend* fe,
+				     struct dvb_frontend_tune_settings* fesettings)
+{
+	fesettings->min_delay_ms = 500;
+	fesettings->step_size = 0;
+	fesettings->max_drift = 0;
+	return 0;
+}
+
+static void or51211_release(struct dvb_frontend* fe)
+{
+	struct or51211_state* state = fe->demodulator_priv;
+	state->config->sleep(fe);
+	kfree(state);
+}
+
+static struct dvb_frontend_ops or51211_ops;
+
+struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct or51211_state* state = NULL;
+
+	/* Allocate memory for the internal state */
+	state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* Setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
+	state->initialized = 0;
+	state->current_frequency = 0;
+
+	/* Create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops or51211_ops = {
+
+	.info = {
+		.name               = "Oren OR51211 VSB Frontend",
+		.type               = FE_ATSC,
+		.frequency_min      = 44000000,
+		.frequency_max      = 958000000,
+		.frequency_stepsize = 166666,
+		.caps = 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_8VSB
+	},
+
+	.release = or51211_release,
+
+	.init = or51211_init,
+	.sleep = or51211_sleep,
+
+	.set_frontend = or51211_set_parameters,
+	.get_tune_settings = or51211_get_tune_settings,
+
+	.read_status = or51211_read_status,
+	.read_ber = or51211_read_ber,
+	.read_signal_strength = or51211_read_signal_strength,
+	.read_snr = or51211_read_snr,
+	.read_ucblocks = or51211_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(or51211_attach);
+
diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb/frontends/or51211.h
new file mode 100644
index 0000000..13a5a3a
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51211.h
@@ -0,0 +1,44 @@
+/*
+ *    Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ *    Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+*/
+
+#ifndef OR51211_H
+#define OR51211_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct or51211_config
+{
+	/* The demodulator's i2c address */
+	u8 demod_address;
+
+	/* Request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+	void (*setmode)(struct dvb_frontend * fe, int mode);
+	void (*reset)(struct dvb_frontend * fe);
+	void (*sleep)(struct dvb_frontend * fe);
+};
+
+extern struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // OR51211_H
+
diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c
new file mode 100644
index 0000000..58ad34e
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp8870.c
@@ -0,0 +1,614 @@
+/*
+    Driver for Spase SP8870 demodulator
+
+    Copyright (C) 1999 Juergen Peitz
+
+    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.
+
+*/
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "sp8870.h"
+
+
+struct sp8870_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct sp8870_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* demodulator private data */
+	u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "sp8870: " args); \
+	} while (0)
+
+/* firmware size for sp8870 */
+#define SP8870_FIRMWARE_SIZE 16382
+
+/* starting point for firmware in file 'Sc_main.mc' */
+#define SP8870_FIRMWARE_OFFSET 0x0A
+
+static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data)
+{
+        u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 };
+	int err;
+
+        if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+		dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+        return 0;
+}
+
+static int sp8870_readreg (struct sp8870_state* state, u16 reg)
+{
+	int ret;
+	u8 b0 [] = { reg >> 8 , reg & 0xff };
+	u8 b1 [] = { 0, 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+
+	if (ret != 2) {
+		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+		return -1;
+	}
+
+	return (b1[0] << 8 | b1[1]);
+}
+
+static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw)
+{
+	struct i2c_msg msg;
+	char *fw_buf = fw->data;
+	int fw_pos;
+	u8 tx_buf[255];
+	int tx_len;
+	int err = 0;
+
+	dprintk ("%s: ...\n", __FUNCTION__);
+
+	if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET)
+		return -EINVAL;
+
+	// system controller stop
+	sp8870_writereg(state, 0x0F00, 0x0000);
+
+	// instruction RAM register hiword
+	sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF));
+
+	// instruction RAM MWR
+	sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16));
+
+	// do firmware upload
+	fw_pos = SP8870_FIRMWARE_OFFSET;
+	while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){
+		tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos;
+		// write register 0xCF0A
+		tx_buf[0] = 0xCF;
+		tx_buf[1] = 0x0A;
+		memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len);
+		msg.addr = state->config->demod_address;
+		msg.flags = 0;
+		msg.buf = tx_buf;
+		msg.len = tx_len + 2;
+		if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+			printk("%s: firmware upload failed!\n", __FUNCTION__);
+			printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
+			return err;
+		}
+		fw_pos += tx_len;
+	}
+
+	dprintk ("%s: done!\n", __FUNCTION__);
+	return 0;
+};
+
+static void sp8870_microcontroller_stop (struct sp8870_state* state)
+{
+	sp8870_writereg(state, 0x0F08, 0x000);
+	sp8870_writereg(state, 0x0F09, 0x000);
+
+	// microcontroller STOP
+	sp8870_writereg(state, 0x0F00, 0x000);
+}
+
+static void sp8870_microcontroller_start (struct sp8870_state* state)
+{
+	sp8870_writereg(state, 0x0F08, 0x000);
+	sp8870_writereg(state, 0x0F09, 0x000);
+
+	// microcontroller START
+	sp8870_writereg(state, 0x0F00, 0x001);
+	// not documented but if we don't read 0x0D01 out here
+	// we don't get a correct data valid signal
+	sp8870_readreg(state, 0x0D01);
+}
+
+static int sp8870_read_data_valid_signal(struct sp8870_state* state)
+{
+	return (sp8870_readreg(state, 0x0D02) > 0);
+}
+
+static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
+{
+	int known_parameters = 1;
+
+	*reg0xc05 = 0x000;
+
+	switch (p->u.ofdm.constellation) {
+	case QPSK:
+		break;
+	case QAM_16:
+		*reg0xc05 |= (1 << 10);
+		break;
+	case QAM_64:
+		*reg0xc05 |= (2 << 10);
+		break;
+	case QAM_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (p->u.ofdm.hierarchy_information) {
+	case HIERARCHY_NONE:
+		break;
+	case HIERARCHY_1:
+		*reg0xc05 |= (1 << 7);
+		break;
+	case HIERARCHY_2:
+		*reg0xc05 |= (2 << 7);
+		break;
+	case HIERARCHY_4:
+		*reg0xc05 |= (3 << 7);
+		break;
+	case HIERARCHY_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (p->u.ofdm.code_rate_HP) {
+	case FEC_1_2:
+		break;
+	case FEC_2_3:
+		*reg0xc05 |= (1 << 3);
+		break;
+	case FEC_3_4:
+		*reg0xc05 |= (2 << 3);
+		break;
+	case FEC_5_6:
+		*reg0xc05 |= (3 << 3);
+		break;
+	case FEC_7_8:
+		*reg0xc05 |= (4 << 3);
+		break;
+	case FEC_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	if (known_parameters)
+		*reg0xc05 |= (2 << 1);	/* use specified parameters */
+	else
+		*reg0xc05 |= (1 << 1);	/* enable autoprobing */
+
+	return 0;
+}
+
+static int sp8870_wake_up(struct sp8870_state* state)
+{
+	// enable TS output and interface pins
+	return sp8870_writereg(state, 0xC18, 0x00D);
+}
+
+static int sp8870_set_frontend_parameters (struct dvb_frontend* fe,
+					   struct dvb_frontend_parameters *p)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int  err;
+	u16 reg0xc05;
+
+	if ((err = configure_reg0xc05(p, &reg0xc05)))
+		return err;
+
+	// system controller stop
+	sp8870_microcontroller_stop(state);
+
+	// set tuner parameters
+	sp8870_writereg(state, 0x206, 0x001);
+	state->config->pll_set(fe, p);
+	sp8870_writereg(state, 0x206, 0x000);
+
+	// sample rate correction bit [23..17]
+	sp8870_writereg(state, 0x0319, 0x000A);
+
+	// sample rate correction bit [16..0]
+	sp8870_writereg(state, 0x031A, 0x0AAB);
+
+	// integer carrier offset
+	sp8870_writereg(state, 0x0309, 0x0400);
+
+	// fractional carrier offset
+	sp8870_writereg(state, 0x030A, 0x0000);
+
+	// filter for 6/7/8 Mhz channel
+	if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+		sp8870_writereg(state, 0x0311, 0x0002);
+	else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+		sp8870_writereg(state, 0x0311, 0x0001);
+	else
+		sp8870_writereg(state, 0x0311, 0x0000);
+
+	// scan order: 2k first = 0x0000, 8k first = 0x0001
+	if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
+		sp8870_writereg(state, 0x0338, 0x0000);
+	else
+		sp8870_writereg(state, 0x0338, 0x0001);
+
+	sp8870_writereg(state, 0xc05, reg0xc05);
+
+	// read status reg in order to clear pending irqs
+	sp8870_readreg(state, 0x200);
+
+	// system controller start
+	sp8870_microcontroller_start(state);
+
+	return 0;
+}
+
+static int sp8870_init (struct dvb_frontend* fe)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+        const struct firmware *fw = NULL;
+
+	sp8870_wake_up(state);
+	if (state->initialised) return 0;
+	state->initialised = 1;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE);
+	if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) {
+		printk("sp8870: no firmware upload (timeout or file not found?)\n");
+		release_firmware(fw);
+		return -EIO;
+	}
+
+	if (sp8870_firmware_upload(state, fw)) {
+		printk("sp8870: writing firmware to device failed\n");
+		release_firmware(fw);
+		return -EIO;
+	}
+	printk("sp8870: firmware upload complete\n");
+
+	/* enable TS output and interface pins */
+	sp8870_writereg(state, 0xc18, 0x00d);
+
+	// system controller stop
+	sp8870_microcontroller_stop(state);
+
+	// ADC mode
+	sp8870_writereg(state, 0x0301, 0x0003);
+
+	// Reed Solomon parity bytes passed to output
+	sp8870_writereg(state, 0x0C13, 0x0001);
+
+	// MPEG clock is suppressed if no valid data
+	sp8870_writereg(state, 0x0C14, 0x0001);
+
+	/* bit 0x010: enable data valid signal */
+	sp8870_writereg(state, 0x0D00, 0x010);
+	sp8870_writereg(state, 0x0D01, 0x000);
+
+	/* setup PLL */
+	if (state->config->pll_init) {
+		sp8870_writereg(state, 0x206, 0x001);
+		state->config->pll_init(fe);
+		sp8870_writereg(state, 0x206, 0x000);
+	}
+
+	return 0;
+}
+
+static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int status;
+	int signal;
+
+	*fe_status = 0;
+
+	status = sp8870_readreg (state, 0x0200);
+	if (status < 0)
+		return -EIO;
+
+	signal = sp8870_readreg (state, 0x0303);
+	if (signal < 0)
+		return -EIO;
+
+	if (signal > 0x0F)
+		*fe_status |= FE_HAS_SIGNAL;
+	if (status & 0x08)
+		*fe_status |= FE_HAS_SYNC;
+	if (status & 0x04)
+		*fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI;
+
+	return 0;
+}
+
+static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int ret;
+	u32 tmp;
+
+	*ber = 0;
+
+	ret = sp8870_readreg(state, 0xC08);
+	if (ret < 0)
+		return -EIO;
+
+	tmp = ret & 0x3F;
+
+	ret = sp8870_readreg(state, 0xC07);
+	if (ret < 0)
+		return -EIO;
+
+	 tmp = ret << 6;
+
+	if (tmp >= 0x3FFF0)
+		tmp = ~0;
+
+	*ber = tmp;
+
+	return 0;
+}
+
+static int sp8870_read_signal_strength(struct dvb_frontend* fe,  u16 * signal)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int ret;
+	u16 tmp;
+
+	*signal = 0;
+
+	ret = sp8870_readreg (state, 0x306);
+	if (ret < 0)
+		return -EIO;
+
+	tmp = ret << 8;
+
+	ret = sp8870_readreg (state, 0x303);
+	if (ret < 0)
+		return -EIO;
+
+	tmp |= ret;
+
+	if (tmp)
+		*signal = 0xFFFF - tmp;
+
+	return 0;
+}
+
+static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int ret;
+
+	*ublocks = 0;
+
+	ret = sp8870_readreg(state, 0xC0C);
+	if (ret < 0)
+		return -EIO;
+
+	if (ret == 0xFFFF)
+		ret = ~0;
+
+	*ublocks = ret;
+
+	return 0;
+}
+
+// number of trials to recover from lockup
+#define MAXTRIALS 5
+// maximum checks for data valid signal
+#define MAXCHECKS 100
+
+// only for debugging: counter for detected lockups
+static int lockups = 0;
+// only for debugging: counter for channel switches
+static int switches = 0;
+
+static int sp8870_set_frontend (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+	/*
+	    The firmware of the sp8870 sometimes locks up after setting frontend parameters.
+	    We try to detect this by checking the data valid signal.
+	    If it is not set after MAXCHECKS we try to recover the lockup by setting
+	    the frontend parameters again.
+	*/
+
+	int err = 0;
+	int valid = 0;
+	int trials = 0;
+	int check_count = 0;
+
+	dprintk("%s: frequency = %i\n", __FUNCTION__, p->frequency);
+
+	for (trials = 1; trials <= MAXTRIALS; trials++) {
+
+		if ((err = sp8870_set_frontend_parameters(fe, p)))
+			return err;
+
+		for (check_count = 0; check_count < MAXCHECKS; check_count++) {
+//			valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0);
+			valid = sp8870_read_data_valid_signal(state);
+			if (valid) {
+				dprintk("%s: delay = %i usec\n",
+					__FUNCTION__, check_count * 10);
+				break;
+			}
+			udelay(10);
+		}
+		if (valid)
+			break;
+	}
+
+	if (!valid) {
+		printk("%s: firmware crash!!!!!!\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	if (debug) {
+		if (valid) {
+			if (trials > 1) {
+				printk("%s: firmware lockup!!!\n", __FUNCTION__);
+				printk("%s: recovered after %i trial(s))\n",  __FUNCTION__, trials - 1);
+				lockups++;
+			}
+		}
+		switches++;
+		printk("%s: switches = %i lockups = %i\n", __FUNCTION__, switches, lockups);
+	}
+
+	return 0;
+}
+
+static int sp8870_sleep(struct dvb_frontend* fe)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+	// tristate TS output and disable interface pins
+	return sp8870_writereg(state, 0xC18, 0x000);
+}
+
+static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 350;
+        fesettings->step_size = 0;
+        fesettings->max_drift = 0;
+        return 0;
+}
+
+static void sp8870_release(struct dvb_frontend* fe)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops sp8870_ops;
+
+struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+				   struct i2c_adapter* i2c)
+{
+	struct sp8870_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct sp8870_state*) kmalloc(sizeof(struct sp8870_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &sp8870_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+
+	/* check if the demod is there */
+	if (sp8870_readreg(state, 0x0200) < 0) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops sp8870_ops = {
+
+	.info = {
+		.name			= "Spase SP8870 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 470000000,
+		.frequency_max		= 860000000,
+		.frequency_stepsize	= 166666,
+		.caps			= 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_HIERARCHY_AUTO |  FE_CAN_RECOVER
+	},
+
+	.release = sp8870_release,
+
+	.init = sp8870_init,
+	.sleep = sp8870_sleep,
+
+	.set_frontend = sp8870_set_frontend,
+	.get_tune_settings = sp8870_get_tune_settings,
+
+	.read_status = sp8870_read_status,
+	.read_ber = sp8870_read_ber,
+	.read_signal_strength = sp8870_read_signal_strength,
+	.read_ucblocks = sp8870_read_uncorrected_blocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver");
+MODULE_AUTHOR("Juergen Peitz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sp8870_attach);
diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h
new file mode 100644
index 0000000..f3b555d
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp8870.h
@@ -0,0 +1,45 @@
+/*
+    Driver for Spase SP8870 demodulator
+
+    Copyright (C) 1999 Juergen Peitz
+
+    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 SP8870_H
+#define SP8870_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp8870_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+	/* request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+					  struct i2c_adapter* i2c);
+
+#endif // SP8870_H
diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c
new file mode 100644
index 0000000..7eae833
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp887x.c
@@ -0,0 +1,606 @@
+/*
+   Driver for the Spase sp887x demodulator
+*/
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define SP887X_DEFAULT_FIRMWARE "dvb-fe-sp887x.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "sp887x.h"
+
+
+struct sp887x_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	const struct sp887x_config* config;
+	struct dvb_frontend frontend;
+
+	/* demodulator private data */
+	u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "sp887x: " args); \
+	} while (0)
+
+static int i2c_writebytes (struct sp887x_state* state, u8 *buf, u8 len)
+{
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = len };
+	int err;
+
+	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+		printk ("%s: i2c write error (addr %02x, err == %i)\n",
+			__FUNCTION__, state->config->demod_address, err);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data)
+{
+	u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 };
+	int ret;
+
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		/**
+		 *  in case of soft reset we ignore ACK errors...
+		 */
+		if (!(reg == 0xf1a && data == 0x000 &&
+			(ret == -EREMOTEIO || ret == -EFAULT)))
+		{
+			printk("%s: writereg error "
+			       "(reg %03x, data %03x, ret == %i)\n",
+			       __FUNCTION__, reg & 0xffff, data & 0xffff, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int sp887x_readreg (struct sp887x_state* state, u16 reg)
+{
+	u8 b0 [] = { reg >> 8 , reg & 0xff };
+	u8 b1 [2];
+	int ret;
+	struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+		         { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }};
+
+	if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) {
+		printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+		return -1;
+	}
+
+	return (((b1[0] << 8) | b1[1]) & 0xfff);
+}
+
+static void sp887x_microcontroller_stop (struct sp887x_state* state)
+{
+	dprintk("%s\n", __FUNCTION__);
+	sp887x_writereg(state, 0xf08, 0x000);
+	sp887x_writereg(state, 0xf09, 0x000);
+
+	/* microcontroller STOP */
+	sp887x_writereg(state, 0xf00, 0x000);
+}
+
+static void sp887x_microcontroller_start (struct sp887x_state* state)
+{
+	dprintk("%s\n", __FUNCTION__);
+	sp887x_writereg(state, 0xf08, 0x000);
+	sp887x_writereg(state, 0xf09, 0x000);
+
+	/* microcontroller START */
+	sp887x_writereg(state, 0xf00, 0x001);
+}
+
+static void sp887x_setup_agc (struct sp887x_state* state)
+{
+	/* setup AGC parameters */
+	dprintk("%s\n", __FUNCTION__);
+	sp887x_writereg(state, 0x33c, 0x054);
+	sp887x_writereg(state, 0x33b, 0x04c);
+	sp887x_writereg(state, 0x328, 0x000);
+	sp887x_writereg(state, 0x327, 0x005);
+	sp887x_writereg(state, 0x326, 0x001);
+	sp887x_writereg(state, 0x325, 0x001);
+	sp887x_writereg(state, 0x324, 0x001);
+	sp887x_writereg(state, 0x318, 0x050);
+	sp887x_writereg(state, 0x317, 0x3fe);
+	sp887x_writereg(state, 0x316, 0x001);
+	sp887x_writereg(state, 0x313, 0x005);
+	sp887x_writereg(state, 0x312, 0x002);
+	sp887x_writereg(state, 0x306, 0x000);
+	sp887x_writereg(state, 0x303, 0x000);
+}
+
+#define BLOCKSIZE 30
+#define FW_SIZE 0x4000
+/**
+ *  load firmware and setup MPEG interface...
+ */
+static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+	u8 buf [BLOCKSIZE+2];
+	int i;
+	int fw_size = fw->size;
+	unsigned char *mem = fw->data;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	/* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */
+	if (fw_size < FW_SIZE+10)
+		return -ENODEV;
+
+	mem = fw->data + 10;
+
+	/* soft reset */
+	sp887x_writereg(state, 0xf1a, 0x000);
+
+	sp887x_microcontroller_stop (state);
+
+	printk ("%s: firmware upload... ", __FUNCTION__);
+
+	/* setup write pointer to -1 (end of memory) */
+	/* bit 0x8000 in address is set to enable 13bit mode */
+	sp887x_writereg(state, 0x8f08, 0x1fff);
+
+	/* dummy write (wrap around to start of memory) */
+	sp887x_writereg(state, 0x8f0a, 0x0000);
+
+	for (i = 0; i < FW_SIZE; i += BLOCKSIZE) {
+		int c = BLOCKSIZE;
+		int err;
+
+		if (i+c > FW_SIZE)
+			c = FW_SIZE - i;
+
+		/* bit 0x8000 in address is set to enable 13bit mode */
+		/* bit 0x4000 enables multibyte read/write transfers */
+		/* write register is 0xf0a */
+		buf[0] = 0xcf;
+		buf[1] = 0x0a;
+
+		memcpy(&buf[2], mem + i, c);
+
+		if ((err = i2c_writebytes (state, buf, c+2)) < 0) {
+			printk ("failed.\n");
+			printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
+			return err;
+		}
+	}
+
+	/* don't write RS bytes between packets */
+	sp887x_writereg(state, 0xc13, 0x001);
+
+	/* suppress clock if (!data_valid) */
+	sp887x_writereg(state, 0xc14, 0x000);
+
+	/* setup MPEG interface... */
+	sp887x_writereg(state, 0xc1a, 0x872);
+	sp887x_writereg(state, 0xc1b, 0x001);
+	sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */
+	sp887x_writereg(state, 0xc1a, 0x871);
+
+	/* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */
+	sp887x_writereg(state, 0x301, 0x002);
+
+	sp887x_setup_agc(state);
+
+	/* bit 0x010: enable data valid signal */
+	sp887x_writereg(state, 0xd00, 0x010);
+	sp887x_writereg(state, 0x0d1, 0x000);
+
+	/* setup the PLL */
+	if (state->config->pll_init) {
+		sp887x_writereg(state, 0x206, 0x001);
+		state->config->pll_init(fe);
+		sp887x_writereg(state, 0x206, 0x000);
+	}
+
+	printk ("done.\n");
+	return 0;
+};
+
+static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
+{
+	int known_parameters = 1;
+
+	*reg0xc05 = 0x000;
+
+	switch (p->u.ofdm.constellation) {
+	case QPSK:
+		break;
+	case QAM_16:
+		*reg0xc05 |= (1 << 10);
+		break;
+	case QAM_64:
+		*reg0xc05 |= (2 << 10);
+		break;
+	case QAM_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (p->u.ofdm.hierarchy_information) {
+	case HIERARCHY_NONE:
+		break;
+	case HIERARCHY_1:
+		*reg0xc05 |= (1 << 7);
+		break;
+	case HIERARCHY_2:
+		*reg0xc05 |= (2 << 7);
+		break;
+	case HIERARCHY_4:
+		*reg0xc05 |= (3 << 7);
+		break;
+	case HIERARCHY_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (p->u.ofdm.code_rate_HP) {
+	case FEC_1_2:
+		break;
+	case FEC_2_3:
+		*reg0xc05 |= (1 << 3);
+		break;
+	case FEC_3_4:
+		*reg0xc05 |= (2 << 3);
+		break;
+	case FEC_5_6:
+		*reg0xc05 |= (3 << 3);
+		break;
+	case FEC_7_8:
+		*reg0xc05 |= (4 << 3);
+		break;
+	case FEC_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	if (known_parameters)
+		*reg0xc05 |= (2 << 1);	/* use specified parameters */
+	else
+		*reg0xc05 |= (1 << 1);	/* enable autoprobing */
+
+	return 0;
+}
+
+/**
+ *  estimates division of two 24bit numbers,
+ *  derived from the ves1820/stv0299 driver code
+ */
+static void divide (int n, int d, int *quotient_i, int *quotient_f)
+{
+	unsigned int q, r;
+
+	r = (n % d) << 8;
+	q = (r / d);
+
+	if (quotient_i)
+		*quotient_i = q;
+
+	if (quotient_f) {
+		r = (r % d) << 8;
+		q = (q << 8) | (r / d);
+		r = (r % d) << 8;
+		*quotient_f = (q << 8) | (r / d);
+	}
+}
+
+static void sp887x_correct_offsets (struct sp887x_state* state,
+				    struct dvb_frontend_parameters *p,
+				    int actual_freq)
+{
+	static const u32 srate_correction [] = { 1879617, 4544878, 8098561 };
+	int bw_index = p->u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+	int freq_offset = actual_freq - p->frequency;
+	int sysclock = 61003; //[kHz]
+	int ifreq = 36000000;
+	int freq;
+	int frequency_shift;
+
+	if (p->inversion == INVERSION_ON)
+		freq = ifreq - freq_offset;
+	else
+		freq = ifreq + freq_offset;
+
+	divide(freq / 333, sysclock, NULL, &frequency_shift);
+
+	if (p->inversion == INVERSION_ON)
+		frequency_shift = -frequency_shift;
+
+	/* sample rate correction */
+	sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12);
+	sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff);
+
+	/* carrier offset correction */
+	sp887x_writereg(state, 0x309, frequency_shift >> 12);
+	sp887x_writereg(state, 0x30a, frequency_shift & 0xfff);
+}
+
+static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe,
+					     struct dvb_frontend_parameters *p)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+	int actual_freq, err;
+	u16 val, reg0xc05;
+
+	if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ &&
+	    p->u.ofdm.bandwidth != BANDWIDTH_7_MHZ &&
+	    p->u.ofdm.bandwidth != BANDWIDTH_6_MHZ)
+		return -EINVAL;
+
+	if ((err = configure_reg0xc05(p, &reg0xc05)))
+		return err;
+
+	sp887x_microcontroller_stop(state);
+
+	/* setup the PLL */
+	sp887x_writereg(state, 0x206, 0x001);
+	actual_freq = state->config->pll_set(fe, p);
+	sp887x_writereg(state, 0x206, 0x000);
+
+	/* read status reg in order to clear <pending irqs */
+	sp887x_readreg(state, 0x200);
+
+	sp887x_correct_offsets(state, p, actual_freq);
+
+	/* filter for 6/7/8 Mhz channel */
+	if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+		val = 2;
+	else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+		val = 1;
+	else
+		val = 0;
+
+	sp887x_writereg(state, 0x311, val);
+
+	/* scan order: 2k first = 0, 8k first = 1 */
+	if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
+		sp887x_writereg(state, 0x338, 0x000);
+	else
+		sp887x_writereg(state, 0x338, 0x001);
+
+	sp887x_writereg(state, 0xc05, reg0xc05);
+
+	if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+		val = 2 << 3;
+	else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+		val = 3 << 3;
+	else
+		val = 0 << 3;
+
+	/* enable OFDM and SAW bits as lock indicators in sync register 0xf17,
+	 * optimize algorithm for given bandwidth...
+	 */
+	sp887x_writereg(state, 0xf14, 0x160 | val);
+	sp887x_writereg(state, 0xf15, 0x000);
+
+	sp887x_microcontroller_start(state);
+	return 0;
+}
+
+static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+	u16 snr12 = sp887x_readreg(state, 0xf16);
+	u16 sync0x200 = sp887x_readreg(state, 0x200);
+	u16 sync0xf17 = sp887x_readreg(state, 0xf17);
+
+	*status = 0;
+
+	if (snr12 > 0x00f)
+		*status |= FE_HAS_SIGNAL;
+
+	//if (sync0x200 & 0x004)
+	//	*status |= FE_HAS_SYNC | FE_HAS_CARRIER;
+
+	//if (sync0x200 & 0x008)
+	//	*status |= FE_HAS_VITERBI;
+
+	if ((sync0xf17 & 0x00f) == 0x002) {
+		*status |= FE_HAS_LOCK;
+		*status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_CARRIER;
+	}
+
+	if (sync0x200 & 0x001) {	/* tuner adjustment requested...*/
+		int steps = (sync0x200 >> 4) & 0x00f;
+		if (steps & 0x008)
+			steps = -steps;
+		dprintk("sp887x: implement tuner adjustment (%+i steps)!!\n",
+		       steps);
+	}
+
+	return 0;
+}
+
+static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	*ber = (sp887x_readreg(state, 0xc08) & 0x3f) |
+	       (sp887x_readreg(state, 0xc07) << 6);
+	sp887x_writereg(state, 0xc08, 0x000);
+	sp887x_writereg(state, 0xc07, 0x000);
+	if (*ber >= 0x3fff0)
+		*ber = ~0;
+
+	return 0;
+}
+
+static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	u16 snr12 = sp887x_readreg(state, 0xf16);
+	u32 signal = 3 * (snr12 << 4);
+	*strength = (signal < 0xffff) ? signal : 0xffff;
+
+	return 0;
+}
+
+static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	u16 snr12 = sp887x_readreg(state, 0xf16);
+	*snr = (snr12 << 4) | (snr12 >> 8);
+
+	return 0;
+}
+
+static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	*ucblocks = sp887x_readreg(state, 0xc0c);
+	if (*ucblocks == 0xfff)
+		*ucblocks = ~0;
+
+	return 0;
+}
+
+static int sp887x_sleep(struct dvb_frontend* fe)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	/* tristate TS output and disable interface pins */
+	sp887x_writereg(state, 0xc18, 0x000);
+
+	return 0;
+}
+
+static int sp887x_init(struct dvb_frontend* fe)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+        const struct firmware *fw = NULL;
+	int ret;
+
+	if (!state->initialised) {
+		/* request the firmware, this will block until someone uploads it */
+		printk("sp887x: waiting for firmware upload (%s)...\n", SP887X_DEFAULT_FIRMWARE);
+		ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE);
+		if (ret) {
+			printk("sp887x: no firmware upload (timeout or file not found?)\n");
+			return ret;
+		}
+
+		ret = sp887x_initial_setup(fe, fw);
+		if (ret) {
+			printk("sp887x: writing firmware to device failed\n");
+			release_firmware(fw);
+			return ret;
+		}
+		printk("sp887x: firmware upload complete\n");
+		state->initialised = 1;
+	}
+
+	/* enable TS output and interface pins */
+	sp887x_writereg(state, 0xc18, 0x00d);
+
+	return 0;
+}
+
+static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 350;
+        fesettings->step_size = 166666*2;
+        fesettings->max_drift = (166666*2)+1;
+        return 0;
+}
+
+static void sp887x_release(struct dvb_frontend* fe)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops sp887x_ops;
+
+struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+				   struct i2c_adapter* i2c)
+{
+	struct sp887x_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct sp887x_state*) kmalloc(sizeof(struct sp887x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &sp887x_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+
+	/* check if the demod is there */
+	if (sp887x_readreg(state, 0x0200) < 0) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops sp887x_ops = {
+
+	.info = {
+		.name = "Spase SP887x DVB-T",
+		.type = FE_OFDM,
+		.frequency_min =  50500000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166666,
+		.caps = 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_RECOVER
+	},
+
+	.release = sp887x_release,
+
+	.init = sp887x_init,
+	.sleep = sp887x_sleep,
+
+	.set_frontend = sp887x_setup_frontend_parameters,
+	.get_tune_settings = sp887x_get_tune_settings,
+
+	.read_status = sp887x_read_status,
+	.read_ber = sp887x_read_ber,
+	.read_signal_strength = sp887x_read_signal_strength,
+	.read_snr = sp887x_read_snr,
+	.read_ucblocks = sp887x_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sp887x_attach);
diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h
new file mode 100644
index 0000000..6a05d8f
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp887x.h
@@ -0,0 +1,29 @@
+/*
+   Driver for the Spase sp887x demodulator
+*/
+
+#ifndef SP887X_H
+#define SP887X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp887x_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+
+	/* this should return the actual frequency tuned to */
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+	/* request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+					  struct i2c_adapter* i2c);
+
+#endif // SP887X_H
diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c
new file mode 100644
index 0000000..502c640
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0297.c
@@ -0,0 +1,798 @@
+/*
+    Driver for STV0297 demodulator
+
+    Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+    Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.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; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "stv0297.h"
+
+struct stv0297_state {
+	struct i2c_adapter *i2c;
+	struct dvb_frontend_ops ops;
+	const struct stv0297_config *config;
+	struct dvb_frontend frontend;
+
+	unsigned long base_freq;
+	u8 pwm;
+};
+
+#if 1
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+#define STV0297_CLOCK_KHZ   28900
+
+static u8 init_tab[] = {
+	0x00, 0x09,
+	0x01, 0x69,
+	0x03, 0x00,
+	0x04, 0x00,
+	0x07, 0x00,
+	0x08, 0x00,
+	0x20, 0x00,
+	0x21, 0x40,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x24, 0x40,
+	0x25, 0x88,
+	0x30, 0xff,
+	0x31, 0x00,
+	0x32, 0xff,
+	0x33, 0x00,
+	0x34, 0x50,
+	0x35, 0x7f,
+	0x36, 0x00,
+	0x37, 0x20,
+	0x38, 0x00,
+	0x40, 0x1c,
+	0x41, 0xff,
+	0x42, 0x29,
+	0x43, 0x00,
+	0x44, 0xff,
+	0x45, 0x00,
+	0x46, 0x00,
+	0x49, 0x04,
+	0x4a, 0xff,
+	0x4b, 0x7f,
+	0x52, 0x30,
+	0x55, 0xae,
+	0x56, 0x47,
+	0x57, 0xe1,
+	0x58, 0x3a,
+	0x5a, 0x1e,
+	0x5b, 0x34,
+	0x60, 0x00,
+	0x63, 0x00,
+	0x64, 0x00,
+	0x65, 0x00,
+	0x66, 0x00,
+	0x67, 0x00,
+	0x68, 0x00,
+	0x69, 0x00,
+	0x6a, 0x02,
+	0x6b, 0x00,
+	0x70, 0xff,
+	0x71, 0x00,
+	0x72, 0x00,
+	0x73, 0x00,
+	0x74, 0x0c,
+	0x80, 0x00,
+	0x81, 0x00,
+	0x82, 0x00,
+	0x83, 0x00,
+	0x84, 0x04,
+	0x85, 0x80,
+	0x86, 0x24,
+	0x87, 0x78,
+	0x88, 0x00,
+	0x89, 0x00,
+	0x90, 0x01,
+	0x91, 0x01,
+	0xa0, 0x00,
+	0xa1, 0x00,
+	0xa2, 0x00,
+	0xb0, 0x91,
+	0xb1, 0x0b,
+	0xc0, 0x53,
+	0xc1, 0x70,
+	0xc2, 0x12,
+	0xd0, 0x00,
+	0xd1, 0x00,
+	0xd2, 0x00,
+	0xd3, 0x00,
+	0xd4, 0x00,
+	0xd5, 0x00,
+	0xde, 0x00,
+	0xdf, 0x00,
+	0x61, 0x49,
+	0x62, 0x0b,
+	0x53, 0x08,
+	0x59, 0x08,
+};
+
+
+static int stv0297_writereg(struct stv0297_state *state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 };
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+			"ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+	return (ret != 1) ? -1 : 0;
+}
+
+static int stv0297_readreg(struct stv0297_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len =
+				  1},
+	{.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
+	};
+
+	// this device needs a STOP between the register and data
+	if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) {
+		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret);
+		return -1;
+	}
+	if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) {
+		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret);
+		return -1;
+	}
+
+	return b1[0];
+}
+
+static int stv0297_writereg_mask(struct stv0297_state *state, u8 reg, u8 mask, u8 data)
+{
+	int val;
+
+	val = stv0297_readreg(state, reg);
+	val &= ~mask;
+	val |= (data & mask);
+	stv0297_writereg(state, reg, val);
+
+	return 0;
+}
+
+static int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len)
+{
+	int ret;
+	struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf =
+				  &reg1,.len = 1},
+	{.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b,.len = len}
+	};
+
+	// this device needs a STOP between the register and data
+	if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) {
+		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret);
+		return -1;
+	}
+	if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) {
+		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret);
+		return -1;
+	}
+
+	return 0;
+}
+
+static u32 stv0297_get_symbolrate(struct stv0297_state *state)
+{
+	u64 tmp;
+
+	tmp = stv0297_readreg(state, 0x55);
+	tmp |= stv0297_readreg(state, 0x56) << 8;
+	tmp |= stv0297_readreg(state, 0x57) << 16;
+	tmp |= stv0297_readreg(state, 0x58) << 24;
+
+	tmp *= STV0297_CLOCK_KHZ;
+	tmp >>= 32;
+
+	return (u32) tmp;
+}
+
+static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate)
+{
+	long tmp;
+
+	tmp = 131072L * srate;	/* 131072 = 2^17  */
+	tmp = tmp / (STV0297_CLOCK_KHZ / 4);	/* 1/4 = 2^-2 */
+	tmp = tmp * 8192L;	/* 8192 = 2^13 */
+
+	stv0297_writereg(state, 0x55, (unsigned char) (tmp & 0xFF));
+	stv0297_writereg(state, 0x56, (unsigned char) (tmp >> 8));
+	stv0297_writereg(state, 0x57, (unsigned char) (tmp >> 16));
+	stv0297_writereg(state, 0x58, (unsigned char) (tmp >> 24));
+}
+
+static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate)
+{
+	long tmp;
+
+	tmp = (long) fshift *262144L;	/* 262144 = 2*18 */
+	tmp /= symrate;
+	tmp *= 1024;		/* 1024 = 2*10   */
+
+	// adjust
+	if (tmp >= 0) {
+		tmp += 500000;
+	} else {
+		tmp -= 500000;
+	}
+	tmp /= 1000000;
+
+	stv0297_writereg(state, 0x60, tmp & 0xFF);
+	stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0);
+}
+
+static void stv0297_set_carrieroffset(struct stv0297_state *state, long offset)
+{
+	long tmp;
+
+	/* symrate is hardcoded to 10000 */
+	tmp = offset * 26844L;	/* (2**28)/10000 */
+	if (tmp < 0)
+		tmp += 0x10000000;
+	tmp &= 0x0FFFFFFF;
+
+	stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF));
+	stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8));
+	stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16));
+	stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f);
+}
+
+/*
+static long stv0297_get_carrieroffset(struct stv0297_state *state)
+{
+	s64 tmp;
+
+	stv0297_writereg(state, 0x6B, 0x00);
+
+	tmp = stv0297_readreg(state, 0x66);
+	tmp |= (stv0297_readreg(state, 0x67) << 8);
+	tmp |= (stv0297_readreg(state, 0x68) << 16);
+	tmp |= (stv0297_readreg(state, 0x69) & 0x0F) << 24;
+
+	tmp *= stv0297_get_symbolrate(state);
+	tmp >>= 28;
+
+	return (s32) tmp;
+}
+*/
+
+static void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq)
+{
+	s32 tmp;
+
+	if (freq > 10000)
+		freq -= STV0297_CLOCK_KHZ;
+
+	tmp = (STV0297_CLOCK_KHZ * 1000) / (1 << 16);
+	tmp = (freq * 1000) / tmp;
+	if (tmp > 0xffff)
+		tmp = 0xffff;
+
+	stv0297_writereg_mask(state, 0x25, 0x80, 0x80);
+	stv0297_writereg(state, 0x21, tmp >> 8);
+	stv0297_writereg(state, 0x20, tmp);
+}
+
+static int stv0297_set_qam(struct stv0297_state *state, fe_modulation_t modulation)
+{
+	int val = 0;
+
+	switch (modulation) {
+	case QAM_16:
+		val = 0;
+		break;
+
+	case QAM_32:
+		val = 1;
+		break;
+
+	case QAM_64:
+		val = 4;
+		break;
+
+	case QAM_128:
+		val = 2;
+		break;
+
+	case QAM_256:
+		val = 3;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	stv0297_writereg_mask(state, 0x00, 0x70, val << 4);
+
+	return 0;
+}
+
+static int stv0297_set_inversion(struct stv0297_state *state, fe_spectral_inversion_t inversion)
+{
+	int val = 0;
+
+	switch (inversion) {
+	case INVERSION_OFF:
+		val = 0;
+		break;
+
+	case INVERSION_ON:
+		val = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	stv0297_writereg_mask(state, 0x83, 0x08, val << 3);
+
+	return 0;
+}
+
+int stv0297_enable_plli2c(struct dvb_frontend *fe)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+	stv0297_writereg(state, 0x87, 0x78);
+	stv0297_writereg(state, 0x86, 0xc8);
+
+	return 0;
+}
+
+static int stv0297_init(struct dvb_frontend *fe)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+	int i;
+
+	/* soft reset */
+	stv0297_writereg_mask(state, 0x80, 1, 1);
+	stv0297_writereg_mask(state, 0x80, 1, 0);
+
+	/* reset deinterleaver */
+	stv0297_writereg_mask(state, 0x81, 1, 1);
+	stv0297_writereg_mask(state, 0x81, 1, 0);
+
+	/* load init table */
+	for (i = 0; i < sizeof(init_tab); i += 2) {
+		stv0297_writereg(state, init_tab[i], init_tab[i + 1]);
+	}
+
+	/* set a dummy symbol rate */
+	stv0297_set_symbolrate(state, 6900);
+
+	/* invert AGC1 polarity */
+	stv0297_writereg_mask(state, 0x88, 0x10, 0x10);
+
+	/* setup bit error counting */
+	stv0297_writereg_mask(state, 0xA0, 0x80, 0x00);
+	stv0297_writereg_mask(state, 0xA0, 0x10, 0x00);
+	stv0297_writereg_mask(state, 0xA0, 0x08, 0x00);
+	stv0297_writereg_mask(state, 0xA0, 0x07, 0x04);
+
+	/* min + max PWM */
+	stv0297_writereg(state, 0x4a, 0x00);
+	stv0297_writereg(state, 0x4b, state->pwm);
+	msleep(200);
+
+	if (state->config->pll_init)
+		state->config->pll_init(fe);
+
+	return 0;
+}
+
+static int stv0297_sleep(struct dvb_frontend *fe)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+	stv0297_writereg_mask(state, 0x80, 1, 1);
+
+	return 0;
+}
+
+static int stv0297_read_status(struct dvb_frontend *fe, fe_status_t * status)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+	u8 sync = stv0297_readreg(state, 0xDF);
+
+	*status = 0;
+	if (sync & 0x80)
+		*status |=
+			FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK;
+	return 0;
+}
+
+static int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+	u8 BER[3];
+
+	stv0297_writereg(state, 0xA0, 0x80);	// Start Counting bit errors for 4096 Bytes
+	mdelay(25);		// Hopefully got 4096 Bytes
+	stv0297_readregs(state, 0xA0, BER, 3);
+	mdelay(25);
+	*ber = (BER[2] << 8 | BER[1]) / (8 * 4096);
+
+	return 0;
+}
+
+
+static int stv0297_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+	u8 STRENGTH[2];
+
+	stv0297_readregs(state, 0x41, STRENGTH, 2);
+	*strength = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0];
+
+	return 0;
+}
+
+static int stv0297_read_snr(struct dvb_frontend *fe, u16 * snr)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+	u8 SNR[2];
+
+	stv0297_readregs(state, 0x07, SNR, 2);
+	*snr = SNR[1] << 8 | SNR[0];
+
+	return 0;
+}
+
+static int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+	*ucblocks = (stv0297_readreg(state, 0xD5) << 8)
+		| stv0297_readreg(state, 0xD4);
+
+	return 0;
+}
+
+static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+	int u_threshold;
+	int initial_u;
+	int blind_u;
+	int delay;
+	int sweeprate;
+	int carrieroffset;
+	unsigned long starttime;
+	unsigned long timeout;
+	fe_spectral_inversion_t inversion;
+
+	switch (p->u.qam.modulation) {
+	case QAM_16:
+	case QAM_32:
+	case QAM_64:
+		delay = 100;
+		sweeprate = 1500;
+		break;
+
+	case QAM_128:
+		delay = 150;
+		sweeprate = 1000;
+		break;
+
+	case QAM_256:
+		delay = 200;
+		sweeprate = 500;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// determine inversion dependant parameters
+	inversion = p->inversion;
+	if (state->config->invert)
+		inversion = (inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON;
+	carrieroffset = -330;
+	switch (inversion) {
+	case INVERSION_OFF:
+		break;
+
+	case INVERSION_ON:
+		sweeprate = -sweeprate;
+		carrieroffset = -carrieroffset;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	stv0297_init(fe);
+	state->config->pll_set(fe, p);
+
+	/* clear software interrupts */
+	stv0297_writereg(state, 0x82, 0x0);
+
+	/* set initial demodulation frequency */
+	stv0297_set_initialdemodfreq(state, 7250);
+
+	/* setup AGC */
+	stv0297_writereg_mask(state, 0x43, 0x10, 0x00);
+	stv0297_writereg(state, 0x41, 0x00);
+	stv0297_writereg_mask(state, 0x42, 0x03, 0x01);
+	stv0297_writereg_mask(state, 0x36, 0x60, 0x00);
+	stv0297_writereg_mask(state, 0x36, 0x18, 0x00);
+	stv0297_writereg_mask(state, 0x71, 0x80, 0x80);
+	stv0297_writereg(state, 0x72, 0x00);
+	stv0297_writereg(state, 0x73, 0x00);
+	stv0297_writereg_mask(state, 0x74, 0x0F, 0x00);
+	stv0297_writereg_mask(state, 0x43, 0x08, 0x00);
+	stv0297_writereg_mask(state, 0x71, 0x80, 0x00);
+
+	/* setup STL */
+	stv0297_writereg_mask(state, 0x5a, 0x20, 0x20);
+	stv0297_writereg_mask(state, 0x5b, 0x02, 0x02);
+	stv0297_writereg_mask(state, 0x5b, 0x02, 0x00);
+	stv0297_writereg_mask(state, 0x5b, 0x01, 0x00);
+	stv0297_writereg_mask(state, 0x5a, 0x40, 0x40);
+
+	/* disable frequency sweep */
+	stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+
+	/* reset deinterleaver */
+	stv0297_writereg_mask(state, 0x81, 0x01, 0x01);
+	stv0297_writereg_mask(state, 0x81, 0x01, 0x00);
+
+	/* ??? */
+	stv0297_writereg_mask(state, 0x83, 0x20, 0x20);
+	stv0297_writereg_mask(state, 0x83, 0x20, 0x00);
+
+	/* reset equaliser */
+	u_threshold = stv0297_readreg(state, 0x00) & 0xf;
+	initial_u = stv0297_readreg(state, 0x01) >> 4;
+	blind_u = stv0297_readreg(state, 0x01) & 0xf;
+	stv0297_writereg_mask(state, 0x84, 0x01, 0x01);
+	stv0297_writereg_mask(state, 0x84, 0x01, 0x00);
+	stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold);
+	stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4);
+	stv0297_writereg_mask(state, 0x01, 0x0f, blind_u);
+
+	/* data comes from internal A/D */
+	stv0297_writereg_mask(state, 0x87, 0x80, 0x00);
+
+	/* clear phase registers */
+	stv0297_writereg(state, 0x63, 0x00);
+	stv0297_writereg(state, 0x64, 0x00);
+	stv0297_writereg(state, 0x65, 0x00);
+	stv0297_writereg(state, 0x66, 0x00);
+	stv0297_writereg(state, 0x67, 0x00);
+	stv0297_writereg(state, 0x68, 0x00);
+	stv0297_writereg_mask(state, 0x69, 0x0f, 0x00);
+
+	/* set parameters */
+	stv0297_set_qam(state, p->u.qam.modulation);
+	stv0297_set_symbolrate(state, p->u.qam.symbol_rate / 1000);
+	stv0297_set_sweeprate(state, sweeprate, p->u.qam.symbol_rate / 1000);
+	stv0297_set_carrieroffset(state, carrieroffset);
+	stv0297_set_inversion(state, inversion);
+
+	/* kick off lock */
+	stv0297_writereg_mask(state, 0x88, 0x08, 0x08);
+	stv0297_writereg_mask(state, 0x5a, 0x20, 0x00);
+	stv0297_writereg_mask(state, 0x6a, 0x01, 0x01);
+	stv0297_writereg_mask(state, 0x43, 0x40, 0x40);
+	stv0297_writereg_mask(state, 0x5b, 0x30, 0x00);
+	stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c);
+	stv0297_writereg_mask(state, 0x03, 0x03, 0x03);
+	stv0297_writereg_mask(state, 0x43, 0x10, 0x10);
+
+	/* wait for WGAGC lock */
+	starttime = jiffies;
+	timeout = jiffies + (200 * HZ) / 1000;
+	while (time_before(jiffies, timeout)) {
+		msleep(10);
+		if (stv0297_readreg(state, 0x43) & 0x08)
+			break;
+	}
+	if (time_after(jiffies, timeout)) {
+		goto timeout;
+	}
+	msleep(20);
+
+	/* wait for equaliser partial convergence */
+	timeout = jiffies + (50 * HZ) / 1000;
+	while (time_before(jiffies, timeout)) {
+		msleep(10);
+
+		if (stv0297_readreg(state, 0x82) & 0x04) {
+			break;
+		}
+	}
+	if (time_after(jiffies, timeout)) {
+		goto timeout;
+	}
+
+	/* wait for equaliser full convergence */
+	timeout = jiffies + (delay * HZ) / 1000;
+	while (time_before(jiffies, timeout)) {
+		msleep(10);
+
+		if (stv0297_readreg(state, 0x82) & 0x08) {
+			break;
+		}
+	}
+	if (time_after(jiffies, timeout)) {
+		goto timeout;
+	}
+
+	/* disable sweep */
+	stv0297_writereg_mask(state, 0x6a, 1, 0);
+	stv0297_writereg_mask(state, 0x88, 8, 0);
+
+	/* wait for main lock */
+	timeout = jiffies + (20 * HZ) / 1000;
+	while (time_before(jiffies, timeout)) {
+		msleep(10);
+
+		if (stv0297_readreg(state, 0xDF) & 0x80) {
+			break;
+		}
+	}
+	if (time_after(jiffies, timeout)) {
+		goto timeout;
+	}
+	msleep(100);
+
+	/* is it still locked after that delay? */
+	if (!(stv0297_readreg(state, 0xDF) & 0x80)) {
+		goto timeout;
+	}
+
+	/* success!! */
+	stv0297_writereg_mask(state, 0x5a, 0x40, 0x00);
+	state->base_freq = p->frequency;
+	return 0;
+
+timeout:
+	stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+	return 0;
+}
+
+static int stv0297_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+	int reg_00, reg_83;
+
+	reg_00 = stv0297_readreg(state, 0x00);
+	reg_83 = stv0297_readreg(state, 0x83);
+
+	p->frequency = state->base_freq;
+	p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF;
+	if (state->config->invert)
+		p->inversion = (p->inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON;
+	p->u.qam.symbol_rate = stv0297_get_symbolrate(state) * 1000;
+	p->u.qam.fec_inner = FEC_NONE;
+
+	switch ((reg_00 >> 4) & 0x7) {
+	case 0:
+		p->u.qam.modulation = QAM_16;
+		break;
+	case 1:
+		p->u.qam.modulation = QAM_32;
+		break;
+	case 2:
+		p->u.qam.modulation = QAM_128;
+		break;
+	case 3:
+		p->u.qam.modulation = QAM_256;
+		break;
+	case 4:
+		p->u.qam.modulation = QAM_64;
+		break;
+	}
+
+	return 0;
+}
+
+static void stv0297_release(struct dvb_frontend *fe)
+{
+	struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops stv0297_ops;
+
+struct dvb_frontend *stv0297_attach(const struct stv0297_config *config,
+				    struct i2c_adapter *i2c, int pwm)
+{
+	struct stv0297_state *state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct stv0297_state *) kmalloc(sizeof(struct stv0297_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &stv0297_ops, sizeof(struct dvb_frontend_ops));
+	state->base_freq = 0;
+	state->pwm = pwm;
+
+	/* check if the demod is there */
+	if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20)
+		goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops stv0297_ops = {
+
+	.info = {
+		 .name = "ST STV0297 DVB-C",
+		 .type = FE_QAM,
+		 .frequency_min = 64000000,
+		 .frequency_max = 1300000000,
+		 .frequency_stepsize = 62500,
+		 .symbol_rate_min = 870000,
+		 .symbol_rate_max = 11700000,
+		 .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+		 FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO},
+
+	.release = stv0297_release,
+
+	.init = stv0297_init,
+	.sleep = stv0297_sleep,
+
+	.set_frontend = stv0297_set_frontend,
+	.get_frontend = stv0297_get_frontend,
+
+	.read_status = stv0297_read_status,
+	.read_ber = stv0297_read_ber,
+	.read_signal_strength = stv0297_read_signal_strength,
+	.read_snr = stv0297_read_snr,
+	.read_ucblocks = stv0297_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver");
+MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(stv0297_attach);
+EXPORT_SYMBOL(stv0297_enable_plli2c);
diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h
new file mode 100644
index 0000000..3be5359
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0297.h
@@ -0,0 +1,44 @@
+/*
+    Driver for STV0297 demodulator
+
+    Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.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; 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 STV0297_H
+#define STV0297_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0297_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* does the "inversion" need inverted? */
+	u8 invert:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+					   struct i2c_adapter* i2c, int pwm);
+extern int stv0297_enable_plli2c(struct dvb_frontend* fe);
+
+#endif // STV0297_H
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
new file mode 100644
index 0000000..15b4054
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -0,0 +1,731 @@
+/*
+    Driver for ST STV0299 demodulator
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	<ralph@convergence.de>,
+	<holger@convergence.de>,
+	<js@convergence.de>
+
+
+    Philips SU1278/SH
+
+    Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de>
+
+
+    LG TDQF-S001F
+
+    Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net>
+		     & Andreas Oberritter <obi@linuxtv.org>
+
+
+    Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B
+
+    Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>:
+
+    Support for Philips SU1278 on Technotrend hardware
+
+    Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "stv0299.h"
+
+struct stv0299_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	const struct stv0299_config* config;
+	struct dvb_frontend frontend;
+
+	u8 initialised:1;
+	u32 tuner_frequency;
+	u32 symbol_rate;
+	fe_code_rate_t fec_inner;
+	int errmode;
+};
+
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "stv0299: " args); \
+	} while (0)
+
+
+static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+	ret = i2c_transfer (state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+			"ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	return stv0299_writeregI(state, reg, data);
+}
+
+static u8 stv0299_readreg (struct stv0299_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+				__FUNCTION__, reg, ret);
+
+	return b1[0];
+}
+
+static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len)
+{
+	int ret;
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+	return ret == 2 ? 0 : ret;
+}
+
+static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec)
+{
+	dprintk ("%s\n", __FUNCTION__);
+
+	switch (fec) {
+	case FEC_AUTO:
+	{
+		return stv0299_writeregI (state, 0x31, 0x1f);
+	}
+	case FEC_1_2:
+	{
+		return stv0299_writeregI (state, 0x31, 0x01);
+	}
+	case FEC_2_3:
+	{
+		return stv0299_writeregI (state, 0x31, 0x02);
+	}
+	case FEC_3_4:
+	{
+		return stv0299_writeregI (state, 0x31, 0x04);
+	}
+	case FEC_5_6:
+	{
+		return stv0299_writeregI (state, 0x31, 0x08);
+	}
+	case FEC_7_8:
+	{
+		return stv0299_writeregI (state, 0x31, 0x10);
+	}
+	default:
+	{
+		return -EINVAL;
+	}
+    }
+}
+
+static fe_code_rate_t stv0299_get_fec (struct stv0299_state* state)
+{
+	static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6,
+					     FEC_7_8, FEC_1_2 };
+	u8 index;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	index = stv0299_readreg (state, 0x1b);
+	index &= 0x7;
+
+	if (index > 4)
+		return FEC_AUTO;
+
+	return fec_tab [index];
+}
+
+static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout)
+{
+	unsigned long start = jiffies;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	while (stv0299_readreg(state, 0x0a) & 1) {
+		if (jiffies - start > timeout) {
+			dprintk ("%s: timeout!!\n", __FUNCTION__);
+			return -ETIMEDOUT;
+		}
+		msleep(10);
+	};
+
+	return 0;
+}
+
+static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout)
+{
+	unsigned long start = jiffies;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) {
+		if (jiffies - start > timeout) {
+			dprintk ("%s: timeout!!\n", __FUNCTION__);
+			return -ETIMEDOUT;
+		}
+		msleep(10);
+	};
+
+	return 0;
+}
+
+static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	u64 big = srate;
+	u32 ratio;
+
+	// check rate is within limits
+	if ((srate < 1000000) || (srate > 45000000)) return -EINVAL;
+
+	// calculate value to program
+	big = big << 20;
+	big += (state->config->mclk-1); // round correctly
+	do_div(big, state->config->mclk);
+	ratio = big << 4;
+
+	return state->config->set_symbol_rate(fe, srate, ratio);
+}
+
+static int stv0299_get_symbolrate (struct stv0299_state* state)
+{
+	u32 Mclk = state->config->mclk / 4096L;
+	u32 srate;
+	s32 offset;
+	u8 sfr[3];
+	s8 rtf;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	stv0299_readregs (state, 0x1f, sfr, 3);
+	stv0299_readregs (state, 0x1a, &rtf, 1);
+
+	srate = (sfr[0] << 8) | sfr[1];
+	srate *= Mclk;
+	srate /= 16;
+	srate += (sfr[2] >> 4) * Mclk / 256;
+	offset = (s32) rtf * (srate / 4096L);
+	offset /= 128;
+
+	dprintk ("%s : srate = %i\n", __FUNCTION__, srate);
+	dprintk ("%s : ofset = %i\n", __FUNCTION__, offset);
+
+	srate += offset;
+
+	srate += 1000;
+	srate /= 2000;
+	srate *= 2000;
+
+	return srate;
+}
+
+static int stv0299_send_diseqc_msg (struct dvb_frontend* fe,
+				    struct dvb_diseqc_master_cmd *m)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	u8 val;
+	int i;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
+		return -ETIMEDOUT;
+
+	val = stv0299_readreg (state, 0x08);
+
+	if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6))  /* DiSEqC mode */
+		return -EREMOTEIO;
+
+	for (i=0; i<m->msg_len; i++) {
+		if (stv0299_wait_diseqc_fifo (state, 100) < 0)
+			return -ETIMEDOUT;
+
+		if (stv0299_writeregI (state, 0x09, m->msg[i]))
+			return -EREMOTEIO;
+	}
+
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	u8 val;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
+		return -ETIMEDOUT;
+
+	val = stv0299_readreg (state, 0x08);
+
+	if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2))	/* burst mode */
+		return -EREMOTEIO;
+
+	if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff))
+		return -EREMOTEIO;
+
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
+		return -ETIMEDOUT;
+
+	if (stv0299_writeregI (state, 0x08, val))
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	u8 val;
+
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
+		return -ETIMEDOUT;
+
+	val = stv0299_readreg (state, 0x08);
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		return stv0299_writeregI (state, 0x08, val | 0x3);
+
+	case SEC_TONE_OFF:
+		return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	u8 reg0x08;
+	u8 reg0x0c;
+
+	dprintk("%s: %s\n", __FUNCTION__,
+		voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+		voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+	reg0x08 = stv0299_readreg (state, 0x08);
+	reg0x0c = stv0299_readreg (state, 0x0c);
+
+	/**
+	 *  H/V switching over OP0, OP1 and OP2 are LNB power enable bits
+	 */
+	reg0x0c &= 0x0f;
+
+	if (voltage == SEC_VOLTAGE_OFF) {
+		stv0299_writeregI (state, 0x0c, 0x00); /*	LNB power off! */
+		return stv0299_writeregI (state, 0x08, 0x00); /*	LNB power off! */
+	}
+
+	stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6));
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) reg0x0c |= 0x10;
+		else reg0x0c |= 0x40;
+
+		return stv0299_writeregI(state, 0x0c, reg0x0c);
+
+	case SEC_VOLTAGE_18:
+		return stv0299_writeregI(state, 0x0c, reg0x0c | 0x50);
+	default:
+		return -EINVAL;
+	};
+}
+
+static int stv0299_send_legacy_dish_cmd(struct dvb_frontend* fe, u32 cmd)
+{
+	u8 last = 1;
+	int i;
+
+	/* reset voltage at the end
+	if((0x50 & stv0299_readreg (i2c, 0x0c)) == 0x50)
+		cmd |= 0x80;
+	else
+		cmd &= 0x7F;
+	*/
+
+	cmd = cmd << 1;
+	dprintk("%s switch command: 0x%04x\n",__FUNCTION__, cmd);
+
+	stv0299_set_voltage(fe,SEC_VOLTAGE_18);
+	msleep(32);
+
+	for (i=0; i<9; i++) {
+		if((cmd & 0x01) != last) {
+			stv0299_set_voltage(fe, last ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
+			last = (last) ? 0 : 1;
+		}
+
+		cmd = cmd >> 1;
+
+		if (i != 8)
+			msleep(8);
+	}
+
+	return 0;
+}
+
+static int stv0299_init (struct dvb_frontend* fe)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	int i;
+
+	dprintk("stv0299: init chip\n");
+
+	for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2)
+		stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]);
+
+	if (state->config->pll_init) {
+		stv0299_writeregI(state, 0x05, 0xb5);	/*  enable i2c repeater on stv0299  */
+		state->config->pll_init(fe);
+		stv0299_writeregI(state, 0x05, 0x35);	/*  disable i2c repeater on stv0299  */
+	}
+
+	return 0;
+}
+
+static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	u8 signal = 0xff - stv0299_readreg (state, 0x18);
+	u8 sync = stv0299_readreg (state, 0x1b);
+
+	dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __FUNCTION__, sync);
+	*status = 0;
+
+	if (signal > 10)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x80)
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x08)
+		*status |= FE_HAS_SYNC;
+
+	if ((sync & 0x98) == 0x98)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	if (state->errmode != STATUS_BER) return 0;
+	*ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e);
+
+	return 0;
+}
+
+static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	s32 signal =  0xffff - ((stv0299_readreg (state, 0x18) << 8)
+			       | stv0299_readreg (state, 0x19));
+
+	dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __FUNCTION__,
+		 stv0299_readreg (state, 0x18),
+		 stv0299_readreg (state, 0x19), (int) signal);
+
+	signal = signal * 5 / 4;
+	*strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
+
+	return 0;
+}
+
+static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8)
+			   | stv0299_readreg (state, 0x25));
+	xsnr = 3 * (xsnr - 0xa100);
+	*snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+
+	return 0;
+}
+
+static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0;
+	else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e);
+
+	return 0;
+}
+
+static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	int invval = 0;
+
+	dprintk ("%s : FE_SET_FRONTEND\n", __FUNCTION__);
+
+	// set the inversion
+	if (p->inversion == INVERSION_OFF) invval = 0;
+	else if (p->inversion == INVERSION_ON) invval = 1;
+	else {
+		printk("stv0299 does not support auto-inversion\n");
+		return -EINVAL;
+	}
+	if (state->config->invert) invval = (~invval) & 1;
+	stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval);
+
+	if (state->config->enhanced_tuning) {
+		/* check if we should do a finetune */
+		int frequency_delta = p->frequency - state->tuner_frequency;
+		int minmax = p->u.qpsk.symbol_rate / 2000;
+		if (minmax < 5000) minmax = 5000;
+
+		if ((frequency_delta > -minmax) && (frequency_delta < minmax) && (frequency_delta != 0) &&
+		    (state->fec_inner == p->u.qpsk.fec_inner) &&
+		    (state->symbol_rate == p->u.qpsk.symbol_rate)) {
+			int Drot_freq = (frequency_delta << 16) / (state->config->mclk / 1000);
+
+			// zap the derotator registers first
+			stv0299_writeregI(state, 0x22, 0x00);
+			stv0299_writeregI(state, 0x23, 0x00);
+
+			// now set them as we want
+			stv0299_writeregI(state, 0x22, Drot_freq >> 8);
+			stv0299_writeregI(state, 0x23, Drot_freq);
+		} else {
+			/* A "normal" tune is requested */
+			stv0299_writeregI(state, 0x05, 0xb5);	/*  enable i2c repeater on stv0299  */
+			state->config->pll_set(fe, p);
+			stv0299_writeregI(state, 0x05, 0x35);	/*  disable i2c repeater on stv0299  */
+
+			stv0299_writeregI(state, 0x32, 0x80);
+			stv0299_writeregI(state, 0x22, 0x00);
+			stv0299_writeregI(state, 0x23, 0x00);
+			stv0299_writeregI(state, 0x32, 0x19);
+			stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate);
+			stv0299_set_FEC (state, p->u.qpsk.fec_inner);
+		}
+	} else {
+		stv0299_writeregI(state, 0x05, 0xb5);	/*  enable i2c repeater on stv0299  */
+		state->config->pll_set(fe, p);
+		stv0299_writeregI(state, 0x05, 0x35);	/*  disable i2c repeater on stv0299  */
+
+		stv0299_set_FEC (state, p->u.qpsk.fec_inner);
+		stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate);
+		stv0299_writeregI(state, 0x22, 0x00);
+		stv0299_writeregI(state, 0x23, 0x00);
+		stv0299_readreg (state, 0x23);
+		stv0299_writeregI(state, 0x12, 0xb9);
+	}
+
+	state->tuner_frequency = p->frequency;
+	state->fec_inner = p->u.qpsk.fec_inner;
+	state->symbol_rate = p->u.qpsk.symbol_rate;
+
+	return 0;
+}
+
+static int stv0299_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	s32 derot_freq;
+	int invval;
+
+	derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8)
+				| stv0299_readreg (state, 0x23));
+
+	derot_freq *= (state->config->mclk >> 16);
+	derot_freq += 500;
+	derot_freq /= 1000;
+
+	p->frequency += derot_freq;
+
+	invval = stv0299_readreg (state, 0x0c) & 1;
+	if (state->config->invert) invval = (~invval) & 1;
+	p->inversion = invval ? INVERSION_ON : INVERSION_OFF;
+
+	p->u.qpsk.fec_inner = stv0299_get_fec (state);
+	p->u.qpsk.symbol_rate = stv0299_get_symbolrate (state);
+
+	return 0;
+}
+
+static int stv0299_sleep(struct dvb_frontend* fe)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	stv0299_writeregI(state, 0x02, 0x80);
+	state->initialised = 0;
+
+	return 0;
+}
+
+static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	fesettings->min_delay_ms = state->config->min_delay_ms;
+	if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) {
+		fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000;
+		fesettings->max_drift = 5000;
+	} else {
+		fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000;
+		fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000;
+	}
+	return 0;
+}
+
+static void stv0299_release(struct dvb_frontend* fe)
+{
+	struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops stv0299_ops;
+
+struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct stv0299_state* state = NULL;
+	int id;
+
+	/* allocate memory for the internal state */
+	state = (struct stv0299_state*) kmalloc(sizeof(struct stv0299_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &stv0299_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+	state->tuner_frequency = 0;
+	state->symbol_rate = 0;
+	state->fec_inner = 0;
+	state->errmode = STATUS_BER;
+
+	/* check if the demod is there */
+	stv0299_writeregI(state, 0x02, 0x34); /* standby off */
+	msleep(200);
+	id = stv0299_readreg(state, 0x00);
+
+	/* register 0x00 contains 0xa1 for STV0299 and STV0299B */
+	/* register 0x00 might contain 0x80 when returning from standby */
+	if (id != 0xa1 && id != 0x80) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+        state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops stv0299_ops = {
+
+	.info = {
+		.name			= "ST STV0299 DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 125,	 /* kHz for QPSK frontends */
+		.frequency_tolerance	= 0,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+		.symbol_rate_tolerance	= 500,	/* ppm */
+		.caps = 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_QPSK |
+		      FE_CAN_FEC_AUTO
+	},
+
+	.release = stv0299_release,
+
+	.init = stv0299_init,
+	.sleep = stv0299_sleep,
+
+	.set_frontend = stv0299_set_frontend,
+	.get_frontend = stv0299_get_frontend,
+	.get_tune_settings = stv0299_get_tune_settings,
+
+	.read_status = stv0299_read_status,
+	.read_ber = stv0299_read_ber,
+	.read_signal_strength = stv0299_read_signal_strength,
+	.read_snr = stv0299_read_snr,
+	.read_ucblocks = stv0299_read_ucblocks,
+
+	.diseqc_send_master_cmd = stv0299_send_diseqc_msg,
+	.diseqc_send_burst = stv0299_send_diseqc_burst,
+	.set_tone = stv0299_set_tone,
+	.set_voltage = stv0299_set_voltage,
+	.dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, "
+              "Andreas Oberritter, Andrew de Quincey, Kenneth Aafløy");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(stv0299_writereg);
+EXPORT_SYMBOL(stv0299_attach);
diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h
new file mode 100644
index 0000000..79457a8
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0299.h
@@ -0,0 +1,104 @@
+/*
+    Driver for ST STV0299 demodulator
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	<ralph@convergence.de>,
+	<holger@convergence.de>,
+	<js@convergence.de>
+
+
+    Philips SU1278/SH
+
+    Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de>
+
+
+    LG TDQF-S001F
+
+    Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net>
+		     & Andreas Oberritter <obi@linuxtv.org>
+
+
+    Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B
+
+    Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>:
+
+    Support for Philips SU1278 on Technotrend hardware
+
+    Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+
+    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 STV0299_H
+#define STV0299_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#define STV0229_LOCKOUTPUT_0  0
+#define STV0229_LOCKOUTPUT_1  1
+#define STV0229_LOCKOUTPUT_CF 2
+#define STV0229_LOCKOUTPUT_LK 3
+
+#define STV0299_VOLT13_OP0 0
+#define STV0299_VOLT13_OP1 1
+
+struct stv0299_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* inittab - array of pairs of values.
+	 * First of each pair is the register, second is the value.
+	 * List should be terminated with an 0xff, 0xff pair.
+	 */
+	u8* inittab;
+
+	/* master clock to use */
+	u32 mclk;
+
+	/* does the inversion require inversion? */
+	u8 invert:1;
+
+	/* Should the enhanced tuning code be used? */
+	u8 enhanced_tuning:1;
+
+	/* Skip reinitialisation? */
+	u8 skip_reinit:1;
+
+	/* LOCK OUTPUT setting */
+	u8 lock_output:2;
+
+	/* Is 13v controlled by OP0 or OP1? */
+	u8 volt13_op0_op1:1;
+
+	/* minimum delay before retuning */
+	int min_delay_ms;
+
+	/* Set the symbol rate */
+	int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data);
+
+extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // STV0299_H
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
new file mode 100644
index 0000000..4e40d95
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10021.c
@@ -0,0 +1,469 @@
+/*
+    TDA10021  - Single Chip Cable Channel Receiver driver module
+               used on the the Siemens DVB-C cards
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+                   Support for TDA10021
+
+    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/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda10021.h"
+
+
+struct tda10021_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	/* configuration settings */
+	const struct tda10021_config* config;
+	struct dvb_frontend frontend;
+
+	u8 pwm;
+	u8 reg0;
+};
+
+
+#if 0
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+static int verbose;
+
+#define XIN 57840000UL
+#define DISABLE_INVERSION(reg0)		do { reg0 |= 0x20; } while (0)
+#define ENABLE_INVERSION(reg0)		do { reg0 &= ~0x20; } while (0)
+#define HAS_INVERSION(reg0)		(!(reg0 & 0x20))
+
+#define FIN (XIN >> 4)
+
+static int tda10021_inittab_size = 0x40;
+static u8 tda10021_inittab[0x40]=
+{
+	0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
+	0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
+	0xb8, 0x3f, 0xa0, 0x00, 0xcd, 0x01, 0x00, 0xff,
+	0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
+	0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
+	0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
+};
+
+static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
+{
+        u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+        int ret;
+
+	ret = i2c_transfer (state->i2c, &msg, 1);
+	if (ret != 1)
+		printk("DVB: TDA10021(%d): %s, writereg error "
+			"(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+			state->frontend.dvb->num, __FUNCTION__, reg, data, ret);
+
+	msleep(10);
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
+{
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+	                          { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+	int ret;
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+	if (ret != 2)
+		printk("DVB: TDA10021(%d): %s: readreg error (ret == %i)\n",
+				state->frontend.dvb->num, __FUNCTION__, ret);
+	return b1[0];
+}
+
+//get access to tuner
+static int lock_tuner(struct tda10021_state* state)
+{
+	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
+	struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+	if(i2c_transfer(state->i2c, &msg, 1) != 1)
+	{
+		printk("tda10021: lock tuner fails\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+//release access from tuner
+static int unlock_tuner(struct tda10021_state* state)
+{
+	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
+	struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+	if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
+	{
+		printk("tda10021: unlock tuner fails\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0,
+				fe_spectral_inversion_t inversion)
+{
+	reg0 |= state->reg0 & 0x63;
+
+	if (INVERSION_ON == inversion)
+		ENABLE_INVERSION(reg0);
+	else if (INVERSION_OFF == inversion)
+		DISABLE_INVERSION(reg0);
+
+	tda10021_writereg (state, 0x00, reg0 & 0xfe);
+	tda10021_writereg (state, 0x00, reg0 | 0x01);
+
+	state->reg0 = reg0;
+	return 0;
+}
+
+static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate)
+{
+	s32 BDR;
+	s32 BDRI;
+	s16 SFIL=0;
+	u16 NDEC = 0;
+	u32 tmp, ratio;
+
+	if (symbolrate > XIN/2)
+		symbolrate = XIN/2;
+	if (symbolrate < 500000)
+		symbolrate = 500000;
+
+	if (symbolrate < XIN/16) NDEC = 1;
+	if (symbolrate < XIN/32) NDEC = 2;
+	if (symbolrate < XIN/64) NDEC = 3;
+
+	if (symbolrate < (u32)(XIN/12.3)) SFIL = 1;
+	if (symbolrate < (u32)(XIN/16))	 SFIL = 0;
+	if (symbolrate < (u32)(XIN/24.6)) SFIL = 1;
+	if (symbolrate < (u32)(XIN/32))	 SFIL = 0;
+	if (symbolrate < (u32)(XIN/49.2)) SFIL = 1;
+	if (symbolrate < (u32)(XIN/64))	 SFIL = 0;
+	if (symbolrate < (u32)(XIN/98.4)) SFIL = 1;
+
+	symbolrate <<= NDEC;
+	ratio = (symbolrate << 4) / FIN;
+	tmp =  ((symbolrate << 4) % FIN) << 8;
+	ratio = (ratio << 8) + tmp / FIN;
+	tmp = (tmp % FIN) << 8;
+	ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
+
+	BDR = ratio;
+	BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
+
+	if (BDRI > 0xFF)
+		BDRI = 0xFF;
+
+	SFIL = (SFIL << 4) | tda10021_inittab[0x0E];
+
+	NDEC = (NDEC << 6) | tda10021_inittab[0x03];
+
+	tda10021_writereg (state, 0x03, NDEC);
+	tda10021_writereg (state, 0x0a, BDR&0xff);
+	tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
+	tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
+
+	tda10021_writereg (state, 0x0d, BDRI);
+	tda10021_writereg (state, 0x0e, SFIL);
+
+	return 0;
+}
+
+static int tda10021_init (struct dvb_frontend *fe)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	int i;
+
+	dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
+
+	//tda10021_writereg (fe, 0, 0);
+
+	for (i=0; i<tda10021_inittab_size; i++)
+		tda10021_writereg (state, i, tda10021_inittab[i]);
+
+	tda10021_writereg (state, 0x34, state->pwm);
+
+	//Comment by markus
+	//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
+	//0x2A[4] == BYPPLL -> Power down mode (default 1)
+	//0x2A[5] == LCK -> PLL Lock Flag
+	//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
+
+	//Activate PLL
+	tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
+
+	if (state->config->pll_init) {
+		lock_tuner(state);
+		state->config->pll_init(fe);
+		unlock_tuner(state);
+	}
+
+	return 0;
+}
+
+static int tda10021_set_parameters (struct dvb_frontend *fe,
+			    struct dvb_frontend_parameters *p)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	//table for QAM4-QAM256 ready  QAM4  QAM16 QAM32 QAM64 QAM128 QAM256
+	//CONF
+	static const u8 reg0x00 [] = { 0x14, 0x00, 0x04, 0x08, 0x0c,  0x10 };
+	//AGCREF value
+	static const u8 reg0x01 [] = { 0x78, 0x8c, 0x8c, 0x6a, 0x78,  0x5c };
+	//LTHR value
+	static const u8 reg0x05 [] = { 0x78, 0x87, 0x64, 0x46, 0x36,  0x26 };
+	//MSETH
+	static const u8 reg0x08 [] = { 0x8c, 0xa2, 0x74, 0x43, 0x34,  0x23 };
+	//AREF
+	static const u8 reg0x09 [] = { 0x96, 0x91, 0x96, 0x6a, 0x7e,  0x6b };
+
+	int qam = p->u.qam.modulation;
+
+	if (qam < 0 || qam > 5)
+		return -EINVAL;
+
+	//printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate);
+
+	lock_tuner(state);
+	state->config->pll_set(fe, p);
+	unlock_tuner(state);
+
+	tda10021_set_symbolrate (state, p->u.qam.symbol_rate);
+	tda10021_writereg (state, 0x34, state->pwm);
+
+	tda10021_writereg (state, 0x01, reg0x01[qam]);
+	tda10021_writereg (state, 0x05, reg0x05[qam]);
+	tda10021_writereg (state, 0x08, reg0x08[qam]);
+	tda10021_writereg (state, 0x09, reg0x09[qam]);
+
+	tda10021_setup_reg0 (state, reg0x00[qam], p->inversion);
+
+	return 0;
+}
+
+static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	int sync;
+
+	*status = 0;
+	//0x11[0] == EQALGO -> Equalizer algorithms state
+	//0x11[1] == CARLOCK -> Carrier locked
+	//0x11[2] == FSYNC -> Frame synchronisation
+	//0x11[3] == FEL -> Front End locked
+	//0x11[6] == NODVB -> DVB Mode Information
+	sync = tda10021_readreg (state, 0x11);
+
+	if (sync & 2)
+		*status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
+
+	if (sync & 4)
+		*status |= FE_HAS_SYNC|FE_HAS_VITERBI;
+
+	if (sync & 8)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	u32 _ber = tda10021_readreg(state, 0x14) |
+		(tda10021_readreg(state, 0x15) << 8) |
+		((tda10021_readreg(state, 0x16) & 0x0f) << 16);
+	*ber = 10 * _ber;
+
+	return 0;
+}
+
+static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	u8 gain = tda10021_readreg(state, 0x17);
+	*strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	u8 quality = ~tda10021_readreg(state, 0x18);
+	*snr = (quality << 8) | quality;
+
+	return 0;
+}
+
+static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	*ucblocks = tda10021_readreg (state, 0x13) & 0x7f;
+	if (*ucblocks == 0x7f)
+		*ucblocks = 0xffffffff;
+
+	/* reset uncorrected block counter */
+	tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
+	tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
+
+	return 0;
+}
+
+static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	int sync;
+	s8 afc = 0;
+
+	sync = tda10021_readreg(state, 0x11);
+	afc = tda10021_readreg(state, 0x19);
+	if (verbose) {
+		/* AFC only valid when carrier has been recovered */
+		printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" :
+				  "DVB: TDA10021(%d): [AFC (%d) %dHz]\n",
+			state->frontend.dvb->num, afc,
+		       -((s32)p->u.qam.symbol_rate * afc) >> 10);
+	}
+
+	p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
+	p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+	p->u.qam.fec_inner = FEC_NONE;
+	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+
+	if (sync & 2)
+		p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10;
+
+	return 0;
+}
+
+static int tda10021_sleep(struct dvb_frontend* fe)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	tda10021_writereg (state, 0x1b, 0x02);  /* pdown ADC */
+	tda10021_writereg (state, 0x00, 0x80);  /* standby */
+
+	return 0;
+}
+
+static void tda10021_release(struct dvb_frontend* fe)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda10021_ops;
+
+struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+				     struct i2c_adapter* i2c,
+				     u8 pwm)
+{
+	struct tda10021_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda10021_state*) kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
+	state->pwm = pwm;
+	state->reg0 = tda10021_inittab[0];
+
+	/* check if the demod is there */
+	if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda10021_ops = {
+
+	.info = {
+		.name = "Philips TDA10021 DVB-C",
+		.type = FE_QAM,
+		.frequency_stepsize = 62500,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.symbol_rate_min = (XIN/2)/64,     /* SACLK/64 == (XIN/2)/64 */
+		.symbol_rate_max = (XIN/2)/4,      /* SACLK/4 */
+	#if 0
+		.frequency_tolerance = ???,
+		.symbol_rate_tolerance = ???,  /* ppm */  /* == 8% (spec p. 5) */
+	#endif
+		.caps = 0x400 | //FE_CAN_QAM_4
+			FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+			FE_CAN_FEC_AUTO
+	},
+
+	.release = tda10021_release,
+
+	.init = tda10021_init,
+	.sleep = tda10021_sleep,
+
+	.set_frontend = tda10021_set_parameters,
+	.get_frontend = tda10021_get_frontend,
+
+	.read_status = tda10021_read_status,
+	.read_ber = tda10021_read_ber,
+	.read_signal_strength = tda10021_read_signal_strength,
+	.read_snr = tda10021_read_snr,
+	.read_ucblocks = tda10021_read_ucblocks,
+};
+
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
+
+MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10021_attach);
diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h
new file mode 100644
index 0000000..7d6a51c
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10021.h
@@ -0,0 +1,42 @@
+/*
+    TDA10021  - Single Chip Cable Channel Receiver driver module
+               used on the the Siemens DVB-C cards
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+                   Support for TDA10021
+
+    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 TDA10021_H
+#define TDA10021_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda10021_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+					    struct i2c_adapter* i2c, u8 pwm);
+
+#endif // TDA10021_H
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
new file mode 100644
index 0000000..687ad9c
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -0,0 +1,1206 @@
+  /*
+     Driver for Philips tda1004xh OFDM Demodulator
+
+     (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach
+
+     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.
+
+   */
+/*
+ * This driver needs external firmware. Please use the commands
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ * download/extract them, and then copy them to /usr/lib/hotplug/firmware.
+ */
+#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw"
+#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include "dvb_frontend.h"
+#include "tda1004x.h"
+
+#define TDA1004X_DEMOD_TDA10045 0
+#define TDA1004X_DEMOD_TDA10046 1
+
+
+struct tda1004x_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	const struct tda1004x_config* config;
+	struct dvb_frontend frontend;
+
+	/* private demod data */
+	u8 initialised:1;
+	u8 demod_type;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "tda1004x: " args); \
+	} while (0)
+
+#define TDA1004X_CHIPID		 0x00
+#define TDA1004X_AUTO		 0x01
+#define TDA1004X_IN_CONF1	 0x02
+#define TDA1004X_IN_CONF2	 0x03
+#define TDA1004X_OUT_CONF1	 0x04
+#define TDA1004X_OUT_CONF2	 0x05
+#define TDA1004X_STATUS_CD	 0x06
+#define TDA1004X_CONFC4		 0x07
+#define TDA1004X_DSSPARE2	 0x0C
+#define TDA10045H_CODE_IN	 0x0D
+#define TDA10045H_FWPAGE	 0x0E
+#define TDA1004X_SCAN_CPT	 0x10
+#define TDA1004X_DSP_CMD	 0x11
+#define TDA1004X_DSP_ARG	 0x12
+#define TDA1004X_DSP_DATA1	 0x13
+#define TDA1004X_DSP_DATA2	 0x14
+#define TDA1004X_CONFADC1	 0x15
+#define TDA1004X_CONFC1		 0x16
+#define TDA10045H_S_AGC		 0x1a
+#define TDA10046H_AGC_TUN_LEVEL	 0x1a
+#define TDA1004X_SNR		 0x1c
+#define TDA1004X_CONF_TS1	 0x1e
+#define TDA1004X_CONF_TS2	 0x1f
+#define TDA1004X_CBER_RESET	 0x20
+#define TDA1004X_CBER_MSB	 0x21
+#define TDA1004X_CBER_LSB	 0x22
+#define TDA1004X_CVBER_LUT	 0x23
+#define TDA1004X_VBER_MSB	 0x24
+#define TDA1004X_VBER_MID	 0x25
+#define TDA1004X_VBER_LSB	 0x26
+#define TDA1004X_UNCOR		 0x27
+
+#define TDA10045H_CONFPLL_P	 0x2D
+#define TDA10045H_CONFPLL_M_MSB	 0x2E
+#define TDA10045H_CONFPLL_M_LSB	 0x2F
+#define TDA10045H_CONFPLL_N	 0x30
+
+#define TDA10046H_CONFPLL1	 0x2D
+#define TDA10046H_CONFPLL2	 0x2F
+#define TDA10046H_CONFPLL3	 0x30
+#define TDA10046H_TIME_WREF1	 0x31
+#define TDA10046H_TIME_WREF2	 0x32
+#define TDA10046H_TIME_WREF3	 0x33
+#define TDA10046H_TIME_WREF4	 0x34
+#define TDA10046H_TIME_WREF5	 0x35
+
+#define TDA10045H_UNSURW_MSB	 0x31
+#define TDA10045H_UNSURW_LSB	 0x32
+#define TDA10045H_WREF_MSB	 0x33
+#define TDA10045H_WREF_MID	 0x34
+#define TDA10045H_WREF_LSB	 0x35
+#define TDA10045H_MUXOUT	 0x36
+#define TDA1004X_CONFADC2	 0x37
+
+#define TDA10045H_IOFFSET	 0x38
+
+#define TDA10046H_CONF_TRISTATE1 0x3B
+#define TDA10046H_CONF_TRISTATE2 0x3C
+#define TDA10046H_CONF_POLARITY	 0x3D
+#define TDA10046H_FREQ_OFFSET	 0x3E
+#define TDA10046H_GPIO_OUT_SEL	 0x41
+#define TDA10046H_GPIO_SELECT	 0x42
+#define TDA10046H_AGC_CONF	 0x43
+#define TDA10046H_AGC_GAINS	 0x46
+#define TDA10046H_AGC_TUN_MIN	 0x47
+#define TDA10046H_AGC_TUN_MAX	 0x48
+#define TDA10046H_AGC_IF_MIN	 0x49
+#define TDA10046H_AGC_IF_MAX	 0x4A
+
+#define TDA10046H_FREQ_PHY2_MSB	 0x4D
+#define TDA10046H_FREQ_PHY2_LSB	 0x4E
+
+#define TDA10046H_CVBER_CTRL	 0x4F
+#define TDA10046H_AGC_IF_LEVEL	 0x52
+#define TDA10046H_CODE_CPT	 0x57
+#define TDA10046H_CODE_IN	 0x58
+
+
+static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data)
+{
+	int ret;
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr=0, .flags=0, .buf=buf, .len=2 };
+
+	dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data);
+
+	msg.addr = state->config->demod_address;
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
+			__FUNCTION__, reg, data, ret);
+
+	dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,
+		reg, data, ret);
+	return (ret != 1) ? -1 : 0;
+}
+
+static int tda1004x_read_byte(struct tda1004x_state *state, int reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {{ .addr=0, .flags=0, .buf=b0, .len=1},
+				{ .addr=0, .flags=I2C_M_RD, .buf=b1, .len = 1}};
+
+	dprintk("%s: reg=0x%x\n", __FUNCTION__, reg);
+
+	msg[0].addr = state->config->demod_address;
+	msg[1].addr = state->config->demod_address;
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg,
+			ret);
+		return -1;
+	}
+
+	dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,
+		reg, b1[0], ret);
+	return b1[0];
+}
+
+static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data)
+{
+	int val;
+	dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg,
+		mask, data);
+
+	// read a byte and check
+	val = tda1004x_read_byte(state, reg);
+	if (val < 0)
+		return val;
+
+	// mask if off
+	val = val & ~mask;
+	val |= data & 0xff;
+
+	// write it out again
+	return tda1004x_write_byteI(state, reg, val);
+}
+
+static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len)
+{
+	int i;
+	int result;
+
+	dprintk("%s: reg=0x%x, len=0x%x\n", __FUNCTION__, reg, len);
+
+	result = 0;
+	for (i = 0; i < len; i++) {
+		result = tda1004x_write_byteI(state, reg + i, buf[i]);
+		if (result != 0)
+			break;
+	}
+
+	return result;
+}
+
+static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state)
+{
+	int result;
+	dprintk("%s\n", __FUNCTION__);
+
+	result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2);
+	msleep(1);
+	return result;
+}
+
+static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0);
+}
+
+static int tda10045h_set_bandwidth(struct tda1004x_state *state,
+				   fe_bandwidth_t bandwidth)
+{
+	static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f };
+	static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb };
+	static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 };
+
+	switch (bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0);
+
+	return 0;
+}
+
+static int tda10046h_set_bandwidth(struct tda1004x_state *state,
+				   fe_bandwidth_t bandwidth)
+{
+	static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e };
+	static u8 bandwidth_7mhz[] = { 0x6e, 0x02, 0x53, 0xc8, 0x25 };
+	static u8 bandwidth_8mhz[] = { 0x60, 0x12, 0xa8, 0xe4, 0xbd };
+
+	switch (bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tda1004x_do_upload(struct tda1004x_state *state,
+			      unsigned char *mem, unsigned int len,
+			      u8 dspCodeCounterReg, u8 dspCodeInReg)
+{
+	u8 buf[65];
+	struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 };
+	int tx_size;
+	int pos = 0;
+
+	/* clear code counter */
+	tda1004x_write_byteI(state, dspCodeCounterReg, 0);
+	fw_msg.addr = state->config->demod_address;
+
+	buf[0] = dspCodeInReg;
+	while (pos != len) {
+
+		// work out how much to send this time
+		tx_size = len - pos;
+		if (tx_size > 0x10) {
+			tx_size = 0x10;
+		}
+
+		// send the chunk
+		memcpy(buf + 1, mem + pos, tx_size);
+		fw_msg.len = tx_size + 1;
+		if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
+			printk("tda1004x: Error during firmware upload\n");
+			return -EIO;
+		}
+		pos += tx_size;
+
+		dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos);
+	}
+	return 0;
+}
+
+static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion)
+{
+	u8 data1, data2;
+
+	// check upload was OK
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
+	tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67);
+
+	data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);
+	data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);
+	if (data1 != 0x67 || data2 != dspVersion) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int tda10045_fwupload(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int ret;
+	const struct firmware *fw;
+
+
+	/* don't re-upload unless necessary */
+	if (tda1004x_check_upload_ok(state, 0x2c) == 0) return 0;
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE);
+	ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
+	if (ret) {
+		printk("tda1004x: no firmware upload (timeout or file not found?)\n");
+		return ret;
+	}
+
+	/* reset chip */
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+	msleep(10);
+
+	/* set parameters */
+	tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ);
+
+	ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);
+	if (ret)
+		return ret;
+	printk("tda1004x: firmware upload complete\n");
+
+	/* wait for DSP to initialise */
+	/* DSPREADY doesn't seem to work on the TDA10045H */
+	msleep(100);
+
+	return tda1004x_check_upload_ok(state, 0x2c);
+}
+
+static int tda10046_fwupload(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	unsigned long timeout;
+	int ret;
+	const struct firmware *fw;
+
+	/* reset + wake up chip */
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0);
+	tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
+	msleep(100);
+
+	/* don't re-upload unless necessary */
+	if (tda1004x_check_upload_ok(state, 0x20) == 0) return 0;
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("tda1004x: waiting for firmware upload (%s)...\n", TDA10046_DEFAULT_FIRMWARE);
+	ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
+	if (ret) {
+		printk("tda1004x: no firmware upload (timeout or file not found?)\n");
+		return ret;
+	}
+
+	/* set parameters */
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10);
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0);
+	tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99);
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+
+	ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
+	if (ret)
+		return ret;
+	printk("tda1004x: firmware upload complete\n");
+
+	/* wait for DSP to initialise */
+	timeout = jiffies + HZ;
+	while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
+		if (time_after(jiffies, timeout)) {
+			printk("tda1004x: DSP failed to initialised.\n");
+			return -EIO;
+		}
+		msleep(1);
+	}
+
+	return tda1004x_check_upload_ok(state, 0x20);
+}
+
+static int tda1004x_encode_fec(int fec)
+{
+	// convert known FEC values
+	switch (fec) {
+	case FEC_1_2:
+		return 0;
+	case FEC_2_3:
+		return 1;
+	case FEC_3_4:
+		return 2;
+	case FEC_5_6:
+		return 3;
+	case FEC_7_8:
+		return 4;
+	}
+
+	// unsupported
+	return -EINVAL;
+}
+
+static int tda1004x_decode_fec(int tdafec)
+{
+	// convert known FEC values
+	switch (tdafec) {
+	case 0:
+		return FEC_1_2;
+	case 1:
+		return FEC_2_3;
+	case 2:
+		return FEC_3_4;
+	case 3:
+		return FEC_5_6;
+	case 4:
+		return FEC_7_8;
+	}
+
+	// unsupported
+	return -1;
+}
+
+int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	return tda1004x_write_byteI(state, reg, data);
+}
+
+static int tda10045_init(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (state->initialised) return 0;
+
+	if (tda10045_fwupload(fe)) {
+		printk("tda1004x: firmware upload failed\n");
+		return -EIO;
+	}
+
+	tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
+
+	// Init the PLL
+	if (state->config->pll_init) {
+		tda1004x_enable_tuner_i2c(state);
+		state->config->pll_init(fe);
+		tda1004x_disable_tuner_i2c(state);
+	}
+
+	// tda setup
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+	tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
+	tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
+	tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
+	tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
+	tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e);
+
+	tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk);
+
+	state->initialised = 1;
+	return 0;
+}
+
+static int tda10046_init(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	dprintk("%s\n", __FUNCTION__);
+
+	if (state->initialised) return 0;
+
+	if (tda10046_fwupload(fe)) {
+		printk("tda1004x: firmware upload failed\n");
+		return -EIO;
+	}
+
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); // wake up the chip
+
+	// Init the PLL
+	if (state->config->pll_init) {
+		tda1004x_enable_tuner_i2c(state);
+		state->config->pll_init(fe);
+		tda1004x_disable_tuner_i2c(state);
+	}
+
+	// tda setup
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0x40);
+	tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); // PLL M = 10
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
+	tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0); // AGC setup
+	tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities
+	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0);	  // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
+	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0);	  // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff);  // }
+	tda1004x_write_mask(state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits
+	tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1
+	tda1004x_write_mask(state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
+	tda1004x_write_mask(state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config
+	tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN
+	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup
+	tda1004x_write_byteI(state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config
+	tda1004x_write_mask(state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select
+	tda10046h_set_bandwidth(state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
+
+	tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
+
+	state->initialised = 1;
+	return 0;
+}
+
+static int tda1004x_set_fe(struct dvb_frontend* fe,
+			   struct dvb_frontend_parameters *fe_params)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+	int inversion;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+		// setup auto offset
+		tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0);
+
+		// disable agc_conf[2]
+		tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0);
+	}
+
+	// set frequency
+	tda1004x_enable_tuner_i2c(state);
+	state->config->pll_set(fe, fe_params);
+	tda1004x_disable_tuner_i2c(state);
+
+	if (state->demod_type == TDA1004X_DEMOD_TDA10046)
+		tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 4);
+
+	// Hardcoded to use auto as much as possible on the TDA10045 as it
+	// is very unreliable if AUTO mode is _not_ used.
+	if (state->demod_type == TDA1004X_DEMOD_TDA10045) {
+		fe_params->u.ofdm.code_rate_HP = FEC_AUTO;
+		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+		fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+	}
+
+	// Set standard params.. or put them to auto
+	if ((fe_params->u.ofdm.code_rate_HP == FEC_AUTO) ||
+	    (fe_params->u.ofdm.code_rate_LP == FEC_AUTO) ||
+	    (fe_params->u.ofdm.constellation == QAM_AUTO) ||
+	    (fe_params->u.ofdm.hierarchy_information == HIERARCHY_AUTO)) {
+		tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1);	// enable auto
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0);	// turn off constellation bits
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0);	// turn off hierarchy bits
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0);	// turn off FEC bits
+	} else {
+		tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0);	// disable auto
+
+		// set HP FEC
+		tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_HP);
+		if (tmp < 0) return tmp;
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp);
+
+		// set LP FEC
+		tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_LP);
+		if (tmp < 0) return tmp;
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3);
+
+		// set constellation
+		switch (fe_params->u.ofdm.constellation) {
+		case QPSK:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0);
+			break;
+
+		case QAM_16:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1);
+			break;
+
+		case QAM_64:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+
+		// set hierarchy
+		switch (fe_params->u.ofdm.hierarchy_information) {
+		case HIERARCHY_NONE:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5);
+			break;
+
+		case HIERARCHY_1:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5);
+			break;
+
+		case HIERARCHY_2:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5);
+			break;
+
+		case HIERARCHY_4:
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5);
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	// set bandwidth
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda10045h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		tda10046h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
+		break;
+	}
+
+	// set inversion
+	inversion = fe_params->inversion;
+	if (state->config->invert) inversion = inversion ? INVERSION_OFF : INVERSION_ON;
+	switch (inversion) {
+	case INVERSION_OFF:
+		tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0);
+		break;
+
+	case INVERSION_ON:
+		tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// set guard interval
+	switch (fe_params->u.ofdm.guard_interval) {
+	case GUARD_INTERVAL_1_32:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+		break;
+
+	case GUARD_INTERVAL_1_16:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2);
+		break;
+
+	case GUARD_INTERVAL_1_8:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2);
+		break;
+
+	case GUARD_INTERVAL_1_4:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2);
+		break;
+
+	case GUARD_INTERVAL_AUTO:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// set transmission mode
+	switch (fe_params->u.ofdm.transmission_mode) {
+	case TRANSMISSION_MODE_2K:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4);
+		break;
+
+	case TRANSMISSION_MODE_8K:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4);
+		break;
+
+	case TRANSMISSION_MODE_AUTO:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// start the lock
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+		msleep(10);
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40);
+		msleep(10);
+		break;
+	}
+
+	return 0;
+}
+
+static int tda1004x_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	dprintk("%s\n", __FUNCTION__);
+
+	// inversion status
+	fe_params->inversion = INVERSION_OFF;
+	if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) {
+		fe_params->inversion = INVERSION_ON;
+	}
+	if (state->config->invert) fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
+
+	// bandwidth
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) {
+		case 0x14:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+			break;
+		case 0xdb:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+			break;
+		case 0x4f:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+			break;
+		}
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) {
+		case 0x60:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+			break;
+		case 0x6e:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+			break;
+		case 0x80:
+			fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+			break;
+		}
+		break;
+	}
+
+	// FEC
+	fe_params->u.ofdm.code_rate_HP =
+	    tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7);
+	fe_params->u.ofdm.code_rate_LP =
+	    tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7);
+
+	// constellation
+	switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) {
+	case 0:
+		fe_params->u.ofdm.constellation = QPSK;
+		break;
+	case 1:
+		fe_params->u.ofdm.constellation = QAM_16;
+		break;
+	case 2:
+		fe_params->u.ofdm.constellation = QAM_64;
+		break;
+	}
+
+	// transmission mode
+	fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+	if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) {
+		fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+	}
+
+	// guard interval
+	switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) {
+	case 0:
+		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+		break;
+	case 1:
+		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+		break;
+	case 2:
+		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+		break;
+	case 3:
+		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+		break;
+	}
+
+	// hierarchy
+	switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) {
+	case 0:
+		fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+		break;
+	case 1:
+		fe_params->u.ofdm.hierarchy_information = HIERARCHY_1;
+		break;
+	case 2:
+		fe_params->u.ofdm.hierarchy_information = HIERARCHY_2;
+		break;
+	case 3:
+		fe_params->u.ofdm.hierarchy_information = HIERARCHY_4;
+		break;
+	}
+
+	return 0;
+}
+
+static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int status;
+	int cber;
+	int vber;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	// read status
+	status = tda1004x_read_byte(state, TDA1004X_STATUS_CD);
+	if (status == -1) {
+		return -EIO;
+	}
+
+	// decode
+	*fe_status = 0;
+	if (status & 4) *fe_status |= FE_HAS_SIGNAL;
+	if (status & 2) *fe_status |= FE_HAS_CARRIER;
+	if (status & 8) *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+
+	// if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi
+	// is getting anything valid
+	if (!(*fe_status & FE_HAS_VITERBI)) {
+		// read the CBER
+		cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
+		if (cber == -1) return -EIO;
+		status = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+		if (status == -1) return -EIO;
+		cber |= (status << 8);
+		tda1004x_read_byte(state, TDA1004X_CBER_RESET);
+
+		if (cber != 65535) {
+			*fe_status |= FE_HAS_VITERBI;
+		}
+	}
+
+	// if we DO have some valid VITERBI output, but don't already have SYNC
+	// bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid.
+	if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) {
+		// read the VBER
+		vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB);
+		if (vber == -1) return -EIO;
+		status = tda1004x_read_byte(state, TDA1004X_VBER_MID);
+		if (status == -1) return -EIO;
+		vber |= (status << 8);
+		status = tda1004x_read_byte(state, TDA1004X_VBER_MSB);
+		if (status == -1) return -EIO;
+		vber |= ((status << 16) & 0x0f);
+		tda1004x_read_byte(state, TDA1004X_CVBER_LUT);
+
+		// if RS has passed some valid TS packets, then we must be
+		// getting some SYNC bytes
+		if (vber < 16632) {
+			*fe_status |= FE_HAS_SYNC;
+		}
+	}
+
+	// success
+	dprintk("%s: fe_status=0x%x\n", __FUNCTION__, *fe_status);
+	return 0;
+}
+
+static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+	int reg = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	// determine the register to use
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		reg = TDA10045H_S_AGC;
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		reg = TDA10046H_AGC_IF_LEVEL;
+		break;
+	}
+
+	// read it
+	tmp = tda1004x_read_byte(state, reg);
+	if (tmp < 0)
+		return -EIO;
+
+	*signal = (tmp << 8) | tmp;
+	dprintk("%s: signal=0x%x\n", __FUNCTION__, *signal);
+	return 0;
+}
+
+static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	// read it
+	tmp = tda1004x_read_byte(state, TDA1004X_SNR);
+	if (tmp < 0)
+		return -EIO;
+	if (tmp) {
+		tmp = 255 - tmp;
+	}
+
+	*snr = ((tmp << 8) | tmp);
+	dprintk("%s: snr=0x%x\n", __FUNCTION__, *snr);
+	return 0;
+}
+
+static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+	int tmp2;
+	int counter;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	// read the UCBLOCKS and reset
+	counter = 0;
+	tmp = tda1004x_read_byte(state, TDA1004X_UNCOR);
+	if (tmp < 0)
+		return -EIO;
+	tmp &= 0x7f;
+	while (counter++ < 5) {
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+
+		tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR);
+		if (tmp2 < 0)
+			return -EIO;
+		tmp2 &= 0x7f;
+		if ((tmp2 < tmp) || (tmp2 == 0))
+			break;
+	}
+
+	if (tmp != 0x7f) {
+		*ucblocks = tmp;
+	} else {
+		*ucblocks = 0xffffffff;
+	}
+	dprintk("%s: ucblocks=0x%x\n", __FUNCTION__, *ucblocks);
+	return 0;
+}
+
+static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	int tmp;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	// read it in
+	tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
+	if (tmp < 0) return -EIO;
+	*ber = tmp << 1;
+	tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+	if (tmp < 0) return -EIO;
+	*ber |= (tmp << 9);
+	tda1004x_read_byte(state, TDA1004X_CBER_RESET);
+
+	dprintk("%s: ber=0x%x\n", __FUNCTION__, *ber);
+	return 0;
+}
+
+static int tda1004x_sleep(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10);
+		break;
+
+	case TDA1004X_DEMOD_TDA10046:
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
+		break;
+	}
+	state->initialised = 0;
+
+	return 0;
+}
+
+static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+	fesettings->min_delay_ms = 800;
+	fesettings->step_size = 166667;
+	fesettings->max_drift = 166667*2;
+	return 0;
+}
+
+static void tda1004x_release(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = (struct tda1004x_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda10045_ops;
+
+struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+				     struct i2c_adapter* i2c)
+{
+	struct tda1004x_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+	state->demod_type = TDA1004X_DEMOD_TDA10045;
+
+	/* check if the demod is there */
+	if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda10046_ops;
+
+struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+				     struct i2c_adapter* i2c)
+{
+	struct tda1004x_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+	state->demod_type = TDA1004X_DEMOD_TDA10046;
+
+	/* check if the demod is there */
+	if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda10045_ops = {
+
+	.info = {
+		.name = "Philips TDA10045H DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166667,
+		.caps =
+		    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
+	},
+
+	.release = tda1004x_release,
+
+	.init = tda10045_init,
+	.sleep = tda1004x_sleep,
+
+	.set_frontend = tda1004x_set_fe,
+	.get_frontend = tda1004x_get_fe,
+	.get_tune_settings = tda1004x_get_tune_settings,
+
+	.read_status = tda1004x_read_status,
+	.read_ber = tda1004x_read_ber,
+	.read_signal_strength = tda1004x_read_signal_strength,
+	.read_snr = tda1004x_read_snr,
+	.read_ucblocks = tda1004x_read_ucblocks,
+};
+
+static struct dvb_frontend_ops tda10046_ops = {
+
+	.info = {
+		.name = "Philips TDA10046H DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166667,
+		.caps =
+		    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
+	},
+
+	.release = tda1004x_release,
+
+	.init = tda10046_init,
+	.sleep = tda1004x_sleep,
+
+	.set_frontend = tda1004x_set_fe,
+	.get_frontend = tda1004x_get_fe,
+	.get_tune_settings = tda1004x_get_tune_settings,
+
+	.read_status = tda1004x_read_status,
+	.read_ber = tda1004x_read_ber,
+	.read_signal_strength = tda1004x_read_signal_strength,
+	.read_snr = tda1004x_read_snr,
+	.read_ucblocks = tda1004x_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator");
+MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10045_attach);
+EXPORT_SYMBOL(tda10046_attach);
+EXPORT_SYMBOL(tda1004x_write_byte);
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
new file mode 100644
index 0000000..e452fc0
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -0,0 +1,56 @@
+  /*
+     Driver for Philips tda1004xh OFDM Frontend
+
+     (c) 2004 Andrew de Quincey
+
+     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 TDA1004X_H
+#define TDA1004X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct tda1004x_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* does the "inversion" need inverted? */
+	u8 invert:1;
+
+	/* Does the OCLK signal need inverted? */
+	u8 invert_oclk:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+	/* request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+					    struct i2c_adapter* i2c);
+
+extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+					    struct i2c_adapter* i2c);
+
+extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data);
+
+#endif // TDA1004X_H
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c
new file mode 100644
index 0000000..da82e90
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8083.c
@@ -0,0 +1,456 @@
+/*
+    Driver for Philips TDA8083 based QPSK Demodulator
+
+    Copyright (C) 2001 Convergence Integrated Media GmbH
+
+    written by Ralph Metzler <ralph@convergence.de>
+
+    adoption to the new DVB frontend API and diagnostic ioctl's
+    by Holger Waechtler <holger@convergence.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; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "tda8083.h"
+
+
+struct tda8083_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	/* configuration settings */
+	const struct tda8083_config* config;
+	struct dvb_frontend frontend;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "tda8083: " args); \
+	} while (0)
+
+
+static u8 tda8083_init_tab [] = {
+	0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+	0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+	0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+	0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+	0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+
+static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+        ret = i2c_transfer(state->i2c, &msg, 1);
+
+        if (ret != 1)
+                dprintk ("%s: writereg error (reg %02x, ret == %i)\n",
+			__FUNCTION__, reg, ret);
+
+        return (ret != 1) ? -1 : 0;
+}
+
+static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len)
+{
+	int ret;
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk ("%s: readreg error (reg %02x, ret == %i)\n",
+			__FUNCTION__, reg1, ret);
+
+        return ret == 2 ? 0 : -1;
+}
+
+static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg)
+{
+	u8 val;
+
+	tda8083_readregs (state, reg, &val, 1);
+
+	return val;
+}
+
+static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion)
+{
+	/*  XXX FIXME: implement other modes than FEC_AUTO */
+	if (inversion == INVERSION_AUTO)
+		return 0;
+
+	return -EINVAL;
+}
+
+static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec)
+{
+	if (fec == FEC_AUTO)
+		return tda8083_writereg (state, 0x07, 0xff);
+
+	if (fec >= FEC_1_2 && fec <= FEC_8_9)
+		return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec));
+
+	return -EINVAL;
+}
+
+static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state)
+{
+	u8 index;
+	static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+				       FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 };
+
+	index = tda8083_readreg(state, 0x0e) & 0x07;
+
+	return fec_tab [index];
+}
+
+static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate)
+{
+        u32 ratio;
+	u32 tmp;
+	u8 filter;
+
+	if (srate > 32000000)
+                srate = 32000000;
+        if (srate < 500000)
+                srate = 500000;
+
+	filter = 0;
+	if (srate < 24000000)
+		filter = 2;
+	if (srate < 16000000)
+		filter = 3;
+
+	tmp = 31250 << 16;
+	ratio = tmp / srate;
+
+	tmp = (tmp % srate) << 8;
+	ratio = (ratio << 8) + tmp / srate;
+
+	tmp = (tmp % srate) << 8;
+	ratio = (ratio << 8) + tmp / srate;
+
+	dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio);
+
+	tda8083_writereg (state, 0x05, filter);
+	tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff);
+	tda8083_writereg (state, 0x03, (ratio >>  8) & 0xff);
+	tda8083_writereg (state, 0x04, (ratio      ) & 0xff);
+
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 1;
+}
+
+static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout)
+{
+	unsigned long start = jiffies;
+
+	while (jiffies - start < timeout &&
+               !(tda8083_readreg(state, 0x02) & 0x80))
+	{
+		msleep(50);
+	};
+}
+
+static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone)
+{
+	tda8083_writereg (state, 0x26, 0xf1);
+
+	switch (tone) {
+	case SEC_TONE_OFF:
+		return tda8083_writereg (state, 0x29, 0x00);
+	case SEC_TONE_ON:
+		return tda8083_writereg (state, 0x29, 0x80);
+	default:
+		return -EINVAL;
+	};
+}
+
+static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage)
+{
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		return tda8083_writereg (state, 0x20, 0x00);
+	case SEC_VOLTAGE_18:
+		return tda8083_writereg (state, 0x20, 0x11);
+	default:
+		return -EINVAL;
+	};
+}
+
+static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst)
+{
+	switch (burst) {
+	case SEC_MINI_A:
+		tda8083_writereg (state, 0x29, (5 << 2));  /* send burst A */
+		break;
+	case SEC_MINI_B:
+		tda8083_writereg (state, 0x29, (7 << 2));  /* send B */
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	tda8083_wait_diseqc_fifo (state, 100);
+
+	return 0;
+}
+
+static int tda8083_send_diseqc_msg (struct dvb_frontend* fe,
+				    struct dvb_diseqc_master_cmd *m)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+	int i;
+
+	tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */
+
+	for (i=0; i<m->msg_len; i++)
+		tda8083_writereg (state, 0x23 + i, m->msg[i]);
+
+	tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */
+
+	tda8083_wait_diseqc_fifo (state, 100);
+
+	return 0;
+}
+
+static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	u8 signal = ~tda8083_readreg (state, 0x01);
+	u8 sync = tda8083_readreg (state, 0x02);
+
+	*status = 0;
+
+	if (signal > 10)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x01)
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 0x02)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_SYNC;
+
+	if ((sync & 0x1f) == 0x1f)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	u8 signal = ~tda8083_readreg (state, 0x01);
+	*strength = (signal << 8) | signal;
+
+	return 0;
+}
+
+static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	u8 _snr = tda8083_readreg (state, 0x08);
+	*snr = (_snr << 8) | _snr;
+
+	return 0;
+}
+
+static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	state->config->pll_set(fe, p);
+	tda8083_set_inversion (state, p->inversion);
+	tda8083_set_fec (state, p->u.qpsk.fec_inner);
+	tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate);
+
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	/*  FIXME: get symbolrate & frequency offset...*/
+	/*p->frequency = ???;*/
+	p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ?
+			INVERSION_ON : INVERSION_OFF;
+	p->u.qpsk.fec_inner = tda8083_get_fec (state);
+	/*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (state);*/
+
+	return 0;
+}
+
+static int tda8083_sleep(struct dvb_frontend* fe)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_writereg (state, 0x00, 0x02);
+	return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+	int i;
+
+	for (i=0; i<44; i++)
+		tda8083_writereg (state, i, tda8083_init_tab[i]);
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_send_diseqc_burst (state, burst);
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_set_tone (state, tone);
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_set_voltage (state, voltage);
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static void tda8083_release(struct dvb_frontend* fe)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda8083_ops;
+
+struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct tda8083_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda8083_state*) kmalloc(sizeof(struct tda8083_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda8083_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if ((tda8083_readreg(state, 0x00)) != 0x05) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda8083_ops = {
+
+	.info = {
+		.name			= "Philips TDA8083 DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,     /* FIXME: guessed! */
+		.frequency_max		= 1400000,    /* FIXME: guessed! */
+		.frequency_stepsize	= 125,   /* kHz for QPSK frontends */
+	/*      .frequency_tolerance	= ???,*/
+		.symbol_rate_min	= 1000000,   /* FIXME: guessed! */
+		.symbol_rate_max	= 45000000,  /* FIXME: guessed! */
+	/*      .symbol_rate_tolerance	= ???,*/
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_MUTE_TS
+	},
+
+	.release = tda8083_release,
+
+	.init = tda8083_init,
+	.sleep = tda8083_sleep,
+
+	.set_frontend = tda8083_set_frontend,
+	.get_frontend = tda8083_get_frontend,
+
+	.read_status = tda8083_read_status,
+	.read_signal_strength = tda8083_read_signal_strength,
+	.read_snr = tda8083_read_snr,
+
+	.diseqc_send_master_cmd = tda8083_send_diseqc_msg,
+	.diseqc_send_burst = tda8083_diseqc_send_burst,
+	.set_tone = tda8083_diseqc_set_tone,
+	.set_voltage = tda8083_diseqc_set_voltage,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda8083_attach);
diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h
new file mode 100644
index 0000000..4666633
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8083.h
@@ -0,0 +1,45 @@
+/*
+    Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend
+
+    Copyright (C) 2001 Convergence Integrated Media GmbH
+
+    written by Ralph Metzler <ralph@convergence.de>
+
+    adoption to the new DVB frontend API and diagnostic ioctl's
+    by Holger Waechtler <holger@convergence.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; 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 TDA8083_H
+#define TDA8083_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda8083_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // TDA8083_H
diff --git a/drivers/media/dvb/frontends/tda80xx.c b/drivers/media/dvb/frontends/tda80xx.c
new file mode 100644
index 0000000..c996321
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda80xx.c
@@ -0,0 +1,734 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@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; 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/config.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "tda80xx.h"
+
+enum {
+	ID_TDA8044 = 0x04,
+	ID_TDA8083 = 0x05,
+};
+
+
+struct tda80xx_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct tda80xx_config* config;
+
+	struct dvb_frontend frontend;
+
+	u32 clk;
+	int afc_loop;
+	struct work_struct worklet;
+	fe_code_rate_t code_rate;
+	fe_spectral_inversion_t spectral_inversion;
+	fe_status_t status;
+	u8 id;
+};
+
+static int debug = 1;
+#define dprintk	if (debug) printk
+
+static u8 tda8044_inittab_pre[] = {
+	0x02, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+	0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x58,
+	0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+	0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x00,
+	0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00
+};
+
+static u8 tda8044_inittab_post[] = {
+	0x04, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+	0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x50,
+	0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+	0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x6c,
+	0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00
+};
+
+static u8 tda8083_inittab[] = {
+	0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+	0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+	0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+	0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+	0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+static __inline__ u32 tda80xx_div(u32 a, u32 b)
+{
+	return (a + (b / 2)) / b;
+}
+
+static __inline__ u32 tda80xx_gcd(u32 a, u32 b)
+{
+	u32 r;
+
+	while ((r = a % b)) {
+		a = b;
+		b = r;
+	}
+
+	return b;
+}
+
+static int tda80xx_read(struct tda80xx_state* state, u8 reg, u8 *buf, u8 len)
+{
+	int ret;
+	struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },
+			  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (reg %02x, ret == %i)\n",
+				__FUNCTION__, reg, ret);
+
+	mdelay(10);
+
+	return (ret == 2) ? 0 : -EREMOTEIO;
+}
+
+static int tda80xx_write(struct tda80xx_state* state, u8 reg, const u8 *buf, u8 len)
+{
+	int ret;
+	u8 wbuf[len + 1];
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = wbuf, .len = len + 1 };
+
+	wbuf[0] = reg;
+	memcpy(&wbuf[1], buf, len);
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: i2c xfer error (ret == %i)\n", __FUNCTION__, ret);
+
+	mdelay(10);
+
+	return (ret == 1) ? 0 : -EREMOTEIO;
+}
+
+static __inline__ u8 tda80xx_readreg(struct tda80xx_state* state, u8 reg)
+{
+	u8 val;
+
+	tda80xx_read(state, reg, &val, 1);
+
+	return val;
+}
+
+static __inline__ int tda80xx_writereg(struct tda80xx_state* state, u8 reg, u8 data)
+{
+	return tda80xx_write(state, reg, &data, 1);
+}
+
+static int tda80xx_set_parameters(struct tda80xx_state* state,
+				  fe_spectral_inversion_t inversion,
+				  u32 symbol_rate,
+				  fe_code_rate_t fec_inner)
+{
+	u8 buf[15];
+	u64 ratio;
+	u32 clk;
+	u32 k;
+	u32 sr = symbol_rate;
+	u32 gcd;
+	u8 scd;
+
+	if (symbol_rate > (state->clk * 3) / 16)
+		scd = 0;
+	else if (symbol_rate > (state->clk * 3) / 32)
+		scd = 1;
+	else if (symbol_rate > (state->clk * 3) / 64)
+		scd = 2;
+	else
+		scd = 3;
+
+	clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+	/*
+	 * Viterbi decoder:
+	 * Differential decoding off
+	 * Spectral inversion unknown
+	 * QPSK modulation
+	 */
+	if (inversion == INVERSION_ON)
+		buf[0] = 0x60;
+	else if (inversion == INVERSION_OFF)
+		buf[0] = 0x20;
+	else
+		buf[0] = 0x00;
+
+	/*
+	 * CLK ratio:
+	 * system clock frequency is up to 64 or 96 MHz
+	 *
+	 * formula:
+	 * r = k * clk / symbol_rate
+	 *
+	 * k:	2^21 for caa 0..3,
+	 *	2^20 for caa 4..5,
+	 *	2^19 for caa 6..7
+	 */
+	if (symbol_rate <= (clk * 3) / 32)
+		k = (1 << 19);
+	else if (symbol_rate <= (clk * 3) / 16)
+		k = (1 << 20);
+	else
+		k = (1 << 21);
+
+	gcd = tda80xx_gcd(clk, sr);
+	clk /= gcd;
+	sr /= gcd;
+
+	gcd = tda80xx_gcd(k, sr);
+	k /= gcd;
+	sr /= gcd;
+
+	ratio = (u64)k * (u64)clk;
+	do_div(ratio, sr);
+
+	buf[1] = ratio >> 16;
+	buf[2] = ratio >> 8;
+	buf[3] = ratio;
+
+	/* nyquist filter roll-off factor 35% */
+	buf[4] = 0x20;
+
+	clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+	/* Anti Alias Filter */
+	if (symbol_rate < (clk * 3) / 64)
+		printk("tda80xx: unsupported symbol rate: %u\n", symbol_rate);
+	else if (symbol_rate <= clk / 16)
+		buf[4] |= 0x07;
+	else if (symbol_rate <= (clk * 3) / 32)
+		buf[4] |= 0x06;
+	else if (symbol_rate <= clk / 8)
+		buf[4] |= 0x05;
+	else if (symbol_rate <= (clk * 3) / 16)
+		buf[4] |= 0x04;
+	else if (symbol_rate <= clk / 4)
+		buf[4] |= 0x03;
+	else if (symbol_rate <= (clk * 3) / 8)
+		buf[4] |= 0x02;
+	else if (symbol_rate <= clk / 2)
+		buf[4] |= 0x01;
+	else
+		buf[4] |= 0x00;
+
+	/* Sigma Delta converter */
+	buf[5] = 0x00;
+
+	/* FEC: Possible puncturing rates */
+	if (fec_inner == FEC_NONE)
+		buf[6] = 0x00;
+	else if ((fec_inner >= FEC_1_2) && (fec_inner <= FEC_8_9))
+		buf[6] = (1 << (8 - fec_inner));
+	else if (fec_inner == FEC_AUTO)
+		buf[6] = 0xff;
+	else
+		return -EINVAL;
+
+	/* carrier lock detector threshold value */
+	buf[7] = 0x30;
+	/* AFC1: proportional part settings */
+	buf[8] = 0x42;
+	/* AFC1: integral part settings */
+	buf[9] = 0x98;
+	/* PD: Leaky integrator SCPC mode */
+	buf[10] = 0x28;
+	/* AFC2, AFC1 controls */
+	buf[11] = 0x30;
+	/* PD: proportional part settings */
+	buf[12] = 0x42;
+	/* PD: integral part settings */
+	buf[13] = 0x99;
+	/* AGC */
+	buf[14] = 0x50 | scd;
+
+	printk("symbol_rate=%u clk=%u\n", symbol_rate, clk);
+
+	return tda80xx_write(state, 0x01, buf, sizeof(buf));
+}
+
+static int tda80xx_set_clk(struct tda80xx_state* state)
+{
+	u8 buf[2];
+
+	/* CLK proportional part */
+	buf[0] = (0x06 << 5) | 0x08;	/* CMP[2:0], CSP[4:0] */
+	/* CLK integral part */
+	buf[1] = (0x04 << 5) | 0x1a;	/* CMI[2:0], CSI[4:0] */
+
+	return tda80xx_write(state, 0x17, buf, sizeof(buf));
+}
+
+#if 0
+static int tda80xx_set_scpc_freq_offset(struct tda80xx_state* state)
+{
+	/* a constant value is nonsense here imho */
+	return tda80xx_writereg(state, 0x22, 0xf9);
+}
+#endif
+
+static int tda80xx_close_loop(struct tda80xx_state* state)
+{
+	u8 buf[2];
+
+	/* PD: Loop closed, LD: lock detect enable, SCPC: Sweep mode - AFC1 loop closed */
+	buf[0] = 0x68;
+	/* AFC1: Loop closed, CAR Feedback: 8192 */
+	buf[1] = 0x70;
+
+	return tda80xx_write(state, 0x0b, buf, sizeof(buf));
+}
+
+static irqreturn_t tda80xx_irq(int irq, void *priv, struct pt_regs *pt)
+{
+	schedule_work(priv);
+
+	return IRQ_HANDLED;
+}
+
+static void tda80xx_read_status_int(struct tda80xx_state* state)
+{
+	u8 val;
+
+	static const fe_spectral_inversion_t inv_tab[] = {
+		INVERSION_OFF, INVERSION_ON
+	};
+
+	static const fe_code_rate_t fec_tab[] = {
+		FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+		FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8,
+	};
+
+	val = tda80xx_readreg(state, 0x02);
+
+	state->status = 0;
+
+	if (val & 0x01) /* demodulator lock */
+		state->status |= FE_HAS_SIGNAL;
+	if (val & 0x02) /* clock recovery lock */
+		state->status |= FE_HAS_CARRIER;
+	if (val & 0x04) /* viterbi lock */
+		state->status |= FE_HAS_VITERBI;
+	if (val & 0x08) /* deinterleaver lock (packet sync) */
+		state->status |= FE_HAS_SYNC;
+	if (val & 0x10) /* derandomizer lock (frame sync) */
+		state->status |= FE_HAS_LOCK;
+	if (val & 0x20) /* frontend can not lock */
+		state->status |= FE_TIMEDOUT;
+
+	if ((state->status & (FE_HAS_CARRIER)) && (state->afc_loop)) {
+		printk("tda80xx: closing loop\n");
+		tda80xx_close_loop(state);
+		state->afc_loop = 0;
+	}
+
+	if (state->status & (FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK)) {
+		val = tda80xx_readreg(state, 0x0e);
+		state->code_rate = fec_tab[val & 0x07];
+		if (state->status & (FE_HAS_SYNC | FE_HAS_LOCK))
+			state->spectral_inversion = inv_tab[(val >> 7) & 0x01];
+		else
+			state->spectral_inversion = INVERSION_AUTO;
+	}
+	else {
+		state->code_rate = FEC_AUTO;
+	}
+}
+
+static void tda80xx_worklet(void *priv)
+{
+	struct tda80xx_state *state = priv;
+
+	tda80xx_writereg(state, 0x00, 0x04);
+	enable_irq(state->config->irq);
+
+	tda80xx_read_status_int(state);
+}
+
+static void tda80xx_wait_diseqc_fifo(struct tda80xx_state* state)
+{
+	size_t i;
+
+	for (i = 0; i < 100; i++) {
+		if (tda80xx_readreg(state, 0x02) & 0x80)
+			break;
+		msleep(10);
+	}
+}
+
+static int tda8044_init(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+	int ret;
+
+	/*
+	 * this function is a mess...
+	 */
+
+	if ((ret = tda80xx_write(state, 0x00, tda8044_inittab_pre, sizeof(tda8044_inittab_pre))))
+		return ret;
+
+	tda80xx_writereg(state, 0x0f, 0x50);
+#if 1
+	tda80xx_writereg(state, 0x20, 0x8F);		/* FIXME */
+	tda80xx_writereg(state, 0x20, state->config->volt18setting);	/* FIXME */
+	//tda80xx_writereg(state, 0x00, 0x04);
+	tda80xx_writereg(state, 0x00, 0x0C);
+#endif
+	//tda80xx_writereg(state, 0x00, 0x08); /* Reset AFC1 loop filter */
+
+	tda80xx_write(state, 0x00, tda8044_inittab_post, sizeof(tda8044_inittab_post));
+
+	if (state->config->pll_init) {
+		tda80xx_writereg(state, 0x1c, 0x80);
+		state->config->pll_init(fe);
+		tda80xx_writereg(state, 0x1c, 0x00);
+	}
+
+	return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	tda80xx_write(state, 0x00, tda8083_inittab, sizeof(tda8083_inittab));
+
+	if (state->config->pll_init) {
+		tda80xx_writereg(state, 0x1c, 0x80);
+		state->config->pll_init(fe);
+		tda80xx_writereg(state, 0x1c, 0x00);
+	}
+
+	return 0;
+}
+
+static int tda80xx_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		return tda80xx_writereg(state, 0x20, state->config->volt13setting);
+	case SEC_VOLTAGE_18:
+		return tda80xx_writereg(state, 0x20, state->config->volt18setting);
+	case SEC_VOLTAGE_OFF:
+		return tda80xx_writereg(state, 0x20, 0);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int tda80xx_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch (tone) {
+	case SEC_TONE_OFF:
+		return tda80xx_writereg(state, 0x29, 0x00);
+	case SEC_TONE_ON:
+		return tda80xx_writereg(state, 0x29, 0x80);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int tda80xx_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (cmd->msg_len > 6)
+		return -EINVAL;
+
+	tda80xx_writereg(state, 0x29, 0x08 | (cmd->msg_len - 3));
+	tda80xx_write(state, 0x23, cmd->msg, cmd->msg_len);
+	tda80xx_writereg(state, 0x29, 0x0c | (cmd->msg_len - 3));
+	tda80xx_wait_diseqc_fifo(state);
+
+	return 0;
+}
+
+static int tda80xx_send_diseqc_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t cmd)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch (cmd) {
+	case SEC_MINI_A:
+		tda80xx_writereg(state, 0x29, 0x14);
+		break;
+	case SEC_MINI_B:
+		tda80xx_writereg(state, 0x29, 0x1c);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tda80xx_wait_diseqc_fifo(state);
+
+	return 0;
+}
+
+static int tda80xx_sleep(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	tda80xx_writereg(state, 0x00, 0x02);	/* enter standby */
+
+	return 0;
+}
+
+static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	tda80xx_writereg(state, 0x1c, 0x80);
+	state->config->pll_set(fe, p);
+	tda80xx_writereg(state, 0x1c, 0x00);
+
+	tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner);
+	tda80xx_set_clk(state);
+	//tda80xx_set_scpc_freq_offset(state);
+	state->afc_loop = 1;
+
+	return 0;
+}
+
+static int tda80xx_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (!state->config->irq)
+		tda80xx_read_status_int(state);
+
+	p->inversion = state->spectral_inversion;
+	p->u.qpsk.fec_inner = state->code_rate;
+
+	return 0;
+}
+
+static int tda80xx_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (!state->config->irq)
+		tda80xx_read_status_int(state);
+	*status = state->status;
+
+	return 0;
+}
+
+static int tda80xx_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[3];
+
+	if ((ret = tda80xx_read(state, 0x0b, buf, sizeof(buf))))
+		return ret;
+
+	*ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2];
+
+	return 0;
+}
+
+static int tda80xx_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	u8 gain = ~tda80xx_readreg(state, 0x01);
+	*strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int tda80xx_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	u8 quality = tda80xx_readreg(state, 0x08);
+	*snr = (quality << 8) | quality;
+
+	return 0;
+}
+
+static int tda80xx_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	*ucblocks = tda80xx_readreg(state, 0x0f);
+	if (*ucblocks == 0xff)
+		*ucblocks = 0xffffffff;
+
+	return 0;
+}
+
+static int tda80xx_init(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch(state->id) {
+	case ID_TDA8044:
+		return tda8044_init(fe);
+
+	case ID_TDA8083:
+		return tda8083_init(fe);
+	}
+	return 0;
+}
+
+static void tda80xx_release(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (state->config->irq)
+		free_irq(state->config->irq, &state->worklet);
+
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda80xx_ops;
+
+struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct tda80xx_state* state = NULL;
+	int ret;
+
+	/* allocate memory for the internal state */
+	state = (struct tda80xx_state*) kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda80xx_ops, sizeof(struct dvb_frontend_ops));
+	state->spectral_inversion = INVERSION_AUTO;
+	state->code_rate = FEC_AUTO;
+	state->status = 0;
+	state->afc_loop = 0;
+
+	/* check if the demod is there */
+	if (tda80xx_writereg(state, 0x89, 0x00) < 0) goto error;
+	state->id = tda80xx_readreg(state, 0x00);
+
+	switch (state->id) {
+	case ID_TDA8044:
+		state->clk = 96000000;
+		printk("tda80xx: Detected tda8044\n");
+		break;
+
+	case ID_TDA8083:
+		state->clk = 64000000;
+		printk("tda80xx: Detected tda8083\n");
+		break;
+
+	default:
+		goto error;
+	}
+
+	/* setup IRQ */
+	if (state->config->irq) {
+		INIT_WORK(&state->worklet, tda80xx_worklet, state);
+		if ((ret = request_irq(state->config->irq, tda80xx_irq, SA_ONESHOT, "tda80xx", &state->worklet)) < 0) {
+			printk(KERN_ERR "tda80xx: request_irq failed (%d)\n", ret);
+			goto error;
+		}
+	}
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda80xx_ops = {
+
+	.info = {
+		.name = "Philips TDA80xx DVB-S",
+		.type = FE_QPSK,
+		.frequency_min = 500000,
+		.frequency_max = 2700000,
+		.frequency_stepsize = 125,
+		.symbol_rate_min = 4500000,
+		.symbol_rate_max = 45000000,
+		.caps =	FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK |
+			FE_CAN_MUTE_TS
+	},
+
+	.release = tda80xx_release,
+
+	.init = tda80xx_init,
+	.sleep = tda80xx_sleep,
+
+	.set_frontend = tda80xx_set_frontend,
+	.get_frontend = tda80xx_get_frontend,
+
+	.read_status = tda80xx_read_status,
+	.read_ber = tda80xx_read_ber,
+	.read_signal_strength = tda80xx_read_signal_strength,
+	.read_snr = tda80xx_read_snr,
+	.read_ucblocks = tda80xx_read_ucblocks,
+
+	.diseqc_send_master_cmd = tda80xx_send_diseqc_msg,
+	.diseqc_send_burst = tda80xx_send_diseqc_burst,
+	.set_tone = tda80xx_set_tone,
+	.set_voltage = tda80xx_set_voltage,
+};
+
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("Philips TDA8044 / TDA8083 DVB-S Demodulator driver");
+MODULE_AUTHOR("Felix Domke, Andreas Oberritter");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda80xx_attach);
diff --git a/drivers/media/dvb/frontends/tda80xx.h b/drivers/media/dvb/frontends/tda80xx.h
new file mode 100644
index 0000000..cd639a0
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda80xx.h
@@ -0,0 +1,51 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@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; 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 TDA80XX_H
+#define TDA80XX_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda80xx_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* IRQ to use (0=>no IRQ used) */
+	u32 irq;
+
+	/* Register setting to use for 13v */
+	u8 volt13setting;
+
+	/* Register setting to use for 18v */
+	u8 volt18setting;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // TDA80XX_H
diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c
new file mode 100644
index 0000000..9c0d23e
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1820.c
@@ -0,0 +1,450 @@
+/*
+    VES1820  - Single Chip Cable Channel Receiver driver module
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.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; 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/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "ves1820.h"
+
+
+
+struct ves1820_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	/* configuration settings */
+	const struct ves1820_config* config;
+	struct dvb_frontend frontend;
+
+	/* private demodulator data */
+	u8 reg0;
+	u8 pwm;
+};
+
+
+static int verbose;
+
+static u8 ves1820_inittab[] = {
+	0x69, 0x6A, 0x93, 0x12, 0x12, 0x46, 0x26, 0x1A,
+	0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
+	0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x40
+};
+
+static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
+{
+	u8 buf[] = { 0x00, reg, data };
+	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 };
+	int ret;
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		printk("ves1820: %s(): writereg error (reg == 0x%02x,"
+			"val == 0x%02x, ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+	msleep(10);
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static u8 ves1820_readreg(struct ves1820_state *state, u8 reg)
+{
+	u8 b0[] = { 0x00, reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2},
+		{.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
+	};
+	int ret;
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		printk("ves1820: %s(): readreg error (reg == 0x%02x,"
+		"ret == %i)\n", __FUNCTION__, reg, ret);
+
+	return b1[0];
+}
+
+static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion)
+{
+	reg0 |= state->reg0 & 0x62;
+
+	if (INVERSION_ON == inversion) {
+		if (!state->config->invert) reg0 |= 0x20;
+		else reg0 &= ~0x20;
+	} else if (INVERSION_OFF == inversion) {
+		if (!state->config->invert) reg0 &= ~0x20;
+		else reg0 |= 0x20;
+	}
+
+	ves1820_writereg(state, 0x00, reg0 & 0xfe);
+	ves1820_writereg(state, 0x00, reg0 | 0x01);
+
+	state->reg0 = reg0;
+
+	return 0;
+}
+
+static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
+{
+	s32 BDR;
+	s32 BDRI;
+	s16 SFIL = 0;
+	u16 NDEC = 0;
+	u32 ratio;
+	u32 fin;
+	u32 tmp;
+	u64 fptmp;
+	u64 fpxin;
+
+	if (symbolrate > state->config->xin / 2)
+		symbolrate = state->config->xin / 2;
+
+	if (symbolrate < 500000)
+		symbolrate = 500000;
+
+	if (symbolrate < state->config->xin / 16)
+		NDEC = 1;
+	if (symbolrate < state->config->xin / 32)
+		NDEC = 2;
+	if (symbolrate < state->config->xin / 64)
+		NDEC = 3;
+
+	/* yeuch! */
+	fpxin = state->config->xin * 10;
+	fptmp = fpxin; do_div(fptmp, 123);
+	if (symbolrate < fptmp);
+		SFIL = 1;
+	fptmp = fpxin; do_div(fptmp, 160);
+	if (symbolrate < fptmp);
+		SFIL = 0;
+	fptmp = fpxin; do_div(fptmp, 246);
+	if (symbolrate < fptmp);
+		SFIL = 1;
+	fptmp = fpxin; do_div(fptmp, 320);
+	if (symbolrate < fptmp);
+		SFIL = 0;
+	fptmp = fpxin; do_div(fptmp, 492);
+	if (symbolrate < fptmp);
+		SFIL = 1;
+	fptmp = fpxin; do_div(fptmp, 640);
+	if (symbolrate < fptmp);
+		SFIL = 0;
+	fptmp = fpxin; do_div(fptmp, 984);
+	if (symbolrate < fptmp);
+		SFIL = 1;
+
+	fin = state->config->xin >> 4;
+	symbolrate <<= NDEC;
+	ratio = (symbolrate << 4) / fin;
+	tmp = ((symbolrate << 4) % fin) << 8;
+	ratio = (ratio << 8) + tmp / fin;
+	tmp = (tmp % fin) << 8;
+	ratio = (ratio << 8) + (tmp + fin / 2) / fin;
+
+	BDR = ratio;
+	BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
+
+	if (BDRI > 0xFF)
+		BDRI = 0xFF;
+
+	SFIL = (SFIL << 4) | ves1820_inittab[0x0E];
+
+	NDEC = (NDEC << 6) | ves1820_inittab[0x03];
+
+	ves1820_writereg(state, 0x03, NDEC);
+	ves1820_writereg(state, 0x0a, BDR & 0xff);
+	ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff);
+	ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f);
+
+	ves1820_writereg(state, 0x0d, BDRI);
+	ves1820_writereg(state, 0x0e, SFIL);
+
+	return 0;
+}
+
+static int ves1820_init(struct dvb_frontend* fe)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+	int i;
+	int val;
+
+	ves1820_writereg(state, 0, 0);
+
+	for (i = 0; i < 53; i++) {
+		val = ves1820_inittab[i];
+		if ((i == 2) && (state->config->selagc)) val |= 0x08;
+		ves1820_writereg(state, i, val);
+	}
+
+	ves1820_writereg(state, 0x34, state->pwm);
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	return 0;
+}
+
+static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+	static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
+	static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
+	static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
+	static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
+	static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
+	int real_qam = p->u.qam.modulation - QAM_16;
+
+	if (real_qam < 0 || real_qam > 4)
+		return -EINVAL;
+
+	state->config->pll_set(fe, p);
+	ves1820_set_symbolrate(state, p->u.qam.symbol_rate);
+	ves1820_writereg(state, 0x34, state->pwm);
+
+	ves1820_writereg(state, 0x01, reg0x01[real_qam]);
+	ves1820_writereg(state, 0x05, reg0x05[real_qam]);
+	ves1820_writereg(state, 0x08, reg0x08[real_qam]);
+	ves1820_writereg(state, 0x09, reg0x09[real_qam]);
+
+	ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
+
+	return 0;
+}
+
+static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+	int sync;
+
+	*status = 0;
+	sync = ves1820_readreg(state, 0x11);
+
+	if (sync & 1)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 2)
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 2)	/* XXX FIXME! */
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 4)
+		*status |= FE_HAS_SYNC;
+
+	if (sync & 8)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+	u32 _ber = ves1820_readreg(state, 0x14) |
+			(ves1820_readreg(state, 0x15) << 8) |
+			((ves1820_readreg(state, 0x16) & 0x0f) << 16);
+	*ber = 10 * _ber;
+
+	return 0;
+}
+
+static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+	u8 gain = ves1820_readreg(state, 0x17);
+	*strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+	u8 quality = ~ves1820_readreg(state, 0x18);
+	*snr = (quality << 8) | quality;
+
+	return 0;
+}
+
+static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+	*ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
+	if (*ucblocks == 0x7f)
+		*ucblocks = 0xffffffff;
+
+	/* reset uncorrected block counter */
+	ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
+	ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
+
+	return 0;
+}
+
+static int ves1820_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+	int sync;
+	s8 afc = 0;
+
+	sync = ves1820_readreg(state, 0x11);
+	afc = ves1820_readreg(state, 0x19);
+	if (verbose) {
+		/* AFC only valid when carrier has been recovered */
+		printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
+			"ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10);
+	}
+
+	if (!state->config->invert) {
+		p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
+	} else {
+		p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
+	}
+
+	p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+	p->u.qam.fec_inner = FEC_NONE;
+
+	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+	if (sync & 2)
+		p->frequency -= ((s32) p->u.qam.symbol_rate * afc) >> 10;
+
+	return 0;
+}
+
+static int ves1820_sleep(struct dvb_frontend* fe)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+	ves1820_writereg(state, 0x1b, 0x02);	/* pdown ADC */
+	ves1820_writereg(state, 0x00, 0x80);	/* standby */
+
+	return 0;
+}
+
+static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+
+	fesettings->min_delay_ms = 200;
+	fesettings->step_size = 0;
+	fesettings->max_drift = 0;
+	return 0;
+}
+
+static void ves1820_release(struct dvb_frontend* fe)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops ves1820_ops;
+
+struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+				    struct i2c_adapter* i2c,
+				    u8 pwm)
+{
+	struct ves1820_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct ves1820_state*) kmalloc(sizeof(struct ves1820_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	memcpy(&state->ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
+	state->reg0 = ves1820_inittab[0];
+	state->config = config;
+	state->i2c = i2c;
+	state->pwm = pwm;
+
+	/* check if the demod is there */
+	if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
+		goto error;
+
+	if (verbose)
+		printk("ves1820: pwm=0x%02x\n", state->pwm);
+
+	state->ops.info.symbol_rate_min = (state->config->xin / 2) / 64;      /* SACLK/64 == (XIN/2)/64 */
+	state->ops.info.symbol_rate_max = (state->config->xin / 2) / 4;       /* SACLK/4 */
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops ves1820_ops = {
+
+	.info = {
+		.name = "VLSI VES1820 DVB-C",
+		.type = FE_QAM,
+		.frequency_stepsize = 62500,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.caps = FE_CAN_QAM_16 |
+			FE_CAN_QAM_32 |
+			FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 |
+			FE_CAN_QAM_256 |
+			FE_CAN_FEC_AUTO
+	},
+
+	.release = ves1820_release,
+
+	.init = ves1820_init,
+	.sleep = ves1820_sleep,
+
+	.set_frontend = ves1820_set_parameters,
+	.get_frontend = ves1820_get_frontend,
+	.get_tune_settings = ves1820_get_tune_settings,
+
+	.read_status = ves1820_read_status,
+	.read_ber = ves1820_read_ber,
+	.read_signal_strength = ves1820_read_signal_strength,
+	.read_snr = ves1820_read_snr,
+	.read_ucblocks = ves1820_read_ucblocks,
+};
+
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
+
+MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ves1820_attach);
diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h
new file mode 100644
index 0000000..355f130
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1820.h
@@ -0,0 +1,51 @@
+/*
+    VES1820  - Single Chip Cable Channel Receiver driver module
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.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; 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 VES1820_H
+#define VES1820_H
+
+#include <linux/dvb/frontend.h>
+
+#define VES1820_SELAGC_PWM 0
+#define VES1820_SELAGC_SIGNAMPERR 1
+
+struct ves1820_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* value of XIN to use */
+	u32 xin;
+
+	/* does inversion need inverted? */
+	u8 invert:1;
+
+	/* SELAGC control */
+	u8 selagc:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+					   struct i2c_adapter* i2c, u8 pwm);
+
+#endif // VES1820_H
diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c
new file mode 100644
index 0000000..edcad28
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1x93.c
@@ -0,0 +1,545 @@
+/*
+    Driver for VES1893 and VES1993 QPSK Demodulators
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de>
+    Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de>
+    Copyright (C) 2002-2003 Andreas Oberritter <obi@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; 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/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "ves1x93.h"
+
+
+struct ves1x93_state {
+	struct i2c_adapter* i2c;
+	struct dvb_frontend_ops ops;
+	/* configuration settings */
+	const struct ves1x93_config* config;
+	struct dvb_frontend frontend;
+
+	/* previous uncorrected block counter */
+	fe_spectral_inversion_t inversion;
+	u8 *init_1x93_tab;
+	u8 *init_1x93_wtab;
+	u8 tab_size;
+	u8 demod_type;
+};
+
+static int debug = 0;
+#define dprintk	if (debug) printk
+
+#define DEMOD_VES1893		0
+#define DEMOD_VES1993		1
+
+static u8 init_1893_tab [] = {
+	0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4,
+	0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7f, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x80, 0x00, 0x21, 0xb0, 0x14, 0x00, 0xdc, 0x00,
+	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x55, 0x00, 0x00, 0x7f, 0x00
+};
+
+static u8 init_1993_tab [] = {
+	0x00, 0x9c, 0x35, 0x80, 0x6a, 0x09, 0x72, 0x8c,
+	0x09, 0x6b, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00,
+	0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x80, 0x40, 0x21, 0xb0, 0x00, 0x00, 0x00, 0x10,
+	0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0x00, 0x0e, 0x80, 0x00
+};
+
+static u8 init_1893_wtab[] =
+{
+	1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0,
+	0,1,0,0,0,0,0,0, 1,0,1,1,0,0,0,1,
+	1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0,
+	1,1,1,0,1,1
+};
+
+static u8 init_1993_wtab[] =
+{
+	1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0,
+	0,1,0,0,0,0,0,0, 1,1,1,1,0,0,0,1,
+	1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0,
+	1,1,1,0,1,1,1,1, 1,1,1,1,1
+};
+
+static int ves1x93_writereg (struct ves1x93_state* state, u8 reg, u8 data)
+{
+	u8 buf [] = { 0x00, reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 };
+	int err;
+
+	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+		dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static u8 ves1x93_readreg (struct ves1x93_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { 0x00, reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+
+	if (ret != 2) return ret;
+
+	return b1[0];
+}
+
+static int ves1x93_clr_bit (struct ves1x93_state* state)
+{
+	msleep(10);
+	ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe);
+	ves1x93_writereg (state, 0, state->init_1x93_tab[0]);
+	msleep(50);
+	return 0;
+}
+
+static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion)
+{
+	u8 val;
+
+	/*
+	 * inversion on/off are interchanged because i and q seem to
+	 * be swapped on the hardware
+	 */
+
+	switch (inversion) {
+	case INVERSION_OFF:
+		val = 0xc0;
+		break;
+	case INVERSION_ON:
+		val = 0x80;
+		break;
+	case INVERSION_AUTO:
+		val = 0x00;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val);
+}
+
+static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec)
+{
+	if (fec == FEC_AUTO)
+		return ves1x93_writereg (state, 0x0d, 0x08);
+	else if (fec < FEC_1_2 || fec > FEC_8_9)
+		return -EINVAL;
+	else
+		return ves1x93_writereg (state, 0x0d, fec - FEC_1_2);
+}
+
+static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state)
+{
+	return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7);
+}
+
+static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate)
+{
+	u32 BDR;
+	u32 ratio;
+	u8  ADCONF, FCONF, FNR, AGCR;
+	u32 BDRI;
+	u32 tmp;
+	u32 FIN;
+
+	dprintk("%s: srate == %d\n", __FUNCTION__, (unsigned int) srate);
+
+	if (srate > state->config->xin/2)
+		srate = state->config->xin/2;
+
+	if (srate < 500000)
+		srate = 500000;
+
+#define MUL (1UL<<26)
+
+	FIN = (state->config->xin + 6000) >> 4;
+
+	tmp = srate << 6;
+	ratio = tmp / FIN;
+
+	tmp = (tmp % FIN) << 8;
+	ratio = (ratio << 8) + tmp / FIN;
+
+	tmp = (tmp % FIN) << 8;
+	ratio = (ratio << 8) + tmp / FIN;
+
+	FNR = 0xff;
+
+	if (ratio < MUL/3)	     FNR = 0;
+	if (ratio < (MUL*11)/50)     FNR = 1;
+	if (ratio < MUL/6)	     FNR = 2;
+	if (ratio < MUL/9)	     FNR = 3;
+	if (ratio < MUL/12)	     FNR = 4;
+	if (ratio < (MUL*11)/200)    FNR = 5;
+	if (ratio < MUL/24)	     FNR = 6;
+	if (ratio < (MUL*27)/1000)   FNR = 7;
+	if (ratio < MUL/48)	     FNR = 8;
+	if (ratio < (MUL*137)/10000) FNR = 9;
+
+	if (FNR == 0xff) {
+		ADCONF = 0x89;
+		FCONF  = 0x80;
+		FNR	= 0;
+	} else {
+		ADCONF = 0x81;
+		FCONF  = 0x88 | (FNR >> 1) | ((FNR & 0x01) << 5);
+		/*FCONF	 = 0x80 | ((FNR & 0x01) << 5) | (((FNR > 1) & 0x03) << 3) | ((FNR >> 1) & 0x07);*/
+	}
+
+	BDR = (( (ratio << (FNR >> 1)) >> 4) + 1) >> 1;
+	BDRI = ( ((FIN << 8) / ((srate << (FNR >> 1)) >> 2)) + 1) >> 1;
+
+	dprintk("FNR= %d\n", FNR);
+	dprintk("ratio= %08x\n", (unsigned int) ratio);
+	dprintk("BDR= %08x\n", (unsigned int) BDR);
+	dprintk("BDRI= %02x\n", (unsigned int) BDRI);
+
+	if (BDRI > 0xff)
+		BDRI = 0xff;
+
+	ves1x93_writereg (state, 0x06, 0xff & BDR);
+	ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8));
+	ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16));
+
+	ves1x93_writereg (state, 0x09, BDRI);
+	ves1x93_writereg (state, 0x20, ADCONF);
+	ves1x93_writereg (state, 0x21, FCONF);
+
+	AGCR = state->init_1x93_tab[0x05];
+	if (state->config->invert_pwm)
+		AGCR |= 0x20;
+
+	if (srate < 6000000)
+		AGCR |= 0x80;
+	else
+		AGCR &= ~0x80;
+
+	ves1x93_writereg (state, 0x05, AGCR);
+
+	/* ves1993 hates this, will lose lock */
+	if (state->demod_type != DEMOD_VES1993)
+		ves1x93_clr_bit (state);
+
+	return 0;
+}
+
+static int ves1x93_init (struct dvb_frontend* fe)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+	int i;
+	int val;
+
+	dprintk("%s: init chip\n", __FUNCTION__);
+
+	for (i = 0; i < state->tab_size; i++) {
+		if (state->init_1x93_wtab[i]) {
+			val = state->init_1x93_tab[i];
+
+			if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */
+			ves1x93_writereg (state, i, val);
+		}
+	}
+
+	if (state->config->pll_init) {
+		ves1x93_writereg(state, 0x00, 0x11);
+		state->config->pll_init(fe);
+		ves1x93_writereg(state, 0x00, 0x01);
+	}
+
+	return 0;
+}
+
+static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		return ves1x93_writereg (state, 0x1f, 0x20);
+	case SEC_VOLTAGE_18:
+		return ves1x93_writereg (state, 0x1f, 0x30);
+	case SEC_VOLTAGE_OFF:
+		return ves1x93_writereg (state, 0x1f, 0x00);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	u8 sync = ves1x93_readreg (state, 0x0e);
+
+	/*
+	 * The ves1893 sometimes returns sync values that make no sense,
+	 * because, e.g., the SIGNAL bit is 0, while some of the higher
+	 * bits are 1 (and how can there be a CARRIER w/o a SIGNAL?).
+	 * Tests showed that the the VITERBI and SYNC bits are returned
+	 * reliably, while the SIGNAL and CARRIER bits ar sometimes wrong.
+	 * If such a case occurs, we read the value again, until we get a
+	 * valid value.
+	 */
+	int maxtry = 10; /* just for safety - let's not get stuck here */
+	while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) {
+		msleep(10);
+		sync = ves1x93_readreg (state, 0x0e);
+	}
+
+	*status = 0;
+
+	if (sync & 1)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 2)
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 4)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 8)
+		*status |= FE_HAS_SYNC;
+
+	if ((sync & 0x1f) == 0x1f)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	*ber = ves1x93_readreg (state, 0x15);
+	*ber |= (ves1x93_readreg (state, 0x16) << 8);
+	*ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16);
+	*ber *= 10;
+
+	return 0;
+}
+
+static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	u8 signal = ~ves1x93_readreg (state, 0x0b);
+	*strength = (signal << 8) | signal;
+
+	return 0;
+}
+
+static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	u8 _snr = ~ves1x93_readreg (state, 0x1c);
+	*snr = (_snr << 8) | _snr;
+
+	return 0;
+}
+
+static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	*ucblocks = ves1x93_readreg (state, 0x18) & 0x7f;
+
+	if (*ucblocks == 0x7f)
+		*ucblocks = 0xffffffff;   /* counter overflow... */
+
+	ves1x93_writereg (state, 0x18, 0x00);  /* reset the counter */
+	ves1x93_writereg (state, 0x18, 0x80);  /* dto. */
+
+	return 0;
+}
+
+static int ves1x93_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	ves1x93_writereg(state, 0x00, 0x11);
+	state->config->pll_set(fe, p);
+	ves1x93_writereg(state, 0x00, 0x01);
+	ves1x93_set_inversion (state, p->inversion);
+	ves1x93_set_fec (state, p->u.qpsk.fec_inner);
+	ves1x93_set_symbolrate (state, p->u.qpsk.symbol_rate);
+	state->inversion = p->inversion;
+
+	return 0;
+}
+
+static int ves1x93_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+	int afc;
+
+	afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2;
+	afc = (afc * (int)(p->u.qpsk.symbol_rate/1000/8))/16;
+
+	p->frequency -= afc;
+
+	/*
+	 * inversion indicator is only valid
+	 * if auto inversion was used
+	 */
+	if (state->inversion == INVERSION_AUTO)
+		p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ?
+				INVERSION_OFF : INVERSION_ON;
+	p->u.qpsk.fec_inner = ves1x93_get_fec (state);
+	/*  XXX FIXME: timing offset !! */
+
+	return 0;
+}
+
+static int ves1x93_sleep(struct dvb_frontend* fe)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	return ves1x93_writereg (state, 0x00, 0x08);
+}
+
+static void ves1x93_release(struct dvb_frontend* fe)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops ves1x93_ops;
+
+struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct ves1x93_state* state = NULL;
+	u8 identity;
+
+	/* allocate memory for the internal state */
+	state = (struct ves1x93_state*) kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops));
+	state->inversion = INVERSION_OFF;
+
+	/* check if the demod is there + identify it */
+	identity = ves1x93_readreg(state, 0x1e);
+	switch (identity) {
+	case 0xdc: /* VES1893A rev1 */
+		printk("ves1x93: Detected ves1893a rev1\n");
+		state->demod_type = DEMOD_VES1893;
+		state->init_1x93_tab = init_1893_tab;
+		state->init_1x93_wtab = init_1893_wtab;
+		state->tab_size = sizeof(init_1893_tab);
+		break;
+
+	case 0xdd: /* VES1893A rev2 */
+		printk("ves1x93: Detected ves1893a rev2\n");
+		state->demod_type = DEMOD_VES1893;
+		state->init_1x93_tab = init_1893_tab;
+		state->init_1x93_wtab = init_1893_wtab;
+		state->tab_size = sizeof(init_1893_tab);
+		break;
+
+	case 0xde: /* VES1993 */
+		printk("ves1x93: Detected ves1993\n");
+		state->demod_type = DEMOD_VES1993;
+		state->init_1x93_tab = init_1993_tab;
+		state->init_1x93_wtab = init_1993_wtab;
+		state->tab_size = sizeof(init_1993_tab);
+		break;
+
+	default:
+		goto error;
+	}
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops ves1x93_ops = {
+
+	.info = {
+		.name			= "VLSI VES1x93 DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 125,		 /* kHz for QPSK frontends */
+		.frequency_tolerance	= 29500,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+	/*	.symbol_rate_tolerance	=	???,*/
+		.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
+	},
+
+	.release = ves1x93_release,
+
+	.init = ves1x93_init,
+	.sleep = ves1x93_sleep,
+
+	.set_frontend = ves1x93_set_frontend,
+	.get_frontend = ves1x93_get_frontend,
+
+	.read_status = ves1x93_read_status,
+	.read_ber = ves1x93_read_ber,
+	.read_signal_strength = ves1x93_read_signal_strength,
+	.read_snr = ves1x93_read_snr,
+	.read_ucblocks = ves1x93_read_ucblocks,
+
+	.set_voltage = ves1x93_set_voltage,
+};
+
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ves1x93_attach);
diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h
new file mode 100644
index 0000000..1627e37
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1x93.h
@@ -0,0 +1,50 @@
+/*
+    Driver for VES1893 and VES1993 QPSK Demodulators
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de>
+    Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de>
+    Copyright (C) 2002-2003 Andreas Oberritter <obi@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; 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 VES1X93_H
+#define VES1X93_H
+
+#include <linux/dvb/frontend.h>
+
+struct ves1x93_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* value of XIN to use */
+	u32 xin;
+
+	/* should PWM be inverted? */
+	u8 invert_pwm:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // VES1X93_H
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
new file mode 100644
index 0000000..7ffa2c7
--- /dev/null
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -0,0 +1,134 @@
+config DVB_AV7110
+	tristate "AV7110 cards"
+	depends on DVB_CORE && PCI
+	select FW_LOADER
+	select VIDEO_DEV
+	select VIDEO_SAA7146_VV
+	select DVB_VES1820
+	select DVB_VES1X93
+	select DVB_STV0299
+	select DVB_TDA8083
+	select DVB_SP8870
+	select DVB_STV0297
+	select DVB_L64781
+	help
+	  Support for SAA7146 and AV7110 based DVB cards as produced 
+	  by Fujitsu-Siemens, Technotrend, Hauppauge and others.
+
+	  This driver only supports the fullfeatured cards with
+	  onboard MPEG2 decoder.
+
+          This driver needs an external firmware. Please use the script
+          "<kerneldir>/Documentation/dvb/get_dvb_firmware av7110" to
+          download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+
+	  Say Y if you own such a card and want to use it.
+
+config DVB_AV7110_FIRMWARE
+	bool "Compile AV7110 firmware into the driver"
+	depends on DVB_AV7110 && !STANDALONE
+	default y if DVB_AV7110=y
+	help
+	  The AV7110 firmware is normally loaded by the firmware hotplug manager.
+	  If you want to compile the firmware into the driver you need to say
+	  Y here and provide the correct path of the firmware. You need this
+	  option if you want to compile the whole driver statically into the
+	  kernel. 
+
+	  All other people say N.
+
+config DVB_AV7110_FIRMWARE_FILE
+	string "Full pathname of av7110 firmware file"
+	depends on DVB_AV7110_FIRMWARE
+	default "/usr/lib/hotplug/firmware/dvb-ttpci-01.fw"
+
+config DVB_AV7110_OSD
+	bool "AV7110 OSD support"
+	depends on DVB_AV7110
+	default y if DVB_AV7110=y || DVB_AV7110=m
+	help
+	  The AV7110 firmware provides some code to generate an OnScreenDisplay
+	  on the video output. This is kind of nonstandard and not guaranteed to
+	  be maintained.
+
+	  Anyway, some popular DVB software like VDR uses this OSD to render
+	  its menus, so say Y if you want to use this software.
+
+	  All other people say N.
+
+config DVB_BUDGET
+	tristate "Budget cards"
+	depends on DVB_CORE && PCI
+	select VIDEO_SAA7146
+	select DVB_STV0299
+	select DVB_VES1X93
+	select DVB_VES1820
+	select DVB_L64781
+	select DVB_TDA8083
+	select DVB_TDA10021
+	help
+	  Support for simple SAA7146 based DVB cards
+	  (so called Budget- or Nova-PCI cards) without onboard
+	  MPEG2 decoder.
+
+	  Say Y if you own such a card and want to use it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget.
+
+config DVB_BUDGET_CI
+	tristate "Budget cards with onboard CI connector"
+	depends on DVB_CORE && PCI
+	select VIDEO_SAA7146
+	select DVB_STV0299
+	select DVB_TDA1004X
+	help
+	  Support for simple SAA7146 based DVB cards
+	  (so called Budget- or Nova-PCI cards) without onboard
+	  MPEG2 decoder, but with onboard Common Interface connector.
+
+	  Note: The Common Interface is not yet supported by this driver
+	  due to lack of information from the vendor.
+
+	  Say Y if you own such a card and want to use it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget-ci.
+
+config DVB_BUDGET_AV
+	tristate "Budget cards with analog video inputs"
+	depends on DVB_CORE && PCI
+	select VIDEO_DEV
+	select VIDEO_SAA7146_VV
+	select DVB_STV0299
+	help
+	  Support for simple SAA7146 based DVB cards
+	  (so called Budget- or Nova-PCI cards) without onboard
+	  MPEG2 decoder, but with one or more analog video inputs.
+
+	  Say Y if you own such a card and want to use it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget-av.
+
+config DVB_BUDGET_PATCH
+	tristate "AV7110 cards with Budget Patch"
+	depends on DVB_CORE && DVB_BUDGET
+	select DVB_AV7110
+	select DVB_STV0299
+	select DVB_VES1X93
+	select DVB_TDA8083
+	help
+	  Support for Budget Patch (full TS) modification on 
+	  SAA7146+AV7110 based cards (DVB-S cards). This
+	  driver doesn't use onboard MPEG2 decoder. The 
+	  card is driven in Budget-only mode. Card is
+	  required to have loaded firmware to tune properly.
+	  Firmware can be loaded by insertion and removal of
+	  standard AV7110 driver prior to loading this
+	  driver.
+
+	  Say Y if you own such a card and want to use it.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called budget-patch.
diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile
new file mode 100644
index 0000000..825ab1c
--- /dev/null
+++ b/drivers/media/dvb/ttpci/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the kernel SAA7146 FULL TS DVB device driver
+# and the AV7110 DVB device driver
+#
+
+dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o av7110_ir.o
+
+obj-$(CONFIG_DVB_BUDGET) += budget-core.o budget.o ttpci-eeprom.o
+obj-$(CONFIG_DVB_BUDGET_AV) += budget-core.o budget-av.o ttpci-eeprom.o
+obj-$(CONFIG_DVB_BUDGET_CI) += budget-core.o budget-ci.o ttpci-eeprom.o
+obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-core.o budget-patch.o ttpci-eeprom.o
+obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o ttpci-eeprom.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+
+hostprogs-y	:= fdump
+
+ifdef CONFIG_DVB_AV7110_FIRMWARE
+$(obj)/av7110.o: $(obj)/fdump $(obj)/av7110_firm.h 
+
+$(obj)/av7110_firm.h:
+	$(obj)/fdump $(CONFIG_DVB_AV7110_FIRMWARE_FILE) dvb_ttpci_fw $@
+endif
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
new file mode 100644
index 0000000..922c205
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -0,0 +1,2739 @@
+/*
+ * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB)
+ * av7110.c: initialization and demux stuff
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+#include <linux/byteorder/swabb.h>
+#include <linux/smp_lock.h>
+
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include <linux/i2c.h>
+
+#include <asm/system.h>
+#include <asm/semaphore.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "dvb_frontend.h"
+
+#include "ttpci-eeprom.h"
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_av.h"
+#include "av7110_ca.h"
+#include "av7110_ipack.h"
+
+#define TS_WIDTH  376
+#define TS_HEIGHT 512
+#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)
+#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)
+
+
+int av7110_debug;
+
+static int vidmode = CVBS_RGB_OUT;
+static int pids_off;
+static int adac = DVB_ADAC_TI;
+static int hw_sections;
+static int rgb_on;
+static int volume = 255;
+static int budgetpatch = 0;
+
+module_param_named(debug, av7110_debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)");
+module_param(vidmode, int, 0444);
+MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC");
+module_param(pids_off, int, 0444);
+MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed");
+module_param(adac, int, 0444);
+MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)");
+module_param(hw_sections, int, 0444);
+MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware");
+module_param(rgb_on, int, 0444);
+MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control"
+		" signal on SCART pin 16 to switch SCART video mode from CVBS to RGB");
+module_param(volume, int, 0444);
+MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)");
+module_param(budgetpatch, int, 0444);
+MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)");
+
+static void restart_feeds(struct av7110 *av7110);
+
+static int av7110_num = 0;
+
+#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \
+{\
+	if (fe_func != NULL) { \
+		av7110_copy = fe_func; \
+		fe_func = av7110_func; \
+	} \
+}
+
+
+static void init_av7110_av(struct av7110 *av7110)
+{
+	struct saa7146_dev *dev = av7110->dev;
+
+	/* set internal volume control to maximum */
+	av7110->adac_type = DVB_ADAC_TI;
+	av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right);
+
+	av7710_set_video_mode(av7110, vidmode);
+
+	/* handle different card types */
+	/* remaining inits according to card and frontend type */
+	av7110->analog_tuner_flags = 0;
+	av7110->current_input = 0;
+	if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) {
+		printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n",
+			av7110->dvb_adapter->num);
+		av7110->adac_type = DVB_ADAC_CRYSTAL;
+		i2c_writereg(av7110, 0x20, 0x01, 0xd2);
+		i2c_writereg(av7110, 0x20, 0x02, 0x49);
+		i2c_writereg(av7110, 0x20, 0x03, 0x00);
+		i2c_writereg(av7110, 0x20, 0x04, 0x00);
+
+		/**
+		 * some special handling for the Siemens DVB-C cards...
+		 */
+	} else if (0 == av7110_init_analog_module(av7110)) {
+		/* done. */
+	}
+	else if (dev->pci->subsystem_vendor == 0x110a) {
+		printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n",
+			av7110->dvb_adapter->num);
+		av7110->adac_type = DVB_ADAC_NONE;
+	}
+	else {
+		av7110->adac_type = adac;
+		printk("dvb-ttpci: adac type set to %d @ card %d\n",
+			av7110->dvb_adapter->num, av7110->adac_type);
+	}
+
+	if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP) {
+		// switch DVB SCART on
+		av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0);
+		av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1);
+		if (rgb_on &&
+		    (av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) {
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16
+			//saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8
+		}
+	}
+
+	av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right);
+	av7110_setup_irc_config(av7110, 0);
+}
+
+static void recover_arm(struct av7110 *av7110)
+{
+	dprintk(4, "%p\n",av7110);
+
+	av7110_bootarm(av7110);
+	msleep(100);
+	restart_feeds(av7110);
+	av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config);
+}
+
+static void arm_error(struct av7110 *av7110)
+{
+	dprintk(4, "%p\n",av7110);
+
+	av7110->arm_errors++;
+	av7110->arm_ready = 0;
+	recover_arm(av7110);
+}
+
+static void av7110_arm_sync(struct av7110 *av7110)
+{
+	av7110->arm_rmmod = 1;
+	wake_up_interruptible(&av7110->arm_wait);
+
+	while (av7110->arm_thread)
+		msleep(1);
+}
+
+static int arm_thread(void *data)
+{
+	struct av7110 *av7110 = data;
+	u16 newloops = 0;
+	int timeout;
+
+	dprintk(4, "%p\n",av7110);
+
+        lock_kernel();
+        daemonize("arm_mon");
+        sigfillset(&current->blocked);
+        unlock_kernel();
+
+	av7110->arm_thread = current;
+
+	for (;;) {
+		timeout = wait_event_interruptible_timeout(av7110->arm_wait,
+							   av7110->arm_rmmod, 5 * HZ);
+		if (-ERESTARTSYS == timeout || av7110->arm_rmmod) {
+			/* got signal or told to quit*/
+			break;
+		}
+
+		if (!av7110->arm_ready)
+			continue;
+
+		if (down_interruptible(&av7110->dcomlock))
+			break;
+
+		newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2);
+		up(&av7110->dcomlock);
+
+		if (newloops == av7110->arm_loops) {
+			printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n",
+			       av7110->dvb_adapter->num);
+
+			arm_error(av7110);
+			av7710_set_video_mode(av7110, vidmode);
+
+			init_av7110_av(av7110);
+
+			if (down_interruptible(&av7110->dcomlock))
+				break;
+
+			newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1;
+			up(&av7110->dcomlock);
+		}
+		av7110->arm_loops = newloops;
+	}
+
+	av7110->arm_thread = NULL;
+	return 0;
+}
+
+
+/**
+ *  Hack! we save the last av7110 ptr. This should be ok, since
+ *  you rarely will use more then one IR control.
+ *
+ *  If we want to support multiple controls we would have to do much more...
+ */
+void av7110_setup_irc_config(struct av7110 *av7110, u32 ir_config)
+{
+	static struct av7110 *last;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (!av7110)
+		av7110 = last;
+	else
+		last = av7110;
+
+	if (av7110) {
+		av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, ir_config);
+		av7110->ir_config = ir_config;
+	}
+}
+
+static void (*irc_handler)(u32);
+
+void av7110_register_irc_handler(void (*func)(u32))
+{
+	dprintk(4, "registering %p\n", func);
+	irc_handler = func;
+}
+
+void av7110_unregister_irc_handler(void (*func)(u32))
+{
+	dprintk(4, "unregistering %p\n", func);
+	irc_handler = NULL;
+}
+
+static void run_handlers(unsigned long ircom)
+{
+	if (irc_handler != NULL)
+		(*irc_handler)((u32) ircom);
+}
+
+static DECLARE_TASKLET(irtask, run_handlers, 0);
+
+static void IR_handle(struct av7110 *av7110, u32 ircom)
+{
+	dprintk(4, "ircommand = %08x\n", ircom);
+	irtask.data = (unsigned long) ircom;
+	tasklet_schedule(&irtask);
+}
+
+/****************************************************************************
+ * IRQ handling
+ ****************************************************************************/
+
+static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len,
+				u8 *buffer2, size_t buffer2_len,
+				struct dvb_demux_filter *dvbdmxfilter,
+				enum dmx_success success,
+				struct av7110 *av7110)
+{
+	if (!dvbdmxfilter->feed->demux->dmx.frontend)
+		return 0;
+	if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE)
+		return 0;
+
+	switch (dvbdmxfilter->type) {
+	case DMX_TYPE_SEC:
+		if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len)
+			return 0;
+		if (dvbdmxfilter->doneq) {
+			struct dmx_section_filter *filter = &dvbdmxfilter->filter;
+			int i;
+			u8 xor, neq = 0;
+
+			for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
+				xor = filter->filter_value[i] ^ buffer1[i];
+				neq |= dvbdmxfilter->maskandnotmode[i] & xor;
+			}
+			if (!neq)
+				return 0;
+		}
+		return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len,
+						  buffer2, buffer2_len,
+						  &dvbdmxfilter->filter,
+						  DMX_OK);
+	case DMX_TYPE_TS:
+		if (!(dvbdmxfilter->feed->ts_type & TS_PACKET))
+			return 0;
+		if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY)
+			return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len,
+							 buffer2, buffer2_len,
+							 &dvbdmxfilter->feed->feed.ts,
+							 DMX_OK);
+		else
+			av7110_p2t_write(buffer1, buffer1_len,
+					 dvbdmxfilter->feed->pid,
+					 &av7110->p2t_filter[dvbdmxfilter->index]);
+	default:
+		return 0;
+	}
+}
+
+
+//#define DEBUG_TIMING
+static inline void print_time(char *s)
+{
+#ifdef DEBUG_TIMING
+	struct timeval tv;
+	do_gettimeofday(&tv);
+	printk("%s: %d.%d\n", s, (int)tv.tv_sec, (int)tv.tv_usec);
+#endif
+}
+
+#define DEBI_READ 0
+#define DEBI_WRITE 1
+static inline void start_debi_dma(struct av7110 *av7110, int dir,
+				  unsigned long addr, unsigned int len)
+{
+	dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len);
+	if (saa7146_wait_for_debi_done(av7110->dev, 0)) {
+		printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __FUNCTION__);
+		return;
+	}
+
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */
+	SAA7146_IER_ENABLE(av7110->dev, MASK_19);
+	if (len < 5)
+		len = 5; /* we want a real DEBI DMA */
+	if (dir == DEBI_WRITE)
+		iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3);
+	else
+		irdebi(av7110, DEBISWAB, addr, 0, len);
+}
+
+static void debiirq(unsigned long data)
+{
+	struct av7110 *av7110 = (struct av7110 *) data;
+	int type = av7110->debitype;
+	int handle = (type >> 8) & 0x1f;
+	unsigned int xfer = 0;
+
+	print_time("debi");
+	dprintk(4, "type 0x%04x\n", type);
+
+	if (type == -1) {
+		printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n",
+		       jiffies, saa7146_read(av7110->dev, PSR),
+		       saa7146_read(av7110->dev, SSR));
+		goto debi_done;
+	}
+	av7110->debitype = -1;
+
+	switch (type & 0xff) {
+
+	case DATA_TS_RECORD:
+		dvb_dmx_swfilter_packets(&av7110->demux,
+					 (const u8 *) av7110->debi_virt,
+					 av7110->debilen / 188);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_PES_RECORD:
+		if (av7110->demux.recording)
+			av7110_record_cb(&av7110->p2t[handle],
+					 (u8 *) av7110->debi_virt,
+					 av7110->debilen);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_IPMPE:
+	case DATA_FSECTION:
+	case DATA_PIPING:
+		if (av7110->handle2filter[handle])
+			DvbDmxFilterCallback((u8 *)av7110->debi_virt,
+					     av7110->debilen, NULL, 0,
+					     av7110->handle2filter[handle],
+					     DMX_OK, av7110);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_CI_GET:
+	{
+		u8 *data = av7110->debi_virt;
+
+		if ((data[0] < 2) && data[2] == 0xff) {
+			int flags = 0;
+			if (data[5] > 0)
+				flags |= CA_CI_MODULE_PRESENT;
+			if (data[5] > 5)
+				flags |= CA_CI_MODULE_READY;
+			av7110->ci_slot[data[0]].flags = flags;
+		} else
+			ci_get_data(&av7110->ci_rbuffer,
+				    av7110->debi_virt,
+				    av7110->debilen);
+		xfer = RX_BUFF;
+		break;
+	}
+
+	case DATA_COMMON_INTERFACE:
+		CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen);
+#if 0
+	{
+		int i;
+
+		printk("av7110%d: ", av7110->num);
+		printk("%02x ", *(u8 *)av7110->debi_virt);
+		printk("%02x ", *(1+(u8 *)av7110->debi_virt));
+		for (i = 2; i < av7110->debilen; i++)
+			printk("%02x ", (*(i+(unsigned char *)av7110->debi_virt)));
+		for (i = 2; i < av7110->debilen; i++)
+			printk("%c", chtrans(*(i+(unsigned char *)av7110->debi_virt)));
+
+		printk("\n");
+	}
+#endif
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_DEBUG_MESSAGE:
+		((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0;
+		printk("%s\n", (s8 *) av7110->debi_virt);
+		xfer = RX_BUFF;
+		break;
+
+	case DATA_CI_PUT:
+		dprintk(4, "debi DATA_CI_PUT\n");
+	case DATA_MPEG_PLAY:
+		dprintk(4, "debi DATA_MPEG_PLAY\n");
+	case DATA_BMP_LOAD:
+		dprintk(4, "debi DATA_BMP_LOAD\n");
+		xfer = TX_BUFF;
+		break;
+	default:
+		break;
+	}
+debi_done:
+	spin_lock(&av7110->debilock);
+	if (xfer)
+		iwdebi(av7110, DEBINOSWAP, xfer, 0, 2);
+	ARM_ClearMailBox(av7110);
+	spin_unlock(&av7110->debilock);
+}
+
+/* irq from av7110 firmware writing the mailbox register in the DPRAM */
+static void gpioirq(unsigned long data)
+{
+	struct av7110 *av7110 = (struct av7110 *) data;
+	u32 rxbuf, txbuf;
+	int len;
+
+	if (av7110->debitype != -1)
+		/* we shouldn't get any irq while a debi xfer is running */
+		printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n",
+		       jiffies, saa7146_read(av7110->dev, PSR),
+		       saa7146_read(av7110->dev, SSR));
+
+	if (saa7146_wait_for_debi_done(av7110->dev, 0)) {
+		printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __FUNCTION__);
+		BUG(); /* maybe we should try resetting the debi? */
+	}
+
+	spin_lock(&av7110->debilock);
+	ARM_ClearIrq(av7110);
+
+	/* see what the av7110 wants */
+	av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2);
+	av7110->debilen  = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+	rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+	txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+	len = (av7110->debilen + 3) & ~3;
+
+	print_time("gpio");
+	dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen);
+
+	switch (av7110->debitype & 0xff) {
+
+	case DATA_TS_PLAY:
+	case DATA_PES_PLAY:
+		break;
+
+	case DATA_MPEG_VIDEO_EVENT:
+	{
+		u32 h_ar;
+		struct video_event event;
+
+		av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2);
+		h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2);
+
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+		iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+
+		av7110->video_size.h = h_ar & 0xfff;
+		dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n",
+			av7110->video_size.w,
+			av7110->video_size.h,
+			av7110->video_size.aspect_ratio);
+
+		event.type = VIDEO_EVENT_SIZE_CHANGED;
+		event.u.size.w = av7110->video_size.w;
+		event.u.size.h = av7110->video_size.h;
+		switch ((h_ar >> 12) & 0xf)
+		{
+		case 3:
+			av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9;
+			event.u.size.aspect_ratio = VIDEO_FORMAT_16_9;
+			av7110->videostate.video_format = VIDEO_FORMAT_16_9;
+			break;
+		case 4:
+			av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1;
+			event.u.size.aspect_ratio = VIDEO_FORMAT_221_1;
+			av7110->videostate.video_format = VIDEO_FORMAT_221_1;
+			break;
+		default:
+			av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3;
+			event.u.size.aspect_ratio = VIDEO_FORMAT_4_3;
+			av7110->videostate.video_format = VIDEO_FORMAT_4_3;
+		}
+		dvb_video_add_event(av7110, &event);
+		break;
+	}
+
+	case DATA_CI_PUT:
+	{
+		int avail;
+		struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer;
+
+		avail = dvb_ringbuffer_avail(cibuf);
+		if (avail <= 2) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8;
+		len |= DVB_RINGBUFFER_PEEK(cibuf, 1);
+		if (avail < len + 2) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		DVB_RINGBUFFER_SKIP(cibuf, 2);
+
+		dvb_ringbuffer_read(cibuf, av7110->debi_virt, len, 0);
+
+		iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2);
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2);
+		dprintk(8, "DMA: CI\n");
+		start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len);
+		spin_unlock(&av7110->debilock);
+		wake_up(&cibuf->queue);
+		return;
+	}
+
+	case DATA_MPEG_PLAY:
+		if (!av7110->playing) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		len = 0;
+		if (av7110->debitype & 0x100) {
+			spin_lock(&av7110->aout.lock);
+			len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048);
+			spin_unlock(&av7110->aout.lock);
+		}
+		if (len <= 0 && (av7110->debitype & 0x200)
+		    &&av7110->videostate.play_state != VIDEO_FREEZED) {
+			spin_lock(&av7110->avout.lock);
+			len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048);
+			spin_unlock(&av7110->avout.lock);
+		}
+		if (len <= 0) {
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			break;
+		}
+		dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len);
+		iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2);
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2);
+		dprintk(8, "DMA: MPEG_PLAY\n");
+		start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_BMP_LOAD:
+		len = av7110->debilen;
+		dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len);
+		if (!len) {
+			av7110->bmp_state = BMP_LOADED;
+			iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2);
+			iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
+			wake_up(&av7110->bmpq);
+			dprintk(8, "gpio DATA_BMP_LOAD done\n");
+			break;
+		}
+		if (len > av7110->bmplen)
+			len = av7110->bmplen;
+		if (len > 2 * 1024)
+			len = 2 * 1024;
+		iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2);
+		iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2);
+		memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len);
+		av7110->bmpp += len;
+		av7110->bmplen -= len;
+		dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len);
+		start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_CI_GET:
+	case DATA_COMMON_INTERFACE:
+	case DATA_FSECTION:
+	case DATA_IPMPE:
+	case DATA_PIPING:
+		if (!len || len > 4 * 1024) {
+			iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+			break;
+		}
+		/* fall through */
+
+	case DATA_TS_RECORD:
+	case DATA_PES_RECORD:
+		dprintk(8, "DMA: TS_REC etc.\n");
+		start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_DEBUG_MESSAGE:
+		if (!len || len > 0xff) {
+			iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+			break;
+		}
+		start_debi_dma(av7110, DEBI_READ, Reserved, len);
+		spin_unlock(&av7110->debilock);
+		return;
+
+	case DATA_IRCOMMAND:
+		IR_handle(av7110,
+			  swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4)));
+		iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2);
+		break;
+
+	default:
+		printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n",
+		       av7110->debitype, av7110->debilen);
+		break;
+	}
+	av7110->debitype = -1;
+	ARM_ClearMailBox(av7110);
+	spin_unlock(&av7110->debilock);
+}
+
+
+#ifdef CONFIG_DVB_AV7110_OSD
+static int dvb_osd_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (cmd == OSD_SEND_CMD)
+		return av7110_osd_cmd(av7110, (osd_cmd_t *) parg);
+	if (cmd == OSD_GET_CAPABILITY)
+		return av7110_osd_capability(av7110, (osd_cap_t *) parg);
+
+	return -EINVAL;
+}
+
+
+static struct file_operations dvb_osd_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= dvb_generic_ioctl,
+	.open		= dvb_generic_open,
+	.release	= dvb_generic_release,
+};
+
+static struct dvb_device dvbdev_osd = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_osd_fops,
+	.kernel_ioctl	= dvb_osd_ioctl,
+};
+#endif /* CONFIG_DVB_AV7110_OSD */
+
+
+static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
+			  u16 subpid, u16 pcrpid)
+{
+	dprintk(4, "%p\n", av7110);
+
+	if (vpid == 0x1fff || apid == 0x1fff ||
+	    ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) {
+		vpid = apid = ttpid = subpid = pcrpid = 0;
+		av7110->pids[DMX_PES_VIDEO] = 0;
+		av7110->pids[DMX_PES_AUDIO] = 0;
+		av7110->pids[DMX_PES_TELETEXT] = 0;
+		av7110->pids[DMX_PES_PCR] = 0;
+	}
+
+	return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 5,
+			     pcrpid, vpid, apid, ttpid, subpid);
+}
+
+void ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
+		u16 subpid, u16 pcrpid)
+{
+	dprintk(4, "%p\n", av7110);
+
+	if (down_interruptible(&av7110->pid_mutex))
+		return;
+
+	if (!(vpid & 0x8000))
+		av7110->pids[DMX_PES_VIDEO] = vpid;
+	if (!(apid & 0x8000))
+		av7110->pids[DMX_PES_AUDIO] = apid;
+	if (!(ttpid & 0x8000))
+		av7110->pids[DMX_PES_TELETEXT] = ttpid;
+	if (!(pcrpid & 0x8000))
+		av7110->pids[DMX_PES_PCR] = pcrpid;
+
+	av7110->pids[DMX_PES_SUBTITLE] = 0;
+
+	if (av7110->fe_synced) {
+		pcrpid = av7110->pids[DMX_PES_PCR];
+		SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid);
+	}
+
+	up(&av7110->pid_mutex);
+}
+
+
+/******************************************************************************
+ * hardware filter functions
+ ******************************************************************************/
+
+static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter)
+{
+	struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed;
+	struct av7110 *av7110 = (struct av7110 *) dvbdmxfeed->demux->priv;
+	u16 buf[20];
+	int ret, i;
+	u16 handle;
+//	u16 mode = 0x0320;
+	u16 mode = 0xb96a;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (dvbdmxfilter->type == DMX_TYPE_SEC) {
+		if (hw_sections) {
+			buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) |
+				dvbdmxfilter->maskandmode[0];
+			for (i = 3; i < 18; i++)
+				buf[i + 4 - 2] =
+					(dvbdmxfilter->filter.filter_value[i] << 8) |
+					dvbdmxfilter->maskandmode[i];
+			mode = 4;
+		}
+	} else if ((dvbdmxfeed->ts_type & TS_PACKET) &&
+		   !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) {
+		av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed);
+	}
+
+	buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter;
+	buf[1] = 16;
+	buf[2] = dvbdmxfeed->pid;
+	buf[3] = mode;
+
+	ret = av7110_fw_request(av7110, buf, 20, &handle, 1);
+	if (ret != 0 || handle >= 32) {
+		printk("dvb-ttpci: %s error  buf %04x %04x %04x %04x  "
+				"ret %x  handle %04x\n",
+				__FUNCTION__, buf[0], buf[1], buf[2], buf[3],
+				ret, handle);
+		dvbdmxfilter->hw_handle = 0xffff;
+		return -1;
+	}
+
+	av7110->handle2filter[handle] = dvbdmxfilter;
+	dvbdmxfilter->hw_handle = handle;
+
+	return ret;
+}
+
+static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter)
+{
+	struct av7110 *av7110 = (struct av7110 *) dvbdmxfilter->feed->demux->priv;
+	u16 buf[3];
+	u16 answ[2];
+	int ret;
+	u16 handle;
+
+	dprintk(4, "%p\n", av7110);
+
+	handle = dvbdmxfilter->hw_handle;
+	if (handle >= 32) {
+		printk("%s tried to stop invalid filter %04x, filter type = %x\n",
+				__FUNCTION__, handle, dvbdmxfilter->type);
+		return 0;
+	}
+
+	av7110->handle2filter[handle] = NULL;
+
+	buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter;
+	buf[1] = 1;
+	buf[2] = handle;
+	ret = av7110_fw_request(av7110, buf, 3, answ, 2);
+	if (ret != 0 || answ[1] != handle) {
+		printk("dvb-ttpci: %s error  cmd %04x %04x %04x  ret %x  "
+				"resp %04x %04x  pid %d\n",
+				__FUNCTION__, buf[0], buf[1], buf[2], ret,
+				answ[0], answ[1], dvbdmxfilter->feed->pid);
+		ret = -1;
+	}
+	return ret;
+}
+
+
+static void dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct av7110 *av7110 = (struct av7110 *) dvbdmx->priv;
+	u16 *pid = dvbdmx->pids, npids[5];
+	int i;
+
+	dprintk(4, "%p\n", av7110);
+
+	npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff;
+	i = dvbdmxfeed->pes_type;
+	npids[i] = (pid[i]&0x8000) ? 0 : pid[i];
+	if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) {
+		npids[i] = 0;
+		ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]);
+		StartHWFilter(dvbdmxfeed->filter);
+		return;
+	}
+	if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4)
+		ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]);
+
+	if (dvbdmxfeed->pes_type < 2 && npids[0])
+		if (av7110->fe_synced)
+			av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+
+	if ((dvbdmxfeed->ts_type & TS_PACKET)) {
+		if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000))
+			av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed);
+		if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000))
+			av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed);
+	}
+}
+
+static void dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct av7110 *av7110 = (struct av7110 *) dvbdmx->priv;
+	u16 *pid = dvbdmx->pids, npids[5];
+	int i;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (dvbdmxfeed->pes_type <= 1) {
+		av7110_av_stop(av7110, dvbdmxfeed->pes_type ?  RP_VIDEO : RP_AUDIO);
+		if (!av7110->rec_mode)
+			dvbdmx->recording = 0;
+		if (!av7110->playing)
+			dvbdmx->playing = 0;
+	}
+	npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff;
+	i = dvbdmxfeed->pes_type;
+	switch (i) {
+	case 2: //teletext
+		if (dvbdmxfeed->ts_type & TS_PACKET)
+			StopHWFilter(dvbdmxfeed->filter);
+		npids[2] = 0;
+		break;
+	case 0:
+	case 1:
+	case 4:
+		if (!pids_off)
+			return;
+		npids[i] = (pid[i]&0x8000) ? 0 : pid[i];
+		break;
+	}
+	ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]);
+}
+
+static int av7110_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *av7110 = demux->priv;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (!demux->dmx.frontend)
+		return -EINVAL;
+
+	if (feed->pid > 0x1fff)
+		return -EINVAL;
+
+	if (feed->type == DMX_TYPE_TS) {
+		if ((feed->ts_type & TS_DECODER) &&
+		    (feed->pes_type < DMX_TS_PES_OTHER)) {
+			switch (demux->dmx.frontend->source) {
+			case DMX_MEMORY_FE:
+				if (feed->ts_type & TS_DECODER)
+				       if (feed->pes_type < 2 &&
+					   !(demux->pids[0] & 0x8000) &&
+					   !(demux->pids[1] & 0x8000)) {
+					       dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+					       dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+					       av7110_av_start_play(av7110,RP_AV);
+					       demux->playing = 1;
+					}
+				break;
+			default:
+				dvb_feed_start_pid(feed);
+				break;
+			}
+		} else if ((feed->ts_type & TS_PACKET) &&
+			   (demux->dmx.frontend->source != DMX_MEMORY_FE)) {
+			StartHWFilter(feed->filter);
+		}
+	}
+
+	if (feed->type == DMX_TYPE_SEC) {
+		int i;
+
+		for (i = 0; i < demux->filternum; i++) {
+			if (demux->filter[i].state != DMX_STATE_READY)
+				continue;
+			if (demux->filter[i].type != DMX_TYPE_SEC)
+				continue;
+			if (demux->filter[i].filter.parent != &feed->feed.sec)
+				continue;
+			demux->filter[i].state = DMX_STATE_GO;
+			if (demux->dmx.frontend->source != DMX_MEMORY_FE)
+				StartHWFilter(&demux->filter[i]);
+		}
+	}
+
+	return 0;
+}
+
+
+static int av7110_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *av7110 = demux->priv;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (feed->type == DMX_TYPE_TS) {
+		if (feed->ts_type & TS_DECODER) {
+			if (feed->pes_type >= DMX_TS_PES_OTHER ||
+			    !demux->pesfilter[feed->pes_type])
+				return -EINVAL;
+			demux->pids[feed->pes_type] |= 0x8000;
+			demux->pesfilter[feed->pes_type] = NULL;
+		}
+		if (feed->ts_type & TS_DECODER &&
+		    feed->pes_type < DMX_TS_PES_OTHER) {
+			dvb_feed_stop_pid(feed);
+		} else
+			if ((feed->ts_type & TS_PACKET) &&
+			    (demux->dmx.frontend->source != DMX_MEMORY_FE))
+				StopHWFilter(feed->filter);
+	}
+
+	if (feed->type == DMX_TYPE_SEC) {
+		int i;
+
+		for (i = 0; i<demux->filternum; i++)
+			if (demux->filter[i].state == DMX_STATE_GO &&
+			    demux->filter[i].filter.parent == &feed->feed.sec) {
+				demux->filter[i].state = DMX_STATE_READY;
+				if (demux->dmx.frontend->source != DMX_MEMORY_FE)
+					StopHWFilter(&demux->filter[i]);
+		}
+	}
+
+	return 0;
+}
+
+
+static void restart_feeds(struct av7110 *av7110)
+{
+	struct dvb_demux *dvbdmx = &av7110->demux;
+	struct dvb_demux_feed *feed;
+	int mode;
+	int i;
+
+	dprintk(4, "%p\n", av7110);
+
+	mode = av7110->playing;
+	av7110->playing = 0;
+	av7110->rec_mode = 0;
+
+	for (i = 0; i < dvbdmx->filternum; i++) {
+		feed = &dvbdmx->feed[i];
+		if (feed->state == DMX_STATE_GO)
+			av7110_start_feed(feed);
+	}
+
+	if (mode)
+		av7110_av_start_play(av7110, mode);
+}
+
+static int dvb_get_stc(struct dmx_demux *demux, unsigned int num,
+		       uint64_t *stc, unsigned int *base)
+{
+	int ret;
+	u16 fwstc[4];
+	u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC);
+	struct dvb_demux *dvbdemux;
+	struct av7110 *av7110;
+
+	/* pointer casting paranoia... */
+	if (!demux)
+		BUG();
+	dvbdemux = (struct dvb_demux *) demux->priv;
+	if (!dvbdemux)
+		BUG();
+	av7110 = (struct av7110 *) dvbdemux->priv;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (num != 0)
+		return -EINVAL;
+
+	ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4);
+	if (ret) {
+		printk(KERN_ERR "%s: av7110_fw_request error\n", __FUNCTION__);
+		return -EIO;
+	}
+	dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n",
+		fwstc[0], fwstc[1], fwstc[2], fwstc[3]);
+
+	*stc =	(((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) |
+		(((uint64_t)  fwstc[1]) << 16) | ((uint64_t) fwstc[0]);
+	*base = 1;
+
+	dprintk(4, "stc = %lu\n", (unsigned long)*stc);
+
+	return 0;
+}
+
+
+/******************************************************************************
+ * SEC device file operations
+ ******************************************************************************/
+
+
+static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct av7110* av7110 = (struct av7110*) fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		Set22K(av7110, 1);
+		break;
+
+	case SEC_TONE_OFF:
+		Set22K(av7110, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe,
+					 struct dvb_diseqc_master_cmd* cmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1);
+
+	return 0;
+}
+
+static int av7110_diseqc_send_burst(struct dvb_frontend* fe,
+				    fe_sec_mini_cmd_t minicmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_diseqc_send(av7110, 0, NULL, minicmd);
+
+	return 0;
+}
+
+/* simplified code from budget-core.c */
+static int stop_ts_capture(struct av7110 *budget)
+{
+	dprintk(2, "budget: %p\n", budget);
+
+	if (--budget->feeding1)
+		return budget->feeding1;
+	saa7146_write(budget->dev, MC1, MASK_20);	/* DMA3 off */
+	SAA7146_IER_DISABLE(budget->dev, MASK_10);
+	SAA7146_ISR_CLEAR(budget->dev, MASK_10);
+	return 0;
+}
+
+static int start_ts_capture(struct av7110 *budget)
+{
+	dprintk(2, "budget: %p\n", budget);
+
+	if (budget->feeding1)
+		return ++budget->feeding1;
+	memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
+	budget->tsf = 0xff;
+	budget->ttbp = 0;
+	SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
+	saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
+	return ++budget->feeding1;
+}
+
+static int budget_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *budget = (struct av7110 *) demux->priv;
+	int status;
+
+	dprintk(2, "av7110: %p\n", budget);
+
+	spin_lock(&budget->feedlock1);
+	feed->pusi_seen = 0; /* have a clean section start */
+	status = start_ts_capture(budget);
+	spin_unlock(&budget->feedlock1);
+	return status;
+}
+
+static int budget_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *budget = (struct av7110 *) demux->priv;
+	int status;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	spin_lock(&budget->feedlock1);
+	status = stop_ts_capture(budget);
+	spin_unlock(&budget->feedlock1);
+	return status;
+}
+
+static void vpeirq(unsigned long data)
+{
+	struct av7110 *budget = (struct av7110 *) data;
+	u8 *mem = (u8 *) (budget->grabbing);
+	u32 olddma = budget->ttbp;
+	u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+
+	if (!budgetpatch) {
+		printk("av7110.c: vpeirq() called while budgetpatch disabled!"
+		       " check saa7146 IER register\n");
+		BUG();
+	}
+	/* nearest lower position divisible by 188 */
+	newdma -= newdma % 188;
+
+	if (newdma >= TS_BUFLEN)
+		return;
+
+	budget->ttbp = newdma;
+
+	if (!budget->feeding1 || (newdma == olddma))
+		return;
+
+#if 0
+	/* track rps1 activity */
+	printk("vpeirq: %02x Event Counter 1 0x%04x\n",
+	       mem[olddma],
+	       saa7146_read(budget->dev, EC1R) & 0x3fff);
+#endif
+
+	if (newdma > olddma)
+		/* no wraparound, dump olddma..newdma */
+		dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (newdma - olddma) / 188);
+	else {
+		/* wraparound, dump olddma..buflen and 0..newdma */
+		dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (TS_BUFLEN - olddma) / 188);
+		dvb_dmx_swfilter_packets(&budget->demux1, mem, newdma / 188);
+	}
+}
+
+static int av7110_register(struct av7110 *av7110)
+{
+	int ret, i;
+	struct dvb_demux *dvbdemux = &av7110->demux;
+	struct dvb_demux *dvbdemux1 = &av7110->demux1;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110->registered)
+		return -1;
+
+	av7110->registered = 1;
+
+	dvbdemux->priv = (void *) av7110;
+
+	for (i = 0; i < 32; i++)
+		av7110->handle2filter[i] = NULL;
+
+	dvbdemux->filternum = 32;
+	dvbdemux->feednum = 32;
+	dvbdemux->start_feed = av7110_start_feed;
+	dvbdemux->stop_feed = av7110_stop_feed;
+	dvbdemux->write_to_decoder = av7110_write_to_decoder;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+				      DMX_MEMORY_BASED_FILTERING);
+
+	dvb_dmx_init(&av7110->demux);
+	av7110->demux.dmx.get_stc = dvb_get_stc;
+
+	av7110->dmxdev.filternum = 32;
+	av7110->dmxdev.demux = &dvbdemux->dmx;
+	av7110->dmxdev.capabilities = 0;
+
+	dvb_dmxdev_init(&av7110->dmxdev, av7110->dvb_adapter);
+
+	av7110->hw_frontend.source = DMX_FRONTEND_0;
+
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend);
+
+	if (ret < 0)
+		return ret;
+
+	av7110->mem_frontend.source = DMX_MEMORY_FE;
+
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend);
+
+	if (ret < 0)
+		return ret;
+
+	ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx,
+					     &av7110->hw_frontend);
+	if (ret < 0)
+		return ret;
+
+	av7110_av_register(av7110);
+	av7110_ca_register(av7110);
+
+#ifdef CONFIG_DVB_AV7110_OSD
+	dvb_register_device(av7110->dvb_adapter, &av7110->osd_dev,
+			    &dvbdev_osd, av7110, DVB_DEVICE_OSD);
+#endif
+
+	dvb_net_init(av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx);
+
+	if (budgetpatch) {
+		/* initialize software demux1 without its own frontend
+		 * demux1 hardware is connected to frontend0 of demux0
+		 */
+		dvbdemux1->priv = (void *) av7110;
+
+		dvbdemux1->filternum = 256;
+		dvbdemux1->feednum = 256;
+		dvbdemux1->start_feed = budget_start_feed;
+		dvbdemux1->stop_feed = budget_stop_feed;
+		dvbdemux1->write_to_decoder = NULL;
+
+		dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+					       DMX_MEMORY_BASED_FILTERING);
+
+		dvb_dmx_init(&av7110->demux1);
+
+		av7110->dmxdev1.filternum = 256;
+		av7110->dmxdev1.demux = &dvbdemux1->dmx;
+		av7110->dmxdev1.capabilities = 0;
+
+		dvb_dmxdev_init(&av7110->dmxdev1, av7110->dvb_adapter);
+
+		dvb_net_init(av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx);
+		printk("dvb-ttpci: additional demux1 for budget-patch registered\n");
+	}
+	return 0;
+}
+
+
+static void dvb_unregister(struct av7110 *av7110)
+{
+	struct dvb_demux *dvbdemux = &av7110->demux;
+	struct dvb_demux *dvbdemux1 = &av7110->demux1;
+
+	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->registered)
+		return;
+
+	if (budgetpatch) {
+		dvb_net_release(&av7110->dvb_net1);
+		dvbdemux->dmx.close(&dvbdemux1->dmx);
+		dvb_dmxdev_release(&av7110->dmxdev1);
+		dvb_dmx_release(&av7110->demux1);
+	}
+
+	dvb_net_release(&av7110->dvb_net);
+
+	dvbdemux->dmx.close(&dvbdemux->dmx);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend);
+
+	dvb_dmxdev_release(&av7110->dmxdev);
+	dvb_dmx_release(&av7110->demux);
+
+	if (av7110->fe != NULL)
+		dvb_unregister_frontend(av7110->fe);
+	dvb_unregister_device(av7110->osd_dev);
+	av7110_av_unregister(av7110);
+	av7110_ca_unregister(av7110);
+}
+
+
+/****************************************************************************
+ * I2C client commands
+ ****************************************************************************/
+
+int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val)
+{
+	u8 msg[2] = { reg, val };
+	struct i2c_msg msgs;
+
+	msgs.flags = 0;
+	msgs.addr = id / 2;
+	msgs.len = 2;
+	msgs.buf = msg;
+	return i2c_transfer(&av7110->i2c_adap, &msgs, 1);
+}
+
+#if 0
+u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg)
+{
+	u8 mm1[] = {0x00};
+	u8 mm2[] = {0x00};
+	struct i2c_msg msgs[2];
+
+	msgs[0].flags = 0;
+	msgs[1].flags = I2C_M_RD;
+	msgs[0].addr = msgs[1].addr = id / 2;
+	mm1[0] = reg;
+	msgs[0].len = 1; msgs[1].len = 1;
+	msgs[0].buf = mm1; msgs[1].buf = mm2;
+	i2c_transfer(&av7110->i2c_adap, msgs, 2);
+
+	return mm2[0];
+}
+#endif
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+
+static int check_firmware(struct av7110* av7110)
+{
+	u32 crc = 0, len = 0;
+	unsigned char *ptr;
+
+	/* check for firmware magic */
+	ptr = av7110->bin_fw;
+	if (ptr[0] != 'A' || ptr[1] != 'V' ||
+	    ptr[2] != 'F' || ptr[3] != 'W') {
+		printk("dvb-ttpci: this is not an av7110 firmware\n");
+		return -EINVAL;
+	}
+	ptr += 4;
+
+	/* check dpram file */
+	crc = ntohl(*(u32*) ptr);
+	ptr += 4;
+	len = ntohl(*(u32*) ptr);
+	ptr += 4;
+	if (len >= 512) {
+		printk("dvb-ttpci: dpram file is way to big.\n");
+		return -EINVAL;
+	}
+	if (crc != crc32_le(0, ptr, len)) {
+		printk("dvb-ttpci: crc32 of dpram file does not match.\n");
+		return -EINVAL;
+	}
+	av7110->bin_dpram = ptr;
+	av7110->size_dpram = len;
+	ptr += len;
+
+	/* check root file */
+	crc = ntohl(*(u32*) ptr);
+	ptr += 4;
+	len = ntohl(*(u32*) ptr);
+	ptr += 4;
+
+	if (len <= 200000 || len >= 300000 ||
+	    len > ((av7110->bin_fw + av7110->size_fw) - ptr)) {
+		printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len);
+		return -EINVAL;
+	}
+	if( crc != crc32_le(0, ptr, len)) {
+		printk("dvb-ttpci: crc32 of root file does not match.\n");
+		return -EINVAL;
+	}
+	av7110->bin_root = ptr;
+	av7110->size_root = len;
+	return 0;
+}
+
+#ifdef CONFIG_DVB_AV7110_FIRMWARE_FILE
+#include "av7110_firm.h"
+static void put_firmware(struct av7110* av7110)
+{
+	av7110->bin_fw = NULL;
+}
+
+static inline int get_firmware(struct av7110* av7110)
+{
+	av7110->bin_fw = dvb_ttpci_fw;
+	av7110->size_fw = sizeof(dvb_ttpci_fw);
+	return check_firmware(av7110);
+}
+#else
+static void put_firmware(struct av7110* av7110)
+{
+	vfree(av7110->bin_fw);
+}
+
+static int get_firmware(struct av7110* av7110)
+{
+	int ret;
+	const struct firmware *fw;
+
+	/* request the av7110 firmware, this will block until someone uploads it */
+	ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev);
+	if (ret) {
+		if (ret == -ENOENT) {
+			printk(KERN_ERR "dvb-ttpci: could not load firmware,"
+			       " file not found: dvb-ttpci-01.fw\n");
+			printk(KERN_ERR "dvb-ttpci: usually this should be in"
+			       " /usr/lib/hotplug/firmware\n");
+			printk(KERN_ERR "dvb-ttpci: and can be downloaded here"
+			       " http://www.linuxtv.org/download/dvb/firmware/\n");
+		} else
+			printk(KERN_ERR "dvb-ttpci: cannot request firmware"
+			       " (error %i)\n", ret);
+		return -EINVAL;
+	}
+
+	if (fw->size <= 200000) {
+		printk("dvb-ttpci: this firmware is way too small.\n");
+		release_firmware(fw);
+		return -EINVAL;
+	}
+
+	/* check if the firmware is available */
+	av7110->bin_fw = (unsigned char *) vmalloc(fw->size);
+	if (NULL == av7110->bin_fw) {
+		dprintk(1, "out of memory\n");
+		release_firmware(fw);
+		return -ENOMEM;
+	}
+
+	memcpy(av7110->bin_fw, fw->data, fw->size);
+	av7110->size_fw = fw->size;
+	if ((ret = check_firmware(av7110)))
+		vfree(av7110->bin_fw);
+
+	release_firmware(fw);
+	return ret;
+}
+#endif
+
+
+static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = (struct av7110*) fe->dvb->priv;
+	u8 pwr = 0;
+	u8 buf[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	u32 div = (params->frequency + 479500) / 125;
+
+	if (params->frequency > 2000000) pwr = 3;
+	else if (params->frequency > 1800000) pwr = 2;
+	else if (params->frequency > 1600000) pwr = 1;
+	else if (params->frequency > 1200000) pwr = 0;
+	else if (params->frequency >= 1100000) pwr = 1;
+	else pwr = 2;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = ((div & 0x18000) >> 10) | 0x95;
+	buf[3] = (pwr << 6) | 0x30;
+
+        // NOTE: since we're using a prescaler of 2, we set the
+	// divisor frequency to 62.5kHz and divide by 125 above
+
+	if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config = {
+	.demod_address = 0x08,
+	.xin = 90100000UL,
+	.invert_pwm = 0,
+	.pll_set = alps_bsrv2_pll_set,
+};
+
+
+static u8 alps_bsru6_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x7d,   /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+	0x05, 0x35,   /* I2CT = 0, SCLT = 1, SDAT = 1 */
+	0x06, 0x40,   /* DAC not used, set to high impendance mode */
+	0x07, 0x00,   /* DAC LSB */
+	0x08, 0x40,   /* DiSEqC off, LNB power on OP2/LOCK pin on */
+	0x09, 0x00,   /* FIFO */
+	0x0c, 0x51,   /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+	0x0d, 0x82,   /* DC offset compensation = ON, beta_agc1 = 2 */
+	0x0e, 0x23,   /* alpha_tmg = 2, beta_tmg = 3 */
+	0x10, 0x3f,   // AGC2  0x3d
+	0x11, 0x84,
+	0x12, 0xb5,   // Lock detect: -64  Carrier freq detect:on
+	0x15, 0xc9,   // lock detector threshold
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,  // out imp: normal  out type: parallel FEC mode:0
+	0x29, 0x1e,  // 1/2 threshold
+	0x2a, 0x14,  // 2/3 threshold
+	0x2b, 0x0f,  // 3/4 threshold
+	0x2c, 0x09,  // 5/6 threshold
+	0x2d, 0x05,  // 7/8 threshold
+	0x2e, 0x01,
+	0x31, 0x1f,  // test all FECs
+	0x32, 0x19,  // viterbi and synchro search
+	0x33, 0xfc,  // rs control
+	0x34, 0x93,  // error control
+	0x0f, 0x52,
+	0xff, 0xff
+};
+
+static int alps_bsru6_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+	else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+	else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+	else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+	else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+	else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
+
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >>  8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio      ) & 0xf0);
+
+	return 0;
+}
+
+static int alps_bsru6_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = (struct av7110*) fe->dvb->priv;
+	int ret;
+	u8 data[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	if ((params->frequency < 950000) || (params->frequency > 2150000))
+		return -EINVAL;
+
+	div = (params->frequency + (125 - 1)) / 125; // round correctly
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+	data[3] = 0xC4;
+
+	if (params->frequency > 1530000) data[3] = 0xc0;
+
+	ret = i2c_transfer(&av7110->i2c_adap, &msg, 1);
+	if (ret != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct stv0299_config alps_bsru6_config = {
+
+	.demod_address = 0x68,
+	.inittab = alps_bsru6_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = alps_bsru6_set_symbol_rate,
+	.pll_set = alps_bsru6_pll_set,
+};
+
+
+
+static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (params->frequency + 35937500 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x85 | ((div >> 10) & 0x60);
+	data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81);
+
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct ves1820_config alps_tdbe2_config = {
+	.demod_address = 0x09,
+	.xin = 57840000UL,
+	.invert = 1,
+	.selagc = VES1820_SELAGC_SIGNAMPERR,
+	.pll_set = alps_tdbe2_pll_set,
+};
+
+
+
+
+static int grundig_29504_451_pll_set(struct dvb_frontend* fe,
+				     struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = params->frequency / 125;
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = 0x00;
+
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+	.demod_address = 0x68,
+	.pll_set = grundig_29504_451_pll_set,
+};
+
+
+
+static int philips_cd1516_pll_set(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters* params)
+{
+        struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u32 f = params->frequency;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (f + 36125000 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34);
+
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct ves1820_config philips_cd1516_config = {
+	.demod_address = 0x09,
+	.xin = 57840000UL,
+	.invert = 1,
+	.selagc = VES1820_SELAGC_SIGNAMPERR,
+	.pll_set = philips_cd1516_pll_set,
+};
+
+
+
+static int alps_tdlb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div, pwr;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (params->frequency + 36200000) / 166666;
+
+	if (params->frequency <= 782000000)
+		pwr = 1;
+	else
+		pwr = 2;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x85;
+	data[3] = pwr << 6;
+
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+	struct av7110* av7110 = (struct av7110*) fe->dvb->priv;
+
+	return request_firmware(fw, name, &av7110->dev->pci->dev);
+}
+
+static struct sp8870_config alps_tdlb7_config = {
+
+	.demod_address = 0x71,
+	.pll_set = alps_tdlb7_pll_set,
+	.request_firmware = alps_tdlb7_request_firmware,
+};
+
+
+
+static int nexusca_stv0297_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) };
+	struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 };
+	int i;
+
+	div = (params->frequency + 36150000 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0xce;
+
+	if (params->frequency < 45000000)
+		return -EINVAL;
+	else if (params->frequency < 137000000)
+		data[3] = 0x01;
+	else if (params->frequency < 403000000)
+		data[3] = 0x02;
+	else if (params->frequency < 860000000)
+		data[3] = 0x04;
+	else
+		return -EINVAL;
+
+	stv0297_enable_plli2c(fe);
+	if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) {
+		printk("nexusca: pll transfer failed!\n");
+		return -EIO;
+	}
+
+	// wait for PLL lock
+	for(i = 0; i < 20; i++) {
+
+		stv0297_enable_plli2c(fe);
+		if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1)
+			if (data[0] & 0x40) break;
+		msleep(10);
+	}
+
+	return 0;
+}
+
+static struct stv0297_config nexusca_stv0297_config = {
+
+	.demod_address = 0x1C,
+	.invert = 1,
+	.pll_set = nexusca_stv0297_pll_set,
+};
+
+
+
+static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = (struct av7110*) fe->dvb->priv;
+	u32 div;
+	u8 cfg, cpump, band_select;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (36125000 + params->frequency) / 166666;
+
+	cfg = 0x88;
+
+	if (params->frequency < 175000000) cpump = 2;
+	else if (params->frequency < 390000000) cpump = 1;
+	else if (params->frequency < 470000000) cpump = 2;
+	else if (params->frequency < 750000000) cpump = 1;
+	else cpump = 3;
+
+	if (params->frequency < 175000000) band_select = 0x0e;
+	else if (params->frequency < 470000000) band_select = 0x05;
+	else band_select = 0x03;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = ((div >> 10) & 0x60) | cfg;
+	data[3] = (cpump << 6) | band_select;
+
+	if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct l64781_config grundig_29504_401_config = {
+	.demod_address = 0x55,
+	.pll_set = grundig_29504_401_pll_set,
+};
+
+
+
+static void av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status)
+{
+	int synced = (status & FE_HAS_LOCK) ? 1 : 0;
+
+	av7110->fe_status = status;
+
+	if (av7110->fe_synced == synced)
+		return;
+
+	av7110->fe_synced = synced;
+
+	if (av7110->playing)
+		return;
+
+	if (down_interruptible(&av7110->pid_mutex))
+		return;
+
+	if (av7110->fe_synced) {
+		SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO],
+			av7110->pids[DMX_PES_AUDIO],
+			av7110->pids[DMX_PES_TELETEXT], 0,
+			av7110->pids[DMX_PES_PCR]);
+		av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+	} else {
+		SetPIDs(av7110, 0, 0, 0, 0, 0);
+		av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0);
+		av7110_wait_msgstate(av7110, GPMQBusy);
+	}
+
+	up(&av7110->pid_mutex);
+}
+
+static int av7110_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_set_frontend(fe, params);
+}
+
+static int av7110_fe_init(struct dvb_frontend* fe)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_init(fe);
+}
+
+static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+	int ret;
+
+	/* call the real implementation */
+	ret = av7110->fe_read_status(fe, status);
+	if (ret)
+		return ret;
+
+	if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) {
+		av7110_fe_lock_fix(av7110, *status);
+	}
+
+	return 0;
+}
+
+static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_diseqc_reset_overload(fe);
+}
+
+static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe,
+					    struct dvb_diseqc_master_cmd* cmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_diseqc_send_master_cmd(fe, cmd);
+}
+
+static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_diseqc_send_burst(fe, minicmd);
+}
+
+static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_set_tone(fe, tone);
+}
+
+static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_set_voltage(fe, voltage);
+}
+
+static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned int cmd)
+{
+	struct av7110* av7110 = fe->dvb->priv;
+
+	av7110_fe_lock_fix(av7110, 0);
+	return av7110->fe_dishnetwork_send_legacy_command(fe, cmd);
+}
+
+static u8 read_pwm(struct av7110* av7110)
+{
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
+				 { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
+
+        if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+static int frontend_init(struct av7110 *av7110)
+{
+	int ret;
+
+	if (av7110->dev->pci->subsystem_vendor == 0x110a) {
+		switch(av7110->dev->pci->subsystem_device) {
+		case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??))
+			av7110->fe = ves1820_attach(&philips_cd1516_config,
+						    &av7110->i2c_adap, read_pwm(av7110));
+			break;
+		}
+
+	} else if (av7110->dev->pci->subsystem_vendor == 0x13c2) {
+		switch(av7110->dev->pci->subsystem_device) {
+		case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X
+		case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X
+		case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE
+
+			// try the ALPS BSRV2 first of all
+			av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops->set_tone = av7110_set_tone;
+				break;
+			}
+
+			// try the ALPS BSRU6 now
+			av7110->fe = stv0299_attach(&alps_bsru6_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops->set_tone = av7110_set_tone;
+				break;
+			}
+
+			// Try the grundig 29504-451
+                        av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops->set_tone = av7110_set_tone;
+				break;
+			}
+
+			/* Try DVB-C cards */
+			switch(av7110->dev->pci->subsystem_device) {
+			case 0x0000:
+				/* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */
+				av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap,
+							read_pwm(av7110));
+				break;
+			case 0x0003:
+				/* Haupauge DVB-C 2.1 VES1820/ALPS TDBE2 */
+				av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap,
+							read_pwm(av7110));
+				break;
+			}
+			break;
+
+		case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X
+
+			// ALPS TDLB7
+                        av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap);
+			break;
+
+		case 0x0002: // Hauppauge/TT DVB-C premium rev2.X
+
+                        av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110));
+			break;
+
+		case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */
+			/* Grundig 29504-451 */
+			av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap);
+			if (av7110->fe) {
+				av7110->fe->ops->diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
+				av7110->fe->ops->diseqc_send_burst = av7110_diseqc_send_burst;
+				av7110->fe->ops->set_tone = av7110_set_tone;
+			}
+			break;
+
+		case 0x0008: // Hauppauge/TT DVB-T
+
+			av7110->fe = l64781_attach(&grundig_29504_401_config, &av7110->i2c_adap);
+			break;
+
+		case 0x000A: // Hauppauge/TT Nexus-CA rev1.X
+
+			av7110->fe = stv0297_attach(&nexusca_stv0297_config, &av7110->i2c_adap, 0x7b);
+			if (av7110->fe) {
+				/* set TDA9819 into DVB mode */
+				saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9198 pin9(STD)
+				saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF)
+
+				/* tuner on this needs a slower i2c bus speed */
+				av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
+				break;
+			}
+		}
+	}
+
+	if (!av7110->fe) {
+		/* FIXME: propagate the failure code from the lower layers */
+		ret = -ENOMEM;
+		printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		       av7110->dev->pci->vendor,
+		       av7110->dev->pci->device,
+		       av7110->dev->pci->subsystem_vendor,
+		       av7110->dev->pci->subsystem_device);
+	} else {
+		FE_FUNC_OVERRIDE(av7110->fe->ops->init, av7110->fe_init, av7110_fe_init);
+		FE_FUNC_OVERRIDE(av7110->fe->ops->read_status, av7110->fe_read_status, av7110_fe_read_status);
+		FE_FUNC_OVERRIDE(av7110->fe->ops->diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload);
+		FE_FUNC_OVERRIDE(av7110->fe->ops->diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd);
+		FE_FUNC_OVERRIDE(av7110->fe->ops->diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst);
+		FE_FUNC_OVERRIDE(av7110->fe->ops->set_tone, av7110->fe_set_tone, av7110_fe_set_tone);
+		FE_FUNC_OVERRIDE(av7110->fe->ops->set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage;)
+		FE_FUNC_OVERRIDE(av7110->fe->ops->dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command);
+		FE_FUNC_OVERRIDE(av7110->fe->ops->set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend);
+
+		ret = dvb_register_frontend(av7110->dvb_adapter, av7110->fe);
+		if (ret < 0) {
+			printk("av7110: Frontend registration failed!\n");
+			if (av7110->fe->ops->release)
+				av7110->fe->ops->release(av7110->fe);
+			av7110->fe = NULL;
+		}
+	}
+	return ret;
+}
+
+/* Budgetpatch note:
+ * Original hardware design by Roberto Deza:
+ * There is a DVB_Wiki at
+ * http://212.227.36.83/linuxtv/wiki/index.php/Main_Page
+ * where is described this 'DVB TT Budget Patch', on Card Modding:
+ * http://212.227.36.83/linuxtv/wiki/index.php/DVB_TT_Budget_Patch
+ * On the short description there is also a link to a external file,
+ * with more details:
+ * http://perso.wanadoo.es/jesussolano/Ttf_tsc1.zip
+ *
+ * New software triggering design by Emard that works on
+ * original Roberto Deza's hardware:
+ *
+ * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin.
+ * GPIO3 is in budget-patch hardware connectd to port B VSYNC
+ * HS is an internal event of 7146, accessible with RPS
+ * and temporarily raised high every n lines
+ * (n in defined in the RPS_THRESH1 counter threshold)
+ * I think HS is raised high on the beginning of the n-th line
+ * and remains high until this n-th line that triggered
+ * it is completely received. When the receiption of n-th line
+ * ends, HS is lowered.
+ *
+ * To transmit data over DMA, 7146 needs changing state at
+ * port B VSYNC pin. Any changing of port B VSYNC will
+ * cause some DMA data transfer, with more or less packets loss.
+ * It depends on the phase and frequency of VSYNC and
+ * the way of 7146 is instructed to trigger on port B (defined
+ * in DD1_INIT register, 3rd nibble from the right valid
+ * numbers are 0-7, see datasheet)
+ *
+ * The correct triggering can minimize packet loss,
+ * dvbtraffic should give this stable bandwidths:
+ *   22k transponder = 33814 kbit/s
+ * 27.5k transponder = 38045 kbit/s
+ * by experiment it is found that the best results
+ * (stable bandwidths and almost no packet loss)
+ * are obtained using DD1_INIT triggering number 2
+ * (Va at rising edge of VS Fa = HS x VS-failing forced toggle)
+ * and a VSYNC phase that occurs in the middle of DMA transfer
+ * (about byte 188*512=96256 in the DMA window).
+ *
+ * Phase of HS is still not clear to me how to control,
+ * It just happens to be so. It can be seen if one enables
+ * RPS_IRQ and print Event Counter 1 in vpeirq(). Every
+ * time RPS_INTERRUPT is called, the Event Counter 1 will
+ * increment. That's how the 7146 is programmed to do event
+ * counting in this budget-patch.c
+ * I *think* HPS setting has something to do with the phase
+ * of HS but I cant be 100% sure in that.
+ *
+ * hardware debug note: a working budget card (including budget patch)
+ * with vpeirq() interrupt setup in mode "0x90" (every 64K) will
+ * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes
+ * and that means 3*25=75 Hz of interrupt freqency, as seen by
+ * watch cat /proc/interrupts
+ *
+ * If this frequency is 3x lower (and data received in the DMA
+ * buffer don't start with 0x47, but in the middle of packets,
+ * whose lengths appear to be like 188 292 188 104 etc.
+ * this means VSYNC line is not connected in the hardware.
+ * (check soldering pcb and pins)
+ * The same behaviour of missing VSYNC can be duplicated on budget
+ * cards, by seting DD1_INIT trigger mode 7 in 3rd nibble.
+ */
+static int av7110_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *pci_ext)
+{
+	const int length = TS_WIDTH * TS_HEIGHT;
+	struct pci_dev *pdev = dev->pci;
+	struct av7110 *av7110;
+	int ret, count = 0;
+
+	dprintk(4, "dev: %p\n", dev);
+
+        /* Set RPS_IRQ to 1 to track rps1 activity.
+         * Enabling this won't send any interrupt to PC CPU.
+         */
+#define RPS_IRQ 0
+
+	if (budgetpatch == 1) {
+		budgetpatch = 0;
+		/* autodetect the presence of budget patch
+		 * this only works if saa7146 has been recently
+		 * reset with with MASK_31 to MC1
+		 *
+		 * will wait for VBI_B event (vertical blank at port B)
+		 * and will reset GPIO3 after VBI_B is detected.
+		 * (GPIO3 should be raised high by CPU to
+		 * test if GPIO3 will generate vertical blank signal
+		 * in budget patch GPIO3 is connected to VSYNC_B
+		 */
+
+		/* RESET SAA7146 */
+		saa7146_write(dev, MC1, MASK_31);
+		/* autodetection success seems to be time-dependend after reset */
+
+		/* Fix VSYNC level */
+		saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		/* set vsync_b triggering */
+		saa7146_write(dev, DD1_STREAM_B, 0);
+		/* port B VSYNC at rising edge */
+		saa7146_write(dev, DD1_INIT, 0x00000200);
+		saa7146_write(dev, BRS_CTRL, 0x00000000);  // VBI
+		saa7146_write(dev, MC2,
+			      1 * (MASK_08 | MASK_24)  |   // BRS control
+			      0 * (MASK_09 | MASK_25)  |   // a
+			      1 * (MASK_10 | MASK_26)  |   // b
+			      0 * (MASK_06 | MASK_22)  |   // HPS_CTRL1
+			      0 * (MASK_05 | MASK_21)  |   // HPS_CTRL2
+			      0 * (MASK_01 | MASK_15)      // DEBI
+		);
+
+		/* start writing RPS1 code from beginning */
+		count = 0;
+		/* Disable RPS1 */
+		saa7146_write(dev, MC1, MASK_29);
+		/* RPS1 timeout disable */
+		saa7146_write(dev, RPS_TOV1, 0);
+		WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_VBI_B));
+		WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
+		WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
+		WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+#if RPS_IRQ
+		/* issue RPS1 interrupt to increment counter */
+		WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+#endif
+		WRITE_RPS1(cpu_to_le32(CMD_STOP));
+		/* Jump to begin of RPS program as safety measure               (p37) */
+		WRITE_RPS1(cpu_to_le32(CMD_JUMP));
+		WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle));
+
+#if RPS_IRQ
+		/* set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
+		 * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
+		 * use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
+		 */
+		saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
+		/* set event counter 1 treshold to maximum allowed value        (rEC p55) */
+		saa7146_write(dev, ECT1R,  0x3fff );
+#endif
+		/* Set RPS1 Address register to point to RPS code               (r108 p42) */
+		saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+		/* Enable RPS1,                                                 (rFC p33) */
+		saa7146_write(dev, MC1, (MASK_13 | MASK_29 ));
+
+		mdelay(10);
+		/* now send VSYNC_B to rps1 by rising GPIO3 */
+		saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+		mdelay(10);
+		/* if rps1 responded by lowering the GPIO3,
+		 * then we have budgetpatch hardware
+		 */
+		if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) {
+			budgetpatch = 1;
+			printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n");
+		}
+		/* Disable RPS1 */
+		saa7146_write(dev, MC1, ( MASK_29 ));
+#if RPS_IRQ
+		printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff );
+#endif
+	}
+
+	/* prepare the av7110 device struct */
+	av7110 = kmalloc(sizeof(struct av7110), GFP_KERNEL);
+	if (!av7110) {
+		dprintk(1, "out of memory\n");
+		return -ENOMEM;
+	}
+
+	memset(av7110, 0, sizeof(struct av7110));
+
+	av7110->card_name = (char*) pci_ext->ext_priv;
+	av7110->dev = dev;
+	dev->ext_priv = av7110;
+
+	ret = get_firmware(av7110);
+	if (ret < 0)
+		goto err_kfree_0;
+
+	ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name,
+				   THIS_MODULE);
+	if (ret < 0)
+		goto err_put_firmware_1;
+
+	/* the Siemens DVB needs this if you want to have the i2c chips
+	   get recognized before the main driver is fully loaded */
+	saa7146_write(dev, GPIO_CTRL, 0x500000);
+
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+	av7110->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL;
+#else
+	av7110->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
+#endif
+	strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name));
+
+	saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */
+
+	ret = i2c_add_adapter(&av7110->i2c_adap);
+	if (ret < 0)
+		goto err_dvb_unregister_adapter_2;
+
+	ttpci_eeprom_parse_mac(&av7110->i2c_adap,
+			       av7110->dvb_adapter->proposed_mac);
+	ret = -ENOMEM;
+
+	if (budgetpatch) {
+		spin_lock_init(&av7110->feedlock1);
+		av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length,
+								 &av7110->pt);
+		if (!av7110->grabbing)
+			goto err_i2c_del_3;
+
+		saa7146_write(dev, PCI_BT_V1, 0x1c1f101f);
+		saa7146_write(dev, BCS_CTRL, 0x80400040);
+		/* set dd1 stream a & b */
+		saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+		saa7146_write(dev, DD1_INIT, 0x03000200);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+		saa7146_write(dev, BRS_CTRL, 0x60000000);
+		saa7146_write(dev, BASE_ODD3, 0);
+		saa7146_write(dev, BASE_EVEN3, 0);
+		saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+		saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90);
+
+		saa7146_write(dev, PITCH3, TS_WIDTH);
+		saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
+
+		/* upload all */
+		saa7146_write(dev, MC2, 0x077c077c);
+		saa7146_write(dev, GPIO_CTRL, 0x000000);
+#if RPS_IRQ
+		/* set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
+		 * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
+		 * use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
+		 */
+		saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
+		/* set event counter 1 treshold to maximum allowed value        (rEC p55) */
+		saa7146_write(dev, ECT1R,  0x3fff );
+#endif
+		/* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */
+		count = 0;
+
+		/* Wait Source Line Counter Threshold                           (p36) */
+		WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_HS));
+		/* Set GPIO3=1                                                  (p42) */
+		WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
+		WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
+		WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTHI<<24));
+#if RPS_IRQ
+		/* issue RPS1 interrupt */
+		WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+#endif
+		/* Wait reset Source Line Counter Threshold                     (p36) */
+		WRITE_RPS1(cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS));
+		/* Set GPIO3=0                                                  (p42) */
+		WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
+		WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
+		WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+#if RPS_IRQ
+		/* issue RPS1 interrupt */
+		WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+#endif
+		/* Jump to begin of RPS program                                 (p37) */
+		WRITE_RPS1(cpu_to_le32(CMD_JUMP));
+		WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle));
+
+		/* Fix VSYNC level */
+		saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		/* Set RPS1 Address register to point to RPS code               (r108 p42) */
+		saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+		/* Set Source Line Counter Threshold, using BRS                 (rCC p43)
+		 * It generates HS event every TS_HEIGHT lines
+		 * this is related to TS_WIDTH set in register
+		 * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits
+		 * are set to TS_WIDTH bytes (TS_WIDTH=2*188),
+		 * then RPS_THRESH1 should be set to trigger
+		 * every TS_HEIGHT (512) lines.
+		 */
+		saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 );
+
+		/* Enable RPS1                                                  (rFC p33) */
+		saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+		/* end of budgetpatch register initialization */
+		tasklet_init (&av7110->vpe_tasklet,  vpeirq,  (unsigned long) av7110);
+	} else {
+		saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+		saa7146_write(dev, BCS_CTRL, 0x80400040);
+
+		/* set dd1 stream a & b */
+		saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+		saa7146_write(dev, DD1_INIT, 0x03000000);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+		/* upload all */
+		saa7146_write(dev, MC2, 0x077c077c);
+		saa7146_write(dev, GPIO_CTRL, 0x000000);
+	}
+
+	tasklet_init (&av7110->debi_tasklet, debiirq, (unsigned long) av7110);
+	tasklet_init (&av7110->gpio_tasklet, gpioirq, (unsigned long) av7110);
+
+	sema_init(&av7110->pid_mutex, 1);
+
+	/* locks for data transfers from/to AV7110 */
+	spin_lock_init(&av7110->debilock);
+	sema_init(&av7110->dcomlock, 1);
+	av7110->debitype = -1;
+
+	/* default OSD window */
+	av7110->osdwin = 1;
+	sema_init(&av7110->osd_sema, 1);
+
+	/* ARM "watchdog" */
+	init_waitqueue_head(&av7110->arm_wait);
+	av7110->arm_thread = NULL;
+
+	/* allocate and init buffers */
+	av7110->debi_virt = pci_alloc_consistent(pdev, 8192, &av7110->debi_bus);
+	if (!av7110->debi_virt)
+		goto err_saa71466_vfree_4;
+
+
+	av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS);
+	if (!av7110->iobuf)
+		goto err_pci_free_5;
+
+	ret = av7110_av_init(av7110);
+	if (ret < 0)
+		goto err_iobuf_vfree_6;
+
+	/* init BMP buffer */
+	av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN;
+	init_waitqueue_head(&av7110->bmpq);
+
+	ret = av7110_ca_init(av7110);
+	if (ret < 0)
+		goto err_av7110_av_exit_7;
+
+	/* load firmware into AV7110 cards */
+	ret = av7110_bootarm(av7110);
+	if (ret < 0)
+		goto err_av7110_ca_exit_8;
+
+	ret = av7110_firmversion(av7110);
+	if (ret < 0)
+		goto err_stop_arm_9;
+
+	if (FW_VERSION(av7110->arm_app)<0x2501)
+		printk ("dvb-ttpci: Warning, firmware version 0x%04x is too old. "
+			"System might be unstable!\n", FW_VERSION(av7110->arm_app));
+
+	ret = kernel_thread(arm_thread, (void *) av7110, 0);
+	if (ret < 0)
+		goto err_stop_arm_9;
+
+	/* set initial volume in mixer struct */
+	av7110->mixer.volume_left  = volume;
+	av7110->mixer.volume_right = volume;
+
+	init_av7110_av(av7110);
+
+	ret = av7110_register(av7110);
+	if (ret < 0)
+		goto err_arm_thread_stop_10;
+
+	/* special case DVB-C: these cards have an analog tuner
+	   plus need some special handling, so we have separate
+	   saa7146_ext_vv data for these... */
+	ret = av7110_init_v4l(av7110);
+	if (ret < 0)
+		goto err_av7110_unregister_11;
+
+	av7110->dvb_adapter->priv = av7110;
+	ret = frontend_init(av7110);
+	if (ret < 0)
+		goto err_av7110_exit_v4l_12;
+
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+	av7110_ir_init();
+#endif
+	printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num);
+	av7110_num++;
+out:
+	return ret;
+
+err_av7110_exit_v4l_12:
+	av7110_exit_v4l(av7110);
+err_av7110_unregister_11:
+	dvb_unregister(av7110);
+err_arm_thread_stop_10:
+	av7110_arm_sync(av7110);
+err_stop_arm_9:
+	/* Nothing to do. Rejoice. */
+err_av7110_ca_exit_8:
+	av7110_ca_exit(av7110);
+err_av7110_av_exit_7:
+	av7110_av_exit(av7110);
+err_iobuf_vfree_6:
+	vfree(av7110->iobuf);
+err_pci_free_5:
+	pci_free_consistent(pdev, 8192, av7110->debi_virt, av7110->debi_bus);
+err_saa71466_vfree_4:
+	if (!av7110->grabbing)
+		saa7146_pgtable_free(pdev, &av7110->pt);
+err_i2c_del_3:
+	i2c_del_adapter(&av7110->i2c_adap);
+err_dvb_unregister_adapter_2:
+	dvb_unregister_adapter(av7110->dvb_adapter);
+err_put_firmware_1:
+	put_firmware(av7110);
+err_kfree_0:
+	kfree(av7110);
+	goto out;
+}
+
+static int av7110_detach(struct saa7146_dev* saa)
+{
+	struct av7110 *av7110 = saa->ext_priv;
+	dprintk(4, "%p\n", av7110);
+
+	if (budgetpatch) {
+		/* Disable RPS1 */
+		saa7146_write(saa, MC1, MASK_29);
+		/* VSYNC LOW (inactive) */
+		saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+		saa7146_write(saa, MC1, MASK_20);	/* DMA3 off */
+		SAA7146_IER_DISABLE(saa, MASK_10);
+		SAA7146_ISR_CLEAR(saa, MASK_10);
+		msleep(50);
+		tasklet_kill(&av7110->vpe_tasklet);
+		saa7146_pgtable_free(saa->pci, &av7110->pt);
+	}
+	av7110_exit_v4l(av7110);
+
+	av7110_arm_sync(av7110);
+
+	tasklet_kill(&av7110->debi_tasklet);
+	tasklet_kill(&av7110->gpio_tasklet);
+
+	dvb_unregister(av7110);
+
+	SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03);
+	SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03);
+
+	av7110_ca_exit(av7110);
+	av7110_av_exit(av7110);
+
+	vfree(av7110->iobuf);
+	pci_free_consistent(saa->pci, 8192, av7110->debi_virt,
+			    av7110->debi_bus);
+
+	i2c_del_adapter(&av7110->i2c_adap);
+
+	dvb_unregister_adapter (av7110->dvb_adapter);
+
+	av7110_num--;
+
+	put_firmware(av7110);
+
+	kfree(av7110);
+
+	saa->ext_priv = NULL;
+
+	return 0;
+}
+
+
+static void av7110_irq(struct saa7146_dev* dev, u32 *isr)
+{
+	struct av7110 *av7110 = dev->ext_priv;
+
+	//print_time("av7110_irq");
+
+	/* Note: Don't try to handle the DEBI error irq (MASK_18), in
+	 * intel mode the timeout is asserted all the time...
+	 */
+
+	if (*isr & MASK_19) {
+		//printk("av7110_irq: DEBI\n");
+		/* Note 1: The DEBI irq is level triggered: We must enable it
+		 * only after we started a DMA xfer, and disable it here
+		 * immediately, or it will be signalled all the time while
+		 * DEBI is idle.
+		 * Note 2: You would think that an irq which is masked is
+		 * not signalled by the hardware. Not so for the SAA7146:
+		 * An irq is signalled as long as the corresponding bit
+		 * in the ISR is set, and disabling irqs just prevents the
+		 * hardware from setting the ISR bit. This means a) that we
+		 * must clear the ISR *after* disabling the irq (which is why
+		 * we must do it here even though saa7146_core did it already),
+		 * and b) that if we were to disable an edge triggered irq
+		 * (like the gpio irqs sadly are) temporarily we would likely
+		 * loose some. This sucks :-(
+		 */
+		SAA7146_IER_DISABLE(av7110->dev, MASK_19);
+		SAA7146_ISR_CLEAR(av7110->dev, MASK_19);
+		tasklet_schedule(&av7110->debi_tasklet);
+	}
+
+	if (*isr & MASK_03) {
+		//printk("av7110_irq: GPIO\n");
+		tasklet_schedule(&av7110->gpio_tasklet);
+	}
+
+	if ((*isr & MASK_10) && budgetpatch)
+		tasklet_schedule(&av7110->vpe_tasklet);
+}
+
+
+static struct saa7146_extension av7110_extension;
+
+#define MAKE_AV7110_INFO(x_var,x_name) \
+static struct saa7146_pci_extension_data x_var = { \
+	.ext_priv = x_name, \
+	.ext = &av7110_extension }
+
+MAKE_AV7110_INFO(tts_1_X,    "Technotrend/Hauppauge WinTV DVB-S rev1.X");
+MAKE_AV7110_INFO(ttt_1_X,    "Technotrend/Hauppauge WinTV DVB-T rev1.X");
+MAKE_AV7110_INFO(ttc_1_X,    "Technotrend/Hauppauge WinTV Nexus-CA rev1.X");
+MAKE_AV7110_INFO(ttc_2_X,    "Technotrend/Hauppauge WinTV DVB-C rev2.X");
+MAKE_AV7110_INFO(tts_2_X,    "Technotrend/Hauppauge WinTV Nexus-S rev2.X");
+MAKE_AV7110_INFO(tts_1_3se,  "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE");
+MAKE_AV7110_INFO(ttt,        "Technotrend/Hauppauge DVB-T");
+MAKE_AV7110_INFO(fsc,        "Fujitsu Siemens DVB-C");
+MAKE_AV7110_INFO(fss,        "Fujitsu Siemens DVB-S rev1.6");
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(tts_1_X,   0x13c2, 0x0000),
+	MAKE_EXTENSION_PCI(ttt_1_X,   0x13c2, 0x0001),
+	MAKE_EXTENSION_PCI(ttc_2_X,   0x13c2, 0x0002),
+	MAKE_EXTENSION_PCI(tts_2_X,   0x13c2, 0x0003),
+	MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002),
+	MAKE_EXTENSION_PCI(fsc,       0x110a, 0x0000),
+	MAKE_EXTENSION_PCI(ttc_1_X,   0x13c2, 0x000a),
+	MAKE_EXTENSION_PCI(fss,       0x13c2, 0x0006),
+	MAKE_EXTENSION_PCI(ttt,       0x13c2, 0x0008),
+
+/*	MAKE_EXTENSION_PCI(???, 0x13c2, 0x0004), UNDEFINED CARD */ // Galaxis DVB PC-Sat-Carte
+/*	MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1
+/*	MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v????
+
+	{
+		.vendor    = 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+
+static struct saa7146_extension av7110_extension = {
+	.name		= "dvb\0",
+	.flags		= SAA7146_I2C_SHORT_DELAY,
+
+	.module		= THIS_MODULE,
+	.pci_tbl	= &pci_tbl[0],
+	.attach		= av7110_attach,
+	.detach		= av7110_detach,
+
+	.irq_mask	= MASK_19 | MASK_03 | MASK_10,
+	.irq_func	= av7110_irq,
+};
+
+
+static int __init av7110_init(void)
+{
+	int retval;
+	retval = saa7146_register_extension(&av7110_extension);
+	return retval;
+}
+
+
+static void __exit av7110_exit(void)
+{
+#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
+	av7110_ir_exit();
+#endif
+	saa7146_unregister_extension(&av7110_extension);
+}
+
+module_init(av7110_init);
+module_exit(av7110_exit);
+
+MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by "
+		   "Siemens, Technotrend, Hauppauge");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h
new file mode 100644
index 0000000..5070e05
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110.h
@@ -0,0 +1,284 @@
+#ifndef _AV7110_H_
+#define _AV7110_H_
+
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+#include <linux/i2c.h>
+
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/ca.h>
+#include <linux/dvb/osd.h>
+#include <linux/dvb/net.h>
+
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+#include "dvb_ringbuffer.h"
+#include "dvb_frontend.h"
+#include "ves1820.h"
+#include "ves1x93.h"
+#include "stv0299.h"
+#include "tda8083.h"
+#include "sp8870.h"
+#include "stv0297.h"
+#include "l64781.h"
+
+#include <media/saa7146_vv.h>
+
+
+#define ANALOG_TUNER_VES1820 1
+#define ANALOG_TUNER_STV0297 2
+#define ANALOG_TUNER_VBI     0x100
+
+extern int av7110_debug;
+
+#define dprintk(level,args...) \
+	    do { if ((av7110_debug & level)) { printk("dvb-ttpci: %s(): ", __FUNCTION__); printk(args); } } while (0)
+
+#define MAXFILT 32
+
+enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM};
+
+struct av7110_p2t {
+	u8		  pes[TS_SIZE];
+	u8		  counter;
+	long int	  pos;
+	int		  frags;
+	struct dvb_demux_feed *feed;
+};
+
+/* video MPEG decoder events: */
+/* (code copied from dvb_frontend.c, should maybe be factored out...) */
+#define MAX_VIDEO_EVENT 8
+struct dvb_video_events {
+	struct video_event	  events[MAX_VIDEO_EVENT];
+	int			  eventw;
+	int			  eventr;
+	int			  overflow;
+	wait_queue_head_t	  wait_queue;
+	spinlock_t		  lock;
+};
+
+
+/* place to store all the necessary device information */
+struct av7110 {
+
+	/* devices */
+
+	struct dvb_device	dvb_dev;
+	struct dvb_net		dvb_net;
+
+	struct video_device	*v4l_dev;
+	struct video_device	*vbi_dev;
+
+	struct saa7146_dev	*dev;
+
+	struct i2c_adapter	i2c_adap;
+
+	char			*card_name;
+
+	/* support for analog module of dvb-c */
+	int			analog_tuner_flags;
+	int			current_input;
+	u32			current_freq;
+
+	struct tasklet_struct	debi_tasklet;
+	struct tasklet_struct	gpio_tasklet;
+
+	int adac_type;	       /* audio DAC type */
+#define DVB_ADAC_TI	  0
+#define DVB_ADAC_CRYSTAL  1
+#define DVB_ADAC_MSP	  2
+#define DVB_ADAC_NONE	 -1
+
+
+	/* buffers */
+
+	void		       *iobuf;	 /* memory for all buffers */
+	struct dvb_ringbuffer	avout;   /* buffer for video or A/V mux */
+#define AVOUTLEN (128*1024)
+	struct dvb_ringbuffer	aout;    /* buffer for audio */
+#define AOUTLEN (64*1024)
+	void		       *bmpbuf;
+#define BMPLEN (8*32768+1024)
+
+	/* bitmap buffers and states */
+
+	int			bmpp;
+	int			bmplen;
+	volatile int		bmp_state;
+#define BMP_NONE     0
+#define BMP_LOADING  1
+#define BMP_LOADINGS 2
+#define BMP_LOADED   3
+	wait_queue_head_t	bmpq;
+
+
+	/* DEBI and polled command interface */
+
+	spinlock_t		debilock;
+	struct semaphore	dcomlock;
+	volatile int		debitype;
+	volatile int		debilen;
+
+
+	/* Recording and playback flags */
+
+	int			rec_mode;
+	int			playing;
+#define RP_NONE  0
+#define RP_VIDEO 1
+#define RP_AUDIO 2
+#define RP_AV	 3
+
+
+	/* OSD */
+
+	int			osdwin;      /* currently active window */
+	u16			osdbpp[8];
+	struct semaphore	osd_sema;
+
+	/* CA */
+
+	ca_slot_info_t		ci_slot[2];
+
+	int			vidmode;
+	struct dmxdev		dmxdev;
+	struct dvb_demux	demux;
+
+	struct dmx_frontend	hw_frontend;
+	struct dmx_frontend	mem_frontend;
+
+	/* for budget mode demux1 */
+	struct dmxdev		dmxdev1;
+	struct dvb_demux	demux1;
+	struct dvb_net		dvb_net1;
+	spinlock_t		feedlock1;
+	int			feeding1;
+	u8			tsf;
+	u32			ttbp;
+	unsigned char           *grabbing;
+	struct saa7146_pgtable  pt;
+	struct tasklet_struct   vpe_tasklet;
+
+	int			fe_synced;
+	struct semaphore	pid_mutex;
+
+	int			video_blank;
+	struct video_status	videostate;
+	int			display_ar;
+	int			trickmode;
+#define TRICK_NONE   0
+#define TRICK_FAST   1
+#define TRICK_SLOW   2
+#define TRICK_FREEZE 3
+	struct audio_status	audiostate;
+
+	struct dvb_demux_filter *handle2filter[32];
+	struct av7110_p2t	 p2t_filter[MAXFILT];
+	struct dvb_filter_pes2ts p2t[2];
+	struct ipack		 ipack[2];
+	u8			*kbuf[2];
+
+	int sinfo;
+	int feeding;
+
+	int arm_errors;
+	int registered;
+
+
+	/* AV711X */
+
+	u32		    arm_fw;
+	u32		    arm_rtsl;
+	u32		    arm_vid;
+	u32		    arm_app;
+	u32		    avtype;
+	int		    arm_ready;
+	struct task_struct *arm_thread;
+	wait_queue_head_t   arm_wait;
+	u16		    arm_loops;
+	int		    arm_rmmod;
+
+	void		   *debi_virt;
+	dma_addr_t	    debi_bus;
+
+	u16		    pids[DMX_PES_OTHER];
+
+	struct dvb_ringbuffer	 ci_rbuffer;
+	struct dvb_ringbuffer	 ci_wbuffer;
+
+	struct audio_mixer	mixer;
+
+	struct dvb_adapter	 *dvb_adapter;
+	struct dvb_device	 *video_dev;
+	struct dvb_device	 *audio_dev;
+	struct dvb_device	 *ca_dev;
+	struct dvb_device	 *osd_dev;
+
+	struct dvb_video_events  video_events;
+	video_size_t		 video_size;
+
+	u32		    ir_config;
+
+	/* firmware stuff */
+	unsigned char *bin_fw;
+	unsigned long size_fw;
+
+	unsigned char *bin_dpram;
+	unsigned long size_dpram;
+
+	unsigned char *bin_root;
+	unsigned long size_root;
+
+	struct dvb_frontend* fe;
+	fe_status_t fe_status;
+	int (*fe_init)(struct dvb_frontend* fe);
+	int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status);
+	int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe);
+	int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
+	int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
+	int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
+	int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+	int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned int cmd);
+	int (*fe_set_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+
+extern void ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
+		       u16 subpid, u16 pcrpid);
+
+extern void av7110_register_irc_handler(void (*func)(u32));
+extern void av7110_unregister_irc_handler(void (*func)(u32));
+extern void av7110_setup_irc_config (struct av7110 *av7110, u32 ir_config);
+
+extern int av7110_ir_init (void);
+extern void av7110_ir_exit (void);
+
+/* msp3400 i2c subaddresses */
+#define MSP_WR_DEM 0x10
+#define MSP_RD_DEM 0x11
+#define MSP_WR_DSP 0x12
+#define MSP_RD_DSP 0x13
+
+extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val);
+extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg);
+extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val);
+extern int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val);
+
+
+extern int av7110_init_analog_module(struct av7110 *av7110);
+extern int av7110_init_v4l(struct av7110 *av7110);
+extern int av7110_exit_v4l(struct av7110 *av7110);
+
+#endif /* _AV7110_H_ */
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
new file mode 100644
index 0000000..d77e8a0
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -0,0 +1,1459 @@
+/*
+ * av7110_av.c: audio and video MPEG decoder stuff
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/byteorder/swabb.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_av.h"
+#include "av7110_ipack.h"
+
+/* MPEG-2 (ISO 13818 / H.222.0) stream types */
+#define PROG_STREAM_MAP  0xBC
+#define PRIVATE_STREAM1  0xBD
+#define PADDING_STREAM	 0xBE
+#define PRIVATE_STREAM2  0xBF
+#define AUDIO_STREAM_S	 0xC0
+#define AUDIO_STREAM_E	 0xDF
+#define VIDEO_STREAM_S	 0xE0
+#define VIDEO_STREAM_E	 0xEF
+#define ECM_STREAM	 0xF0
+#define EMM_STREAM	 0xF1
+#define DSM_CC_STREAM	 0xF2
+#define ISO13522_STREAM  0xF3
+#define PROG_STREAM_DIR  0xFF
+
+#define PTS_DTS_FLAGS	 0xC0
+
+//pts_dts flags
+#define PTS_ONLY	 0x80
+#define PTS_DTS		 0xC0
+#define TS_SIZE		 188
+#define TRANS_ERROR	 0x80
+#define PAY_START	 0x40
+#define TRANS_PRIO	 0x20
+#define PID_MASK_HI	 0x1F
+//flags
+#define TRANS_SCRMBL1	 0x80
+#define TRANS_SCRMBL2	 0x40
+#define ADAPT_FIELD	 0x20
+#define PAYLOAD		 0x10
+#define COUNT_MASK	 0x0F
+
+// adaptation flags
+#define DISCON_IND	 0x80
+#define RAND_ACC_IND	 0x40
+#define ES_PRI_IND	 0x20
+#define PCR_FLAG	 0x10
+#define OPCR_FLAG	 0x08
+#define SPLICE_FLAG	 0x04
+#define TRANS_PRIV	 0x02
+#define ADAP_EXT_FLAG	 0x01
+
+// adaptation extension flags
+#define LTW_FLAG	 0x80
+#define PIECE_RATE	 0x40
+#define SEAM_SPLICE	 0x20
+
+
+static void p_to_t(u8 const *buf, long int length, u16 pid,
+		   u8 *counter, struct dvb_demux_feed *feed);
+
+
+int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv;
+
+	if (!(dvbdmxfeed->ts_type & TS_PACKET))
+		return 0;
+	if (buf[3] == 0xe0)	 // video PES do not have a length in TS
+		buf[4] = buf[5] = 0;
+	if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
+		return dvbdmxfeed->cb.ts(buf, len, NULL, 0,
+					 &dvbdmxfeed->feed.ts, DMX_OK);
+	else
+		return dvb_filter_pes2ts(p2t, buf, len, 1);
+}
+
+static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv;
+
+	dvbdmxfeed->cb.ts(data, 188, NULL, 0,
+			  &dvbdmxfeed->feed.ts, DMX_OK);
+	return 0;
+}
+
+int av7110_av_start_record(struct av7110 *av7110, int av,
+			   struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed);
+
+	if (av7110->playing || (av7110->rec_mode & av))
+		return -EBUSY;
+	av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+	dvbdmx->recording = 1;
+	av7110->rec_mode |= av;
+
+	switch (av7110->rec_mode) {
+	case RP_AUDIO:
+		dvb_filter_pes2ts_init(&av7110->p2t[0],
+				       dvbdmx->pesfilter[0]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[0]);
+		av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0);
+		break;
+
+	case RP_VIDEO:
+		dvb_filter_pes2ts_init(&av7110->p2t[1],
+				       dvbdmx->pesfilter[1]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[1]);
+		av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0);
+		break;
+
+	case RP_AV:
+		dvb_filter_pes2ts_init(&av7110->p2t[0],
+				       dvbdmx->pesfilter[0]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[0]);
+		dvb_filter_pes2ts_init(&av7110->p2t[1],
+				       dvbdmx->pesfilter[1]->pid,
+				       dvb_filter_pes2ts_cb,
+				       (void *) dvbdmx->pesfilter[1]);
+		av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0);
+		break;
+	}
+	return 0;
+}
+
+int av7110_av_start_play(struct av7110 *av7110, int av)
+{
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (av7110->rec_mode)
+		return -EBUSY;
+	if (av7110->playing & av)
+		return -EBUSY;
+
+	av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+
+	if (av7110->playing == RP_NONE) {
+		av7110_ipack_reset(&av7110->ipack[0]);
+		av7110_ipack_reset(&av7110->ipack[1]);
+	}
+
+	av7110->playing |= av;
+	switch (av7110->playing) {
+	case RP_AUDIO:
+		av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0);
+		break;
+	case RP_VIDEO:
+		av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0);
+		av7110->sinfo = 0;
+		break;
+	case RP_AV:
+		av7110->sinfo = 0;
+		av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0);
+		break;
+	}
+	return av7110->playing;
+}
+
+void av7110_av_stop(struct av7110 *av7110, int av)
+{
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!(av7110->playing & av) && !(av7110->rec_mode & av))
+		return;
+
+	av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+	if (av7110->playing) {
+		av7110->playing &= ~av;
+		switch (av7110->playing) {
+		case RP_AUDIO:
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0);
+			break;
+		case RP_VIDEO:
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0);
+			break;
+		case RP_NONE:
+			av7110_set_vidmode(av7110, av7110->vidmode);
+			break;
+		}
+	} else {
+		av7110->rec_mode &= ~av;
+		switch (av7110->rec_mode) {
+		case RP_AUDIO:
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0);
+			break;
+		case RP_VIDEO:
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0);
+			break;
+		case RP_NONE:
+			break;
+		}
+	}
+}
+
+
+int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen)
+{
+	int len;
+	u32 sync;
+	u16 blen;
+
+	if (!dlen) {
+		wake_up(&buf->queue);
+		return -1;
+	}
+	while (1) {
+		if ((len = dvb_ringbuffer_avail(buf)) < 6)
+			return -1;
+		sync =  DVB_RINGBUFFER_PEEK(buf, 0) << 24;
+		sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16;
+		sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8;
+		sync |= DVB_RINGBUFFER_PEEK(buf, 3);
+
+		if (((sync &~ 0x0f) == 0x000001e0) ||
+		    ((sync &~ 0x1f) == 0x000001c0) ||
+		    (sync == 0x000001bd))
+			break;
+		printk("resync\n");
+		DVB_RINGBUFFER_SKIP(buf, 1);
+	}
+	blen =  DVB_RINGBUFFER_PEEK(buf, 4) << 8;
+	blen |= DVB_RINGBUFFER_PEEK(buf, 5);
+	blen += 6;
+	if (len < blen || blen > dlen) {
+		//printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen);
+		wake_up(&buf->queue);
+		return -1;
+	}
+
+	dvb_ringbuffer_read(buf, dest, (size_t) blen, 0);
+
+	dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n",
+	       (unsigned long) buf->pread, (unsigned long) buf->pwrite);
+	wake_up(&buf->queue);
+	return blen;
+}
+
+
+int av7110_set_volume(struct av7110 *av7110, int volleft, int volright)
+{
+	int err, vol, val, balance = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	av7110->mixer.volume_left = volleft;
+	av7110->mixer.volume_right = volright;
+
+	switch (av7110->adac_type) {
+	case DVB_ADAC_TI:
+		volleft = (volleft * 256) / 1036;
+		volright = (volright * 256) / 1036;
+		if (volleft > 0x3f)
+			volleft = 0x3f;
+		if (volright > 0x3f)
+			volright = 0x3f;
+		if ((err = SendDAC(av7110, 3, 0x80 + volleft)))
+			return err;
+		return SendDAC(av7110, 4, volright);
+
+	case DVB_ADAC_CRYSTAL:
+		volleft = 127 - volleft / 2;
+		volright = 127 - volright / 2;
+		i2c_writereg(av7110, 0x20, 0x03, volleft);
+		i2c_writereg(av7110, 0x20, 0x04, volright);
+		return 0;
+
+	case DVB_ADAC_MSP:
+		vol  = (volleft > volright) ? volleft : volright;
+		val	= (vol * 0x73 / 255) << 8;
+		if (vol > 0)
+		       balance = ((volright - volleft) * 127) / vol;
+		msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8);
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */
+		msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */
+		return 0;
+	}
+	return 0;
+}
+
+void av7110_set_vidmode(struct av7110 *av7110, int mode)
+{
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode);
+
+	if (!av7110->playing) {
+		ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO],
+			   av7110->pids[DMX_PES_AUDIO],
+			   av7110->pids[DMX_PES_TELETEXT],
+			   0, av7110->pids[DMX_PES_PCR]);
+		av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+	}
+}
+
+
+static int sw2mode[16] = {
+	VIDEO_MODE_PAL, VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL,
+	VIDEO_MODE_NTSC, VIDEO_MODE_NTSC, VIDEO_MODE_PAL, VIDEO_MODE_NTSC,
+	VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL,
+	VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL, VIDEO_MODE_PAL,
+};
+
+static void get_video_format(struct av7110 *av7110, u8 *buf, int count)
+{
+	int i;
+	int hsize, vsize;
+	int sw;
+	u8 *p;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (av7110->sinfo)
+		return;
+	for (i = 7; i < count - 10; i++) {
+		p = buf + i;
+		if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3)
+			continue;
+		p += 4;
+		hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4);
+		vsize = ((p[1] &0x0F) << 8) | (p[2]);
+		sw = (p[3] & 0x0F);
+		av7110_set_vidmode(av7110, sw2mode[sw]);
+		dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw);
+		av7110->sinfo = 1;
+		break;
+	}
+}
+
+
+/****************************************************************************
+ * I/O buffer management and control
+ ****************************************************************************/
+
+static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf,
+					 const char *buf, unsigned long count)
+{
+	unsigned long todo = count;
+	int free;
+
+	while (todo > 0) {
+		if (dvb_ringbuffer_free(rbuf) < 2048) {
+			if (wait_event_interruptible(rbuf->queue,
+						     (dvb_ringbuffer_free(rbuf) >= 2048)))
+				return count - todo;
+		}
+		free = dvb_ringbuffer_free(rbuf);
+		if (free > todo)
+			free = todo;
+		dvb_ringbuffer_write(rbuf, buf, free);
+		todo -= free;
+		buf += free;
+	}
+
+	return count - todo;
+}
+
+static void play_video_cb(u8 *buf, int count, void *priv)
+{
+	struct av7110 *av7110 = (struct av7110 *) priv;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((buf[3] & 0xe0) == 0xe0) {
+		get_video_format(av7110, buf, count);
+		aux_ring_buffer_write(&av7110->avout, buf, count);
+	} else
+		aux_ring_buffer_write(&av7110->aout, buf, count);
+}
+
+static void play_audio_cb(u8 *buf, int count, void *priv)
+{
+	struct av7110 *av7110 = (struct av7110 *) priv;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	aux_ring_buffer_write(&av7110->aout, buf, count);
+}
+
+#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \
+		   dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024)
+
+static ssize_t dvb_play(struct av7110 *av7110, const u8 __user *buf,
+			unsigned long count, int nonblock, int type)
+{
+	unsigned long todo = count, n;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!av7110->kbuf[type])
+		return -ENOBUFS;
+
+	if (nonblock && !FREE_COND)
+		return -EWOULDBLOCK;
+
+	while (todo > 0) {
+		if (!FREE_COND) {
+			if (nonblock)
+				return count - todo;
+			if (wait_event_interruptible(av7110->avout.queue,
+						     FREE_COND))
+				return count - todo;
+		}
+		n = todo;
+		if (n > IPACKS * 2)
+			n = IPACKS * 2;
+		if (copy_from_user(av7110->kbuf[type], buf, n))
+			return -EFAULT;
+		av7110_ipack_instant_repack(av7110->kbuf[type], n,
+					    &av7110->ipack[type]);
+		todo -= n;
+		buf += n;
+	}
+	return count - todo;
+}
+
+static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf,
+			unsigned long count, int nonblock, int type)
+{
+	unsigned long todo = count, n;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!av7110->kbuf[type])
+		return -ENOBUFS;
+
+	if (nonblock && !FREE_COND)
+		return -EWOULDBLOCK;
+
+	while (todo > 0) {
+		if (!FREE_COND) {
+			if (nonblock)
+				return count - todo;
+			if (wait_event_interruptible(av7110->avout.queue,
+						     FREE_COND))
+				return count - todo;
+		}
+		n = todo;
+		if (n > IPACKS * 2)
+			n = IPACKS * 2;
+		av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]);
+		todo -= n;
+		buf += n;
+	}
+	return count - todo;
+}
+
+static ssize_t dvb_aplay(struct av7110 *av7110, const u8 __user *buf,
+			 unsigned long count, int nonblock, int type)
+{
+	unsigned long todo = count, n;
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!av7110->kbuf[type])
+		return -ENOBUFS;
+	if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024)
+		return -EWOULDBLOCK;
+
+	while (todo > 0) {
+		if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) {
+			if (nonblock)
+				return count - todo;
+			if (wait_event_interruptible(av7110->aout.queue,
+					(dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024)))
+				return count-todo;
+		}
+		n = todo;
+		if (n > IPACKS * 2)
+			n = IPACKS * 2;
+		if (copy_from_user(av7110->kbuf[type], buf, n))
+			return -EFAULT;
+		av7110_ipack_instant_repack(av7110->kbuf[type], n,
+					    &av7110->ipack[type]);
+		todo -= n;
+		buf += n;
+	}
+	return count - todo;
+}
+
+void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed)
+{
+	memset(p->pes, 0, TS_SIZE);
+	p->counter = 0;
+	p->pos = 0;
+	p->frags = 0;
+	if (feed)
+		p->feed = feed;
+}
+
+static void clear_p2t(struct av7110_p2t *p)
+{
+	memset(p->pes, 0, TS_SIZE);
+//	p->counter = 0;
+	p->pos = 0;
+	p->frags = 0;
+}
+
+
+static int find_pes_header(u8 const *buf, long int length, int *frags)
+{
+	int c = 0;
+	int found = 0;
+
+	*frags = 0;
+
+	while (c < length - 3 && !found) {
+		if (buf[c] == 0x00 && buf[c + 1] == 0x00 &&
+		    buf[c + 2] == 0x01) {
+			switch ( buf[c + 3] ) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+			case PRIVATE_STREAM1:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+				found = 1;
+				break;
+
+			default:
+				c++;
+				break;
+			}
+		} else
+			c++;
+	}
+	if (c == length - 3 && !found) {
+		if (buf[length - 1] == 0x00)
+			*frags = 1;
+		if (buf[length - 2] == 0x00 &&
+		    buf[length - 1] == 0x00)
+			*frags = 2;
+		if (buf[length - 3] == 0x00 &&
+		    buf[length - 2] == 0x00 &&
+		    buf[length - 1] == 0x01)
+			*frags = 3;
+		return -1;
+	}
+
+	return c;
+}
+
+void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p)
+{
+	int c, c2, l, add;
+	int check, rest;
+
+	c = 0;
+	c2 = 0;
+	if (p->frags){
+		check = 0;
+		switch(p->frags) {
+		case 1:
+			if (buf[c] == 0x00 && buf[c + 1] == 0x01) {
+				check = 1;
+				c += 2;
+			}
+			break;
+		case 2:
+			if (buf[c] == 0x01) {
+				check = 1;
+				c++;
+			}
+			break;
+		case 3:
+			check = 1;
+		}
+		if (check) {
+			switch (buf[c]) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+			case PRIVATE_STREAM1:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+				p->pes[0] = 0x00;
+				p->pes[1] = 0x00;
+				p->pes[2] = 0x01;
+				p->pes[3] = buf[c];
+				p->pos = 4;
+				memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos);
+				c += (TS_SIZE - 4) - p->pos;
+				p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed);
+				clear_p2t(p);
+				break;
+
+			default:
+				c = 0;
+				break;
+			}
+		}
+		p->frags = 0;
+	}
+
+	if (p->pos) {
+		c2 = find_pes_header(buf + c, length - c, &p->frags);
+		if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos)
+			l = c2+c;
+		else
+			l = (TS_SIZE - 4) - p->pos;
+		memcpy(p->pes + p->pos, buf, l);
+		c += l;
+		p->pos += l;
+		p_to_t(p->pes, p->pos, pid, &p->counter, p->feed);
+		clear_p2t(p);
+	}
+
+	add = 0;
+	while (c < length) {
+		c2 = find_pes_header(buf + c + add, length - c - add, &p->frags);
+		if (c2 >= 0) {
+			c2 += c + add;
+			if (c2 > c){
+				p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed);
+				c = c2;
+				clear_p2t(p);
+				add = 0;
+			} else
+				add = 1;
+		} else {
+			l = length - c;
+			rest = l % (TS_SIZE - 4);
+			l -= rest;
+			p_to_t(buf + c, l, pid, &p->counter, p->feed);
+			memcpy(p->pes, buf + c + l, rest);
+			p->pos = rest;
+			c = length;
+		}
+	}
+}
+
+
+static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length)
+{
+	int i;
+	int c = 0;
+	int fill;
+	u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 };
+
+	fill = (TS_SIZE - 4) - length;
+	if (pes_start)
+		tshead[1] = 0x40;
+	if (fill)
+		tshead[3] = 0x30;
+	tshead[1] |= (u8)((pid & 0x1F00) >> 8);
+	tshead[2] |= (u8)(pid & 0x00FF);
+	tshead[3] |= ((*counter)++ & 0x0F);
+	memcpy(buf, tshead, 4);
+	c += 4;
+
+	if (fill) {
+		buf[4] = fill - 1;
+		c++;
+		if (fill > 1) {
+			buf[5] = 0x00;
+			c++;
+		}
+		for (i = 6; i < fill + 4; i++) {
+			buf[i] = 0xFF;
+			c++;
+		}
+	}
+
+	return c;
+}
+
+
+static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter,
+		   struct dvb_demux_feed *feed)
+{
+	int l, pes_start;
+	u8 obuf[TS_SIZE];
+	long c = 0;
+
+	pes_start = 0;
+	if (length > 3 &&
+	     buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01)
+		switch (buf[3]) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+			case PRIVATE_STREAM1:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+				pes_start = 1;
+				break;
+
+			default:
+				break;
+		}
+
+	while (c < length) {
+		memset(obuf, 0, TS_SIZE);
+		if (length - c >= (TS_SIZE - 4)){
+			l = write_ts_header2(pid, counter, pes_start,
+					     obuf, (TS_SIZE - 4));
+			memcpy(obuf + l, buf + c, TS_SIZE - l);
+			c += TS_SIZE - l;
+		} else {
+			l = write_ts_header2(pid, counter, pes_start,
+					     obuf, length - c);
+			memcpy(obuf + l, buf + c, TS_SIZE - l);
+			c = length;
+		}
+		feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+		pes_start = 0;
+	}
+}
+
+
+int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct av7110 *av7110 = (struct av7110 *) demux->priv;
+	struct ipack *ipack = &av7110->ipack[feed->pes_type];
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	switch (feed->pes_type) {
+	case 0:
+		if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
+			return -EINVAL;
+		break;
+	case 1:
+		if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY)
+			return -EINVAL;
+		break;
+	default:
+		return -1;
+	}
+
+	if (!(buf[3] & 0x10)) /* no payload? */
+		return -1;
+	if (buf[1] & 0x40)
+		av7110_ipack_flush(ipack);
+
+	if (buf[3] & 0x20) {  /* adaptation field? */
+		len -= buf[4] + 1;
+		buf += buf[4] + 1;
+		if (!len)
+			return 0;
+	}
+
+	av7110_ipack_instant_repack(buf + 4, len - 4, &av7110->ipack[feed->pes_type]);
+	return 0;
+}
+
+
+
+/******************************************************************************
+ * Video MPEG decoder events
+ ******************************************************************************/
+void dvb_video_add_event(struct av7110 *av7110, struct video_event *event)
+{
+	struct dvb_video_events *events = &av7110->video_events;
+	int wp;
+
+	spin_lock_bh(&events->lock);
+
+	wp = (events->eventw + 1) % MAX_VIDEO_EVENT;
+	if (wp == events->eventr) {
+		events->overflow = 1;
+		events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT;
+	}
+
+	//FIXME: timestamp?
+	memcpy(&events->events[events->eventw], event, sizeof(struct video_event));
+	events->eventw = wp;
+
+	spin_unlock_bh(&events->lock);
+
+	wake_up_interruptible(&events->wait_queue);
+}
+
+
+static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags)
+{
+	struct dvb_video_events *events = &av7110->video_events;
+
+	if (events->overflow) {
+		events->overflow = 0;
+		return -EOVERFLOW;
+	}
+	if (events->eventw == events->eventr) {
+		int ret;
+
+		if (flags & O_NONBLOCK)
+			return -EWOULDBLOCK;
+
+		ret = wait_event_interruptible(events->wait_queue,
+					       events->eventw != events->eventr);
+		if (ret < 0)
+			return ret;
+	}
+
+	spin_lock_bh(&events->lock);
+
+	memcpy(event, &events->events[events->eventr],
+	       sizeof(struct video_event));
+	events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT;
+
+	spin_unlock_bh(&events->lock);
+
+	return 0;
+}
+
+
+/******************************************************************************
+ * DVB device file operations
+ ******************************************************************************/
+
+static unsigned int dvb_video_poll(struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	unsigned int mask = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY)
+		poll_wait(file, &av7110->avout.queue, wait);
+
+	poll_wait(file, &av7110->video_events.wait_queue, wait);
+
+	if (av7110->video_events.eventw != av7110->video_events.eventr)
+		mask = POLLPRI;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		if (av7110->playing) {
+			if (FREE_COND)
+				mask |= (POLLOUT | POLLWRNORM);
+			} else /* if not playing: may play if asked for */
+				mask |= (POLLOUT | POLLWRNORM);
+	}
+
+	return mask;
+}
+
+static ssize_t dvb_video_write(struct file *file, const char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+		return -EPERM;
+
+	if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY)
+		return -EPERM;
+
+	return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1);
+}
+
+static unsigned int dvb_audio_poll(struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	unsigned int mask = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	poll_wait(file, &av7110->aout.queue, wait);
+
+	if (av7110->playing) {
+		if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024)
+			mask |= (POLLOUT | POLLWRNORM);
+	} else /* if not playing: may play if asked for */
+		mask = (POLLOUT | POLLWRNORM);
+
+	return mask;
+}
+
+static ssize_t dvb_audio_write(struct file *file, const char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) {
+		printk(KERN_ERR "not audio source memory\n");
+		return -EPERM;
+	}
+	return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0);
+}
+
+static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 };
+
+#define MIN_IFRAME 400000
+
+static int play_iframe(struct av7110 *av7110, u8 __user *buf, unsigned int len, int nonblock)
+{
+	int i, n;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (!(av7110->playing & RP_VIDEO)) {
+		if (av7110_av_start_play(av7110, RP_VIDEO) < 0)
+			return -EBUSY;
+	}
+
+	/* setting n always > 1, fixes problems when playing stillframes
+	   consisting of I- and P-Frames */
+	n = MIN_IFRAME / len + 1;
+
+	/* FIXME: nonblock? */
+	dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1);
+
+	for (i = 0; i < n; i++)
+		dvb_play(av7110, buf, len, 0, 1);
+
+	av7110_ipack_flush(&av7110->ipack[1]);
+	return 0;
+}
+
+
+static int dvb_video_ioctl(struct inode *inode, struct file *file,
+			   unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	unsigned long arg = (unsigned long) parg;
+	int ret = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+		if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT &&
+		     cmd != VIDEO_GET_SIZE ) {
+			return -EPERM;
+		}
+	}
+
+	switch (cmd) {
+	case VIDEO_STOP:
+		av7110->videostate.play_state = VIDEO_STOPPED;
+		if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY)
+			av7110_av_stop(av7110, RP_VIDEO);
+		else
+			vidcom(av7110, VIDEO_CMD_STOP,
+			       av7110->videostate.video_blank ? 0 : 1);
+		av7110->trickmode = TRICK_NONE;
+		break;
+
+	case VIDEO_PLAY:
+		av7110->trickmode = TRICK_NONE;
+		if (av7110->videostate.play_state == VIDEO_FREEZED) {
+			av7110->videostate.play_state = VIDEO_PLAYING;
+			vidcom(av7110, VIDEO_CMD_PLAY, 0);
+		}
+
+		if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) {
+			if (av7110->playing == RP_AV) {
+				av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+				av7110->playing &= ~RP_VIDEO;
+			}
+			av7110_av_start_play(av7110, RP_VIDEO);
+			vidcom(av7110, VIDEO_CMD_PLAY, 0);
+		} else {
+			//av7110_av_stop(av7110, RP_VIDEO);
+			vidcom(av7110, VIDEO_CMD_PLAY, 0);
+		}
+		av7110->videostate.play_state = VIDEO_PLAYING;
+		break;
+
+	case VIDEO_FREEZE:
+		av7110->videostate.play_state = VIDEO_FREEZED;
+		if (av7110->playing & RP_VIDEO)
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0);
+		else
+			vidcom(av7110, VIDEO_CMD_FREEZE, 1);
+		av7110->trickmode = TRICK_FREEZE;
+		break;
+
+	case VIDEO_CONTINUE:
+		if (av7110->playing & RP_VIDEO)
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0);
+		vidcom(av7110, VIDEO_CMD_PLAY, 0);
+		av7110->videostate.play_state = VIDEO_PLAYING;
+		av7110->trickmode = TRICK_NONE;
+		break;
+
+	case VIDEO_SELECT_SOURCE:
+		av7110->videostate.stream_source = (video_stream_source_t) arg;
+		break;
+
+	case VIDEO_SET_BLANK:
+		av7110->videostate.video_blank = (int) arg;
+		break;
+
+	case VIDEO_GET_STATUS:
+		memcpy(parg, &av7110->videostate, sizeof(struct video_status));
+		break;
+
+	case VIDEO_GET_EVENT:
+		ret=dvb_video_get_event(av7110, parg, file->f_flags);
+		break;
+
+	case VIDEO_GET_SIZE:
+		memcpy(parg, &av7110->video_size, sizeof(video_size_t));
+		break;
+
+	case VIDEO_SET_DISPLAY_FORMAT:
+	{
+		video_displayformat_t format = (video_displayformat_t) arg;
+		u16 val = 0;
+
+		switch (format) {
+		case VIDEO_PAN_SCAN:
+			val = VID_PAN_SCAN_PREF;
+			break;
+
+		case VIDEO_LETTER_BOX:
+			val = VID_VC_AND_PS_PREF;
+			break;
+
+		case VIDEO_CENTER_CUT_OUT:
+			val = VID_CENTRE_CUT_PREF;
+			break;
+
+		default:
+			ret = -EINVAL;
+		}
+		if (ret < 0)
+			break;
+		av7110->videostate.video_format = format;
+		ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType,
+				    1, (u16) val);
+		break;
+	}
+
+	case VIDEO_SET_FORMAT:
+		if (arg > 1) {
+			ret = -EINVAL;
+			break;
+		}
+		av7110->display_ar = arg;
+		ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType,
+				    1, (u16) arg);
+		break;
+
+	case VIDEO_STILLPICTURE:
+	{
+		struct video_still_picture *pic =
+			(struct video_still_picture *) parg;
+		av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY;
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+		ret = play_iframe(av7110, pic->iFrame, pic->size,
+				  file->f_flags & O_NONBLOCK);
+		break;
+	}
+
+	case VIDEO_FAST_FORWARD:
+		//note: arg is ignored by firmware
+		if (av7110->playing & RP_VIDEO)
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+				      __Scan_I, 2, AV_PES, 0);
+		else
+			vidcom(av7110, VIDEO_CMD_FFWD, arg);
+		av7110->trickmode = TRICK_FAST;
+		av7110->videostate.play_state = VIDEO_PLAYING;
+		break;
+
+	case VIDEO_SLOWMOTION:
+		if (av7110->playing&RP_VIDEO) {
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0);
+			vidcom(av7110, VIDEO_CMD_SLOW, arg);
+		} else {
+			vidcom(av7110, VIDEO_CMD_PLAY, 0);
+			vidcom(av7110, VIDEO_CMD_STOP, 0);
+			vidcom(av7110, VIDEO_CMD_SLOW, arg);
+		}
+		av7110->trickmode = TRICK_SLOW;
+		av7110->videostate.play_state = VIDEO_PLAYING;
+		break;
+
+	case VIDEO_GET_CAPABILITIES:
+		*(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 |
+			VIDEO_CAP_SYS | VIDEO_CAP_PROG;
+		break;
+
+	case VIDEO_CLEAR_BUFFER:
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+		av7110_ipack_reset(&av7110->ipack[1]);
+
+		if (av7110->playing == RP_AV) {
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+				      __Play, 2, AV_PES, 0);
+			if (av7110->trickmode == TRICK_FAST)
+				av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+					      __Scan_I, 2, AV_PES, 0);
+			if (av7110->trickmode == TRICK_SLOW) {
+				av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+					      __Slow, 2, 0, 0);
+				vidcom(av7110, VIDEO_CMD_SLOW, arg);
+			}
+			if (av7110->trickmode == TRICK_FREEZE)
+				vidcom(av7110, VIDEO_CMD_STOP, 1);
+		}
+		break;
+
+	case VIDEO_SET_STREAMTYPE:
+
+		break;
+
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	return ret;
+}
+
+static int dvb_audio_ioctl(struct inode *inode, struct file *file,
+			   unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	unsigned long arg = (unsigned long) parg;
+	int ret = 0;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (((file->f_flags & O_ACCMODE) == O_RDONLY) &&
+	    (cmd != AUDIO_GET_STATUS))
+		return -EPERM;
+
+	switch (cmd) {
+	case AUDIO_STOP:
+		if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
+			av7110_av_stop(av7110, RP_AUDIO);
+		else
+			audcom(av7110, AUDIO_CMD_MUTE);
+		av7110->audiostate.play_state = AUDIO_STOPPED;
+		break;
+
+	case AUDIO_PLAY:
+		if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)
+			av7110_av_start_play(av7110, RP_AUDIO);
+		audcom(av7110, AUDIO_CMD_UNMUTE);
+		av7110->audiostate.play_state = AUDIO_PLAYING;
+		break;
+
+	case AUDIO_PAUSE:
+		audcom(av7110, AUDIO_CMD_MUTE);
+		av7110->audiostate.play_state = AUDIO_PAUSED;
+		break;
+
+	case AUDIO_CONTINUE:
+		if (av7110->audiostate.play_state == AUDIO_PAUSED) {
+			av7110->audiostate.play_state = AUDIO_PLAYING;
+			audcom(av7110, AUDIO_CMD_MUTE | AUDIO_CMD_PCM16);
+		}
+		break;
+
+	case AUDIO_SELECT_SOURCE:
+		av7110->audiostate.stream_source = (audio_stream_source_t) arg;
+		break;
+
+	case AUDIO_SET_MUTE:
+	{
+		audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE);
+		av7110->audiostate.mute_state = (int) arg;
+		break;
+	}
+
+	case AUDIO_SET_AV_SYNC:
+		av7110->audiostate.AV_sync_state = (int) arg;
+		audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF);
+		break;
+
+	case AUDIO_SET_BYPASS_MODE:
+		ret = -EINVAL;
+		break;
+
+	case AUDIO_CHANNEL_SELECT:
+		av7110->audiostate.channel_select = (audio_channel_select_t) arg;
+
+		switch(av7110->audiostate.channel_select) {
+		case AUDIO_STEREO:
+			audcom(av7110, AUDIO_CMD_STEREO);
+			break;
+
+		case AUDIO_MONO_LEFT:
+			audcom(av7110, AUDIO_CMD_MONO_L);
+			break;
+
+		case AUDIO_MONO_RIGHT:
+			audcom(av7110, AUDIO_CMD_MONO_R);
+			break;
+
+		default:
+			ret = -EINVAL;
+			break;
+		}
+		break;
+
+	case AUDIO_GET_STATUS:
+		memcpy(parg, &av7110->audiostate, sizeof(struct audio_status));
+		break;
+
+	case AUDIO_GET_CAPABILITIES:
+		*(int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2;
+		break;
+
+	case AUDIO_CLEAR_BUFFER:
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+		av7110_ipack_reset(&av7110->ipack[0]);
+		if (av7110->playing == RP_AV)
+			av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+			       __Play, 2, AV_PES, 0);
+		break;
+	case AUDIO_SET_ID:
+
+		break;
+	case AUDIO_SET_MIXER:
+	{
+		struct audio_mixer *amix = (struct audio_mixer *)parg;
+
+		av7110_set_volume(av7110, amix->volume_left, amix->volume_right);
+		break;
+	}
+	case AUDIO_SET_STREAMTYPE:
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+	}
+	return ret;
+}
+
+
+static int dvb_video_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	int err;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((err = dvb_generic_open(inode, file)) < 0)
+		return err;
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+		dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout);
+		av7110->video_blank = 1;
+		av7110->audiostate.AV_sync_state = 1;
+		av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX;
+
+		/*  empty event queue */
+		av7110->video_events.eventr = av7110->video_events.eventw = 0;
+	}
+
+	return 0;
+}
+
+static int dvb_video_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+		av7110_av_stop(av7110, RP_VIDEO);
+	}
+
+	return dvb_generic_release(inode, file);
+}
+
+static int dvb_audio_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	int err=dvb_generic_open(inode, file);
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	if (err < 0)
+		return err;
+	dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout);
+	av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX;
+	return 0;
+}
+
+static int dvb_audio_release(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+
+	dprintk(2, "av7110:%p, \n", av7110);
+
+	av7110_av_stop(av7110, RP_AUDIO);
+	return dvb_generic_release(inode, file);
+}
+
+
+
+/******************************************************************************
+ * driver registration
+ ******************************************************************************/
+
+static struct file_operations dvb_video_fops = {
+	.owner		= THIS_MODULE,
+	.write		= dvb_video_write,
+	.ioctl		= dvb_generic_ioctl,
+	.open		= dvb_video_open,
+	.release	= dvb_video_release,
+	.poll		= dvb_video_poll,
+};
+
+static struct dvb_device dvbdev_video = {
+	.priv		= NULL,
+	.users		= 6,
+	.readers	= 5,	/* arbitrary */
+	.writers	= 1,
+	.fops		= &dvb_video_fops,
+	.kernel_ioctl	= dvb_video_ioctl,
+};
+
+static struct file_operations dvb_audio_fops = {
+	.owner		= THIS_MODULE,
+	.write		= dvb_audio_write,
+	.ioctl		= dvb_generic_ioctl,
+	.open		= dvb_audio_open,
+	.release	= dvb_audio_release,
+	.poll		= dvb_audio_poll,
+};
+
+static struct dvb_device dvbdev_audio = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_audio_fops,
+	.kernel_ioctl	= dvb_audio_ioctl,
+};
+
+
+int av7110_av_register(struct av7110 *av7110)
+{
+	av7110->audiostate.AV_sync_state = 0;
+	av7110->audiostate.mute_state = 0;
+	av7110->audiostate.play_state = AUDIO_STOPPED;
+	av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX;
+	av7110->audiostate.channel_select = AUDIO_STEREO;
+	av7110->audiostate.bypass_mode = 0;
+
+	av7110->videostate.video_blank = 0;
+	av7110->videostate.play_state = VIDEO_STOPPED;
+	av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX;
+	av7110->videostate.video_format = VIDEO_FORMAT_4_3;
+	av7110->videostate.display_format = VIDEO_CENTER_CUT_OUT;
+	av7110->display_ar = VIDEO_FORMAT_4_3;
+
+	init_waitqueue_head(&av7110->video_events.wait_queue);
+	spin_lock_init(&av7110->video_events.lock);
+	av7110->video_events.eventw = av7110->video_events.eventr = 0;
+	av7110->video_events.overflow = 0;
+	memset(&av7110->video_size, 0, sizeof (video_size_t));
+
+	dvb_register_device(av7110->dvb_adapter, &av7110->video_dev,
+			    &dvbdev_video, av7110, DVB_DEVICE_VIDEO);
+
+	dvb_register_device(av7110->dvb_adapter, &av7110->audio_dev,
+			    &dvbdev_audio, av7110, DVB_DEVICE_AUDIO);
+
+	return 0;
+}
+
+void av7110_av_unregister(struct av7110 *av7110)
+{
+	dvb_unregister_device(av7110->audio_dev);
+	dvb_unregister_device(av7110->video_dev);
+}
+
+int av7110_av_init(struct av7110 *av7110)
+{
+	void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb };
+	int i, ret;
+
+	av7110->vidmode = VIDEO_MODE_PAL;
+
+	for (i = 0; i < 2; i++) {
+		struct ipack *ipack = av7110->ipack + i;
+
+		ret = av7110_ipack_init(ipack, IPACKS, play[i]);
+		if (ret < 0) {
+			if (i)
+				av7110_ipack_free(--ipack);
+			goto out;
+		}
+		ipack->data = av7110;
+	}
+
+	dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN);
+	dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN);
+
+	av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN);
+	av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS;
+out:
+	return ret;
+}
+
+void av7110_av_exit(struct av7110 *av7110)
+{
+	av7110_ipack_free(&av7110->ipack[0]);
+	av7110_ipack_free(&av7110->ipack[1]);
+}
diff --git a/drivers/media/dvb/ttpci/av7110_av.h b/drivers/media/dvb/ttpci/av7110_av.h
new file mode 100644
index 0000000..cc5e7a7
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_av.h
@@ -0,0 +1,29 @@
+#ifndef _AV7110_AV_H_
+#define _AV7110_AV_H_
+
+struct av7110;
+
+extern void av7110_set_vidmode(struct av7110 *av7110, int mode);
+
+extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len);
+extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen);
+extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len);
+
+extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright);
+extern void av7110_av_stop(struct av7110 *av7110, int av);
+extern int av7110_av_start_record(struct av7110 *av7110, int av,
+			  struct dvb_demux_feed *dvbdmxfeed);
+extern int av7110_av_start_play(struct av7110 *av7110, int av);
+
+extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event);
+
+extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed);
+extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p);
+
+extern int av7110_av_register(struct av7110 *av7110);
+extern void av7110_av_unregister(struct av7110 *av7110);
+extern int av7110_av_init(struct av7110 *av7110);
+extern void av7110_av_exit(struct av7110 *av7110);
+
+
+#endif /* _AV7110_AV_H_ */
diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c
new file mode 100644
index 0000000..21f7aac
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_ca.c
@@ -0,0 +1,390 @@
+/*
+ * av7110_ca.c: CA and CI stuff
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+#include <linux/byteorder/swabb.h>
+#include <linux/smp_lock.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+
+
+void CI_handle(struct av7110 *av7110, u8 *data, u16 len)
+{
+	dprintk(8, "av7110:%p\n",av7110);
+
+	if (len < 3)
+		return;
+	switch (data[0]) {
+	case CI_MSG_CI_INFO:
+		if (data[2] != 1 && data[2] != 2)
+			break;
+		switch (data[1]) {
+		case 0:
+			av7110->ci_slot[data[2] - 1].flags = 0;
+			break;
+		case 1:
+			av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT;
+			break;
+		case 2:
+			av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY;
+			break;
+		}
+		break;
+	case CI_SWITCH_PRG_REPLY:
+		//av7110->ci_stat=data[1];
+		break;
+	default:
+		break;
+	}
+}
+
+
+void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len)
+{
+	if (dvb_ringbuffer_free(cibuf) < len + 2)
+		return;
+
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8);
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff);
+	dvb_ringbuffer_write(cibuf, data, len);
+	wake_up_interruptible(&cibuf->queue);
+}
+
+
+/******************************************************************************
+ * CI link layer file ops
+ ******************************************************************************/
+
+static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size)
+{
+	struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p;
+	void *data;
+
+	for (p = tab; *p; p++) {
+		data = vmalloc(size);
+		if (!data) {
+			while (p-- != tab) {
+				vfree(p[0]->data);
+				p[0]->data = NULL;
+			}
+			return -ENOMEM;
+		}
+		dvb_ringbuffer_init(*p, data, size);
+	}
+	return 0;
+}
+
+static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)
+{
+	dvb_ringbuffer_flush_spinlock_wakeup(cirbuf);
+	dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf);
+}
+
+static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)
+{
+	vfree(cirbuf->data);
+	cirbuf->data = NULL;
+	vfree(ciwbuf->data);
+	ciwbuf->data = NULL;
+}
+
+static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file,
+		int slots, ca_slot_info_t *slot)
+{
+	int i;
+	int len = 0;
+	u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 };
+
+	for (i = 0; i < 2; i++) {
+		if (slots & (1 << i))
+			len += 8;
+	}
+
+	if (dvb_ringbuffer_free(cibuf) < len)
+		return -EBUSY;
+
+	for (i = 0; i < 2; i++) {
+		if (slots & (1 << i)) {
+			msg[2] = i;
+			dvb_ringbuffer_write(cibuf, msg, 8);
+			slot[i].flags = 0;
+		}
+	}
+
+	return 0;
+}
+
+static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file,
+			   const char __user *buf, size_t count, loff_t *ppos)
+{
+	int free;
+	int non_blocking = file->f_flags & O_NONBLOCK;
+	char *page = (char *)__get_free_page(GFP_USER);
+	int res;
+
+	if (!page)
+		return -ENOMEM;
+
+	res = -EINVAL;
+	if (count > 2048)
+		goto out;
+
+	res = -EFAULT;
+	if (copy_from_user(page, buf, count))
+		goto out;
+
+	free = dvb_ringbuffer_free(cibuf);
+	if (count + 2 > free) {
+		res = -EWOULDBLOCK;
+		if (non_blocking)
+			goto out;
+		res = -ERESTARTSYS;
+		if (wait_event_interruptible(cibuf->queue,
+					     (dvb_ringbuffer_free(cibuf) >= count + 2)))
+			goto out;
+	}
+
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8);
+	DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff);
+
+	res = dvb_ringbuffer_write(cibuf, page, count);
+out:
+	free_page((unsigned long)page);
+	return res;
+}
+
+static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file,
+			  char __user *buf, size_t count, loff_t *ppos)
+{
+	int avail;
+	int non_blocking = file->f_flags & O_NONBLOCK;
+	ssize_t len;
+
+	if (!cibuf->data || !count)
+		return 0;
+	if (non_blocking && (dvb_ringbuffer_empty(cibuf)))
+		return -EWOULDBLOCK;
+	if (wait_event_interruptible(cibuf->queue,
+				     !dvb_ringbuffer_empty(cibuf)))
+		return -ERESTARTSYS;
+	avail = dvb_ringbuffer_avail(cibuf);
+	if (avail < 4)
+		return 0;
+	len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8;
+	len |= DVB_RINGBUFFER_PEEK(cibuf, 1);
+	if (avail < len + 2 || count < len)
+		return -EINVAL;
+	DVB_RINGBUFFER_SKIP(cibuf, 2);
+
+	return dvb_ringbuffer_read(cibuf, buf, len, 1);
+}
+
+static int dvb_ca_open(struct inode *inode, struct file *file)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	int err = dvb_generic_open(inode, file);
+
+	dprintk(8, "av7110:%p\n",av7110);
+
+	if (err < 0)
+		return err;
+	ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer);
+	return 0;
+}
+
+static unsigned int dvb_ca_poll (struct file *file, poll_table *wait)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer;
+	struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer;
+	unsigned int mask = 0;
+
+	dprintk(8, "av7110:%p\n",av7110);
+
+	poll_wait(file, &rbuf->queue, wait);
+	poll_wait(file, &wbuf->queue, wait);
+
+	if (!dvb_ringbuffer_empty(rbuf))
+		mask |= (POLLIN | POLLRDNORM);
+
+	if (dvb_ringbuffer_free(wbuf) > 1024)
+		mask |= (POLLOUT | POLLWRNORM);
+
+	return mask;
+}
+
+static int dvb_ca_ioctl(struct inode *inode, struct file *file,
+		 unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+	unsigned long arg = (unsigned long) parg;
+
+	dprintk(8, "av7110:%p\n",av7110);
+
+	switch (cmd) {
+	case CA_RESET:
+		return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]);
+		break;
+	case CA_GET_CAP:
+	{
+		ca_caps_t cap;
+
+		cap.slot_num = 2;
+		cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ?
+				 CA_CI_LINK : CA_CI) | CA_DESCR;
+		cap.descr_num = 16;
+		cap.descr_type = CA_ECD;
+		memcpy(parg, &cap, sizeof(cap));
+		break;
+	}
+
+	case CA_GET_SLOT_INFO:
+	{
+		ca_slot_info_t *info=(ca_slot_info_t *)parg;
+
+		if (info->num > 1)
+			return -EINVAL;
+		av7110->ci_slot[info->num].num = info->num;
+		av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?
+							CA_CI_LINK : CA_CI;
+		memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));
+		break;
+	}
+
+	case CA_GET_MSG:
+		break;
+
+	case CA_SEND_MSG:
+		break;
+
+	case CA_GET_DESCR_INFO:
+	{
+		ca_descr_info_t info;
+
+		info.num = 16;
+		info.type = CA_ECD;
+		memcpy(parg, &info, sizeof (info));
+		break;
+	}
+
+	case CA_SET_DESCR:
+	{
+		ca_descr_t *descr = (ca_descr_t*) parg;
+
+		if (descr->index >= 16)
+			return -EINVAL;
+		if (descr->parity > 1)
+			return -EINVAL;
+		av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5,
+			      (descr->index<<8)|descr->parity,
+			      (descr->cw[0]<<8)|descr->cw[1],
+			      (descr->cw[2]<<8)|descr->cw[3],
+			      (descr->cw[4]<<8)|descr->cw[5],
+			      (descr->cw[6]<<8)|descr->cw[7]);
+		break;
+	}
+
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t dvb_ca_write(struct file *file, const char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+
+	dprintk(8, "av7110:%p\n",av7110);
+	return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos);
+}
+
+static ssize_t dvb_ca_read(struct file *file, char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+	struct av7110 *av7110 = (struct av7110 *) dvbdev->priv;
+
+	dprintk(8, "av7110:%p\n",av7110);
+	return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos);
+}
+
+
+
+static struct file_operations dvb_ca_fops = {
+	.owner		= THIS_MODULE,
+	.read		= dvb_ca_read,
+	.write		= dvb_ca_write,
+	.ioctl		= dvb_generic_ioctl,
+	.open		= dvb_ca_open,
+	.release	= dvb_generic_release,
+	.poll		= dvb_ca_poll,
+};
+
+static struct dvb_device dvbdev_ca = {
+	.priv		= NULL,
+	.users		= 1,
+	.writers	= 1,
+	.fops		= &dvb_ca_fops,
+	.kernel_ioctl	= dvb_ca_ioctl,
+};
+
+
+int av7110_ca_register(struct av7110 *av7110)
+{
+	return dvb_register_device(av7110->dvb_adapter, &av7110->ca_dev,
+				   &dvbdev_ca, av7110, DVB_DEVICE_CA);
+}
+
+void av7110_ca_unregister(struct av7110 *av7110)
+{
+	dvb_unregister_device(av7110->ca_dev);
+}
+
+int av7110_ca_init(struct av7110* av7110)
+{
+	return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192);
+}
+
+void av7110_ca_exit(struct av7110* av7110)
+{
+	ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer);
+}
diff --git a/drivers/media/dvb/ttpci/av7110_ca.h b/drivers/media/dvb/ttpci/av7110_ca.h
new file mode 100644
index 0000000..70ee855
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_ca.h
@@ -0,0 +1,14 @@
+#ifndef _AV7110_CA_H_
+#define _AV7110_CA_H_
+
+struct av7110;
+
+extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len);
+extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len);
+
+extern int av7110_ca_register(struct av7110 *av7110);
+extern void av7110_ca_unregister(struct av7110 *av7110);
+extern int av7110_ca_init(struct av7110* av7110);
+extern void av7110_ca_exit(struct av7110* av7110);
+
+#endif /* _AV7110_CA_H_ */
diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c
new file mode 100644
index 0000000..bd6e5ea
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_hw.c
@@ -0,0 +1,1170 @@
+/*
+ * av7110_hw.c: av7110 low level hardware access and firmware interface
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+/* for debugging ARM communication: */
+//#define COM_DEBUG
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/byteorder/swabb.h>
+#include <linux/smp_lock.h>
+#include <linux/fs.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+
+/****************************************************************************
+ * DEBI functions
+ ****************************************************************************/
+
+/* This DEBI code is based on the Stradis driver
+   by Nathan Laredo <laredo@gnu.org> */
+
+int av7110_debiwrite(struct av7110 *av7110, u32 config,
+		     int addr, u32 val, int count)
+{
+	struct saa7146_dev *dev = av7110->dev;
+
+	if (count <= 0 || count > 32764) {
+		printk("%s: invalid count %d\n", __FUNCTION__, count);
+		return -1;
+	}
+	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
+		printk("%s: wait_for_debi_done failed\n", __FUNCTION__);
+		return -1;
+	}
+	saa7146_write(dev, DEBI_CONFIG, config);
+	if (count <= 4)		/* immediate transfer */
+		saa7146_write(dev, DEBI_AD, val);
+	else			/* block transfer */
+		saa7146_write(dev, DEBI_AD, av7110->debi_bus);
+	saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff));
+	saa7146_write(dev, MC2, (2 << 16) | 2);
+	return 0;
+}
+
+u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count)
+{
+	struct saa7146_dev *dev = av7110->dev;
+	u32 result = 0;
+
+	if (count > 32764 || count <= 0) {
+		printk("%s: invalid count %d\n", __FUNCTION__, count);
+		return 0;
+	}
+	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
+		printk("%s: wait_for_debi_done #1 failed\n", __FUNCTION__);
+		return 0;
+	}
+	saa7146_write(dev, DEBI_AD, av7110->debi_bus);
+	saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
+
+	saa7146_write(dev, DEBI_CONFIG, config);
+	saa7146_write(dev, MC2, (2 << 16) | 2);
+	if (count > 4)
+		return count;
+	if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) {
+		printk("%s: wait_for_debi_done #2 failed\n", __FUNCTION__);
+		return 0;
+	}
+
+	result = saa7146_read(dev, DEBI_AD);
+	result &= (0xffffffffUL >> ((4 - count) * 8));
+	return result;
+}
+
+
+
+/* av7110 ARM core boot stuff */
+
+void av7110_reset_arm(struct av7110 *av7110)
+{
+	saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
+
+	/* Disable DEBI and GPIO irq */
+	SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+
+	saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
+	msleep(30);	/* the firmware needs some time to initialize */
+
+	ARM_ResetMailBox(av7110);
+
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+	SAA7146_IER_ENABLE(av7110->dev, MASK_03);
+
+	av7110->arm_ready = 1;
+	dprintk(1, "reset ARM\n");
+}
+
+
+static int waitdebi(struct av7110 *av7110, int adr, int state)
+{
+	int k;
+
+	dprintk(4, "%p\n", av7110);
+
+	for (k = 0; k < 100; k++) {
+		if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state)
+			return 0;
+		udelay(5);
+	}
+	return -1;
+}
+
+static int load_dram(struct av7110 *av7110, u32 *data, int len)
+{
+	int i;
+	int blocks, rest;
+	u32 base, bootblock = BOOT_BLOCK;
+
+	dprintk(4, "%p\n", av7110);
+
+	blocks = len / BOOT_MAX_SIZE;
+	rest = len % BOOT_MAX_SIZE;
+	base = DRAM_START_CODE;
+
+	for (i = 0; i < blocks; i++) {
+		if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
+			printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i);
+			return -1;
+		}
+		dprintk(4, "writing DRAM block %d\n", i);
+		mwdebi(av7110, DEBISWAB, bootblock,
+		       ((char*)data) + i * BOOT_MAX_SIZE, BOOT_MAX_SIZE);
+		bootblock ^= 0x1400;
+		iwdebi(av7110, DEBISWAB, BOOT_BASE, swab32(base), 4);
+		iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, BOOT_MAX_SIZE, 2);
+		iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+		base += BOOT_MAX_SIZE;
+	}
+
+	if (rest > 0) {
+		if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
+			printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n");
+			return -1;
+		}
+		if (rest > 4)
+			mwdebi(av7110, DEBISWAB, bootblock,
+			       ((char*)data) + i * BOOT_MAX_SIZE, rest);
+		else
+			mwdebi(av7110, DEBISWAB, bootblock,
+			       ((char*)data) + i * BOOT_MAX_SIZE - 4, rest + 4);
+
+		iwdebi(av7110, DEBISWAB, BOOT_BASE, swab32(base), 4);
+		iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, rest, 2);
+		iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+	}
+	if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) {
+		printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n");
+		return -1;
+	}
+	iwdebi(av7110, DEBINOSWAP, BOOT_SIZE, 0, 2);
+	iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+	if (waitdebi(av7110, BOOT_STATE, BOOTSTATE_BOOT_COMPLETE) < 0) {
+		printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+/* we cannot write av7110 DRAM directly, so load a bootloader into
+ * the DPRAM which implements a simple boot protocol */
+static u8 bootcode[] = {
+  0xea, 0x00, 0x00, 0x0e, 0xe1, 0xb0, 0xf0, 0x0e, 0xe2, 0x5e, 0xf0, 0x04,
+  0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x08, 0xe2, 0x5e, 0xf0, 0x04,
+  0xe2, 0x5e, 0xf0, 0x04, 0xe2, 0x5e, 0xf0, 0x04, 0x2c, 0x00, 0x00, 0x24,
+  0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x34,
+  0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0x5a, 0x5a, 0x00, 0x1f, 0x15, 0x55,
+  0x00, 0x00, 0x00, 0x09, 0xe5, 0x9f, 0xd0, 0x7c, 0xe5, 0x9f, 0x40, 0x74,
+  0xe3, 0xa0, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x00, 0xe5, 0x84, 0x00, 0x04,
+  0xe5, 0x9f, 0x10, 0x70, 0xe5, 0x9f, 0x20, 0x70, 0xe5, 0x9f, 0x30, 0x64,
+  0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe1, 0x51, 0x00, 0x02,
+  0xda, 0xff, 0xff, 0xfb, 0xe5, 0x9f, 0xf0, 0x50, 0xe1, 0xd4, 0x10, 0xb0,
+  0xe3, 0x51, 0x00, 0x00, 0x0a, 0xff, 0xff, 0xfc, 0xe1, 0xa0, 0x10, 0x0d,
+  0xe5, 0x94, 0x30, 0x04, 0xe1, 0xd4, 0x20, 0xb2, 0xe2, 0x82, 0x20, 0x3f,
+  0xe1, 0xb0, 0x23, 0x22, 0x03, 0xa0, 0x00, 0x02, 0xe1, 0xc4, 0x00, 0xb0,
+  0x0a, 0xff, 0xff, 0xf4, 0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0,
+  0xe8, 0xb1, 0x1f, 0xe0, 0xe8, 0xa3, 0x1f, 0xe0, 0xe2, 0x52, 0x20, 0x01,
+  0x1a, 0xff, 0xff, 0xf9, 0xe2, 0x2d, 0xdb, 0x05, 0xea, 0xff, 0xff, 0xec,
+  0x2c, 0x00, 0x03, 0xf8, 0x2c, 0x00, 0x04, 0x00, 0x9e, 0x00, 0x08, 0x00,
+  0x2c, 0x00, 0x00, 0x74, 0x2c, 0x00, 0x00, 0xc0
+};
+
+int av7110_bootarm(struct av7110 *av7110)
+{
+	struct saa7146_dev *dev = av7110->dev;
+	u32 ret;
+	int i;
+
+	dprintk(4, "%p\n", av7110);
+
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
+
+	/* Disable DEBI and GPIO irq */
+	SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19);
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+
+	/* enable DEBI */
+	saa7146_write(av7110->dev, MC1, 0x08800880);
+	saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	/* test DEBI */
+	iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4);
+	if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) {
+		printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: "
+		       "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n",
+		       ret, 0x10325476);
+		return -1;
+	}
+	for (i = 0; i < 8192; i += 4)
+		iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4);
+	dprintk(2, "debi test OK\n");
+
+	/* boot */
+	dprintk(1, "load boot code\n");
+	saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO);
+	//saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT);
+	//saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT);
+
+	mwdebi(av7110, DEBISWAB, DPRAM_BASE, bootcode, sizeof(bootcode));
+	iwdebi(av7110, DEBINOSWAP, BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2);
+
+	if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
+		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
+		       "saa7146_wait_for_debi_done() timed out\n");
+		return -1;
+	}
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
+	mdelay(1);
+
+	dprintk(1, "load dram code\n");
+	if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) {
+		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
+		       "load_dram() failed\n");
+		return -1;
+	}
+
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO);
+	mdelay(1);
+
+	dprintk(1, "load dpram code\n");
+	mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram);
+
+	if (saa7146_wait_for_debi_done(av7110->dev, 1)) {
+		printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): "
+		       "saa7146_wait_for_debi_done() timed out after loading DRAM\n");
+		return -1;
+	}
+	saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI);
+	msleep(30);	/* the firmware needs some time to initialize */
+
+	//ARM_ClearIrq(av7110);
+	ARM_ResetMailBox(av7110);
+	SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
+	SAA7146_IER_ENABLE(av7110->dev, MASK_03);
+
+	av7110->arm_errors = 0;
+	av7110->arm_ready = 1;
+	return 0;
+}
+
+
+/****************************************************************************
+ * DEBI command polling
+ ****************************************************************************/
+
+int av7110_wait_msgstate(struct av7110 *av7110, u16 flags)
+{
+	unsigned long start;
+	u32 stat;
+
+	if (FW_VERSION(av7110->arm_app) <= 0x261c) {
+		/* not supported by old firmware */
+		msleep(50);
+		return 0;
+	}
+
+	/* new firmware */
+	start = jiffies;
+	for (;;) {
+		if (down_interruptible(&av7110->dcomlock))
+			return -ERESTARTSYS;
+		stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+		up(&av7110->dcomlock);
+		if ((stat & flags) == 0) {
+			break;
+		}
+		if (time_after(jiffies, start + ARM_WAIT_FREE)) {
+			printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n",
+				__FUNCTION__, stat & flags);
+			return -1;
+		}
+		msleep(1);
+	}
+	return 0;
+}
+
+int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
+{
+	int i;
+	unsigned long start;
+	char *type = NULL;
+	u16 flags[2] = {0, 0};
+	u32 stat;
+
+//	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->arm_ready) {
+		dprintk(1, "arm not ready.\n");
+		return -ENXIO;
+	}
+
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2 )) {
+		msleep(1);
+		if (time_after(jiffies, start + ARM_WAIT_FREE)) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __FUNCTION__);
+			return -ETIMEDOUT;
+		}
+	}
+
+	wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2);
+
+#ifndef _NOHANDSHAKE
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2 )) {
+		msleep(1);
+		if (time_after(jiffies, start + ARM_WAIT_SHAKE)) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
+			return -ETIMEDOUT;
+		}
+	}
+#endif
+
+	switch ((buf[0] >> 8) & 0xff) {
+	case COMTYPE_PIDFILTER:
+	case COMTYPE_ENCODER:
+	case COMTYPE_REC_PLAY:
+	case COMTYPE_MPEGDECODER:
+		type = "MSG";
+		flags[0] = GPMQOver;
+		flags[1] = GPMQFull;
+		break;
+	case COMTYPE_OSD:
+		type = "OSD";
+		flags[0] = OSDQOver;
+		flags[1] = OSDQFull;
+		break;
+	case COMTYPE_MISC:
+		if (FW_VERSION(av7110->arm_app) >= 0x261d) {
+			type = "MSG";
+			flags[0] = GPMQOver;
+			flags[1] = GPMQBusy;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (type != NULL) {
+		/* non-immediate COMMAND type */
+		start = jiffies;
+		for (;;) {
+			stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+			if (stat & flags[0]) {
+				printk(KERN_ERR "%s: %s QUEUE overflow\n",
+					__FUNCTION__, type);
+				return -1;
+			}
+			if ((stat & flags[1]) == 0)
+				break;
+			if (time_after(jiffies, start + ARM_WAIT_FREE)) {
+				printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n",
+					__FUNCTION__, type);
+				return -1;
+			}
+			msleep(1);
+		}
+	}
+
+	for (i = 2; i < length; i++)
+		wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2);
+
+	if (length)
+		wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2);
+	else
+		wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2);
+
+	wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2);
+
+	wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2);
+
+#ifdef COM_DEBUG
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2 )) {
+		msleep(1);
+		if (time_after(jiffies, start + ARM_WAIT_FREE)) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND to complete\n",
+			       __FUNCTION__);
+			return -ETIMEDOUT;
+		}
+	}
+
+	stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+	if (stat & GPMQOver) {
+		printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __FUNCTION__);
+		return -ENOSPC;
+	}
+	else if (stat & OSDQOver) {
+		printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __FUNCTION__);
+		return -ENOSPC;
+	}
+#endif
+
+	return 0;
+}
+
+int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length)
+{
+	int ret;
+
+//	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->arm_ready) {
+		dprintk(1, "arm not ready.\n");
+		return -1;
+	}
+	if (down_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+
+	ret = __av7110_send_fw_cmd(av7110, buf, length);
+	up(&av7110->dcomlock);
+	if (ret)
+		printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n",
+		       __FUNCTION__, ret);
+	return ret;
+}
+
+int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
+{
+	va_list args;
+	u16 buf[num + 2];
+	int i, ret;
+
+//	dprintk(4, "%p\n", av7110);
+
+	buf[0] = ((type << 8) | com);
+	buf[1] = num;
+
+	if (num) {
+		va_start(args, num);
+		for (i = 0; i < num; i++)
+			buf[i + 2] = va_arg(args, u32);
+		va_end(args);
+	}
+
+	ret = av7110_send_fw_cmd(av7110, buf, num + 2);
+	if (ret)
+		printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret);
+	return ret;
+}
+
+int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
+{
+	int i, ret;
+	u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
+		16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	dprintk(4, "%p\n", av7110);
+
+	for(i = 0; i < len && i < 32; i++)
+	{
+		if(i % 2 == 0)
+			cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
+		else
+			cmd[(i / 2) + 2] |= buf[i];
+	}
+
+	ret = av7110_send_fw_cmd(av7110, cmd, 18);
+	if (ret)
+		printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret);
+	return ret;
+}
+
+int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
+		      int request_buf_len, u16 *reply_buf, int reply_buf_len)
+{
+	int err;
+	s16 i;
+	unsigned long start;
+#ifdef COM_DEBUG
+	u32 stat;
+#endif
+
+	dprintk(4, "%p\n", av7110);
+
+	if (!av7110->arm_ready) {
+		dprintk(1, "arm not ready.\n");
+		return -1;
+	}
+
+	if (down_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+
+	if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) {
+		up(&av7110->dcomlock);
+		printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err);
+		return err;
+	}
+
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2)) {
+#ifdef _NOHANDSHAKE
+		msleep(1);
+#endif
+		if (time_after(jiffies, start + ARM_WAIT_FREE)) {
+			printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __FUNCTION__);
+			up(&av7110->dcomlock);
+			return -1;
+		}
+	}
+
+#ifndef _NOHANDSHAKE
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2 )) {
+		msleep(1);
+		if (time_after(jiffies, start + ARM_WAIT_SHAKE)) {
+			printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __FUNCTION__);
+			up(&av7110->dcomlock);
+			return -1;
+		}
+	}
+#endif
+
+#ifdef COM_DEBUG
+	stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2);
+	if (stat & GPMQOver) {
+		printk(KERN_ERR "%s: GPMQOver\n", __FUNCTION__);
+		up(&av7110->dcomlock);
+		return -1;
+	}
+	else if (stat & OSDQOver) {
+		printk(KERN_ERR "%s: OSDQOver\n", __FUNCTION__);
+		up(&av7110->dcomlock);
+		return -1;
+	}
+#endif
+
+	for (i = 0; i < reply_buf_len; i++)
+		reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2);
+
+	up(&av7110->dcomlock);
+	return 0;
+}
+
+int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length)
+{
+	int ret;
+	ret = av7110_fw_request(av7110, &tag, 0, buf, length);
+	if (ret)
+		printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret);
+	return ret;
+}
+
+
+/****************************************************************************
+ * Firmware commands
+ ****************************************************************************/
+
+/* get version of the firmware ROM, RTSL, video ucode and ARM application  */
+int av7110_firmversion(struct av7110 *av7110)
+{
+	u16 buf[20];
+	u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion);
+
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110_fw_query(av7110, tag, buf, 16)) {
+		printk("dvb-ttpci: failed to boot firmware @ card %d\n",
+		       av7110->dvb_adapter->num);
+		return -EIO;
+	}
+
+	av7110->arm_fw = (buf[0] << 16) + buf[1];
+	av7110->arm_rtsl = (buf[2] << 16) + buf[3];
+	av7110->arm_vid = (buf[4] << 16) + buf[5];
+	av7110->arm_app = (buf[6] << 16) + buf[7];
+	av7110->avtype = (buf[8] << 16) + buf[9];
+
+	printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n",
+	       av7110->dvb_adapter->num, av7110->arm_fw,
+	       av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app);
+
+	/* print firmware capabilities */
+	if (FW_CI_LL_SUPPORT(av7110->arm_app))
+		printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n",
+		       av7110->dvb_adapter->num);
+	else
+		printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n",
+		       av7110->dvb_adapter->num);
+
+	return 0;
+}
+
+
+int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst)
+{
+	int i, ret;
+	u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC),
+			16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	dprintk(4, "%p\n", av7110);
+
+	if (len > 10)
+		len = 10;
+
+	buf[1] = len + 2;
+	buf[2] = len;
+
+	if (burst != -1)
+		buf[3] = burst ? 0x01 : 0x00;
+	else
+		buf[3] = 0xffff;
+
+	for (i = 0; i < len; i++)
+		buf[i + 4] = msg[i];
+
+	if ((ret = av7110_send_fw_cmd(av7110, buf, 18)))
+		printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret);
+
+	return 0;
+}
+
+
+#ifdef CONFIG_DVB_AV7110_OSD
+
+static inline int SetColorBlend(struct av7110 *av7110, u8 windownr)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr);
+}
+
+static inline int SetBlend_(struct av7110 *av7110, u8 windownr,
+		     enum av7110_osd_palette_type colordepth, u16 index, u8 blending)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4,
+			     windownr, colordepth, index, blending);
+}
+
+static inline int SetColor_(struct av7110 *av7110, u8 windownr,
+		     enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5,
+			     windownr, colordepth, index, colorhi, colorlo);
+}
+
+static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize,
+			  u16 colorfg, u16 colorbg)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4,
+			     windownr, fontsize, colorfg, colorbg);
+}
+
+static int FlushText(struct av7110 *av7110)
+{
+	unsigned long start;
+
+	if (down_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2)) {
+		msleep(1);
+		if (time_after(jiffies, start + ARM_WAIT_OSD)) {
+			printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n",
+			       __FUNCTION__);
+			up(&av7110->dcomlock);
+			return -1;
+		}
+	}
+	up(&av7110->dcomlock);
+	return 0;
+}
+
+static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, u8* buf)
+{
+	int i, ret;
+	unsigned long start;
+	int length = strlen(buf) + 1;
+	u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y };
+
+	if (down_interruptible(&av7110->dcomlock))
+		return -ERESTARTSYS;
+
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2)) {
+		msleep(1);
+		if (time_after(jiffies, start + ARM_WAIT_OSD)) {
+			printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n",
+			       __FUNCTION__);
+			up(&av7110->dcomlock);
+			return -1;
+		}
+	}
+#ifndef _NOHANDSHAKE
+	start = jiffies;
+	while (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2)) {
+		msleep(1);
+		if (time_after(jiffies, start + ARM_WAIT_SHAKE)) {
+			printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n",
+			       __FUNCTION__);
+			up(&av7110->dcomlock);
+			return -1;
+		}
+	}
+#endif
+	for (i = 0; i < length / 2; i++)
+		wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2,
+		      swab16(*(u16 *)(buf + 2 * i)), 2);
+	if (length & 1)
+		wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2);
+	ret = __av7110_send_fw_cmd(av7110, cbuf, 5);
+	up(&av7110->dcomlock);
+	if (ret)
+		printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret);
+	return ret;
+}
+
+static inline int DrawLine(struct av7110 *av7110, u8 windownr,
+			   u16 x, u16 y, u16 dx, u16 dy, u16 color)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6,
+			     windownr, x, y, dx, dy, color);
+}
+
+static inline int DrawBlock(struct av7110 *av7110, u8 windownr,
+			    u16 x, u16 y, u16 dx, u16 dy, u16 color)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6,
+			     windownr, x, y, dx, dy, color);
+}
+
+static inline int HideWindow(struct av7110 *av7110, u8 windownr)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr);
+}
+
+static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y);
+}
+
+static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y);
+}
+
+static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr);
+}
+
+static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr,
+				  osd_raw_window_t disptype,
+				  u16 width, u16 height)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4,
+			     windownr, disptype, width, height);
+}
+
+
+static enum av7110_osd_palette_type bpp2pal[8] = {
+	Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit
+};
+static osd_raw_window_t bpp2bit[8] = {
+	OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8
+};
+
+static inline int LoadBitmap(struct av7110 *av7110, u16 format,
+			     u16 dx, u16 dy, int inc, u8 __user * data)
+{
+	int bpp;
+	int i;
+	int d, delta;
+	u8 c;
+	int ret;
+
+	dprintk(4, "%p\n", av7110);
+
+	ret = wait_event_interruptible_timeout(av7110->bmpq, av7110->bmp_state != BMP_LOADING, HZ);
+	if (ret == -ERESTARTSYS || ret == 0) {
+		printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n",
+		       ret, av7110->bmp_state);
+		av7110->bmp_state = BMP_NONE;
+		return -1;
+	}
+	BUG_ON (av7110->bmp_state == BMP_LOADING);
+
+	av7110->bmp_state = BMP_LOADING;
+	if	(format == OSD_BITMAP8) {
+		bpp=8; delta = 1;
+	} else if (format == OSD_BITMAP4) {
+		bpp=4; delta = 2;
+	} else if (format == OSD_BITMAP2) {
+		bpp=2; delta = 4;
+	} else if (format == OSD_BITMAP1) {
+		bpp=1; delta = 8;
+	} else {
+		av7110->bmp_state = BMP_NONE;
+		return -1;
+	}
+	av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8;
+	av7110->bmpp = 0;
+	if (av7110->bmplen > 32768) {
+		av7110->bmp_state = BMP_NONE;
+		return -1;
+	}
+	for (i = 0; i < dy; i++) {
+		if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) {
+			av7110->bmp_state = BMP_NONE;
+			return -1;
+		}
+	}
+	if (format != OSD_BITMAP8) {
+		for (i = 0; i < dx * dy / delta; i++) {
+			c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1];
+			for (d = delta - 2; d >= 0; d--) {
+				c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d]
+				      << ((delta - d - 1) * bpp));
+				((u8 *)av7110->bmpbuf)[1024 + i] = c;
+			}
+		}
+	}
+	av7110->bmplen += 1024;
+	dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen);
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy);
+}
+
+static int BlitBitmap(struct av7110 *av7110, u16 win, u16 x, u16 y, u16 trans)
+{
+	int ret;
+
+	dprintk(4, "%p\n", av7110);
+
+	BUG_ON (av7110->bmp_state == BMP_NONE);
+
+	ret = wait_event_interruptible_timeout(av7110->bmpq,
+				av7110->bmp_state != BMP_LOADING, 10*HZ);
+	if (ret == -ERESTARTSYS || ret == 0) {
+		printk("dvb-ttpci: warning: timeout waiting in BlitBitmap: %d, %d\n",
+		       ret, av7110->bmp_state);
+		av7110->bmp_state = BMP_NONE;
+		return (ret == 0) ? -ETIMEDOUT : ret;
+	}
+
+	BUG_ON (av7110->bmp_state != BMP_LOADED);
+
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, win, x, y, trans);
+}
+
+static inline int ReleaseBitmap(struct av7110 *av7110)
+{
+	dprintk(4, "%p\n", av7110);
+
+	if (av7110->bmp_state != BMP_LOADED)
+		return -1;
+	av7110->bmp_state = BMP_NONE;
+	return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0);
+}
+
+static u32 RGB2YUV(u16 R, u16 G, u16 B)
+{
+	u16 y, u, v;
+	u16 Y, Cr, Cb;
+
+	y = R * 77 + G * 150 + B * 29;	/* Luma=0.299R+0.587G+0.114B 0..65535 */
+	u = 2048 + B * 8 -(y >> 5);	/* Cr 0..4095 */
+	v = 2048 + R * 8 -(y >> 5);	/* Cb 0..4095 */
+
+	Y = y / 256;
+	Cb = u / 16;
+	Cr = v / 16;
+
+	return Cr | (Cb << 16) | (Y << 8);
+}
+
+static void OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend)
+{
+	u16 ch, cl;
+	u32 yuv;
+
+	yuv = blend ? RGB2YUV(r,g,b) : 0;
+	cl = (yuv & 0xffff);
+	ch = ((yuv >> 16) & 0xffff);
+	SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
+		  color, ch, cl);
+	SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]],
+		  color, ((blend >> 4) & 0x0f));
+}
+
+static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last)
+{
+       int i;
+       int length = last - first + 1;
+
+       if (length * 4 > DATA_BUFF3_SIZE)
+	       return -EINVAL;
+
+       for (i = 0; i < length; i++) {
+	       u32 color, blend, yuv;
+
+	       if (get_user(color, colors + i))
+		       return -EFAULT;
+	       blend = (color & 0xF0000000) >> 4;
+	       yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF,
+				     (color >> 16) & 0xFF) | blend : 0;
+	       yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16);
+	       wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4);
+       }
+       return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4,
+			    av7110->osdwin,
+			    bpp2pal[av7110->osdbpp[av7110->osdwin]],
+			    first, last);
+}
+
+static int OSDSetBlock(struct av7110 *av7110, int x0, int y0,
+		       int x1, int y1, int inc, u8 __user * data)
+{
+	uint w, h, bpp, bpl, size, lpb, bnum, brest;
+	int i;
+	int rc;
+
+	w = x1 - x0 + 1;
+	h = y1 - y0 + 1;
+	if (inc <= 0)
+		inc = w;
+	if (w <= 0 || w > 720 || h <= 0 || h > 576)
+		return -1;
+	bpp = av7110->osdbpp[av7110->osdwin] + 1;
+	bpl = ((w * bpp + 7) & ~7) / 8;
+	size = h * bpl;
+	lpb = (32 * 1024) / bpl;
+	bnum = size / (lpb * bpl);
+	brest = size - bnum * lpb * bpl;
+
+	for (i = 0; i < bnum; i++) {
+		rc = LoadBitmap(av7110, bpp2bit[av7110->osdbpp[av7110->osdwin]],
+			   w, lpb, inc, data);
+		if (rc)
+			return rc;
+		rc = BlitBitmap(av7110, av7110->osdwin, x0, y0 + i * lpb, 0);
+		if (rc)
+			return rc;
+		data += lpb * inc;
+	}
+	if (brest) {
+		rc = LoadBitmap(av7110, bpp2bit[av7110->osdbpp[av7110->osdwin]],
+			   w, brest / bpl, inc, data);
+		if (rc)
+			return rc;
+		rc = BlitBitmap(av7110, av7110->osdwin, x0, y0 + bnum * lpb, 0);
+		if (rc)
+			return rc;
+	}
+	ReleaseBitmap(av7110);
+	return 0;
+}
+
+int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc)
+{
+	int ret;
+
+	ret = down_interruptible(&av7110->osd_sema);
+	if (ret)
+		return -ERESTARTSYS;
+
+	/* stupid, but OSD functions don't provide a return code anyway */
+	ret = 0;
+
+	switch (dc->cmd) {
+	case OSD_Close:
+		DestroyOSDWindow(av7110, av7110->osdwin);
+		goto out;
+	case OSD_Open:
+		av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7;
+		CreateOSDWindow(av7110, av7110->osdwin,
+				bpp2bit[av7110->osdbpp[av7110->osdwin]],
+				dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
+		if (!dc->data) {
+			MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
+			SetColorBlend(av7110, av7110->osdwin);
+		}
+		goto out;
+	case OSD_Show:
+		MoveWindowRel(av7110, av7110->osdwin, 0, 0);
+		goto out;
+	case OSD_Hide:
+		HideWindow(av7110, av7110->osdwin);
+		goto out;
+	case OSD_Clear:
+		DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0);
+		goto out;
+	case OSD_Fill:
+		DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color);
+		goto out;
+	case OSD_SetColor:
+		OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1);
+		goto out;
+	case OSD_SetPalette:
+	{
+		if (FW_VERSION(av7110->arm_app) >= 0x2618) {
+			ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0);
+			goto out;
+		} else {
+			int i, len = dc->x0-dc->color+1;
+			u8 __user *colors = (u8 __user *)dc->data;
+			u8 r, g, b, blend;
+
+			for (i = 0; i<len; i++) {
+				if (get_user(r, colors + i * 4) ||
+				    get_user(g, colors + i * 4 + 1) ||
+				    get_user(b, colors + i * 4 + 2) ||
+				    get_user(blend, colors + i * 4 + 3)) {
+					ret = -EFAULT;
+					goto out;
+				    }
+				OSDSetColor(av7110, dc->color + i, r, g, b, blend);
+			}
+		}
+		ret = 0;
+		goto out;
+	}
+	case OSD_SetTrans:
+		goto out;
+	case OSD_SetPixel:
+		DrawLine(av7110, av7110->osdwin,
+			 dc->x0, dc->y0, 0, 0, dc->color);
+		goto out;
+	case OSD_GetPixel:
+		goto out;
+	case OSD_SetRow:
+		dc->y1 = dc->y0;
+		/* fall through */
+	case OSD_SetBlock:
+		ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data);
+		goto out;
+	case OSD_FillRow:
+		DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
+			  dc->x1-dc->x0+1, dc->y1, dc->color);
+		goto out;
+	case OSD_FillBlock:
+		DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0,
+			  dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color);
+		goto out;
+	case OSD_Line:
+		DrawLine(av7110, av7110->osdwin,
+			 dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color);
+		goto out;
+	case OSD_Query:
+		goto out;
+	case OSD_Test:
+		goto out;
+	case OSD_Text:
+	{
+		char textbuf[240];
+
+		if (strncpy_from_user(textbuf, dc->data, 240) < 0) {
+			ret = -EFAULT;
+			goto out;
+		}
+		textbuf[239] = 0;
+		if (dc->x1 > 3)
+			dc->x1 = 3;
+		SetFont(av7110, av7110->osdwin, dc->x1,
+			(u16) (dc->color & 0xffff), (u16) (dc->color >> 16));
+		FlushText(av7110);
+		WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf);
+		goto out;
+	}
+	case OSD_SetWindow:
+		if (dc->x0 < 1 || dc->x0 > 7) {
+			ret = -EINVAL;
+			goto out;
+		}
+		av7110->osdwin = dc->x0;
+		goto out;
+	case OSD_MoveWindow:
+		MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
+		SetColorBlend(av7110, av7110->osdwin);
+		goto out;
+	case OSD_OpenRaw:
+		if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) {
+			ret = -EINVAL;
+			goto out;
+		}
+		if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR) {
+			av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1;
+		}
+		else {
+			av7110->osdbpp[av7110->osdwin] = 0;
+		}
+		CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color,
+				dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1);
+		if (!dc->data) {
+			MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0);
+			SetColorBlend(av7110, av7110->osdwin);
+		}
+		goto out;
+	default:
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	up(&av7110->osd_sema);
+	return ret;
+}
+
+int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap)
+{
+        switch (cap->cmd) {
+        case OSD_CAP_MEMSIZE:
+                if (FW_4M_SDRAM(av7110->arm_app))
+                        cap->val = 1000000;
+                else
+                        cap->val = 92000;
+                return 0;
+        default:
+                return -EINVAL;
+        }
+}
+#endif /* CONFIG_DVB_AV7110_OSD */
diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h
new file mode 100644
index 0000000..bf901c6
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_hw.h
@@ -0,0 +1,500 @@
+#ifndef _AV7110_HW_H_
+#define _AV7110_HW_H_
+
+#include "av7110.h"
+
+/* DEBI transfer mode defs */
+
+#define DEBINOSWAP 0x000e0000
+#define DEBISWAB   0x001e0000
+#define DEBISWAP   0x002e0000
+
+#define ARM_WAIT_FREE  (HZ)
+#define ARM_WAIT_SHAKE (HZ/5)
+#define ARM_WAIT_OSD (HZ)
+
+
+enum av7110_bootstate
+{
+	BOOTSTATE_BUFFER_EMPTY	= 0,
+	BOOTSTATE_BUFFER_FULL	= 1,
+	BOOTSTATE_BOOT_COMPLETE	= 2
+};
+
+enum av7110_type_rec_play_format
+{	RP_None,
+	AudioPES,
+	AudioMp2,
+	AudioPCM,
+	VideoPES,
+	AV_PES
+};
+
+enum av7110_osd_palette_type
+{
+	NoPalet =  0,	   /* No palette */
+	Pal1Bit =  2,	   /* 2 colors for 1 Bit Palette    */
+	Pal2Bit =  4,	   /* 4 colors for 2 bit palette    */
+	Pal4Bit =  16,	   /* 16 colors for 4 bit palette   */
+	Pal8Bit =  256	   /* 256 colors for 16 bit palette */
+};
+
+/* switch defines */
+#define SB_GPIO 3
+#define SB_OFF	SAA7146_GPIO_OUTLO  /* SlowBlank off (TV-Mode) */
+#define SB_ON	SAA7146_GPIO_INPUT  /* SlowBlank on  (AV-Mode) */
+#define SB_WIDE SAA7146_GPIO_OUTHI  /* SlowBlank 6V  (16/9-Mode) (not implemented) */
+
+#define FB_GPIO 1
+#define FB_OFF	SAA7146_GPIO_LO     /* FastBlank off (CVBS-Mode) */
+#define FB_ON	SAA7146_GPIO_OUTHI  /* FastBlank on  (RGB-Mode) */
+#define FB_LOOP	SAA7146_GPIO_INPUT  /* FastBlank loop-through (PC graphics ???) */
+
+enum av7110_video_output_mode
+{
+	NO_OUT	     = 0,		/* disable analog output */
+	CVBS_RGB_OUT = 1,
+	CVBS_YC_OUT  = 2,
+	YC_OUT	     = 3
+};
+
+/* firmware internal msg q status: */
+#define GPMQFull	0x0001		/* Main Message Queue Full */
+#define GPMQOver	0x0002		/* Main Message Queue Overflow */
+#define HPQFull		0x0004		/* High Priority Msg Queue Full */
+#define HPQOver		0x0008
+#define OSDQFull	0x0010		/* OSD Queue Full */
+#define OSDQOver	0x0020
+#define GPMQBusy	0x0040		/* Queue not empty, FW >= 261d */
+#define HPQBusy		0x0080
+#define OSDQBusy	0x0100
+
+/* hw section filter flags */
+#define	SECTION_EIT		0x01
+#define	SECTION_SINGLE		0x00
+#define	SECTION_CYCLE		0x02
+#define	SECTION_CONTINUOS	0x04
+#define	SECTION_MODE		0x06
+#define SECTION_IPMPE		0x0C	/* size up to 4k */
+#define SECTION_HIGH_SPEED	0x1C	/* larger buffer */
+#define DATA_PIPING_FLAG	0x20	/* for Data Piping Filter */
+
+#define	PBUFSIZE_NONE 0x0000
+#define	PBUFSIZE_1P   0x0100
+#define	PBUFSIZE_2P   0x0200
+#define	PBUFSIZE_1K   0x0300
+#define	PBUFSIZE_2K   0x0400
+#define	PBUFSIZE_4K   0x0500
+#define	PBUFSIZE_8K   0x0600
+#define PBUFSIZE_16K  0x0700
+#define PBUFSIZE_32K  0x0800
+
+
+/* firmware command codes */
+enum av7110_osd_command {
+	WCreate,
+	WDestroy,
+	WMoveD,
+	WMoveA,
+	WHide,
+	WTop,
+	DBox,
+	DLine,
+	DText,
+	Set_Font,
+	SetColor,
+	SetBlend,
+	SetWBlend,
+	SetCBlend,
+	SetNonBlend,
+	LoadBmp,
+	BlitBmp,
+	ReleaseBmp,
+	SetWTrans,
+	SetWNoTrans,
+	Set_Palette
+};
+
+enum av7110_pid_command {
+	MultiPID,
+	VideoPID,
+	AudioPID,
+	InitFilt,
+	FiltError,
+	NewVersion,
+	CacheError,
+	AddPIDFilter,
+	DelPIDFilter,
+	Scan,
+	SetDescr,
+	SetIR,
+	FlushTSQueue
+};
+
+enum av7110_mpeg_command {
+	SelAudChannels
+};
+
+enum av7110_audio_command {
+	AudioDAC,
+	CabADAC,
+	ON22K,
+	OFF22K,
+	MainSwitch,
+	ADSwitch,
+	SendDiSEqC,
+	SetRegister
+};
+
+enum av7110_request_command {
+	AudioState,
+	AudioBuffState,
+	VideoState1,
+	VideoState2,
+	VideoState3,
+	CrashCounter,
+	ReqVersion,
+	ReqVCXO,
+	ReqRegister,
+	ReqSecFilterError,
+	ReqSTC
+};
+
+enum av7110_encoder_command {
+	SetVidMode,
+	SetTestMode,
+	LoadVidCode,
+	SetMonitorType,
+	SetPanScanType,
+	SetFreezeMode
+};
+
+enum av7110_rec_play_state {
+	__Record,
+	__Stop,
+	__Play,
+	__Pause,
+	__Slow,
+	__FF_IP,
+	__Scan_I,
+	__Continue
+};
+
+enum av7110_fw_cmd_misc {
+	AV7110_FW_VIDEO_ZOOM = 1,
+	AV7110_FW_VIDEO_COMMAND,
+	AV7110_FW_AUDIO_COMMAND
+};
+
+enum av7110_command_type {
+	COMTYPE_NOCOM,
+	COMTYPE_PIDFILTER,
+	COMTYPE_MPEGDECODER,
+	COMTYPE_OSD,
+	COMTYPE_BMP,
+	COMTYPE_ENCODER,
+	COMTYPE_AUDIODAC,
+	COMTYPE_REQUEST,
+	COMTYPE_SYSTEM,
+	COMTYPE_REC_PLAY,
+	COMTYPE_COMMON_IF,
+	COMTYPE_PID_FILTER,
+	COMTYPE_PES,
+	COMTYPE_TS,
+	COMTYPE_VIDEO,
+	COMTYPE_AUDIO,
+	COMTYPE_CI_LL,
+	COMTYPE_MISC = 0x80
+};
+
+#define VID_NONE_PREF		0x00	/* No aspect ration processing preferred */
+#define VID_PAN_SCAN_PREF	0x01	/* Pan and Scan Display preferred */
+#define VID_VERT_COMP_PREF	0x02	/* Vertical compression display preferred */
+#define VID_VC_AND_PS_PREF	0x03	/* PanScan and vertical Compression if allowed */
+#define VID_CENTRE_CUT_PREF	0x05	/* PanScan with zero vector */
+
+/* MPEG video decoder commands */
+#define VIDEO_CMD_STOP		0x000e
+#define VIDEO_CMD_PLAY		0x000d
+#define VIDEO_CMD_FREEZE	0x0102
+#define VIDEO_CMD_FFWD		0x0016
+#define VIDEO_CMD_SLOW		0x0022
+
+/* MPEG audio decoder commands */
+#define AUDIO_CMD_MUTE		0x0001
+#define AUDIO_CMD_UNMUTE	0x0002
+#define AUDIO_CMD_PCM16		0x0010
+#define AUDIO_CMD_STEREO	0x0080
+#define AUDIO_CMD_MONO_L	0x0100
+#define AUDIO_CMD_MONO_R	0x0200
+#define AUDIO_CMD_SYNC_OFF	0x000e
+#define AUDIO_CMD_SYNC_ON	0x000f
+
+/* firmware data interface codes */
+#define DATA_NONE		 0x00
+#define DATA_FSECTION		 0x01
+#define DATA_IPMPE		 0x02
+#define DATA_MPEG_RECORD	 0x03
+#define DATA_DEBUG_MESSAGE	 0x04
+#define DATA_COMMON_INTERFACE	 0x05
+#define DATA_MPEG_PLAY		 0x06
+#define DATA_BMP_LOAD		 0x07
+#define DATA_IRCOMMAND		 0x08
+#define DATA_PIPING		 0x09
+#define DATA_STREAMING		 0x0a
+#define DATA_CI_GET		 0x0b
+#define DATA_CI_PUT		 0x0c
+#define DATA_MPEG_VIDEO_EVENT	 0x0d
+
+#define DATA_PES_RECORD		 0x10
+#define DATA_PES_PLAY		 0x11
+#define DATA_TS_RECORD		 0x12
+#define DATA_TS_PLAY		 0x13
+
+/* ancient CI command codes, only two are actually still used
+ * by the link level CI firmware */
+#define CI_CMD_ERROR		 0x00
+#define CI_CMD_ACK		 0x01
+#define CI_CMD_SYSTEM_READY	 0x02
+#define CI_CMD_KEYPRESS		 0x03
+#define CI_CMD_ON_TUNED		 0x04
+#define CI_CMD_ON_SWITCH_PROGRAM 0x05
+#define CI_CMD_SECTION_ARRIVED	 0x06
+#define CI_CMD_SECTION_TIMEOUT	 0x07
+#define CI_CMD_TIME		 0x08
+#define CI_CMD_ENTER_MENU	 0x09
+#define CI_CMD_FAST_PSI		 0x0a
+#define CI_CMD_GET_SLOT_INFO	 0x0b
+
+#define CI_MSG_NONE		 0x00
+#define CI_MSG_CI_INFO		 0x01
+#define CI_MSG_MENU		 0x02
+#define CI_MSG_LIST		 0x03
+#define CI_MSG_TEXT		 0x04
+#define CI_MSG_REQUEST_INPUT	 0x05
+#define CI_MSG_INPUT_COMPLETE	 0x06
+#define CI_MSG_LIST_MORE	 0x07
+#define CI_MSG_MENU_MORE	 0x08
+#define CI_MSG_CLOSE_MMI_IMM	 0x09
+#define CI_MSG_SECTION_REQUEST	 0x0a
+#define CI_MSG_CLOSE_FILTER	 0x0b
+#define CI_PSI_COMPLETE		 0x0c
+#define CI_MODULE_READY		 0x0d
+#define CI_SWITCH_PRG_REPLY	 0x0e
+#define CI_MSG_TEXT_MORE	 0x0f
+
+#define CI_MSG_CA_PMT		 0xe0
+#define CI_MSG_ERROR		 0xf0
+
+
+/* base address of the dual ported RAM which serves as communication
+ * area between PCI bus and av7110,
+ * as seen by the DEBI bus of the saa7146 */
+#define	DPRAM_BASE 0x4000
+
+/* boot protocol area */
+#define BOOT_STATE	(DPRAM_BASE + 0x3F8)
+#define BOOT_SIZE	(DPRAM_BASE + 0x3FA)
+#define BOOT_BASE	(DPRAM_BASE + 0x3FC)
+#define BOOT_BLOCK	(DPRAM_BASE + 0x400)
+#define BOOT_MAX_SIZE	0xc00
+
+/* firmware command protocol area */
+#define IRQ_STATE	(DPRAM_BASE + 0x0F4)
+#define IRQ_STATE_EXT	(DPRAM_BASE + 0x0F6)
+#define MSGSTATE	(DPRAM_BASE + 0x0F8)
+#define FILT_STATE	(DPRAM_BASE + 0x0FA)
+#define COMMAND		(DPRAM_BASE + 0x0FC)
+#define COM_BUFF	(DPRAM_BASE + 0x100)
+#define COM_BUFF_SIZE	0x20
+
+/* various data buffers */
+#define BUFF1_BASE	(DPRAM_BASE + 0x120)
+#define BUFF1_SIZE	0xE0
+
+#define DATA_BUFF0_BASE	(DPRAM_BASE + 0x200)
+#define DATA_BUFF0_SIZE	0x0800
+
+#define DATA_BUFF1_BASE	(DATA_BUFF0_BASE+DATA_BUFF0_SIZE)
+#define DATA_BUFF1_SIZE	0x0800
+
+#define DATA_BUFF2_BASE	(DATA_BUFF1_BASE+DATA_BUFF1_SIZE)
+#define DATA_BUFF2_SIZE	0x0800
+
+#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE)
+#define DATA_BUFF3_SIZE 0x0400
+
+#define Reserved	(DPRAM_BASE + 0x1E00)
+#define Reserved_SIZE	0x1C0
+
+
+/* firmware status area */
+#define STATUS_BASE	(DPRAM_BASE + 0x1FC0)
+#define STATUS_SCR	(STATUS_BASE + 0x00)
+#define STATUS_MODES	(STATUS_BASE + 0x04)
+#define STATUS_LOOPS	(STATUS_BASE + 0x08)
+
+#define STATUS_MPEG_WIDTH     (STATUS_BASE + 0x0C)
+/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */
+#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E)
+
+/* firmware data protocol area */
+#define RX_TYPE		(DPRAM_BASE + 0x1FE8)
+#define RX_LEN		(DPRAM_BASE + 0x1FEA)
+#define TX_TYPE		(DPRAM_BASE + 0x1FEC)
+#define TX_LEN		(DPRAM_BASE + 0x1FEE)
+
+#define RX_BUFF		(DPRAM_BASE + 0x1FF4)
+#define TX_BUFF		(DPRAM_BASE + 0x1FF6)
+
+#define HANDSHAKE_REG	(DPRAM_BASE + 0x1FF8)
+#define COM_IF_LOCK	(DPRAM_BASE + 0x1FFA)
+
+#define IRQ_RX		(DPRAM_BASE + 0x1FFC)
+#define IRQ_TX		(DPRAM_BASE + 0x1FFE)
+
+/* used by boot protocol to load firmware into av7110 DRAM */
+#define DRAM_START_CODE		0x2e000404
+#define DRAM_MAX_CODE_SIZE	0x00100000
+
+/* saa7146 gpio lines */
+#define RESET_LINE		2
+#define DEBI_DONE_LINE		1
+#define ARM_IRQ_LINE		0
+
+
+
+extern void av7110_reset_arm(struct av7110 *av7110);
+extern int av7110_bootarm(struct av7110 *av7110);
+extern int av7110_firmversion(struct av7110 *av7110);
+#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000)
+#define FW_4M_SDRAM(arm_app)      ((arm_app) & 0x40000000)
+#define FW_VERSION(arm_app)	  ((arm_app) & 0x0000FFFF)
+
+extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags);
+extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...);
+extern int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length);
+extern int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length);
+extern int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len);
+extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
+			     int request_buf_len, u16 *reply_buf, int reply_buf_len);
+extern int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* Buff, s16 length);
+
+
+/* DEBI (saa7146 data extension bus interface) access */
+extern int av7110_debiwrite(struct av7110 *av7110, u32 config,
+			    int addr, u32 val, int count);
+extern u32 av7110_debiread(struct av7110 *av7110, u32 config,
+			   int addr, int count);
+
+
+/* DEBI during interrupt */
+/* single word writes */
+static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	av7110_debiwrite(av7110, config, addr, val, count);
+}
+
+/* buffer writes */
+static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, char *val, int count)
+{
+	memcpy(av7110->debi_virt, val, count);
+	av7110_debiwrite(av7110, config, addr, 0, count);
+}
+
+static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	u32 res;
+
+	res=av7110_debiread(av7110, config, addr, count);
+	if (count<=4)
+		memcpy(av7110->debi_virt, (char *) &res, count);
+	return res;
+}
+
+/* DEBI outside interrupts, only for count <= 4! */
+static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&av7110->debilock, flags);
+	av7110_debiwrite(av7110, config, addr, val, count);
+	spin_unlock_irqrestore(&av7110->debilock, flags);
+}
+
+static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+{
+	unsigned long flags;
+	u32 res;
+
+	spin_lock_irqsave(&av7110->debilock, flags);
+	res=av7110_debiread(av7110, config, addr, count);
+	spin_unlock_irqrestore(&av7110->debilock, flags);
+	return res;
+}
+
+/* handle mailbox registers of the dual ported RAM */
+static inline void ARM_ResetMailBox(struct av7110 *av7110)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&av7110->debilock, flags);
+	av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2);
+	av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
+	spin_unlock_irqrestore(&av7110->debilock, flags);
+}
+
+static inline void ARM_ClearMailBox(struct av7110 *av7110)
+{
+	iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
+}
+
+static inline void ARM_ClearIrq(struct av7110 *av7110)
+{
+	irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2);
+}
+
+/****************************************************************************
+ * Firmware commands
+ ****************************************************************************/
+
+static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data);
+}
+
+static inline void av7710_set_video_mode(struct av7110 *av7110, int mode)
+{
+	av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode);
+}
+
+static int inline vidcom(struct av7110 *av7110, u32 com, u32 arg)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4,
+			     (com>>16), (com&0xffff),
+			     (arg>>16), (arg&0xffff));
+}
+
+static int inline audcom(struct av7110 *av7110, u32 com)
+{
+	return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2,
+			     (com>>16), (com&0xffff));
+}
+
+static inline void Set22K(struct av7110 *av7110, int state)
+{
+	av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0);
+}
+
+
+extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst);
+
+
+#ifdef CONFIG_DVB_AV7110_OSD
+extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc);
+extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap);
+#endif /* CONFIG_DVB_AV7110_OSD */
+
+
+
+#endif /* _AV7110_HW_H_ */
diff --git a/drivers/media/dvb/ttpci/av7110_ipack.c b/drivers/media/dvb/ttpci/av7110_ipack.c
new file mode 100644
index 0000000..2466407
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_ipack.c
@@ -0,0 +1,403 @@
+#include "dvb_filter.h"
+#include "av7110_ipack.h"
+#include <linux/string.h>	/* for memcpy() */
+#include <linux/vmalloc.h>
+
+
+void av7110_ipack_reset(struct ipack *p)
+{
+	p->found = 0;
+	p->cid = 0;
+	p->plength = 0;
+	p->flag1 = 0;
+	p->flag2 = 0;
+	p->hlength = 0;
+	p->mpeg = 0;
+	p->check = 0;
+	p->which = 0;
+	p->done = 0;
+	p->count = 0;
+}
+
+
+int av7110_ipack_init(struct ipack *p, int size,
+		      void (*func)(u8 *buf, int size, void *priv))
+{
+	if (!(p->buf = vmalloc(size*sizeof(u8)))) {
+		printk ("Couldn't allocate memory for ipack\n");
+		return -ENOMEM;
+	}
+	p->size = size;
+	p->func = func;
+	p->repack_subids = 0;
+	av7110_ipack_reset(p);
+	return 0;
+}
+
+
+void av7110_ipack_free(struct ipack *p)
+{
+	vfree(p->buf);
+}
+
+
+static void send_ipack(struct ipack *p)
+{
+	int off;
+	struct dvb_audio_info ai;
+	int ac3_off = 0;
+	int streamid = 0;
+	int nframes = 0;
+	int f = 0;
+
+	switch (p->mpeg) {
+	case 2:
+		if (p->count < 10)
+			return;
+		p->buf[3] = p->cid;
+		p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
+		p->buf[5] = (u8)((p->count - 6) & 0x00ff);
+		if (p->repack_subids && p->cid == PRIVATE_STREAM1) {
+			off = 9 + p->buf[8];
+			streamid = p->buf[off];
+			if ((streamid & 0xf8) == 0x80) {
+				ai.off = 0;
+				ac3_off = ((p->buf[off + 2] << 8)|
+					   p->buf[off + 3]);
+				if (ac3_off < p->count)
+					f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off,
+								   p->count - ac3_off, &ai, 0);
+				if (!f) {
+					nframes = (p->count - off - 3 - ac3_off) /
+						ai.framesize + 1;
+					p->buf[off + 2] = (ac3_off >> 8) & 0xff;
+					p->buf[off + 3] = (ac3_off) & 0xff;
+					p->buf[off + 1] = nframes;
+					ac3_off +=  nframes * ai.framesize - p->count;
+				}
+			}
+		}
+		p->func(p->buf, p->count, p->data);
+
+		p->buf[6] = 0x80;
+		p->buf[7] = 0x00;
+		p->buf[8] = 0x00;
+		p->count = 9;
+		if (p->repack_subids && p->cid == PRIVATE_STREAM1
+		    && (streamid & 0xf8) == 0x80) {
+			p->count += 4;
+			p->buf[9] = streamid;
+			p->buf[10] = (ac3_off >> 8) & 0xff;
+			p->buf[11] = (ac3_off) & 0xff;
+			p->buf[12] = 0;
+		}
+		break;
+
+	case 1:
+		if (p->count < 8)
+			return;
+		p->buf[3] = p->cid;
+		p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8);
+		p->buf[5] = (u8)((p->count - 6) & 0x00ff);
+		p->func(p->buf, p->count, p->data);
+
+		p->buf[6] = 0x0f;
+		p->count = 7;
+		break;
+	}
+}
+
+
+void av7110_ipack_flush(struct ipack *p)
+{
+	if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6)
+		return;
+	p->plength = p->found - 6;
+	p->found = 0;
+	send_ipack(p);
+	av7110_ipack_reset(p);
+}
+
+
+static void write_ipack(struct ipack *p, const u8 *data, int count)
+{
+	u8 headr[3] = { 0x00, 0x00, 0x01 };
+
+	if (p->count < 6) {
+		memcpy(p->buf, headr, 3);
+		p->count = 6;
+	}
+
+	if (p->count + count < p->size){
+		memcpy(p->buf+p->count, data, count);
+		p->count += count;
+	} else {
+		int rest = p->size - p->count;
+		memcpy(p->buf+p->count, data, rest);
+		p->count += rest;
+		send_ipack(p);
+		if (count - rest > 0)
+			write_ipack(p, data + rest, count - rest);
+	}
+}
+
+
+int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p)
+{
+	int l;
+	int c = 0;
+
+	while (c < count && (p->mpeg == 0 ||
+			     (p->mpeg == 1 && p->found < 7) ||
+			     (p->mpeg == 2 && p->found < 9))
+	       &&  (p->found < 5 || !p->done)) {
+		switch (p->found) {
+		case 0:
+		case 1:
+			if (buf[c] == 0x00)
+				p->found++;
+			else
+				p->found = 0;
+			c++;
+			break;
+		case 2:
+			if (buf[c] == 0x01)
+				p->found++;
+			else if (buf[c] == 0)
+				p->found = 2;
+			else
+				p->found = 0;
+			c++;
+			break;
+		case 3:
+			p->cid = 0;
+			switch (buf[c]) {
+			case PROG_STREAM_MAP:
+			case PRIVATE_STREAM2:
+			case PROG_STREAM_DIR:
+			case ECM_STREAM     :
+			case EMM_STREAM     :
+			case PADDING_STREAM :
+			case DSM_CC_STREAM  :
+			case ISO13522_STREAM:
+				p->done = 1;
+				/* fall through */
+			case PRIVATE_STREAM1:
+			case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+			case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+				p->found++;
+				p->cid = buf[c];
+				c++;
+				break;
+			default:
+				p->found = 0;
+				break;
+			}
+			break;
+
+		case 4:
+			if (count-c > 1) {
+				p->plen[0] = buf[c];
+				c++;
+				p->plen[1] = buf[c];
+				c++;
+				p->found += 2;
+				p->plength = (p->plen[0] << 8) | p->plen[1];
+			} else {
+				p->plen[0] = buf[c];
+				p->found++;
+				return count;
+			}
+			break;
+		case 5:
+			p->plen[1] = buf[c];
+			c++;
+			p->found++;
+			p->plength = (p->plen[0] << 8) | p->plen[1];
+			break;
+		case 6:
+			if (!p->done) {
+				p->flag1 = buf[c];
+				c++;
+				p->found++;
+				if ((p->flag1 & 0xc0) == 0x80)
+					p->mpeg = 2;
+				else {
+					p->hlength = 0;
+					p->which = 0;
+					p->mpeg = 1;
+					p->flag2 = 0;
+				}
+			}
+			break;
+
+		case 7:
+			if (!p->done && p->mpeg == 2) {
+				p->flag2 = buf[c];
+				c++;
+				p->found++;
+			}
+			break;
+
+		case 8:
+			if (!p->done && p->mpeg == 2) {
+				p->hlength = buf[c];
+				c++;
+				p->found++;
+			}
+			break;
+		}
+	}
+
+	if (c == count)
+		return count;
+
+	if (!p->plength)
+		p->plength = MMAX_PLENGTH - 6;
+
+	if (p->done || ((p->mpeg == 2 && p->found >= 9) ||
+			(p->mpeg == 1 && p->found >= 7))) {
+		switch (p->cid) {
+		case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+		case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+		case PRIVATE_STREAM1:
+			if (p->mpeg == 2 && p->found == 9) {
+				write_ipack(p, &p->flag1, 1);
+				write_ipack(p, &p->flag2, 1);
+				write_ipack(p, &p->hlength, 1);
+			}
+
+			if (p->mpeg == 1 && p->found == 7)
+				write_ipack(p, &p->flag1, 1);
+
+			if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) &&
+			    p->found < 14) {
+				while (c < count && p->found < 14) {
+					p->pts[p->found - 9] = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+				}
+				if (c == count)
+					return count;
+			}
+
+			if (p->mpeg == 1 && p->which < 2000) {
+
+				if (p->found == 7) {
+					p->check = p->flag1;
+					p->hlength = 1;
+				}
+
+				while (!p->which && c < count &&
+				       p->check == 0xff){
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+				}
+
+				if (c == count)
+					return count;
+
+				if ((p->check & 0xc0) == 0x40 && !p->which) {
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+
+					p->which = 1;
+					if (c == count)
+						return count;
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+					p->which = 2;
+					if (c == count)
+						return count;
+				}
+
+				if (p->which == 1) {
+					p->check = buf[c];
+					write_ipack(p, buf + c, 1);
+					c++;
+					p->found++;
+					p->hlength++;
+					p->which = 2;
+					if (c == count)
+						return count;
+				}
+
+				if ((p->check & 0x30) && p->check != 0xff) {
+					p->flag2 = (p->check & 0xf0) << 2;
+					p->pts[0] = p->check;
+					p->which = 3;
+				}
+
+				if (c == count)
+					return count;
+				if (p->which > 2){
+					if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) {
+						while (c < count && p->which < 7) {
+							p->pts[p->which - 2] = buf[c];
+							write_ipack(p, buf + c, 1);
+							c++;
+							p->found++;
+							p->which++;
+							p->hlength++;
+						}
+						if (c == count)
+							return count;
+					} else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) {
+						while (c < count && p->which < 12) {
+							if (p->which < 7)
+								p->pts[p->which - 2] = buf[c];
+							write_ipack(p, buf + c, 1);
+							c++;
+							p->found++;
+							p->which++;
+							p->hlength++;
+						}
+						if (c == count)
+							return count;
+					}
+					p->which = 2000;
+				}
+
+			}
+
+			while (c < count && p->found < p->plength + 6) {
+				l = count - c;
+				if (l + p->found > p->plength + 6)
+					l = p->plength + 6 - p->found;
+				write_ipack(p, buf + c, l);
+				p->found += l;
+				c += l;
+			}
+			break;
+		}
+
+
+		if (p->done) {
+			if (p->found + count - c < p->plength + 6) {
+				p->found += count - c;
+				c = count;
+			} else {
+				c += p->plength + 6 - p->found;
+				p->found = p->plength + 6;
+			}
+		}
+
+		if (p->plength && p->found == p->plength + 6) {
+			send_ipack(p);
+			av7110_ipack_reset(p);
+			if (c < count)
+				av7110_ipack_instant_repack(buf + c, count - c, p);
+		}
+	}
+	return count;
+}
diff --git a/drivers/media/dvb/ttpci/av7110_ipack.h b/drivers/media/dvb/ttpci/av7110_ipack.h
new file mode 100644
index 0000000..becf94d
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_ipack.h
@@ -0,0 +1,12 @@
+#ifndef _AV7110_IPACK_H_
+#define _AV7110_IPACK_H_
+
+extern int av7110_ipack_init(struct ipack *p, int size,
+			     void (*func)(u8 *buf,  int size, void *priv));
+
+extern void av7110_ipack_reset(struct ipack *p);
+extern int  av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p);
+extern void av7110_ipack_free(struct ipack * p);
+extern void av7110_ipack_flush(struct ipack *p);
+
+#endif
diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c
new file mode 100644
index 0000000..6d2256f
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_ir.c
@@ -0,0 +1,212 @@
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/input.h>
+#include <linux/proc_fs.h>
+#include <asm/bitops.h>
+
+#include "av7110.h"
+
+#define UP_TIMEOUT (HZ/4)
+
+/* enable ir debugging by or'ing av7110_debug with 16 */
+
+static int ir_initialized;
+static struct input_dev input_dev;
+
+static u32 ir_config;
+
+static u16 key_map [256] = {
+	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7,
+	KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO,
+	KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0,
+	0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0,
+	KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0,
+	KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW,
+	KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN,
+	0, 0, 0, 0, KEY_EPG, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR
+};
+
+
+static void av7110_emit_keyup(unsigned long data)
+{
+	if (!data || !test_bit(data, input_dev.key))
+		return;
+
+	input_event(&input_dev, EV_KEY, data, !!0);
+}
+
+
+static struct timer_list keyup_timer = { .function = av7110_emit_keyup };
+
+
+static void av7110_emit_key(u32 ircom)
+{
+	u8 data;
+	u8 addr;
+	static u16 old_toggle = 0;
+	u16 new_toggle;
+	u16 keycode;
+
+	/* extract device address and data */
+	if (ir_config & 0x0001) {
+		/* TODO RCMM: ? bits device address, 8 bits data */
+		data = ircom & 0xff;
+		addr = (ircom >> 8) & 0xff;
+	} else {
+		/* RC5: 5 bits device address, 6 bits data */
+		data = ircom & 0x3f;
+		addr = (ircom >> 6) & 0x1f;
+	}
+
+	keycode = key_map[data];
+
+	dprintk(16, "#########%08x######### addr %i data 0x%02x (keycode %i)\n",
+		ircom, addr, data, keycode);
+
+	/* check device address (if selected) */
+	if (ir_config & 0x4000)
+		if (addr != ((ir_config >> 16) & 0xff))
+			return;
+
+	if (!keycode) {
+		printk ("%s: unknown key 0x%02x!!\n", __FUNCTION__, data);
+		return;
+	}
+
+	if (ir_config & 0x0001)
+		new_toggle = 0; /* RCMM */
+	else
+		new_toggle = (ircom & 0x800); /* RC5 */
+
+	if (timer_pending(&keyup_timer)) {
+		del_timer(&keyup_timer);
+		if (keyup_timer.data != keycode || new_toggle != old_toggle) {
+			input_event(&input_dev, EV_KEY, keyup_timer.data, !!0);
+			input_event(&input_dev, EV_KEY, keycode, !0);
+		} else
+			input_event(&input_dev, EV_KEY, keycode, 2);
+
+	} else
+		input_event(&input_dev, EV_KEY, keycode, !0);
+
+	keyup_timer.expires = jiffies + UP_TIMEOUT;
+	keyup_timer.data = keycode;
+
+	add_timer(&keyup_timer);
+
+	old_toggle = new_toggle;
+}
+
+static void input_register_keys(void)
+{
+	int i;
+
+	memset(input_dev.keybit, 0, sizeof(input_dev.keybit));
+
+	for (i = 0; i < sizeof(key_map) / sizeof(key_map[0]); i++) {
+		if (key_map[i] > KEY_MAX)
+			key_map[i] = 0;
+		else if (key_map[i] > KEY_RESERVED)
+			set_bit(key_map[i], input_dev.keybit);
+	}
+}
+
+
+static void input_repeat_key(unsigned long data)
+{
+       /* dummy routine to disable autorepeat in the input driver */
+}
+
+
+static int av7110_ir_write_proc(struct file *file, const char __user *buffer,
+				unsigned long count, void *data)
+{
+	char *page;
+	int size = 4 + 256 * sizeof(u16);
+
+	if (count < size)
+		return -EINVAL;
+
+	page = (char *) vmalloc(size);
+	if (!page)
+		return -ENOMEM;
+
+	if (copy_from_user(page, buffer, size)) {
+		vfree(page);
+		return -EFAULT;
+	}
+
+	memcpy(&ir_config, page, 4);
+	memcpy(&key_map, page + 4, 256 * sizeof(u16));
+	vfree(page);
+	av7110_setup_irc_config(NULL, ir_config);
+	input_register_keys();
+	return count;
+}
+
+
+int __init av7110_ir_init(void)
+{
+	static struct proc_dir_entry *e;
+
+	if (ir_initialized)
+		return 0;
+
+	init_timer(&keyup_timer);
+	keyup_timer.data = 0;
+
+	input_dev.name = "DVB on-card IR receiver";
+
+	/**
+	 *  enable keys
+	 */
+	set_bit(EV_KEY, input_dev.evbit);
+	set_bit(EV_REP, input_dev.evbit);
+
+	input_register_keys();
+
+	input_register_device(&input_dev);
+	input_dev.timer.function = input_repeat_key;
+
+	av7110_setup_irc_config(NULL, 0x0001);
+	av7110_register_irc_handler(av7110_emit_key);
+
+	e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
+	if (e) {
+		e->write_proc = av7110_ir_write_proc;
+		e->size = 4 + 256 * sizeof(u16);
+	}
+
+	ir_initialized = 1;
+	return 0;
+}
+
+
+void __exit av7110_ir_exit(void)
+{
+	if (ir_initialized == 0)
+		return;
+	del_timer_sync(&keyup_timer);
+	remove_proc_entry("av7110_ir", NULL);
+	av7110_unregister_irc_handler(av7110_emit_key);
+	input_unregister_device(&input_dev);
+	ir_initialized = 0;
+}
+
+//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>");
+//MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
new file mode 100644
index 0000000..eb84fb0
--- /dev/null
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -0,0 +1,771 @@
+/*
+ * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.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; 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+#include <linux/byteorder/swabb.h>
+#include <linux/smp_lock.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_av.h"
+
+int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val)
+{
+	u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff };
+	struct i2c_msg msgs = { .flags = 0, .addr = 0x40, .len = 5, .buf = msg };
+
+	if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) {
+		dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n",
+		       av7110->dvb_adapter->num, reg, val);
+		return -EIO;
+	}
+	return 0;
+}
+
+int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val)
+{
+	u8 msg1[3] = { dev, reg >> 8, reg & 0xff };
+	u8 msg2[2];
+	struct i2c_msg msgs[2] = {
+		{ .flags = 0,	     .addr = 0x40, .len = 3, .buf = msg1 },
+		{ .flags = I2C_M_RD, .addr = 0x40, .len = 2, .buf = msg2 }
+	};
+
+	if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) {
+		dprintk(1, "dvb-ttpci: failed @ card %d, %u\n",
+		       av7110->dvb_adapter->num, reg);
+		return -EIO;
+	}
+	*val = (msg2[0] << 8) | msg2[1];
+	return 0;
+}
+
+static struct v4l2_input inputs[2] = {
+	{
+		.index		= 0,
+		.name		= "DVB",
+		.type		= V4L2_INPUT_TYPE_CAMERA,
+		.audioset	= 1,
+		.tuner		= 0, /* ignored */
+		.std		= V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+		.status		= 0,
+	}, {
+		.index		= 1,
+		.name		= "Television",
+		.type		= V4L2_INPUT_TYPE_TUNER,
+		.audioset	= 2,
+		.tuner		= 0,
+		.std		= V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+		.status		= 0,
+	}
+};
+
+static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data)
+{
+	u8 buf[] = { 0x00, reg, data };
+	struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 };
+
+	dprintk(4, "dev: %p\n", dev);
+
+	if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1))
+		return -1;
+	return 0;
+}
+
+static int stv0297_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data)
+{
+        u8 buf [] = { reg, data };
+        struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 2 };
+
+	if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1))
+		return -1;
+	return 0;
+}
+
+
+static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4])
+{
+	struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 };
+
+	dprintk(4, "dev: %p\n", dev);
+
+	if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1))
+		return -1;
+	return 0;
+}
+
+static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq)
+{
+	u32 div;
+	u8 config;
+	u8 buf[4];
+
+	dprintk(4, "freq: 0x%08x\n", freq);
+
+	/* magic number: 614. tuning with the frequency given by v4l2
+	   is always off by 614*62.5 = 38375 kHz...*/
+	div = freq + 614;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x8e;
+
+	if (freq < (u32) (16 * 168.25))
+		config = 0xa0;
+	else if (freq < (u32) (16 * 447.25))
+		config = 0x90;
+	else
+		config = 0x30;
+	config &= ~0x02;
+
+	buf[3] = config;
+
+	return tuner_write(dev, 0x61, buf);
+}
+
+static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq)
+{
+	u32 div;
+	u8 data[4];
+
+	div = (freq + 38900000 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0xce;
+
+	if (freq < 45000000)
+		return -EINVAL;
+	else if (freq < 137000000)
+		data[3] = 0x01;
+	else if (freq < 403000000)
+		data[3] = 0x02;
+	else if (freq < 860000000)
+		data[3] = 0x04;
+	else
+		return -EINVAL;
+
+	stv0297_writereg(dev, 0x1C, 0x87, 0x78);
+	stv0297_writereg(dev, 0x1C, 0x86, 0xc8);
+	return tuner_write(dev, 0x63, data);
+}
+
+
+
+static struct saa7146_standard analog_standard[];
+static struct saa7146_standard dvb_standard[];
+static struct saa7146_standard standard[];
+
+static struct v4l2_audio msp3400_v4l2_audio = {
+	.index = 0,
+	.name = "Television",
+	.capability = V4L2_AUDCAP_STEREO
+};
+
+static int av7110_dvb_c_switch(struct saa7146_fh *fh)
+{
+	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
+	u16 adswitch;
+	int source, sync, err;
+
+	dprintk(4, "%p\n", av7110);
+
+	if ((vv->video_status & STATUS_OVERLAY) != 0) {
+		vv->ov_suspend = vv->video_fh;
+		err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+		if (err != 0) {
+			dprintk(2, "suspending video failed\n");
+			vv->ov_suspend = NULL;
+		}
+	}
+
+	if (0 != av7110->current_input) {
+		adswitch = 1;
+		source = SAA7146_HPS_SOURCE_PORT_B;
+		sync = SAA7146_HPS_SYNC_PORT_B;
+		memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2);
+		dprintk(1, "switching to analog TV\n");
+		msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
+		msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
+		msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
+		msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
+		msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
+
+		if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+			if (ves1820_writereg(dev, 0x09, 0x0f, 0x60))
+				dprintk(1, "setting band in demodulator failed.\n");
+		} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9198 pin9(STD)
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9198 pin30(VIF)
+		}
+	} else {
+		adswitch = 0;
+		source = SAA7146_HPS_SOURCE_PORT_A;
+		sync = SAA7146_HPS_SYNC_PORT_A;
+		memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
+		dprintk(1, "switching DVB mode\n");
+		msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
+		msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
+		msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
+		msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
+		msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
+
+		if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+			if (ves1820_writereg(dev, 0x09, 0x0f, 0x20))
+				dprintk(1, "setting band in demodulator failed.\n");
+		} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+			saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9198 pin9(STD)
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF)
+		}
+	}
+
+	/* hmm, this does not do anything!? */
+	if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch))
+		dprintk(1, "ADSwitch error\n");
+
+	saa7146_set_hps_source_and_sync(dev, source, sync);
+
+	if (vv->ov_suspend != NULL) {
+		saa7146_start_preview(vv->ov_suspend);
+		vv->ov_suspend = NULL;
+	}
+
+	return 0;
+}
+
+static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+{
+	struct saa7146_dev *dev = fh->dev;
+	struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+	dprintk(4, "saa7146_dev: %p\n", dev);
+
+	switch (cmd) {
+	case VIDIOC_G_TUNER:
+	{
+		struct v4l2_tuner *t = arg;
+		u16 stereo_det;
+		s8 stereo;
+
+		dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
+
+		if (!av7110->analog_tuner_flags || t->index != 0)
+			return -EINVAL;
+
+		memset(t, 0, sizeof(*t));
+		strcpy(t->name, "Television");
+
+		t->type = V4L2_TUNER_ANALOG_TV;
+		t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+			V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+		t->rangelow = 772;	/* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
+		t->rangehigh = 13684;	/* 855.25 MHz / 62.5 kHz = 13684 */
+		/* FIXME: add the real signal strength here */
+		t->signal = 0xffff;
+		t->afc = 0;
+
+		// FIXME: standard / stereo detection is still broken
+		msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
+		dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
+
+		msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
+		dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
+		stereo = (s8)(stereo_det >> 8);
+		if (stereo > 0x10) {
+			/* stereo */
+			t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+			t->audmode = V4L2_TUNER_MODE_STEREO;
+		}
+		else if (stereo < -0x10) {
+			/* bilingual*/
+			t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+			t->audmode = V4L2_TUNER_MODE_LANG1;
+		}
+		else /* mono */
+			t->rxsubchans = V4L2_TUNER_SUB_MONO;
+
+		return 0;
+	}
+	case VIDIOC_S_TUNER:
+	{
+		struct v4l2_tuner *t = arg;
+		u16 fm_matrix, src;
+		dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
+
+		if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+			return -EINVAL;
+
+		switch (t->audmode) {
+		case V4L2_TUNER_MODE_STEREO:
+			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
+			fm_matrix = 0x3001; // stereo
+			src = 0x0020;
+			break;
+		case V4L2_TUNER_MODE_LANG1:
+			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
+			fm_matrix = 0x3000; // mono
+			src = 0x0000;
+			break;
+		case V4L2_TUNER_MODE_LANG2:
+			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
+			fm_matrix = 0x3000; // mono
+			src = 0x0010;
+			break;
+		default: /* case V4L2_TUNER_MODE_MONO: {*/
+			dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
+			fm_matrix = 0x3000; // mono
+			src = 0x0030;
+			break;
+		}
+		msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
+		msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
+		msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
+		msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
+		return 0;
+	}
+	case VIDIOC_G_FREQUENCY:
+	{
+		struct v4l2_frequency *f = arg;
+
+		dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency);
+
+		if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+			return -EINVAL;
+
+		memset(f, 0, sizeof(*f));
+		f->type = V4L2_TUNER_ANALOG_TV;
+		f->frequency =	av7110->current_freq;
+		return 0;
+	}
+	case VIDIOC_S_FREQUENCY:
+	{
+		struct v4l2_frequency *f = arg;
+
+		dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency);
+
+		if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+			return -EINVAL;
+
+		if (V4L2_TUNER_ANALOG_TV != f->type)
+			return -EINVAL;
+
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); // fast mute
+		msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
+
+		/* tune in desired frequency */
+		if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+			ves1820_set_tv_freq(dev, f->frequency);
+		} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+			stv0297_set_tv_freq(dev, f->frequency);
+		}
+		av7110->current_freq = f->frequency;
+
+		msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); // start stereo detection
+		msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
+		msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
+		msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
+		return 0;
+	}
+	case VIDIOC_ENUMINPUT:
+	{
+		struct v4l2_input *i = arg;
+
+		dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
+
+		if (av7110->analog_tuner_flags) {
+			if (i->index < 0 || i->index >= 2)
+				return -EINVAL;
+		} else {
+			if (i->index != 0)
+				return -EINVAL;
+		}
+
+		memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
+
+		return 0;
+	}
+	case VIDIOC_G_INPUT:
+	{
+		int *input = (int *)arg;
+		*input = av7110->current_input;
+		dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
+		return 0;
+	}
+	case VIDIOC_S_INPUT:
+	{
+		int input = *(int *)arg;
+
+		dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
+
+		if (!av7110->analog_tuner_flags)
+			return 0;
+
+		if (input < 0 || input >= 2)
+			return -EINVAL;
+
+		/* FIXME: switch inputs here */
+		av7110->current_input = input;
+		return av7110_dvb_c_switch(fh);
+	}
+	case VIDIOC_G_AUDIO:
+	{
+		struct v4l2_audio *a = arg;
+
+		dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+		if (a->index != 0)
+			return -EINVAL;
+		memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio));
+		break;
+	}
+	case VIDIOC_S_AUDIO:
+	{
+		struct v4l2_audio *a = arg;
+		dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
+		break;
+	}
+	default:
+		printk("no such ioctl\n");
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+static struct saa7146_extension_ioctls ioctls[] = {
+	{ VIDIOC_ENUMINPUT,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_G_INPUT,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_S_INPUT,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_G_FREQUENCY,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_S_FREQUENCY,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_G_TUNER,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_S_TUNER,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_G_AUDIO,	SAA7146_EXCLUSIVE },
+	{ VIDIOC_S_AUDIO,	SAA7146_EXCLUSIVE },
+	{ 0, 0 }
+};
+
+static u8 saa7113_init_regs[] = {
+	0x02, 0xd0,
+	0x03, 0x23,
+	0x04, 0x00,
+	0x05, 0x00,
+	0x06, 0xe9,
+	0x07, 0x0d,
+	0x08, 0x98,
+	0x09, 0x02,
+	0x0a, 0x80,
+	0x0b, 0x40,
+	0x0c, 0x40,
+	0x0d, 0x00,
+	0x0e, 0x01,
+	0x0f, 0x7c,
+	0x10, 0x48,
+	0x11, 0x0c,
+	0x12, 0x8b,
+	0x13, 0x1a,
+	0x14, 0x00,
+	0x15, 0x00,
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1b, 0x00,
+	0x1c, 0x00,
+	0x1d, 0x00,
+	0x1e, 0x00,
+
+	0x41, 0x77,
+	0x42, 0x77,
+	0x43, 0x77,
+	0x44, 0x77,
+	0x45, 0x77,
+	0x46, 0x77,
+	0x47, 0x77,
+	0x48, 0x77,
+	0x49, 0x77,
+	0x4a, 0x77,
+	0x4b, 0x77,
+	0x4c, 0x77,
+	0x4d, 0x77,
+	0x4e, 0x77,
+	0x4f, 0x77,
+	0x50, 0x77,
+	0x51, 0x77,
+	0x52, 0x77,
+	0x53, 0x77,
+	0x54, 0x77,
+	0x55, 0x77,
+	0x56, 0x77,
+	0x57, 0xff,
+
+	0xff
+};
+
+
+static struct saa7146_ext_vv av7110_vv_data_st;
+static struct saa7146_ext_vv av7110_vv_data_c;
+
+int av7110_init_analog_module(struct av7110 *av7110)
+{
+	u16 version1, version2;
+
+	if (i2c_writereg(av7110, 0x80, 0x0, 0x80) != 1
+	    || i2c_writereg(av7110, 0x80, 0x0, 0) != 1)
+		return -ENODEV;
+
+	printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3400\n",
+		av7110->dvb_adapter->num);
+	av7110->adac_type = DVB_ADAC_MSP;
+	msleep(100); // the probing above resets the msp...
+	msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1);
+	msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2);
+	dprintk(1, "dvb-ttpci: @ card %d MSP3400 version 0x%04x 0x%04x\n",
+		av7110->dvb_adapter->num, version1, version2);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
+	msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
+	msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
+	msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume
+	msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
+	msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
+	msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x4800); // prescale SCART
+
+	if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
+		INFO(("saa7113 not accessible.\n"));
+	} else {
+		u8 *i = saa7113_init_regs;
+
+		if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) {
+			/* Fujitsu/Siemens DVB-Cable */
+			av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
+		} else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) {
+			/* Hauppauge/TT DVB-C premium */
+			av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
+		} else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) {
+			/* Hauppauge/TT DVB-C premium */
+			av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297;
+		}
+
+		/* setup for DVB by default */
+		if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+			if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20))
+				dprintk(1, "setting band in demodulator failed.\n");
+		} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+			saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9198 pin9(STD)
+			saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9198 pin30(VIF)
+		}
+
+		/* init the saa7113 */
+		while (*i != 0xff) {
+			if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) {
+				dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter->num);
+				break;
+			}
+			i += 2;
+		}
+		/* setup msp for analog sound: B/G Dual-FM */
+		msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001,  3); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005,  4); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005,  0); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005,  3); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2
+		msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG
+		msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz
+		msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI
+		msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz
+		msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI
+		msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2
+	}
+
+	memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
+	/* set dd1 stream a & b */
+	saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(av7110->dev, DD1_INIT, 0x03000700);
+	saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	return 0;
+}
+
+int av7110_init_v4l(struct av7110 *av7110)
+{
+	struct saa7146_dev* dev = av7110->dev;
+	int ret;
+
+	/* special case DVB-C: these cards have an analog tuner
+	   plus need some special handling, so we have separate
+	   saa7146_ext_vv data for these... */
+	if (av7110->analog_tuner_flags)
+		ret = saa7146_vv_init(dev, &av7110_vv_data_c);
+	else
+		ret = saa7146_vv_init(dev, &av7110_vv_data_st);
+
+	if (ret) {
+		ERR(("cannot init capture device. skipping.\n"));
+		return -ENODEV;
+	}
+
+	if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
+		ERR(("cannot register capture device. skipping.\n"));
+		saa7146_vv_release(dev);
+		return -ENODEV;
+	}
+	if (av7110->analog_tuner_flags) {
+		if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) {
+			ERR(("cannot register vbi v4l2 device. skipping.\n"));
+		} else {
+			av7110->analog_tuner_flags |= ANALOG_TUNER_VBI;
+		}
+	}
+	return 0;
+}
+
+int av7110_exit_v4l(struct av7110 *av7110)
+{
+	saa7146_unregister_device(&av7110->v4l_dev, av7110->dev);
+	if (av7110->analog_tuner_flags & ANALOG_TUNER_VBI)
+		saa7146_unregister_device(&av7110->vbi_dev, av7110->dev);
+	return 0;
+}
+
+
+
+/* FIXME: these values are experimental values that look better than the
+   values from the latest "official" driver -- at least for me... (MiHu) */
+static struct saa7146_standard standard[] = {
+	{
+		.name	= "PAL",	.id		= V4L2_STD_PAL_BG,
+		.v_offset	= 0x15,	.v_field	= 288,
+		.h_offset	= 0x48,	.h_pixels	= 708,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC",	.id		= V4L2_STD_NTSC,
+		.v_offset	= 0x10,	.v_field	= 244,
+		.h_offset	= 0x40,	.h_pixels	= 708,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}
+};
+
+static struct saa7146_standard analog_standard[] = {
+	{
+		.name	= "PAL",	.id		= V4L2_STD_PAL_BG,
+		.v_offset	= 0x1b,	.v_field	= 288,
+		.h_offset	= 0x08,	.h_pixels	= 708,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC",	.id		= V4L2_STD_NTSC,
+		.v_offset	= 0x10,	.v_field	= 244,
+		.h_offset	= 0x40,	.h_pixels	= 708,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}
+};
+
+static struct saa7146_standard dvb_standard[] = {
+	{
+		.name	= "PAL",	.id		= V4L2_STD_PAL_BG,
+		.v_offset	= 0x14,	.v_field	= 288,
+		.h_offset	= 0x48,	.h_pixels	= 708,
+		.v_max_out	= 576,	.h_max_out	= 768,
+	}, {
+		.name	= "NTSC",	.id		= V4L2_STD_NTSC,
+		.v_offset	= 0x10,	.v_field	= 244,
+		.h_offset	= 0x40,	.h_pixels	= 708,
+		.v_max_out	= 480,	.h_max_out	= 640,
+	}
+};
+
+static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
+{
+	struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+
+	if (std->id == V4L2_STD_PAL) {
+		av7110->vidmode = VIDEO_MODE_PAL;
+		av7110_set_vidmode(av7110, av7110->vidmode);
+	}
+	else if (std->id == V4L2_STD_NTSC) {
+		av7110->vidmode = VIDEO_MODE_NTSC;
+		av7110_set_vidmode(av7110, av7110->vidmode);
+	}
+	else
+		return -1;
+
+	return 0;
+}
+
+
+static struct saa7146_ext_vv av7110_vv_data_st = {
+	.inputs		= 1,
+	.audios		= 1,
+	.capabilities	= 0,
+	.flags		= 0,
+
+	.stds		= &standard[0],
+	.num_stds	= ARRAY_SIZE(standard),
+	.std_callback	= &std_callback,
+
+	.ioctls		= &ioctls[0],
+	.ioctl		= av7110_ioctl,
+};
+
+static struct saa7146_ext_vv av7110_vv_data_c = {
+	.inputs		= 1,
+	.audios		= 1,
+	.capabilities	= V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE,
+	.flags		= SAA7146_USE_PORT_B_FOR_VBI,
+
+	.stds		= &standard[0],
+	.num_stds	= ARRAY_SIZE(standard),
+	.std_callback	= &std_callback,
+
+	.ioctls		= &ioctls[0],
+	.ioctl		= av7110_ioctl,
+};
+
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
new file mode 100644
index 0000000..14e9632
--- /dev/null
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -0,0 +1,1014 @@
+/*
+ * budget-av.c: driver for the SAA7146 based Budget DVB cards
+ *              with analog video in
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
+ *                               Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "budget.h"
+#include "stv0299.h"
+#include "tda10021.h"
+#include "tda1004x.h"
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+
+#include "dvb_ca_en50221.h"
+
+#define DEBICICAM		0x02420000
+
+struct budget_av {
+	struct budget budget;
+	struct video_device *vd;
+	int cur_input;
+	int has_saa7113;
+	struct tasklet_struct ciintf_irq_tasklet;
+	int slot_status;
+	struct dvb_ca_en50221 ca;
+};
+
+static int enable_ci = 0;
+
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg)
+{
+	u8 mm1[] = { 0x00 };
+	u8 mm2[] = { 0x00 };
+	struct i2c_msg msgs[2];
+
+	msgs[0].flags = 0;
+	msgs[1].flags = I2C_M_RD;
+	msgs[0].addr = msgs[1].addr = id / 2;
+	mm1[0] = reg;
+	msgs[0].len = 1;
+	msgs[1].len = 1;
+	msgs[0].buf = mm1;
+	msgs[1].buf = mm2;
+
+	i2c_transfer(i2c, msgs, 2);
+
+	return mm2[0];
+}
+
+static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len)
+{
+	u8 mm1[] = { reg };
+	struct i2c_msg msgs[2] = {
+		{.addr = id / 2,.flags = 0,.buf = mm1,.len = 1},
+		{.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len}
+	};
+
+	if (i2c_transfer(i2c, msgs, 2) != 2)
+		return -EIO;
+
+	return 0;
+}
+
+static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val)
+{
+	u8 msg[2] = { reg, val };
+	struct i2c_msg msgs;
+
+	msgs.flags = 0;
+	msgs.addr = id / 2;
+	msgs.len = 2;
+	msgs.buf = msg;
+	return i2c_transfer(i2c, &msgs, 1);
+}
+
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
+	udelay(1);
+
+	result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 0);
+
+	if (result == -ETIMEDOUT)
+		budget_av->slot_status = 0;
+	return result;
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI);
+	udelay(1);
+
+	result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 0);
+
+	if (result == -ETIMEDOUT)
+		budget_av->slot_status = 0;
+	return result;
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+	udelay(1);
+
+	result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0);
+
+	if (result == -ETIMEDOUT)
+		budget_av->slot_status = 0;
+	return result;
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	int result;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+	udelay(1);
+
+	result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0);
+
+	if (result == -ETIMEDOUT)
+		budget_av->slot_status = 0;
+	return result;
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+	int max = 20;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	dprintk(1, "ciintf_slot_reset\n");
+
+	/* reset the card */
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI);
+	msleep(100);
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
+
+	while (--max > 0 && ciintf_read_attribute_mem(ca, slot, 0) != 0x1d)
+		msleep(100);
+
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	dprintk(1, "ciintf_slot_shutdown\n");
+
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	budget_av->slot_status = 0;
+	return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status);
+
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
+	return 0;
+}
+
+static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+	struct budget_av *budget_av = (struct budget_av *) ca->data;
+	struct saa7146_dev *saa = budget_av->budget.dev;
+	int cam = 0;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	if (!budget_av->slot_status) {
+		saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+		udelay(1);
+		cam = saa7146_read(saa, PSR) & MASK_06;
+		saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+
+		if (cam)
+			budget_av->slot_status = 1;
+	} else if (!open) {
+		saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO);
+		if (ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1) == -ETIMEDOUT)
+			budget_av->slot_status = 0;
+	}
+
+	if (budget_av->slot_status == 1)
+		return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
+
+	return 0;
+}
+
+static int ciintf_init(struct budget_av *budget_av)
+{
+	struct saa7146_dev *saa = budget_av->budget.dev;
+	int result;
+
+	memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221));
+
+	/* setup GPIOs */
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+	saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO);
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+
+	/* Reset the card */
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI);
+	msleep(50);
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
+	msleep(100);
+
+	/* Enable DEBI pins */
+	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+
+	/* register CI interface */
+	budget_av->ca.owner = THIS_MODULE;
+	budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem;
+	budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem;
+	budget_av->ca.read_cam_control = ciintf_read_cam_control;
+	budget_av->ca.write_cam_control = ciintf_write_cam_control;
+	budget_av->ca.slot_reset = ciintf_slot_reset;
+	budget_av->ca.slot_shutdown = ciintf_slot_shutdown;
+	budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable;
+	budget_av->ca.poll_slot_status = ciintf_poll_slot_status;
+	budget_av->ca.data = budget_av;
+	if ((result = dvb_ca_en50221_init(budget_av->budget.dvb_adapter,
+					  &budget_av->ca, 0, 1)) != 0) {
+		printk("budget_av: CI interface detected, but initialisation failed.\n");
+		goto error;
+	}
+	// success!
+	printk("ciintf_init: CI interface initialised\n");
+	budget_av->budget.ci_present = 1;
+	return 0;
+
+error:
+	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	return result;
+}
+
+static void ciintf_deinit(struct budget_av *budget_av)
+{
+	struct saa7146_dev *saa = budget_av->budget.dev;
+
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+	saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+
+	/* release the CA device */
+	dvb_ca_en50221_release(&budget_av->ca);
+
+	/* disable DEBI pins */
+	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+}
+
+
+static const u8 saa7113_tab[] = {
+	0x01, 0x08,
+	0x02, 0xc0,
+	0x03, 0x33,
+	0x04, 0x00,
+	0x05, 0x00,
+	0x06, 0xeb,
+	0x07, 0xe0,
+	0x08, 0x28,
+	0x09, 0x00,
+	0x0a, 0x80,
+	0x0b, 0x47,
+	0x0c, 0x40,
+	0x0d, 0x00,
+	0x0e, 0x01,
+	0x0f, 0x44,
+
+	0x10, 0x08,
+	0x11, 0x0c,
+	0x12, 0x7b,
+	0x13, 0x00,
+	0x15, 0x00, 0x16, 0x00, 0x17, 0x00,
+
+	0x57, 0xff,
+	0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07,
+	0x5b, 0x83, 0x5e, 0x00,
+	0xff
+};
+
+static int saa7113_init(struct budget_av *budget_av)
+{
+	struct budget *budget = &budget_av->budget;
+	const u8 *data = saa7113_tab;
+
+	if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) {
+		dprintk(1, "saa7113 not found on KNC card\n");
+		return -ENODEV;
+	}
+
+	dprintk(1, "saa7113 detected and initializing\n");
+
+	while (*data != 0xff) {
+		i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1));
+		data += 2;
+	}
+
+	dprintk(1, "saa7113  status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f));
+
+	return 0;
+}
+
+static int saa7113_setinput(struct budget_av *budget_av, int input)
+{
+	struct budget *budget = &budget_av->budget;
+
+	if (1 != budget_av->has_saa7113)
+		return -ENODEV;
+
+	if (input == 1) {
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7);
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80);
+	} else if (input == 0) {
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0);
+		i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00);
+	} else
+		return -EINVAL;
+
+	budget_av->cur_input = input;
+	return 0;
+}
+
+
+static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+	u8 m1;
+
+	aclk = 0xb5;
+	if (srate < 2000000)
+		bclk = 0x86;
+	else if (srate < 5000000)
+		bclk = 0x89;
+	else if (srate < 15000000)
+		bclk = 0x8f;
+	else if (srate < 45000000)
+		bclk = 0x95;
+
+	m1 = 0x14;
+	if (srate < 4000000)
+		m1 = 0x10;
+
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+	stv0299_writereg(fe, 0x0f, 0x80 | m1);
+
+	return 0;
+}
+
+static int philips_su1278_ty_ci_pll_set(struct dvb_frontend *fe,
+					struct dvb_frontend_parameters *params)
+{
+	struct budget_av *budget_av = (struct budget_av *) fe->dvb->priv;
+	u32 div;
+	u8 buf[4];
+	struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+	if ((params->frequency < 950000) || (params->frequency > 2150000))
+		return -EINVAL;
+
+	div = (params->frequency + (125 - 1)) / 125;	// round correctly
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+	buf[3] = 0x20;
+
+	if (params->u.qpsk.symbol_rate < 4000000)
+		buf[3] |= 1;
+
+	if (params->frequency < 1250000)
+		buf[3] |= 0;
+	else if (params->frequency < 1550000)
+		buf[3] |= 0x40;
+	else if (params->frequency < 2050000)
+		buf[3] |= 0x80;
+	else if (params->frequency < 2150000)
+		buf[3] |= 0xC0;
+
+	if (i2c_transfer(&budget_av->budget.i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static u8 typhoon_cinergy1200s_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x7d,		/* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+	0x05, 0x35,		/* I2CT = 0, SCLT = 1, SDAT = 1 */
+	0x06, 0x40,		/* DAC not used, set to high impendance mode */
+	0x07, 0x00,		/* DAC LSB */
+	0x08, 0x40,		/* DiSEqC off */
+	0x09, 0x00,		/* FIFO */
+	0x0c, 0x51,		/* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+	0x0d, 0x82,		/* DC offset compensation = ON, beta_agc1 = 2 */
+	0x0e, 0x23,		/* alpha_tmg = 2, beta_tmg = 3 */
+	0x10, 0x3f,		// AGC2  0x3d
+	0x11, 0x84,
+	0x12, 0xb5,		// Lock detect: -64  Carrier freq detect:on
+	0x15, 0xc9,		// lock detector threshold
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,		// out imp: normal  out type: parallel FEC mode:0
+	0x29, 0x1e,		// 1/2 threshold
+	0x2a, 0x14,		// 2/3 threshold
+	0x2b, 0x0f,		// 3/4 threshold
+	0x2c, 0x09,		// 5/6 threshold
+	0x2d, 0x05,		// 7/8 threshold
+	0x2e, 0x01,
+	0x31, 0x1f,		// test all FECs
+	0x32, 0x19,		// viterbi and synchro search
+	0x33, 0xfc,		// rs control
+	0x34, 0x93,		// error control
+	0x0f, 0x92,
+	0xff, 0xff
+};
+
+static struct stv0299_config typhoon_config = {
+	.demod_address = 0x68,
+	.inittab = typhoon_cinergy1200s_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP0,
+	.min_delay_ms = 100,
+	.set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+	.pll_set = philips_su1278_ty_ci_pll_set,
+};
+
+
+static struct stv0299_config cinergy_1200s_config = {
+	.demod_address = 0x68,
+	.inittab = typhoon_cinergy1200s_inittab,
+	.mclk = 88000000UL,
+	.invert = 0,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_0,
+	.volt13_op0_op1 = STV0299_VOLT13_OP0,
+	.min_delay_ms = 100,
+	.set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate,
+	.pll_set = philips_su1278_ty_ci_pll_set,
+};
+
+
+static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	u8 buf[4];
+	struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+#define TUNER_MUL 62500
+
+	u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x8e;
+	buf[3] = (params->frequency < 174500000 ? 0xa1 :
+		  params->frequency < 454000000 ? 0x92 : 0x34);
+
+	if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct tda10021_config philips_cu1216_config = {
+	.demod_address = 0x0c,
+	.pll_set = philips_cu1216_pll_set,
+};
+
+
+
+
+static int philips_tu1216_pll_init(struct dvb_frontend *fe)
+{
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+
+	// setup PLL configuration
+	if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	msleep(1);
+
+	return 0;
+}
+
+static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len =
+			sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = params->frequency + 36166000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000)
+		cp = 3;
+	else if (tuner_frequency < 160000000)
+		cp = 5;
+	else if (tuner_frequency < 200000000)
+		cp = 6;
+	else if (tuner_frequency < 290000000)
+		cp = 3;
+	else if (tuner_frequency < 420000000)
+		cp = 5;
+	else if (tuner_frequency < 480000000)
+		cp = 6;
+	else if (tuner_frequency < 620000000)
+		cp = 3;
+	else if (tuner_frequency < 830000000)
+		cp = 5;
+	else if (tuner_frequency < 895000000)
+		cp = 7;
+	else
+		return -EINVAL;
+
+	// determine band
+	if (params->frequency < 49000000)
+		return -EINVAL;
+	else if (params->frequency < 161000000)
+		band = 1;
+	else if (params->frequency < 444000000)
+		band = 2;
+	else if (params->frequency < 861000000)
+		band = 4;
+	else
+		return -EINVAL;
+
+	// setup PLL filter
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		filter = 0;
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		filter = 0;
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		filter = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// calculate divisor
+	// ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+	tuner_frequency = (((params->frequency / 1000) * 6) + 217496) / 1000;
+
+	// setup tuner buffer
+	tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xca;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+	if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(1);
+	return 0;
+}
+
+static int philips_tu1216_request_firmware(struct dvb_frontend *fe,
+					   const struct firmware **fw, char *name)
+{
+	struct budget *budget = (struct budget *) fe->dvb->priv;
+
+	return request_firmware(fw, name, &budget->dev->pci->dev);
+}
+
+static struct tda1004x_config philips_tu1216_config = {
+
+	.demod_address = 0x8,
+	.invert = 1,
+	.invert_oclk = 1,
+	.pll_init = philips_tu1216_pll_init,
+	.pll_set = philips_tu1216_pll_set,
+	.request_firmware = philips_tu1216_request_firmware,
+};
+
+
+
+
+static u8 read_pwm(struct budget_av *budget_av)
+{
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1},
+	{.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
+	};
+
+	if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2)
+	    || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+
+static void frontend_init(struct budget_av *budget_av)
+{
+	switch (budget_av->budget.dev->pci->subsystem_device) {
+	case 0x4f56:		// Typhoon/KNC1 DVB-S budget (stv0299/Philips SU1278(tsa5059))
+		budget_av->budget.dvb_frontend =
+			stv0299_attach(&typhoon_config, &budget_av->budget.i2c_adap);
+		if (budget_av->budget.dvb_frontend != NULL) {
+			break;
+		}
+		break;
+
+	case 0x0020:		// KNC1 DVB-C budget (tda10021/Philips CU1216(tua6034))
+		budget_av->budget.dvb_frontend =
+			tda10021_attach(&philips_cu1216_config,
+					&budget_av->budget.i2c_adap, read_pwm(budget_av));
+		if (budget_av->budget.dvb_frontend != NULL) {
+			break;
+		}
+		break;
+
+	case 0x0030:		// KNC1 DVB-T budget (tda10046/Philips TU1216(tda6651tt))
+		budget_av->budget.dvb_frontend =
+			tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap);
+		if (budget_av->budget.dvb_frontend != NULL) {
+			break;
+		}
+		break;
+
+	case 0x1154:		// TerraTec Cinergy 1200 DVB-S (stv0299/Philips SU1278(tsa5059))
+		budget_av->budget.dvb_frontend =
+			stv0299_attach(&cinergy_1200s_config, &budget_av->budget.i2c_adap);
+		if (budget_av->budget.dvb_frontend != NULL) {
+			break;
+		}
+		break;
+
+	case 0x1156:		// Terratec Cinergy 1200 DVB-C (tda10021/Philips CU1216(tua6034))
+		budget_av->budget.dvb_frontend =
+			tda10021_attach(&philips_cu1216_config,
+					&budget_av->budget.i2c_adap, read_pwm(budget_av));
+		if (budget_av->budget.dvb_frontend) {
+			break;
+		}
+		break;
+
+	case 0x1157:		// Terratec Cinergy 1200 DVB-T (tda10046/Philips TU1216(tda6651tt))
+		budget_av->budget.dvb_frontend =
+			tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap);
+		if (budget_av->budget.dvb_frontend) {
+			break;
+		}
+		break;
+	}
+
+	if (budget_av->budget.dvb_frontend == NULL) {
+		printk("budget_av: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		       budget_av->budget.dev->pci->vendor,
+		       budget_av->budget.dev->pci->device,
+		       budget_av->budget.dev->pci->subsystem_vendor,
+		       budget_av->budget.dev->pci->subsystem_device);
+	} else {
+		if (dvb_register_frontend
+		    (budget_av->budget.dvb_adapter, budget_av->budget.dvb_frontend)) {
+			printk("budget-av: Frontend registration failed!\n");
+			if (budget_av->budget.dvb_frontend->ops->release)
+				budget_av->budget.dvb_frontend->ops->release(budget_av->budget.dvb_frontend);
+			budget_av->budget.dvb_frontend = NULL;
+		}
+	}
+}
+
+
+static void budget_av_irq(struct saa7146_dev *dev, u32 * isr)
+{
+	struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+
+	dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av);
+
+	if (*isr & MASK_10)
+		ttpci_budget_irq10_handler(dev, isr);
+}
+
+static int budget_av_detach(struct saa7146_dev *dev)
+{
+	struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+	int err;
+
+	dprintk(2, "dev: %p\n", dev);
+
+	if (1 == budget_av->has_saa7113) {
+		saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
+
+		msleep(200);
+
+		saa7146_unregister_device(&budget_av->vd, dev);
+	}
+
+	if (budget_av->budget.ci_present)
+		ciintf_deinit(budget_av);
+
+	if (budget_av->budget.dvb_frontend != NULL)
+		dvb_unregister_frontend(budget_av->budget.dvb_frontend);
+	err = ttpci_budget_deinit(&budget_av->budget);
+
+	kfree(budget_av);
+
+	return err;
+}
+
+static struct saa7146_ext_vv vv_data;
+
+static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+	struct budget_av *budget_av;
+	u8 *mac;
+	int err;
+
+	dprintk(2, "dev: %p\n", dev);
+
+	if (!(budget_av = kmalloc(sizeof(struct budget_av), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(budget_av, 0, sizeof(struct budget_av));
+
+	budget_av->budget.ci_present = 0;
+
+	dev->ext_priv = budget_av;
+
+	if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) {
+		kfree(budget_av);
+		return err;
+	}
+
+	/* knc1 initialization */
+	saa7146_write(dev, DD1_STREAM_B, 0x04000000);
+	saa7146_write(dev, DD1_INIT, 0x07000600);
+	saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26);
+
+	saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI);
+	msleep(500);
+
+	if (0 == saa7113_init(budget_av)) {
+		budget_av->has_saa7113 = 1;
+
+		if (0 != saa7146_vv_init(dev, &vv_data)) {
+			/* fixme: proper cleanup here */
+			ERR(("cannot init vv subsystem.\n"));
+			return err;
+		}
+
+		if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
+			/* fixme: proper cleanup here */
+			ERR(("cannot register capture v4l2 device.\n"));
+			return err;
+		}
+
+		/* beware: this modifies dev->vv ... */
+		saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A,
+						SAA7146_HPS_SYNC_PORT_A);
+
+		saa7113_setinput(budget_av, 0);
+	} else {
+		budget_av->has_saa7113 = 0;
+
+		saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO);
+	}
+
+	/* fixme: find some sane values here... */
+	saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+	mac = budget_av->budget.dvb_adapter->proposed_mac;
+	if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) {
+		printk("KNC1-%d: Could not read MAC from KNC1 card\n",
+		       budget_av->budget.dvb_adapter->num);
+		memset(mac, 0, 6);
+	} else {
+		printk("KNC1-%d: MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+		       budget_av->budget.dvb_adapter->num,
+		       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	}
+
+	budget_av->budget.dvb_adapter->priv = budget_av;
+	frontend_init(budget_av);
+
+	if (enable_ci)
+		ciintf_init(budget_av);
+
+	return 0;
+}
+
+#define KNC1_INPUTS 2
+static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
+	{0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
+	{1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
+};
+
+static struct saa7146_extension_ioctls ioctls[] = {
+	{VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE},
+	{VIDIOC_G_INPUT, SAA7146_EXCLUSIVE},
+	{VIDIOC_S_INPUT, SAA7146_EXCLUSIVE},
+	{0, 0}
+};
+
+static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+{
+	struct saa7146_dev *dev = fh->dev;
+	struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+
+	switch (cmd) {
+	case VIDIOC_ENUMINPUT:{
+		struct v4l2_input *i = arg;
+
+		dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
+		if (i->index < 0 || i->index >= KNC1_INPUTS) {
+			return -EINVAL;
+		}
+		memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
+		return 0;
+	}
+	case VIDIOC_G_INPUT:{
+		int *input = (int *) arg;
+
+		*input = budget_av->cur_input;
+
+		dprintk(1, "VIDIOC_G_INPUT %d.\n", *input);
+		return 0;
+	}
+	case VIDIOC_S_INPUT:{
+		int input = *(int *) arg;
+		dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
+		return saa7113_setinput(budget_av, input);
+	}
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static struct saa7146_standard standard[] = {
+	{.name = "PAL",.id = V4L2_STD_PAL,
+	 .v_offset = 0x17,.v_field = 288,
+	 .h_offset = 0x14,.h_pixels = 680,
+	 .v_max_out = 576,.h_max_out = 768 },
+
+	{.name = "NTSC",.id = V4L2_STD_NTSC,
+	 .v_offset = 0x16,.v_field = 240,
+	 .h_offset = 0x06,.h_pixels = 708,
+	 .v_max_out = 480,.h_max_out = 640, },
+};
+
+static struct saa7146_ext_vv vv_data = {
+	.inputs = 2,
+	.capabilities = 0,	// perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113
+	.flags = 0,
+	.stds = &standard[0],
+	.num_stds = sizeof(standard) / sizeof(struct saa7146_standard),
+	.ioctls = &ioctls[0],
+	.ioctl = av_ioctl,
+};
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);
+MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);
+MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
+MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);
+MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);
+MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T);
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56),
+	MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
+	MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
+	MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154),
+	MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156),
+	MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157),
+	{
+	 .vendor = 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+	.name = "budget dvb /w video in\0",
+	.pci_tbl = pci_tbl,
+
+	.module = THIS_MODULE,
+	.attach = budget_av_attach,
+	.detach = budget_av_detach,
+
+	.irq_mask = MASK_10,
+	.irq_func = budget_av_irq,
+};
+
+static int __init budget_av_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_av_exit(void)
+{
+	saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_av_init);
+module_exit(budget_av_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+		   "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)");
+module_param_named(enable_ci, enable_ci, int, 0644);
+MODULE_PARM_DESC(enable_ci, "Turn on/off CI module (default:off).");
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
new file mode 100644
index 0000000..521111be
--- /dev/null
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -0,0 +1,995 @@
+/*
+ * budget-ci.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ *     msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM>
+ *     partially based on the Siemens DVB driver by Ralph+Marcus Metzler
+ *
+ * CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "budget.h"
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/spinlock.h>
+
+#include "dvb_ca_en50221.h"
+#include "stv0299.h"
+#include "tda1004x.h"
+
+#define DEBIADDR_IR		0x1234
+#define DEBIADDR_CICONTROL	0x0000
+#define DEBIADDR_CIVERSION	0x4000
+#define DEBIADDR_IO		0x1000
+#define DEBIADDR_ATTR		0x3000
+
+#define CICONTROL_RESET		0x01
+#define CICONTROL_ENABLETS	0x02
+#define CICONTROL_CAMDETECT	0x08
+
+#define DEBICICTL		0x00420000
+#define DEBICICAM		0x02420000
+
+#define SLOTSTATUS_NONE		1
+#define SLOTSTATUS_PRESENT	2
+#define SLOTSTATUS_RESET	4
+#define SLOTSTATUS_READY	8
+#define SLOTSTATUS_OCCUPIED	(SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
+
+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;
+	struct dvb_ca_en50221 ca;
+	char ir_dev_name[50];
+};
+
+/* 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)
+{
+	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 */
+}
+
+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;
+
+	if (code & 0x40) {
+		code &= 0x3f;
+
+		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);
+		}
+
+		if (!key_map[code]) {
+			printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code);
+			return;
+		}
+
+		/* 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);
+	}
+}
+
+static int msp430_ir_init(struct budget_ci *budget_ci)
+{
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	int i;
+
+	memset(&budget_ci->input_dev, 0, sizeof(struct input_dev));
+
+	sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name);
+	budget_ci->input_dev.name = budget_ci->ir_dev_name;
+
+	set_bit(EV_KEY, budget_ci->input_dev.evbit);
+
+	for (i = 0; i < sizeof(key_map) / sizeof(*key_map); i++)
+		if (key_map[i])
+			set_bit(key_map[i], budget_ci->input_dev.keybit);
+
+	input_register_device(&budget_ci->input_dev);
+
+	budget_ci->input_dev.timer.function = msp430_ir_debounce;
+
+	saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06);
+
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
+
+	return 0;
+}
+
+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;
+
+	saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06);
+	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+
+	if (del_timer(&dev->timer))
+		input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+
+	input_unregister_device(dev);
+}
+
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
+				     DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0);
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
+				      DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0);
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
+				     DEBIADDR_IO | (address & 3), 1, 1, 0);
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
+				      DEBIADDR_IO | (address & 3), 1, value, 1, 0);
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	// trigger on RISING edge during reset so we know when READY is re-asserted
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+	budget_ci->slot_status = SLOTSTATUS_RESET;
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
+	msleep(1);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       CICONTROL_RESET, 1, 0);
+
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
+	return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	int tmp;
+
+	if (slot != 0)
+		return -EINVAL;
+
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
+
+	tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       tmp | CICONTROL_ENABLETS, 1, 0);
+
+	ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
+	return 0;
+}
+
+static void ciintf_interrupt(unsigned long data)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) data;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	unsigned int flags;
+
+	// ensure we don't get spurious IRQs during initialisation
+	if (!budget_ci->budget.ci_present)
+		return;
+
+	// read the CAM status
+	flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+	if (flags & CICONTROL_CAMDETECT) {
+
+		// GPIO should be set to trigger on falling edge if a CAM is present
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+
+		if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+			// CAM insertion IRQ
+			budget_ci->slot_status = SLOTSTATUS_PRESENT;
+			dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
+						     DVB_CA_EN50221_CAMCHANGE_INSERTED);
+
+		} else if (budget_ci->slot_status & SLOTSTATUS_RESET) {
+			// CAM ready (reset completed)
+			budget_ci->slot_status = SLOTSTATUS_READY;
+			dvb_ca_en50221_camready_irq(&budget_ci->ca, 0);
+
+		} else if (budget_ci->slot_status & SLOTSTATUS_READY) {
+			// FR/DA IRQ
+			dvb_ca_en50221_frda_irq(&budget_ci->ca, 0);
+		}
+	} else {
+
+		// trigger on rising edge if a CAM is not present - when a CAM is inserted, we
+		// only want to get the IRQ when it sets READY. If we trigger on the falling edge,
+		// the CAM might not actually be ready yet.
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+
+		// generate a CAM removal IRQ if we haven't already
+		if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) {
+			// CAM removal IRQ
+			budget_ci->slot_status = SLOTSTATUS_NONE;
+			dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
+						     DVB_CA_EN50221_CAMCHANGE_REMOVED);
+		}
+	}
+}
+
+static int ciintf_init(struct budget_ci *budget_ci)
+{
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	int flags;
+	int result;
+
+	memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
+
+	// enable DEBI pins
+	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+
+	// test if it is there
+	if ((ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0) & 0xa0) != 0xa0) {
+		result = -ENODEV;
+		goto error;
+	}
+	// determine whether a CAM is present or not
+	flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
+	budget_ci->slot_status = SLOTSTATUS_NONE;
+	if (flags & CICONTROL_CAMDETECT)
+		budget_ci->slot_status = SLOTSTATUS_PRESENT;
+
+	// register CI interface
+	budget_ci->ca.owner = THIS_MODULE;
+	budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem;
+	budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem;
+	budget_ci->ca.read_cam_control = ciintf_read_cam_control;
+	budget_ci->ca.write_cam_control = ciintf_write_cam_control;
+	budget_ci->ca.slot_reset = ciintf_slot_reset;
+	budget_ci->ca.slot_shutdown = ciintf_slot_shutdown;
+	budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable;
+	budget_ci->ca.data = budget_ci;
+	if ((result = dvb_ca_en50221_init(budget_ci->budget.dvb_adapter,
+					  &budget_ci->ca,
+					  DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE |
+					  DVB_CA_EN50221_FLAG_IRQ_FR |
+					  DVB_CA_EN50221_FLAG_IRQ_DA, 1)) != 0) {
+		printk("budget_ci: CI interface detected, but initialisation failed.\n");
+		goto error;
+	}
+	// Setup CI slot IRQ
+	tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
+	if (budget_ci->slot_status != SLOTSTATUS_NONE) {
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+	} else {
+		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+	}
+	saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       CICONTROL_RESET, 1, 0);
+
+	// success!
+	printk("budget_ci: CI interface initialised\n");
+	budget_ci->budget.ci_present = 1;
+
+	// forge a fake CI IRQ so the CAM state is setup correctly
+	flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+	if (budget_ci->slot_status != SLOTSTATUS_NONE)
+		flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+	dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
+
+	return 0;
+
+error:
+	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	return result;
+}
+
+static void ciintf_deinit(struct budget_ci *budget_ci)
+{
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+
+	// disable CI interrupts
+	saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
+	saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+	tasklet_kill(&budget_ci->ciintf_irq_tasklet);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
+	msleep(1);
+	ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
+			       CICONTROL_RESET, 1, 0);
+
+	// disable TS data stream to CI interface
+	saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+
+	// release the CA device
+	dvb_ca_en50221_release(&budget_ci->ca);
+
+	// disable DEBI pins
+	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+}
+
+static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
+
+	dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
+
+	if (*isr & MASK_06)
+		tasklet_schedule(&budget_ci->msp430_irq_tasklet);
+
+	if (*isr & MASK_10)
+		ttpci_budget_irq10_handler(dev, isr);
+
+	if ((*isr & MASK_03) && (budget_ci->budget.ci_present))
+		tasklet_schedule(&budget_ci->ciintf_irq_tasklet);
+}
+
+
+static u8 alps_bsru6_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x00,
+	0x03, 0x00,
+	0x04, 0x7d,		/* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+	0x05, 0x35,		/* I2CT = 0, SCLT = 1, SDAT = 1 */
+	0x06, 0x40,		/* DAC not used, set to high impendance mode */
+	0x07, 0x00,		/* DAC LSB */
+	0x08, 0x40,		/* DiSEqC off, LNB power on OP2/LOCK pin on */
+	0x09, 0x00,		/* FIFO */
+	0x0c, 0x51,		/* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+	0x0d, 0x82,		/* DC offset compensation = ON, beta_agc1 = 2 */
+	0x0e, 0x23,		/* alpha_tmg = 2, beta_tmg = 3 */
+	0x10, 0x3f,		// AGC2  0x3d
+	0x11, 0x84,
+	0x12, 0xb5,		// Lock detect: -64  Carrier freq detect:on
+	0x15, 0xc9,		// lock detector threshold
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,		// out imp: normal  out type: parallel FEC mode:0
+	0x29, 0x1e,		// 1/2 threshold
+	0x2a, 0x14,		// 2/3 threshold
+	0x2b, 0x0f,		// 3/4 threshold
+	0x2c, 0x09,		// 5/6 threshold
+	0x2d, 0x05,		// 7/8 threshold
+	0x2e, 0x01,
+	0x31, 0x1f,		// test all FECs
+	0x32, 0x19,		// viterbi and synchro search
+	0x33, 0xfc,		// rs control
+	0x34, 0x93,		// error control
+	0x0f, 0x52,
+	0xff, 0xff
+};
+
+static int alps_bsru6_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) {
+		aclk = 0xb7;
+		bclk = 0x47;
+	} else if (srate < 3000000) {
+		aclk = 0xb7;
+		bclk = 0x4b;
+	} else if (srate < 7000000) {
+		aclk = 0xb7;
+		bclk = 0x4f;
+	} else if (srate < 14000000) {
+		aclk = 0xb7;
+		bclk = 0x53;
+	} else if (srate < 30000000) {
+		aclk = 0xb6;
+		bclk = 0x53;
+	} else if (srate < 45000000) {
+		aclk = 0xb4;
+		bclk = 0x51;
+	}
+
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+
+	return 0;
+}
+
+static int alps_bsru6_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+	if ((params->frequency < 950000) || (params->frequency > 2150000))
+		return -EINVAL;
+
+	div = (params->frequency + (125 - 1)) / 125;	// round correctly
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+	buf[3] = 0xC4;
+
+	if (params->frequency > 1530000)
+		buf[3] = 0xc0;
+
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct stv0299_config alps_bsru6_config = {
+
+	.demod_address = 0x68,
+	.inittab = alps_bsru6_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = alps_bsru6_set_symbol_rate,
+	.pll_set = alps_bsru6_pll_set,
+};
+
+
+
+
+static u8 philips_su1278_tt_inittab[] = {
+	0x01, 0x0f,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x5b,
+	0x05, 0x85,
+	0x06, 0x02,
+	0x07, 0x00,
+	0x08, 0x02,
+	0x09, 0x00,
+	0x0C, 0x01,
+	0x0D, 0x81,
+	0x0E, 0x44,
+	0x0f, 0x14,
+	0x10, 0x3c,
+	0x11, 0x84,
+	0x12, 0xda,
+	0x13, 0x97,
+	0x14, 0x95,
+	0x15, 0xc9,
+	0x16, 0x19,
+	0x17, 0x8c,
+	0x18, 0x59,
+	0x19, 0xf8,
+	0x1a, 0xfe,
+	0x1c, 0x7f,
+	0x1d, 0x00,
+	0x1e, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,
+	0x29, 0x28,
+	0x2a, 0x14,
+	0x2b, 0x0f,
+	0x2c, 0x09,
+	0x2d, 0x09,
+	0x31, 0x1f,
+	0x32, 0x19,
+	0x33, 0xfc,
+	0x34, 0x93,
+	0xff, 0xff
+};
+
+static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+	stv0299_writereg(fe, 0x0e, 0x44);
+	if (srate >= 10000000) {
+		stv0299_writereg(fe, 0x13, 0x97);
+		stv0299_writereg(fe, 0x14, 0x95);
+		stv0299_writereg(fe, 0x15, 0xc9);
+		stv0299_writereg(fe, 0x17, 0x8c);
+		stv0299_writereg(fe, 0x1a, 0xfe);
+		stv0299_writereg(fe, 0x1c, 0x7f);
+		stv0299_writereg(fe, 0x2d, 0x09);
+	} else {
+		stv0299_writereg(fe, 0x13, 0x99);
+		stv0299_writereg(fe, 0x14, 0x8d);
+		stv0299_writereg(fe, 0x15, 0xce);
+		stv0299_writereg(fe, 0x17, 0x43);
+		stv0299_writereg(fe, 0x1a, 0x1d);
+		stv0299_writereg(fe, 0x1c, 0x12);
+		stv0299_writereg(fe, 0x2d, 0x05);
+	}
+	stv0299_writereg(fe, 0x0e, 0x23);
+	stv0299_writereg(fe, 0x0f, 0x94);
+	stv0299_writereg(fe, 0x10, 0x39);
+	stv0299_writereg(fe, 0x15, 0xc9);
+
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+
+	return 0;
+}
+
+static int philips_su1278_tt_pll_set(struct dvb_frontend *fe,
+				     struct dvb_frontend_parameters *params)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	u32 div;
+	u8 buf[4];
+	struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+	if ((params->frequency < 950000) || (params->frequency > 2150000))
+		return -EINVAL;
+
+	div = (params->frequency + (500 - 1)) / 500;	// round correctly
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2;
+	buf[3] = 0x20;
+
+	if (params->u.qpsk.symbol_rate < 4000000)
+		buf[3] |= 1;
+
+	if (params->frequency < 1250000)
+		buf[3] |= 0;
+	else if (params->frequency < 1550000)
+		buf[3] |= 0x40;
+	else if (params->frequency < 2050000)
+		buf[3] |= 0x80;
+	else if (params->frequency < 2150000)
+		buf[3] |= 0xC0;
+
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1)
+		return -EIO;
+	return 0;
+}
+
+static struct stv0299_config philips_su1278_tt_config = {
+
+	.demod_address = 0x68,
+	.inittab = philips_su1278_tt_inittab,
+	.mclk = 64000000UL,
+	.invert = 0,
+	.enhanced_tuning = 1,
+	.skip_reinit = 1,
+	.lock_output = STV0229_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 50,
+	.set_symbol_rate = philips_su1278_tt_set_symbol_rate,
+	.pll_set = philips_su1278_tt_pll_set,
+};
+
+
+
+static int philips_tdm1316l_pll_init(struct dvb_frontend *fe)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+	static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
+	struct i2c_msg tuner_msg = {.addr = 0x63,.flags = 0,.buf = td1316_init,.len =
+			sizeof(td1316_init) };
+
+	// setup PLL configuration
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+	msleep(1);
+
+	// disable the mc44BC374c (do not check for errors)
+	tuner_msg.addr = 0x65;
+	tuner_msg.buf = disable_mc44BC374c;
+	tuner_msg.len = sizeof(disable_mc44BC374c);
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) {
+		i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1);
+	}
+
+	return 0;
+}
+
+static int philips_tdm1316l_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr = 0x63,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = params->frequency + 36130000;
+	if (tuner_frequency < 87000000)
+		return -EINVAL;
+	else if (tuner_frequency < 130000000)
+		cp = 3;
+	else if (tuner_frequency < 160000000)
+		cp = 5;
+	else if (tuner_frequency < 200000000)
+		cp = 6;
+	else if (tuner_frequency < 290000000)
+		cp = 3;
+	else if (tuner_frequency < 420000000)
+		cp = 5;
+	else if (tuner_frequency < 480000000)
+		cp = 6;
+	else if (tuner_frequency < 620000000)
+		cp = 3;
+	else if (tuner_frequency < 830000000)
+		cp = 5;
+	else if (tuner_frequency < 895000000)
+		cp = 7;
+	else
+		return -EINVAL;
+
+	// determine band
+	if (params->frequency < 49000000)
+		return -EINVAL;
+	else if (params->frequency < 159000000)
+		band = 1;
+	else if (params->frequency < 444000000)
+		band = 2;
+	else if (params->frequency < 861000000)
+		band = 4;
+	else
+		return -EINVAL;
+
+	// setup PLL filter and TDA9889
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		tda1004x_write_byte(fe, 0x0C, 0x14);
+		filter = 0;
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		tda1004x_write_byte(fe, 0x0C, 0x80);
+		filter = 0;
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		tda1004x_write_byte(fe, 0x0C, 0x14);
+		filter = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// calculate divisor
+	// ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
+	tuner_frequency = (((params->frequency / 1000) * 6) + 217280) / 1000;
+
+	// setup tuner buffer
+	tuner_buf[0] = tuner_frequency >> 8;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xca;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+	if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(1);
+	return 0;
+}
+
+static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe,
+					     const struct firmware **fw, char *name)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
+
+	return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev);
+}
+
+static struct tda1004x_config philips_tdm1316l_config = {
+
+	.demod_address = 0x8,
+	.invert = 0,
+	.invert_oclk = 0,
+	.pll_init = philips_tdm1316l_pll_init,
+	.pll_set = philips_tdm1316l_pll_set,
+	.request_firmware = philips_tdm1316l_request_firmware,
+};
+
+
+
+static void frontend_init(struct budget_ci *budget_ci)
+{
+	switch (budget_ci->budget.dev->pci->subsystem_device) {
+	case 0x100c:		// Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059))
+		budget_ci->budget.dvb_frontend =
+			stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			break;
+		}
+		break;
+
+	case 0x100f:		// Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059))
+		budget_ci->budget.dvb_frontend =
+			stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			break;
+		}
+		break;
+
+	case 0x1011:		// Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889)
+		budget_ci->budget.dvb_frontend =
+			tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+		if (budget_ci->budget.dvb_frontend) {
+			break;
+		}
+		break;
+	}
+
+	if (budget_ci->budget.dvb_frontend == NULL) {
+		printk("budget-ci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		       budget_ci->budget.dev->pci->vendor,
+		       budget_ci->budget.dev->pci->device,
+		       budget_ci->budget.dev->pci->subsystem_vendor,
+		       budget_ci->budget.dev->pci->subsystem_device);
+	} else {
+		if (dvb_register_frontend
+		    (budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) {
+			printk("budget-ci: Frontend registration failed!\n");
+			if (budget_ci->budget.dvb_frontend->ops->release)
+				budget_ci->budget.dvb_frontend->ops->release(budget_ci->budget.dvb_frontend);
+			budget_ci->budget.dvb_frontend = NULL;
+		}
+	}
+}
+
+static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+	struct budget_ci *budget_ci;
+	int err;
+
+	if (!(budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL)))
+		return -ENOMEM;
+
+	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;
+	}
+
+	tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt,
+		     (unsigned long) budget_ci);
+
+	msp430_ir_init(budget_ci);
+
+	ciintf_init(budget_ci);
+
+	budget_ci->budget.dvb_adapter->priv = budget_ci;
+	frontend_init(budget_ci);
+
+	return 0;
+}
+
+static int budget_ci_detach(struct saa7146_dev *dev)
+{
+	struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
+	struct saa7146_dev *saa = budget_ci->budget.dev;
+	int err;
+
+	if (budget_ci->budget.ci_present)
+		ciintf_deinit(budget_ci);
+	if (budget_ci->budget.dvb_frontend)
+		dvb_unregister_frontend(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);
+
+	kfree(budget_ci);
+
+	return err;
+}
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T	 PCI", BUDGET_TT);
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
+	MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f),
+	MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011),
+	{
+	 .vendor = 0,
+	 }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+	.name = "budget_ci dvb\0",
+	.flags = 0,
+
+	.module = THIS_MODULE,
+	.pci_tbl = &pci_tbl[0],
+	.attach = budget_ci_attach,
+	.detach = budget_ci_detach,
+
+	.irq_mask = MASK_03 | MASK_06 | MASK_10,
+	.irq_func = budget_ci_irq,
+};
+
+static int __init budget_ci_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_ci_exit(void)
+{
+	saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_ci_init);
+module_exit(budget_ci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+		   "budget PCI DVB cards w/ CI-module produced by "
+		   "Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c
new file mode 100644
index 0000000..93a9b40
--- /dev/null
+++ b/drivers/media/dvb/ttpci/budget-core.c
@@ -0,0 +1,480 @@
+/*
+ * budget-core.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *			 & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ *	     Michael Dreher <michael@5dot1.de>,
+ *	     Oliver Endriss <o.endriss@gmx.de>,
+ *	     Andreas 'randy' Weinberger
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include <linux/moduleparam.h>
+
+#include "budget.h"
+#include "ttpci-eeprom.h"
+
+int budget_debug;
+module_param_named(debug, budget_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
+
+/****************************************************************************
+ * TT budget / WinTV Nova
+ ****************************************************************************/
+
+static int stop_ts_capture(struct budget *budget)
+{
+	dprintk(2, "budget: %p\n", budget);
+
+	if (--budget->feeding)
+		return budget->feeding;
+
+	saa7146_write(budget->dev, MC1, MASK_20);	// DMA3 off
+	SAA7146_IER_DISABLE(budget->dev, MASK_10);
+	return 0;
+}
+
+static int start_ts_capture(struct budget *budget)
+{
+	struct saa7146_dev *dev = budget->dev;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	if (budget->feeding)
+		return ++budget->feeding;
+
+	saa7146_write(dev, MC1, MASK_20);	// DMA3 off
+
+	memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
+
+	saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+
+	budget->tsf = 0xff;
+	budget->ttbp = 0;
+
+	/*
+	 *  Signal path on the Activy:
+	 *
+	 *  tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory
+	 *
+	 *  Since the tuner feeds 204 bytes packets into the SAA7146,
+	 *  DMA3 is configured to strip the trailing 16 FEC bytes:
+	 *      Pitch: 188, NumBytes3: 188, NumLines3: 1024
+	 */
+
+        switch(budget->card->type) {
+	case BUDGET_FS_ACTIVY:
+		saa7146_write(dev, DD1_INIT, 0x04000000);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+		saa7146_write(dev, BRS_CTRL, 0x00000000);
+		break;
+	case BUDGET_PATCH:
+		saa7146_write(dev, DD1_INIT, 0x00000200);
+		saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+		saa7146_write(dev, BRS_CTRL, 0x60000000);
+		break;
+	default:
+		if (budget->video_port == BUDGET_VIDEO_PORTA) {
+			saa7146_write(dev, DD1_INIT, 0x06000200);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x00000000);
+		} else {
+			saa7146_write(dev, DD1_INIT, 0x02000600);
+			saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+			saa7146_write(dev, BRS_CTRL, 0x60000000);
+		}
+	}
+
+	saa7146_write(dev, MC2, (MASK_08 | MASK_24));
+	mdelay(10);
+
+	saa7146_write(dev, BASE_ODD3, 0);
+	saa7146_write(dev, BASE_EVEN3, 0);
+	saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+	saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90);
+
+	if (budget->card->type == BUDGET_FS_ACTIVY) {
+		saa7146_write(dev, PITCH3, TS_WIDTH / 2);
+		saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT * 2) << 16) | (TS_WIDTH / 2));
+	} else {
+		saa7146_write(dev, PITCH3, TS_WIDTH);
+		saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
+	}
+
+	saa7146_write(dev, MC2, (MASK_04 | MASK_20));
+
+	SAA7146_ISR_CLEAR(budget->dev, MASK_10);	/* VPE */
+	SAA7146_IER_ENABLE(budget->dev, MASK_10);	/* VPE */
+	saa7146_write(dev, MC1, (MASK_04 | MASK_20));	/* DMA3 on */
+
+	return ++budget->feeding;
+}
+
+static void vpeirq(unsigned long data)
+{
+	struct budget *budget = (struct budget *) data;
+	u8 *mem = (u8 *) (budget->grabbing);
+	u32 olddma = budget->ttbp;
+	u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+
+	/* nearest lower position divisible by 188 */
+	newdma -= newdma % 188;
+
+	if (newdma >= TS_BUFLEN)
+		return;
+
+	budget->ttbp = newdma;
+
+	if (budget->feeding == 0 || newdma == olddma)
+		return;
+
+	if (newdma > olddma) {	/* no wraparound, dump olddma..newdma */
+		dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (newdma - olddma) / 188);
+	} else {		/* wraparound, dump olddma..buflen and 0..newdma */
+		dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (TS_BUFLEN - olddma) / 188);
+		dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188);
+	}
+}
+
+
+int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
+			  int uselocks, int nobusyloop)
+{
+	struct saa7146_dev *saa = budget->dev;
+	int result = 0;
+	unsigned long flags = 0;
+
+	if (count > 4 || count <= 0)
+		return 0;
+
+	if (uselocks)
+		spin_lock_irqsave(&budget->debilock, flags);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff));
+	saa7146_write(saa, DEBI_CONFIG, config);
+	saa7146_write(saa, DEBI_PAGE, 0);
+	saa7146_write(saa, MC2, (2 << 16) | 2);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	result = saa7146_read(saa, DEBI_AD);
+	result &= (0xffffffffUL >> ((4 - count) * 8));
+
+	if (uselocks)
+		spin_unlock_irqrestore(&budget->debilock, flags);
+
+	return result;
+}
+
+int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr,
+			   int count, u32 value, int uselocks, int nobusyloop)
+{
+	struct saa7146_dev *saa = budget->dev;
+	unsigned long flags = 0;
+	int result;
+
+	if (count > 4 || count <= 0)
+		return 0;
+
+	if (uselocks)
+		spin_lock_irqsave(&budget->debilock, flags);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff));
+	saa7146_write(saa, DEBI_CONFIG, config);
+	saa7146_write(saa, DEBI_PAGE, 0);
+	saa7146_write(saa, DEBI_AD, value);
+	saa7146_write(saa, MC2, (2 << 16) | 2);
+
+	if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) {
+		if (uselocks)
+			spin_unlock_irqrestore(&budget->debilock, flags);
+		return result;
+	}
+
+	if (uselocks)
+		spin_unlock_irqrestore(&budget->debilock, flags);
+	return 0;
+}
+
+
+/****************************************************************************
+ * DVB API SECTION
+ ****************************************************************************/
+
+static int budget_start_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct budget *budget = (struct budget *) demux->priv;
+	int status;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	if (!demux->dmx.frontend)
+		return -EINVAL;
+
+	spin_lock(&budget->feedlock);
+	feed->pusi_seen = 0; /* have a clean section start */
+	status = start_ts_capture(budget);
+	spin_unlock(&budget->feedlock);
+	return status;
+}
+
+static int budget_stop_feed(struct dvb_demux_feed *feed)
+{
+	struct dvb_demux *demux = feed->demux;
+	struct budget *budget = (struct budget *) demux->priv;
+	int status;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	spin_lock(&budget->feedlock);
+	status = stop_ts_capture(budget);
+	spin_unlock(&budget->feedlock);
+	return status;
+}
+
+static int budget_register(struct budget *budget)
+{
+	struct dvb_demux *dvbdemux = &budget->demux;
+	int ret;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	dvbdemux->priv = (void *) budget;
+
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = budget_start_feed;
+	dvbdemux->stop_feed = budget_stop_feed;
+	dvbdemux->write_to_decoder = NULL;
+
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+				      DMX_MEMORY_BASED_FILTERING);
+
+	dvb_dmx_init(&budget->demux);
+
+	budget->dmxdev.filternum = 256;
+	budget->dmxdev.demux = &dvbdemux->dmx;
+	budget->dmxdev.capabilities = 0;
+
+	dvb_dmxdev_init(&budget->dmxdev, budget->dvb_adapter);
+
+	budget->hw_frontend.source = DMX_FRONTEND_0;
+
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+
+	if (ret < 0)
+		return ret;
+
+	budget->mem_frontend.source = DMX_MEMORY_FE;
+	ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+	if (ret < 0)
+		return ret;
+
+	ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+	if (ret < 0)
+		return ret;
+
+	dvb_net_init(budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx);
+
+	return 0;
+}
+
+static void budget_unregister(struct budget *budget)
+{
+	struct dvb_demux *dvbdemux = &budget->demux;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	dvb_net_release(&budget->dvb_net);
+
+	dvbdemux->dmx.close(&dvbdemux->dmx);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+	dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+
+	dvb_dmxdev_release(&budget->dmxdev);
+	dvb_dmx_release(&budget->demux);
+}
+
+int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
+		      struct saa7146_pci_extension_data *info,
+		      struct module *owner)
+{
+	int length = TS_WIDTH * TS_HEIGHT;
+	int ret = 0;
+	struct budget_info *bi = info->ext_priv;
+
+	memset(budget, 0, sizeof(struct budget));
+
+	dprintk(2, "dev: %p, budget: %p\n", dev, budget);
+
+	budget->card = bi;
+	budget->dev = (struct saa7146_dev *) dev;
+
+	dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner);
+
+	/* set dd1 stream a & b */
+	saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+	saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+	saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+	saa7146_write(dev, DD1_INIT, 0x02000000);
+	saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+	if (bi->type != BUDGET_FS_ACTIVY)
+		budget->video_port = BUDGET_VIDEO_PORTB;
+	else
+		budget->video_port = BUDGET_VIDEO_PORTA;
+	spin_lock_init(&budget->feedlock);
+	spin_lock_init(&budget->debilock);
+
+	/* the Siemens DVB needs this if you want to have the i2c chips
+	   get recognized before the main driver is loaded */
+	if (bi->type != BUDGET_FS_ACTIVY)
+		saa7146_write(dev, GPIO_CTRL, 0x500000);	/* GPIO 3 = 1 */
+
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+	budget->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL;
+#else
+	budget->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
+#endif
+
+	strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name));
+
+	saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120);
+	strcpy(budget->i2c_adap.name, budget->card->name);
+
+	if (i2c_add_adapter(&budget->i2c_adap) < 0) {
+		dvb_unregister_adapter(budget->dvb_adapter);
+		return -ENOMEM;
+	}
+
+	ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter->proposed_mac);
+
+	if (NULL ==
+	    (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, length, &budget->pt))) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	saa7146_write(dev, PCI_BT_V1, 0x001c0000);
+	/* upload all */
+	saa7146_write(dev, GPIO_CTRL, 0x000000);
+
+	tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget);
+
+	/* frontend power on */
+	if (bi->type == BUDGET_FS_ACTIVY)
+		saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+	else
+		saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+
+	if (budget_register(budget) == 0) {
+		return 0;
+	}
+err:
+	i2c_del_adapter(&budget->i2c_adap);
+
+	vfree(budget->grabbing);
+
+	dvb_unregister_adapter(budget->dvb_adapter);
+
+	return ret;
+}
+
+int ttpci_budget_deinit(struct budget *budget)
+{
+	struct saa7146_dev *dev = budget->dev;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	budget_unregister(budget);
+
+	i2c_del_adapter(&budget->i2c_adap);
+
+	dvb_unregister_adapter(budget->dvb_adapter);
+
+	tasklet_kill(&budget->vpe_tasklet);
+
+	saa7146_pgtable_free(dev->pci, &budget->pt);
+
+	vfree(budget->grabbing);
+
+	return 0;
+}
+
+void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr)
+{
+	struct budget *budget = (struct budget *) dev->ext_priv;
+
+	dprintk(8, "dev: %p, budget: %p\n", dev, budget);
+
+	if (*isr & MASK_10)
+		tasklet_schedule(&budget->vpe_tasklet);
+}
+
+void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port)
+{
+	struct budget *budget = (struct budget *) dev->ext_priv;
+
+	spin_lock(&budget->feedlock);
+	budget->video_port = video_port;
+	if (budget->feeding) {
+		int oldfeeding = budget->feeding;
+		budget->feeding = 1;
+		stop_ts_capture(budget);
+		start_ts_capture(budget);
+		budget->feeding = oldfeeding;
+	}
+	spin_unlock(&budget->feedlock);
+}
+
+EXPORT_SYMBOL_GPL(ttpci_budget_debiread);
+EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite);
+EXPORT_SYMBOL_GPL(ttpci_budget_init);
+EXPORT_SYMBOL_GPL(ttpci_budget_deinit);
+EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler);
+EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port);
+EXPORT_SYMBOL_GPL(budget_debug);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c
new file mode 100644
index 0000000..5d524a4
--- /dev/null
+++ b/drivers/media/dvb/ttpci/budget-patch.c
@@ -0,0 +1,754 @@
+/*
+ * budget-patch.c: driver for Budget Patch,
+ * hardware modification of DVB-S cards enabling full TS
+ *
+ * Written by Emard <emard@softhome.net>
+ *
+ * Original idea by Roberto Deza <rdeza@unav.es>
+ *
+ * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic
+ * and Metzlerbros
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "budget.h"
+#include "stv0299.h"
+#include "ves1x93.h"
+#include "tda8083.h"
+
+#define budget_patch budget
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH);
+//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC);
+
+static struct pci_device_id pci_tbl[] = {
+        MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000),
+//        MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+        {
+                .vendor    = 0,
+        }
+};
+
+/* those lines are for budget-patch to be tried
+** on a true budget card and observe the
+** behaviour of VSYNC generated by rps1.
+** this code was shamelessly copy/pasted from budget.c
+*/
+static void gpio_Set22K (struct budget *budget, int state)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+	saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
+}
+
+/* Diseqc functions only for TT Budget card */
+/* taken from the Skyvision DVB driver by
+   Ralph Metzler <rjkm@metzlerbros.de> */
+
+static void DiseqcSendBit (struct budget *budget, int data)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+	udelay(data ? 500 : 1000);
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	udelay(data ? 1000 : 500);
+}
+
+static void DiseqcSendByte (struct budget *budget, int data)
+{
+	int i, par=1, d;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	for (i=7; i>=0; i--) {
+		d = (data>>i)&1;
+		par ^= d;
+		DiseqcSendBit(budget, d);
+	}
+
+	DiseqcSendBit(budget, par);
+}
+
+static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
+{
+	struct saa7146_dev *dev=budget->dev;
+	int i;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	mdelay(16);
+
+	for (i=0; i<len; i++)
+		DiseqcSendByte(budget, msg[i]);
+
+	mdelay(16);
+
+	if (burst!=-1) {
+		if (burst)
+			DiseqcSendByte(budget, 0xff);
+		else {
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+			udelay(12500);
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		}
+		msleep(20);
+	}
+
+	return 0;
+}
+
+/* shamelessly copy/pasted from budget.c
+*/
+static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		gpio_Set22K (budget, 1);
+		break;
+
+	case SEC_TONE_OFF:
+		gpio_Set22K (budget, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+
+	return 0;
+}
+
+static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, 0, NULL, minicmd);
+
+	return 0;
+}
+
+static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length)
+{
+        int i;
+
+        dprintk(2, "budget: %p\n", budget);
+
+        for (i = 2; i < length; i++)
+        {
+                  ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0);
+                  msleep(5);
+        }
+        if (length)
+                  ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0);
+        else
+                  ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0);
+        msleep(5);
+        ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0);
+        msleep(5);
+        return 0;
+}
+
+static void av7110_set22k(struct budget_patch *budget, int state)
+{
+        u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0};
+
+        dprintk(2, "budget: %p\n", budget);
+        budget_av7110_send_fw_cmd(budget, buf, 2);
+}
+
+static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst)
+{
+        int i;
+        u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC),
+                16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+        dprintk(2, "budget: %p\n", budget);
+
+        if (len>10)
+                len=10;
+
+        buf[1] = len+2;
+        buf[2] = len;
+
+        if (burst != -1)
+                buf[3]=burst ? 0x01 : 0x00;
+        else
+                buf[3]=0xffff;
+
+        for (i=0; i<len; i++)
+                buf[i+4]=msg[i];
+
+        budget_av7110_send_fw_cmd(budget, buf, 18);
+        return 0;
+}
+
+static int budget_patch_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		av7110_set22k (budget, 1);
+		break;
+
+	case SEC_TONE_OFF:
+		av7110_set22k (budget, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+
+	av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0);
+
+	return 0;
+}
+
+static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+
+	av7110_send_diseqc_msg (budget, 0, NULL, minicmd);
+
+	return 0;
+}
+
+static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+	u8 pwr = 0;
+	u8 buf[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	u32 div = (params->frequency + 479500) / 125;
+
+	if (params->frequency > 2000000) pwr = 3;
+	else if (params->frequency > 1800000) pwr = 2;
+	else if (params->frequency > 1600000) pwr = 1;
+	else if (params->frequency > 1200000) pwr = 0;
+	else if (params->frequency >= 1100000) pwr = 1;
+	else pwr = 2;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = ((div & 0x18000) >> 10) | 0x95;
+	buf[3] = (pwr << 6) | 0x30;
+
+        // NOTE: since we're using a prescaler of 2, we set the
+	// divisor frequency to 62.5kHz and divide by 125 above
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config = {
+	.demod_address = 0x08,
+	.xin = 90100000UL,
+	.invert_pwm = 0,
+	.pll_set = alps_bsrv2_pll_set,
+};
+
+static u8 alps_bsru6_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x00,
+	0x03, 0x00,
+        0x04, 0x7d,   /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+	0x05, 0x35,   /* I2CT = 0, SCLT = 1, SDAT = 1 */
+	0x06, 0x40,   /* DAC not used, set to high impendance mode */
+	0x07, 0x00,   /* DAC LSB */
+	0x08, 0x40,   /* DiSEqC off, LNB power on OP2/LOCK pin on */
+	0x09, 0x00,   /* FIFO */
+	0x0c, 0x51,   /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+	0x0d, 0x82,   /* DC offset compensation = ON, beta_agc1 = 2 */
+	0x0e, 0x23,   /* alpha_tmg = 2, beta_tmg = 3 */
+	0x10, 0x3f,   // AGC2  0x3d
+	0x11, 0x84,
+	0x12, 0xb5,   // Lock detect: -64  Carrier freq detect:on
+	0x15, 0xc9,   // lock detector threshold
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,  // out imp: normal  out type: parallel FEC mode:0
+	0x29, 0x1e,  // 1/2 threshold
+	0x2a, 0x14,  // 2/3 threshold
+	0x2b, 0x0f,  // 3/4 threshold
+	0x2c, 0x09,  // 5/6 threshold
+	0x2d, 0x05,  // 7/8 threshold
+	0x2e, 0x01,
+	0x31, 0x1f,  // test all FECs
+	0x32, 0x19,  // viterbi and synchro search
+	0x33, 0xfc,  // rs control
+	0x34, 0x93,  // error control
+	0x0f, 0x52,
+	0xff, 0xff
+};
+
+static int alps_bsru6_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+	else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+	else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+	else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+	else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+	else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
+
+	stv0299_writereg (fe, 0x13, aclk);
+	stv0299_writereg (fe, 0x14, bclk);
+	stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg (fe, 0x20, (ratio >>  8) & 0xff);
+	stv0299_writereg (fe, 0x21, (ratio      ) & 0xf0);
+
+	return 0;
+}
+
+static int alps_bsru6_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+	u8 data[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL;
+
+	div = (params->frequency + (125 - 1)) / 125; // round correctly
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+	data[3] = 0xC4;
+
+	if (params->frequency > 1530000) data[3] = 0xc0;
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct stv0299_config alps_bsru6_config = {
+
+	.demod_address = 0x68,
+	.inittab = alps_bsru6_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = alps_bsru6_set_symbol_rate,
+	.pll_set = alps_bsru6_pll_set,
+};
+
+static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = params->frequency / 125;
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = 0x00;
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+	.demod_address = 0x68,
+	.pll_set = grundig_29504_451_pll_set,
+};
+
+static void frontend_init(struct budget_patch* budget)
+{
+	switch(budget->dev->pci->subsystem_device) {
+	case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X
+        case 0x1013: // SATELCO Multimedia PCI
+
+		// try the ALPS BSRV2 first of all
+		budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops->diseqc_send_burst = budget_patch_diseqc_send_burst;
+			budget->dvb_frontend->ops->set_tone = budget_patch_set_tone;
+			break;
+		}
+
+		// try the ALPS BSRU6 now
+		budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst;
+			budget->dvb_frontend->ops->set_tone = budget_set_tone;
+			break;
+		}
+
+		// Try the grundig 29504-451
+		budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst;
+			budget->dvb_frontend->ops->set_tone = budget_set_tone;
+			break;
+		}
+		break;
+	}
+
+	if (budget->dvb_frontend == NULL) {
+		printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		       budget->dev->pci->vendor,
+		       budget->dev->pci->device,
+		       budget->dev->pci->subsystem_vendor,
+		       budget->dev->pci->subsystem_device);
+	} else {
+		if (dvb_register_frontend(budget->dvb_adapter, budget->dvb_frontend)) {
+			printk("budget-av: Frontend registration failed!\n");
+			if (budget->dvb_frontend->ops->release)
+				budget->dvb_frontend->ops->release(budget->dvb_frontend);
+			budget->dvb_frontend = NULL;
+		}
+	}
+}
+
+/* written by Emard */
+static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+        struct budget_patch *budget;
+        int err;
+	int count = 0;
+	int detected = 0;
+
+#define PATCH_RESET 0
+#define RPS_IRQ 0
+#define HPS_SETUP 0
+#if PATCH_RESET
+        saa7146_write(dev, MC1, MASK_31);
+        msleep(40);
+#endif
+#if HPS_SETUP
+        // initialize registers. Better to have it like this
+        // than leaving something unconfigured
+	saa7146_write(dev, DD1_STREAM_B, 0);
+	// port B VSYNC at rising edge
+	saa7146_write(dev, DD1_INIT, 0x00000200);  // have this in budget-core too!
+	saa7146_write(dev, BRS_CTRL, 0x00000000);  // VBI
+
+	// debi config
+	// saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18);
+
+        // zero all HPS registers
+        saa7146_write(dev, HPS_H_PRESCALE, 0);                  // r68
+        saa7146_write(dev, HPS_H_SCALE, 0);                     // r6c
+        saa7146_write(dev, BCS_CTRL, 0);                        // r70
+        saa7146_write(dev, HPS_V_SCALE, 0);                     // r60
+        saa7146_write(dev, HPS_V_GAIN, 0);                      // r64
+        saa7146_write(dev, CHROMA_KEY_RANGE, 0);                // r74
+        saa7146_write(dev, CLIP_FORMAT_CTRL, 0);                // r78
+        // Set HPS prescaler for port B input
+        saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) );
+        saa7146_write(dev, MC2,
+          0 * (MASK_08 | MASK_24)  |   // BRS control
+          0 * (MASK_09 | MASK_25)  |   // a
+          0 * (MASK_10 | MASK_26)  |   // b
+          1 * (MASK_06 | MASK_22)  |   // HPS_CTRL1
+          1 * (MASK_05 | MASK_21)  |   // HPS_CTRL2
+          0 * (MASK_01 | MASK_15)      // DEBI
+           );
+#endif
+	// Disable RPS1 and RPS0
+        saa7146_write(dev, MC1, ( MASK_29 | MASK_28));
+        // RPS1 timeout disable
+        saa7146_write(dev, RPS_TOV1, 0);
+
+	// code for autodetection
+	// will wait for VBI_B event (vertical blank at port B)
+	// and will reset GPIO3 after VBI_B is detected.
+	// (GPIO3 should be raised high by CPU to
+	// test if GPIO3 will generate vertical blank signal
+	// in budget patch GPIO3 is connected to VSYNC_B
+	count = 0;
+#if 0
+	WRITE_RPS1(cpu_to_le32(CMD_UPLOAD |
+	  MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ));
+#endif
+        WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_VBI_B));
+        WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
+        WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
+        WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+#if RPS_IRQ
+        // issue RPS1 interrupt to increment counter
+        WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+        // at least a NOP is neede between two interrupts
+        WRITE_RPS1(cpu_to_le32(CMD_NOP));
+        // interrupt again
+        WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+#endif
+        WRITE_RPS1(cpu_to_le32(CMD_STOP));
+
+#if RPS_IRQ
+        // set event counter 1 source as RPS1 interrupt (0x03)          (rE4 p53)
+        // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
+        // use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
+        saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
+        // set event counter 1 treshold to maximum allowed value        (rEC p55)
+        saa7146_write(dev, ECT1R,  0x3fff );
+#endif
+        // Fix VSYNC level
+        saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+        // Set RPS1 Address register to point to RPS code               (r108 p42)
+        saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+        // Enable RPS1,                                                 (rFC p33)
+        saa7146_write(dev, MC1, (MASK_13 | MASK_29 ));
+
+
+        mdelay(50);
+        saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+	mdelay(150);
+
+
+	if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0)
+		detected = 1;
+
+#if RPS_IRQ
+        printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff );
+#endif
+	// Disable RPS1
+        saa7146_write(dev, MC1, ( MASK_29 ));
+
+	if(detected == 0)
+                printk("budget-patch not detected or saa7146 in non-default state.\n"
+                       "try enabling ressetting of 7146 with MASK_31 in MC1 register\n");
+
+	else
+                printk("BUDGET-PATCH DETECTED.\n");
+
+
+/*      OLD (Original design by Roberto Deza):
+**      This code will setup the SAA7146_RPS1 to generate a square
+**      wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of
+**      TS_WIDTH packets) has been acquired on SAA7146_D1B video port;
+**      then, this GPIO3 output which is connected to the D1B_VSYNC
+**      input, will trigger the acquisition of the alternate field
+**      and so on.
+**      Currently, the TT_budget / WinTV_Nova cards have two ICs
+**      (74HCT4040, LVC74) for the generation of this VSYNC signal,
+**      which seems that can be done perfectly without this :-)).
+*/
+
+/*      New design (By Emard)
+**      this rps1 code will copy internal HS event to GPIO3 pin.
+**      GPIO3 is in budget-patch hardware connectd to port B VSYNC
+
+**      HS is an internal event of 7146, accessible with RPS
+**      and temporarily raised high every n lines
+**      (n in defined in the RPS_THRESH1 counter threshold)
+**      I think HS is raised high on the beginning of the n-th line
+**      and remains high until this n-th line that triggered
+**      it is completely received. When the receiption of n-th line
+**      ends, HS is lowered.
+
+**      To transmit data over DMA, 7146 needs changing state at
+**      port B VSYNC pin. Any changing of port B VSYNC will
+**      cause some DMA data transfer, with more or less packets loss.
+**      It depends on the phase and frequency of VSYNC and
+**      the way of 7146 is instructed to trigger on port B (defined
+**      in DD1_INIT register, 3rd nibble from the right valid
+**      numbers are 0-7, see datasheet)
+**
+**      The correct triggering can minimize packet loss,
+**      dvbtraffic should give this stable bandwidths:
+**        22k transponder = 33814 kbit/s
+**      27.5k transponder = 38045 kbit/s
+**      by experiment it is found that the best results
+**      (stable bandwidths and almost no packet loss)
+**      are obtained using DD1_INIT triggering number 2
+**      (Va at rising edge of VS Fa = HS x VS-failing forced toggle)
+**      and a VSYNC phase that occurs in the middle of DMA transfer
+**      (about byte 188*512=96256 in the DMA window).
+**
+**      Phase of HS is still not clear to me how to control,
+**      It just happens to be so. It can be seen if one enables
+**      RPS_IRQ and print Event Counter 1 in vpeirq(). Every
+**      time RPS_INTERRUPT is called, the Event Counter 1 will
+**      increment. That's how the 7146 is programmed to do event
+**      counting in this budget-patch.c
+**      I *think* HPS setting has something to do with the phase
+**      of HS but I cant be 100% sure in that.
+
+**      hardware debug note: a working budget card (including budget patch)
+**      with vpeirq() interrupt setup in mode "0x90" (every 64K) will
+**      generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes
+**      and that means 3*25=75 Hz of interrupt freqency, as seen by
+**      watch cat /proc/interrupts
+**
+**      If this frequency is 3x lower (and data received in the DMA
+**      buffer don't start with 0x47, but in the middle of packets,
+**      whose lengths appear to be like 188 292 188 104 etc.
+**      this means VSYNC line is not connected in the hardware.
+**      (check soldering pcb and pins)
+**      The same behaviour of missing VSYNC can be duplicated on budget
+**      cards, by seting DD1_INIT trigger mode 7 in 3rd nibble.
+*/
+
+	// Setup RPS1 "program" (p35)
+        count = 0;
+
+
+        // Wait Source Line Counter Threshold                           (p36)
+        WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_HS));
+        // Set GPIO3=1                                                  (p42)
+        WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
+        WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
+        WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTHI<<24));
+#if RPS_IRQ
+        // issue RPS1 interrupt
+        WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+#endif
+        // Wait reset Source Line Counter Threshold                     (p36)
+        WRITE_RPS1(cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS));
+        // Set GPIO3=0                                                  (p42)
+        WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2)));
+        WRITE_RPS1(cpu_to_le32(GPIO3_MSK));
+        WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24));
+#if RPS_IRQ
+        // issue RPS1 interrupt
+        WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT));
+#endif
+        // Jump to begin of RPS program                                 (p37)
+        WRITE_RPS1(cpu_to_le32(CMD_JUMP));
+        WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle));
+
+        // Fix VSYNC level
+        saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+        // Set RPS1 Address register to point to RPS code               (r108 p42)
+        saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
+        // Set Source Line Counter Threshold, using BRS                 (rCC p43)
+        // It generates HS event every TS_HEIGHT lines
+        // this is related to TS_WIDTH set in register
+        // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE
+        // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188
+        //,then RPS_THRESH1
+        // should be set to trigger every TS_HEIGHT (512) lines.
+        //
+        saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 );
+
+        // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 );
+        // Enable RPS1                                                  (rFC p33)
+        saa7146_write(dev, MC1, (MASK_13 | MASK_29));
+
+
+        if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL)))
+                return -ENOMEM;
+
+        dprintk(2, "budget: %p\n", budget);
+
+        if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
+                kfree (budget);
+                return err;
+        }
+
+
+        dev->ext_priv = budget;
+
+	budget->dvb_adapter->priv = budget;
+	frontend_init(budget);
+
+        return 0;
+}
+
+static int budget_patch_detach (struct saa7146_dev* dev)
+{
+        struct budget_patch *budget = (struct budget_patch*) dev->ext_priv;
+        int err;
+
+	if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend);
+
+        err = ttpci_budget_deinit (budget);
+
+        kfree (budget);
+
+        return err;
+}
+
+static int __init budget_patch_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_patch_exit(void)
+{
+        saa7146_unregister_extension(&budget_extension);
+}
+
+static struct saa7146_extension budget_extension = {
+        .name           = "budget_patch dvb\0",
+        .flags          = 0,
+
+        .module         = THIS_MODULE,
+        .pci_tbl        = pci_tbl,
+        .attach         = budget_patch_attach,
+        .detach         = budget_patch_detach,
+
+        .irq_mask       = MASK_10,
+        .irq_func       = ttpci_budget_irq10_handler,
+};
+
+module_init(budget_patch_init);
+module_exit(budget_patch_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others");
+MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 "
+                   "based so-called Budget Patch cards");
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
new file mode 100644
index 0000000..5e6a10f
--- /dev/null
+++ b/drivers/media/dvb/ttpci/budget.c
@@ -0,0 +1,573 @@
+/*
+ * budget.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph  Metzler
+ *                       & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ *           Michael Dreher <michael@5dot1.de>,
+ *           Oliver Endriss <o.endriss@gmx.de> and
+ *           Andreas 'randy' Weinberger
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/dvb/
+ */
+
+#include "budget.h"
+#include "stv0299.h"
+#include "ves1x93.h"
+#include "ves1820.h"
+#include "l64781.h"
+#include "tda8083.h"
+
+static void Set22K (struct budget *budget, int state)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+	saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
+}
+
+/* Diseqc functions only for TT Budget card */
+/* taken from the Skyvision DVB driver by
+   Ralph Metzler <rjkm@metzlerbros.de> */
+
+static void DiseqcSendBit (struct budget *budget, int data)
+{
+	struct saa7146_dev *dev=budget->dev;
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+	udelay(data ? 500 : 1000);
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	udelay(data ? 1000 : 500);
+}
+
+static void DiseqcSendByte (struct budget *budget, int data)
+{
+	int i, par=1, d;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	for (i=7; i>=0; i--) {
+		d = (data>>i)&1;
+		par ^= d;
+		DiseqcSendBit(budget, d);
+	}
+
+	DiseqcSendBit(budget, par);
+}
+
+static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
+{
+	struct saa7146_dev *dev=budget->dev;
+	int i;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+	mdelay(16);
+
+	for (i=0; i<len; i++)
+		DiseqcSendByte(budget, msg[i]);
+
+	mdelay(16);
+
+	if (burst!=-1) {
+		if (burst)
+			DiseqcSendByte(budget, 0xff);
+		else {
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+			udelay(12500);
+			saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+		}
+		msleep(20);
+	}
+
+	return 0;
+}
+
+/*
+ *   Routines for the Fujitsu Siemens Activy budget card
+ *   22 kHz tone and DiSEqC are handled by the frontend.
+ *   Voltage must be set here.
+ */
+static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage)
+{
+	struct saa7146_dev *dev=budget->dev;
+
+	dprintk(2, "budget: %p\n", budget);
+
+	switch (voltage) {
+		case SEC_VOLTAGE_13:
+			saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
+			break;
+		case SEC_VOLTAGE_18:
+			saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	return SetVoltage_Activy (budget, voltage);
+}
+
+static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		Set22K (budget, 1);
+		break;
+
+	case SEC_TONE_OFF:
+		Set22K (budget, 0);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+
+	return 0;
+}
+
+static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+
+	SendDiSEqCMsg (budget, 0, NULL, minicmd);
+
+	return 0;
+}
+
+static int alps_bsrv2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u8 pwr = 0;
+	u8 buf[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+	u32 div = (params->frequency + 479500) / 125;
+
+	if (params->frequency > 2000000) pwr = 3;
+	else if (params->frequency > 1800000) pwr = 2;
+	else if (params->frequency > 1600000) pwr = 1;
+	else if (params->frequency > 1200000) pwr = 0;
+	else if (params->frequency >= 1100000) pwr = 1;
+	else pwr = 2;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = ((div & 0x18000) >> 10) | 0x95;
+	buf[3] = (pwr << 6) | 0x30;
+
+        // NOTE: since we're using a prescaler of 2, we set the
+	// divisor frequency to 62.5kHz and divide by 125 above
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config =
+{
+	.demod_address = 0x08,
+	.xin = 90100000UL,
+	.invert_pwm = 0,
+	.pll_set = alps_bsrv2_pll_set,
+};
+
+static u8 alps_bsru6_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x00,
+	0x03, 0x00,
+        0x04, 0x7d,   /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+	0x05, 0x35,   /* I2CT = 0, SCLT = 1, SDAT = 1 */
+	0x06, 0x40,   /* DAC not used, set to high impendance mode */
+	0x07, 0x00,   /* DAC LSB */
+	0x08, 0x40,   /* DiSEqC off, LNB power on OP2/LOCK pin on */
+	0x09, 0x00,   /* FIFO */
+	0x0c, 0x51,   /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+	0x0d, 0x82,   /* DC offset compensation = ON, beta_agc1 = 2 */
+	0x0e, 0x23,   /* alpha_tmg = 2, beta_tmg = 3 */
+	0x10, 0x3f,   // AGC2  0x3d
+	0x11, 0x84,
+	0x12, 0xb5,   // Lock detect: -64  Carrier freq detect:on
+	0x15, 0xc9,   // lock detector threshold
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,  // out imp: normal  out type: parallel FEC mode:0
+	0x29, 0x1e,  // 1/2 threshold
+	0x2a, 0x14,  // 2/3 threshold
+	0x2b, 0x0f,  // 3/4 threshold
+	0x2c, 0x09,  // 5/6 threshold
+	0x2d, 0x05,  // 7/8 threshold
+	0x2e, 0x01,
+	0x31, 0x1f,  // test all FECs
+	0x32, 0x19,  // viterbi and synchro search
+	0x33, 0xfc,  // rs control
+	0x34, 0x93,  // error control
+	0x0f, 0x52,
+	0xff, 0xff
+};
+
+static int alps_bsru6_set_symbol_rate(struct dvb_frontend* fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+	else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+	else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+	else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+	else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+	else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
+
+	stv0299_writereg (fe, 0x13, aclk);
+	stv0299_writereg (fe, 0x14, bclk);
+	stv0299_writereg (fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg (fe, 0x20, (ratio >>  8) & 0xff);
+	stv0299_writereg (fe, 0x21, (ratio      ) & 0xf0);
+
+	return 0;
+}
+
+static int alps_bsru6_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u8 data[4];
+	u32 div;
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	if ((params->frequency < 950000) || (params->frequency > 2150000)) return -EINVAL;
+
+	div = (params->frequency + (125 - 1)) / 125; // round correctly
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+	data[3] = 0xC4;
+
+	if (params->frequency > 1530000) data[3] = 0xc0;
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct stv0299_config alps_bsru6_config = {
+
+	.demod_address = 0x68,
+	.inittab = alps_bsru6_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = alps_bsru6_set_symbol_rate,
+	.pll_set = alps_bsru6_pll_set,
+};
+
+static int alps_tdbe2_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (params->frequency + 35937500 + 31250) / 62500;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x85 | ((div >> 10) & 0x60);
+	data[3] = (params->frequency < 174000000 ? 0x88 : params->frequency < 470000000 ? 0x84 : 0x81);
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct ves1820_config alps_tdbe2_config = {
+	.demod_address = 0x09,
+	.xin = 57840000UL,
+	.invert = 1,
+	.selagc = VES1820_SELAGC_SIGNAMPERR,
+	.pll_set = alps_tdbe2_pll_set,
+};
+
+static int grundig_29504_401_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u32 div;
+	u8 cfg, cpump, band_select;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = (36125000 + params->frequency) / 166666;
+
+	cfg = 0x88;
+
+	if (params->frequency < 175000000) cpump = 2;
+	else if (params->frequency < 390000000) cpump = 1;
+	else if (params->frequency < 470000000) cpump = 2;
+	else if (params->frequency < 750000000) cpump = 1;
+	else cpump = 3;
+
+	if (params->frequency < 175000000) band_select = 0x0e;
+	else if (params->frequency < 470000000) band_select = 0x05;
+	else band_select = 0x03;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = ((div >> 10) & 0x60) | cfg;
+	data[3] = (cpump << 6) | band_select;
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct l64781_config grundig_29504_401_config = {
+	.demod_address = 0x55,
+	.pll_set = grundig_29504_401_pll_set,
+};
+
+static int grundig_29504_451_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct budget* budget = (struct budget*) fe->dvb->priv;
+	u32 div;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+	div = params->frequency / 125;
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x8e;
+	data[3] = 0x00;
+
+	if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+	.demod_address = 0x68,
+	.pll_set = grundig_29504_451_pll_set,
+};
+
+static u8 read_pwm(struct budget* budget)
+{
+	u8 b = 0xff;
+	u8 pwm;
+	struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
+				 { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
+
+        if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
+		pwm = 0x48;
+
+	return pwm;
+}
+
+static void frontend_init(struct budget *budget)
+{
+	switch(budget->dev->pci->subsystem_device) {
+	case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
+	case 0x1013:
+		// try the ALPS BSRV2 first of all
+		budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+		        budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst;
+			budget->dvb_frontend->ops->set_tone = budget_set_tone;
+			break;
+		}
+
+		// try the ALPS BSRU6 now
+		budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops->diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+			budget->dvb_frontend->ops->diseqc_send_burst = budget_diseqc_send_burst;
+			budget->dvb_frontend->ops->set_tone = budget_set_tone;
+			break;
+		}
+		break;
+
+	case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
+
+		budget->dvb_frontend = ves1820_attach(&alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
+		if (budget->dvb_frontend) break;
+		break;
+
+	case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060))
+
+		budget->dvb_frontend = l64781_attach(&grundig_29504_401_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) break;
+		break;
+
+	case 0x4f60: // Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/ALPS BSRU6(tsa5059))
+		budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops->set_voltage = siemens_budget_set_voltage;
+			break;
+		}
+		break;
+
+	case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522))
+		budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap);
+		if (budget->dvb_frontend) {
+			budget->dvb_frontend->ops->set_voltage = siemens_budget_set_voltage;
+			break;
+		}
+		break;
+	}
+
+	if (budget->dvb_frontend == NULL) {
+		printk("budget: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		       budget->dev->pci->vendor,
+		       budget->dev->pci->device,
+		       budget->dev->pci->subsystem_vendor,
+		       budget->dev->pci->subsystem_device);
+	} else {
+		if (dvb_register_frontend(budget->dvb_adapter, budget->dvb_frontend)) {
+			printk("budget: Frontend registration failed!\n");
+			if (budget->dvb_frontend->ops->release)
+				budget->dvb_frontend->ops->release(budget->dvb_frontend);
+			budget->dvb_frontend = NULL;
+		}
+	}
+}
+
+static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+	struct budget *budget = NULL;
+	int err;
+
+	budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
+	if( NULL == budget ) {
+		return -ENOMEM;
+	}
+
+	dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
+
+	dev->ext_priv = budget;
+
+	if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
+		printk("==> failed\n");
+		kfree (budget);
+		return err;
+	}
+
+	budget->dvb_adapter->priv = budget;
+	frontend_init(budget);
+
+	return 0;
+}
+
+static int budget_detach (struct saa7146_dev* dev)
+{
+	struct budget *budget = (struct budget*) dev->ext_priv;
+	int err;
+
+	if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend);
+
+	err = ttpci_budget_deinit (budget);
+
+	kfree (budget);
+	dev->ext_priv = NULL;
+
+	return err;
+}
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbs,	"TT-Budget/WinTV-NOVA-S  PCI",	BUDGET_TT);
+MAKE_BUDGET_INFO(ttbc,	"TT-Budget/WinTV-NOVA-C  PCI",	BUDGET_TT);
+MAKE_BUDGET_INFO(ttbt,	"TT-Budget/WinTV-NOVA-T  PCI",	BUDGET_TT);
+MAKE_BUDGET_INFO(satel,	"SATELCO Multimedia PCI",	BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
+
+static struct pci_device_id pci_tbl[] = {
+	MAKE_EXTENSION_PCI(ttbs,  0x13c2, 0x1003),
+	MAKE_EXTENSION_PCI(ttbc,  0x13c2, 0x1004),
+	MAKE_EXTENSION_PCI(ttbt,  0x13c2, 0x1005),
+	MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+	MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
+	MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
+	{
+		.vendor    = 0,
+	}
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+	.name		= "budget dvb\0",
+	.flags		= 0,
+
+	.module		= THIS_MODULE,
+	.pci_tbl	= pci_tbl,
+	.attach		= budget_attach,
+	.detach		= budget_detach,
+
+	.irq_mask	= MASK_10,
+	.irq_func	= ttpci_budget_irq10_handler,
+};
+
+static int __init budget_init(void)
+{
+	return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_exit(void)
+{
+	saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_init);
+module_exit(budget_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+		   "budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h
new file mode 100644
index 0000000..10bd41f
--- /dev/null
+++ b/drivers/media/dvb/ttpci/budget.h
@@ -0,0 +1,110 @@
+#ifndef __BUDGET_DVB__
+#define __BUDGET_DVB__
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include "demux.h"
+#include "dvb_demux.h"
+#include "dmxdev.h"
+#include "dvb_filter.h"
+#include "dvb_net.h"
+
+#include <linux/module.h>
+#include <media/saa7146.h>
+
+extern int budget_debug;
+
+#ifdef dprintk
+#undef dprintk
+#endif
+
+#define dprintk(level,args...) \
+            do { if ((budget_debug & level)) { printk("%s: %s(): ",__stringify(KBUILD_MODNAME), __FUNCTION__); printk(args); } } while (0)
+
+struct budget_info {
+	char *name;
+	int type;
+};
+
+/* place to store all the necessary device information */
+struct budget {
+
+	/* devices */
+	struct dvb_device dvb_dev;
+	struct dvb_net dvb_net;
+
+	struct saa7146_dev *dev;
+
+	struct i2c_adapter i2c_adap;
+	struct budget_info *card;
+
+	unsigned char *grabbing;
+	struct saa7146_pgtable pt;
+
+	struct tasklet_struct fidb_tasklet;
+	struct tasklet_struct vpe_tasklet;
+
+	struct dmxdev dmxdev;
+	struct dvb_demux demux;
+
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+
+	int fe_synced;
+	struct semaphore pid_mutex;
+
+	int ci_present;
+	int video_port;
+
+	u8 tsf;
+	u32 ttbp;
+	int feeding;
+
+	spinlock_t feedlock;
+
+	spinlock_t debilock;
+
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_frontend *dvb_frontend;
+	void *priv;
+};
+
+#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \
+static struct budget_info x_var ## _info = { \
+	.name=x_name,	\
+	.type=x_type };	\
+static struct saa7146_pci_extension_data x_var = { \
+	.ext_priv = &x_var ## _info, \
+	.ext = &budget_extension };
+
+#define TS_WIDTH  (376)
+#define TS_HEIGHT (512)
+#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)
+#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)
+
+#define BUDGET_TT		   0
+#define BUDGET_TT_HW_DISEQC	   1
+#define BUDGET_PATCH		   3
+#define BUDGET_FS_ACTIVY	   4
+#define BUDGET_CIN1200S		   5
+#define BUDGET_CIN1200C		   6
+#define BUDGET_CIN1200T		   7
+#define BUDGET_KNC1S		   8
+#define BUDGET_KNC1C		   9
+#define BUDGET_KNC1T		   10
+
+#define BUDGET_VIDEO_PORTA         0
+#define BUDGET_VIDEO_PORTB         1
+
+extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
+			     struct saa7146_pci_extension_data *info,
+			     struct module *owner);
+extern int ttpci_budget_deinit(struct budget *budget);
+extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr);
+extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port);
+extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count,
+				 int uselocks, int nobusyloop);
+extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value,
+				  int uselocks, int nobusyloop);
+
+#endif
diff --git a/drivers/media/dvb/ttpci/fdump.c b/drivers/media/dvb/ttpci/fdump.c
new file mode 100644
index 0000000..0b478db
--- /dev/null
+++ b/drivers/media/dvb/ttpci/fdump.c
@@ -0,0 +1,44 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+    unsigned char buf[8];
+    unsigned int i, count, bytes = 0;
+    FILE *fd_in, *fd_out;
+
+    if (argc != 4) {
+	fprintf(stderr, "\n\tusage: %s <ucode.bin> <array_name> <output_name>\n\n", argv[0]);
+	return -1;
+    }
+
+    fd_in = fopen(argv[1], "rb");
+    if (fd_in == NULL) {
+	fprintf(stderr, "firmware file '%s' not found\n", argv[1]);
+	return -1;
+    }
+
+    fd_out = fopen(argv[3], "w+");
+    if (fd_out == NULL) {
+	fprintf(stderr, "cannot create output file '%s'\n", argv[3]);
+	return -1;
+    }
+
+    fprintf(fd_out, "\n#include <asm/types.h>\n\nu8 %s [] = {", argv[2]);
+
+    while ((count = fread(buf, 1, 8, fd_in)) > 0) {
+	fprintf(fd_out, "\n\t");
+	for (i = 0; i < count; i++, bytes++)
+	    fprintf(fd_out, "0x%02x, ", buf[i]);
+    }
+
+    fprintf(fd_out, "\n};\n\n");
+    
+    fclose(fd_in);
+    fclose(fd_out);
+
+    return 0;
+}
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.c b/drivers/media/dvb/ttpci/ttpci-eeprom.c
new file mode 100644
index 0000000..e9a8457
--- /dev/null
+++ b/drivers/media/dvb/ttpci/ttpci-eeprom.c
@@ -0,0 +1,146 @@
+/*
+    Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM,
+    decode it and store it in the associated adapter struct for
+    use by dvb_net.c
+
+    This card appear to have the 24C16 write protect held to ground,
+    thus permitting normal read/write operation. Theoretically it
+    would be possible to write routines to burn a different (encoded)
+    MAC address into the EEPROM.
+
+    Robert Schlabbach	GMX
+    Michael Glaum	KVH Industries
+    Holger Waechtler	Convergence
+
+    Copyright (C) 2002-2003 Ralph Metzler <rjkm@metzlerbros.de>
+                            Metzler Brothers Systementwicklung GbR
+
+    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 <asm/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/i2c.h>
+
+
+#if 1
+#define dprintk(x...) do { printk(x); } while (0)
+#else
+#define dprintk(x...) do { } while (0)
+#endif
+
+
+static int check_mac_tt(u8 *buf)
+{
+        int i;
+        u16 tmp = 0xffff;
+
+        for (i = 0; i < 8; i++) {
+                tmp  = (tmp << 8) | ((tmp >> 8) ^ buf[i]);
+                tmp ^= (tmp >> 4) & 0x0f;
+                tmp ^= (tmp << 12) ^ ((tmp & 0xff) << 5);
+        }
+        tmp ^= 0xffff;
+        return (((tmp >> 8) ^ buf[8]) | ((tmp & 0xff) ^ buf[9]));
+}
+
+static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC)
+{
+        u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c,
+		       0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6,
+		       0x1d, 0x36, 0x64, 0x78};
+        u8 data[20];
+        int i;
+
+	/* In case there is a sig check failure have the orig contents available */
+	memcpy(data, encodedMAC, 20);
+
+	for (i = 0; i < 20; i++)
+                data[i] ^= xor[i];
+        for (i = 0; i < 10; i++)
+                data[i] = ((data[2 * i + 1] << 8) | data[2 * i])
+			>> ((data[2 * i + 1] >> 6) & 3);
+
+        if (check_mac_tt(data))
+                return -ENODEV;
+
+	decodedMAC[0] = data[2]; decodedMAC[1] = data[1]; decodedMAC[2] = data[0];
+	decodedMAC[3] = data[6]; decodedMAC[4] = data[5]; decodedMAC[5] = data[4];
+        return 0;
+}
+
+static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC)
+{
+	int ret;
+	u8 b0[] = { 0xcc };
+
+	struct i2c_msg msg[] = {
+		{ .addr = 0x50, .flags = 0, .buf = b0, .len = 1 },
+		{ .addr = 0x50, .flags = I2C_M_RD, .buf = encodedMAC, .len = 20 }
+	};
+
+	/* dprintk("%s\n", __FUNCTION__); */
+
+	ret = i2c_transfer(adapter, msg, 2);
+
+	if (ret != 2)		/* Assume EEPROM isn't there */
+		return (-ENODEV);
+
+	return 0;
+}
+
+
+int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac)
+{
+	int ret, i;
+	u8 encodedMAC[20];
+	u8 decodedMAC[6];
+
+	ret = ttpci_eeprom_read_encodedMAC(adapter, encodedMAC);
+
+	if (ret != 0) {		/* Will only be -ENODEV */
+		dprintk("Couldn't read from EEPROM: not there?\n");
+		memset(proposed_mac, 0, 6);
+		return ret;
+	}
+
+	ret = getmac_tt(decodedMAC, encodedMAC);
+	if( ret != 0 ) {
+		dprintk("adapter failed MAC signature check\n");
+		dprintk("encoded MAC from EEPROM was " );
+		for(i=0; i<19; i++) {
+			dprintk( "%.2x:", encodedMAC[i]);
+		}
+		dprintk("%.2x\n", encodedMAC[19]);
+		memset(proposed_mac, 0, 6);
+		return ret;
+	}
+
+	memcpy(proposed_mac, decodedMAC, 6);
+	dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+		decodedMAC[0], decodedMAC[1], decodedMAC[2],
+		decodedMAC[3], decodedMAC[4], decodedMAC[5]);
+	return 0;
+}
+
+EXPORT_SYMBOL(ttpci_eeprom_parse_mac);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others");
+MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards "
+		"made by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.h b/drivers/media/dvb/ttpci/ttpci-eeprom.h
new file mode 100644
index 0000000..e2dc6cf
--- /dev/null
+++ b/drivers/media/dvb/ttpci/ttpci-eeprom.h
@@ -0,0 +1,33 @@
+/*
+    Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM,
+    decode it and store it in associated adapter net device
+
+    Robert Schlabbach	GMX
+    Michael Glaum	KVH Industries
+    Holger Waechtler	Convergence
+
+    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 __TTPCI_EEPROM_H__
+#define __TTPCI_EEPROM_H__
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac);
+
+#endif
diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig
new file mode 100644
index 0000000..4aa714a
--- /dev/null
+++ b/drivers/media/dvb/ttusb-budget/Kconfig
@@ -0,0 +1,15 @@
+config DVB_TTUSB_BUDGET
+	tristate "Technotrend/Hauppauge Nova-USB devices"
+	depends on DVB_CORE && USB
+	select DVB_CX22700
+	select DVB_TDA1004X
+	select DVB_TDA8083
+	select DVB_STV0299
+	help
+	  Support for external USB adapters designed by Technotrend and
+	  produced by Hauppauge, shipped under the brand name 'Nova-USB'.
+
+          These devices don't have a MPEG decoder built in, so you need
+	  an external software decoder to watch TV.
+
+	  Say Y if you own such a device and want to use it.
diff --git a/drivers/media/dvb/ttusb-budget/Makefile b/drivers/media/dvb/ttusb-budget/Makefile
new file mode 100644
index 0000000..6ab97f6
--- /dev/null
+++ b/drivers/media/dvb/ttusb-budget/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
new file mode 100644
index 0000000..4c046ec
--- /dev/null
+++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
@@ -0,0 +1,1610 @@
+/*
+ * TTUSB DVB driver
+ *
+ * Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ *
+ *	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.
+ */
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/errno.h>
+#include <asm/semaphore.h>
+
+#include "dvb_frontend.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+#include "cx22700.h"
+#include "tda1004x.h"
+#include "stv0299.h"
+#include "tda8083.h"
+
+#include <linux/dvb/frontend.h>
+#include <linux/dvb/dmx.h>
+#include <linux/pci.h>
+
+
+/*
+  TTUSB_HWSECTIONS:
+    the DSP supports filtering in hardware, however, since the "muxstream"
+    is a bit braindead (no matching channel masks or no matching filter mask),
+    we won't support this - yet. it doesn't event support negative filters,
+    so the best way is maybe to keep TTUSB_HWSECTIONS undef'd and just
+    parse TS data. USB bandwith will be a problem when having large
+    datastreams, especially for dvb-net, but hey, that's not my problem.
+
+  TTUSB_DISEQC, TTUSB_TONE:
+    let the STC do the diseqc/tone stuff. this isn't supported at least with
+    my TTUSB, so let it undef'd unless you want to implement another
+    frontend. never tested.
+
+  DEBUG:
+    define it to > 3 for really hardcore debugging. you probably don't want
+    this unless the device doesn't load at all. > 2 for bandwidth statistics.
+*/
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(x...) do { if (debug) printk(KERN_DEBUG x); } while (0)
+
+#define ISO_BUF_COUNT      4
+#define FRAMES_PER_ISO_BUF 4
+#define ISO_FRAME_SIZE     912
+#define TTUSB_MAXCHANNEL   32
+#ifdef TTUSB_HWSECTIONS
+#define TTUSB_MAXFILTER    16	/* ??? */
+#endif
+
+#define TTUSB_REV_2_2	0x22
+#define TTUSB_BUDGET_NAME "ttusb_stc_fw"
+
+/**
+ *  since we're casting (struct ttusb*) <-> (struct dvb_demux*) around
+ *  the dvb_demux field must be the first in struct!!
+ */
+struct ttusb {
+	struct dvb_demux dvb_demux;
+	struct dmxdev dmxdev;
+	struct dvb_net dvbnet;
+
+	/* and one for USB access. */
+	struct semaphore semi2c;
+	struct semaphore semusb;
+
+	struct dvb_adapter *adapter;
+	struct usb_device *dev;
+
+	struct i2c_adapter i2c_adap;
+
+	int disconnecting;
+	int iso_streaming;
+
+	unsigned int bulk_out_pipe;
+	unsigned int bulk_in_pipe;
+	unsigned int isoc_in_pipe;
+
+	void *iso_buffer;
+	dma_addr_t iso_dma_handle;
+
+	struct urb *iso_urb[ISO_BUF_COUNT];
+
+	int running_feed_count;
+	int last_channel;
+	int last_filter;
+
+	u8 c;			/* transaction counter, wraps around...  */
+	fe_sec_tone_mode_t tone;
+	fe_sec_voltage_t voltage;
+
+	int mux_state;		// 0..2 - MuxSyncWord, 3 - nMuxPacks,    4 - muxpack
+	u8 mux_npacks;
+	u8 muxpack[256 + 8];
+	int muxpack_ptr, muxpack_len;
+
+	int insync;
+
+	int cc;			/* MuxCounter - will increment on EVERY MUX PACKET */
+	/* (including stuffing. yes. really.) */
+
+	u8 last_result[32];
+
+	int revision;
+
+#if 0
+	devfs_handle_t stc_devfs_handle;
+#endif
+
+	struct dvb_frontend* fe;
+};
+
+/* ugly workaround ... don't know why it's neccessary to read */
+/* all result codes. */
+
+#define DEBUG 0
+static int ttusb_cmd(struct ttusb *ttusb,
+	      const u8 * data, int len, int needresult)
+{
+	int actual_len;
+	int err;
+#if DEBUG >= 3
+	int i;
+
+	printk(">");
+	for (i = 0; i < len; ++i)
+		printk(" %02x", data[i]);
+	printk("\n");
+#endif
+
+	if (down_interruptible(&ttusb->semusb) < 0)
+		return -EAGAIN;
+
+	err = usb_bulk_msg(ttusb->dev, ttusb->bulk_out_pipe,
+			   (u8 *) data, len, &actual_len, 1000);
+	if (err != 0) {
+		dprintk("%s: usb_bulk_msg(send) failed, err == %i!\n",
+			__FUNCTION__, err);
+		up(&ttusb->semusb);
+		return err;
+	}
+	if (actual_len != len) {
+		dprintk("%s: only wrote %d of %d bytes\n", __FUNCTION__,
+			actual_len, len);
+		up(&ttusb->semusb);
+		return -1;
+	}
+
+	err = usb_bulk_msg(ttusb->dev, ttusb->bulk_in_pipe,
+			   ttusb->last_result, 32, &actual_len, 1000);
+
+	if (err != 0) {
+		printk("%s: failed, receive error %d\n", __FUNCTION__,
+		       err);
+		up(&ttusb->semusb);
+		return err;
+	}
+#if DEBUG >= 3
+	actual_len = ttusb->last_result[3] + 4;
+	printk("<");
+	for (i = 0; i < actual_len; ++i)
+		printk(" %02x", ttusb->last_result[i]);
+	printk("\n");
+#endif
+	if (!needresult)
+		up(&ttusb->semusb);
+	return 0;
+}
+
+static int ttusb_result(struct ttusb *ttusb, u8 * data, int len)
+{
+	memcpy(data, ttusb->last_result, len);
+	up(&ttusb->semusb);
+	return 0;
+}
+
+static int ttusb_i2c_msg(struct ttusb *ttusb,
+		  u8 addr, u8 * snd_buf, u8 snd_len, u8 * rcv_buf,
+		  u8 rcv_len)
+{
+	u8 b[0x28];
+	u8 id = ++ttusb->c;
+	int i, err;
+
+	if (snd_len > 0x28 - 7 || rcv_len > 0x20 - 7)
+		return -EINVAL;
+
+	b[0] = 0xaa;
+	b[1] = id;
+	b[2] = 0x31;
+	b[3] = snd_len + 3;
+	b[4] = addr << 1;
+	b[5] = snd_len;
+	b[6] = rcv_len;
+
+	for (i = 0; i < snd_len; i++)
+		b[7 + i] = snd_buf[i];
+
+	err = ttusb_cmd(ttusb, b, snd_len + 7, 1);
+
+	if (err)
+		return -EREMOTEIO;
+
+	err = ttusb_result(ttusb, b, 0x20);
+
+        /* check if the i2c transaction was successful */
+        if ((snd_len != b[5]) || (rcv_len != b[6])) return -EREMOTEIO;
+
+	if (rcv_len > 0) {
+
+		if (err || b[0] != 0x55 || b[1] != id) {
+			dprintk
+			    ("%s: usb_bulk_msg(recv) failed, err == %i, id == %02x, b == ",
+			     __FUNCTION__, err, id);
+			return -EREMOTEIO;
+		}
+
+		for (i = 0; i < rcv_len; i++)
+			rcv_buf[i] = b[7 + i];
+	}
+
+	return rcv_len;
+}
+
+static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num)
+{
+	struct ttusb *ttusb = i2c_get_adapdata(adapter);
+	int i = 0;
+	int inc;
+
+	if (down_interruptible(&ttusb->semi2c) < 0)
+		return -EAGAIN;
+
+	while (i < num) {
+		u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf;
+		int err;
+
+		if (num > i + 1 && (msg[i + 1].flags & I2C_M_RD)) {
+			addr = msg[i].addr;
+			snd_buf = msg[i].buf;
+			snd_len = msg[i].len;
+			rcv_buf = msg[i + 1].buf;
+			rcv_len = msg[i + 1].len;
+			inc = 2;
+		} else {
+			addr = msg[i].addr;
+			snd_buf = msg[i].buf;
+			snd_len = msg[i].len;
+			rcv_buf = NULL;
+			rcv_len = 0;
+			inc = 1;
+		}
+
+		err = ttusb_i2c_msg(ttusb, addr,
+				    snd_buf, snd_len, rcv_buf, rcv_len);
+
+		if (err < rcv_len) {
+			dprintk("%s: i == %i\n", __FUNCTION__, i);
+			break;
+		}
+
+		i += inc;
+	}
+
+	up(&ttusb->semi2c);
+	return i;
+}
+
+#include "dvb-ttusb-dspbootcode.h"
+
+static int ttusb_boot_dsp(struct ttusb *ttusb)
+{
+	int i, err;
+	u8 b[40];
+
+	/* BootBlock */
+	b[0] = 0xaa;
+	b[2] = 0x13;
+	b[3] = 28;
+
+	/* upload dsp code in 32 byte steps (36 didn't work for me ...) */
+	/* 32 is max packet size, no messages should be splitted. */
+	for (i = 0; i < sizeof(dsp_bootcode); i += 28) {
+		memcpy(&b[4], &dsp_bootcode[i], 28);
+
+		b[1] = ++ttusb->c;
+
+		err = ttusb_cmd(ttusb, b, 32, 0);
+		if (err)
+			goto done;
+	}
+
+	/* last block ... */
+	b[1] = ++ttusb->c;
+	b[2] = 0x13;
+	b[3] = 0;
+
+	err = ttusb_cmd(ttusb, b, 4, 0);
+	if (err)
+		goto done;
+
+	/* BootEnd */
+	b[1] = ++ttusb->c;
+	b[2] = 0x14;
+	b[3] = 0;
+
+	err = ttusb_cmd(ttusb, b, 4, 0);
+
+      done:
+	if (err) {
+		dprintk("%s: usb_bulk_msg() failed, return value %i!\n",
+			__FUNCTION__, err);
+	}
+
+	return err;
+}
+
+static int ttusb_set_channel(struct ttusb *ttusb, int chan_id, int filter_type,
+		      int pid)
+{
+	int err;
+	/* SetChannel */
+	u8 b[] = { 0xaa, ++ttusb->c, 0x22, 4, chan_id, filter_type,
+		(pid >> 8) & 0xff, pid & 0xff
+	};
+
+	err = ttusb_cmd(ttusb, b, sizeof(b), 0);
+	return err;
+}
+
+static int ttusb_del_channel(struct ttusb *ttusb, int channel_id)
+{
+	int err;
+	/* DelChannel */
+	u8 b[] = { 0xaa, ++ttusb->c, 0x23, 1, channel_id };
+
+	err = ttusb_cmd(ttusb, b, sizeof(b), 0);
+	return err;
+}
+
+#ifdef TTUSB_HWSECTIONS
+static int ttusb_set_filter(struct ttusb *ttusb, int filter_id,
+		     int associated_chan, u8 filter[8], u8 mask[8])
+{
+	int err;
+	/* SetFilter */
+	u8 b[] = { 0xaa, 0, 0x24, 0x1a, filter_id, associated_chan,
+		filter[0], filter[1], filter[2], filter[3],
+		filter[4], filter[5], filter[6], filter[7],
+		filter[8], filter[9], filter[10], filter[11],
+		mask[0], mask[1], mask[2], mask[3],
+		mask[4], mask[5], mask[6], mask[7],
+		mask[8], mask[9], mask[10], mask[11]
+	};
+
+	err = ttusb_cmd(ttusb, b, sizeof(b), 0);
+	return err;
+}
+
+static int ttusb_del_filter(struct ttusb *ttusb, int filter_id)
+{
+	int err;
+	/* DelFilter */
+	u8 b[] = { 0xaa, ++ttusb->c, 0x25, 1, filter_id };
+
+	err = ttusb_cmd(ttusb, b, sizeof(b), 0);
+	return err;
+}
+#endif
+
+static int ttusb_init_controller(struct ttusb *ttusb)
+{
+	u8 b0[] = { 0xaa, ++ttusb->c, 0x15, 1, 0 };
+	u8 b1[] = { 0xaa, ++ttusb->c, 0x15, 1, 1 };
+	u8 b2[] = { 0xaa, ++ttusb->c, 0x32, 1, 0 };
+	/* i2c write read: 5 bytes, addr 0x10, 0x02 bytes write, 1 bytes read. */
+	u8 b3[] =
+	    { 0xaa, ++ttusb->c, 0x31, 5, 0x10, 0x02, 0x01, 0x00, 0x1e };
+	u8 b4[] =
+	    { 0x55, ttusb->c, 0x31, 4, 0x10, 0x02, 0x01, 0x00, 0x1e };
+
+	u8 get_version[] = { 0xaa, ++ttusb->c, 0x17, 5, 0, 0, 0, 0, 0 };
+	u8 get_dsp_version[0x20] =
+	    { 0xaa, ++ttusb->c, 0x26, 28, 0, 0, 0, 0, 0 };
+	int err;
+
+	/* reset board */
+	if ((err = ttusb_cmd(ttusb, b0, sizeof(b0), 0)))
+		return err;
+
+	/* reset board (again?) */
+	if ((err = ttusb_cmd(ttusb, b1, sizeof(b1), 0)))
+		return err;
+
+	ttusb_boot_dsp(ttusb);
+
+	/* set i2c bit rate */
+	if ((err = ttusb_cmd(ttusb, b2, sizeof(b2), 0)))
+		return err;
+
+	if ((err = ttusb_cmd(ttusb, b3, sizeof(b3), 1)))
+		return err;
+
+	err = ttusb_result(ttusb, b4, sizeof(b4));
+
+	if ((err = ttusb_cmd(ttusb, get_version, sizeof(get_version), 1)))
+		return err;
+
+	if ((err = ttusb_result(ttusb, get_version, sizeof(get_version))))
+		return err;
+
+	dprintk("%s: stc-version: %c%c%c%c%c\n", __FUNCTION__,
+		get_version[4], get_version[5], get_version[6],
+		get_version[7], get_version[8]);
+
+	if (memcmp(get_version + 4, "V 0.0", 5) &&
+	    memcmp(get_version + 4, "V 1.1", 5) &&
+	    memcmp(get_version + 4, "V 2.1", 5) &&
+	    memcmp(get_version + 4, "V 2.2", 5)) {
+		printk
+		    ("%s: unknown STC version %c%c%c%c%c, please report!\n",
+		     __FUNCTION__, get_version[4], get_version[5],
+		     get_version[6], get_version[7], get_version[8]);
+	}
+
+	ttusb->revision = ((get_version[6] - '0') << 4) |
+			   (get_version[8] - '0');
+
+	err =
+	    ttusb_cmd(ttusb, get_dsp_version, sizeof(get_dsp_version), 1);
+	if (err)
+		return err;
+
+	err =
+	    ttusb_result(ttusb, get_dsp_version, sizeof(get_dsp_version));
+	if (err)
+		return err;
+	printk("%s: dsp-version: %c%c%c\n", __FUNCTION__,
+	       get_dsp_version[4], get_dsp_version[5], get_dsp_version[6]);
+	return 0;
+}
+
+#ifdef TTUSB_DISEQC
+static int ttusb_send_diseqc(struct dvb_frontend* fe,
+			     const struct dvb_diseqc_master_cmd *cmd)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+	u8 b[12] = { 0xaa, ++ttusb->c, 0x18 };
+
+	int err;
+
+	b[3] = 4 + 2 + cmd->msg_len;
+	b[4] = 0xFF;		/* send diseqc master, not burst */
+	b[5] = cmd->msg_len;
+
+	memcpy(b + 5, cmd->msg, cmd->msg_len);
+
+	/* Diseqc */
+	if ((err = ttusb_cmd(ttusb, b, 4 + b[3], 0))) {
+		dprintk("%s: usb_bulk_msg() failed, return value %i!\n",
+			__FUNCTION__, err);
+	}
+
+	return err;
+}
+#endif
+
+static int lnbp21_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+        struct  ttusb* ttusb = (struct ttusb*)  fe->dvb->priv;
+        int ret;
+        u8 data[1];
+        struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = data, .len = sizeof(data) };
+
+        switch(voltage) {
+        case SEC_VOLTAGE_OFF:
+                data[0] = 0x00;
+                break;
+        case SEC_VOLTAGE_13:
+                data[0] = 0x44;
+                break;
+        case SEC_VOLTAGE_18:
+                data[0] = 0x4c;
+                break;
+        default:
+                return -EINVAL;
+        };
+
+        ret = i2c_transfer(&ttusb->i2c_adap, &msg, 1);
+        return (ret != 1) ? -EIO : 0;
+}
+
+static int ttusb_update_lnb(struct ttusb *ttusb)
+{
+	u8 b[] = { 0xaa, ++ttusb->c, 0x16, 5, /*power: */ 1,
+		ttusb->voltage == SEC_VOLTAGE_18 ? 0 : 1,
+		ttusb->tone == SEC_TONE_ON ? 1 : 0, 1, 1
+	};
+	int err;
+
+	/* SetLNB */
+	if ((err = ttusb_cmd(ttusb, b, sizeof(b), 0))) {
+		dprintk("%s: usb_bulk_msg() failed, return value %i!\n",
+			__FUNCTION__, err);
+	}
+
+	return err;
+}
+
+static int ttusb_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+
+	ttusb->voltage = voltage;
+	return ttusb_update_lnb(ttusb);
+}
+
+#ifdef TTUSB_TONE
+static int ttusb_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+
+	ttusb->tone = tone;
+	return ttusb_update_lnb(ttusb);
+}
+#endif
+
+
+#if 0
+static void ttusb_set_led_freq(struct ttusb *ttusb, u8 freq)
+{
+	u8 b[] = { 0xaa, ++ttusb->c, 0x19, 1, freq };
+	int err, actual_len;
+
+	err = ttusb_cmd(ttusb, b, sizeof(b), 0);
+	if (err) {
+		dprintk("%s: usb_bulk_msg() failed, return value %i!\n",
+			__FUNCTION__, err);
+	}
+}
+#endif
+
+/*****************************************************************************/
+
+#ifdef TTUSB_HWSECTIONS
+static void ttusb_handle_ts_data(struct ttusb_channel *channel,
+				 const u8 * data, int len);
+static void ttusb_handle_sec_data(struct ttusb_channel *channel,
+				  const u8 * data, int len);
+#endif
+
+static int numpkt = 0, lastj, numts, numstuff, numsec, numinvalid;
+
+static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack,
+			   int len)
+{
+	u16 csum = 0, cc;
+	int i;
+	for (i = 0; i < len; i += 2)
+		csum ^= le16_to_cpup((u16 *) (muxpack + i));
+	if (csum) {
+		printk("%s: muxpack with incorrect checksum, ignoring\n",
+		       __FUNCTION__);
+		numinvalid++;
+		return;
+	}
+
+	cc = (muxpack[len - 4] << 8) | muxpack[len - 3];
+	cc &= 0x7FFF;
+	if ((cc != ttusb->cc) && (ttusb->cc != -1))
+		printk("%s: cc discontinuity (%d frames missing)\n",
+		       __FUNCTION__, (cc - ttusb->cc) & 0x7FFF);
+	ttusb->cc = (cc + 1) & 0x7FFF;
+	if (muxpack[0] & 0x80) {
+#ifdef TTUSB_HWSECTIONS
+		/* section data */
+		int pusi = muxpack[0] & 0x40;
+		int channel = muxpack[0] & 0x1F;
+		int payload = muxpack[1];
+		const u8 *data = muxpack + 2;
+		/* check offset flag */
+		if (muxpack[0] & 0x20)
+			data++;
+
+		ttusb_handle_sec_data(ttusb->channel + channel, data,
+				      payload);
+		data += payload;
+
+		if ((!!(ttusb->muxpack[0] & 0x20)) ^
+		    !!(ttusb->muxpack[1] & 1))
+			data++;
+#warning TODO: pusi
+		printk("cc: %04x\n", (data[0] << 8) | data[1]);
+#endif
+		numsec++;
+	} else if (muxpack[0] == 0x47) {
+#ifdef TTUSB_HWSECTIONS
+		/* we have TS data here! */
+		int pid = ((muxpack[1] & 0x0F) << 8) | muxpack[2];
+		int channel;
+		for (channel = 0; channel < TTUSB_MAXCHANNEL; ++channel)
+			if (ttusb->channel[channel].active
+			    && (pid == ttusb->channel[channel].pid))
+				ttusb_handle_ts_data(ttusb->channel +
+						     channel, muxpack,
+						     188);
+#endif
+		numts++;
+		dvb_dmx_swfilter_packets(&ttusb->dvb_demux, muxpack, 1);
+	} else if (muxpack[0] != 0) {
+		numinvalid++;
+		printk("illegal muxpack type %02x\n", muxpack[0]);
+	} else
+		numstuff++;
+}
+
+static void ttusb_process_frame(struct ttusb *ttusb, u8 * data, int len)
+{
+	int maxwork = 1024;
+	while (len) {
+		if (!(maxwork--)) {
+			printk("%s: too much work\n", __FUNCTION__);
+			break;
+		}
+
+		switch (ttusb->mux_state) {
+		case 0:
+		case 1:
+		case 2:
+			len--;
+			if (*data++ == 0xAA)
+				++ttusb->mux_state;
+			else {
+				ttusb->mux_state = 0;
+#if DEBUG > 3
+				if (ttusb->insync)
+					printk("%02x ", data[-1]);
+#else
+				if (ttusb->insync) {
+					printk("%s: lost sync.\n",
+					       __FUNCTION__);
+					ttusb->insync = 0;
+				}
+#endif
+			}
+			break;
+		case 3:
+			ttusb->insync = 1;
+			len--;
+			ttusb->mux_npacks = *data++;
+			++ttusb->mux_state;
+			ttusb->muxpack_ptr = 0;
+			/* maximum bytes, until we know the length */
+			ttusb->muxpack_len = 2;
+			break;
+		case 4:
+			{
+				int avail;
+				avail = len;
+				if (avail >
+				    (ttusb->muxpack_len -
+				     ttusb->muxpack_ptr))
+					avail =
+					    ttusb->muxpack_len -
+					    ttusb->muxpack_ptr;
+				memcpy(ttusb->muxpack + ttusb->muxpack_ptr,
+				       data, avail);
+				ttusb->muxpack_ptr += avail;
+				if (ttusb->muxpack_ptr > 264)
+					BUG();
+				data += avail;
+				len -= avail;
+				/* determine length */
+				if (ttusb->muxpack_ptr == 2) {
+					if (ttusb->muxpack[0] & 0x80) {
+						ttusb->muxpack_len =
+						    ttusb->muxpack[1] + 2;
+						if (ttusb->
+						    muxpack[0] & 0x20)
+							ttusb->
+							    muxpack_len++;
+						if ((!!
+						     (ttusb->
+						      muxpack[0] & 0x20)) ^
+						    !!(ttusb->
+						       muxpack[1] & 1))
+							ttusb->
+							    muxpack_len++;
+						ttusb->muxpack_len += 4;
+					} else if (ttusb->muxpack[0] ==
+						   0x47)
+						ttusb->muxpack_len =
+						    188 + 4;
+					else if (ttusb->muxpack[0] == 0x00)
+						ttusb->muxpack_len =
+						    ttusb->muxpack[1] + 2 +
+						    4;
+					else {
+						dprintk
+						    ("%s: invalid state: first byte is %x\n",
+						     __FUNCTION__,
+						     ttusb->muxpack[0]);
+						ttusb->mux_state = 0;
+					}
+				}
+
+			/**
+			 * if length is valid and we reached the end:
+			 * goto next muxpack
+			 */
+				if ((ttusb->muxpack_ptr >= 2) &&
+				    (ttusb->muxpack_ptr ==
+				     ttusb->muxpack_len)) {
+					ttusb_process_muxpack(ttusb,
+							      ttusb->
+							      muxpack,
+							      ttusb->
+							      muxpack_ptr);
+					ttusb->muxpack_ptr = 0;
+					/* maximum bytes, until we know the length */
+					ttusb->muxpack_len = 2;
+
+				/**
+				 * no muxpacks left?
+				 * return to search-sync state
+				 */
+					if (!ttusb->mux_npacks--) {
+						ttusb->mux_state = 0;
+						break;
+					}
+				}
+				break;
+			}
+		default:
+			BUG();
+			break;
+		}
+	}
+}
+
+static void ttusb_iso_irq(struct urb *urb, struct pt_regs *ptregs)
+{
+	struct ttusb *ttusb = urb->context;
+
+	if (!ttusb->iso_streaming)
+		return;
+
+#if 0
+	printk("%s: status %d, errcount == %d, length == %i\n",
+	       __FUNCTION__,
+	       urb->status, urb->error_count, urb->actual_length);
+#endif
+
+	if (!urb->status) {
+		int i;
+		for (i = 0; i < urb->number_of_packets; ++i) {
+			struct usb_iso_packet_descriptor *d;
+			u8 *data;
+			int len;
+			numpkt++;
+			if ((jiffies - lastj) >= HZ) {
+#if DEBUG > 2
+				printk
+				    ("frames/s: %d (ts: %d, stuff %d, sec: %d, invalid: %d, all: %d)\n",
+				     numpkt * HZ / (jiffies - lastj),
+				     numts, numstuff, numsec, numinvalid,
+				     numts + numstuff + numsec +
+				     numinvalid);
+#endif
+				numts = numstuff = numsec = numinvalid = 0;
+				lastj = jiffies;
+				numpkt = 0;
+			}
+			d = &urb->iso_frame_desc[i];
+			data = urb->transfer_buffer + d->offset;
+			len = d->actual_length;
+			d->actual_length = 0;
+			d->status = 0;
+			ttusb_process_frame(ttusb, data, len);
+		}
+	}
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static void ttusb_free_iso_urbs(struct ttusb *ttusb)
+{
+	int i;
+
+	for (i = 0; i < ISO_BUF_COUNT; i++)
+		if (ttusb->iso_urb[i])
+			usb_free_urb(ttusb->iso_urb[i]);
+
+	pci_free_consistent(NULL,
+			    ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF *
+			    ISO_BUF_COUNT, ttusb->iso_buffer,
+			    ttusb->iso_dma_handle);
+}
+
+static int ttusb_alloc_iso_urbs(struct ttusb *ttusb)
+{
+	int i;
+
+	ttusb->iso_buffer = pci_alloc_consistent(NULL,
+						 ISO_FRAME_SIZE *
+						 FRAMES_PER_ISO_BUF *
+						 ISO_BUF_COUNT,
+						 &ttusb->iso_dma_handle);
+
+	memset(ttusb->iso_buffer, 0,
+	       ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * ISO_BUF_COUNT);
+
+	for (i = 0; i < ISO_BUF_COUNT; i++) {
+		struct urb *urb;
+
+		if (!
+		    (urb =
+		     usb_alloc_urb(FRAMES_PER_ISO_BUF, GFP_ATOMIC))) {
+			ttusb_free_iso_urbs(ttusb);
+			return -ENOMEM;
+		}
+
+		ttusb->iso_urb[i] = urb;
+	}
+
+	return 0;
+}
+
+static void ttusb_stop_iso_xfer(struct ttusb *ttusb)
+{
+	int i;
+
+	for (i = 0; i < ISO_BUF_COUNT; i++)
+		usb_kill_urb(ttusb->iso_urb[i]);
+
+	ttusb->iso_streaming = 0;
+}
+
+static int ttusb_start_iso_xfer(struct ttusb *ttusb)
+{
+	int i, j, err, buffer_offset = 0;
+
+	if (ttusb->iso_streaming) {
+		printk("%s: iso xfer already running!\n", __FUNCTION__);
+		return 0;
+	}
+
+	ttusb->cc = -1;
+	ttusb->insync = 0;
+	ttusb->mux_state = 0;
+
+	for (i = 0; i < ISO_BUF_COUNT; i++) {
+		int frame_offset = 0;
+		struct urb *urb = ttusb->iso_urb[i];
+
+		urb->dev = ttusb->dev;
+		urb->context = ttusb;
+		urb->complete = ttusb_iso_irq;
+		urb->pipe = ttusb->isoc_in_pipe;
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->interval = 1;
+		urb->number_of_packets = FRAMES_PER_ISO_BUF;
+		urb->transfer_buffer_length =
+		    ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF;
+		urb->transfer_buffer = ttusb->iso_buffer + buffer_offset;
+		buffer_offset += ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF;
+
+		for (j = 0; j < FRAMES_PER_ISO_BUF; j++) {
+			urb->iso_frame_desc[j].offset = frame_offset;
+			urb->iso_frame_desc[j].length = ISO_FRAME_SIZE;
+			frame_offset += ISO_FRAME_SIZE;
+		}
+	}
+
+	for (i = 0; i < ISO_BUF_COUNT; i++) {
+		if ((err = usb_submit_urb(ttusb->iso_urb[i], GFP_ATOMIC))) {
+			ttusb_stop_iso_xfer(ttusb);
+			printk
+			    ("%s: failed urb submission (%i: err = %i)!\n",
+			     __FUNCTION__, i, err);
+			return err;
+		}
+	}
+
+	ttusb->iso_streaming = 1;
+
+	return 0;
+}
+
+#ifdef TTUSB_HWSECTIONS
+static void ttusb_handle_ts_data(struct dvb_demux_feed *dvbdmxfeed, const u8 * data,
+			  int len)
+{
+	dvbdmxfeed->cb.ts(data, len, 0, 0, &dvbdmxfeed->feed.ts, 0);
+}
+
+static void ttusb_handle_sec_data(struct dvb_demux_feed *dvbdmxfeed, const u8 * data,
+			   int len)
+{
+//      struct dvb_demux_feed *dvbdmxfeed = channel->dvbdmxfeed;
+#error TODO: handle ugly stuff
+//      dvbdmxfeed->cb.sec(data, len, 0, 0, &dvbdmxfeed->feed.sec, 0);
+}
+#endif
+
+static int ttusb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct ttusb *ttusb = (struct ttusb *) dvbdmxfeed->demux;
+	int feed_type = 1;
+
+	dprintk("ttusb_start_feed\n");
+
+	switch (dvbdmxfeed->type) {
+	case DMX_TYPE_TS:
+		break;
+	case DMX_TYPE_SEC:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (dvbdmxfeed->type == DMX_TYPE_TS) {
+		switch (dvbdmxfeed->pes_type) {
+		case DMX_TS_PES_VIDEO:
+		case DMX_TS_PES_AUDIO:
+		case DMX_TS_PES_TELETEXT:
+		case DMX_TS_PES_PCR:
+		case DMX_TS_PES_OTHER:
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+#ifdef TTUSB_HWSECTIONS
+#error TODO: allocate filters
+	if (dvbdmxfeed->type == DMX_TYPE_TS) {
+		feed_type = 1;
+	} else if (dvbdmxfeed->type == DMX_TYPE_SEC) {
+		feed_type = 2;
+	}
+#endif
+
+	ttusb_set_channel(ttusb, dvbdmxfeed->index, feed_type, dvbdmxfeed->pid);
+
+	if (0 == ttusb->running_feed_count++)
+		ttusb_start_iso_xfer(ttusb);
+
+	return 0;
+}
+
+static int ttusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct ttusb *ttusb = (struct ttusb *) dvbdmxfeed->demux;
+
+	ttusb_del_channel(ttusb, dvbdmxfeed->index);
+
+	if (--ttusb->running_feed_count == 0)
+		ttusb_stop_iso_xfer(ttusb);
+
+	return 0;
+}
+
+static int ttusb_setup_interfaces(struct ttusb *ttusb)
+{
+	usb_set_interface(ttusb->dev, 1, 1);
+
+	ttusb->bulk_out_pipe = usb_sndbulkpipe(ttusb->dev, 1);
+	ttusb->bulk_in_pipe = usb_rcvbulkpipe(ttusb->dev, 1);
+	ttusb->isoc_in_pipe = usb_rcvisocpipe(ttusb->dev, 2);
+
+	return 0;
+}
+
+#if 0
+static u8 stc_firmware[8192];
+
+static int stc_open(struct inode *inode, struct file *file)
+{
+	struct ttusb *ttusb = file->private_data;
+	int addr;
+
+	for (addr = 0; addr < 8192; addr += 16) {
+		u8 snd_buf[2] = { addr >> 8, addr & 0xFF };
+		ttusb_i2c_msg(ttusb, 0x50, snd_buf, 2, stc_firmware + addr,
+			      16);
+	}
+
+	return 0;
+}
+
+static ssize_t stc_read(struct file *file, char *buf, size_t count,
+		 loff_t * offset)
+{
+	int tc = count;
+
+	if ((tc + *offset) > 8192)
+		tc = 8192 - *offset;
+
+	if (tc < 0)
+		return 0;
+
+	if (copy_to_user(buf, stc_firmware + *offset, tc))
+		return -EFAULT;
+
+	*offset += tc;
+
+	return tc;
+}
+
+static int stc_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static struct file_operations stc_fops = {
+	.owner = THIS_MODULE,
+	.read = stc_read,
+	.open = stc_open,
+	.release = stc_release,
+};
+#endif
+
+static u32 functionality(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+
+
+static int alps_tdmb7_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+	u8 data[4];
+	struct i2c_msg msg = {.addr=0x61, .flags=0, .buf=data, .len=sizeof(data) };
+	u32 div;
+
+	div = (params->frequency + 36166667) / 166667;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = ((div >> 10) & 0x60) | 0x85;
+	data[3] = params->frequency < 592000000 ? 0x40 : 0x80;
+
+	if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO;
+	return 0;
+}
+
+struct cx22700_config alps_tdmb7_config = {
+	.demod_address = 0x43,
+	.pll_set = alps_tdmb7_pll_set,
+};
+
+
+
+
+
+static int philips_tdm1316l_pll_init(struct dvb_frontend* fe)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+	static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+	static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
+	struct i2c_msg tuner_msg = { .addr=0x60, .flags=0, .buf=td1316_init, .len=sizeof(td1316_init) };
+
+	// setup PLL configuration
+	if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) return -EIO;
+	msleep(1);
+
+	// disable the mc44BC374c (do not check for errors)
+	tuner_msg.addr = 0x65;
+	tuner_msg.buf = disable_mc44BC374c;
+	tuner_msg.len = sizeof(disable_mc44BC374c);
+	if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) {
+		i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1);
+	}
+
+	return 0;
+}
+
+static int philips_tdm1316l_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+	u8 tuner_buf[4];
+	struct i2c_msg tuner_msg = {.addr=0x60, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) };
+	int tuner_frequency = 0;
+	u8 band, cp, filter;
+
+	// determine charge pump
+	tuner_frequency = params->frequency + 36130000;
+	if (tuner_frequency < 87000000) return -EINVAL;
+	else if (tuner_frequency < 130000000) cp = 3;
+	else if (tuner_frequency < 160000000) cp = 5;
+	else if (tuner_frequency < 200000000) cp = 6;
+	else if (tuner_frequency < 290000000) cp = 3;
+	else if (tuner_frequency < 420000000) cp = 5;
+	else if (tuner_frequency < 480000000) cp = 6;
+	else if (tuner_frequency < 620000000) cp = 3;
+	else if (tuner_frequency < 830000000) cp = 5;
+	else if (tuner_frequency < 895000000) cp = 7;
+	else return -EINVAL;
+
+	// determine band
+	if (params->frequency < 49000000) return -EINVAL;
+	else if (params->frequency < 159000000) band = 1;
+	else if (params->frequency < 444000000) band = 2;
+	else if (params->frequency < 861000000) band = 4;
+	else return -EINVAL;
+
+	// setup PLL filter
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		tda1004x_write_byte(fe, 0x0C, 0);
+		filter = 0;
+		break;
+
+	case BANDWIDTH_7_MHZ:
+		tda1004x_write_byte(fe, 0x0C, 0);
+		filter = 0;
+		break;
+
+	case BANDWIDTH_8_MHZ:
+		tda1004x_write_byte(fe, 0x0C, 0xFF);
+		filter = 1;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	// calculate divisor
+	// ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
+	tuner_frequency = (((params->frequency / 1000) * 6) + 217280) / 1000;
+
+	// setup tuner buffer
+	tuner_buf[0] = tuner_frequency >> 8;
+	tuner_buf[1] = tuner_frequency & 0xff;
+	tuner_buf[2] = 0xca;
+	tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+	if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1)
+		return -EIO;
+
+	msleep(1);
+	return 0;
+}
+
+static int philips_tdm1316l_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+
+	return request_firmware(fw, name, &ttusb->dev->dev);
+}
+
+static struct tda1004x_config philips_tdm1316l_config = {
+
+	.demod_address = 0x8,
+	.invert = 1,
+	.invert_oclk = 0,
+	.pll_init = philips_tdm1316l_pll_init,
+	.pll_set = philips_tdm1316l_pll_set,
+	.request_firmware = philips_tdm1316l_request_firmware,
+};
+
+static u8 alps_bsbe1_inittab[] = {
+        0x01, 0x15,
+        0x02, 0x30,
+        0x03, 0x00,
+        0x04, 0x7d,             /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+        0x05, 0x35,             /* I2CT = 0, SCLT = 1, SDAT = 1 */
+        0x06, 0x40,             /* DAC not used, set to high impendance mode */
+        0x07, 0x00,             /* DAC LSB */
+        0x08, 0x40,             /* DiSEqC off, LNB power on OP2/LOCK pin on */
+        0x09, 0x00,             /* FIFO */
+        0x0c, 0x51,             /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+        0x0d, 0x82,             /* DC offset compensation = ON, beta_agc1 = 2 */
+        0x0e, 0x23,             /* alpha_tmg = 2, beta_tmg = 3 */
+        0x10, 0x3f,             // AGC2  0x3d
+        0x11, 0x84,
+        0x12, 0xb5,             // Lock detect: -64  Carrier freq detect:on
+        0x15, 0xc9,             // lock detector threshold
+        0x16, 0x00,
+        0x17, 0x00,
+        0x18, 0x00,
+        0x19, 0x00,
+        0x1a, 0x00,
+        0x1f, 0x50,
+        0x20, 0x00,
+        0x21, 0x00,
+        0x22, 0x00,
+        0x23, 0x00,
+        0x28, 0x00,             // out imp: normal  out type: parallel FEC mode:0
+        0x29, 0x1e,             // 1/2 threshold
+        0x2a, 0x14,             // 2/3 threshold
+        0x2b, 0x0f,             // 3/4 threshold
+        0x2c, 0x09,             // 5/6 threshold
+        0x2d, 0x05,             // 7/8 threshold
+        0x2e, 0x01,
+        0x31, 0x1f,             // test all FECs
+        0x32, 0x19,             // viterbi and synchro search
+        0x33, 0xfc,             // rs control
+        0x34, 0x93,             // error control
+        0x0f, 0x92,
+        0xff, 0xff
+};
+
+static u8 alps_bsru6_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x30,
+	0x03, 0x00,
+	0x04, 0x7d,		/* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */
+	0x05, 0x35,		/* I2CT = 0, SCLT = 1, SDAT = 1 */
+	0x06, 0x40,		/* DAC not used, set to high impendance mode */
+	0x07, 0x00,		/* DAC LSB */
+	0x08, 0x40,		/* DiSEqC off, LNB power on OP2/LOCK pin on */
+	0x09, 0x00,		/* FIFO */
+	0x0c, 0x51,		/* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
+	0x0d, 0x82,		/* DC offset compensation = ON, beta_agc1 = 2 */
+	0x0e, 0x23,		/* alpha_tmg = 2, beta_tmg = 3 */
+	0x10, 0x3f,		// AGC2  0x3d
+	0x11, 0x84,
+	0x12, 0xb5,		// Lock detect: -64  Carrier freq detect:on
+	0x15, 0xc9,		// lock detector threshold
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1f, 0x50,
+	0x20, 0x00,
+	0x21, 0x00,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x28, 0x00,		// out imp: normal  out type: parallel FEC mode:0
+	0x29, 0x1e,		// 1/2 threshold
+	0x2a, 0x14,		// 2/3 threshold
+	0x2b, 0x0f,		// 3/4 threshold
+	0x2c, 0x09,		// 5/6 threshold
+	0x2d, 0x05,		// 7/8 threshold
+	0x2e, 0x01,
+	0x31, 0x1f,		// test all FECs
+	0x32, 0x19,		// viterbi and synchro search
+	0x33, 0xfc,		// rs control
+	0x34, 0x93,		// error control
+	0x0f, 0x52,
+	0xff, 0xff
+};
+
+static int alps_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
+{
+	u8 aclk = 0;
+	u8 bclk = 0;
+
+	if (srate < 1500000) {
+		aclk = 0xb7;
+		bclk = 0x47;
+	} else if (srate < 3000000) {
+		aclk = 0xb7;
+		bclk = 0x4b;
+	} else if (srate < 7000000) {
+		aclk = 0xb7;
+		bclk = 0x4f;
+	} else if (srate < 14000000) {
+		aclk = 0xb7;
+		bclk = 0x53;
+	} else if (srate < 30000000) {
+		aclk = 0xb6;
+		bclk = 0x53;
+	} else if (srate < 45000000) {
+		aclk = 0xb4;
+		bclk = 0x51;
+	}
+
+	stv0299_writereg(fe, 0x13, aclk);
+	stv0299_writereg(fe, 0x14, bclk);
+	stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+	stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+	stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
+
+	return 0;
+}
+
+static int philips_tsa5059_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+	if ((params->frequency < 950000) || (params->frequency > 2150000))
+		return -EINVAL;
+
+	div = (params->frequency + (125 - 1)) / 125;	// round correctly
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4;
+	buf[3] = 0xC4;
+
+	if (params->frequency > 1530000)
+		buf[3] = 0xC0;
+
+	/* BSBE1 wants XCE bit set */
+	if (ttusb->revision == TTUSB_REV_2_2)
+		buf[3] |= 0x20;
+
+	if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static struct stv0299_config alps_stv0299_config = {
+	.demod_address = 0x68,
+	.inittab = alps_bsru6_inittab,
+	.mclk = 88000000UL,
+	.invert = 1,
+	.enhanced_tuning = 0,
+	.skip_reinit = 0,
+	.lock_output = STV0229_LOCKOUTPUT_1,
+	.volt13_op0_op1 = STV0299_VOLT13_OP1,
+	.min_delay_ms = 100,
+	.set_symbol_rate = alps_stv0299_set_symbol_rate,
+	.pll_set = philips_tsa5059_pll_set,
+};
+
+static int ttusb_novas_grundig_29504_491_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv;
+	u8 buf[4];
+	u32 div;
+	struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };
+
+        div = params->frequency / 125;
+
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = div & 0xff;
+	buf[2] = 0x8e;
+	buf[3] = 0x00;
+
+	if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+static struct tda8083_config ttusb_novas_grundig_29504_491_config = {
+
+	.demod_address = 0x68,
+	.pll_set = ttusb_novas_grundig_29504_491_pll_set,
+};
+
+
+
+static void frontend_init(struct ttusb* ttusb)
+{
+	switch(le16_to_cpu(ttusb->dev->descriptor.idProduct)) {
+	case 0x1003: // Hauppauge/TT Nova-USB-S budget (stv0299/ALPS BSRU6|BSBE1(tsa5059))
+		// try the stv0299 based first
+		ttusb->fe = stv0299_attach(&alps_stv0299_config, &ttusb->i2c_adap);
+		if (ttusb->fe != NULL) {
+			if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1
+				alps_stv0299_config.inittab = alps_bsbe1_inittab;
+				ttusb->fe->ops->set_voltage = lnbp21_set_voltage;
+			} else { // ALPS BSRU6
+				ttusb->fe->ops->set_voltage = ttusb_set_voltage;
+			}
+			break;
+		}
+
+		// Grundig 29504-491
+		ttusb->fe = tda8083_attach(&ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap);
+		if (ttusb->fe != NULL) {
+			ttusb->fe->ops->set_voltage = ttusb_set_voltage;
+			break;
+		}
+
+		break;
+
+	case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??))
+		// try the ALPS TDMB7 first
+		ttusb->fe = cx22700_attach(&alps_tdmb7_config, &ttusb->i2c_adap);
+		if (ttusb->fe != NULL)
+			break;
+
+		// Philips td1316
+		ttusb->fe = tda10046_attach(&philips_tdm1316l_config, &ttusb->i2c_adap);
+		if (ttusb->fe != NULL)
+			break;
+		break;
+	}
+
+	if (ttusb->fe == NULL) {
+		printk("dvb-ttusb-budget: A frontend driver was not found for device %04x/%04x\n",
+		       le16_to_cpu(ttusb->dev->descriptor.idVendor),
+		       le16_to_cpu(ttusb->dev->descriptor.idProduct));
+	} else {
+		if (dvb_register_frontend(ttusb->adapter, ttusb->fe)) {
+			printk("dvb-ttusb-budget: Frontend registration failed!\n");
+			if (ttusb->fe->ops->release)
+				ttusb->fe->ops->release(ttusb->fe);
+			ttusb->fe = NULL;
+		}
+	}
+}
+
+
+
+static struct i2c_algorithm ttusb_dec_algo = {
+	.name		= "ttusb dec i2c algorithm",
+	.id		= I2C_ALGO_BIT,
+	.master_xfer	= master_xfer,
+	.functionality	= functionality,
+};
+
+static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *udev;
+	struct ttusb *ttusb;
+	int result;
+
+	dprintk("%s: TTUSB DVB connected\n", __FUNCTION__);
+
+	udev = interface_to_usbdev(intf);
+
+        if (intf->altsetting->desc.bInterfaceNumber != 1) return -ENODEV;
+
+	if (!(ttusb = kmalloc(sizeof(struct ttusb), GFP_KERNEL)))
+		return -ENOMEM;
+
+	memset(ttusb, 0, sizeof(struct ttusb));
+
+	ttusb->dev = udev;
+	ttusb->c = 0;
+	ttusb->mux_state = 0;
+	sema_init(&ttusb->semi2c, 0);
+	sema_init(&ttusb->semusb, 1);
+
+	ttusb_setup_interfaces(ttusb);
+
+	ttusb_alloc_iso_urbs(ttusb);
+	if (ttusb_init_controller(ttusb))
+		printk("ttusb_init_controller: error\n");
+
+	up(&ttusb->semi2c);
+
+	dvb_register_adapter(&ttusb->adapter, "Technotrend/Hauppauge Nova-USB", THIS_MODULE);
+	ttusb->adapter->priv = ttusb;
+
+	/* i2c */
+	memset(&ttusb->i2c_adap, 0, sizeof(struct i2c_adapter));
+	strcpy(ttusb->i2c_adap.name, "TTUSB DEC");
+
+	i2c_set_adapdata(&ttusb->i2c_adap, ttusb);
+
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+	ttusb->i2c_adap.class		  = I2C_ADAP_CLASS_TV_DIGITAL;
+#else
+	ttusb->i2c_adap.class		  = I2C_CLASS_TV_DIGITAL;
+#endif
+	ttusb->i2c_adap.algo              = &ttusb_dec_algo;
+	ttusb->i2c_adap.algo_data         = NULL;
+	ttusb->i2c_adap.id                = I2C_ALGO_BIT;
+
+	result = i2c_add_adapter(&ttusb->i2c_adap);
+	if (result) {
+		dvb_unregister_adapter (ttusb->adapter);
+		return result;
+	}
+
+	memset(&ttusb->dvb_demux, 0, sizeof(ttusb->dvb_demux));
+
+	ttusb->dvb_demux.dmx.capabilities =
+	    DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+	ttusb->dvb_demux.priv = NULL;
+#ifdef TTUSB_HWSECTIONS
+	ttusb->dvb_demux.filternum = TTUSB_MAXFILTER;
+#else
+	ttusb->dvb_demux.filternum = 32;
+#endif
+	ttusb->dvb_demux.feednum = TTUSB_MAXCHANNEL;
+	ttusb->dvb_demux.start_feed = ttusb_start_feed;
+	ttusb->dvb_demux.stop_feed = ttusb_stop_feed;
+	ttusb->dvb_demux.write_to_decoder = NULL;
+
+	if ((result = dvb_dmx_init(&ttusb->dvb_demux)) < 0) {
+		printk("ttusb_dvb: dvb_dmx_init failed (errno = %d)\n", result);
+		i2c_del_adapter(&ttusb->i2c_adap);
+		dvb_unregister_adapter (ttusb->adapter);
+		return -ENODEV;
+	}
+//FIXME dmxdev (nur WAS?)
+	ttusb->dmxdev.filternum = ttusb->dvb_demux.filternum;
+	ttusb->dmxdev.demux = &ttusb->dvb_demux.dmx;
+	ttusb->dmxdev.capabilities = 0;
+
+	if ((result = dvb_dmxdev_init(&ttusb->dmxdev, ttusb->adapter)) < 0) {
+		printk("ttusb_dvb: dvb_dmxdev_init failed (errno = %d)\n",
+		       result);
+		dvb_dmx_release(&ttusb->dvb_demux);
+		i2c_del_adapter(&ttusb->i2c_adap);
+		dvb_unregister_adapter (ttusb->adapter);
+		return -ENODEV;
+	}
+
+	if (dvb_net_init(ttusb->adapter, &ttusb->dvbnet, &ttusb->dvb_demux.dmx)) {
+		printk("ttusb_dvb: dvb_net_init failed!\n");
+		dvb_dmxdev_release(&ttusb->dmxdev);
+		dvb_dmx_release(&ttusb->dvb_demux);
+		i2c_del_adapter(&ttusb->i2c_adap);
+		dvb_unregister_adapter (ttusb->adapter);
+		return -ENODEV;
+	}
+
+#if 0
+	ttusb->stc_devfs_handle =
+	    devfs_register(ttusb->adapter->devfs_handle, TTUSB_BUDGET_NAME,
+			   DEVFS_FL_DEFAULT, 0, 192,
+			   S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP
+			   | S_IROTH | S_IWOTH, &stc_fops, ttusb);
+#endif
+	usb_set_intfdata(intf, (void *) ttusb);
+
+	frontend_init(ttusb);
+
+	return 0;
+}
+
+static void ttusb_disconnect(struct usb_interface *intf)
+{
+	struct ttusb *ttusb = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	ttusb->disconnecting = 1;
+
+	ttusb_stop_iso_xfer(ttusb);
+
+	ttusb->dvb_demux.dmx.close(&ttusb->dvb_demux.dmx);
+	dvb_net_release(&ttusb->dvbnet);
+	dvb_dmxdev_release(&ttusb->dmxdev);
+	dvb_dmx_release(&ttusb->dvb_demux);
+	if (ttusb->fe != NULL) dvb_unregister_frontend(ttusb->fe);
+	i2c_del_adapter(&ttusb->i2c_adap);
+	dvb_unregister_adapter(ttusb->adapter);
+
+	ttusb_free_iso_urbs(ttusb);
+
+	kfree(ttusb);
+
+	dprintk("%s: TTUSB DVB disconnected\n", __FUNCTION__);
+}
+
+static struct usb_device_id ttusb_table[] = {
+	{USB_DEVICE(0xb48, 0x1003)},
+/*	{USB_DEVICE(0xb48, 0x1004)},UNDEFINED HARDWARE - mail linuxtv.org list*/	/* to be confirmed ????  */
+	{USB_DEVICE(0xb48, 0x1005)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, ttusb_table);
+
+static struct usb_driver ttusb_driver = {
+      .name		= "Technotrend/Hauppauge USB-Nova",
+      .probe		= ttusb_probe,
+      .disconnect	= ttusb_disconnect,
+      .id_table		= ttusb_table,
+};
+
+static int __init ttusb_init(void)
+{
+	int err;
+
+	if ((err = usb_register(&ttusb_driver)) < 0) {
+		printk("%s: usb_register failed! Error number %d",
+		       __FILE__, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void __exit ttusb_exit(void)
+{
+	usb_deregister(&ttusb_driver);
+}
+
+module_init(ttusb_init);
+module_exit(ttusb_exit);
+
+MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>");
+MODULE_DESCRIPTION("TTUSB DVB Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h b/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h
new file mode 100644
index 0000000..95ee799
--- /dev/null
+++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-dspbootcode.h
@@ -0,0 +1,1644 @@
+
+#include <asm/types.h>
+
+static u8 dsp_bootcode [] = {
+	0x08, 0xaa, 0x00, 0x18, 0x00, 0x03, 0x08, 0x00, 
+	0x00, 0x10, 0x00, 0x00, 0x01, 0x80, 0x18, 0x5f, 
+	0x00, 0x00, 0x01, 0x80, 0x77, 0x18, 0x2a, 0xeb, 
+	0x6b, 0xf8, 0x00, 0x18, 0x03, 0xff, 0x68, 0xf8, 
+	0x00, 0x18, 0xff, 0xfe, 0xf7, 0xb8, 0xf7, 0xbe, 
+	0xf6, 0xb9, 0xf4, 0xa0, 0xf6, 0xb7, 0xf6, 0xb5, 
+	0xf6, 0xb6, 0xf0, 0x20, 0x19, 0xdf, 0xf1, 0x00, 
+	0x00, 0x01, 0xf8, 0x4d, 0x01, 0xab, 0xf6, 0xb8, 
+	0xf0, 0x20, 0x19, 0xdf, 0xf0, 0x73, 0x01, 0xa5, 
+	0x7e, 0xf8, 0x00, 0x12, 0xf0, 0x00, 0x00, 0x01, 
+	0x47, 0xf8, 0x00, 0x11, 0x7e, 0x92, 0x00, 0xf8, 
+	0x00, 0x11, 0xf0, 0x00, 0x00, 0x01, 0x7e, 0xf8, 
+	0x00, 0x11, 0xf0, 0x00, 0x00, 0x01, 0x6c, 0x89, 
+	0x01, 0x9a, 0xf7, 0xb8, 0xee, 0xfc, 0xf0, 0x20, 
+	0xff, 0xff, 0xf1, 0x00, 0x00, 0x01, 0xf8, 0x4d, 
+	0x01, 0xbf, 0xf2, 0x73, 0x01, 0xb9, 0x4e, 0x02, 
+	0xf4, 0x95, 0xf5, 0xe3, 0x56, 0x02, 0x7e, 0x00, 
+	0x11, 0x00, 0xfa, 0x4c, 0x01, 0xb7, 0x6b, 0x03, 
+	0x00, 0x01, 0xf6, 0xb8, 0xee, 0x04, 0xf0, 0x74, 
+	0x0d, 0xa7, 0xf0, 0x74, 0x01, 0xc5, 0x4a, 0x11, 
+	0x4a, 0x16, 0x72, 0x11, 0x2a, 0xe6, 0x10, 0xf8, 
+	0x00, 0x11, 0xfa, 0x45, 0x01, 0xdb, 0xf4, 0x95, 
+	0xee, 0xff, 0x48, 0x11, 0xf0, 0x00, 0x2a, 0xc6, 
+	0x88, 0x16, 0xf4, 0x95, 0xf4, 0x95, 0x10, 0xee, 
+	0xff, 0xff, 0xf4, 0xe3, 0x6c, 0xe9, 0xff, 0xff, 
+	0x01, 0xd5, 0x10, 0xf8, 0x2a, 0xe7, 0xf8, 0x45, 
+	0x01, 0xe2, 0x10, 0xf8, 0x2a, 0xe7, 0xf4, 0xe3, 
+	0xf0, 0x74, 0x01, 0xff, 0xee, 0x01, 0x8a, 0x16, 
+	0x8a, 0x11, 0xfc, 0x00, 0xf7, 0xb8, 0xe9, 0x20, 
+	0x4a, 0x11, 0x09, 0xf8, 0x2a, 0xe6, 0xf8, 0x4e, 
+	0x01, 0xf3, 0xf2, 0x73, 0x01, 0xfd, 0xf4, 0x95, 
+	0xe8, 0x01, 0x72, 0x11, 0x2a, 0xe6, 0x49, 0x11, 
+	0x80, 0xe1, 0x2a, 0xc6, 0xf3, 0x00, 0x00, 0x01, 
+	0xe8, 0x00, 0x81, 0xf8, 0x2a, 0xe6, 0x8a, 0x11, 
+	0xfc, 0x00, 0xf4, 0x95, 0xf0, 0x73, 0x02, 0x00, 
+	0x10, 0xf8, 0x2a, 0x0f, 0xfc, 0x00, 0x4a, 0x11, 
+	0xf0, 0x74, 0x02, 0x02, 0x80, 0xf8, 0x2a, 0x10, 
+	0x73, 0x08, 0x00, 0x09, 0x40, 0xf8, 0x2a, 0x15, 
+	0x82, 0xf8, 0x00, 0x11, 0xf4, 0x95, 0x77, 0x10, 
+	0x03, 0xe8, 0xf5, 0xa9, 0xf8, 0x30, 0x02, 0x21, 
+	0x71, 0xf8, 0x2a, 0x10, 0x2a, 0x15, 0x56, 0xf8, 
+	0x2a, 0x0c, 0xf0, 0xe3, 0x4e, 0xf8, 0x2a, 0x16, 
+	0xe8, 0x00, 0x4e, 0xf8, 0x2a, 0x0c, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x06, 0x4a, 0x07, 0x4a, 0x1d, 
+	0x68, 0xf8, 0x00, 0x07, 0x7d, 0x3f, 0x69, 0xf8, 
+	0x00, 0x07, 0x40, 0x00, 0x68, 0xf8, 0x00, 0x1d, 
+	0xff, 0xfc, 0x6b, 0xf8, 0x2a, 0x0f, 0x00, 0x01, 
+	0x8a, 0x1d, 0x8a, 0x07, 0x8a, 0x06, 0xf4, 0xeb, 
+	0xee, 0xfd, 0x76, 0xf8, 0x2a, 0x0f, 0x00, 0x00, 
+	0x76, 0x00, 0x00, 0x00, 0xfb, 0x80, 0x19, 0x4c, 
+	0xf4, 0x95, 0xe8, 0x00, 0x80, 0xf8, 0x2a, 0x11, 
+	0xf9, 0x80, 0x19, 0x07, 0x80, 0xf8, 0x2a, 0x0e, 
+	0xf9, 0x80, 0x16, 0x66, 0x76, 0x00, 0x2a, 0x12, 
+	0x10, 0xf8, 0x2a, 0x11, 0xf9, 0x80, 0x18, 0xe3, 
+	0x10, 0xf8, 0x2a, 0x0e, 0xf9, 0x80, 0x16, 0x66, 
+	0x10, 0xf8, 0x2a, 0x0e, 0xf9, 0x80, 0x16, 0x87, 
+	0xee, 0x03, 0xfc, 0x00, 0x4a, 0x11, 0xf6, 0xb8, 
+	0xf4, 0x95, 0xf0, 0x20, 0x80, 0x00, 0x11, 0xf8, 
+	0x2a, 0x5a, 0xf8, 0x4d, 0x02, 0x93, 0x11, 0xf8, 
+	0x2a, 0x9f, 0xf8, 0x4c, 0x02, 0x7c, 0x77, 0x12, 
+	0x2a, 0x39, 0x49, 0x12, 0x01, 0xf8, 0x2a, 0x9f, 
+	0x89, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x71, 0x81, 
+	0x00, 0x11, 0x6c, 0xe1, 0xff, 0xab, 0x02, 0x93, 
+	0x6b, 0xf8, 0x2a, 0x9f, 0x00, 0x01, 0xe9, 0x05, 
+	0x01, 0xe2, 0x00, 0x03, 0x81, 0xf8, 0x2a, 0xa0, 
+	0xf0, 0x73, 0x02, 0x95, 0x72, 0x11, 0x2a, 0x9f, 
+	0xf4, 0x95, 0x10, 0xe1, 0x2a, 0x39, 0x6b, 0xf8, 
+	0x2a, 0x9f, 0x00, 0x01, 0x11, 0xf8, 0x2a, 0x9f, 
+	0x09, 0xf8, 0x2a, 0xa0, 0xf8, 0x4c, 0x02, 0x93, 
+	0x76, 0xf8, 0x2a, 0x5a, 0x00, 0x00, 0x76, 0xf8, 
+	0x2a, 0x9f, 0x00, 0x00, 0x76, 0xf8, 0x2a, 0xa0, 
+	0x00, 0x00, 0x88, 0x11, 0xf4, 0x95, 0x48, 0x11, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfe, 
+	0x10, 0xf8, 0x2a, 0x5a, 0xf8, 0x44, 0x02, 0xb2, 
+	0x76, 0xf8, 0x2a, 0x5a, 0x00, 0x01, 0xf0, 0x74, 
+	0x02, 0x58, 0x88, 0x11, 0xf4, 0x95, 0x77, 0x10, 
+	0x80, 0x00, 0xf4, 0xa9, 0xf8, 0x30, 0x02, 0xb2, 
+	0x48, 0x11, 0xf0, 0x30, 0x00, 0xff, 0x80, 0x00, 
+	0x10, 0xf8, 0x2a, 0x5b, 0xf9, 0x80, 0x18, 0xd6, 
+	0xee, 0x02, 0x8a, 0x11, 0xfc, 0x00, 0xf4, 0x95, 
+	0x4a, 0x08, 0x4a, 0x09, 0x4a, 0x0a, 0x4a, 0x0b, 
+	0x4a, 0x0c, 0x4a, 0x0d, 0x4a, 0x10, 0x4a, 0x11, 
+	0x4a, 0x12, 0x4a, 0x13, 0x4a, 0x14, 0x4a, 0x15, 
+	0x4a, 0x16, 0x4a, 0x17, 0x4a, 0x17, 0x4a, 0x19, 
+	0x4a, 0x0e, 0x4a, 0x06, 0x4a, 0x07, 0x4a, 0x1a, 
+	0x4a, 0x1d, 0x4a, 0x1b, 0x4a, 0x1c, 0x68, 0xf8, 
+	0x00, 0x07, 0x7d, 0x3f, 0x69, 0xf8, 0x00, 0x07, 
+	0x40, 0x00, 0x68, 0xf8, 0x00, 0x1d, 0xff, 0xfc, 
+	0x48, 0x18, 0x68, 0xf8, 0x00, 0x18, 0xff, 0xfe, 
+	0xf4, 0x95, 0xf4, 0x95, 0x4a, 0x08, 0xee, 0xfd, 
+	0xf0, 0x74, 0x02, 0x58, 0x88, 0x11, 0xf4, 0x95, 
+	0x77, 0x10, 0x80, 0x00, 0xf4, 0xa9, 0xf8, 0x30, 
+	0x02, 0xef, 0x48, 0x11, 0xf0, 0x30, 0x00, 0xff, 
+	0x80, 0x00, 0x10, 0xf8, 0x2a, 0x5b, 0xf9, 0x80, 
+	0x18, 0xd6, 0xee, 0x03, 0x8a, 0x18, 0xf4, 0x95, 
+	0x8a, 0x1c, 0x8a, 0x1b, 0x8a, 0x1d, 0x8a, 0x1a, 
+	0x8a, 0x07, 0x8a, 0x06, 0x8a, 0x0e, 0x8a, 0x19, 
+	0x8a, 0x17, 0x8a, 0x17, 0x8a, 0x16, 0x8a, 0x15, 
+	0x8a, 0x14, 0x8a, 0x13, 0x8a, 0x12, 0x8a, 0x11, 
+	0x8a, 0x10, 0x8a, 0x0d, 0x8a, 0x0c, 0x8a, 0x0b, 
+	0x8a, 0x0a, 0x8a, 0x09, 0x8a, 0x08, 0xf4, 0xeb, 
+	0x4a, 0x11, 0x77, 0x11, 0x2a, 0x39, 0x76, 0x81, 
+	0x00, 0x55, 0x77, 0x12, 0x2a, 0x18, 0x10, 0xe2, 
+	0x00, 0x01, 0x80, 0xe1, 0x00, 0x01, 0x10, 0xe2, 
+	0x00, 0x02, 0x80, 0xe1, 0x00, 0x02, 0x76, 0xe1, 
+	0x00, 0x03, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x04, 
+	0x00, 0xaa, 0xf0, 0x74, 0x02, 0x98, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0x88, 0x11, 0xf4, 0x95, 
+	0xf4, 0x95, 0x10, 0x81, 0x6f, 0xf8, 0x2a, 0x9e, 
+	0x0c, 0x88, 0xe8, 0xff, 0x18, 0xe1, 0x00, 0x01, 
+	0x1a, 0xf8, 0x2a, 0x9e, 0xf0, 0x30, 0x1f, 0xff, 
+	0x80, 0xf8, 0x2a, 0x9e, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0x77, 0x11, 0x2a, 0x39, 0x76, 0x81, 
+	0x00, 0x55, 0x77, 0x12, 0x2a, 0x18, 0x11, 0xe2, 
+	0x00, 0x01, 0x81, 0xe1, 0x00, 0x01, 0x11, 0xe2, 
+	0x00, 0x02, 0x81, 0xe1, 0x00, 0x02, 0x76, 0xe1, 
+	0x00, 0x03, 0x00, 0x02, 0x48, 0x08, 0x6f, 0xe1, 
+	0x00, 0x04, 0x0c, 0x98, 0xf0, 0x30, 0x00, 0xff, 
+	0x80, 0xe1, 0x00, 0x05, 0x76, 0xe1, 0x00, 0x06, 
+	0x00, 0xaa, 0xf0, 0x74, 0x02, 0x98, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0x77, 0x11, 0x2a, 0x39, 
+	0x76, 0x81, 0x00, 0x55, 0x77, 0x12, 0x2a, 0x18, 
+	0x10, 0xe2, 0x00, 0x01, 0x80, 0xe1, 0x00, 0x01, 
+	0x10, 0xe2, 0x00, 0x02, 0x80, 0xe1, 0x00, 0x02, 
+	0x76, 0xe1, 0x00, 0x03, 0x00, 0x04, 0x48, 0x11, 
+	0xf0, 0x00, 0x00, 0x04, 0x88, 0x12, 0xf4, 0x95, 
+	0x77, 0x13, 0x2a, 0x76, 0xe9, 0x00, 0xe5, 0x98, 
+	0xf3, 0x00, 0x00, 0x01, 0xf6, 0xb8, 0x48, 0x0b, 
+	0x08, 0xf8, 0x2a, 0x3c, 0xf8, 0x43, 0x03, 0x71, 
+	0x76, 0x82, 0x00, 0xaa, 0xf0, 0x74, 0x02, 0x98, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xee, 0xf0, 
+	0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x71, 0x81, 
+	0x00, 0x14, 0x71, 0xe1, 0x00, 0x01, 0x00, 0x15, 
+	0x49, 0x11, 0xf3, 0x00, 0x00, 0x02, 0x89, 0x11, 
+	0xe7, 0x82, 0x6d, 0xea, 0x00, 0x04, 0xe7, 0x83, 
+	0x6d, 0xeb, 0x00, 0x0a, 0x77, 0x1a, 0x00, 0x05, 
+	0xf0, 0x72, 0x03, 0xaa, 0x11, 0x81, 0xf2, 0xe8, 
+	0x80, 0x82, 0xe9, 0xff, 0x19, 0xe1, 0x00, 0x01, 
+	0xf1, 0xa0, 0x81, 0x92, 0x11, 0xe1, 0x00, 0x0c, 
+	0xf2, 0xe8, 0x80, 0x83, 0xe9, 0xff, 0x19, 0xe1, 
+	0x00, 0x0d, 0xf1, 0xa0, 0x81, 0x93, 0x6d, 0xe9, 
+	0x00, 0x02, 0x48, 0x18, 0x49, 0x18, 0x70, 0x00, 
+	0x00, 0x15, 0xf0, 0x00, 0x00, 0x04, 0xf3, 0x00, 
+	0x00, 0x0a, 0x80, 0x01, 0x81, 0x02, 0xf2, 0x74, 
+	0x0e, 0x54, 0xf4, 0x95, 0x48, 0x14, 0xee, 0x10, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xf0, 0x74, 
+	0x0c, 0x5e, 0x80, 0xf8, 0x2a, 0x5c, 0x77, 0x12, 
+	0x2a, 0x39, 0x76, 0x82, 0x00, 0x55, 0x77, 0x11, 
+	0x2a, 0x18, 0x10, 0xe1, 0x00, 0x01, 0x80, 0xe2, 
+	0x00, 0x01, 0x10, 0xe1, 0x00, 0x02, 0x80, 0xe2, 
+	0x00, 0x02, 0x76, 0xe2, 0x00, 0x03, 0x00, 0x1c, 
+	0xf6, 0xb8, 0x56, 0xf8, 0x2a, 0x16, 0xf0, 0xf0, 
+	0xf0, 0xf8, 0x80, 0xe2, 0x00, 0x07, 0x56, 0xf8, 
+	0x2a, 0x16, 0xf1, 0xf0, 0xe8, 0xff, 0xf2, 0x80, 
+	0x80, 0xe2, 0x00, 0x06, 0x56, 0xf8, 0x2a, 0x16, 
+	0xf1, 0xf8, 0xe8, 0xff, 0xf2, 0x80, 0x80, 0xe2, 
+	0x00, 0x05, 0x57, 0xf8, 0x2a, 0x16, 0xe8, 0xff, 
+	0xf2, 0x80, 0x80, 0xe2, 0x00, 0x04, 0x56, 0xf8, 
+	0x27, 0x6c, 0xf0, 0xf0, 0xf0, 0xf8, 0x80, 0xe2, 
+	0x00, 0x0b, 0x56, 0xf8, 0x27, 0x6c, 0xf1, 0xf0, 
+	0xe8, 0xff, 0xf2, 0x80, 0x80, 0xe2, 0x00, 0x0a, 
+	0x56, 0xf8, 0x27, 0x6c, 0xf1, 0xf8, 0xe8, 0xff, 
+	0xf2, 0x80, 0x80, 0xe2, 0x00, 0x09, 0xe8, 0xff, 
+	0x57, 0xf8, 0x27, 0x6c, 0xf2, 0x80, 0x80, 0xe2, 
+	0x00, 0x08, 0x56, 0xf8, 0x27, 0x6a, 0xf0, 0xf0, 
+	0xf0, 0xf8, 0x80, 0xe2, 0x00, 0x0f, 0x56, 0xf8, 
+	0x27, 0x6a, 0xf1, 0xf0, 0xe8, 0xff, 0xf2, 0x80, 
+	0x80, 0xe2, 0x00, 0x0e, 0x56, 0xf8, 0x27, 0x6a, 
+	0xf1, 0xf8, 0xe8, 0xff, 0xf2, 0x80, 0x80, 0xe2, 
+	0x00, 0x0d, 0x57, 0xf8, 0x27, 0x6a, 0xe8, 0xff, 
+	0xf2, 0x80, 0x80, 0xe2, 0x00, 0x0c, 0x76, 0xe2, 
+	0x00, 0x13, 0x00, 0x00, 0x76, 0xe2, 0x00, 0x12, 
+	0x00, 0x00, 0x6f, 0xf8, 0x2a, 0x5c, 0x0c, 0x58, 
+	0x80, 0xe2, 0x00, 0x11, 0xe8, 0xff, 0x18, 0xf8, 
+	0x2a, 0x5c, 0x80, 0xe2, 0x00, 0x10, 0x76, 0xe2, 
+	0x00, 0x17, 0x00, 0x00, 0x76, 0xe2, 0x00, 0x16, 
+	0x00, 0x00, 0x6f, 0xf8, 0x2a, 0x9e, 0x0c, 0x58, 
+	0x80, 0xe2, 0x00, 0x15, 0xe8, 0xff, 0x18, 0xf8, 
+	0x2a, 0x9e, 0x80, 0xe2, 0x00, 0x14, 0x76, 0xe2, 
+	0x00, 0x1b, 0x00, 0x00, 0x76, 0xe2, 0x00, 0x1a, 
+	0x00, 0x00, 0x76, 0xe2, 0x00, 0x19, 0x00, 0x00, 
+	0x70, 0xe2, 0x00, 0x18, 0x27, 0x6e, 0x76, 0xe2, 
+	0x00, 0x1f, 0x00, 0x00, 0x76, 0xe2, 0x00, 0x1e, 
+	0x00, 0x00, 0x76, 0xe2, 0x00, 0x1d, 0x00, 0x00, 
+	0x76, 0xe2, 0x00, 0x1c, 0x00, 0x00, 0x76, 0xe2, 
+	0x00, 0x20, 0x00, 0xaa, 0xf0, 0x74, 0x02, 0x98, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfe, 
+	0x10, 0xf8, 0x2a, 0x38, 0xf8, 0x45, 0x04, 0xed, 
+	0x77, 0x12, 0x2a, 0x18, 0x10, 0xe2, 0x00, 0x02, 
+	0x88, 0x11, 0xf4, 0x95, 0x77, 0x10, 0x00, 0x08, 
+	0x6d, 0xe9, 0xff, 0xdf, 0xf6, 0xa9, 0xf8, 0x20, 
+	0x04, 0x75, 0xf0, 0x73, 0x04, 0x7d, 0xf0, 0x10, 
+	0x00, 0x21, 0xf0, 0x00, 0x1a, 0x83, 0x48, 0x08, 
+	0x7e, 0xf8, 0x00, 0x08, 0xf4, 0xe2, 0xf0, 0x74, 
+	0x03, 0x0a, 0xf0, 0x73, 0x04, 0xea, 0x48, 0x12, 
+	0xf2, 0x74, 0x03, 0x23, 0xf0, 0x00, 0x00, 0x04, 
+	0xf2, 0x74, 0x03, 0x36, 0xf4, 0x95, 0xe8, 0x00, 
+	0xf0, 0x73, 0x04, 0xea, 0x77, 0x11, 0x2a, 0x18, 
+	0xe8, 0xff, 0x6f, 0xe1, 0x00, 0x04, 0x0d, 0x48, 
+	0x18, 0xe1, 0x00, 0x05, 0xf2, 0x74, 0x09, 0x69, 
+	0xf4, 0x95, 0xf2, 0xa0, 0xf0, 0x74, 0x03, 0x36, 
+	0xf0, 0x73, 0x04, 0xea, 0x77, 0x11, 0x2a, 0x18, 
+	0xe8, 0xff, 0x6f, 0xe1, 0x00, 0x04, 0x0d, 0x48, 
+	0x18, 0xe1, 0x00, 0x05, 0xf2, 0x74, 0x09, 0x41, 
+	0xf4, 0x95, 0xf2, 0xa0, 0xf0, 0x74, 0x03, 0x36, 
+	0xf0, 0x73, 0x04, 0xea, 0xf0, 0x74, 0x03, 0x57, 
+	0xf0, 0x73, 0x04, 0xea, 0x10, 0xf8, 0x2a, 0x1c, 
+	0xf0, 0x74, 0x12, 0xa4, 0xf2, 0x74, 0x03, 0x36, 
+	0xf4, 0x95, 0xe8, 0x00, 0xf0, 0x73, 0x04, 0xea, 
+	0x48, 0x12, 0xf2, 0x74, 0x03, 0x80, 0xf0, 0x00, 
+	0x00, 0x04, 0xf2, 0x74, 0x03, 0x36, 0xf4, 0x95, 
+	0xe8, 0x00, 0xf0, 0x73, 0x04, 0xea, 0x10, 0xf8, 
+	0x2a, 0x1c, 0xf0, 0x74, 0x12, 0xc5, 0xf2, 0x74, 
+	0x03, 0x36, 0xf4, 0x95, 0xe8, 0x00, 0xf0, 0x73, 
+	0x04, 0xea, 0x77, 0x11, 0x2a, 0x18, 0xe8, 0xff, 
+	0x6f, 0xe1, 0x00, 0x06, 0x0d, 0x48, 0x18, 0xe1, 
+	0x00, 0x07, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0xf2, 0xa0, 0x70, 0x00, 0x00, 0x12, 0x80, 0x01, 
+	0x10, 0xe1, 0x00, 0x04, 0xf0, 0x74, 0x0e, 0x7a, 
+	0xf2, 0x74, 0x03, 0x36, 0xf4, 0x95, 0xe8, 0x00, 
+	0xf0, 0x73, 0x04, 0xea, 0xf0, 0x74, 0x03, 0xbc, 
+	0x76, 0xf8, 0x2a, 0x38, 0x00, 0x00, 0xee, 0x02, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0x77, 0x11, 
+	0x2a, 0x39, 0x76, 0x81, 0x00, 0x55, 0x77, 0x12, 
+	0x2a, 0x18, 0x10, 0xe2, 0x00, 0x01, 0x80, 0xe1, 
+	0x00, 0x01, 0x10, 0xe2, 0x00, 0x02, 0x80, 0xe1, 
+	0x00, 0x02, 0x76, 0xe1, 0x00, 0x03, 0x00, 0x09, 
+	0x48, 0x11, 0xf0, 0x00, 0x00, 0x04, 0x88, 0x12, 
+	0xf4, 0x95, 0x77, 0x13, 0x2a, 0x86, 0xe9, 0x00, 
+	0xe5, 0x98, 0xf3, 0x00, 0x00, 0x01, 0xf6, 0xb8, 
+	0x48, 0x0b, 0x08, 0xf8, 0x2a, 0x3c, 0xf8, 0x43, 
+	0x05, 0x0a, 0x76, 0x82, 0x00, 0xaa, 0xf0, 0x74, 
+	0x02, 0x98, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0x77, 0x11, 0x2a, 0x39, 0x76, 0x81, 0x00, 0x55, 
+	0x77, 0x13, 0x2a, 0x18, 0x10, 0xe3, 0x00, 0x01, 
+	0x80, 0xe1, 0x00, 0x01, 0x10, 0xe3, 0x00, 0x02, 
+	0x80, 0xe1, 0x00, 0x02, 0x13, 0xe3, 0x00, 0x03, 
+	0x81, 0xe1, 0x00, 0x03, 0x48, 0x11, 0x77, 0x11, 
+	0x00, 0x00, 0xf8, 0x4d, 0x05, 0x44, 0xf0, 0x00, 
+	0x00, 0x04, 0x88, 0x12, 0x48, 0x13, 0xf0, 0x00, 
+	0x00, 0x04, 0x88, 0x13, 0xf4, 0x95, 0xf4, 0x95, 
+	0xe5, 0x98, 0x6d, 0x91, 0xf6, 0xb8, 0x48, 0x11, 
+	0x08, 0xf8, 0x2a, 0x3c, 0xf8, 0x43, 0x05, 0x3a, 
+	0xf0, 0x20, 0x2a, 0x39, 0x49, 0x11, 0xf5, 0x00, 
+	0x89, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x76, 0xe1, 
+	0x00, 0x04, 0x00, 0xaa, 0xf0, 0x74, 0x02, 0x98, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0x77, 0x11, 
+	0x2a, 0x39, 0x76, 0x81, 0x00, 0x55, 0x77, 0x12, 
+	0x2a, 0x18, 0x10, 0xe2, 0x00, 0x01, 0x80, 0xe1, 
+	0x00, 0x01, 0x10, 0xe2, 0x00, 0x02, 0x80, 0xe1, 
+	0x00, 0x02, 0x76, 0xe1, 0x00, 0x03, 0x00, 0x0c, 
+	0x48, 0x11, 0xf0, 0x00, 0x00, 0x04, 0x88, 0x12, 
+	0xf4, 0x95, 0x77, 0x13, 0x2a, 0x7a, 0xe9, 0x00, 
+	0xe5, 0x98, 0xf3, 0x00, 0x00, 0x01, 0xf6, 0xb8, 
+	0x48, 0x0b, 0x08, 0xf8, 0x2a, 0x3c, 0xf8, 0x43, 
+	0x05, 0x6a, 0x76, 0x82, 0x00, 0xaa, 0xf0, 0x74, 
+	0x02, 0x98, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0x77, 0x11, 0x2a, 0x39, 0x76, 0x81, 0x00, 0x55, 
+	0x77, 0x12, 0x2a, 0x18, 0x10, 0xe2, 0x00, 0x01, 
+	0x80, 0xe1, 0x00, 0x01, 0x10, 0xe2, 0x00, 0x02, 
+	0x80, 0xe1, 0x00, 0x02, 0x76, 0xe1, 0x00, 0x03, 
+	0x00, 0x19, 0x48, 0x11, 0xf0, 0x00, 0x00, 0x04, 
+	0x88, 0x12, 0xf4, 0x95, 0x77, 0x13, 0x2a, 0x5d, 
+	0xe9, 0x00, 0xe5, 0x98, 0xf3, 0x00, 0x00, 0x01, 
+	0xf6, 0xb8, 0x48, 0x0b, 0x08, 0xf8, 0x2a, 0x3c, 
+	0xf8, 0x43, 0x05, 0x93, 0x76, 0x82, 0x00, 0xaa, 
+	0xf0, 0x74, 0x02, 0x98, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0x88, 0x11, 0x10, 0xf8, 0x2a, 0x38, 
+	0xf8, 0x44, 0x05, 0xe3, 0x10, 0xf8, 0x2a, 0xa1, 
+	0xf8, 0x44, 0x05, 0xba, 0x6c, 0xe1, 0xff, 0x56, 
+	0x05, 0xe3, 0x72, 0x12, 0x2a, 0xa1, 0xf4, 0x95, 
+	0x70, 0xe2, 0x2a, 0x18, 0x00, 0x11, 0x6b, 0xf8, 
+	0x2a, 0xa1, 0x00, 0x01, 0xf0, 0x73, 0x05, 0xe3, 
+	0x72, 0x12, 0x2a, 0xa1, 0xf4, 0x95, 0x70, 0xe2, 
+	0x2a, 0x18, 0x00, 0x11, 0x10, 0xf8, 0x2a, 0xa1, 
+	0xf0, 0x00, 0x00, 0x01, 0x88, 0x12, 0xf4, 0x95, 
+	0xf4, 0x95, 0x6e, 0xe2, 0xff, 0xfc, 0x05, 0xd1, 
+	0x73, 0x12, 0x2a, 0xa1, 0x48, 0x11, 0xf0, 0x00, 
+	0x00, 0x05, 0x80, 0xf8, 0x2a, 0xa2, 0x10, 0xf8, 
+	0x2a, 0xa1, 0x08, 0xf8, 0x2a, 0xa2, 0xf8, 0x44, 
+	0x05, 0xe3, 0x6c, 0xe1, 0xff, 0xab, 0x05, 0xdd, 
+	0x76, 0xf8, 0x2a, 0x38, 0x00, 0x01, 0x76, 0xf8, 
+	0x2a, 0xa1, 0x00, 0x00, 0x76, 0xf8, 0x2a, 0xa2, 
+	0x00, 0x00, 0x8a, 0x11, 0xfc, 0x00, 0xf4, 0x95, 
+	0x4a, 0x08, 0x4a, 0x09, 0x4a, 0x0a, 0x4a, 0x0b, 
+	0x4a, 0x0c, 0x4a, 0x0d, 0x4a, 0x10, 0x4a, 0x11, 
+	0x4a, 0x12, 0x4a, 0x13, 0x4a, 0x14, 0x4a, 0x15, 
+	0x4a, 0x16, 0x4a, 0x17, 0x4a, 0x17, 0x4a, 0x19, 
+	0x4a, 0x0e, 0x4a, 0x06, 0x4a, 0x07, 0x4a, 0x1a, 
+	0x4a, 0x1d, 0x4a, 0x1b, 0x4a, 0x1c, 0x68, 0xf8, 
+	0x00, 0x07, 0x7d, 0x3f, 0x69, 0xf8, 0x00, 0x07, 
+	0x40, 0x00, 0x68, 0xf8, 0x00, 0x1d, 0xff, 0xfc, 
+	0x48, 0x18, 0x68, 0xf8, 0x00, 0x18, 0xff, 0xfe, 
+	0xf4, 0x95, 0xf4, 0x95, 0x4a, 0x08, 0xee, 0xff, 
+	0x10, 0xf8, 0x2a, 0x5b, 0xf9, 0x80, 0x18, 0x04, 
+	0xf0, 0x74, 0x05, 0xa2, 0xee, 0x01, 0x8a, 0x18, 
+	0xf4, 0x95, 0x8a, 0x1c, 0x8a, 0x1b, 0x8a, 0x1d, 
+	0x8a, 0x1a, 0x8a, 0x07, 0x8a, 0x06, 0x8a, 0x0e, 
+	0x8a, 0x19, 0x8a, 0x17, 0x8a, 0x17, 0x8a, 0x16, 
+	0x8a, 0x15, 0x8a, 0x14, 0x8a, 0x13, 0x8a, 0x12, 
+	0x8a, 0x11, 0x8a, 0x10, 0x8a, 0x0d, 0x8a, 0x0c, 
+	0x8a, 0x0b, 0x8a, 0x0a, 0x8a, 0x09, 0x8a, 0x08, 
+	0xf4, 0xeb, 0xee, 0xfd, 0x76, 0xf8, 0x2a, 0x38, 
+	0x00, 0x00, 0x76, 0xf8, 0x2a, 0x5a, 0x00, 0x00, 
+	0xe8, 0x01, 0x4e, 0x00, 0xfb, 0x80, 0x17, 0xd6, 
+	0xf4, 0x95, 0xe8, 0x01, 0x80, 0xf8, 0x2a, 0x5b, 
+	0x76, 0x00, 0x2a, 0x8f, 0xf9, 0x80, 0x16, 0xaa, 
+	0x10, 0xf8, 0x2a, 0x5b, 0xf9, 0x80, 0x17, 0x5c, 
+	0x10, 0xf8, 0x2a, 0x5b, 0xf9, 0x80, 0x17, 0x6f, 
+	0xfb, 0x80, 0x16, 0x66, 0xf4, 0x95, 0xe8, 0x1a, 
+	0xfb, 0x80, 0x16, 0x87, 0xf4, 0x95, 0xe8, 0x1a, 
+	0xfb, 0x80, 0x16, 0x66, 0xf4, 0x95, 0xe8, 0x1b, 
+	0xfb, 0x80, 0x16, 0x87, 0xf4, 0x95, 0xe8, 0x1b, 
+	0xee, 0x03, 0xfc, 0x00, 0x4a, 0x11, 0xf4, 0x95, 
+	0x13, 0x02, 0x88, 0x11, 0xe8, 0x00, 0xf8, 0x4d, 
+	0x06, 0x6a, 0xf3, 0x10, 0x00, 0x01, 0x89, 0x1a, 
+	0xf4, 0x95, 0xf0, 0x72, 0x06, 0x69, 0x1c, 0x91, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0x88, 0x11, 
+	0x12, 0x03, 0x11, 0x02, 0xf8, 0x45, 0x06, 0x79, 
+	0xf0, 0x10, 0x00, 0x01, 0x88, 0x1a, 0xf4, 0x95, 
+	0xf0, 0x72, 0x06, 0x78, 0x81, 0x91, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0xf4, 0x95, 0x71, 0x02, 
+	0x00, 0x11, 0x11, 0x03, 0x61, 0xf8, 0x00, 0x11, 
+	0x00, 0x01, 0xf8, 0x30, 0x06, 0x91, 0xf6, 0xb8, 
+	0x6f, 0xf8, 0x00, 0x11, 0x0c, 0x1f, 0x88, 0x11, 
+	0xf3, 0xe8, 0xe8, 0xff, 0x18, 0x81, 0xf1, 0xa0, 
+	0x81, 0x81, 0xf0, 0x73, 0x06, 0x9d, 0xf6, 0xb8, 
+	0x6f, 0xf8, 0x00, 0x11, 0x0c, 0x1f, 0x88, 0x11, 
+	0xf3, 0x30, 0x00, 0xff, 0xf0, 0x20, 0xff, 0x00, 
+	0x18, 0x81, 0xf1, 0xa0, 0x81, 0x81, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0xf4, 0x95, 0x11, 0x02, 
+	0x61, 0xf8, 0x00, 0x0b, 0x00, 0x01, 0xf8, 0x20, 
+	0x06, 0xb1, 0x49, 0x0b, 0xf6, 0x1f, 0x88, 0x11, 
+	0xf4, 0x95, 0xf4, 0x95, 0x10, 0x81, 0xf2, 0x73, 
+	0x06, 0xb8, 0xf0, 0x30, 0x00, 0xff, 0x49, 0x0b, 
+	0xf6, 0x1f, 0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 
+	0x12, 0x81, 0xf4, 0x78, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0xf4, 0x95, 0x71, 0x02, 0x00, 0x12, 
+	0x13, 0x03, 0x88, 0x11, 0xe8, 0x00, 0xf8, 0x4d, 
+	0x06, 0xcc, 0xf3, 0x10, 0x00, 0x01, 0x89, 0x1a, 
+	0xf4, 0x95, 0xf0, 0x72, 0x06, 0xcb, 0x11, 0x92, 
+	0xf2, 0xc0, 0x81, 0x91, 0x8a, 0x11, 0xfc, 0x00, 
+	0x88, 0x12, 0x12, 0x02, 0x71, 0x01, 0x00, 0x13, 
+	0xf8, 0x45, 0x06, 0xdb, 0xf0, 0x10, 0x00, 0x01, 
+	0x88, 0x1a, 0xf4, 0x95, 0xf0, 0x72, 0x06, 0xda, 
+	0xe5, 0x98, 0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfe, 
+	0x88, 0x11, 0x11, 0x04, 0x10, 0x06, 0x71, 0x05, 
+	0x00, 0x12, 0x61, 0xf8, 0x00, 0x12, 0x00, 0x01, 
+	0xf8, 0x20, 0x06, 0xea, 0xf0, 0x00, 0x00, 0x01, 
+	0xf6, 0xb8, 0xf0, 0x00, 0x00, 0x01, 0x6f, 0xf8, 
+	0x00, 0x12, 0x0f, 0x1f, 0x48, 0x08, 0x81, 0x00, 
+	0xf4, 0x7f, 0x80, 0x01, 0xf2, 0x74, 0x06, 0xba, 
+	0xf4, 0x95, 0x48, 0x11, 0xee, 0x02, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfe, 0x88, 0x12, 
+	0x11, 0x04, 0x10, 0x06, 0x71, 0x05, 0x00, 0x13, 
+	0x61, 0xf8, 0x00, 0x13, 0x00, 0x01, 0xf8, 0x20, 
+	0x07, 0x09, 0xf0, 0x00, 0x00, 0x01, 0xf0, 0x00, 
+	0x00, 0x01, 0x88, 0x11, 0xf6, 0xb8, 0x6f, 0xf8, 
+	0x00, 0x13, 0x0f, 0x1f, 0x81, 0x00, 0x48, 0x11, 
+	0xf4, 0x7f, 0x80, 0x01, 0xf2, 0x74, 0x06, 0xce, 
+	0xf4, 0x95, 0x48, 0x12, 0x48, 0x11, 0xf0, 0x30, 
+	0xff, 0xfe, 0xee, 0x02, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0x4a, 0x16, 0x4a, 0x17, 0xee, 0xfc, 
+	0xf4, 0x95, 0x80, 0x02, 0x71, 0x08, 0x00, 0x16, 
+	0x10, 0x09, 0x71, 0x0b, 0x00, 0x17, 0x80, 0x03, 
+	0x71, 0x0a, 0x00, 0x11, 0x48, 0x17, 0xf8, 0x45, 
+	0x07, 0x3f, 0x70, 0x00, 0x00, 0x11, 0x10, 0x03, 
+	0xf0, 0x74, 0x06, 0x9f, 0x80, 0x01, 0x70, 0x00, 
+	0x00, 0x16, 0x10, 0x02, 0xf0, 0x74, 0x06, 0x7b, 
+	0x6d, 0x91, 0x6d, 0x96, 0x6c, 0xef, 0xff, 0xff, 
+	0x07, 0x2f, 0xee, 0x04, 0x8a, 0x17, 0x8a, 0x16, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfe, 
+	0x10, 0xf8, 0x2a, 0xe8, 0x08, 0xf8, 0x2a, 0xe9, 
+	0xf8, 0x45, 0x07, 0x64, 0x76, 0x00, 0x00, 0x01, 
+	0x62, 0xf8, 0x2a, 0xe9, 0x00, 0x5e, 0xf2, 0x74, 
+	0x12, 0x0b, 0xf0, 0x00, 0x30, 0x40, 0x72, 0x11, 
+	0x2a, 0xe9, 0x77, 0x10, 0x00, 0x0f, 0xf5, 0xa9, 
+	0xf8, 0x20, 0x07, 0x61, 0x6b, 0xf8, 0x2a, 0xe9, 
+	0x00, 0x01, 0xf0, 0x73, 0x07, 0x64, 0x76, 0xf8, 
+	0x2a, 0xe9, 0x00, 0x00, 0xee, 0x02, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0x88, 0x11, 0xe8, 0x00, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x08, 0xe8, 0x00, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x09, 0xf6, 0xb8, 
+	0xf4, 0x95, 0xf0, 0x20, 0xfc, 0x3f, 0x75, 0xf8, 
+	0x00, 0x08, 0x00, 0x0d, 0xf0, 0x20, 0x0c, 0x30, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x0c, 0x76, 0xf8, 
+	0x2a, 0xe8, 0x00, 0x00, 0x76, 0xf8, 0x2a, 0xe9, 
+	0x00, 0x00, 0x6c, 0x81, 0x07, 0x92, 0x76, 0xf8, 
+	0x2a, 0xea, 0x00, 0x00, 0xfb, 0x80, 0x16, 0x76, 
+	0xf4, 0x95, 0xe8, 0x10, 0xe8, 0x00, 0x75, 0xf8, 
+	0x00, 0x08, 0x00, 0x00, 0xf0, 0x73, 0x07, 0xa8, 
+	0x76, 0xf8, 0x2a, 0xea, 0x00, 0x01, 0xfb, 0x80, 
+	0x16, 0x66, 0xf4, 0x95, 0xe8, 0x10, 0xfb, 0x80, 
+	0x16, 0x87, 0xf4, 0x95, 0xe8, 0x10, 0xe8, 0x00, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x00, 0xf6, 0xb8, 
+	0xf4, 0x95, 0xf0, 0x20, 0xff, 0xff, 0x75, 0xf8, 
+	0x00, 0x08, 0x00, 0x00, 0x8a, 0x11, 0xfc, 0x00, 
+	0xf4, 0x95, 0x4a, 0x08, 0x4a, 0x09, 0x4a, 0x0a, 
+	0x4a, 0x06, 0x4a, 0x07, 0x4a, 0x1d, 0x68, 0xf8, 
+	0x00, 0x07, 0x7d, 0x3f, 0x69, 0xf8, 0x00, 0x07, 
+	0x40, 0x00, 0x68, 0xf8, 0x00, 0x1d, 0xff, 0xfc, 
+	0x10, 0xf8, 0x2a, 0xea, 0xf8, 0x45, 0x07, 0xe1, 
+	0x10, 0xf8, 0x2a, 0xe8, 0xf0, 0x00, 0x00, 0x01, 
+	0xf0, 0x30, 0x00, 0x0f, 0x80, 0xf8, 0x2a, 0xe8, 
+	0x10, 0xf8, 0x2a, 0xe8, 0xf8, 0x44, 0x07, 0xd6, 
+	0xf6, 0xb8, 0xf4, 0x95, 0xf0, 0x20, 0xfc, 0x3f, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x0d, 0xf0, 0x20, 
+	0x0c, 0x30, 0x75, 0xf8, 0x00, 0x08, 0x00, 0x0c, 
+	0xe8, 0x00, 0x75, 0xf8, 0x00, 0x08, 0x00, 0x00, 
+	0xf6, 0xb8, 0xf4, 0x95, 0xf0, 0x20, 0xff, 0xff, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x00, 0x8a, 0x1d, 
+	0x8a, 0x07, 0x8a, 0x06, 0x8a, 0x0a, 0x8a, 0x09, 
+	0x8a, 0x08, 0xf4, 0xeb, 0xee, 0xff, 0xf2, 0x74, 
+	0x07, 0x67, 0xf4, 0x95, 0xe8, 0x01, 0xee, 0x01, 
+	0xfc, 0x00, 0x4a, 0x07, 0x4a, 0x1d, 0x68, 0xf8, 
+	0x00, 0x07, 0x7d, 0x3f, 0x69, 0xf8, 0x00, 0x07, 
+	0x40, 0x00, 0x68, 0xf8, 0x00, 0x1d, 0xff, 0xfc, 
+	0x8a, 0x1d, 0x8a, 0x07, 0xf4, 0xeb, 0x4a, 0x11, 
+	0x77, 0x11, 0x00, 0x28, 0x76, 0x81, 0x24, 0x00, 
+	0xe8, 0x00, 0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 
+	0xf2, 0x74, 0x07, 0x67, 0xf4, 0x95, 0xe8, 0x00, 
+	0x77, 0x11, 0x00, 0x1d, 0x68, 0x81, 0x00, 0x7f, 
+	0xf6, 0xb8, 0xf4, 0x95, 0xf0, 0x20, 0xff, 0x80, 
+	0x77, 0x11, 0x00, 0x1d, 0xf0, 0x30, 0x01, 0x00, 
+	0x1a, 0x81, 0x80, 0x81, 0xf0, 0x74, 0x0a, 0x33, 
+	0xf0, 0x74, 0x11, 0xac, 0xf9, 0x80, 0x13, 0x25, 
+	0xf9, 0x80, 0x16, 0x53, 0xf9, 0x80, 0x17, 0x82, 
+	0xf0, 0x74, 0x06, 0x2f, 0xf9, 0x80, 0x14, 0xb2, 
+	0xf9, 0x80, 0x19, 0x10, 0xf0, 0x74, 0x0d, 0xe3, 
+	0xf0, 0x74, 0x07, 0xe8, 0xf0, 0x74, 0x02, 0x36, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0x60, 0xf8, 
+	0x27, 0x7b, 0xff, 0xff, 0xf8, 0x30, 0x08, 0x39, 
+	0x71, 0xf8, 0x27, 0x7b, 0x27, 0x79, 0x60, 0xf8, 
+	0x27, 0x79, 0xff, 0xff, 0xf8, 0x30, 0x08, 0xb2, 
+	0x10, 0xf8, 0x29, 0x86, 0x08, 0xf8, 0x27, 0x79, 
+	0xf0, 0x30, 0x7f, 0xff, 0x88, 0x11, 0xf4, 0x95, 
+	0x77, 0x10, 0x40, 0x00, 0xf6, 0xa9, 0xf8, 0x30, 
+	0x08, 0x58, 0x10, 0xf8, 0x27, 0x79, 0x08, 0xf8, 
+	0x27, 0x7a, 0xf0, 0x30, 0x7f, 0xff, 0x88, 0x11, 
+	0xf4, 0x95, 0x77, 0x10, 0x40, 0x00, 0xf6, 0xa9, 
+	0xf8, 0x20, 0x08, 0x63, 0x76, 0xf8, 0x27, 0x79, 
+	0xff, 0xff, 0x76, 0xf8, 0x27, 0x7b, 0xff, 0xff, 
+	0xf7, 0xb8, 0xf2, 0x73, 0x08, 0xd9, 0xf0, 0x20, 
+	0xff, 0xff, 0xf6, 0xb8, 0x56, 0xf8, 0x27, 0x74, 
+	0xf0, 0xf9, 0x88, 0x11, 0x56, 0xf8, 0x27, 0x72, 
+	0xf0, 0xf9, 0x88, 0x12, 0xf4, 0x95, 0xf4, 0x95, 
+	0xe7, 0x20, 0xf4, 0xa9, 0xf8, 0x30, 0x08, 0x8f, 
+	0xf1, 0x20, 0x27, 0x7c, 0x48, 0x11, 0xf6, 0x00, 
+	0x88, 0x13, 0xf4, 0x95, 0xf4, 0x95, 0x10, 0x83, 
+	0x08, 0xf8, 0x27, 0x79, 0xf0, 0x30, 0x7f, 0xff, 
+	0x88, 0x13, 0xf4, 0x95, 0x77, 0x10, 0x40, 0x00, 
+	0xf5, 0xab, 0xf8, 0x30, 0x08, 0x8f, 0x6d, 0x91, 
+	0x48, 0x11, 0xf0, 0x30, 0x01, 0xff, 0x88, 0x11, 
+	0xf4, 0x95, 0xe7, 0x20, 0xf7, 0xa9, 0xf8, 0x30, 
+	0x08, 0x74, 0x6d, 0x89, 0x48, 0x11, 0xf0, 0x30, 
+	0x01, 0xff, 0xf0, 0xe7, 0xf4, 0x95, 0x48, 0x08, 
+	0x4e, 0xf8, 0x27, 0x74, 0x48, 0x08, 0xf1, 0xf9, 
+	0x89, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x71, 0xe1, 
+	0x27, 0x7c, 0x27, 0x7a, 0x60, 0xf8, 0x27, 0x7b, 
+	0xff, 0xff, 0xf8, 0x30, 0x08, 0xab, 0x48, 0x08, 
+	0x4e, 0xf8, 0x27, 0x72, 0x76, 0xf8, 0x27, 0x7b, 
+	0xff, 0xff, 0x76, 0xf8, 0x27, 0x79, 0xff, 0xff, 
+	0xf2, 0x73, 0x08, 0xd9, 0xf4, 0x95, 0xe8, 0x00, 
+	0x44, 0xf8, 0x27, 0x73, 0x40, 0xf8, 0x27, 0x75, 
+	0x82, 0xf8, 0x00, 0x11, 0xf4, 0x95, 0x77, 0x10, 
+	0x80, 0x00, 0xf6, 0xa9, 0xf8, 0x20, 0x08, 0xd8, 
+	0xf6, 0xb8, 0x10, 0xf8, 0x27, 0x73, 0xf0, 0x00, 
+	0x80, 0x00, 0x48, 0x08, 0x4e, 0xf8, 0x27, 0x74, 
+	0x48, 0x08, 0xf0, 0xf9, 0x88, 0x11, 0xf4, 0x95, 
+	0xf4, 0x95, 0x71, 0xe1, 0x27, 0x7c, 0x27, 0x7a, 
+	0xf7, 0xb8, 0x57, 0xf8, 0x27, 0x74, 0xf0, 0x62, 
+	0xff, 0xff, 0xf0, 0x40, 0xff, 0x80, 0xf2, 0x80, 
+	0x4e, 0xf8, 0x27, 0x74, 0xe8, 0x00, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0x4a, 0x16, 0xee, 0xfb, 
+	0x11, 0xf8, 0x27, 0x71, 0x09, 0xf8, 0x27, 0x73, 
+	0x89, 0x11, 0x88, 0x10, 0xf4, 0x95, 0xf4, 0x95, 
+	0xf6, 0xa9, 0xf8, 0x20, 0x08, 0xed, 0xf2, 0x73, 
+	0x09, 0x0e, 0xf4, 0x95, 0xe8, 0x00, 0xf6, 0x20, 
+	0x76, 0x00, 0x00, 0x41, 0xf0, 0x74, 0x12, 0xee, 
+	0x88, 0x16, 0xf4, 0x95, 0xf7, 0xb8, 0x6d, 0x96, 
+	0x10, 0xf8, 0x00, 0x16, 0xf8, 0x47, 0x09, 0x0a, 
+	0xe7, 0x61, 0x76, 0x00, 0x00, 0x00, 0x76, 0x01, 
+	0x00, 0x80, 0x76, 0x02, 0x00, 0xff, 0x76, 0x03, 
+	0x00, 0x00, 0xf2, 0x74, 0x0c, 0xb9, 0xf4, 0x95, 
+	0xe8, 0x00, 0x6c, 0xe9, 0xff, 0xff, 0x08, 0xfb, 
+	0x73, 0x16, 0x00, 0x0e, 0xf0, 0x66, 0x00, 0x41, 
+	0xee, 0x05, 0x8a, 0x16, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0xf4, 0x95, 0x71, 0x02, 0x00, 0x13, 
+	0xf6, 0xb8, 0x77, 0x11, 0x7f, 0xff, 0x57, 0xf8, 
+	0x27, 0x72, 0x48, 0x11, 0xf2, 0x80, 0xf0, 0x00, 
+	0x80, 0x00, 0x88, 0x11, 0xf6, 0x40, 0xf0, 0xe0, 
+	0xf1, 0xf1, 0xe8, 0x01, 0xf2, 0x80, 0x80, 0xf8, 
+	0x27, 0x78, 0x77, 0x12, 0x80, 0x00, 0x57, 0xf8, 
+	0x27, 0x72, 0x48, 0x12, 0xf2, 0x80, 0x88, 0x12, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x82, 0x09, 0x38, 
+	0xe8, 0x00, 0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 
+	0xf0, 0x73, 0x09, 0x3d, 0xf0, 0x20, 0x80, 0x01, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 0x70, 0x81, 
+	0x00, 0x13, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0xf0, 0x30, 0x7f, 0xff, 0x11, 0xf8, 0x29, 0x86, 
+	0xf5, 0x20, 0xf3, 0x30, 0x7f, 0xff, 0x89, 0x11, 
+	0xf4, 0x95, 0x77, 0x10, 0x40, 0x00, 0xf6, 0xa9, 
+	0xf8, 0x20, 0x09, 0x54, 0xf2, 0x73, 0x09, 0x67, 
+	0xf4, 0x95, 0xe8, 0x02, 0x6f, 0xf8, 0x27, 0x7a, 
+	0x0d, 0x20, 0xf3, 0x30, 0x7f, 0xff, 0x89, 0x11, 
+	0xf4, 0x95, 0x77, 0x10, 0x40, 0x00, 0xf6, 0xa9, 
+	0xf8, 0x20, 0x09, 0x64, 0xf2, 0x73, 0x09, 0x67, 
+	0xf4, 0x95, 0xe8, 0x01, 0x80, 0xf8, 0x27, 0x7b, 
+	0xe8, 0x00, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0x11, 0xf8, 0x29, 0x86, 0xf5, 0x20, 0xf3, 0x30, 
+	0x7f, 0xff, 0x89, 0x11, 0xf4, 0x95, 0x77, 0x10, 
+	0x40, 0x00, 0xf6, 0xa9, 0xf8, 0x20, 0x09, 0x7a, 
+	0xf2, 0x73, 0x09, 0x8d, 0xf4, 0x95, 0xe8, 0x02, 
+	0x6f, 0xf8, 0x27, 0x7a, 0x0d, 0x20, 0xf3, 0x30, 
+	0x7f, 0xff, 0x89, 0x11, 0xf4, 0x95, 0x77, 0x10, 
+	0x40, 0x00, 0xf6, 0xa9, 0xf8, 0x20, 0x09, 0x8a, 
+	0xf2, 0x73, 0x09, 0x8d, 0xf4, 0x95, 0xe8, 0x01, 
+	0x80, 0xf8, 0x27, 0x79, 0xe8, 0x00, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0xf4, 0x95, 0x71, 0x02, 
+	0x00, 0x12, 0x88, 0x11, 0xf6, 0xb8, 0x57, 0xf8, 
+	0x27, 0x72, 0xf0, 0x20, 0x7f, 0xff, 0xf2, 0x80, 
+	0xf0, 0x00, 0x80, 0x00, 0x80, 0x81, 0x57, 0xf8, 
+	0x27, 0x72, 0xe8, 0x01, 0xf3, 0xf1, 0xf2, 0x80, 
+	0x80, 0xf8, 0x27, 0x78, 0x77, 0x11, 0x80, 0x00, 
+	0x48, 0x11, 0x57, 0xf8, 0x27, 0x72, 0xf2, 0x80, 
+	0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x81, 
+	0x09, 0xb5, 0xe8, 0x00, 0x75, 0xf8, 0x00, 0x08, 
+	0x00, 0x01, 0xf0, 0x73, 0x09, 0xba, 0xf0, 0x20, 
+	0x80, 0x01, 0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 
+	0x45, 0xf8, 0x27, 0x71, 0x43, 0xf8, 0x27, 0x73, 
+	0x83, 0xf8, 0x00, 0x11, 0xf4, 0x95, 0xe7, 0x20, 
+	0xf6, 0xa9, 0xf8, 0x30, 0x09, 0xc9, 0xf2, 0x73, 
+	0x09, 0xe4, 0x77, 0x12, 0x00, 0x00, 0x57, 0xf8, 
+	0x27, 0x72, 0xf0, 0x20, 0x7f, 0xff, 0xf2, 0x80, 
+	0x49, 0x12, 0xf5, 0x00, 0xf3, 0x00, 0x80, 0x00, 
+	0x61, 0xf8, 0x00, 0x0b, 0x80, 0x00, 0xf8, 0x30, 
+	0x09, 0xdc, 0xf1, 0x20, 0x80, 0x00, 0xf5, 0x20, 
+	0x89, 0x12, 0xf4, 0x95, 0x48, 0x12, 0x6f, 0xf8, 
+	0x27, 0x73, 0x0d, 0x00, 0xf4, 0x95, 0x49, 0x0b, 
+	0x4f, 0xf8, 0x27, 0x72, 0x8a, 0x11, 0xfe, 0x00, 
+	0x48, 0x12, 0xf4, 0x95, 0x4a, 0x11, 0x4a, 0x16, 
+	0x4a, 0x17, 0xee, 0xfc, 0xf4, 0x95, 0x71, 0x08, 
+	0x00, 0x16, 0x88, 0x17, 0xf0, 0x74, 0x08, 0x30, 
+	0x48, 0x18, 0x70, 0x00, 0x00, 0x16, 0xf2, 0x74, 
+	0x09, 0x8f, 0xf0, 0x00, 0x00, 0x02, 0x88, 0x11, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x81, 0x0a, 0x0a, 
+	0xf2, 0x74, 0x08, 0xdb, 0xf4, 0x95, 0x48, 0x16, 
+	0x48, 0x18, 0x70, 0x00, 0x00, 0x16, 0xf2, 0x74, 
+	0x09, 0x8f, 0xf0, 0x00, 0x00, 0x02, 0x88, 0x11, 
+	0x10, 0x02, 0x70, 0x01, 0x00, 0x11, 0x80, 0x00, 
+	0xf2, 0x74, 0x06, 0xce, 0xf4, 0x95, 0x48, 0x17, 
+	0x49, 0x11, 0x48, 0x17, 0xf6, 0x00, 0x88, 0x17, 
+	0xe7, 0x60, 0xf5, 0xa9, 0xf8, 0x20, 0x0a, 0x2d, 
+	0x48, 0x16, 0xf6, 0x20, 0x88, 0x11, 0x48, 0x18, 
+	0x70, 0x00, 0x00, 0x11, 0xf2, 0x74, 0x09, 0x8f, 
+	0xf0, 0x00, 0x00, 0x02, 0x88, 0x11, 0x70, 0x01, 
+	0x00, 0x11, 0x10, 0x02, 0x80, 0x00, 0xf2, 0x74, 
+	0x06, 0xce, 0xf4, 0x95, 0x48, 0x17, 0xee, 0x04, 
+	0x48, 0x16, 0x8a, 0x17, 0x8a, 0x16, 0x8a, 0x11, 
+	0xfc, 0x00, 0xee, 0xfd, 0xe8, 0x00, 0x4e, 0xf8, 
+	0x27, 0x70, 0xe8, 0x00, 0x4e, 0xf8, 0x27, 0x72, 
+	0xe8, 0x00, 0x4e, 0xf8, 0x27, 0x74, 0xe8, 0x00, 
+	0x4e, 0xf8, 0x27, 0x76, 0x76, 0xf8, 0x27, 0x79, 
+	0xff, 0xff, 0x76, 0xf8, 0x27, 0x7a, 0x00, 0x00, 
+	0x76, 0xf8, 0x27, 0x7b, 0xff, 0xff, 0x76, 0xf8, 
+	0x27, 0x78, 0x00, 0x00, 0xe8, 0x00, 0x75, 0xf8, 
+	0x00, 0x08, 0x00, 0x01, 0x76, 0x00, 0x00, 0x00, 
+	0x76, 0x01, 0x02, 0x00, 0xf2, 0x74, 0x12, 0xdc, 
+	0xf0, 0x20, 0x27, 0x7c, 0xee, 0x03, 0xfc, 0x00, 
+	0x4a, 0x11, 0xee, 0xfc, 0xf4, 0x95, 0x4e, 0x00, 
+	0x77, 0x12, 0x7f, 0xff, 0xf6, 0xb8, 0x49, 0x12, 
+	0xf1, 0x80, 0xf3, 0x00, 0x80, 0x00, 0x89, 0x12, 
+	0xf0, 0xe0, 0xf1, 0xf1, 0x4f, 0x02, 0xe9, 0x01, 
+	0xf4, 0x95, 0x48, 0x0b, 0xf5, 0x40, 0x56, 0x02, 
+	0xf1, 0x80, 0x81, 0xf8, 0x27, 0x78, 0x77, 0x11, 
+	0x80, 0x00, 0x56, 0x00, 0x49, 0x11, 0xf1, 0x80, 
+	0x89, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x81, 
+	0x0a, 0x81, 0xe8, 0x00, 0x75, 0xf8, 0x00, 0x08, 
+	0x00, 0x01, 0xf0, 0x73, 0x0a, 0x86, 0xf0, 0x20, 
+	0x80, 0x01, 0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 
+	0x10, 0x82, 0xee, 0x04, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0xee, 0xfe, 0xf4, 0x95, 0x4e, 0x00, 
+	0x77, 0x11, 0x7f, 0xff, 0xf6, 0xb8, 0x49, 0x11, 
+	0xf1, 0x80, 0xf3, 0x00, 0x80, 0x00, 0x89, 0x11, 
+	0xf0, 0xe0, 0xf1, 0xf1, 0xe8, 0x01, 0xf2, 0x80, 
+	0x80, 0xf8, 0x27, 0x78, 0x56, 0x00, 0xf1, 0x20, 
+	0x80, 0x00, 0xf1, 0x80, 0xf4, 0x95, 0x49, 0x0b, 
+	0xf8, 0x4d, 0x0a, 0xab, 0xf0, 0x20, 0x80, 0x01, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 0xf0, 0x73, 
+	0x0a, 0xaf, 0xe8, 0x00, 0x75, 0xf8, 0x00, 0x08, 
+	0x00, 0x01, 0xee, 0x02, 0x48, 0x11, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0x88, 0x12, 0x13, 0x02, 
+	0x77, 0x11, 0x00, 0x00, 0xf8, 0x4d, 0x0a, 0xcb, 
+	0xf3, 0x10, 0x00, 0x01, 0x89, 0x1a, 0xf4, 0x95, 
+	0xf0, 0x72, 0x0a, 0xca, 0x48, 0x11, 0x1c, 0xf8, 
+	0x29, 0x7e, 0x88, 0x11, 0x11, 0xf8, 0x29, 0x7e, 
+	0xf2, 0x00, 0x00, 0x01, 0x80, 0xf8, 0x29, 0x7e, 
+	0x81, 0x92, 0x48, 0x11, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0xf4, 0x95, 0x71, 0x02, 0x00, 0x11, 
+	0x88, 0x12, 0xf6, 0xb8, 0xf0, 0x20, 0x7f, 0xff, 
+	0x57, 0xf8, 0x27, 0x70, 0xf2, 0x80, 0xf0, 0x00, 
+	0x80, 0x00, 0x80, 0x82, 0x57, 0xf8, 0x27, 0x70, 
+	0xe8, 0x01, 0xf3, 0xf1, 0xf2, 0x80, 0x80, 0xf8, 
+	0x27, 0x78, 0x77, 0x12, 0x80, 0x00, 0x48, 0x12, 
+	0x57, 0xf8, 0x27, 0x70, 0xf2, 0x80, 0x88, 0x12, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x82, 0x0a, 0xf4, 
+	0xe8, 0x00, 0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 
+	0xf0, 0x73, 0x0a, 0xf9, 0xf0, 0x20, 0x80, 0x01, 
+	0x75, 0xf8, 0x00, 0x08, 0x00, 0x01, 0x45, 0xf8, 
+	0x27, 0x75, 0xe7, 0x10, 0x43, 0xf8, 0x27, 0x71, 
+	0x83, 0xf8, 0x00, 0x12, 0x6d, 0xe8, 0x00, 0x04, 
+	0x6d, 0x8a, 0xf6, 0xaa, 0xf8, 0x30, 0x0b, 0x0a, 
+	0xf2, 0x73, 0x0b, 0x25, 0x77, 0x11, 0x00, 0x00, 
+	0x57, 0xf8, 0x27, 0x70, 0xf0, 0x20, 0x7f, 0xff, 
+	0xf2, 0x80, 0x49, 0x11, 0xf5, 0x00, 0xf3, 0x00, 
+	0x80, 0x00, 0x61, 0xf8, 0x00, 0x0b, 0x80, 0x00, 
+	0xf8, 0x30, 0x0b, 0x1d, 0xf1, 0x20, 0x80, 0x00, 
+	0xf5, 0x20, 0x89, 0x11, 0xf4, 0x95, 0x48, 0x11, 
+	0x6f, 0xf8, 0x27, 0x71, 0x0d, 0x00, 0xf4, 0x95, 
+	0x49, 0x0b, 0x4f, 0xf8, 0x27, 0x70, 0x48, 0x11, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0x4a, 0x16, 
+	0x4a, 0x17, 0xee, 0xf0, 0x88, 0x17, 0x10, 0x17, 
+	0x80, 0x05, 0x10, 0x16, 0x80, 0x06, 0x10, 0x15, 
+	0x80, 0x07, 0x71, 0x14, 0x00, 0x11, 0x10, 0x05, 
+	0xf0, 0x30, 0x00, 0x01, 0x88, 0x10, 0x10, 0x06, 
+	0xf0, 0x30, 0x00, 0x01, 0x80, 0x08, 0x49, 0x11, 
+	0x10, 0x05, 0xf6, 0x01, 0x80, 0x09, 0x10, 0x06, 
+	0x61, 0xf8, 0x00, 0x08, 0x00, 0x01, 0xf8, 0x20, 
+	0x0b, 0x4b, 0x10, 0x09, 0xf0, 0x00, 0x00, 0x01, 
+	0x80, 0x09, 0x71, 0x08, 0x00, 0x12, 0xf4, 0xaa, 
+	0xf8, 0x30, 0x0b, 0x54, 0x10, 0x09, 0xf0, 0x00, 
+	0x00, 0x01, 0x80, 0x09, 0x12, 0x09, 0x49, 0x11, 
+	0xf4, 0x7f, 0x80, 0x09, 0xf6, 0x20, 0x80, 0x0a, 
+	0x56, 0xf8, 0x27, 0x70, 0x4e, 0x0c, 0x10, 0x09, 
+	0x80, 0x00, 0x48, 0x18, 0xf2, 0x74, 0x0a, 0xce, 
+	0xf0, 0x00, 0x00, 0x04, 0x88, 0x16, 0xf4, 0x95, 
+	0xf4, 0x95, 0x6c, 0x86, 0x0b, 0x6d, 0xf2, 0x73, 
+	0x0c, 0x59, 0xf4, 0x95, 0xe8, 0x00, 0xf6, 0xb8, 
+	0xf4, 0x95, 0x56, 0x0c, 0xf0, 0xf9, 0x88, 0x12, 
+	0xf4, 0x95, 0xf4, 0x95, 0x70, 0xe2, 0x27, 0x7c, 
+	0x29, 0x86, 0xe8, 0x00, 0x80, 0x0e, 0x48, 0x11, 
+	0xf8, 0x45, 0x0b, 0xcc, 0x77, 0x10, 0x00, 0x01, 
+	0xf4, 0xa9, 0xf8, 0x30, 0x0b, 0x89, 0x6c, 0xe1, 
+	0xff, 0xfd, 0x0b, 0x8b, 0x10, 0xe7, 0x00, 0x02, 
+	0x80, 0x0e, 0xf0, 0x73, 0x0b, 0x8b, 0x10, 0x87, 
+	0x80, 0x0e, 0xe7, 0x10, 0xf5, 0xae, 0xf8, 0x20, 
+	0x0b, 0xb2, 0x70, 0x00, 0x00, 0x17, 0x70, 0x01, 
+	0x00, 0x16, 0x10, 0x04, 0xf0, 0x74, 0x06, 0xce, 
+	0x48, 0x17, 0x49, 0x16, 0xf6, 0x00, 0x88, 0x17, 
+	0x48, 0x11, 0xf6, 0x20, 0x88, 0x11, 0x10, 0x09, 
+	0xf6, 0x20, 0x80, 0x00, 0x48, 0x18, 0xf2, 0x74, 
+	0x0a, 0xce, 0xf0, 0x00, 0x00, 0x04, 0x88, 0x16, 
+	0x10, 0x04, 0x70, 0x00, 0x00, 0x17, 0x70, 0x01, 
+	0x00, 0x11, 0xf0, 0x74, 0x06, 0xce, 0x48, 0x11, 
+	0x00, 0x04, 0x80, 0x04, 0xf0, 0x73, 0x0b, 0xbc, 
+	0x70, 0x00, 0x00, 0x17, 0x70, 0x01, 0x00, 0x11, 
+	0x10, 0x04, 0xf0, 0x74, 0x06, 0xce, 0x48, 0x11, 
+	0x00, 0x04, 0x80, 0x04, 0x49, 0x11, 0x48, 0x16, 
+	0xf6, 0x20, 0x88, 0x16, 0xf4, 0x95, 0xf4, 0x95, 
+	0x6c, 0x86, 0x0b, 0xcc, 0x10, 0x0a, 0x80, 0x00, 
+	0x48, 0x18, 0xf2, 0x74, 0x0a, 0xce, 0xf0, 0x00, 
+	0x00, 0x04, 0x88, 0x16, 0x12, 0x0a, 0xf8, 0x45, 
+	0x0c, 0x33, 0x71, 0x0a, 0x00, 0x10, 0xf4, 0xae, 
+	0xf8, 0x30, 0x0c, 0x1c, 0x48, 0x16, 0xf0, 0xe1, 
+	0x88, 0x11, 0x12, 0x08, 0xf8, 0x45, 0x0b, 0xdb, 
+	0x6d, 0x89, 0x12, 0x07, 0xf8, 0x45, 0x0b, 0xe9, 
+	0x10, 0x07, 0x80, 0x00, 0x70, 0x02, 0x00, 0x11, 
+	0x10, 0x06, 0x80, 0x01, 0x10, 0x04, 0xf0, 0x74, 
+	0x06, 0xdc, 0xf0, 0x73, 0x0b, 0xef, 0x48, 0x11, 
+	0x6f, 0x00, 0x0c, 0x9f, 0x10, 0x04, 0xf0, 0x74, 
+	0x0a, 0xb3, 0x11, 0x0e, 0xf1, 0xc0, 0x81, 0x0e, 
+	0x10, 0x06, 0x49, 0x11, 0xf6, 0x00, 0x80, 0x06, 
+	0x10, 0x05, 0xf6, 0x20, 0x88, 0x11, 0xf0, 0x00, 
+	0x00, 0x01, 0x48, 0x08, 0x6f, 0x00, 0x0c, 0x9f, 
+	0x48, 0x18, 0xf2, 0x74, 0x0a, 0xce, 0xf0, 0x00, 
+	0x00, 0x04, 0x12, 0x07, 0xf8, 0x45, 0x0c, 0x11, 
+	0x10, 0x07, 0x80, 0x00, 0x70, 0x02, 0x00, 0x11, 
+	0x10, 0x06, 0x80, 0x01, 0x10, 0x04, 0xf0, 0x74, 
+	0x06, 0xdc, 0xf0, 0x73, 0x0c, 0x17, 0x48, 0x11, 
+	0x6f, 0x00, 0x0c, 0x9f, 0x10, 0x04, 0xf0, 0x74, 
+	0x0a, 0xb3, 0x11, 0x0e, 0xf1, 0xc0, 0x81, 0x0e, 
+	0xf0, 0x73, 0x0c, 0x33, 0x12, 0x07, 0xf8, 0x45, 
+	0x0c, 0x2a, 0x10, 0x07, 0x80, 0x00, 0x10, 0x06, 
+	0x80, 0x01, 0x10, 0x05, 0x80, 0x02, 0x10, 0x04, 
+	0xf0, 0x74, 0x06, 0xdc, 0xf0, 0x73, 0x0c, 0x30, 
+	0x12, 0x05, 0x6f, 0x00, 0x0c, 0x9f, 0x10, 0x04, 
+	0xf0, 0x74, 0x0a, 0xb3, 0x11, 0x0e, 0xf1, 0xc0, 
+	0x81, 0x0e, 0x76, 0x00, 0x00, 0x01, 0x48, 0x18, 
+	0xf2, 0x74, 0x0a, 0xce, 0xf0, 0x00, 0x00, 0x04, 
+	0x71, 0x04, 0x00, 0x11, 0x70, 0x81, 0x29, 0x86, 
+	0x10, 0x0e, 0x1c, 0xf8, 0x29, 0x86, 0x80, 0x0e, 
+	0x76, 0x00, 0x00, 0x01, 0x48, 0x18, 0xf2, 0x74, 
+	0x0a, 0xce, 0xf0, 0x00, 0x00, 0x04, 0x10, 0x0e, 
+	0x71, 0x04, 0x00, 0x11, 0x80, 0x81, 0x10, 0xf8, 
+	0x29, 0x86, 0xf0, 0x00, 0x00, 0x01, 0xf0, 0x30, 
+	0x7f, 0xff, 0x80, 0xf8, 0x29, 0x86, 0x10, 0x09, 
+	0xf0, 0x00, 0x00, 0x02, 0x80, 0x09, 0xee, 0x10, 
+	0x8a, 0x17, 0x8a, 0x16, 0x8a, 0x11, 0xfc, 0x00, 
+	0x10, 0xf8, 0x27, 0x75, 0x08, 0xf8, 0x27, 0x71, 
+	0xf0, 0x10, 0x00, 0x01, 0x48, 0x08, 0xfc, 0x00, 
+	0x4a, 0x11, 0x4a, 0x16, 0xee, 0xff, 0xf4, 0x95, 
+	0x71, 0x04, 0x00, 0x16, 0xf0, 0x00, 0x00, 0x01, 
+	0x48, 0x08, 0x4e, 0xf8, 0x29, 0x7c, 0x6d, 0xee, 
+	0xff, 0xfd, 0x48, 0x16, 0xf8, 0x45, 0x0c, 0x99, 
+	0x56, 0xf8, 0x29, 0x7c, 0xf0, 0x74, 0x0a, 0x5a, 
+	0x88, 0x11, 0x10, 0xf8, 0x29, 0x7d, 0xf0, 0x00, 
+	0x00, 0x01, 0x48, 0x08, 0x4e, 0xf8, 0x29, 0x7c, 
+	0x10, 0xf8, 0x29, 0x82, 0xf0, 0x00, 0x00, 0x01, 
+	0x88, 0x10, 0xf4, 0x95, 0xf4, 0x95, 0xf4, 0xa9, 
+	0xfa, 0x30, 0x0c, 0x96, 0x80, 0xf8, 0x29, 0x82, 
+	0x56, 0xf8, 0x29, 0x80, 0xf0, 0x00, 0x00, 0x01, 
+	0x4e, 0xf8, 0x29, 0x80, 0x73, 0x11, 0x29, 0x82, 
+	0x6c, 0xee, 0xff, 0xff, 0x0c, 0x76, 0xee, 0x01, 
+	0x8a, 0x16, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0x76, 0xf8, 0x29, 0x84, 0x00, 0x00, 0x76, 0xf8, 
+	0x29, 0x85, 0x00, 0x01, 0xe8, 0x00, 0x4e, 0xf8, 
+	0x2a, 0x0c, 0x76, 0xf8, 0x29, 0x86, 0x00, 0x00, 
+	0x76, 0xf8, 0x29, 0x87, 0x00, 0x00, 0x77, 0x11, 
+	0x29, 0x88, 0x76, 0x81, 0xaa, 0xaa, 0x76, 0xe1, 
+	0x00, 0x01, 0xaa, 0xaa, 0x76, 0xe1, 0x00, 0x02, 
+	0x00, 0x00, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0xee, 0xfc, 0xf4, 0x95, 0x71, 0x06, 0x00, 0x14, 
+	0x71, 0x07, 0x00, 0x13, 0x71, 0x08, 0x00, 0x12, 
+	0x71, 0x09, 0x00, 0x15, 0x77, 0x10, 0x00, 0xff, 
+	0xf4, 0xaa, 0xf8, 0x30, 0x0d, 0x44, 0x49, 0x13, 
+	0x53, 0xf8, 0x2a, 0x0c, 0x4f, 0xf8, 0x2a, 0x0c, 
+	0x73, 0x12, 0x00, 0x0e, 0xf1, 0x66, 0x00, 0x0d, 
+	0x89, 0x11, 0xf4, 0x95, 0x77, 0x10, 0x00, 0x01, 
+	0x71, 0xe1, 0x24, 0x00, 0x00, 0x11, 0xf4, 0xa9, 
+	0xf8, 0x30, 0x0d, 0x17, 0x77, 0x10, 0x00, 0x02, 
+	0xf4, 0xa9, 0xf8, 0x30, 0x0c, 0xec, 0x77, 0x11, 
+	0x29, 0x8a, 0x76, 0x81, 0x00, 0x00, 0xe8, 0x00, 
+	0x77, 0x14, 0x00, 0x00, 0x77, 0x13, 0x00, 0x00, 
+	0xf0, 0x73, 0x0d, 0x48, 0x6c, 0x83, 0x0c, 0xfa, 
+	0x77, 0x11, 0x29, 0x8a, 0x48, 0x12, 0xf0, 0xe8, 
+	0xf0, 0x40, 0x80, 0x00, 0x80, 0x81, 0xe8, 0x00, 
+	0x77, 0x14, 0x00, 0x00, 0xf0, 0x73, 0x0d, 0x48, 
+	0x49, 0x13, 0xf3, 0x40, 0x80, 0x00, 0x81, 0xf8, 
+	0x29, 0x8a, 0x61, 0xf8, 0x00, 0x15, 0x00, 0x01, 
+	0xf8, 0x20, 0x0d, 0x07, 0x69, 0xf8, 0x29, 0x8a, 
+	0x40, 0x00, 0x61, 0xf8, 0x00, 0x14, 0x00, 0x01, 
+	0xf8, 0x20, 0x0d, 0x0f, 0x69, 0xf8, 0x29, 0x8a, 
+	0x20, 0x00, 0x77, 0x11, 0x29, 0x8a, 0x49, 0x12, 
+	0xf3, 0xe8, 0x1b, 0x81, 0x81, 0x81, 0xf0, 0x73, 
+	0x0d, 0x48, 0x11, 0xf8, 0x29, 0x84, 0xf8, 0x4c, 
+	0x0d, 0x37, 0x77, 0x11, 0x29, 0x88, 0x76, 0x81, 
+	0xaa, 0xaa, 0x11, 0xf8, 0x29, 0x85, 0xf3, 0x10, 
+	0x00, 0x01, 0xf3, 0x40, 0xaa, 0x00, 0x81, 0xe1, 
+	0x00, 0x01, 0x76, 0x00, 0x00, 0x02, 0x80, 0x01, 
+	0x70, 0x02, 0x00, 0x14, 0x70, 0x03, 0x00, 0x13, 
+	0xf2, 0x74, 0x0b, 0x28, 0xf4, 0x95, 0x48, 0x11, 
+	0x71, 0xf8, 0x29, 0x85, 0x29, 0x84, 0xf0, 0x73, 
+	0x0d, 0x73, 0x76, 0x00, 0x00, 0x00, 0x80, 0x01, 
+	0x76, 0x02, 0x00, 0x00, 0x70, 0x03, 0x00, 0x13, 
+	0xf2, 0x74, 0x0b, 0x28, 0xf4, 0x95, 0xe8, 0x00, 
+	0xf0, 0x73, 0x0d, 0x73, 0x77, 0x11, 0x29, 0x8a, 
+	0x70, 0x81, 0x00, 0x13, 0x11, 0xf8, 0x29, 0x84, 
+	0xf8, 0x4c, 0x0d, 0x68, 0x77, 0x11, 0x29, 0x88, 
+	0x76, 0x81, 0xaa, 0xaa, 0x11, 0xf8, 0x29, 0x85, 
+	0xf3, 0x10, 0x00, 0x01, 0xf3, 0x40, 0xaa, 0x00, 
+	0x81, 0xe1, 0x00, 0x01, 0x76, 0x00, 0x00, 0x03, 
+	0x80, 0x01, 0x70, 0x02, 0x00, 0x14, 0x70, 0x03, 
+	0x00, 0x13, 0xf2, 0x74, 0x0b, 0x28, 0xf4, 0x95, 
+	0x48, 0x11, 0x71, 0xf8, 0x29, 0x85, 0x29, 0x84, 
+	0xf0, 0x73, 0x0d, 0x73, 0x76, 0x00, 0x00, 0x01, 
+	0x80, 0x01, 0x70, 0x02, 0x00, 0x14, 0x70, 0x03, 
+	0x00, 0x13, 0xf2, 0x74, 0x0b, 0x28, 0xf4, 0x95, 
+	0x48, 0x11, 0x6b, 0xf8, 0x29, 0x84, 0xff, 0xff, 
+	0xee, 0x04, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0xf5, 0x40, 0xf4, 0x95, 0x48, 0x0b, 0xf4, 0x78, 
+	0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x6c, 0xe1, 
+	0xff, 0xb9, 0x0d, 0x88, 0xf2, 0x73, 0x0d, 0xa5, 
+	0xf4, 0x95, 0xe8, 0x60, 0xf2, 0x00, 0x00, 0x06, 
+	0x61, 0xf8, 0x00, 0x11, 0x00, 0x20, 0xf8, 0x30, 
+	0x0d, 0x98, 0x61, 0xf8, 0x00, 0x0b, 0x00, 0x01, 
+	0xf8, 0x20, 0x0d, 0xa3, 0xf2, 0x00, 0x00, 0x07, 
+	0xf0, 0x73, 0x0d, 0xa3, 0x61, 0xf8, 0x00, 0x0b, 
+	0x00, 0x01, 0xf8, 0x20, 0x0d, 0xa1, 0xf2, 0x73, 
+	0x0d, 0xa3, 0xf0, 0x00, 0x00, 0x01, 0xf0, 0x00, 
+	0x00, 0x02, 0x48, 0x08, 0xf4, 0x7f, 0x8a, 0x11, 
+	0xfc, 0x00, 0xee, 0xff, 0xf0, 0x74, 0x07, 0xfd, 
+	0xf0, 0x74, 0x07, 0x44, 0xf0, 0x74, 0x0d, 0xb4, 
+	0xf0, 0x74, 0x02, 0x05, 0xf0, 0x74, 0x04, 0x60, 
+	0xf0, 0x73, 0x0d, 0xaa, 0xee, 0xfd, 0x10, 0xf8, 
+	0x2a, 0xa3, 0xf8, 0x44, 0x0d, 0xcb, 0x10, 0xf8, 
+	0x2a, 0xa4, 0xf8, 0x45, 0x0d, 0xd7, 0x76, 0x00, 
+	0x02, 0x00, 0xf2, 0x74, 0x09, 0xe8, 0xf0, 0x20, 
+	0x22, 0x00, 0x76, 0xf8, 0x2a, 0xa4, 0x00, 0x00, 
+	0x76, 0xf8, 0x2a, 0xa7, 0x00, 0x00, 0xf0, 0x73, 
+	0x0d, 0xd7, 0x76, 0x00, 0x02, 0x00, 0xf2, 0x74, 
+	0x09, 0xe8, 0xf0, 0x20, 0x20, 0x00, 0x76, 0xf8, 
+	0x2a, 0xa3, 0x00, 0x00, 0x76, 0xf8, 0x2a, 0xa7, 
+	0x00, 0x01, 0xf0, 0x74, 0x0c, 0x5e, 0xf0, 0xe0, 
+	0xf0, 0x10, 0x3a, 0x98, 0xf8, 0x47, 0x0d, 0xe1, 
+	0x76, 0xf8, 0x27, 0x6e, 0x00, 0x00, 0xee, 0x03, 
+	0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfe, 0x77, 0x11, 
+	0x20, 0x00, 0x76, 0x00, 0xaa, 0xaa, 0x76, 0x01, 
+	0x02, 0x00, 0xf2, 0x74, 0x06, 0x6c, 0xf4, 0x95, 
+	0x48, 0x11, 0x76, 0x00, 0x55, 0x55, 0x76, 0x01, 
+	0x02, 0x00, 0x48, 0x11, 0xf2, 0x74, 0x06, 0x6c, 
+	0xf0, 0x00, 0x02, 0x00, 0x76, 0xf8, 0x2a, 0xa3, 
+	0x00, 0x00, 0x76, 0xf8, 0x2a, 0xa4, 0x00, 0x00, 
+	0xe8, 0x00, 0x4e, 0x00, 0xfb, 0x80, 0x15, 0x3e, 
+	0xf4, 0x95, 0xe8, 0x04, 0x80, 0xf8, 0x2a, 0xa5, 
+	0x76, 0x00, 0x2a, 0xa8, 0xf9, 0x80, 0x14, 0x87, 
+	0x76, 0x00, 0x2a, 0xad, 0xfb, 0x80, 0x13, 0x62, 
+	0xf4, 0x95, 0xe8, 0x02, 0x10, 0xf8, 0x2a, 0xa5, 
+	0xf9, 0x80, 0x14, 0x63, 0xfb, 0x80, 0x16, 0x66, 
+	0xf4, 0x95, 0xe8, 0x1c, 0xfb, 0x80, 0x16, 0x87, 
+	0xf4, 0x95, 0xe8, 0x1c, 0xe8, 0x01, 0x4e, 0x00, 
+	0xfb, 0x80, 0x17, 0xd6, 0xf4, 0x95, 0xe8, 0x00, 
+	0x80, 0xf8, 0x2a, 0xa6, 0x76, 0x00, 0x2a, 0xb7, 
+	0xf9, 0x80, 0x16, 0xaa, 0x10, 0xf8, 0x2a, 0xa6, 
+	0xf9, 0x80, 0x17, 0x5c, 0x10, 0xf8, 0x2a, 0xa6, 
+	0xf9, 0x80, 0x17, 0x6f, 0xee, 0x02, 0x8a, 0x11, 
+	0xfc, 0x00, 0xf4, 0x95, 0x4a, 0x08, 0x4a, 0x09, 
+	0x4a, 0x0a, 0x4a, 0x07, 0x4a, 0x1d, 0x68, 0xf8, 
+	0x00, 0x07, 0x7d, 0x3f, 0x69, 0xf8, 0x00, 0x07, 
+	0x40, 0x00, 0x68, 0xf8, 0x00, 0x1d, 0xff, 0xfc, 
+	0x10, 0xf8, 0x2a, 0xa7, 0xf8, 0x44, 0x0e, 0x4b, 
+	0x76, 0xf8, 0x2a, 0xa3, 0x00, 0x01, 0xf0, 0x73, 
+	0x0e, 0x4e, 0x76, 0xf8, 0x2a, 0xa4, 0x00, 0x01, 
+	0x8a, 0x1d, 0x8a, 0x07, 0x8a, 0x0a, 0x8a, 0x09, 
+	0x8a, 0x08, 0xf4, 0xeb, 0x4a, 0x11, 0x4a, 0x16, 
+	0x4a, 0x17, 0xee, 0xfe, 0x88, 0x0e, 0x71, 0x08, 
+	0x00, 0x16, 0x71, 0x06, 0x00, 0x17, 0x11, 0x07, 
+	0xf0, 0x66, 0x00, 0x0d, 0xf0, 0x00, 0x25, 0xa0, 
+	0x88, 0x11, 0x76, 0x01, 0x00, 0x06, 0x81, 0x00, 
+	0xf2, 0x74, 0x06, 0xce, 0xf0, 0x00, 0x00, 0x01, 
+	0x76, 0x01, 0x00, 0x06, 0x70, 0x00, 0x00, 0x16, 
+	0x48, 0x11, 0xf2, 0x74, 0x06, 0xce, 0xf0, 0x00, 
+	0x00, 0x07, 0x70, 0x81, 0x00, 0x17, 0xee, 0x02, 
+	0x8a, 0x17, 0x8a, 0x16, 0x8a, 0x11, 0xfc, 0x00, 
+	0x4a, 0x11, 0x88, 0x0e, 0x71, 0x02, 0x00, 0x12, 
+	0x11, 0x03, 0xf0, 0x66, 0x00, 0x0d, 0xf0, 0x00, 
+	0x24, 0x00, 0x88, 0x11, 0xf4, 0x95, 0x70, 0x81, 
+	0x00, 0x12, 0x6e, 0xe2, 0xff, 0xfe, 0x0e, 0x8d, 
+	0xf4, 0x95, 0xe8, 0x00, 0xe8, 0x01, 0x80, 0xe1, 
+	0x00, 0x02, 0x76, 0xe1, 0x00, 0x03, 0x00, 0xff, 
+	0x76, 0xe1, 0x00, 0x04, 0x00, 0x00, 0x76, 0xe1, 
+	0x00, 0x0b, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x0c, 
+	0x00, 0x00, 0x81, 0xe1, 0x00, 0x01, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfc, 0x88, 0x0e, 
+	0xf4, 0x95, 0xf1, 0x66, 0x00, 0x0d, 0xf3, 0x00, 
+	0x24, 0x00, 0x89, 0x11, 0xf4, 0x95, 0xf4, 0x95, 
+	0x76, 0xe1, 0x00, 0x0c, 0x00, 0x00, 0x76, 0xe1, 
+	0x00, 0x0b, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x02, 
+	0x00, 0x01, 0x76, 0x00, 0x00, 0x00, 0x76, 0x01, 
+	0x00, 0x00, 0x80, 0x02, 0x76, 0x03, 0x00, 0x00, 
+	0xf2, 0x74, 0x0c, 0xb9, 0xf4, 0x95, 0xe8, 0x00, 
+	0xee, 0x04, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0x88, 0x19, 0xf4, 0x95, 0x73, 0x19, 0x00, 0x0e, 
+	0xf1, 0x66, 0x00, 0x0d, 0xf2, 0x00, 0x24, 0x00, 
+	0x77, 0x15, 0x25, 0xa0, 0x77, 0x14, 0x00, 0x00, 
+	0x77, 0x1a, 0x00, 0x1f, 0xf0, 0x72, 0x0f, 0x14, 
+	0xf6, 0xb8, 0x49, 0x19, 0x09, 0x85, 0xf8, 0x4c, 
+	0x0f, 0x13, 0xf1, 0x00, 0x00, 0x05, 0x89, 0x11, 
+	0x49, 0x15, 0xf3, 0x00, 0x00, 0x01, 0x89, 0x13, 
+	0x49, 0x15, 0xf3, 0x00, 0x00, 0x07, 0x89, 0x12, 
+	0x11, 0x93, 0x1d, 0x91, 0x19, 0x92, 0x89, 0x10, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x80, 0x0f, 0x13, 
+	0x11, 0x93, 0x1d, 0x91, 0x19, 0x92, 0x89, 0x10, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x80, 0x0f, 0x13, 
+	0x11, 0x93, 0x1d, 0x91, 0x19, 0x92, 0x89, 0x10, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x80, 0x0f, 0x13, 
+	0x11, 0x93, 0x1d, 0x91, 0x19, 0x92, 0x89, 0x10, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x80, 0x0f, 0x13, 
+	0x11, 0x93, 0x1d, 0x91, 0x19, 0x92, 0x89, 0x10, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x80, 0x0f, 0x13, 
+	0x11, 0x93, 0x1d, 0x91, 0x19, 0x92, 0x89, 0x11, 
+	0xf4, 0x95, 0xf4, 0x95, 0x6c, 0x81, 0x0f, 0x13, 
+	0x6d, 0x94, 0x6d, 0xed, 0x00, 0x0d, 0x48, 0x14, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0x4a, 0x16, 
+	0x4a, 0x17, 0xee, 0xf8, 0x88, 0x17, 0x10, 0x0d, 
+	0x80, 0x04, 0x10, 0x0c, 0x80, 0x05, 0x71, 0x0e, 
+	0x00, 0x16, 0x73, 0x17, 0x00, 0x0e, 0xf0, 0x66, 
+	0x00, 0x0d, 0xf0, 0x00, 0x24, 0x00, 0x88, 0x11, 
+	0x10, 0xf8, 0x27, 0x63, 0xf8, 0x45, 0x0f, 0x32, 
+	0xf2, 0x74, 0x0e, 0x9f, 0xf4, 0x95, 0x48, 0x17, 
+	0x10, 0xf8, 0x27, 0x60, 0xf8, 0x44, 0x0f, 0x3d, 
+	0x60, 0xe1, 0x00, 0x02, 0x00, 0x01, 0xf8, 0x20, 
+	0x0f, 0x6d, 0xf0, 0x73, 0x11, 0x33, 0x10, 0x04, 
+	0x80, 0x00, 0x10, 0x05, 0xf0, 0x74, 0x06, 0x9f, 
+	0x11, 0x04, 0xf3, 0x00, 0x00, 0x01, 0x81, 0x04, 
+	0x6d, 0x8e, 0x77, 0x10, 0x00, 0x01, 0x71, 0xe1, 
+	0x00, 0x02, 0x00, 0x12, 0xf4, 0xaa, 0xf8, 0x30, 
+	0x0f, 0x62, 0x77, 0x10, 0x00, 0x02, 0xf4, 0xaa, 
+	0xf8, 0x30, 0x0f, 0x6d, 0x45, 0xe1, 0x00, 0x0b, 
+	0x88, 0x10, 0x43, 0xe1, 0x00, 0x0c, 0x83, 0xf8, 
+	0x00, 0x12, 0xf4, 0x95, 0xf4, 0x95, 0xf4, 0xaa, 
+	0xf8, 0x30, 0x0f, 0x6d, 0xf0, 0x73, 0x0f, 0x96, 
+	0xf5, 0x00, 0x81, 0x04, 0x49, 0x16, 0xf5, 0x20, 
+	0x89, 0x16, 0x76, 0xe1, 0x00, 0x0c, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0x04, 0x00, 0x00, 0x48, 0x16, 
+	0xf8, 0x45, 0x11, 0x33, 0xf7, 0xb8, 0x71, 0xe1, 
+	0x00, 0x02, 0x00, 0x12, 0x10, 0xf8, 0x00, 0x12, 
+	0xf0, 0x10, 0x00, 0x03, 0xf8, 0x46, 0x0f, 0x8c, 
+	0x10, 0xf8, 0x00, 0x12, 0xf0, 0x10, 0x00, 0x03, 
+	0xf8, 0x45, 0x10, 0x16, 0x77, 0x10, 0x00, 0x01, 
+	0xf4, 0xaa, 0xf8, 0x30, 0x0f, 0x9c, 0x77, 0x10, 
+	0x00, 0x02, 0xf4, 0xaa, 0xf8, 0x30, 0x0f, 0xa8, 
+	0xf0, 0x73, 0x0f, 0x96, 0x77, 0x10, 0x00, 0x04, 
+	0xf4, 0xaa, 0xf8, 0x30, 0x10, 0xb7, 0x77, 0x10, 
+	0x00, 0x05, 0xf4, 0xaa, 0xf8, 0x30, 0x10, 0xbc, 
+	0xf2, 0x74, 0x0e, 0x9f, 0xf4, 0x95, 0x48, 0x17, 
+	0xf0, 0x73, 0x11, 0x31, 0x76, 0xe1, 0x00, 0x0c, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x0b, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0x04, 0x00, 0x00, 0x76, 0xe1, 
+	0x00, 0x02, 0x00, 0x02, 0x11, 0xe1, 0x00, 0x0c, 
+	0xe8, 0x03, 0xf6, 0x20, 0x89, 0x12, 0xf4, 0x95, 
+	0x77, 0x10, 0x00, 0x03, 0xf5, 0xaa, 0xf8, 0x30, 
+	0x0f, 0xb6, 0x6b, 0xf8, 0x27, 0x6f, 0x00, 0x01, 
+	0x88, 0x10, 0xf4, 0x95, 0xf4, 0x95, 0xf5, 0xae, 
+	0xf8, 0x20, 0x0f, 0xbd, 0x48, 0x16, 0x80, 0x06, 
+	0x88, 0x13, 0xf4, 0x95, 0x77, 0x10, 0x00, 0x03, 
+	0xf6, 0xab, 0xf8, 0x20, 0x0f, 0xc8, 0x6b, 0xf8, 
+	0x27, 0x6f, 0x00, 0x01, 0x12, 0x06, 0xf8, 0x45, 
+	0x10, 0x00, 0x10, 0xe1, 0x00, 0x04, 0x80, 0x00, 
+	0x10, 0x05, 0x80, 0x01, 0x10, 0x04, 0x80, 0x02, 
+	0x10, 0x06, 0x80, 0x03, 0x48, 0x11, 0xf2, 0x74, 
+	0x07, 0x1e, 0xf0, 0x00, 0x00, 0x05, 0x10, 0x06, 
+	0x00, 0xe1, 0x00, 0x04, 0x80, 0xe1, 0x00, 0x04, 
+	0x10, 0x06, 0x00, 0xe1, 0x00, 0x0c, 0x80, 0xe1, 
+	0x00, 0x0c, 0x88, 0x12, 0x11, 0x06, 0x10, 0x04, 
+	0xf6, 0x00, 0x80, 0x04, 0x48, 0x16, 0xf6, 0x20, 
+	0x88, 0x16, 0x89, 0x13, 0xf4, 0x95, 0x77, 0x10, 
+	0x00, 0x03, 0xf6, 0xab, 0xf8, 0x20, 0x0f, 0xf5, 
+	0x6b, 0xf8, 0x27, 0x6f, 0x00, 0x01, 0x77, 0x10, 
+	0x00, 0x0c, 0x71, 0xe1, 0x00, 0x04, 0x00, 0x13, 
+	0xf6, 0xab, 0xf8, 0x20, 0x10, 0x00, 0x6b, 0xf8, 
+	0x27, 0x6f, 0x00, 0x01, 0x6c, 0xe2, 0xff, 0xfd, 
+	0x11, 0x31, 0xf6, 0xb8, 0x6f, 0xe1, 0x00, 0x05, 
+	0x0c, 0x48, 0x6f, 0xe1, 0x00, 0x06, 0x0c, 0x18, 
+	0xf0, 0x30, 0x0f, 0xff, 0xf0, 0x00, 0x00, 0x03, 
+	0x80, 0xe1, 0x00, 0x0b, 0x76, 0xe1, 0x00, 0x02, 
+	0x00, 0x03, 0x48, 0x16, 0xf8, 0x45, 0x11, 0x33, 
+	0x71, 0xe1, 0x00, 0x0c, 0x00, 0x12, 0x10, 0xe1, 
+	0x00, 0x0b, 0x49, 0x12, 0xf6, 0x20, 0x88, 0x13, 
+	0xe8, 0x0c, 0xf6, 0x20, 0x88, 0x10, 0xf4, 0x95, 
+	0xf4, 0x95, 0xf5, 0xab, 0xf8, 0x20, 0x10, 0x27, 
+	0x48, 0x13, 0x80, 0x06, 0x88, 0x10, 0xf4, 0x95, 
+	0xf4, 0x95, 0xf5, 0xae, 0xf8, 0x20, 0x10, 0x30, 
+	0x70, 0x06, 0x00, 0x16, 0x12, 0x06, 0xf8, 0x45, 
+	0x10, 0x5f, 0x10, 0xe1, 0x00, 0x04, 0x80, 0x00, 
+	0x10, 0x05, 0x80, 0x01, 0x10, 0x04, 0x80, 0x02, 
+	0x10, 0x06, 0x80, 0x03, 0x48, 0x11, 0xf2, 0x74, 
+	0x07, 0x1e, 0xf0, 0x00, 0x00, 0x05, 0x10, 0x06, 
+	0x00, 0xe1, 0x00, 0x04, 0x80, 0xe1, 0x00, 0x04, 
+	0x10, 0x06, 0x00, 0xe1, 0x00, 0x0c, 0x80, 0xe1, 
+	0x00, 0x0c, 0x88, 0x12, 0x11, 0x06, 0x10, 0x04, 
+	0xf6, 0x00, 0x80, 0x04, 0x48, 0x16, 0xf6, 0x20, 
+	0x88, 0x16, 0xf4, 0x95, 0x77, 0x10, 0x00, 0x0c, 
+	0x71, 0xe1, 0x00, 0x04, 0x00, 0x13, 0xf6, 0xab, 
+	0xf8, 0x20, 0x10, 0x5f, 0x6b, 0xf8, 0x27, 0x6f, 
+	0x00, 0x01, 0x77, 0x10, 0x00, 0x0c, 0xf6, 0xaa, 
+	0xf8, 0x20, 0x10, 0x6b, 0xf2, 0x74, 0x0e, 0x9f, 
+	0xf4, 0x95, 0x48, 0x17, 0x71, 0xe1, 0x00, 0x0c, 
+	0x00, 0x12, 0x77, 0x10, 0x00, 0x0c, 0xf4, 0xaa, 
+	0xf8, 0x30, 0x10, 0x7c, 0x77, 0x10, 0x00, 0x0c, 
+	0x71, 0xe1, 0x00, 0x0b, 0x00, 0x13, 0xf6, 0xab, 
+	0xf8, 0x30, 0x10, 0xb4, 0xe7, 0x30, 0xf7, 0xaa, 
+	0xf8, 0x30, 0x10, 0xb4, 0xf2, 0x74, 0x0e, 0xc1, 
+	0xf4, 0x95, 0x48, 0x17, 0x88, 0x12, 0xf4, 0x95, 
+	0xf4, 0x95, 0x6c, 0x82, 0x10, 0x8d, 0x76, 0xe1, 
+	0x00, 0x04, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x02, 
+	0x00, 0x05, 0xf0, 0x73, 0x10, 0xb4, 0x76, 0xe1, 
+	0x00, 0x02, 0x00, 0x04, 0x77, 0x10, 0x00, 0x0c, 
+	0x71, 0xe1, 0x00, 0x0b, 0x00, 0x12, 0xf5, 0xaa, 
+	0xf8, 0x20, 0x10, 0x9a, 0xf0, 0x73, 0x10, 0x9c, 
+	0x77, 0x12, 0x00, 0x0c, 0x76, 0x00, 0x00, 0x00, 
+	0x70, 0x01, 0x00, 0x12, 0x70, 0x02, 0x00, 0x17, 
+	0x76, 0x03, 0x00, 0x01, 0x48, 0x11, 0xf2, 0x74, 
+	0x0c, 0xb9, 0xf0, 0x00, 0x00, 0x05, 0x76, 0xe1, 
+	0x00, 0x04, 0x00, 0x00, 0x77, 0x10, 0x00, 0x0c, 
+	0x71, 0xe1, 0x00, 0x0b, 0x00, 0x12, 0xf6, 0xaa, 
+	0xf8, 0x20, 0x11, 0x1c, 0x48, 0x16, 0xf8, 0x45, 
+	0x11, 0x33, 0x60, 0xe1, 0x00, 0x02, 0x00, 0x05, 
+	0xf8, 0x20, 0x10, 0xdf, 0x10, 0xe1, 0x00, 0x0b, 
+	0x08, 0xe1, 0x00, 0x0c, 0x11, 0xe1, 0x00, 0x04, 
+	0xf8, 0x4d, 0x10, 0xc7, 0x6b, 0xf8, 0x27, 0x6f, 
+	0x00, 0x01, 0x88, 0x10, 0xf4, 0x95, 0xf4, 0x95, 
+	0xf5, 0xae, 0xf8, 0x20, 0x10, 0xcf, 0x48, 0x16, 
+	0xf4, 0x95, 0x48, 0x08, 0xf8, 0x45, 0x11, 0x16, 
+	0x6f, 0xe1, 0x00, 0x0c, 0x0d, 0x00, 0x81, 0xe1, 
+	0x00, 0x0c, 0x11, 0x04, 0xf5, 0x00, 0x81, 0x04, 
+	0x49, 0x16, 0xf5, 0x20, 0x89, 0x16, 0xf0, 0x73, 
+	0x11, 0x0e, 0x10, 0xe1, 0x00, 0x0b, 0x71, 0xe1, 
+	0x00, 0x0c, 0x00, 0x12, 0x88, 0x10, 0xf4, 0x95, 
+	0xf4, 0x95, 0xf6, 0xaa, 0xf8, 0x30, 0x11, 0x16, 
+	0x49, 0x12, 0xf6, 0x20, 0x88, 0x10, 0xf4, 0x95, 
+	0xf4, 0x95, 0xf5, 0xae, 0xf8, 0x20, 0x10, 0xf3, 
+	0x48, 0x16, 0x80, 0x06, 0x48, 0x08, 0xf8, 0x45, 
+	0x11, 0x16, 0x10, 0x04, 0x70, 0x02, 0x00, 0x17, 
+	0x80, 0x00, 0x76, 0x03, 0x00, 0x00, 0x10, 0x06, 
+	0x80, 0x01, 0x10, 0x05, 0xf0, 0x74, 0x0c, 0xb9, 
+	0x10, 0x06, 0x00, 0xe1, 0x00, 0x0c, 0x80, 0xe1, 
+	0x00, 0x0c, 0x11, 0x06, 0x10, 0x04, 0xf6, 0x00, 
+	0x80, 0x04, 0x48, 0x16, 0xf6, 0x20, 0x88, 0x16, 
+	0x10, 0xe1, 0x00, 0x0c, 0x08, 0xe1, 0x00, 0x0b, 
+	0xf8, 0x45, 0x11, 0x1c, 0xf0, 0x73, 0x11, 0x31, 
+	0xf2, 0x74, 0x0e, 0x9f, 0xf4, 0x95, 0x48, 0x17, 
+	0xf0, 0x73, 0x11, 0x33, 0x76, 0xe1, 0x00, 0x0c, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x0b, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0x02, 0x00, 0x01, 0x10, 0x04, 
+	0x80, 0x00, 0x10, 0x05, 0xf0, 0x74, 0x06, 0x9f, 
+	0x88, 0x12, 0xf4, 0x95, 0x77, 0x10, 0x00, 0xff, 
+	0xf4, 0xaa, 0xf8, 0x30, 0x11, 0x33, 0x6c, 0x86, 
+	0x0f, 0x70, 0xee, 0x08, 0x8a, 0x17, 0x8a, 0x16, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfc, 
+	0xf4, 0x95, 0x71, 0x06, 0x00, 0x12, 0x88, 0x11, 
+	0x73, 0x12, 0x00, 0x0e, 0xf1, 0x66, 0x00, 0x0d, 
+	0xf3, 0x00, 0x24, 0x00, 0x89, 0x14, 0x13, 0x81, 
+	0xf7, 0x7a, 0xf3, 0x30, 0x00, 0x01, 0x81, 0xf8, 
+	0x27, 0x60, 0x13, 0xe1, 0x00, 0x01, 0xf7, 0x7c, 
+	0xf3, 0x30, 0x00, 0x03, 0x81, 0xf8, 0x27, 0x61, 
+	0xe9, 0x0f, 0x19, 0xe1, 0x00, 0x01, 0x81, 0xf8, 
+	0x27, 0x62, 0x71, 0xe4, 0x00, 0x03, 0x00, 0x13, 
+	0xf6, 0xb8, 0x49, 0x13, 0xf3, 0x00, 0x00, 0x01, 
+	0xf3, 0x30, 0x00, 0x0f, 0x49, 0x0b, 0x09, 0xf8, 
+	0x27, 0x62, 0xf8, 0x4d, 0x11, 0x75, 0x77, 0x10, 
+	0x00, 0xff, 0xf4, 0xab, 0xf8, 0x30, 0x11, 0x75, 
+	0x57, 0xf8, 0x27, 0x6c, 0xf3, 0x00, 0x00, 0x01, 
+	0x4f, 0xf8, 0x27, 0x6c, 0x76, 0xf8, 0x27, 0x63, 
+	0x00, 0x01, 0xf0, 0x73, 0x11, 0x78, 0x76, 0xf8, 
+	0x27, 0x63, 0x00, 0x00, 0x70, 0xe4, 0x00, 0x03, 
+	0x27, 0x62, 0x76, 0xf8, 0x27, 0x64, 0x00, 0x00, 
+	0x11, 0xf8, 0x27, 0x61, 0x61, 0xf8, 0x00, 0x0b, 
+	0x00, 0x02, 0xf8, 0x20, 0x11, 0x8d, 0xe9, 0x01, 
+	0x6f, 0xe1, 0x00, 0x02, 0x0f, 0x18, 0x81, 0xf8, 
+	0x27, 0x64, 0x11, 0xf8, 0x27, 0x61, 0x61, 0xf8, 
+	0x00, 0x0b, 0x00, 0x01, 0xf8, 0x20, 0x11, 0xa9, 
+	0x10, 0xf8, 0x27, 0x64, 0xf1, 0x00, 0x00, 0x04, 
+	0x89, 0x13, 0xe9, 0xb8, 0xf5, 0x20, 0x81, 0xf8, 
+	0x27, 0x65, 0x60, 0x84, 0x00, 0x02, 0xf8, 0x20, 
+	0x11, 0xa9, 0x70, 0x00, 0x00, 0x11, 0x70, 0x01, 
+	0x00, 0x13, 0x70, 0x02, 0x27, 0x65, 0xf2, 0x74, 
+	0x0f, 0x18, 0xf4, 0x95, 0x48, 0x12, 0xee, 0x04, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0x4a, 0x16, 
+	0x4a, 0x17, 0xee, 0xfc, 0xe8, 0x00, 0x4e, 0xf8, 
+	0x27, 0x66, 0xe8, 0x00, 0x4e, 0xf8, 0x27, 0x68, 
+	0xe8, 0x00, 0x4e, 0xf8, 0x27, 0x6c, 0xe8, 0x00, 
+	0x4e, 0xf8, 0x27, 0x6a, 0x77, 0x12, 0x27, 0x40, 
+	0x77, 0x11, 0x24, 0x00, 0x77, 0x1a, 0x00, 0x1f, 
+	0xf0, 0x72, 0x11, 0xdb, 0x70, 0x92, 0x00, 0x11, 
+	0x76, 0xe1, 0x00, 0x01, 0xff, 0xff, 0x76, 0x81, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x02, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0x03, 0x00, 0xff, 0x76, 0xe1, 
+	0x00, 0x0c, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x0b, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x04, 0x00, 0x00, 
+	0x6d, 0xe9, 0x00, 0x0d, 0xf0, 0x20, 0x25, 0xa0, 
+	0xf1, 0x00, 0x00, 0x07, 0x89, 0x11, 0xf1, 0x00, 
+	0x00, 0x01, 0x81, 0x02, 0x88, 0x16, 0xf4, 0x95, 
+	0x77, 0x17, 0x00, 0x20, 0x76, 0x86, 0x00, 0xff, 
+	0x76, 0x00, 0x00, 0x00, 0x76, 0x01, 0x00, 0x06, 
+	0x10, 0x02, 0xf0, 0x74, 0x06, 0x6c, 0x76, 0x00, 
+	0x00, 0x00, 0x76, 0x01, 0x00, 0x06, 0xf2, 0x74, 
+	0x06, 0x6c, 0xf4, 0x95, 0x48, 0x11, 0x10, 0x02, 
+	0xf0, 0x00, 0x00, 0x0d, 0x80, 0x02, 0x6d, 0xe9, 
+	0x00, 0x0d, 0x6d, 0xee, 0x00, 0x0d, 0x6c, 0xef, 
+	0xff, 0xff, 0x11, 0xe8, 0xf0, 0x74, 0x0c, 0x9d, 
+	0xee, 0x04, 0x8a, 0x17, 0x8a, 0x16, 0x8a, 0x11, 
+	0xfc, 0x00, 0x4a, 0x11, 0x4a, 0x16, 0x4a, 0x17, 
+	0xee, 0xfa, 0x88, 0x11, 0x10, 0x0a, 0x49, 0x11, 
+	0xf8, 0x4d, 0x12, 0x9f, 0x48, 0x08, 0xf8, 0x45, 
+	0x12, 0x9f, 0x80, 0x04, 0x12, 0x81, 0xf5, 0x78, 
+	0x89, 0x12, 0xf4, 0x95, 0xf4, 0x95, 0x6c, 0xe2, 
+	0xff, 0xb9, 0x12, 0x8a, 0x61, 0xf8, 0x00, 0x08, 
+	0x00, 0x80, 0xf8, 0x30, 0x12, 0x8a, 0x13, 0xe1, 
+	0x00, 0x01, 0xf0, 0xe8, 0xf7, 0x78, 0xf1, 0xa0, 
+	0xf2, 0x30, 0x1f, 0xff, 0x88, 0x17, 0xf4, 0x95, 
+	0x77, 0x12, 0x24, 0x00, 0x77, 0x16, 0x00, 0x00, 
+	0x77, 0x13, 0x00, 0x20, 0xf6, 0xb8, 0x48, 0x17, 
+	0x08, 0xe2, 0x00, 0x01, 0xf8, 0x45, 0x12, 0x42, 
+	0x6d, 0xea, 0x00, 0x0d, 0x6d, 0x96, 0x6c, 0xeb, 
+	0xff, 0xff, 0x12, 0x34, 0xf0, 0x73, 0x12, 0x90, 
+	0x56, 0xf8, 0x27, 0x6a, 0xf0, 0x00, 0x00, 0x01, 
+	0x4e, 0xf8, 0x27, 0x6a, 0x60, 0x82, 0x00, 0x01, 
+	0xf8, 0x30, 0x12, 0x54, 0x70, 0x00, 0x00, 0x16, 
+	0xf2, 0x74, 0x11, 0x38, 0xf4, 0x95, 0x48, 0x11, 
+	0xf0, 0x73, 0x12, 0x90, 0x70, 0x00, 0x00, 0x16, 
+	0xf2, 0x74, 0x11, 0x38, 0xf4, 0x95, 0x48, 0x11, 
+	0x72, 0x10, 0x2a, 0x9e, 0xf4, 0x95, 0xf4, 0xaf, 
+	0xf8, 0x30, 0x12, 0x6e, 0x76, 0x00, 0x00, 0x00, 
+	0x76, 0x01, 0x00, 0xbc, 0x70, 0x02, 0x00, 0x16, 
+	0x76, 0x03, 0x00, 0x00, 0xf2, 0x74, 0x0c, 0xb9, 
+	0xf4, 0x95, 0x48, 0x11, 0xf0, 0x73, 0x12, 0x90, 
+	0x10, 0xf8, 0x27, 0x6e, 0xf8, 0x44, 0x12, 0x90, 
+	0x76, 0x00, 0x00, 0x00, 0x76, 0x01, 0x00, 0xbc, 
+	0x70, 0x02, 0x00, 0x16, 0x76, 0x03, 0x00, 0x00, 
+	0xf2, 0x74, 0x0c, 0xb9, 0xf4, 0x95, 0x48, 0x11, 
+	0xf0, 0x74, 0x0c, 0x5e, 0xf0, 0xe0, 0xf0, 0x10, 
+	0x13, 0x88, 0xf8, 0x42, 0x12, 0x90, 0x76, 0xf8, 
+	0x27, 0x6e, 0x00, 0x01, 0xf0, 0x73, 0x12, 0x90, 
+	0x56, 0xf8, 0x27, 0x66, 0xf0, 0x00, 0x00, 0x01, 
+	0x4e, 0xf8, 0x27, 0x66, 0x6d, 0xe9, 0x00, 0x5e, 
+	0x56, 0xf8, 0x27, 0x68, 0xf0, 0x00, 0x00, 0x01, 
+	0x4e, 0xf8, 0x27, 0x68, 0x71, 0x04, 0x00, 0x12, 
+	0x6e, 0xea, 0xff, 0xff, 0x12, 0x18, 0x70, 0x04, 
+	0x00, 0x12, 0xee, 0x06, 0x8a, 0x17, 0x8a, 0x16, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xee, 0xfe, 
+	0x88, 0x0e, 0xf4, 0x95, 0xf0, 0x66, 0x00, 0x0d, 
+	0xf0, 0x00, 0x25, 0xa0, 0x88, 0x11, 0xf4, 0x95, 
+	0xf4, 0x95, 0x76, 0x81, 0x00, 0xff, 0x76, 0x00, 
+	0x00, 0x00, 0x76, 0x01, 0x00, 0x06, 0xf2, 0x74, 
+	0x06, 0x6c, 0xf0, 0x00, 0x00, 0x01, 0x76, 0x00, 
+	0x00, 0x00, 0x76, 0x01, 0x00, 0x06, 0x48, 0x11, 
+	0xf2, 0x74, 0x06, 0x6c, 0xf0, 0x00, 0x00, 0x07, 
+	0xee, 0x02, 0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 
+	0x88, 0x0e, 0xf4, 0x95, 0xf0, 0x66, 0x00, 0x0d, 
+	0xf0, 0x00, 0x24, 0x00, 0x88, 0x11, 0xf4, 0x95, 
+	0xf4, 0x95, 0x76, 0xe1, 0x00, 0x01, 0xff, 0xff, 
+	0x76, 0x81, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x02, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x03, 0x00, 0xff, 
+	0x8a, 0x11, 0xfc, 0x00, 0x4a, 0x11, 0xf4, 0x95, 
+	0x13, 0x03, 0x88, 0x11, 0xfa, 0x4d, 0x12, 0xec, 
+	0x71, 0x02, 0x00, 0x12, 0xf3, 0x10, 0x00, 0x01, 
+	0x89, 0x1a, 0xf4, 0x95, 0xf0, 0x72, 0x12, 0xeb, 
+	0x70, 0x91, 0x00, 0x12, 0x8a, 0x11, 0xfc, 0x00, 
+	0xf4, 0x95, 0x4a, 0x0b, 0x4a, 0x0c, 0x4a, 0x0d, 
+	0xf7, 0xb8, 0xee, 0xfe, 0x10, 0xf8, 0x00, 0x08, 
+	0x11, 0x06, 0xf1, 0xc0, 0x83, 0x00, 0xf4, 0x85, 
+	0x11, 0x06, 0xf7, 0x85, 0x81, 0x06, 0xf6, 0xb8, 
+	0xec, 0x0f, 0x1e, 0x06, 0x61, 0x00, 0x80, 0x00, 
+	0xf8, 0x20, 0x13, 0x05, 0xf4, 0x84, 0xee, 0x02, 
+	0x8a, 0x0d, 0x8a, 0x0c, 0x8a, 0x0b, 0xfc, 0x00, 
+	0xf4, 0x95, 0x4a, 0x0b, 0x4a, 0x0c, 0x4a, 0x0d, 
+	0xee, 0xfe, 0xf7, 0xb8, 0x80, 0x00, 0x10, 0xf8, 
+	0x00, 0x08, 0xf4, 0x85, 0x11, 0x06, 0xf7, 0x85, 
+	0x81, 0x06, 0xf6, 0xb8, 0xec, 0x0f, 0x1e, 0x06, 
+	0xf0, 0xf0, 0x61, 0x00, 0x80, 0x00, 0xf8, 0x20, 
+	0x13, 0x20, 0xf4, 0x84, 0xee, 0x02, 0x8a, 0x0d, 
+	0x8a, 0x0c, 0x8a, 0x0b, 0xfc, 0x00, 0x4a, 0x11, 
+	0x77, 0x11, 0x00, 0x7b, 0x76, 0x81, 0x2e, 0xec, 
+	0x77, 0x11, 0x00, 0x7b, 0xee, 0xff, 0x71, 0x81, 
+	0x00, 0x11, 0xee, 0x01, 0x76, 0xe1, 0x00, 0x01, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x04, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0x06, 0x00, 0x00, 0x76, 0xe1, 
+	0x00, 0x62, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x76, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x92, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0x94, 0x00, 0x00, 0x76, 0xe1, 
+	0x00, 0xb0, 0x00, 0x00, 0x76, 0xe1, 0x00, 0xb3, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0xbe, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0xbf, 0x00, 0x00, 0x76, 0xe1, 
+	0x00, 0xc1, 0x00, 0x00, 0x76, 0xe1, 0x00, 0xc3, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0xc5, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0xc7, 0x00, 0x00, 0x76, 0x81, 
+	0x00, 0x00, 0x8a, 0x11, 0xf4, 0x95, 0xf4, 0xe4, 
+	0x4a, 0x11, 0x4a, 0x16, 0x4a, 0x17, 0xee, 0xff, 
+	0xf4, 0x95, 0x71, 0x06, 0x00, 0x16, 0xfb, 0x80, 
+	0x16, 0xa2, 0x88, 0x17, 0xf4, 0x95, 0xf7, 0xb8, 
+	0x10, 0xf8, 0x00, 0x17, 0xf0, 0x10, 0x00, 0x02, 
+	0xfa, 0x46, 0x13, 0x88, 0x77, 0x11, 0x00, 0x00, 
+	0x10, 0xf8, 0x00, 0x17, 0xf0, 0x10, 0x00, 0x02, 
+	0xf8, 0x45, 0x13, 0xf9, 0x10, 0xf8, 0x00, 0x17, 
+	0xf8, 0x45, 0x14, 0x39, 0x10, 0xf8, 0x00, 0x17, 
+	0xf0, 0x10, 0x00, 0x01, 0xf8, 0x45, 0x14, 0x1f, 
+	0xf0, 0x73, 0x14, 0x52, 0x10, 0xf8, 0x00, 0x17, 
+	0xf0, 0x10, 0x00, 0x03, 0xf8, 0x45, 0x13, 0xd3, 
+	0x10, 0xf8, 0x00, 0x17, 0xf0, 0x10, 0x00, 0x06, 
+	0xf8, 0x44, 0x14, 0x52, 0x77, 0x12, 0x00, 0x7b, 
+	0x71, 0x82, 0x00, 0x14, 0x61, 0xe4, 0x00, 0x07, 
+	0x00, 0x40, 0xf8, 0x30, 0x14, 0x52, 0x49, 0x14, 
+	0x48, 0x17, 0xf6, 0x00, 0x88, 0x12, 0xf4, 0x95, 
+	0x77, 0x13, 0x00, 0x55, 0x77, 0x11, 0x00, 0x57, 
+	0x6d, 0xea, 0x00, 0x3b, 0xe5, 0x01, 0x10, 0xe6, 
+	0x00, 0x06, 0x80, 0x81, 0x48, 0x14, 0x00, 0xf8, 
+	0x00, 0x17, 0x88, 0x12, 0xf4, 0x95, 0x77, 0x11, 
+	0x00, 0x55, 0x10, 0xe2, 0x00, 0x40, 0x80, 0x81, 
+	0x77, 0x11, 0x00, 0x57, 0x10, 0xe6, 0x00, 0x07, 
+	0x80, 0x81, 0x77, 0x11, 0x00, 0x55, 0x10, 0xe2, 
+	0x00, 0x45, 0x80, 0x81, 0x10, 0xe6, 0x00, 0x08, 
+	0x77, 0x11, 0x00, 0x57, 0x80, 0x81, 0x77, 0x11, 
+	0x00, 0x55, 0x10, 0xe2, 0x00, 0x4a, 0x80, 0x81, 
+	0x77, 0x11, 0x00, 0x57, 0x10, 0xe6, 0x00, 0x09, 
+	0x80, 0x81, 0xf2, 0x73, 0x14, 0x52, 0x77, 0x11, 
+	0x03, 0xc0, 0x77, 0x12, 0x00, 0x7b, 0x10, 0x82, 
+	0xf0, 0x00, 0x00, 0x07, 0x88, 0x13, 0xf4, 0x95, 
+	0xf4, 0x95, 0x96, 0x1b, 0xf8, 0x30, 0x14, 0x52, 
+	0x10, 0xe3, 0x00, 0x35, 0x77, 0x12, 0x00, 0x55, 
+	0x80, 0x82, 0x77, 0x12, 0x00, 0x57, 0x10, 0xe6, 
+	0x00, 0x04, 0x80, 0x82, 0x77, 0x12, 0x00, 0x55, 
+	0x10, 0xe3, 0x00, 0x37, 0x80, 0x82, 0x77, 0x12, 
+	0x00, 0x57, 0x10, 0xe6, 0x00, 0x05, 0x80, 0x82, 
+	0x48, 0x11, 0xf0, 0x40, 0x00, 0x10, 0xf2, 0x73, 
+	0x14, 0x50, 0xf0, 0x40, 0x00, 0x20, 0x77, 0x12, 
+	0x00, 0x7b, 0x10, 0x82, 0xf0, 0x00, 0x00, 0x07, 
+	0x88, 0x12, 0xf4, 0x95, 0xf4, 0x95, 0x96, 0x0d, 
+	0xf8, 0x30, 0x14, 0x52, 0x10, 0xe2, 0x00, 0x34, 
+	0x77, 0x13, 0x00, 0x55, 0x80, 0x83, 0x77, 0x13, 
+	0x00, 0x57, 0x10, 0xe6, 0x00, 0x02, 0x80, 0x83, 
+	0x10, 0xe2, 0x00, 0x36, 0x77, 0x12, 0x00, 0x55, 
+	0x80, 0x82, 0x77, 0x12, 0x00, 0x57, 0x10, 0xe6, 
+	0x00, 0x03, 0x80, 0x82, 0x48, 0x11, 0xf0, 0x40, 
+	0x00, 0x04, 0xf2, 0x73, 0x14, 0x50, 0xf0, 0x40, 
+	0x00, 0x08, 0x77, 0x12, 0x00, 0x7b, 0x10, 0x82, 
+	0xf0, 0x00, 0x00, 0x07, 0x88, 0x12, 0xf4, 0x95, 
+	0xf4, 0x95, 0x96, 0x0e, 0xf8, 0x30, 0x14, 0x52, 
+	0x10, 0xe2, 0x00, 0x33, 0x77, 0x12, 0x00, 0x55, 
+	0x80, 0x82, 0x77, 0x12, 0x00, 0x57, 0x10, 0xe6, 
+	0x00, 0x01, 0x80, 0x82, 0x48, 0x11, 0xf2, 0x73, 
+	0x14, 0x50, 0xf0, 0x40, 0x00, 0x02, 0x77, 0x12, 
+	0x00, 0x7b, 0x10, 0x82, 0xf0, 0x00, 0x00, 0x07, 
+	0x88, 0x12, 0xf4, 0x95, 0xf4, 0x95, 0x96, 0x0f, 
+	0xf8, 0x30, 0x14, 0x52, 0x10, 0xe2, 0x00, 0x32, 
+	0x77, 0x12, 0x00, 0x55, 0x77, 0x13, 0x00, 0x57, 
+	0x80, 0x82, 0x48, 0x11, 0xe7, 0x62, 0xf0, 0x40, 
+	0x00, 0x01, 0xe5, 0x01, 0x88, 0x11, 0xf4, 0x95, 
+	0x77, 0x12, 0x00, 0x7b, 0x48, 0x11, 0x71, 0x82, 
+	0x00, 0x12, 0x1a, 0xe2, 0x00, 0x07, 0x80, 0xe2, 
+	0x00, 0x07, 0xf9, 0x80, 0x16, 0x9a, 0xee, 0x01, 
+	0x8a, 0x17, 0x48, 0x11, 0x8a, 0x16, 0x8a, 0x11, 
+	0xf4, 0xe4, 0x4a, 0x11, 0x88, 0x11, 0x77, 0x0e, 
+	0x00, 0x05, 0x77, 0x12, 0x00, 0x55, 0xe8, 0x04, 
+	0xf6, 0xb8, 0x28, 0xe1, 0x00, 0x02, 0xee, 0xff, 
+	0x80, 0x82, 0x77, 0x12, 0x00, 0x57, 0xf0, 0x20, 
+	0x80, 0x00, 0xee, 0x01, 0x1a, 0x82, 0x77, 0x12, 
+	0x00, 0x57, 0x80, 0x82, 0xe8, 0x01, 0x32, 0xe1, 
+	0x00, 0x02, 0xf5, 0x82, 0x77, 0x11, 0x00, 0x54, 
+	0xf6, 0x93, 0x18, 0x81, 0x77, 0x11, 0x00, 0x54, 
+	0xf2, 0xa0, 0x80, 0x81, 0x8a, 0x11, 0xf4, 0x95, 
+	0xf4, 0xe4, 0x4a, 0x11, 0x4a, 0x16, 0xf4, 0x95, 
+	0x71, 0x04, 0x00, 0x11, 0xfb, 0x80, 0x16, 0xa2, 
+	0x88, 0x16, 0xf4, 0x95, 0x77, 0x12, 0x00, 0x55, 
+	0x10, 0xe6, 0x00, 0x03, 0x80, 0x82, 0x77, 0x12, 
+	0x00, 0x56, 0x10, 0xe1, 0x00, 0x02, 0x77, 0x13, 
+	0x00, 0x56, 0x80, 0x82, 0x77, 0x12, 0x00, 0x56, 
+	0x10, 0xe1, 0x00, 0x03, 0x80, 0x82, 0x10, 0xe1, 
+	0x00, 0x04, 0x77, 0x12, 0x00, 0x56, 0x80, 0x82, 
+	0x77, 0x12, 0x00, 0x56, 0x10, 0xe1, 0x00, 0x01, 
+	0x80, 0x82, 0xe7, 0x12, 0xe5, 0x01, 0xf9, 0x80, 
+	0x16, 0x9a, 0x8a, 0x16, 0x8a, 0x11, 0xf4, 0xe4, 
+	0x4a, 0x11, 0x4a, 0x16, 0x4a, 0x17, 0xee, 0xf9, 
+	0x77, 0x11, 0x00, 0x7b, 0x76, 0x00, 0x00, 0x16, 
+	0x76, 0x01, 0x00, 0x17, 0x76, 0x02, 0x00, 0x1a, 
+	0x76, 0x03, 0x00, 0x1b, 0x76, 0x04, 0x00, 0x1c, 
+	0x76, 0x05, 0x00, 0x1d, 0x71, 0x81, 0x00, 0x17, 
+	0x71, 0xe7, 0x00, 0x06, 0x00, 0x11, 0x10, 0x81, 
+	0xf8, 0x44, 0x14, 0xdf, 0xf9, 0x80, 0x16, 0x53, 
+	0xf6, 0xb8, 0xfb, 0x80, 0x15, 0x85, 0xf0, 0x20, 
+	0xff, 0xff, 0xf6, 0xb8, 0xfb, 0x80, 0x16, 0x08, 
+	0xf0, 0x20, 0xff, 0xff, 0x77, 0x11, 0x00, 0x7b, 
+	0x71, 0x81, 0x00, 0x17, 0x76, 0xe7, 0x00, 0x06, 
+	0x00, 0x01, 0x48, 0x17, 0x77, 0x16, 0x00, 0x00, 
+	0x77, 0x10, 0x00, 0x04, 0x77, 0x15, 0x00, 0x03, 
+	0x77, 0x14, 0x00, 0x02, 0x77, 0x13, 0x00, 0x01, 
+	0xf0, 0x00, 0x00, 0x39, 0x76, 0xe7, 0x00, 0x08, 
+	0x00, 0x1f, 0x76, 0xe7, 0x00, 0x07, 0x00, 0x00, 
+	0x88, 0x0e, 0x77, 0x1a, 0x00, 0x05, 0x48, 0x17, 
+	0xf0, 0x00, 0x00, 0x09, 0x88, 0x12, 0x48, 0x18, 
+	0x88, 0x19, 0xe8, 0x00, 0xf0, 0x72, 0x15, 0x2c, 
+	0x73, 0x19, 0x00, 0x11, 0x76, 0x82, 0x00, 0x00, 
+	0x11, 0x91, 0x73, 0x11, 0x00, 0x19, 0x70, 0xe2, 
+	0x00, 0x03, 0x00, 0x16, 0x70, 0xe2, 0x00, 0x04, 
+	0x00, 0x13, 0x70, 0xe2, 0x00, 0x05, 0x00, 0x14, 
+	0x81, 0xe2, 0x00, 0x01, 0x70, 0xe2, 0x00, 0x06, 
+	0x00, 0x15, 0x70, 0xe2, 0x00, 0x07, 0x00, 0x10, 
+	0x80, 0xe2, 0x00, 0x02, 0x73, 0x0e, 0x00, 0x11, 
+	0xf1, 0x00, 0x00, 0x1e, 0x6d, 0xee, 0x00, 0x05, 
+	0x6d, 0xeb, 0x00, 0x05, 0x6d, 0xec, 0x00, 0x05, 
+	0x6d, 0xed, 0x00, 0x05, 0x6d, 0xe8, 0x00, 0x05, 
+	0xf0, 0x00, 0x00, 0x01, 0x81, 0x91, 0x6d, 0xea, 
+	0x00, 0x08, 0x73, 0x11, 0x00, 0x0e, 0xee, 0x07, 
+	0x76, 0xe7, 0x00, 0x41, 0x00, 0x24, 0x76, 0xe7, 
+	0x00, 0x46, 0x00, 0x25, 0x76, 0xe7, 0x00, 0x4b, 
+	0x00, 0x26, 0x76, 0xe7, 0x00, 0x50, 0x00, 0x27, 
+	0x8a, 0x17, 0x8a, 0x16, 0x8a, 0x11, 0xf4, 0xe4, 
+	0x4a, 0x11, 0x4a, 0x16, 0xee, 0xfe, 0x88, 0x11, 
+	0x56, 0x06, 0x4e, 0x00, 0xf9, 0x80, 0x16, 0xa2, 
+	0xf7, 0xb8, 0x10, 0xf8, 0x00, 0x11, 0xf0, 0x10, 
+	0xff, 0xff, 0xfa, 0x45, 0x15, 0x60, 0x77, 0x16, 
+	0xff, 0xff, 0x77, 0x12, 0x00, 0x7b, 0x49, 0x11, 
+	0x10, 0x82, 0xf6, 0x03, 0xf0, 0x00, 0x00, 0x09, 
+	0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x10, 0x81, 
+	0xf8, 0x44, 0x15, 0x71, 0xf2, 0x73, 0x15, 0x71, 
+	0xf4, 0x95, 0xe7, 0x16, 0x77, 0x11, 0x00, 0x7b, 
+	0x10, 0x81, 0xf0, 0x00, 0x00, 0x09, 0x88, 0x11, 
+	0xf4, 0x95, 0x77, 0x12, 0x00, 0x06, 0x10, 0x81, 
+	0xf8, 0x45, 0x15, 0x5c, 0x6e, 0xea, 0xff, 0xff, 
+	0x15, 0x69, 0x6d, 0xe9, 0x00, 0x08, 0x76, 0x86, 
+	0x00, 0x01, 0xe9, 0x01, 0x56, 0x00, 0xf1, 0x80, 
+	0x10, 0xf8, 0x00, 0x0b, 0xf8, 0x45, 0x15, 0x7e, 
+	0xfb, 0x80, 0x15, 0x85, 0xf4, 0x95, 0x48, 0x16, 
+	0xf9, 0x80, 0x16, 0x9a, 0xee, 0x02, 0x48, 0x16, 
+	0x8a, 0x16, 0x8a, 0x11, 0xf4, 0xe4, 0x4a, 0x11, 
+	0xee, 0xff, 0xfb, 0x80, 0x16, 0xa2, 0x88, 0x11, 
+	0xf4, 0x95, 0x77, 0x10, 0xff, 0xff, 0xf4, 0xa9, 
+	0xf8, 0x30, 0x15, 0xc4, 0x10, 0xe1, 0x00, 0x03, 
+	0x77, 0x12, 0x00, 0x55, 0x80, 0x82, 0x77, 0x12, 
+	0x00, 0x56, 0x76, 0x82, 0x00, 0x00, 0x77, 0x12, 
+	0x00, 0x56, 0x76, 0x82, 0x00, 0x00, 0x77, 0x12, 
+	0x00, 0x56, 0x76, 0x82, 0x00, 0x00, 0x77, 0x12, 
+	0x00, 0x56, 0x76, 0x82, 0x00, 0x00, 0x77, 0x12, 
+	0x00, 0x56, 0x76, 0x82, 0x00, 0x00, 0x10, 0xe1, 
+	0x00, 0x02, 0xf0, 0x00, 0x00, 0x08, 0x32, 0xf8, 
+	0x00, 0x08, 0x77, 0x12, 0x00, 0x54, 0xe8, 0x01, 
+	0xf4, 0x82, 0xf4, 0x93, 0x18, 0x82, 0x77, 0x12, 
+	0x00, 0x54, 0xf0, 0x40, 0x00, 0x00, 0x80, 0x82, 
+	0x10, 0xe1, 0x00, 0x01, 0xf9, 0x80, 0x16, 0x76, 
+	0x10, 0xe1, 0x00, 0x01, 0xf9, 0x80, 0x16, 0x66, 
+	0xf0, 0x73, 0x16, 0x03, 0x77, 0x11, 0x00, 0x7b, 
+	0x71, 0x81, 0x00, 0x11, 0x71, 0xe1, 0x00, 0x07, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x10, 0xe1, 
+	0x00, 0x09, 0xf9, 0x80, 0x15, 0x85, 0x77, 0x11, 
+	0x00, 0x7b, 0x71, 0x81, 0x00, 0x11, 0x10, 0xe1, 
+	0x00, 0x09, 0xfb, 0x80, 0x15, 0x85, 0xf0, 0x00, 
+	0x00, 0x08, 0x77, 0x11, 0x00, 0x7b, 0x71, 0x81, 
+	0x00, 0x11, 0x10, 0xe1, 0x00, 0x09, 0xfb, 0x80, 
+	0x15, 0x85, 0xf0, 0x00, 0x00, 0x10, 0x77, 0x11, 
+	0x00, 0x7b, 0x71, 0x81, 0x00, 0x11, 0x10, 0xe1, 
+	0x00, 0x09, 0xfb, 0x80, 0x15, 0x85, 0xf0, 0x00, 
+	0x00, 0x18, 0x77, 0x11, 0x00, 0x7b, 0x71, 0x81, 
+	0x00, 0x11, 0x10, 0xe1, 0x00, 0x09, 0xfb, 0x80, 
+	0x15, 0x85, 0xf0, 0x00, 0x00, 0x20, 0x77, 0x11, 
+	0x00, 0x7b, 0x71, 0x81, 0x00, 0x11, 0x10, 0xe1, 
+	0x00, 0x09, 0xfb, 0x80, 0x15, 0x85, 0xf0, 0x00, 
+	0x00, 0x28, 0xf9, 0x80, 0x16, 0x9a, 0xee, 0x01, 
+	0x8a, 0x11, 0xf4, 0xe4, 0x4a, 0x11, 0xee, 0xff, 
+	0xfb, 0x80, 0x16, 0xa2, 0x88, 0x11, 0xf4, 0x95, 
+	0x77, 0x10, 0xff, 0xff, 0xf4, 0xa9, 0xf8, 0x30, 
+	0x16, 0x41, 0x77, 0x11, 0x00, 0x55, 0x76, 0x81, 
+	0x00, 0x1e, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0x76, 0x81, 
+	0x00, 0x00, 0x77, 0x11, 0x00, 0x56, 0xf2, 0x73, 
+	0x16, 0x4e, 0x76, 0x81, 0x00, 0x00, 0x77, 0x11, 
+	0x00, 0x7b, 0x71, 0x81, 0x00, 0x11, 0x71, 0xe1, 
+	0x00, 0x07, 0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 
+	0x10, 0xe1, 0x00, 0x39, 0xf9, 0x80, 0x16, 0x08, 
+	0xf9, 0x80, 0x16, 0x9a, 0xee, 0x01, 0x8a, 0x11, 
+	0xf4, 0xe4, 0x4a, 0x11, 0x77, 0x11, 0x00, 0x7b, 
+	0x10, 0x81, 0xf0, 0x00, 0x00, 0x04, 0x88, 0x11, 
+	0xf4, 0x95, 0xf4, 0x95, 0x10, 0x81, 0xfa, 0x44, 
+	0x16, 0x63, 0xf4, 0x95, 0xee, 0xff, 0x76, 0x81, 
+	0x00, 0x01, 0xee, 0x01, 0x8a, 0x11, 0xf4, 0xe4, 
+	0xf0, 0x10, 0x00, 0x10, 0x4a, 0x11, 0x32, 0xf8, 
+	0x00, 0x08, 0xee, 0xff, 0x77, 0x11, 0x00, 0x01, 
+	0xe8, 0x01, 0xee, 0x01, 0xf4, 0x82, 0x1a, 0x81, 
+	0x80, 0x81, 0x8a, 0x11, 0xf4, 0x95, 0xf4, 0xe4, 
+	0xf0, 0x10, 0x00, 0x10, 0x4a, 0x11, 0x32, 0xf8, 
+	0x00, 0x08, 0xee, 0xff, 0xe8, 0x01, 0x77, 0x11, 
+	0x00, 0x00, 0xf4, 0x82, 0xee, 0x01, 0xf4, 0x93, 
+	0x18, 0x81, 0x80, 0x81, 0x8a, 0x11, 0xf4, 0x95, 
+	0xf4, 0xe4, 0x4a, 0x11, 0xf0, 0x10, 0x00, 0x10, 
+	0x77, 0x11, 0x00, 0x00, 0x32, 0xf8, 0x00, 0x08, 
+	0xee, 0xff, 0x11, 0x81, 0xe8, 0x01, 0xee, 0x01, 
+	0x77, 0x11, 0x00, 0x00, 0xf4, 0x82, 0xf2, 0xa0, 
+	0x80, 0x81, 0x8a, 0x11, 0xf4, 0x95, 0xf4, 0xe4, 
+	0xf2, 0x73, 0x16, 0x9e, 0xf6, 0xbb, 0xf4, 0x95, 
+	0xf4, 0x95, 0xf4, 0x95, 0xf4, 0x95, 0xf4, 0xe4, 
+	0xf2, 0x73, 0x16, 0xa6, 0xf7, 0xbb, 0xf4, 0x95, 
+	0xf4, 0x95, 0xf4, 0x95, 0xf4, 0x95, 0xf4, 0xe4, 
+	0x4a, 0x11, 0x4a, 0x16, 0xf4, 0x95, 0x71, 0x04, 
+	0x00, 0x16, 0xfb, 0x80, 0x16, 0xa2, 0x88, 0x11, 
+	0xf4, 0x95, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x0e, 0x10, 0xe6, 0x00, 0x0e, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x80, 0x82, 
+	0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x0d, 0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 
+	0x10, 0xe6, 0x00, 0x0d, 0x80, 0x82, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x0c, 
+	0x10, 0xe6, 0x00, 0x0c, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x80, 0x82, 0x71, 0xe1, 0x00, 0x05, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x0b, 0x10, 0xe6, 
+	0x00, 0x0b, 0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 
+	0x80, 0x82, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x0a, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x10, 0xe6, 0x00, 0x0a, 0x80, 0x82, 
+	0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x09, 0x10, 0xe6, 0x00, 0x09, 0x71, 0xe1, 
+	0x00, 0x06, 0x00, 0x12, 0x80, 0x82, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x08, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x10, 0xe6, 
+	0x00, 0x08, 0x80, 0x82, 0x71, 0xe1, 0x00, 0x05, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x07, 0x10, 0xe6, 
+	0x00, 0x07, 0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 
+	0x80, 0x82, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x06, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x10, 0xe6, 0x00, 0x06, 0x80, 0x82, 
+	0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x05, 0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 
+	0x10, 0xe6, 0x00, 0x05, 0x80, 0x82, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x04, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x10, 0xe6, 
+	0x00, 0x04, 0x80, 0x82, 0x71, 0xe1, 0x00, 0x05, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x03, 0x71, 0xe1, 
+	0x00, 0x06, 0x00, 0x12, 0x10, 0xe6, 0x00, 0x03, 
+	0x80, 0x82, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x02, 0x10, 0xe6, 0x00, 0x02, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x80, 0x82, 
+	0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x01, 0x10, 0xe6, 0x00, 0x01, 0x71, 0xe1, 
+	0x00, 0x06, 0x00, 0x12, 0x80, 0x82, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x13, 0xe7, 0x62, 
+	0xe5, 0x01, 0xf9, 0x80, 0x16, 0x9a, 0x8a, 0x16, 
+	0x8a, 0x11, 0xf4, 0xe4, 0x4a, 0x11, 0x88, 0x11, 
+	0xf4, 0x95, 0xf4, 0x95, 0x71, 0xe1, 0x00, 0x05, 
+	0x00, 0x12, 0xee, 0xff, 0x76, 0x82, 0x00, 0x00, 
+	0xee, 0x01, 0x71, 0xe1, 0x00, 0x06, 0x00, 0x11, 
+	0x69, 0x81, 0x00, 0x01, 0x8a, 0x11, 0xf4, 0x95, 
+	0xf4, 0xe4, 0x4a, 0x11, 0x88, 0x11, 0xf4, 0x95, 
+	0xf4, 0x95, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0xee, 0xff, 0x76, 0x82, 0x00, 0x01, 0xee, 0x01, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x11, 0x69, 0x81, 
+	0x00, 0x01, 0x8a, 0x11, 0xf4, 0x95, 0xf4, 0xe4, 
+	0x4a, 0x11, 0x77, 0x11, 0x00, 0x7b, 0x10, 0x81, 
+	0xf0, 0x00, 0x00, 0x94, 0x88, 0x11, 0xf4, 0x95, 
+	0xf4, 0x95, 0x10, 0x81, 0xfa, 0x44, 0x17, 0x9c, 
+	0xf4, 0x95, 0xee, 0xff, 0xf9, 0x80, 0x16, 0x53, 
+	0x77, 0x11, 0x00, 0x7b, 0x10, 0x81, 0xf0, 0x00, 
+	0x00, 0x94, 0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 
+	0x76, 0x81, 0x00, 0x01, 0xee, 0x01, 0x76, 0xe1, 
+	0x00, 0x01, 0x00, 0x00, 0x76, 0xe1, 0x00, 0x02, 
+	0x00, 0x21, 0x76, 0xe1, 0x00, 0x03, 0x00, 0x20, 
+	0x76, 0xe1, 0x00, 0x04, 0x00, 0x23, 0x76, 0xe1, 
+	0x00, 0x05, 0x00, 0x22, 0x76, 0xe1, 0x00, 0x06, 
+	0x00, 0x38, 0x76, 0xe1, 0x00, 0x07, 0x00, 0x39, 
+	0x76, 0xe1, 0x00, 0x08, 0x00, 0x15, 0x76, 0xe1, 
+	0x00, 0x09, 0x00, 0x14, 0x76, 0xe1, 0x00, 0x0a, 
+	0x00, 0x00, 0x76, 0xe1, 0x00, 0x0b, 0x00, 0x41, 
+	0x76, 0xe1, 0x00, 0x0c, 0x00, 0x40, 0x76, 0xe1, 
+	0x00, 0x0d, 0x00, 0x43, 0x76, 0xe1, 0x00, 0x0e, 
+	0x00, 0x42, 0x76, 0xe1, 0x00, 0x0f, 0x00, 0x48, 
+	0x76, 0xe1, 0x00, 0x10, 0x00, 0x49, 0x76, 0xe1, 
+	0x00, 0x11, 0x00, 0x1b, 0x76, 0xe1, 0x00, 0x12, 
+	0x00, 0x1a, 0x8a, 0x11, 0xf4, 0x95, 0xf4, 0xe4, 
+	0x4a, 0x11, 0xee, 0xfd, 0x88, 0x11, 0x56, 0x06, 
+	0x4e, 0x00, 0xf9, 0x80, 0x16, 0xa2, 0x77, 0x12, 
+	0x00, 0x7b, 0x77, 0x0e, 0x00, 0x09, 0x10, 0x82, 
+	0x28, 0xf8, 0x00, 0x11, 0xf0, 0x00, 0x00, 0x95, 
+	0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x10, 0x81, 
+	0xf8, 0x45, 0x17, 0xf0, 0xf2, 0x73, 0x17, 0xfd, 
+	0x77, 0x11, 0xff, 0xff, 0x76, 0x81, 0x00, 0x01, 
+	0xe9, 0x01, 0x56, 0x00, 0xf1, 0x80, 0x10, 0xf8, 
+	0x00, 0x0b, 0xf8, 0x45, 0x17, 0xfd, 0xfb, 0x80, 
+	0x18, 0x10, 0xf4, 0x95, 0x48, 0x11, 0xf9, 0x80, 
+	0x16, 0x9a, 0xee, 0x03, 0x48, 0x11, 0x8a, 0x11, 
+	0xf4, 0x95, 0xf4, 0xe4, 0x4a, 0x11, 0x88, 0x11, 
+	0xf4, 0x95, 0xee, 0xff, 0x71, 0xe1, 0x00, 0x01, 
+	0x00, 0x11, 0xee, 0x01, 0x10, 0x81, 0x8a, 0x11, 
+	0xf4, 0x95, 0xf4, 0xe4, 0x4a, 0x11, 0xee, 0xff, 
+	0xfb, 0x80, 0x16, 0xa2, 0x88, 0x11, 0xf4, 0x95, 
+	0x77, 0x10, 0xff, 0xff, 0xf4, 0xa9, 0xf8, 0x30, 
+	0x18, 0xc3, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x00, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x01, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x00, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x02, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x03, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x00, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x04, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x05, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x00, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x06, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x01, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x07, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x76, 0x82, 
+	0x20, 0x00, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x08, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x09, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x00, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x0a, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x0b, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x00, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x0c, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x71, 0xe1, 
+	0x00, 0x05, 0x00, 0x12, 0x76, 0x82, 0x00, 0x0d, 
+	0x71, 0xe1, 0x00, 0x06, 0x00, 0x12, 0x76, 0x82, 
+	0x00, 0x00, 0x71, 0xe1, 0x00, 0x05, 0x00, 0x12, 
+	0x76, 0x82, 0x00, 0x0e, 0x71, 0xe1, 0x00, 0x06, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x00, 0x10, 0xe1, 
+	0x00, 0x07, 0xf9, 0x80, 0x16, 0x76, 0x10, 0xe1, 
+	0x00, 0x08, 0xf9, 0x80, 0x16, 0x76, 0x10, 0xe1, 
+	0x00, 0x07, 0xf9, 0x80, 0x16, 0x66, 0x10, 0xe1, 
+	0x00, 0x08, 0xf9, 0x80, 0x16, 0x66, 0xf0, 0x73, 
+	0x18, 0xd1, 0x77, 0x11, 0x00, 0x7b, 0x10, 0x81, 
+	0xfb, 0x80, 0x18, 0x10, 0xf0, 0x00, 0x00, 0x95, 
+	0x77, 0x11, 0x00, 0x7b, 0x10, 0x81, 0xfb, 0x80, 
+	0x18, 0x10, 0xf0, 0x00, 0x00, 0x9e, 0xf9, 0x80, 
+	0x16, 0x9a, 0xee, 0x01, 0x8a, 0x11, 0xf4, 0xe4, 
+	0x4a, 0x11, 0x88, 0x11, 0xee, 0xff, 0xf4, 0x95, 
+	0x10, 0x04, 0x71, 0xe1, 0x00, 0x03, 0x00, 0x11, 
+	0xee, 0x01, 0x80, 0x81, 0x8a, 0x11, 0xf4, 0x95, 
+	0xf4, 0xe4, 0x4a, 0x11, 0x4a, 0x16, 0xf4, 0x95, 
+	0x71, 0x04, 0x00, 0x16, 0xfb, 0x80, 0x16, 0xa2, 
+	0x88, 0x11, 0xf4, 0x95, 0x71, 0xe1, 0x00, 0x02, 
+	0x00, 0x12, 0x76, 0x82, 0x00, 0x10, 0x10, 0xe6, 
+	0x00, 0x01, 0x71, 0xe1, 0x00, 0x03, 0x00, 0x12, 
+	0x80, 0x82, 0x71, 0xe1, 0x00, 0x04, 0x00, 0x12, 
+	0x10, 0xe6, 0x00, 0x02, 0x80, 0x82, 0xe7, 0x62, 
+	0x71, 0xe1, 0x00, 0x02, 0x00, 0x13, 0xe5, 0x01, 
+	0xf9, 0x80, 0x16, 0x9a, 0x8a, 0x16, 0x8a, 0x11, 
+	0xf4, 0xe4, 0x4a, 0x11, 0x88, 0x11, 0xee, 0xff, 
+	0xee, 0x01, 0x10, 0xe1, 0x00, 0x01, 0x8a, 0x11, 
+	0xf4, 0x95, 0xf4, 0xe4, 0x4a, 0x11, 0x77, 0x11, 
+	0x00, 0x7b, 0x10, 0x81, 0xf0, 0x00, 0x00, 0xb3, 
+	0x88, 0x11, 0xf4, 0x95, 0xf4, 0x95, 0x10, 0x81, 
+	0xfa, 0x44, 0x19, 0x2a, 0xf4, 0x95, 0xee, 0xff, 
+	0xf9, 0x80, 0x16, 0x53, 0x77, 0x11, 0x00, 0x7b, 
+	0x10, 0x81, 0xf0, 0x00, 0x00, 0xb3, 0x88, 0x11, 
+	0xf4, 0x95, 0xf4, 0x95, 0x76, 0x81, 0x00, 0x01, 
+	0xee, 0x01, 0x76, 0xe1, 0x00, 0x01, 0x00, 0x00, 
+	0x76, 0xe1, 0x00, 0x02, 0x00, 0x13, 0x76, 0xe1, 
+	0x00, 0x03, 0x00, 0x26, 0x76, 0xe1, 0x00, 0x04, 
+	0x00, 0x25, 0x76, 0xe1, 0x00, 0x05, 0x00, 0x24, 
+	0x76, 0xe1, 0x00, 0x06, 0x00, 0x00, 0x76, 0xe1, 
+	0x00, 0x07, 0x00, 0x17, 0x76, 0xe1, 0x00, 0x08, 
+	0x00, 0x32, 0x76, 0xe1, 0x00, 0x09, 0x00, 0x31, 
+	0x76, 0xe1, 0x00, 0x0a, 0x00, 0x30, 0x8a, 0x11, 
+	0xf4, 0x95, 0xf4, 0xe4, 0x4a, 0x11, 0x4a, 0x16, 
+	0x4a, 0x17, 0xee, 0xff, 0xf4, 0x95, 0x71, 0x06, 
+	0x00, 0x17, 0xfb, 0x80, 0x16, 0xa2, 0x88, 0x11, 
+	0xf4, 0x95, 0xf7, 0xb8, 0x10, 0xf8, 0x00, 0x11, 
+	0xf0, 0x10, 0xff, 0xff, 0xfa, 0x45, 0x19, 0x73, 
+	0x77, 0x16, 0xff, 0xff, 0x77, 0x12, 0x00, 0x7b, 
+	0x77, 0x0e, 0x00, 0x05, 0x10, 0x82, 0x28, 0xf8, 
+	0x00, 0x11, 0xf0, 0x00, 0x00, 0xb4, 0x88, 0x11, 
+	0xf4, 0x95, 0xf4, 0x95, 0x10, 0x81, 0xf8, 0x44, 
+	0x19, 0x84, 0xf2, 0x73, 0x19, 0x84, 0xf4, 0x95, 
+	0xe7, 0x16, 0x77, 0x11, 0x00, 0x7b, 0x10, 0x81, 
+	0xf0, 0x00, 0x00, 0xb4, 0x88, 0x11, 0xf4, 0x95, 
+	0x77, 0x12, 0x00, 0x02, 0x10, 0x81, 0xf8, 0x45, 
+	0x19, 0x6f, 0x6e, 0xea, 0xff, 0xff, 0x19, 0x7c, 
+	0x6d, 0xe9, 0x00, 0x05, 0x61, 0xf8, 0x00, 0x17, 
+	0x00, 0x01, 0xfa, 0x20, 0x19, 0x8f, 0x76, 0x86, 
+	0x00, 0x01, 0xfb, 0x80, 0x19, 0x97, 0xf4, 0x95, 
+	0x48, 0x16, 0xf9, 0x80, 0x16, 0x9a, 0xee, 0x01, 
+	0x8a, 0x17, 0x48, 0x16, 0x8a, 0x16, 0x8a, 0x11, 
+	0xf4, 0xe4, 0x4a, 0x11, 0xee, 0xff, 0xfb, 0x80, 
+	0x16, 0xa2, 0x88, 0x11, 0xf4, 0x95, 0x77, 0x10, 
+	0xff, 0xff, 0xf4, 0xa9, 0xf8, 0x30, 0x19, 0xcc, 
+	0x71, 0xe1, 0x00, 0x02, 0x00, 0x12, 0x69, 0x82, 
+	0x00, 0x10, 0x71, 0xe1, 0x00, 0x02, 0x00, 0x12, 
+	0x68, 0x82, 0xf7, 0xff, 0x71, 0xe1, 0x00, 0x02, 
+	0x00, 0x12, 0x68, 0x82, 0xfb, 0xff, 0x71, 0xe1, 
+	0x00, 0x02, 0x00, 0x12, 0x68, 0x82, 0xff, 0xf0, 
+	0x71, 0xe1, 0x00, 0x03, 0x00, 0x12, 0x76, 0x82, 
+	0xff, 0xff, 0x71, 0xe1, 0x00, 0x04, 0x00, 0x12, 
+	0x76, 0x82, 0xff, 0xff, 0x71, 0xe1, 0x00, 0x02, 
+	0x00, 0x12, 0x69, 0x82, 0x00, 0x20, 0x71, 0xe1, 
+	0x00, 0x02, 0x00, 0x11, 0xf2, 0x73, 0x19, 0xda, 
+	0x68, 0x81, 0xff, 0xef, 0x77, 0x11, 0x00, 0x7b, 
+	0x10, 0x81, 0xfb, 0x80, 0x19, 0x97, 0xf0, 0x00, 
+	0x00, 0xb4, 0x77, 0x11, 0x00, 0x7b, 0x10, 0x81, 
+	0xfb, 0x80, 0x19, 0x97, 0xf0, 0x00, 0x00, 0xb9, 
+	0xf9, 0x80, 0x16, 0x9a, 0xee, 0x01, 0x8a, 0x11, 
+	0xf4, 0xe4, 0x00, 0xa4, 0x00, 0x00, 0x19, 0xdf, 
+	0x00, 0x01, 0x2a, 0xe6, 0x00, 0x00, 0x00, 0x01, 
+	0x2a, 0xe7, 0x00, 0x00, 0x00, 0x03, 0x2a, 0x12, 
+	0x0c, 0x01, 0xc3, 0x4f, 0x00, 0x00, 0x00, 0x01, 
+	0x2a, 0x15, 0x00, 0x00, 0x00, 0x02, 0x2a, 0x16, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x2a, 0x5d, 
+	0x00, 0x43, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x79, 
+	0x00, 0x72, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 
+	0x00, 0x74, 0x00, 0x20, 0x00, 0x54, 0x00, 0x65, 
+	0x00, 0x63, 0x00, 0x68, 0x00, 0x6e, 0x00, 0x6f, 
+	0x00, 0x54, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 
+	0x00, 0x64, 0x00, 0x20, 0x00, 0x41, 0x00, 0x47, 
+	0x00, 0x00, 0x00, 0x04, 0x2a, 0x76, 0x00, 0x30, 
+	0x00, 0x2e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x0c, 
+	0x2a, 0x7a, 0x00, 0x46, 0x00, 0x65, 0x00, 0x62, 
+	0x00, 0x20, 0x00, 0x32, 0x00, 0x37, 0x00, 0x20, 
+	0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 
+	0x00, 0x00, 0x00, 0x09, 0x2a, 0x86, 0x00, 0x31, 
+	0x00, 0x34, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x35, 
+	0x00, 0x3a, 0x00, 0x33, 0x00, 0x33, 0x00, 0x00, 
+	0x00, 0x0f, 0x2a, 0x8f, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x01, 0x2a, 0x9e, 0x00, 0x00, 
+	0x00, 0x01, 0x2a, 0x9f, 0x00, 0x00, 0x00, 0x01, 
+	0x2a, 0xa0, 0x00, 0x00, 0x00, 0x01, 0x2a, 0xa1, 
+	0x00, 0x00, 0x00, 0x01, 0x2a, 0xa2, 0x00, 0x00, 
+	0x00, 0x01, 0x29, 0x7e, 0x00, 0x00, 0x00, 0x02, 
+	0x29, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 
+	0x29, 0x82, 0xff, 0xff, 0x00, 0x01, 0x2a, 0xa7, 
+	0x00, 0x00, 0x00, 0x05, 0x2a, 0xa8, 0x71, 0x41, 
+	0x20, 0x00, 0x20, 0x00, 0x00, 0x23, 0x04, 0x00, 
+	0x00, 0x0a, 0x2a, 0xad, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x0f, 0x2a, 0xb7, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x40, 0x00, 0xa0, 0x82, 0x40, 
+	0x00, 0x08, 0x30, 0x7f, 0x00, 0x80, 0x01, 0x80, 
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x00, 0x00, 0x01, 0x27, 0x6e, 0x00, 0x00, 
+	0x00, 0x01, 0x27, 0x6f, 0x00, 0x00, 0x00, 0x00, 
+	0x00, 0x09, 0x00, 0x00, 0x1a, 0x83, 0x04, 0xe8, 
+	0x04, 0xcf, 0x04, 0xc5, 0x04, 0xba, 0x04, 0xb0, 
+	0x04, 0xac, 0x04, 0x9c, 0x04, 0x8c, 0x04, 0x81, 
+	0x00, 0x78, 0x00, 0x00, 0x01, 0x00, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xaa, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x02, 0x23, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x05, 0xe5, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x02, 0xb5, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x0e, 0x33, 0xf4, 0x95, 0xf4, 0x95, 0xf2, 0x73, 
+	0x07, 0xef, 0xf4, 0x95, 0xf4, 0x95, 0x00, 0x00, 
+};
+
diff --git a/drivers/media/dvb/ttusb-dec/Kconfig b/drivers/media/dvb/ttusb-dec/Kconfig
new file mode 100644
index 0000000..c334526
--- /dev/null
+++ b/drivers/media/dvb/ttusb-dec/Kconfig
@@ -0,0 +1,21 @@
+config DVB_TTUSB_DEC
+	tristate "Technotrend/Hauppauge USB DEC devices"
+	depends on DVB_CORE && USB
+	select FW_LOADER
+	select CRC32
+	help
+	  Support for external USB adapters designed by Technotrend and
+	  produced by Hauppauge, shipped under the brand name 'DEC2000-t'
+	  and 'DEC3000-s'.
+
+          Even if these devices have a MPEG decoder built in, they transmit
+	  only compressed MPEG data over the USB bus, so you need
+	  an external software decoder to watch TV on your computer.
+
+          This driver needs external firmware. Please use the commands
+          "<kerneldir>/Documentation/dvb/get_dvb_firmware dec2000t",
+          "<kerneldir>/Documentation/dvb/get_dvb_firmware dec2540t",
+          "<kerneldir>/Documentation/dvb/get_dvb_firmware dec3000s",
+          download/extract them, and then copy them to /usr/lib/hotplug/firmware.
+
+	  Say Y if you own such a device and want to use it.
diff --git a/drivers/media/dvb/ttusb-dec/Makefile b/drivers/media/dvb/ttusb-dec/Makefile
new file mode 100644
index 0000000..b41bf1f
--- /dev/null
+++ b/drivers/media/dvb/ttusb-dec/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
new file mode 100644
index 0000000..64e771b
--- /dev/null
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -0,0 +1,1744 @@
+/*
+ * TTUSB DEC Driver
+ *
+ * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.org>
+ * IR support by Peter Beutner <p.beutner@gmx.net>
+ *
+ * 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 <asm/semaphore.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/input.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_filter.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "ttusbdecfe.h"
+
+static int debug;
+static int output_pva;
+static int enable_rc;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+module_param(output_pva, int, 0444);
+MODULE_PARM_DESC(output_pva, "Output PVA from dvr device (default:off)");
+module_param(enable_rc, int, 0644);
+MODULE_PARM_DESC(enable_rc, "Turn on/off IR remote control(default: off)");
+
+#define dprintk	if (debug) printk
+
+#define DRIVER_NAME		"TechnoTrend/Hauppauge DEC USB"
+
+#define COMMAND_PIPE		0x03
+#define RESULT_PIPE		0x04
+#define IN_PIPE			0x08
+#define OUT_PIPE		0x07
+#define IRQ_PIPE		0x0A
+
+#define COMMAND_PACKET_SIZE	0x3c
+#define ARM_PACKET_SIZE		0x1000
+#define IRQ_PACKET_SIZE		0x8
+
+#define ISO_BUF_COUNT		0x04
+#define FRAMES_PER_ISO_BUF	0x04
+#define ISO_FRAME_SIZE		0x0380
+
+#define	MAX_PVA_LENGTH		6144
+
+enum ttusb_dec_model {
+	TTUSB_DEC2000T,
+	TTUSB_DEC2540T,
+	TTUSB_DEC3000S
+};
+
+enum ttusb_dec_packet_type {
+	TTUSB_DEC_PACKET_PVA,
+	TTUSB_DEC_PACKET_SECTION,
+	TTUSB_DEC_PACKET_EMPTY
+};
+
+enum ttusb_dec_interface {
+	TTUSB_DEC_INTERFACE_INITIAL,
+	TTUSB_DEC_INTERFACE_IN,
+	TTUSB_DEC_INTERFACE_OUT
+};
+
+struct ttusb_dec {
+	enum ttusb_dec_model		model;
+	char				*model_name;
+	char				*firmware_name;
+	int				can_playback;
+
+	/* DVB bits */
+	struct dvb_adapter		*adapter;
+	struct dmxdev			dmxdev;
+	struct dvb_demux		demux;
+	struct dmx_frontend		frontend;
+	struct dvb_net			dvb_net;
+	struct dvb_frontend*		fe;
+
+	u16			pid[DMX_PES_OTHER];
+
+	/* USB bits */
+	struct usb_device		*udev;
+	u8				trans_count;
+	unsigned int			command_pipe;
+	unsigned int			result_pipe;
+	unsigned int			in_pipe;
+	unsigned int			out_pipe;
+	unsigned int			irq_pipe;
+	enum ttusb_dec_interface	interface;
+	struct semaphore		usb_sem;
+
+	void			*irq_buffer;
+	struct urb		*irq_urb;
+	dma_addr_t		irq_dma_handle;
+	void			*iso_buffer;
+	dma_addr_t		iso_dma_handle;
+	struct urb		*iso_urb[ISO_BUF_COUNT];
+	int			iso_stream_count;
+	struct semaphore	iso_sem;
+
+	u8				packet[MAX_PVA_LENGTH + 4];
+	enum ttusb_dec_packet_type	packet_type;
+	int				packet_state;
+	int				packet_length;
+	int				packet_payload_length;
+	u16				next_packet_id;
+
+	int				pva_stream_count;
+	int				filter_stream_count;
+
+	struct dvb_filter_pes2ts	a_pes2ts;
+	struct dvb_filter_pes2ts	v_pes2ts;
+
+	u8			v_pes[16 + MAX_PVA_LENGTH];
+	int			v_pes_length;
+	int			v_pes_postbytes;
+
+	struct list_head	urb_frame_list;
+	struct tasklet_struct	urb_tasklet;
+	spinlock_t		urb_frame_list_lock;
+
+	struct dvb_demux_filter	*audio_filter;
+	struct dvb_demux_filter	*video_filter;
+	struct list_head	filter_info_list;
+	spinlock_t		filter_info_list_lock;
+
+	struct input_dev	rc_input_dev;
+
+	int			active; /* Loaded successfully */
+};
+
+struct urb_frame {
+	u8			data[ISO_FRAME_SIZE];
+	int			length;
+	struct list_head	urb_frame_list;
+};
+
+struct filter_info {
+	u8			stream_id;
+	struct dvb_demux_filter	*filter;
+	struct list_head	filter_info_list;
+};
+
+static u16 rc_keys[] = {
+	KEY_POWER,
+	KEY_MUTE,
+	KEY_1,
+	KEY_2,
+	KEY_3,
+	KEY_4,
+	KEY_5,
+	KEY_6,
+	KEY_7,
+	KEY_8,
+	KEY_9,
+	KEY_0,
+	KEY_CHANNELUP,
+	KEY_VOLUMEDOWN,
+	KEY_OK,
+	KEY_VOLUMEUP,
+	KEY_CHANNELDOWN,
+	KEY_PREVIOUS,
+	KEY_ESC,
+	KEY_RED,
+	KEY_GREEN,
+	KEY_YELLOW,
+	KEY_BLUE,
+	KEY_OPTION,
+	KEY_M,
+	KEY_RADIO
+};
+
+static void ttusb_dec_set_model(struct ttusb_dec *dec,
+				enum ttusb_dec_model model);
+
+static void ttusb_dec_handle_irq( struct urb *urb, struct pt_regs *regs)
+{
+	struct ttusb_dec * dec = urb->context;
+	char *buffer = dec->irq_buffer;
+	int retval;
+
+	switch(urb->status) {
+		case 0: /*success*/
+			break;
+		case -ECONNRESET:
+		case -ENOENT:
+		case -ESHUTDOWN:
+		case -ETIMEDOUT:
+			/* this urb is dead, cleanup */
+			dprintk("%s:urb shutting down with status: %d\n",
+					__FUNCTION__, urb->status);
+			return;
+		default:
+			dprintk("%s:nonzero status received: %d\n",
+					__FUNCTION__,urb->status);
+			goto exit;
+	}
+
+	if( (buffer[0] == 0x1) && (buffer[2] == 0x15) )  {
+		/* IR - Event */
+		/* this is an fact a bit too simple implementation;
+		 * the box also reports a keyrepeat signal
+		 * (with buffer[3] == 0x40) in an intervall of ~100ms.
+		 * But to handle this correctly we had to imlemenent some
+		 * kind of timer which signals a 'key up' event if no
+		 * keyrepeat signal is recieved for lets say 200ms.
+		 * this should/could be added later ...
+		 * 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_report_key(&dec->rc_input_dev,rc_keys[buffer[4]-1],0);
+		input_sync(&dec->rc_input_dev);
+	}
+
+exit:	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if(retval)
+		printk("%s - usb_commit_urb failed with result: %d\n",
+			__FUNCTION__, retval);
+}
+
+static u16 crc16(u16 crc, const u8 *buf, size_t len)
+{
+	u16 tmp;
+
+	while (len--) {
+		crc ^= *buf++;
+		crc ^= (u8)crc >> 4;
+		tmp = (u8)crc;
+		crc ^= (tmp ^ (tmp << 1)) << 4;
+	}
+	return crc;
+}
+
+static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command,
+				  int param_length, const u8 params[],
+				  int *result_length, u8 cmd_result[])
+{
+	int result, actual_len, i;
+	u8 *b;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	b = kmalloc(COMMAND_PACKET_SIZE + 4, GFP_KERNEL);
+	if (!b)
+		return -ENOMEM;
+
+	if ((result = down_interruptible(&dec->usb_sem))) {
+		kfree(b);
+		printk("%s: Failed to down usb semaphore.\n", __FUNCTION__);
+		return result;
+	}
+
+	b[0] = 0xaa;
+	b[1] = ++dec->trans_count;
+	b[2] = command;
+	b[3] = param_length;
+
+	if (params)
+		memcpy(&b[4], params, param_length);
+
+	if (debug) {
+		printk("%s: command: ", __FUNCTION__);
+		for (i = 0; i < param_length + 4; i++)
+			printk("0x%02X ", b[i]);
+		printk("\n");
+	}
+
+	result = usb_bulk_msg(dec->udev, dec->command_pipe, b,
+			      COMMAND_PACKET_SIZE + 4, &actual_len, 1000);
+
+	if (result) {
+		printk("%s: command bulk message failed: error %d\n",
+		       __FUNCTION__, result);
+		up(&dec->usb_sem);
+		kfree(b);
+		return result;
+	}
+
+	result = usb_bulk_msg(dec->udev, dec->result_pipe, b,
+			      COMMAND_PACKET_SIZE + 4, &actual_len, 1000);
+
+	if (result) {
+		printk("%s: result bulk message failed: error %d\n",
+		       __FUNCTION__, result);
+		up(&dec->usb_sem);
+		kfree(b);
+		return result;
+	} else {
+		if (debug) {
+			printk("%s: result: ", __FUNCTION__);
+			for (i = 0; i < actual_len; i++)
+				printk("0x%02X ", b[i]);
+			printk("\n");
+		}
+
+		if (result_length)
+			*result_length = b[3];
+		if (cmd_result && b[3] > 0)
+			memcpy(cmd_result, &b[4], b[3]);
+
+		up(&dec->usb_sem);
+
+		kfree(b);
+		return 0;
+	}
+}
+
+static int ttusb_dec_get_stb_state (struct ttusb_dec *dec, unsigned int *mode,
+				    unsigned int *model, unsigned int *version)
+{
+	u8 c[COMMAND_PACKET_SIZE];
+	int c_length;
+	int result;
+	unsigned int tmp;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	result = ttusb_dec_send_command(dec, 0x08, 0, NULL, &c_length, c);
+	if (result)
+		return result;
+
+	if (c_length >= 0x0c) {
+		if (mode != NULL) {
+			memcpy(&tmp, c, 4);
+			*mode = ntohl(tmp);
+		}
+		if (model != NULL) {
+			memcpy(&tmp, &c[4], 4);
+			*model = ntohl(tmp);
+		}
+		if (version != NULL) {
+			memcpy(&tmp, &c[8], 4);
+			*version = ntohl(tmp);
+		}
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+static int ttusb_dec_audio_pes2ts_cb(void *priv, unsigned char *data)
+{
+	struct ttusb_dec *dec = (struct ttusb_dec *)priv;
+
+	dec->audio_filter->feed->cb.ts(data, 188, NULL, 0,
+				       &dec->audio_filter->feed->feed.ts,
+				       DMX_OK);
+
+	return 0;
+}
+
+static int ttusb_dec_video_pes2ts_cb(void *priv, unsigned char *data)
+{
+	struct ttusb_dec *dec = (struct ttusb_dec *)priv;
+
+	dec->video_filter->feed->cb.ts(data, 188, NULL, 0,
+				       &dec->video_filter->feed->feed.ts,
+				       DMX_OK);
+
+	return 0;
+}
+
+static void ttusb_dec_set_pids(struct ttusb_dec *dec)
+{
+	u8 b[] = { 0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0xff, 0xff,
+		   0xff, 0xff, 0xff, 0xff };
+
+	u16 pcr = htons(dec->pid[DMX_PES_PCR]);
+	u16 audio = htons(dec->pid[DMX_PES_AUDIO]);
+	u16 video = htons(dec->pid[DMX_PES_VIDEO]);
+
+	dprintk("%s\n", __FUNCTION__);
+
+	memcpy(&b[0], &pcr, 2);
+	memcpy(&b[2], &audio, 2);
+	memcpy(&b[4], &video, 2);
+
+	ttusb_dec_send_command(dec, 0x50, sizeof(b), b, NULL, NULL);
+
+	dvb_filter_pes2ts_init(&dec->a_pes2ts, dec->pid[DMX_PES_AUDIO],
+			       ttusb_dec_audio_pes2ts_cb, dec);
+	dvb_filter_pes2ts_init(&dec->v_pes2ts, dec->pid[DMX_PES_VIDEO],
+			       ttusb_dec_video_pes2ts_cb, dec);
+	dec->v_pes_length = 0;
+	dec->v_pes_postbytes = 0;
+}
+
+static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length)
+{
+	if (length < 8) {
+		printk("%s: packet too short - discarding\n", __FUNCTION__);
+		return;
+	}
+
+	if (length > 8 + MAX_PVA_LENGTH) {
+		printk("%s: packet too long - discarding\n", __FUNCTION__);
+		return;
+	}
+
+	switch (pva[2]) {
+
+	case 0x01: {		/* VideoStream */
+		int prebytes = pva[5] & 0x03;
+		int postbytes = (pva[5] & 0x0c) >> 2;
+		u16 v_pes_payload_length;
+
+		if (output_pva) {
+			dec->video_filter->feed->cb.ts(pva, length, NULL, 0,
+				&dec->video_filter->feed->feed.ts, DMX_OK);
+			return;
+		}
+
+		if (dec->v_pes_postbytes > 0 &&
+		    dec->v_pes_postbytes == prebytes) {
+			memcpy(&dec->v_pes[dec->v_pes_length],
+			       &pva[12], prebytes);
+
+			dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes,
+					  dec->v_pes_length + prebytes, 1);
+		}
+
+		if (pva[5] & 0x10) {
+			dec->v_pes[7] = 0x80;
+			dec->v_pes[8] = 0x05;
+
+			dec->v_pes[9] = 0x21 | ((pva[8] & 0xc0) >> 5);
+			dec->v_pes[10] = ((pva[8] & 0x3f) << 2) |
+					 ((pva[9] & 0xc0) >> 6);
+			dec->v_pes[11] = 0x01 |
+					 ((pva[9] & 0x3f) << 2) |
+					 ((pva[10] & 0x80) >> 6);
+			dec->v_pes[12] = ((pva[10] & 0x7f) << 1) |
+					 ((pva[11] & 0xc0) >> 7);
+			dec->v_pes[13] = 0x01 | ((pva[11] & 0x7f) << 1);
+
+			memcpy(&dec->v_pes[14], &pva[12 + prebytes],
+			       length - 12 - prebytes);
+			dec->v_pes_length = 14 + length - 12 - prebytes;
+		} else {
+			dec->v_pes[7] = 0x00;
+			dec->v_pes[8] = 0x00;
+
+			memcpy(&dec->v_pes[9], &pva[8], length - 8);
+			dec->v_pes_length = 9 + length - 8;
+		}
+
+		dec->v_pes_postbytes = postbytes;
+
+		if (dec->v_pes[9 + dec->v_pes[8]] == 0x00 &&
+		    dec->v_pes[10 + dec->v_pes[8]] == 0x00 &&
+		    dec->v_pes[11 + dec->v_pes[8]] == 0x01)
+			dec->v_pes[6] = 0x84;
+		else
+			dec->v_pes[6] = 0x80;
+
+		v_pes_payload_length = htons(dec->v_pes_length - 6 +
+					     postbytes);
+		memcpy(&dec->v_pes[4], &v_pes_payload_length, 2);
+
+		if (postbytes == 0)
+			dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes,
+					  dec->v_pes_length, 1);
+
+		break;
+	}
+
+	case 0x02:		/* MainAudioStream */
+		if (output_pva) {
+			dec->audio_filter->feed->cb.ts(pva, length, NULL, 0,
+				&dec->audio_filter->feed->feed.ts, DMX_OK);
+			return;
+		}
+
+		dvb_filter_pes2ts(&dec->a_pes2ts, &pva[8], length - 8,
+				  pva[5] & 0x10);
+		break;
+
+	default:
+		printk("%s: unknown PVA type: %02x.\n", __FUNCTION__,
+		       pva[2]);
+		break;
+	}
+}
+
+static void ttusb_dec_process_filter(struct ttusb_dec *dec, u8 *packet,
+				     int length)
+{
+	struct list_head *item;
+	struct filter_info *finfo;
+	struct dvb_demux_filter *filter = NULL;
+	unsigned long flags;
+	u8 sid;
+
+	sid = packet[1];
+	spin_lock_irqsave(&dec->filter_info_list_lock, flags);
+	for (item = dec->filter_info_list.next; item != &dec->filter_info_list;
+	     item = item->next) {
+		finfo = list_entry(item, struct filter_info, filter_info_list);
+		if (finfo->stream_id == sid) {
+			filter = finfo->filter;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dec->filter_info_list_lock, flags);
+
+	if (filter)
+		filter->feed->cb.sec(&packet[2], length - 2, NULL, 0,
+				     &filter->filter, DMX_OK);
+}
+
+static void ttusb_dec_process_packet(struct ttusb_dec *dec)
+{
+	int i;
+	u16 csum = 0;
+	u16 packet_id;
+
+	if (dec->packet_length % 2) {
+		printk("%s: odd sized packet - discarding\n", __FUNCTION__);
+		return;
+	}
+
+	for (i = 0; i < dec->packet_length; i += 2)
+		csum ^= ((dec->packet[i] << 8) + dec->packet[i + 1]);
+
+	if (csum) {
+		printk("%s: checksum failed - discarding\n", __FUNCTION__);
+		return;
+	}
+
+	packet_id = dec->packet[dec->packet_length - 4] << 8;
+	packet_id += dec->packet[dec->packet_length - 3];
+
+	if ((packet_id != dec->next_packet_id) && dec->next_packet_id) {
+		printk("%s: warning: lost packets between %u and %u\n",
+		       __FUNCTION__, dec->next_packet_id - 1, packet_id);
+	}
+
+	if (packet_id == 0xffff)
+		dec->next_packet_id = 0x8000;
+	else
+		dec->next_packet_id = packet_id + 1;
+
+	switch (dec->packet_type) {
+	case TTUSB_DEC_PACKET_PVA:
+		if (dec->pva_stream_count)
+			ttusb_dec_process_pva(dec, dec->packet,
+					      dec->packet_payload_length);
+		break;
+
+	case TTUSB_DEC_PACKET_SECTION:
+		if (dec->filter_stream_count)
+			ttusb_dec_process_filter(dec, dec->packet,
+						 dec->packet_payload_length);
+		break;
+
+	case TTUSB_DEC_PACKET_EMPTY:
+		break;
+	}
+}
+
+static void swap_bytes(u8 *b, int length)
+{
+	u8 c;
+
+	length -= length % 2;
+	for (; length; b += 2, length -= 2) {
+		c = *b;
+		*b = *(b + 1);
+		*(b + 1) = c;
+	}
+}
+
+static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b,
+					int length)
+{
+	swap_bytes(b, length);
+
+	while (length) {
+		switch (dec->packet_state) {
+
+		case 0:
+		case 1:
+		case 2:
+			if (*b++ == 0xaa)
+				dec->packet_state++;
+			else
+				dec->packet_state = 0;
+
+			length--;
+			break;
+
+		case 3:
+			if (*b == 0x00) {
+				dec->packet_state++;
+				dec->packet_length = 0;
+			} else if (*b != 0xaa) {
+				dec->packet_state = 0;
+			}
+
+			b++;
+			length--;
+			break;
+
+		case 4:
+			dec->packet[dec->packet_length++] = *b++;
+
+			if (dec->packet_length == 2) {
+				if (dec->packet[0] == 'A' &&
+				    dec->packet[1] == 'V') {
+					dec->packet_type =
+						TTUSB_DEC_PACKET_PVA;
+					dec->packet_state++;
+				} else if (dec->packet[0] == 'S') {
+					dec->packet_type =
+						TTUSB_DEC_PACKET_SECTION;
+					dec->packet_state++;
+				} else if (dec->packet[0] == 0x00) {
+					dec->packet_type =
+						TTUSB_DEC_PACKET_EMPTY;
+					dec->packet_payload_length = 2;
+					dec->packet_state = 7;
+				} else {
+					printk("%s: unknown packet type: "
+					       "%02x%02x\n", __FUNCTION__,
+					       dec->packet[0], dec->packet[1]);
+					dec->packet_state = 0;
+				}
+			}
+
+			length--;
+			break;
+
+		case 5:
+			dec->packet[dec->packet_length++] = *b++;
+
+			if (dec->packet_type == TTUSB_DEC_PACKET_PVA &&
+			    dec->packet_length == 8) {
+				dec->packet_state++;
+				dec->packet_payload_length = 8 +
+					(dec->packet[6] << 8) +
+					dec->packet[7];
+			} else if (dec->packet_type ==
+					TTUSB_DEC_PACKET_SECTION &&
+				   dec->packet_length == 5) {
+				dec->packet_state++;
+				dec->packet_payload_length = 5 +
+					((dec->packet[3] & 0x0f) << 8) +
+					dec->packet[4];
+			}
+
+			length--;
+			break;
+
+		case 6: {
+			int remainder = dec->packet_payload_length -
+					dec->packet_length;
+
+			if (length >= remainder) {
+				memcpy(dec->packet + dec->packet_length,
+				       b, remainder);
+				dec->packet_length += remainder;
+				b += remainder;
+				length -= remainder;
+				dec->packet_state++;
+			} else {
+				memcpy(&dec->packet[dec->packet_length],
+				       b, length);
+				dec->packet_length += length;
+				length = 0;
+			}
+
+			break;
+		}
+
+		case 7: {
+			int tail = 4;
+
+			dec->packet[dec->packet_length++] = *b++;
+
+			if (dec->packet_type == TTUSB_DEC_PACKET_SECTION &&
+			    dec->packet_payload_length % 2)
+				tail++;
+
+			if (dec->packet_length ==
+			    dec->packet_payload_length + tail) {
+				ttusb_dec_process_packet(dec);
+				dec->packet_state = 0;
+			}
+
+			length--;
+			break;
+		}
+
+		default:
+			printk("%s: illegal packet state encountered.\n",
+			       __FUNCTION__);
+			dec->packet_state = 0;
+		}
+	}
+}
+
+static void ttusb_dec_process_urb_frame_list(unsigned long data)
+{
+	struct ttusb_dec *dec = (struct ttusb_dec *)data;
+	struct list_head *item;
+	struct urb_frame *frame;
+	unsigned long flags;
+
+	while (1) {
+		spin_lock_irqsave(&dec->urb_frame_list_lock, flags);
+		if ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) {
+			frame = list_entry(item, struct urb_frame,
+					   urb_frame_list);
+			list_del(&frame->urb_frame_list);
+		} else {
+			spin_unlock_irqrestore(&dec->urb_frame_list_lock,
+					       flags);
+			return;
+		}
+		spin_unlock_irqrestore(&dec->urb_frame_list_lock, flags);
+
+		ttusb_dec_process_urb_frame(dec, frame->data, frame->length);
+		kfree(frame);
+	}
+}
+
+static void ttusb_dec_process_urb(struct urb *urb, struct pt_regs *ptregs)
+{
+	struct ttusb_dec *dec = urb->context;
+
+	if (!urb->status) {
+		int i;
+
+		for (i = 0; i < FRAMES_PER_ISO_BUF; i++) {
+			struct usb_iso_packet_descriptor *d;
+			u8 *b;
+			int length;
+			struct urb_frame *frame;
+
+			d = &urb->iso_frame_desc[i];
+			b = urb->transfer_buffer + d->offset;
+			length = d->actual_length;
+
+			if ((frame = kmalloc(sizeof(struct urb_frame),
+					     GFP_ATOMIC))) {
+				unsigned long flags;
+
+				memcpy(frame->data, b, length);
+				frame->length = length;
+
+				spin_lock_irqsave(&dec->urb_frame_list_lock,
+						     flags);
+				list_add_tail(&frame->urb_frame_list,
+					      &dec->urb_frame_list);
+				spin_unlock_irqrestore(&dec->urb_frame_list_lock,
+						       flags);
+
+				tasklet_schedule(&dec->urb_tasklet);
+			}
+		}
+	} else {
+		 /* -ENOENT is expected when unlinking urbs */
+		if (urb->status != -ENOENT)
+			dprintk("%s: urb error: %d\n", __FUNCTION__,
+				urb->status);
+	}
+
+	if (dec->iso_stream_count)
+		usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static void ttusb_dec_setup_urbs(struct ttusb_dec *dec)
+{
+	int i, j, buffer_offset = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	for (i = 0; i < ISO_BUF_COUNT; i++) {
+		int frame_offset = 0;
+		struct urb *urb = dec->iso_urb[i];
+
+		urb->dev = dec->udev;
+		urb->context = dec;
+		urb->complete = ttusb_dec_process_urb;
+		urb->pipe = dec->in_pipe;
+		urb->transfer_flags = URB_ISO_ASAP;
+		urb->interval = 1;
+		urb->number_of_packets = FRAMES_PER_ISO_BUF;
+		urb->transfer_buffer_length = ISO_FRAME_SIZE *
+					      FRAMES_PER_ISO_BUF;
+		urb->transfer_buffer = dec->iso_buffer + buffer_offset;
+		buffer_offset += ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF;
+
+		for (j = 0; j < FRAMES_PER_ISO_BUF; j++) {
+			urb->iso_frame_desc[j].offset = frame_offset;
+			urb->iso_frame_desc[j].length = ISO_FRAME_SIZE;
+			frame_offset += ISO_FRAME_SIZE;
+		}
+	}
+}
+
+static void ttusb_dec_stop_iso_xfer(struct ttusb_dec *dec)
+{
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (down_interruptible(&dec->iso_sem))
+		return;
+
+	dec->iso_stream_count--;
+
+	if (!dec->iso_stream_count) {
+		for (i = 0; i < ISO_BUF_COUNT; i++)
+			usb_kill_urb(dec->iso_urb[i]);
+	}
+
+	up(&dec->iso_sem);
+}
+
+/* Setting the interface of the DEC tends to take down the USB communications
+ * for a short period, so it's important not to call this function just before
+ * trying to talk to it.
+ */
+static int ttusb_dec_set_interface(struct ttusb_dec *dec,
+				   enum ttusb_dec_interface interface)
+{
+	int result = 0;
+	u8 b[] = { 0x05 };
+
+	if (interface != dec->interface) {
+		switch (interface) {
+		case TTUSB_DEC_INTERFACE_INITIAL:
+			result = usb_set_interface(dec->udev, 0, 0);
+			break;
+		case TTUSB_DEC_INTERFACE_IN:
+			result = ttusb_dec_send_command(dec, 0x80, sizeof(b),
+							b, NULL, NULL);
+			if (result)
+				return result;
+			result = usb_set_interface(dec->udev, 0, 8);
+			break;
+		case TTUSB_DEC_INTERFACE_OUT:
+			result = usb_set_interface(dec->udev, 0, 1);
+			break;
+		}
+
+		if (result)
+			return result;
+
+		dec->interface = interface;
+	}
+
+	return 0;
+}
+
+static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec)
+{
+	int i, result;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (down_interruptible(&dec->iso_sem))
+		return -EAGAIN;
+
+	if (!dec->iso_stream_count) {
+		ttusb_dec_setup_urbs(dec);
+
+		dec->packet_state = 0;
+		dec->v_pes_postbytes = 0;
+		dec->next_packet_id = 0;
+
+		for (i = 0; i < ISO_BUF_COUNT; i++) {
+			if ((result = usb_submit_urb(dec->iso_urb[i],
+						     GFP_ATOMIC))) {
+				printk("%s: failed urb submission %d: "
+				       "error %d\n", __FUNCTION__, i, result);
+
+				while (i) {
+					usb_kill_urb(dec->iso_urb[i - 1]);
+					i--;
+				}
+
+				up(&dec->iso_sem);
+				return result;
+			}
+		}
+	}
+
+	dec->iso_stream_count++;
+
+	up(&dec->iso_sem);
+
+	return 0;
+}
+
+static int ttusb_dec_start_ts_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+	struct ttusb_dec *dec = dvbdmx->priv;
+	u8 b0[] = { 0x05 };
+	int result = 0;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	dprintk("  ts_type:");
+
+	if (dvbdmxfeed->ts_type & TS_DECODER)
+		dprintk(" TS_DECODER");
+
+	if (dvbdmxfeed->ts_type & TS_PACKET)
+		dprintk(" TS_PACKET");
+
+	if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
+		dprintk(" TS_PAYLOAD_ONLY");
+
+	dprintk("\n");
+
+	switch (dvbdmxfeed->pes_type) {
+
+	case DMX_TS_PES_VIDEO:
+		dprintk("  pes_type: DMX_TS_PES_VIDEO\n");
+		dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid;
+		dec->pid[DMX_PES_VIDEO] = dvbdmxfeed->pid;
+		dec->video_filter = dvbdmxfeed->filter;
+		ttusb_dec_set_pids(dec);
+		break;
+
+	case DMX_TS_PES_AUDIO:
+		dprintk("  pes_type: DMX_TS_PES_AUDIO\n");
+		dec->pid[DMX_PES_AUDIO] = dvbdmxfeed->pid;
+		dec->audio_filter = dvbdmxfeed->filter;
+		ttusb_dec_set_pids(dec);
+		break;
+
+	case DMX_TS_PES_TELETEXT:
+		dec->pid[DMX_PES_TELETEXT] = dvbdmxfeed->pid;
+		dprintk("  pes_type: DMX_TS_PES_TELETEXT\n");
+		break;
+
+	case DMX_TS_PES_PCR:
+		dprintk("  pes_type: DMX_TS_PES_PCR\n");
+		dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid;
+		ttusb_dec_set_pids(dec);
+		break;
+
+	case DMX_TS_PES_OTHER:
+		dprintk("  pes_type: DMX_TS_PES_OTHER\n");
+		break;
+
+	default:
+		dprintk("  pes_type: unknown (%d)\n", dvbdmxfeed->pes_type);
+		return -EINVAL;
+
+	}
+
+	result = ttusb_dec_send_command(dec, 0x80, sizeof(b0), b0, NULL, NULL);
+	if (result)
+		return result;
+
+	dec->pva_stream_count++;
+	return ttusb_dec_start_iso_xfer(dec);
+}
+
+static int ttusb_dec_start_sec_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct ttusb_dec *dec = dvbdmxfeed->demux->priv;
+	u8 b0[] = { 0x00, 0x00, 0x00, 0x01,
+		    0x00, 0x00, 0x00, 0x00,
+		    0x00, 0x00, 0x00, 0x00,
+		    0x00, 0x00, 0x00, 0x00,
+		    0x00, 0xff, 0x00, 0x00,
+		    0x00, 0x00, 0x00, 0x00,
+		    0x00, 0x00, 0x00, 0x00,
+		    0x00 };
+	u16 pid;
+	u8 c[COMMAND_PACKET_SIZE];
+	int c_length;
+	int result;
+	struct filter_info *finfo;
+	unsigned long flags;
+	u8 x = 1;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	pid = htons(dvbdmxfeed->pid);
+	memcpy(&b0[0], &pid, 2);
+	memcpy(&b0[4], &x, 1);
+	memcpy(&b0[5], &dvbdmxfeed->filter->filter.filter_value[0], 1);
+
+	result = ttusb_dec_send_command(dec, 0x60, sizeof(b0), b0,
+					&c_length, c);
+
+	if (!result) {
+		if (c_length == 2) {
+			if (!(finfo = kmalloc(sizeof(struct filter_info),
+					      GFP_ATOMIC)))
+				return -ENOMEM;
+
+			finfo->stream_id = c[1];
+			finfo->filter = dvbdmxfeed->filter;
+
+			spin_lock_irqsave(&dec->filter_info_list_lock, flags);
+			list_add_tail(&finfo->filter_info_list,
+				      &dec->filter_info_list);
+			spin_unlock_irqrestore(&dec->filter_info_list_lock,
+					       flags);
+
+			dvbdmxfeed->priv = finfo;
+
+			dec->filter_stream_count++;
+			return ttusb_dec_start_iso_xfer(dec);
+		}
+
+		return -EAGAIN;
+	} else
+		return result;
+}
+
+static int ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (!dvbdmx->dmx.frontend)
+		return -EINVAL;
+
+	dprintk("  pid: 0x%04X\n", dvbdmxfeed->pid);
+
+	switch (dvbdmxfeed->type) {
+
+	case DMX_TYPE_TS:
+		return ttusb_dec_start_ts_feed(dvbdmxfeed);
+		break;
+
+	case DMX_TYPE_SEC:
+		return ttusb_dec_start_sec_feed(dvbdmxfeed);
+		break;
+
+	default:
+		dprintk("  type: unknown (%d)\n", dvbdmxfeed->type);
+		return -EINVAL;
+
+	}
+}
+
+static int ttusb_dec_stop_ts_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct ttusb_dec *dec = dvbdmxfeed->demux->priv;
+	u8 b0[] = { 0x00 };
+
+	ttusb_dec_send_command(dec, 0x81, sizeof(b0), b0, NULL, NULL);
+
+	dec->pva_stream_count--;
+
+	ttusb_dec_stop_iso_xfer(dec);
+
+	return 0;
+}
+
+static int ttusb_dec_stop_sec_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	struct ttusb_dec *dec = dvbdmxfeed->demux->priv;
+	u8 b0[] = { 0x00, 0x00 };
+	struct filter_info *finfo = (struct filter_info *)dvbdmxfeed->priv;
+	unsigned long flags;
+
+	b0[1] = finfo->stream_id;
+	spin_lock_irqsave(&dec->filter_info_list_lock, flags);
+	list_del(&finfo->filter_info_list);
+	spin_unlock_irqrestore(&dec->filter_info_list_lock, flags);
+	kfree(finfo);
+	ttusb_dec_send_command(dec, 0x62, sizeof(b0), b0, NULL, NULL);
+
+	dec->filter_stream_count--;
+
+	ttusb_dec_stop_iso_xfer(dec);
+
+	return 0;
+}
+
+static int ttusb_dec_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	switch (dvbdmxfeed->type) {
+	case DMX_TYPE_TS:
+		return ttusb_dec_stop_ts_feed(dvbdmxfeed);
+		break;
+
+	case DMX_TYPE_SEC:
+		return ttusb_dec_stop_sec_feed(dvbdmxfeed);
+		break;
+	}
+
+	return 0;
+}
+
+static void ttusb_dec_free_iso_urbs(struct ttusb_dec *dec)
+{
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	for (i = 0; i < ISO_BUF_COUNT; i++)
+		if (dec->iso_urb[i])
+			usb_free_urb(dec->iso_urb[i]);
+
+	pci_free_consistent(NULL,
+			    ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF *
+					      ISO_BUF_COUNT),
+			    dec->iso_buffer, dec->iso_dma_handle);
+}
+
+static int ttusb_dec_alloc_iso_urbs(struct ttusb_dec *dec)
+{
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	dec->iso_buffer = pci_alloc_consistent(NULL,
+					       ISO_FRAME_SIZE *
+					       (FRAMES_PER_ISO_BUF *
+						ISO_BUF_COUNT),
+					       &dec->iso_dma_handle);
+
+	memset(dec->iso_buffer, 0,
+	       ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * ISO_BUF_COUNT));
+
+	for (i = 0; i < ISO_BUF_COUNT; i++) {
+		struct urb *urb;
+
+		if (!(urb = usb_alloc_urb(FRAMES_PER_ISO_BUF, GFP_ATOMIC))) {
+			ttusb_dec_free_iso_urbs(dec);
+			return -ENOMEM;
+		}
+
+		dec->iso_urb[i] = urb;
+	}
+
+	ttusb_dec_setup_urbs(dec);
+
+	return 0;
+}
+
+static void ttusb_dec_init_tasklet(struct ttusb_dec *dec)
+{
+	spin_lock_init(&dec->urb_frame_list_lock);
+	INIT_LIST_HEAD(&dec->urb_frame_list);
+	tasklet_init(&dec->urb_tasklet, ttusb_dec_process_urb_frame_list,
+		     (unsigned long)dec);
+}
+
+static void ttusb_init_rc( struct ttusb_dec *dec)
+{
+	u8 b[] = { 0x00, 0x01 };
+	int i;
+
+	init_input_dev(&dec->rc_input_dev);
+
+	dec->rc_input_dev.name = "ttusb_dec remote control";
+	dec->rc_input_dev.evbit[0] = BIT(EV_KEY);
+	dec->rc_input_dev.keycodesize = sizeof(u16);
+	dec->rc_input_dev.keycodemax = 0x1a;
+	dec->rc_input_dev.keycode = rc_keys;
+
+	 for (i = 0; i < sizeof(rc_keys)/sizeof(rc_keys[0]); i++)
+                set_bit(rc_keys[i], dec->rc_input_dev.keybit);
+
+	input_register_device(&dec->rc_input_dev);
+
+	if(usb_submit_urb(dec->irq_urb,GFP_KERNEL)) {
+		printk("%s: usb_submit_urb failed\n",__FUNCTION__);
+	}
+	/* enable irq pipe */
+	ttusb_dec_send_command(dec,0xb0,sizeof(b),b,NULL,NULL);
+}
+
+static void ttusb_dec_init_v_pes(struct ttusb_dec *dec)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	dec->v_pes[0] = 0x00;
+	dec->v_pes[1] = 0x00;
+	dec->v_pes[2] = 0x01;
+	dec->v_pes[3] = 0xe0;
+}
+
+static int ttusb_dec_init_usb(struct ttusb_dec *dec)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	sema_init(&dec->usb_sem, 1);
+	sema_init(&dec->iso_sem, 1);
+
+	dec->command_pipe = usb_sndbulkpipe(dec->udev, COMMAND_PIPE);
+	dec->result_pipe = usb_rcvbulkpipe(dec->udev, RESULT_PIPE);
+	dec->in_pipe = usb_rcvisocpipe(dec->udev, IN_PIPE);
+	dec->out_pipe = usb_sndisocpipe(dec->udev, OUT_PIPE);
+	dec->irq_pipe = usb_rcvintpipe(dec->udev, IRQ_PIPE);
+
+	if(enable_rc) {
+		dec->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if(!dec->irq_urb) {
+			return -ENOMEM;
+		}
+		dec->irq_buffer = usb_buffer_alloc(dec->udev,IRQ_PACKET_SIZE,
+					SLAB_ATOMIC, &dec->irq_dma_handle);
+		if(!dec->irq_buffer) {
+			return -ENOMEM;
+		}
+		usb_fill_int_urb(dec->irq_urb, dec->udev,dec->irq_pipe,
+				 dec->irq_buffer, IRQ_PACKET_SIZE,
+				 ttusb_dec_handle_irq, dec, 1);
+		dec->irq_urb->transfer_dma = dec->irq_dma_handle;
+		dec->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	}
+
+	return ttusb_dec_alloc_iso_urbs(dec);
+}
+
+static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
+{
+	int i, j, actual_len, result, size, trans_count;
+	u8 b0[] = { 0x00, 0x00, 0x00, 0x00,
+		    0x00, 0x00, 0x00, 0x00,
+		    0x61, 0x00 };
+	u8 b1[] = { 0x61 };
+	u8 *b;
+	char idstring[21];
+	u8 *firmware = NULL;
+	size_t firmware_size = 0;
+	u16 firmware_csum = 0;
+	u16 firmware_csum_ns;
+	u32 firmware_size_nl;
+	u32 crc32_csum, crc32_check, tmp;
+	const struct firmware *fw_entry = NULL;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev)) {
+		printk(KERN_ERR "%s: Firmware (%s) unavailable.\n",
+		       __FUNCTION__, dec->firmware_name);
+		return 1;
+	}
+
+	firmware = fw_entry->data;
+	firmware_size = fw_entry->size;
+
+	if (firmware_size < 60) {
+		printk("%s: firmware size too small for DSP code (%zu < 60).\n",
+			__FUNCTION__, firmware_size);
+		return -1;
+	}
+
+	/* a 32 bit checksum over the first 56 bytes of the DSP Code is stored
+	   at offset 56 of file, so use it to check if the firmware file is
+	   valid. */
+	crc32_csum = crc32(~0L, firmware, 56) ^ ~0L;
+	memcpy(&tmp, &firmware[56], 4);
+	crc32_check = htonl(tmp);
+	if (crc32_csum != crc32_check) {
+		printk("%s: crc32 check of DSP code failed (calculated "
+		       "0x%08x != 0x%08x in file), file invalid.\n",
+			__FUNCTION__, crc32_csum, crc32_check);
+		return -1;
+	}
+	memcpy(idstring, &firmware[36], 20);
+	idstring[20] = '\0';
+	printk(KERN_INFO "ttusb_dec: found DSP code \"%s\".\n", idstring);
+
+	firmware_size_nl = htonl(firmware_size);
+	memcpy(b0, &firmware_size_nl, 4);
+	firmware_csum = crc16(~0, firmware, firmware_size) ^ ~0;
+	firmware_csum_ns = htons(firmware_csum);
+	memcpy(&b0[6], &firmware_csum_ns, 2);
+
+	result = ttusb_dec_send_command(dec, 0x41, sizeof(b0), b0, NULL, NULL);
+
+	if (result)
+		return result;
+
+	trans_count = 0;
+	j = 0;
+
+	b = kmalloc(ARM_PACKET_SIZE, GFP_KERNEL);
+	if (b == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < firmware_size; i += COMMAND_PACKET_SIZE) {
+		size = firmware_size - i;
+		if (size > COMMAND_PACKET_SIZE)
+			size = COMMAND_PACKET_SIZE;
+
+		b[j + 0] = 0xaa;
+		b[j + 1] = trans_count++;
+		b[j + 2] = 0xf0;
+		b[j + 3] = size;
+		memcpy(&b[j + 4], &firmware[i], size);
+
+		j += COMMAND_PACKET_SIZE + 4;
+
+		if (j >= ARM_PACKET_SIZE) {
+			result = usb_bulk_msg(dec->udev, dec->command_pipe, b,
+					      ARM_PACKET_SIZE, &actual_len,
+					      100);
+			j = 0;
+		} else if (size < COMMAND_PACKET_SIZE) {
+			result = usb_bulk_msg(dec->udev, dec->command_pipe, b,
+					      j - COMMAND_PACKET_SIZE + size,
+					      &actual_len, 100);
+		}
+	}
+
+	result = ttusb_dec_send_command(dec, 0x43, sizeof(b1), b1, NULL, NULL);
+
+	kfree(b);
+
+	return result;
+}
+
+static int ttusb_dec_init_stb(struct ttusb_dec *dec)
+{
+	int result;
+	unsigned int mode, model, version;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	result = ttusb_dec_get_stb_state(dec, &mode, &model, &version);
+
+	if (!result) {
+		if (!mode) {
+			if (version == 0xABCDEFAB)
+				printk(KERN_INFO "ttusb_dec: no version "
+				       "info in Firmware\n");
+			else
+				printk(KERN_INFO "ttusb_dec: Firmware "
+				       "%x.%02x%c%c\n",
+				       version >> 24, (version >> 16) & 0xff,
+				       (version >> 8) & 0xff, version & 0xff);
+
+			result = ttusb_dec_boot_dsp(dec);
+			if (result)
+				return result;
+			else
+				return 1;
+		} else {
+			/* We can't trust the USB IDs that some firmwares
+			   give the box */
+			switch (model) {
+			case 0x00070008:
+			case 0x0007000c:
+				ttusb_dec_set_model(dec, TTUSB_DEC3000S);
+				break;
+			case 0x00070009:
+			case 0x00070013:
+				ttusb_dec_set_model(dec, TTUSB_DEC2000T);
+				break;
+			case 0x00070011:
+				ttusb_dec_set_model(dec, TTUSB_DEC2540T);
+				break;
+			default:
+				printk(KERN_ERR "%s: unknown model returned "
+				       "by firmware (%08x) - please report\n",
+				       __FUNCTION__, model);
+				return -1;
+				break;
+			}
+
+			if (version >= 0x01770000)
+				dec->can_playback = 1;
+
+			return 0;
+		}
+	}
+	else
+		return result;
+}
+
+static int ttusb_dec_init_dvb(struct ttusb_dec *dec)
+{
+	int result;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if ((result = dvb_register_adapter(&dec->adapter,
+					   dec->model_name, THIS_MODULE)) < 0) {
+		printk("%s: dvb_register_adapter failed: error %d\n",
+		       __FUNCTION__, result);
+
+		return result;
+	}
+
+	dec->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+
+	dec->demux.priv = (void *)dec;
+	dec->demux.filternum = 31;
+	dec->demux.feednum = 31;
+	dec->demux.start_feed = ttusb_dec_start_feed;
+	dec->demux.stop_feed = ttusb_dec_stop_feed;
+	dec->demux.write_to_decoder = NULL;
+
+	if ((result = dvb_dmx_init(&dec->demux)) < 0) {
+		printk("%s: dvb_dmx_init failed: error %d\n", __FUNCTION__,
+		       result);
+
+		dvb_unregister_adapter(dec->adapter);
+
+		return result;
+	}
+
+	dec->dmxdev.filternum = 32;
+	dec->dmxdev.demux = &dec->demux.dmx;
+	dec->dmxdev.capabilities = 0;
+
+	if ((result = dvb_dmxdev_init(&dec->dmxdev, dec->adapter)) < 0) {
+		printk("%s: dvb_dmxdev_init failed: error %d\n",
+		       __FUNCTION__, result);
+
+		dvb_dmx_release(&dec->demux);
+		dvb_unregister_adapter(dec->adapter);
+
+		return result;
+	}
+
+	dec->frontend.source = DMX_FRONTEND_0;
+
+	if ((result = dec->demux.dmx.add_frontend(&dec->demux.dmx,
+						  &dec->frontend)) < 0) {
+		printk("%s: dvb_dmx_init failed: error %d\n", __FUNCTION__,
+		       result);
+
+		dvb_dmxdev_release(&dec->dmxdev);
+		dvb_dmx_release(&dec->demux);
+		dvb_unregister_adapter(dec->adapter);
+
+		return result;
+	}
+
+	if ((result = dec->demux.dmx.connect_frontend(&dec->demux.dmx,
+						      &dec->frontend)) < 0) {
+		printk("%s: dvb_dmx_init failed: error %d\n", __FUNCTION__,
+		       result);
+
+		dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend);
+		dvb_dmxdev_release(&dec->dmxdev);
+		dvb_dmx_release(&dec->demux);
+		dvb_unregister_adapter(dec->adapter);
+
+		return result;
+	}
+
+	dvb_net_init(dec->adapter, &dec->dvb_net, &dec->demux.dmx);
+
+	return 0;
+}
+
+static void ttusb_dec_exit_dvb(struct ttusb_dec *dec)
+{
+	dprintk("%s\n", __FUNCTION__);
+
+	dvb_net_release(&dec->dvb_net);
+	dec->demux.dmx.close(&dec->demux.dmx);
+	dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend);
+	dvb_dmxdev_release(&dec->dmxdev);
+	dvb_dmx_release(&dec->demux);
+	if (dec->fe) dvb_unregister_frontend(dec->fe);
+	dvb_unregister_adapter(dec->adapter);
+}
+
+static void ttusb_dec_exit_rc(struct ttusb_dec *dec)
+{
+
+	dprintk("%s\n", __FUNCTION__);
+	/* we have to check whether the irq URB is already submitted.
+	  * As the irq is submitted after the interface is changed,
+	  * this is the best method i figured out.
+	  * Any others?*/
+	if(dec->interface == TTUSB_DEC_INTERFACE_IN)
+		usb_kill_urb(dec->irq_urb);
+
+	usb_free_urb(dec->irq_urb);
+
+	usb_buffer_free(dec->udev,IRQ_PACKET_SIZE,
+		           dec->irq_buffer, dec->irq_dma_handle);
+
+	input_unregister_device(&dec->rc_input_dev);
+}
+
+
+static void ttusb_dec_exit_usb(struct ttusb_dec *dec)
+{
+	int i;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	dec->iso_stream_count = 0;
+
+	for (i = 0; i < ISO_BUF_COUNT; i++)
+		usb_kill_urb(dec->iso_urb[i]);
+
+	ttusb_dec_free_iso_urbs(dec);
+}
+
+static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec)
+{
+	struct list_head *item;
+	struct urb_frame *frame;
+
+	tasklet_kill(&dec->urb_tasklet);
+
+	while ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) {
+		frame = list_entry(item, struct urb_frame, urb_frame_list);
+		list_del(&frame->urb_frame_list);
+		kfree(frame);
+	}
+}
+
+static void ttusb_dec_init_filters(struct ttusb_dec *dec)
+{
+	INIT_LIST_HEAD(&dec->filter_info_list);
+	spin_lock_init(&dec->filter_info_list_lock);
+}
+
+static void ttusb_dec_exit_filters(struct ttusb_dec *dec)
+{
+	struct list_head *item;
+	struct filter_info *finfo;
+
+	while ((item = dec->filter_info_list.next) != &dec->filter_info_list) {
+		finfo = list_entry(item, struct filter_info, filter_info_list);
+		list_del(&finfo->filter_info_list);
+		kfree(finfo);
+	}
+}
+
+int fe_send_command(struct dvb_frontend* fe, const u8 command,
+		    int param_length, const u8 params[],
+		    int *result_length, u8 cmd_result[])
+{
+	struct ttusb_dec* dec = (struct ttusb_dec*) fe->dvb->priv;
+	return ttusb_dec_send_command(dec, command, param_length, params, result_length, cmd_result);
+}
+
+struct ttusbdecfe_config fe_config = {
+	.send_command = fe_send_command
+};
+
+static int ttusb_dec_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *udev;
+	struct ttusb_dec *dec;
+
+	dprintk("%s\n", __FUNCTION__);
+
+	udev = interface_to_usbdev(intf);
+
+	if (!(dec = kmalloc(sizeof(struct ttusb_dec), GFP_KERNEL))) {
+		printk("%s: couldn't allocate memory.\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	usb_set_intfdata(intf, (void *)dec);
+
+	memset(dec, 0, sizeof(struct ttusb_dec));
+
+	switch (le16_to_cpu(id->idProduct)) {
+	case 0x1006:
+		ttusb_dec_set_model(dec, TTUSB_DEC3000S);
+		break;
+
+	case 0x1008:
+		ttusb_dec_set_model(dec, TTUSB_DEC2000T);
+		break;
+
+	case 0x1009:
+		ttusb_dec_set_model(dec, TTUSB_DEC2540T);
+		break;
+	}
+
+	dec->udev = udev;
+
+	if (ttusb_dec_init_usb(dec))
+		return 0;
+	if (ttusb_dec_init_stb(dec)) {
+		ttusb_dec_exit_usb(dec);
+		return 0;
+	}
+	ttusb_dec_init_dvb(dec);
+
+	dec->adapter->priv = dec;
+	switch (le16_to_cpu(id->idProduct)) {
+	case 0x1006:
+		dec->fe = ttusbdecfe_dvbs_attach(&fe_config);
+		break;
+
+	case 0x1008:
+	case 0x1009:
+		dec->fe = ttusbdecfe_dvbt_attach(&fe_config);
+		break;
+	}
+
+	if (dec->fe == NULL) {
+		printk("dvb-ttusb-dec: A frontend driver was not found for device %04x/%04x\n",
+		       le16_to_cpu(dec->udev->descriptor.idVendor),
+		       le16_to_cpu(dec->udev->descriptor.idProduct));
+	} else {
+		if (dvb_register_frontend(dec->adapter, dec->fe)) {
+			printk("budget-ci: Frontend registration failed!\n");
+			if (dec->fe->ops->release)
+				dec->fe->ops->release(dec->fe);
+			dec->fe = NULL;
+		}
+	}
+
+	ttusb_dec_init_v_pes(dec);
+	ttusb_dec_init_filters(dec);
+	ttusb_dec_init_tasklet(dec);
+
+	dec->active = 1;
+
+	ttusb_dec_set_interface(dec, TTUSB_DEC_INTERFACE_IN);
+
+	if(enable_rc)
+		ttusb_init_rc(dec);
+
+	return 0;
+}
+
+static void ttusb_dec_disconnect(struct usb_interface *intf)
+{
+	struct ttusb_dec *dec = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	dprintk("%s\n", __FUNCTION__);
+
+	if (dec->active) {
+		ttusb_dec_exit_tasklet(dec);
+		ttusb_dec_exit_filters(dec);
+		if(enable_rc)
+			ttusb_dec_exit_rc(dec);
+		ttusb_dec_exit_usb(dec);
+		ttusb_dec_exit_dvb(dec);
+	}
+
+	kfree(dec);
+}
+
+static void ttusb_dec_set_model(struct ttusb_dec *dec,
+				enum ttusb_dec_model model)
+{
+	dec->model = model;
+
+	switch (model) {
+	case TTUSB_DEC2000T:
+		dec->model_name = "DEC2000-t";
+		dec->firmware_name = "dvb-ttusb-dec-2000t.fw";
+		break;
+
+	case TTUSB_DEC2540T:
+		dec->model_name = "DEC2540-t";
+		dec->firmware_name = "dvb-ttusb-dec-2540t.fw";
+		break;
+
+	case TTUSB_DEC3000S:
+		dec->model_name = "DEC3000-s";
+		dec->firmware_name = "dvb-ttusb-dec-3000s.fw";
+		break;
+	}
+}
+
+static struct usb_device_id ttusb_dec_table[] = {
+	{USB_DEVICE(0x0b48, 0x1006)},	/* DEC3000-s */
+	/*{USB_DEVICE(0x0b48, 0x1007)},	   Unconfirmed */
+	{USB_DEVICE(0x0b48, 0x1008)},	/* DEC2000-t */
+	{USB_DEVICE(0x0b48, 0x1009)},	/* DEC2540-t */
+	{}
+};
+
+static struct usb_driver ttusb_dec_driver = {
+	.name		= "ttusb-dec",
+	.probe		= ttusb_dec_probe,
+	.disconnect	= ttusb_dec_disconnect,
+	.id_table	= ttusb_dec_table,
+};
+
+static int __init ttusb_dec_init(void)
+{
+	int result;
+
+	if ((result = usb_register(&ttusb_dec_driver)) < 0) {
+		printk("%s: initialisation failed: error %d.\n", __FUNCTION__,
+		       result);
+		return result;
+	}
+
+	return 0;
+}
+
+static void __exit ttusb_dec_exit(void)
+{
+	usb_deregister(&ttusb_dec_driver);
+}
+
+module_init(ttusb_dec_init);
+module_exit(ttusb_dec_exit);
+
+MODULE_AUTHOR("Alex Woods <linux-dvb@giblets.org>");
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, ttusb_dec_table);
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
new file mode 100644
index 0000000..1699cc9
--- /dev/null
+++ b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
@@ -0,0 +1,255 @@
+/*
+ * TTUSB DEC Frontend Driver
+ *
+ * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "dvb_frontend.h"
+#include "ttusbdecfe.h"
+
+
+#define LOF_HI			10600000
+#define LOF_LO			9750000
+
+struct ttusbdecfe_state {
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct ttusbdecfe_config* config;
+
+	struct dvb_frontend frontend;
+
+	u8 hi_band;
+	u8 voltage;
+};
+
+
+static int ttusbdecfe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	*status = FE_HAS_SIGNAL | FE_HAS_VITERBI |
+		  FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int ttusbdecfe_dvbt_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+	u8 b[] = { 0x00, 0x00, 0x00, 0x03,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x01,
+		   0x00, 0x00, 0x00, 0xff,
+		   0x00, 0x00, 0x00, 0xff };
+
+	u32 freq = htonl(p->frequency / 1000);
+	memcpy(&b[4], &freq, sizeof (u32));
+	state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL);
+
+	return 0;
+}
+
+static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+
+	u8 b[] = { 0x00, 0x00, 0x00, 0x01,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x01,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00 };
+	u32 freq;
+	u32 sym_rate;
+	u32 band;
+	u32 lnb_voltage;
+
+	freq = htonl(p->frequency +
+	       (state->hi_band ? LOF_HI : LOF_LO));
+	memcpy(&b[4], &freq, sizeof(u32));
+	sym_rate = htonl(p->u.qam.symbol_rate);
+	memcpy(&b[12], &sym_rate, sizeof(u32));
+	band = htonl(state->hi_band ? LOF_HI : LOF_LO);
+	memcpy(&b[24], &band, sizeof(u32));
+	lnb_voltage = htonl(state->voltage);
+	memcpy(&b[28], &lnb_voltage, sizeof(u32));
+
+	state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL);
+
+	return 0;
+}
+
+static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
+{
+	struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+	u8 b[] = { 0x00, 0xff, 0x00, 0x00,
+		   0x00, 0x00, 0x00, 0x00,
+		   0x00, 0x00 };
+
+	memcpy(&b[4], cmd->msg, cmd->msg_len);
+
+	state->config->send_command(fe, 0x72,
+				    sizeof(b) - (6 - cmd->msg_len), b,
+				    NULL, NULL);
+
+	return 0;
+}
+
+
+static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+
+	state->hi_band = (SEC_TONE_ON == tone);
+
+	return 0;
+}
+
+
+static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		state->voltage = 13;
+		break;
+	case SEC_VOLTAGE_18:
+		state->voltage = 18;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ttusbdecfe_release(struct dvb_frontend* fe)
+{
+	struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops ttusbdecfe_dvbt_ops;
+
+struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config)
+{
+	struct ttusbdecfe_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	memcpy(&state->ops, &ttusbdecfe_dvbt_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops ttusbdecfe_dvbs_ops;
+
+struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config)
+{
+	struct ttusbdecfe_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct ttusbdecfe_state*) kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->voltage = 0;
+	state->hi_band = 0;
+	memcpy(&state->ops, &ttusbdecfe_dvbs_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops ttusbdecfe_dvbt_ops = {
+
+	.info = {
+		.name			= "TechnoTrend/Hauppauge DEC2000-t Frontend",
+		.type			= FE_OFDM,
+		.frequency_min		= 51000000,
+		.frequency_max		= 858000000,
+		.frequency_stepsize	= 62500,
+		.caps =	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_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = ttusbdecfe_release,
+
+	.set_frontend = ttusbdecfe_dvbt_set_frontend,
+
+	.read_status = ttusbdecfe_read_status,
+};
+
+static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = {
+
+	.info = {
+		.name			= "TechnoTrend/Hauppauge DEC3000-s Frontend",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 125,
+		.caps =	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_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = ttusbdecfe_release,
+
+	.set_frontend = ttusbdecfe_dvbs_set_frontend,
+
+	.read_status = ttusbdecfe_read_status,
+
+	.diseqc_send_master_cmd = ttusbdecfe_dvbs_diseqc_send_master_cmd,
+	.set_voltage = ttusbdecfe_dvbs_set_voltage,
+	.set_tone = ttusbdecfe_dvbs_set_tone,
+};
+
+MODULE_DESCRIPTION("TTUSB DEC DVB-T/S Demodulator driver");
+MODULE_AUTHOR("Alex Woods/Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ttusbdecfe_dvbt_attach);
+EXPORT_SYMBOL(ttusbdecfe_dvbs_attach);
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.h b/drivers/media/dvb/ttusb-dec/ttusbdecfe.h
new file mode 100644
index 0000000..15ccc3d
--- /dev/null
+++ b/drivers/media/dvb/ttusb-dec/ttusbdecfe.h
@@ -0,0 +1,38 @@
+/*
+ * TTUSB DEC Driver
+ *
+ * Copyright (C) 2003-2004 Alex Woods <linux-dvb@giblets.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef TTUSBDECFE_H
+#define TTUSBDECFE_H
+
+#include <linux/dvb/frontend.h>
+
+struct ttusbdecfe_config
+{
+	int (*send_command)(struct dvb_frontend* fe, const u8 command,
+			    int param_length, const u8 params[],
+			    int *result_length, u8 cmd_result[]);
+};
+
+extern struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config);
+
+extern struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config);
+
+#endif // TTUSBDECFE_H