diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/dvb/dvb-core/dvb_frontend.c v4l-dvb/linux/drivers/media/dvb/dvb-core/dvb_frontend.c
--- v4l-dvb.clean/linux/drivers/media/dvb/dvb-core/dvb_frontend.c	2008-08-31 23:36:14.561799130 +0100
+++ v4l-dvb/linux/drivers/media/dvb/dvb-core/dvb_frontend.c	2008-08-31 23:17:45.961801997 +0100
@@ -216,8 +216,9 @@
 
 static void dvb_frontend_init(struct dvb_frontend *fe)
 {
-	dprintk ("DVB: initialising frontend %i (%s)...\n",
+	dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n",
 		 fe->dvb->num,
+		 fe->id,
 		 fe->ops.info.name);
 
 	if (fe->ops.init)
@@ -696,7 +697,7 @@
 	mb();
 
 	fe_thread = kthread_run(dvb_frontend_thread, fe,
-		"kdvb-fe-%i", fe->dvb->num);
+		"kdvb-ad-%i-fe-%i", fe->dvb->num, fe->id);
 	if (IS_ERR(fe_thread)) {
 		ret = PTR_ERR(fe_thread);
 		printk("dvb_frontend_start: failed to start kthread (%d)\n", ret);
@@ -720,8 +721,8 @@
 		*freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max);
 
 	if (*freq_min == 0 || *freq_max == 0)
-		printk(KERN_WARNING "DVB: frontend %u frequency limits undefined - fix the driver\n",
-		       fe->dvb->num);
+		printk(KERN_WARNING "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
+		       fe->dvb->num, fe->id);
 }
 
 static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
@@ -734,8 +735,8 @@
 	dvb_frontend_get_frequeny_limits(fe, &freq_min, &freq_max);
 	if ((freq_min && parms->frequency < freq_min) ||
 	    (freq_max && parms->frequency > freq_max)) {
-		printk(KERN_WARNING "DVB: frontend %u frequency %u out of range (%u..%u)\n",
-		       fe->dvb->num, parms->frequency, freq_min, freq_max);
+		printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
+		       fe->dvb->num, fe->id, parms->frequency, freq_min, freq_max);
 		return -EINVAL;
 	}
 
@@ -745,8 +746,8 @@
 		     parms->u.qpsk.symbol_rate < fe->ops.info.symbol_rate_min) ||
 		    (fe->ops.info.symbol_rate_max &&
 		     parms->u.qpsk.symbol_rate > fe->ops.info.symbol_rate_max)) {
-			printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n",
-			       fe->dvb->num, parms->u.qpsk.symbol_rate,
+			printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
+			       fe->dvb->num, fe->id, parms->u.qpsk.symbol_rate,
 			       fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max);
 			return -EINVAL;
 		}
@@ -756,8 +757,8 @@
 		     parms->u.qam.symbol_rate < fe->ops.info.symbol_rate_min) ||
 		    (fe->ops.info.symbol_rate_max &&
 		     parms->u.qam.symbol_rate > fe->ops.info.symbol_rate_max)) {
-			printk(KERN_WARNING "DVB: frontend %u symbol rate %u out of range (%u..%u)\n",
-			       fe->dvb->num, parms->u.qam.symbol_rate,
+			printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
+			       fe->dvb->num, fe->id, parms->u.qam.symbol_rate,
 			       fe->ops.info.symbol_rate_min, fe->ops.info.symbol_rate_max);
 			return -EINVAL;
 		}
@@ -950,6 +951,11 @@
 			err = fe->ops.enable_high_lnb_voltage(fe, (long) parg);
 		break;
 
+	case FE_ENABLE_S2_EXTENSION:	/* DVB-S2 HACK */
+		if (fe->ops.enable_s2_extension)
+			err = fe->ops.enable_s2_extension(fe, (long) parg);
+		break;
+
 	case FE_SET_FRONTEND: {
 		struct dvb_frontend_tune_settings fetunesettings;
 
@@ -1165,8 +1171,9 @@
 	fe->dvb = dvb;
 	fepriv->inversion = INVERSION_OFF;
 
-	printk ("DVB: registering frontend %i (%s)...\n",
+	printk ("DVB: registering adapter %i frontend %i (%s)...\n",
 		fe->dvb->num,
+		fe->id,
 		fe->ops.info.name);
 
 	dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/dvb/dvb-core/dvb_frontend.h v4l-dvb/linux/drivers/media/dvb/dvb-core/dvb_frontend.h
--- v4l-dvb.clean/linux/drivers/media/dvb/dvb-core/dvb_frontend.h	2008-08-31 23:36:14.565799393 +0100
+++ v4l-dvb/linux/drivers/media/dvb/dvb-core/dvb_frontend.h	2008-08-31 23:17:45.961801997 +0100
@@ -38,6 +38,7 @@
 #include <linux/mutex.h>
 
 #include <linux/dvb/frontend.h>
+#include <linux/dvb/frontend_wrapper.h> /* DVB-S2 HACK */
 
 #include "dvbdev.h"
 
@@ -148,6 +149,8 @@
 	int (*set_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 (*enable_s2_extension)(struct dvb_frontend* fe, long arg); /* DVB-S2 HACK */
+
 	int (*get_frontend)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
 
 	int (*read_status)(struct dvb_frontend* fe, fe_status_t* status);
@@ -190,6 +193,7 @@
 	void *frontend_priv;
 	void *sec_priv;
 	void *analog_demod_priv;
+	int id;
 };
 
 extern int dvb_register_frontend(struct dvb_adapter *dvb,
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/dvb/frontends/cx24116.c v4l-dvb/linux/drivers/media/dvb/frontends/cx24116.c
--- v4l-dvb.clean/linux/drivers/media/dvb/frontends/cx24116.c	1970-01-01 01:00:00.000000000 +0100
+++ v4l-dvb/linux/drivers/media/dvb/frontends/cx24116.c	2008-08-31 23:17:45.977800713 +0100
@@ -0,0 +1,1535 @@
+/*
+    Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2006 Steven Toth <stoth@hauppauge.com>
+    Copyright (C) 2006 Georg Acher
+    Copyright (C) 2007 Steven Toth, Georg Acher, Darron Broad
+
+    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.
+ 
+    HISTORY (Darron):
+
+    March 2007
+	Removed DVB-S2 multiproto support.
+	Fixed legacy v4l-dvb support.
+	Fixed some bugs.
+
+    August 2007
+	Added diseqc support from patch set.
+	Added corrected signal strength support from patch set.
+	Some clean ups.
+
+    November 2007
+	Revert diseqc patch to include caching mechanism
+	as the default. Derived burst method (hack) must
+	now be enabled explicitly.
+	Inspection of reelbox driver reveals new data
+	concerning signal readings. This has been added.
+	Signal strength reading was previously calibrated
+	against dvb2000. Inspection of reelbox code and
+	calibration against a FORTEC receiver confirm that
+	it is indeed signal quality and not strength. This
+	is now returned as SNR.
+	Added detail gleaned from reelbox driver.
+
+    December 2007
+	Defaulted to diseqc burst hack again (now named tone cache).
+	Added option to never send a tone burst.
+	Added sysctl for toneburst switch.
+	Added firmware id dump as per updated reelbox driver.
+	Added DVB-S2 SYSCTLs for testing purposes
+	Updated ISL6421 resolving DiSEqC signalling with some setups.
+	Small tidy ups.
+	Fix newly introduced bug toggling pilot for DVB-S
+
+    February 2008
+	Experimental support+docs of using DVB-S2 from userland
+	on the hvr4000 with minimal changes to v4l-dvb.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/sysctl.h>
+
+#include "dvb_frontend.h"
+#include "cx24116.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk ("cx24116: " args); \
+	} while (0)
+
+/*
+ * Fetch firmware (version 1.20.79.0) in the following manner:
+ *
+ * #!/bin/sh
+ * wget ftp://167.206.143.11/outgoing/Oxford/88x_2_117_24275_1_INF.zip
+ * unzip 88x_2_117_24275_1_INF.zip
+ * dd if=Driver88/hcw88bda.sys of=dvb-fe-cx24116.fw skip=81768 bs=1 count=32522
+ */
+#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
+#define CX24116_SEARCH_RANGE_KHZ 5000
+
+/* known registers */
+#define CX24116_REG_COMMAND (0x00)	/* command args 0x00..0x1e */
+#define CX24116_REG_EXECUTE (0x1f)	/* execute command */ 
+#define CX24116_REG_MAILBOX (0x96)	/* FW or multipurpose mailbox? */
+#define CX24116_REG_RESET   (0x20)	/* reset status > 0     */
+#define CX24116_REG_SIGNAL  (0x9e)	/* signal low           */
+#define CX24116_REG_SSTATUS (0x9d)	/* signal high / status */
+#define CX24116_REG_QSTATUS (0xbc)
+#define CX24116_REG_QUALITY (0xd5)
+#define CX24116_REG_BER0    (0xc9)
+#define CX24116_REG_BER8    (0xc8)
+#define CX24116_REG_BER16   (0xc7)
+#define CX24116_REG_BER24   (0xc6)
+#define CX24116_REG_UCB0    (0xcb)
+#define CX24116_REG_UCB8    (0xca)
+
+#define CX24116_ARGLEN (0x1e)	/* arg buffer size */
+
+/* signal status */
+#define CX24116_HAS_SIGNAL   (0x01)
+#define CX24116_HAS_CARRIER  (0x02)
+#define CX24116_HAS_VITERBI  (0x04)
+#define CX24116_HAS_SYNCLOCK (0x08)
+#define CX24116_HAS_UNKNOWN1 (0x10)
+#define CX24116_HAS_UNKNOWN2 (0x20)
+#define CX24116_STATUS_MASK  (0x3f)
+#define CX24116_SIGNAL_MASK  (0xc0)
+
+/* unconfirmed or just wrong */
+#define CX24116_REG_FECSTATUS (0x9c)	/* FEC in use (nb. for DVB-S range is 1..8, for DVB-S2 as configured)  */
+#define CX24116_FEC_FECMASK   (0x1f)	/* mask to determine configured fec (not tuned) or actual fec (tuned) */
+#define CX24116_FEC_DVBS      (0x20)	/* Select DVB-S demodulator */
+#define CX24116_FEC_UNKNOWN2  (0x40)	/* This bit represents ? */
+#define CX24116_FEC_PILOT     (0x80)	/* This bit represents the pilot mode requested when not tuned and is 0 when tuned */
+
+/* DiSEqC tone burst */
+static int toneburst = 1;
+static struct ctl_table_header *kernel_table_header;
+static ctl_table toneburst_table[] = {
+	{
+	.ctl_name       = 0,
+	.procname       = "toneburst",
+	.data           = &toneburst,
+	.maxlen         = sizeof(int),
+	.mode           = 0666,
+	.child          = NULL,
+	.parent         = NULL,
+	.proc_handler   = &proc_dointvec,
+	.strategy       = NULL,
+	.extra1         = NULL,
+	.extra2         = NULL,
+	},
+	{
+	.ctl_name       = 0,
+	.procname       = "debug",
+	.data           = &debug,
+	.maxlen         = sizeof(int),
+	.mode           = 0666,
+	.child          = NULL,
+	.parent         = NULL,
+	.proc_handler   = &proc_dointvec,
+	.strategy       = NULL,
+	.extra1         = NULL,
+	.extra2         = NULL,
+	},
+	{0},
+};
+static ctl_table cx24116_table[] = {
+	{
+	.ctl_name       = 0,
+	.procname       = "cx24116",
+	.data           = NULL,
+	.maxlen         = 0,
+	.mode           = 0555,
+	.child          = toneburst_table,
+	.parent         = NULL,
+	.proc_handler   = NULL,
+	.strategy       = NULL,
+	.extra1         = NULL,
+	.extra2         = NULL,
+	},
+	{0},
+};
+static ctl_table kernel_table[] = {
+	{
+	.ctl_name       = CTL_DEV,
+	.procname       = "dev",
+	.data           = NULL,
+	.maxlen         = 0,
+	.mode           = 0555,
+	.child          = cx24116_table,
+	.parent         = NULL,
+	.proc_handler   = NULL,
+	.strategy       = NULL,
+	.extra1         = NULL,
+	.extra2         = NULL,
+	},
+	{0},
+};
+#define CX24116_DISEQC_TONEOFF   (0)	/* toneburst never sent */
+#define CX24116_DISEQC_TONECACHE (1)	/* toneburst cached     */
+#define CX24116_DISEQC_MESGCACHE (2)	/* message cached       */
+
+/* arg offset for DiSEqC */
+#define CX24116_DISEQC_BURST  (1)
+#define CX24116_DISEQC_ARG2_2 (2)	/* unknown value=2 */
+#define CX24116_DISEQC_ARG3_0 (3)	/* unknown value=0 */
+#define CX24116_DISEQC_ARG4_0 (4)	/* unknown value=0 */
+#define CX24116_DISEQC_MSGLEN (5)
+#define CX24116_DISEQC_MSGOFS (6)
+
+/* DiSEqC burst */
+#define CX24116_DISEQC_MINI_A (0)
+#define CX24116_DISEQC_MINI_B (1)
+
+enum cmds
+{
+	CMD_SET_VCO	= 0x10,
+	CMD_TUNEREQUEST = 0x11,
+	CMD_MPEGCONFIG  = 0x13,
+	CMD_TUNERINIT	= 0x14,
+	CMD_BANDWIDTH	= 0x15,
+	CMD_GETAGC      = 0x19,
+	CMD_LNBCONFIG   = 0x20,
+	CMD_LNBSEND	= 0x21,	/* Formerly CMD_SEND_DISEQC */
+	CMD_SET_TONEPRE = 0x22,
+	CMD_SET_TONE    = 0x23,
+	CMD_UPDFWVERS   = 0x35,
+	CMD_TUNERSLEEP  = 0x36,
+	CMD_AGCCONTROL  = 0x3b, /* Unknown */
+};
+
+/* The Demod/Tuner can't easily provide these, we cache them */
+struct cx24116_tuning
+{
+	u32 frequency;
+	u32 symbol_rate;
+	u8 modulation;
+	u8 rolloff;
+	u8 pilot;
+	fe_spectral_inversion_t inversion;
+	enum fe_code_rate fec;
+
+	/* Demod values */
+	u8 fec_val;
+	u8 fec_mask;
+	u8 inversion_val;
+};
+
+/* Basic commands that are sent to the firmware */
+struct cx24116_cmd
+{
+	u8 len;
+	u8 args[CX24116_ARGLEN];
+};
+
+struct cx24116_state
+{
+	struct i2c_adapter* i2c;
+	const struct cx24116_config* config;
+
+	struct dvb_frontend frontend;
+
+	struct cx24116_tuning dcur;
+	struct cx24116_tuning dnxt;
+
+	u8 skip_fw_load;
+	struct cx24116_cmd dsec_cmd;
+	int s2_extension;
+};
+
+static int cx24116_writereg(struct cx24116_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 (debug>1)
+		printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
+						__FUNCTION__,reg, data);
+
+	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		printk("%s: writereg error(err == %i, reg == 0x%02x,"
+			 " value == 0x%02x)\n", __FUNCTION__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+/* Bulk byte writes to a single I2C address, for 32k firmware load */
+static int cx24116_writeregN(struct cx24116_state* state, int reg, u8 *data, u16 len)
+{
+	int ret = -EREMOTEIO;
+	struct i2c_msg msg;
+	u8 *buf;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (buf == NULL) {
+		printk("Unable to kmalloc\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	*(buf) = reg;
+	memcpy(buf + 1, data, len);
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = len + 1;
+
+	if (debug>1)
+		printk("cx24116: %s:  write regN 0x%02x, len = %d\n",
+						__FUNCTION__,reg, len);
+
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		printk("%s: writereg error(err == %i, reg == 0x%02x\n",
+			 __FUNCTION__, ret, reg);
+		ret = -EREMOTEIO;
+	}
+
+error:
+	kfree(buf);
+
+	return ret;
+}
+
+static int cx24116_readreg(struct cx24116_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: reg=0x%x (error=%d)\n", __FUNCTION__, reg, ret);
+		return ret;
+	}
+
+	if (debug>1)
+		printk("cx24116: read reg 0x%02x, value 0x%02x\n",reg, b1[0]);
+
+	return b1[0];
+}
+
+static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_inversion_t inversion)
+{
+	dprintk("%s(%d)\n", __FUNCTION__, inversion);
+
+	switch (inversion) {
+	case INVERSION_OFF:
+		state->dnxt.inversion_val = 0x00;
+		break;
+	case INVERSION_ON:
+		state->dnxt.inversion_val = 0x04;
+		break;
+	case INVERSION_AUTO:
+		state->dnxt.inversion_val = 0x0C;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	state->dnxt.inversion = inversion;
+	
+	return 0;
+}
+
+/*
+ * modfec (modulation and FEC)
+ * ===========================
+ *
+ * MOD		FEC		mask/val    standard
+ * ----		--------	----------- --------
+ * QPSK		FEC_1_2		0x02 0x02+X DVB-S
+ * QPSK		FEC_2_3		0x04 0x02+X DVB-S
+ * QPSK		FEC_3_4		0x08 0x02+X DVB-S
+ * QPSK		FEC_4_5		0x10 0x02+X DVB-S (?)
+ * QPSK		FEC_5_6		0x20 0x02+X DVB-S
+ * QPSK		FEC_6_7		0x40 0x02+X DVB-S
+ * QPSK		FEC_7_8		0x80 0x02+X DVB-S
+ * QPSK		FEC_8_9		0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
+ * QPSK		AUTO		0xff 0x02+X DVB-S
+ *
+ * For DVB-S high byte probably represents FEC
+ * and low byte selects the modulator. The high
+ * byte is search range mask. Bit 5 may turn
+ * on DVB-S and remaining bits represent some
+ * kind of calibration (how/what i do not know).
+ *
+ * Eg.(2/3) szap "Zone Horror"
+ *
+ * mask/val = 0x04, 0x20
+ * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 00000000 | FE_HAS_LOCK
+ *
+ * mask/val = 0x04, 0x30
+ * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 00000000 | FE_HAS_LOCK
+ *
+ * After tuning FECSTATUS contains actual FEC
+ * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
+ *
+ * NBC-QPSK	FEC_1_2		0x00, 0x04	DVB-S2
+ * NBC-QPSK	FEC_3_5		0x00, 0x05	DVB-S2
+ * NBC-QPSK	FEC_2_3		0x00, 0x06	DVB-S2
+ * NBC-QPSK	FEC_3_4		0x00, 0x07	DVB-S2
+ * NBC-QPSK	FEC_4_5		0x00, 0x08	DVB-S2
+ * NBC-QPSK	FEC_5_6		0x00, 0x09	DVB-S2
+ * NBC-QPSK	FEC_8_9		0x00, 0x0a	DVB-S2
+ * NBC-QPSK	FEC_9_10	0x00, 0x0b	DVB-S2
+ *
+ * NBC-8PSK	FEC_3_5		0x00, 0x0c	DVB-S2
+ * NBC-8PSK	FEC_2_3		0x00, 0x0d	DVB-S2
+ * NBC-8PSK	FEC_3_4		0x00, 0x0e	DVB-S2
+ * NBC-8PSK	FEC_5_6		0x00, 0x0f	DVB-S2
+ * NBC-8PSK	FEC_8_9		0x00, 0x10	DVB-S2
+ * NBC-8PSK	FEC_9_10	0x00, 0x11	DVB-S2
+ *
+ * For DVB-S2 low bytes selects both modulator
+ * and FEC. High byte is meaningless here. To
+ * set pilot, bit 6 (0x40) is set. When inspecting
+ * FECSTATUS bit 7 (0x80) represents the pilot
+ * selection whilst not tuned. When tuned, actual FEC
+ * in use is found in FECSTATUS as per above. Pilot
+ * value is reset.
+ */
+
+/* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
+struct cx24116_modfec {
+	fe_modulation_t modulation;
+	fe_code_rate_t fec;
+	u8 mask;
+	u8 val;
+} CX24116_MODFEC[] = {
+
+	/*mod   fec	  mask/val */
+	{ QPSK, FEC_NONE, 0xfe, 0x30 },
+ 	{ QPSK, FEC_1_2,  0x02, 0x2e },	/* 00000010 00101110 */
+ 	{ QPSK, FEC_2_3,  0x04, 0x2f },	/* 00000100 00101111 */
+ 	{ QPSK, FEC_3_4,  0x08, 0x30 },	/* 00001000 00110000 */
+ 	{ QPSK, FEC_4_5,  0xfe, 0x30 },	/* 000?0000 ?        */
+ 	{ QPSK, FEC_5_6,  0x20, 0x31 },	/* 00100000 00110001 */
+ 	{ QPSK, FEC_6_7,  0xfe, 0x30 },	/* 0?000000 ?        */
+ 	{ QPSK, FEC_7_8,  0x80, 0x32 },	/* 10000000 00110010 */
+ 	{ QPSK, FEC_8_9,  0xfe, 0x30 },	/* 0000000? ?        */
+ 	{ QPSK,	FEC_AUTO, 0xfe, 0x30 },
+	
+	{ NBC_QPSK, FEC_1_2,  0x00, 0x04 },
+	{ NBC_QPSK, FEC_3_5,  0x00, 0x05 },
+	{ NBC_QPSK, FEC_2_3,  0x00, 0x06 },
+	{ NBC_QPSK, FEC_3_4,  0x00, 0x07 },
+	{ NBC_QPSK, FEC_4_5,  0x00, 0x08 },
+	{ NBC_QPSK, FEC_5_6,  0x00, 0x09 },
+	{ NBC_QPSK, FEC_8_9,  0x00, 0x0a },
+	{ NBC_QPSK, FEC_9_10, 0x00, 0x0b },
+
+	{ NBC_8PSK, FEC_3_5,  0x00, 0x0c },
+	{ NBC_8PSK, FEC_2_3,  0x00, 0x0d },
+	{ NBC_8PSK, FEC_3_4,  0x00, 0x0e },
+	{ NBC_8PSK, FEC_5_6,  0x00, 0x0f },
+	{ NBC_8PSK, FEC_8_9,  0x00, 0x10 },
+	{ NBC_8PSK, FEC_9_10, 0x00, 0x11 },
+
+	/*
+	 * `val' can be found in the FECSTATUS register when tuning.
+	 * FECSTATUS will give the actual FEC in use if tuning was successful.
+	 */
+};
+
+static int cx24116_lookup_fec(struct cx24116_state* state, fe_modulation_t m, fe_code_rate_t f)
+{
+	int i, ret = -EOPNOTSUPP;
+
+	dprintk("%s(0x%02x,0x%02x)\n", __FUNCTION__, m, f);
+
+	for(i=0 ; i < sizeof(CX24116_MODFEC) / sizeof(struct cx24116_modfec) ; i++)
+	{
+		if( m==CX24116_MODFEC[i].modulation && f==CX24116_MODFEC[i].fec )
+		{
+			ret = i;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int cx24116_set_fec(struct cx24116_state* state, struct dvb_frontend_parameters *p)
+{
+	int ret;
+
+	dprintk("%s()\n", __FUNCTION__);
+
+	ret = cx24116_lookup_fec(state, state->dnxt.modulation, p->u.qpsk.fec_inner);
+
+	if( ret == -EOPNOTSUPP )
+	{
+		dprintk("%s() fec unsupported\n", __FUNCTION__);
+	}
+	else
+	{
+		state->dnxt.fec      = p->u.qpsk.fec_inner;
+		state->dnxt.fec_mask = CX24116_MODFEC[ret].mask;
+		state->dnxt.fec_val  = CX24116_MODFEC[ret].val;
+		dprintk("%s() fec supported fec/mask/val = 0x%02x/0x%02x/0x%02x\n", __FUNCTION__,
+			state->dnxt.fec, state->dnxt.fec_mask, state->dnxt.fec_val);
+
+		ret = 0; /* OK */
+	}
+
+	return ret;
+}
+
+static int cx24116_set_symbolrate(struct cx24116_state* state, struct dvb_frontend_parameters *p)
+{
+	int ret = 0;
+
+	dprintk("%s()\n", __FUNCTION__);
+
+	/*  check if symbol rate is within limits */
+	if ((p->u.qpsk.symbol_rate > state->frontend.ops.info.symbol_rate_max) ||
+	    (p->u.qpsk.symbol_rate < state->frontend.ops.info.symbol_rate_min))
+	{
+		ret = -EOPNOTSUPP;
+	}
+	else
+	{
+		state->dnxt.symbol_rate = p->u.qpsk.symbol_rate;
+		dprintk("%s() symbol_rate = %d\n", __FUNCTION__, state->dnxt.symbol_rate);
+	}
+
+	return ret;
+}
+
+static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw);
+
+static int cx24116_firmware_ondemand(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	const struct firmware *fw;
+	int ret = 0;
+
+	dprintk("%s()\n",__FUNCTION__);
+
+	if (cx24116_readreg(state, CX24116_REG_RESET) > 0)
+	{
+		if (state->skip_fw_load)
+			return 0;
+
+		/* Load firmware */
+		/* request the firmware, this will block until someone uploads it */
+		printk("%s: Waiting for firmware upload (%s)...\n", __FUNCTION__, CX24116_DEFAULT_FIRMWARE);
+		ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, &state->i2c->dev);
+		printk("%s: Waiting for firmware upload(2)...\n", __FUNCTION__);
+		if (ret) {
+			printk("%s: No firmware uploaded (timeout or file not found?)\n", __FUNCTION__);
+			return ret;
+		}
+
+		/* Make sure we don't recurse back through here during loading */
+		state->skip_fw_load = 1;
+
+		ret = cx24116_load_firmware(fe, fw);
+		if (ret)
+			printk("%s: Writing firmware to device failed\n", __FUNCTION__);
+
+		release_firmware(fw);
+
+		printk("%s: Firmware upload %s\n", __FUNCTION__, ret == 0 ? "complete" : "failed");
+
+		/* Ensure firmware is always loaded if required */
+		state->skip_fw_load = 0;
+	}
+
+	return ret;
+}
+
+/* Take a basic firmware command structure, format it and forward it for processing */
+static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int i, ret;
+
+	dprintk("%s()\n", __FUNCTION__);
+
+	/* Load the firmware if required */
+	if ( (ret = cx24116_firmware_ondemand(fe)) != 0)
+	{
+		printk("%s(): Unable initialise the firmware\n", __FUNCTION__);
+		return ret;
+	}
+
+	/* Write the command */
+	for(i = CX24116_REG_COMMAND /* =0 */; (i < cmd->len) && (i <= CX24116_ARGLEN) ; i++)
+	{
+		dprintk("%s: 0x%02x == 0x%02x\n", __FUNCTION__, i, cmd->args[i]);
+		cx24116_writereg(state, i, cmd->args[i]);
+	}
+
+	/* Start execution and wait for cmd to terminate */
+	cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
+	while( cx24116_readreg(state, CX24116_REG_EXECUTE) )
+	{
+		msleep(10);
+		if(i++ > 64)
+		{
+			/* Avoid looping forever if the firmware does no respond */
+			printk("%s() Firmware not responding\n", __FUNCTION__);
+			return -EREMOTEIO;
+		}
+	}
+	return 0;
+}
+
+static int cx24116_load_firmware(struct dvb_frontend* fe, const struct firmware *fw)
+{
+	struct cx24116_state* state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int i, ret;
+	unsigned char vers[4];
+
+	dprintk("%s\n", __FUNCTION__);
+	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n"
+			,fw->size
+			,fw->data[0]
+			,fw->data[1]
+			,fw->data[ fw->size-2 ]
+			,fw->data[ fw->size-1 ]
+			);
+
+	/* Toggle 88x SRST pin to reset demod */
+	if (state->config->reset_device)
+		state->config->reset_device(fe);
+
+	/* Begin the firmware load process */
+	/* Prepare the demod, load the firmware, cleanup after load */
+
+	/* Init PLL */
+	cx24116_writereg(state, 0xE5, 0x00);
+	cx24116_writereg(state, 0xF1, 0x08);
+	cx24116_writereg(state, 0xF2, 0x13);
+	
+	/* Start PLL */
+	cx24116_writereg(state, 0xe0, 0x03);
+	cx24116_writereg(state, 0xe0, 0x00);
+
+	/* Unknown */
+	cx24116_writereg(state, 0xF3, 0x46);
+	cx24116_writereg(state, 0xF9, 0x00);
+	
+	/* Unknown */
+	cx24116_writereg(state, 0xF0, 0x03);
+	cx24116_writereg(state, 0xF4, 0x81);
+	cx24116_writereg(state, 0xF5, 0x00);
+	cx24116_writereg(state, 0xF6, 0x00);
+
+	/* write the entire firmware as one transaction */
+	cx24116_writeregN(state, 0xF7, fw->data, fw->size);
+
+	cx24116_writereg(state, 0xF4, 0x10);
+	cx24116_writereg(state, 0xF0, 0x00);
+	cx24116_writereg(state, 0xF8, 0x06);
+
+	/* Firmware CMD 10: VCO config */
+	cmd.args[0x00] = CMD_SET_VCO;
+	cmd.args[0x01] = 0x05;
+	cmd.args[0x02] = 0xdc;
+	cmd.args[0x03] = 0xda;
+	cmd.args[0x04] = 0xae;
+	cmd.args[0x05] = 0xaa;
+	cmd.args[0x06] = 0x04;
+	cmd.args[0x07] = 0x9d;
+	cmd.args[0x08] = 0xfc;
+	cmd.args[0x09] = 0x06;
+	cmd.len= 0x0a;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
+
+	/* Firmware CMD 14: Tuner config */
+	cmd.args[0x00] = CMD_TUNERINIT;
+	cmd.args[0x01] = 0x00;
+	cmd.args[0x02] = 0x00;
+	cmd.len= 0x03;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	cx24116_writereg(state, 0xe5, 0x00);
+
+	/* Firmware CMD 13: MPEG config */
+	cmd.args[0x00] = CMD_MPEGCONFIG;
+	cmd.args[0x01] = 0x01;
+	cmd.args[0x02] = 0x75;
+	cmd.args[0x03] = 0x00;
+	cmd.args[0x04] = 0x02;
+	cmd.args[0x05] = 0x00;
+	cmd.len= 0x06;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Firmware CMD 35: Get firmware version */
+	cmd.args[0x00] = CMD_UPDFWVERS;
+	cmd.len= 0x02;
+	for(i=0; i<4; i++) {
+		cmd.args[0x01] = i;
+		ret = cx24116_cmd_execute(fe, &cmd);
+		if (ret != 0)
+			return ret;
+		vers[i]= cx24116_readreg(state, CX24116_REG_MAILBOX);
+	}
+	printk("%s: FW version %i.%i.%i.%i\n", __FUNCTION__, vers[0], vers[1], vers[2], vers[3]);
+
+	return 0;
+}
+
+static int cx24116_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	/* The isl6421 module will override this function in the fops. */
+	dprintk("%s() This should never appear if the isl6421 module is loaded correctly\n",__FUNCTION__);
+
+	return -EOPNOTSUPP;
+}
+
+/*
+ * Registers affected by tuning (?)
+ *
+ * 0x2e DVB-S2
+ * 0x2f DVB-S2
+ *
+ * 0x70 DVB-S
+ *
+ * 0x83 DVB-S2
+ *
+ * 0x97 DVB-S
+ * 0x98 DVB-S
+ * 0x99 DVB-S
+ *
+ * 0x9c BOTH
+ * 0x9d BOTH
+ * 0x9e BOTH
+ * 0x9f BOTH
+ *
+ * 0xa0 BOTH
+ *
+ * 0xb1 DVB-S
+ * 0xb2 DVB-S
+ * 0xb3 DVB-S
+ * 0xb7 DVB-S
+ * 0xb8 DVB-S
+ * 0xbc DVB-S
+ *
+ * 0xd4 DVB-S
+ * 0xd5 BOTH
+ * 0xda DVB-S
+ *
+ * 0xf5 DVB-S
+ * 0xfd DVB-S
+ */
+static void cx24116_dump_registers(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int x, y, reg=0, val;
+
+	for(y=2 /* Ignore command mailbox */; y<16; y++) {
+		printk("%s: %1x0: ", __FUNCTION__, y);
+		for(x=0; x<16; x++) {
+			reg = (y << 4) + x;
+			val = cx24116_readreg(state, reg);
+			if(x!=15)
+				printk("%02x ",  val);
+			else
+				printk("%02x\n", val);
+		}
+	}
+	printk("%s: -- MARK --\n", __FUNCTION__);
+}
+
+#if 0
+static void cx24116_dump_reg(struct dvb_frontend *fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	printk("%s: 9c=%02x 9d=%02x 9e=%02x 9f=%02x\n", __FUNCTION__,
+		cx24116_readreg(state, 0x9c),
+		cx24116_readreg(state, 0x9d),
+		cx24116_readreg(state, 0x9e),
+		cx24116_readreg(state, 0x9f));
+}
+#endif
+
+static int cx24116_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	int lock = cx24116_readreg(state, CX24116_REG_SSTATUS);
+
+	dprintk("%s: status = 0x%02x\n", __FUNCTION__, lock);
+
+#if 0
+	if(debug)
+		cx24116_dump_registers(fe);
+#endif
+
+	*status = 0;
+
+	if (lock & CX24116_HAS_SIGNAL)
+		*status |= FE_HAS_SIGNAL;
+
+	if (lock & CX24116_HAS_CARRIER)
+		*status |= FE_HAS_CARRIER;
+
+	if (lock & CX24116_HAS_VITERBI)
+		*status |= FE_HAS_VITERBI;
+
+	if (lock & CX24116_HAS_SYNCLOCK)
+		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+	return 0;
+}
+
+#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK)
+static int cx24116_is_tuned(struct dvb_frontend* fe)
+{
+	fe_status_t tunerstat;
+	
+	cx24116_read_status(fe, &tunerstat);
+
+	return ( (tunerstat & FE_IS_TUNED) == FE_IS_TUNED );
+}
+
+static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	dprintk("%s()\n", __FUNCTION__);
+
+	*ber = ( cx24116_readreg(state, CX24116_REG_BER24) << 24 ) | ( cx24116_readreg(state, CX24116_REG_BER16) << 16 ) |
+	       ( cx24116_readreg(state, CX24116_REG_BER8 ) << 8  ) |   cx24116_readreg(state, CX24116_REG_BER0 );
+
+	return 0;
+}
+
+/* TODO Determine function and scale appropriately */
+static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+	u16 sig_reading;
+	
+	dprintk("%s()\n", __FUNCTION__);
+
+	/* Firmware CMD 19: Get AGC */
+	cmd.args[0x00] = CMD_GETAGC;
+	cmd.len= 0x01;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	sig_reading =	( cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK ) |
+			( cx24116_readreg(state, CX24116_REG_SIGNAL) << 6 );
+
+	*signal_strength= 0 - sig_reading;
+
+	dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __FUNCTION__, sig_reading, *signal_strength);
+
+	return 0;
+}
+
+/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
+static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	u8 snr_reading;
+	static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
+		0x00000,0x0199A,0x03333,0x04ccD,0x06667,0x08000,0x0999A,0x0b333,0x0cccD,0x0e667,
+		0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 };
+
+	dprintk("%s()\n", __FUNCTION__);
+
+	snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY);
+
+	if(snr_reading >= 0xa0 /* 100% */)
+		*snr = 0xffff;
+	else
+		*snr = snr_tab [ ( snr_reading & 0xf0 )   >> 4 ] +
+		     ( snr_tab [ ( snr_reading & 0x0f ) ] >> 4 );
+
+	dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __FUNCTION__, snr_reading, *snr);
+	
+	return 0;
+}
+
+static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	dprintk("%s()\n", __FUNCTION__);
+
+	*ucblocks = ( cx24116_readreg(state, CX24116_REG_UCB8) << 8 ) | cx24116_readreg(state, CX24116_REG_UCB0);
+
+	return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void cx24116_clone_params(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
+}
+
+/* Wait for LNB */
+static int cx24116_wait_for_lnb(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int i;
+
+	dprintk("%s() qstatus = 0x%02x\n", __FUNCTION__, cx24116_readreg(state, CX24116_REG_QSTATUS));
+		
+	/* Wait for up to 300 ms */
+	for(i = 0; i < 30 ; i++) {
+		if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
+			return 0;
+		msleep(10);
+	}
+
+	dprintk("%s(): LNB not ready\n", __FUNCTION__);
+
+	return -ETIMEDOUT; /* -EBUSY ? */
+}
+
+static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct cx24116_cmd cmd;
+	int ret;
+
+	dprintk("%s(%d)\n", __FUNCTION__, tone);
+	if ( (tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF) ) {
+		printk("%s: Invalid, tone=%d\n", __FUNCTION__, tone);
+		return -EINVAL;
+	}
+	
+	/* Wait for LNB ready */
+	ret = cx24116_wait_for_lnb(fe);
+	if(ret != 0)
+		return ret;
+
+	/* Min delay time after DiSEqC send */
+	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+	/* This is always done before the tone is set */
+	cmd.args[0x00] = CMD_SET_TONEPRE;
+	cmd.args[0x01] = 0x00;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Now we set the tone */
+	cmd.args[0x00] = CMD_SET_TONE;
+	cmd.args[0x01] = 0x00;
+	cmd.args[0x02] = 0x00;
+	switch (tone) {
+	case SEC_TONE_ON:
+		dprintk("%s: setting tone on\n", __FUNCTION__);
+		cmd.args[0x03] = 0x01;
+		break;
+	case SEC_TONE_OFF:
+		dprintk("%s: setting tone off\n",__FUNCTION__);
+		cmd.args[0x03] = 0x00;
+		break;
+	}
+	cmd.len= 0x04;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	
+	/* Min delay time before DiSEqC send */
+	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+	
+	return ret;
+}
+
+/* Initialise DiSEqC */
+static int cx24116_diseqc_init(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+
+	/* Firmware CMD 20: LNB/DiSEqC config */
+	cmd.args[0x00] = CMD_LNBCONFIG;
+	cmd.args[0x01] = 0x00;
+	cmd.args[0x02] = 0x10;
+	cmd.args[0x03] = 0x00;
+	cmd.args[0x04] = 0x8f;
+	cmd.args[0x05] = 0x28;
+	cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
+	cmd.args[0x07] = 0x01;
+	cmd.len= 0x08;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Prepare a DiSEqC command */
+	state->dsec_cmd.args[0x00] = CMD_LNBSEND;
+
+	/* DiSEqC burst */
+	state->dsec_cmd.args[CX24116_DISEQC_BURST]  = CX24116_DISEQC_MINI_A;
+
+	/* Unknown */
+	state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
+	state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
+	state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; /* Continuation flag? */
+
+	/* DiSEqC message length */
+	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
+
+	/* Command length */
+	state->dsec_cmd.len= CX24116_DISEQC_MSGOFS;
+
+	return 0;
+}
+
+/* If toneburst enabled send with derived burst (hack) OR cache message until burst is known in send burst */
+static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int i, ret;
+
+	/* Dump DiSEqC message */
+	if (debug) {
+		printk("cx24116: %s(", __FUNCTION__);
+		for(i = 0 ; i < d->msg_len ;) {
+			printk("0x%02x", d->msg[i]);
+			if(++i < d->msg_len)
+				printk(", ");
+		}
+		printk(") toneburst=%d\n", toneburst);
+	}
+
+	/* Validate length */
+	if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS))
+		return -EINVAL;
+
+	/* DiSEqC message */
+	for (i = 0; i < d->msg_len; i++)
+		state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
+		
+	/* DiSEqC message length */
+	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
+
+	/* Command length */
+	state->dsec_cmd.len= CX24116_DISEQC_MSGOFS + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
+
+	/* DiSEqC toneburst */
+	if(toneburst == CX24116_DISEQC_MESGCACHE)
+		/* Message is cached */
+		return 0;
+
+	else if(toneburst == CX24116_DISEQC_TONEOFF)
+		/* Message is sent without burst */
+		state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
+
+	else if(toneburst == CX24116_DISEQC_TONECACHE) {
+		/*
+		 * Message is sent with derived else cached burst
+		 *
+		 * WRITE PORT GROUP COMMAND 38
+		 *
+		 * 0/A/A: E0 10 38 F0..F3
+		 * 1/B/B: E0 10 38 F4..F7
+		 * 2/C/A: E0 10 38 F8..FB
+		 * 3/D/B: E0 10 38 FC..FF
+		 *
+		 * datebyte[3]= 8421:8421
+		 *		ABCD:WXYZ
+		 *		CLR :SET
+		 *
+		 *		WX= PORT SELECT 0..3	(X=TONEBURST)
+		 *		Y = VOLTAGE		(0=13V, 1=18V)
+		 *		Z = BAND		(0=LOW, 1=HIGH(22K))
+		 */
+		if(d->msg_len >= 4 && d->msg[2] == 0x38)
+			state->dsec_cmd.args[CX24116_DISEQC_BURST] = ((d->msg[3] & 4) >> 2);
+		if(debug)
+			dprintk("%s burst=%d\n", __FUNCTION__, state->dsec_cmd.args[CX24116_DISEQC_BURST]);
+	}
+
+	/* Wait for LNB ready */
+	ret = cx24116_wait_for_lnb(fe);
+	if(ret != 0)
+		return ret;
+	
+	/* Wait for voltage/min repeat delay */
+	msleep(100);
+
+	/* Command */
+	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+	if(ret != 0)
+		return ret;
+
+	/* 
+	 * Wait for send
+	 *
+	 * Eutelsat spec:
+	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
+	 *  13.5ms per byte     +
+	 * >15ms delay          +
+	 *  12.5ms burst        +
+	 * >15ms delay		  (XXX determine if FW does this, see set_tone)
+	 */
+	msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60) );
+
+	return 0;
+}
+
+/* Send DiSEqC burst */
+static int cx24116_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int ret;
+
+	dprintk("%s(%d) toneburst=%d\n",__FUNCTION__, burst, toneburst);
+
+	/* DiSEqC burst */
+	if (burst == SEC_MINI_A)
+		state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+	else if(burst == SEC_MINI_B)
+		state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B;
+	else
+		return -EINVAL;
+
+	/* DiSEqC toneburst */
+	if(toneburst != CX24116_DISEQC_MESGCACHE)
+		/* Burst is cached */
+		return 0;
+
+	/* Burst is to be sent with cached message */
+
+	/* Wait for LNB ready */
+	ret = cx24116_wait_for_lnb(fe);
+	if(ret != 0)
+		return ret;
+	
+	/* Wait for voltage/min repeat delay */
+	msleep(100);
+	
+	/* Command */
+	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+	if(ret != 0)
+		return ret;
+	
+	/* 
+	 * Wait for send
+	 *
+	 * Eutelsat spec:
+	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
+	 *  13.5ms per byte     +
+	 * >15ms delay          +
+	 *  12.5ms burst        +
+	 * >15ms delay		  (XXX determine if FW does this, see set_tone)
+	 */
+	msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 );
+
+	return 0;
+}
+
+static void cx24116_release(struct dvb_frontend* fe)
+{
+	struct cx24116_state* state = fe->demodulator_priv;
+	dprintk("%s\n",__FUNCTION__);
+	kfree(state);
+	unregister_sysctl_table(kernel_table_header);
+}
+
+static struct dvb_frontend_ops cx24116_ops;
+
+struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct cx24116_state* state = NULL;
+	int ret;
+
+	dprintk("%s\n",__FUNCTION__);
+
+	kernel_table_header = register_sysctl_table(kernel_table);
+	if(!kernel_table_header)
+		goto error1;
+
+	/* allocate memory for the internal state */
+	state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL);
+	if (state == NULL) {
+		printk("Unable to kmalloc\n");
+		goto error2;
+	}
+
+	/* setup the state */
+	memset(state, 0, sizeof(struct cx24116_state));
+
+	state->config = config;
+	state->i2c = i2c;
+
+	/* check if the demod is present */
+	ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE);
+	if (ret != 0x0501) {
+		printk("Invalid probe, probably not a CX24116 device\n");
+		goto error3;
+	}
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &cx24116_ops, sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error3: kfree(state);
+error2: unregister_sysctl_table(kernel_table_header);
+error1: return NULL;
+}
+
+static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int i, ret;
+	u8 status;
+	int retune, fec_val;
+
+	dprintk("%s() s2_extension=%d\n",__FUNCTION__, state->s2_extension);
+
+	state->dnxt.frequency  = p->frequency;
+
+	if(state->s2_extension) {
+		/* DVB-S2 extension on */
+		if( DVBS_GET_MODULATION(p) == QPSK || DVBS_GET_MODULATION(p) == NBC_QPSK ||
+				DVBS_GET_MODULATION(p) == NBC_8PSK ) {
+			state->dnxt.modulation = DVBS_GET_MODULATION(p);
+		} else {
+			return -EOPNOTSUPP;
+		}
+		if( DVBS_GET_ROLLOFF(p) == ROLLOFF_020 || DVBS_GET_ROLLOFF(p) == ROLLOFF_025 ||
+				DVBS_GET_ROLLOFF(p) == ROLLOFF_035 ) {
+			state->dnxt.rolloff = DVBS_GET_ROLLOFF(p);
+		} else {
+			return -EOPNOTSUPP;
+		}
+		if( DVBS_GET_PILOT(p) == PILOT_OFF || DVBS_GET_PILOT(p) == PILOT_ON ||
+				DVBS_GET_PILOT(p) == PILOT_AUTO ) {
+			state->dnxt.pilot = DVBS_GET_PILOT(p);
+		} else {
+			return -EOPNOTSUPP;
+		}
+	} else {
+		/* DVB-S2 extension off */
+		state->dnxt.modulation = QPSK;
+		state->dnxt.rolloff    = ROLLOFF_035;
+		state->dnxt.pilot      = PILOT_OFF;
+	}
+
+	if ((ret = cx24116_set_inversion(state, p->inversion)) !=  0)
+		return ret;
+
+	if ((ret = cx24116_set_fec(state, p)) !=  0)
+		return ret;
+
+	if ((ret = cx24116_set_symbolrate(state, p)) !=  0)
+		return ret;
+	
+	/* discard the 'current' tuning parameters and prepare to tune */
+	cx24116_clone_params(fe);
+
+	retune  = 1;
+	fec_val = state->dcur.fec_val;
+	if(state->dcur.modulation != QPSK) {
+		if(state->dcur.pilot == PILOT_AUTO )
+			retune += 1;
+		else if( state->dcur.pilot == PILOT_ON )
+			fec_val |= 0x40;
+		else /* PILOT OFF */
+			fec_val &= ~0x40;
+	}
+
+	if(debug) {
+		switch (state->dcur.modulation) {
+			case QPSK :
+				printk("cx24116: %s:   modulation  = QPSK\n", __FUNCTION__);
+				break;
+			case NBC_QPSK:
+				printk("cx24116: %s:   modulation  = NBC-QPSK\n", __FUNCTION__);
+				break;
+			case NBC_8PSK:
+				printk("cx24116: %s:   modulation  = NBC-8PSK\n", __FUNCTION__);
+				break;
+		}
+	}
+	dprintk("%s:   retune      = %d\n", __FUNCTION__, retune);
+	dprintk("%s:   rolloff     = %d\n", __FUNCTION__, state->dcur.rolloff);
+	dprintk("%s:   frequency   = %d\n", __FUNCTION__, state->dcur.frequency);
+	dprintk("%s:   symbol_rate = %d\n", __FUNCTION__, state->dcur.symbol_rate);
+	dprintk("%s:   FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __FUNCTION__,
+		state->dcur.fec, state->dcur.fec_mask, fec_val);
+	dprintk("%s:   Inversion   = %d (val = 0x%02x)\n", __FUNCTION__,
+		state->dcur.inversion, state->dcur.inversion_val);
+
+	/* XXX This is also done in advise/acquire on HVR4000 (not LITE) */
+	if (state->config->set_ts_params)
+		state->config->set_ts_params(fe, 0);
+
+	/* Set/Reset B/W */
+	cmd.args[0x00] = CMD_BANDWIDTH;
+	cmd.args[0x01] = 0x01;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Prepare a tune request */
+	cmd.args[0x00] = CMD_TUNEREQUEST;
+
+	/* Frequency */
+	cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;
+	cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;
+	cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);
+
+	/* Symbol Rate */
+	cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
+	cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
+
+	/* Automatic Inversion */
+	cmd.args[0x06] = state->dcur.inversion_val;
+
+	/* Modulation / FEC */
+	cmd.args[0x07] = fec_val;
+	cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
+	cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
+	cmd.args[0x0a] = 0x00;
+	cmd.args[0x0b] = 0x00;
+	cmd.args[0x0c] = state->dcur.rolloff;
+	cmd.args[0x0d] = state->dcur.fec_mask;
+	cmd.args[0x0e] = 0x06;
+	cmd.args[0x0f] = 0x00;
+	cmd.args[0x10] = 0x00;
+	cmd.args[0x11] = 0xFA;
+	cmd.args[0x12] = 0x24;
+	cmd.len= 0x13;
+
+	/* Set/Reset unknown */
+	cx24116_writereg(state, 0xF9, 0x00);
+	cx24116_writereg(state, 0xF3, 0x46);
+	
+	do {
+		/* Reset status register */
+		status = cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK;
+		cx24116_writereg(state, CX24116_REG_SSTATUS, status);
+
+		/* Tune */
+		ret = cx24116_cmd_execute(fe, &cmd);
+		if (ret != 0)
+			return ret;
+
+		/*
+		 * Wait for up to 500 ms before retrying
+		 *
+		 * If we able to tune then generally it occurs within 100ms
+		 * If it takes longer, try a different toneburst setting.
+		 */
+		for(i = 0; i < 50 ; i++) {
+			if( cx24116_is_tuned(fe) ) {
+				if(debug) {
+					printk("%s: Tuned\n", __FUNCTION__);
+					cx24116_dump_registers(fe);
+				}
+				goto tuned;
+			}
+			msleep(10);
+		}
+
+		if(debug) {
+			printk("%s: Not tuned\n", __FUNCTION__);
+			cx24116_dump_registers(fe);
+		}
+
+		if(state->dcur.pilot == PILOT_AUTO) {
+			/* DVB-S2: Toggle pilot bit */
+			cmd.args[0x07] ^= 0x40;
+		}
+
+	} while(--retune);
+
+tuned:	/* Set/Reset B/W */
+	cmd.args[0x00] = CMD_BANDWIDTH;
+	cmd.args[0x01] = 0x00;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	return ret;
+}
+
+/*
+ * Enable DVB-S2 extension for SET_FRONTEND
+ */
+static int cx24116_enable_s2_extension(struct dvb_frontend* fe, long arg)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	state->s2_extension = arg;
+
+	return state->s2_extension;
+}
+
+static int cx24116_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int ret = 0;
+
+	dprintk("%s()\n",__FUNCTION__);
+
+	p->frequency = state->dcur.frequency;
+	p->inversion = state->dcur.inversion;
+	p->u.qpsk.fec_inner = state->dcur.fec;
+	p->u.qpsk.symbol_rate = state->dcur.symbol_rate;
+	DVBS_SET_MODULATION(p, state->dcur.modulation);
+	DVBS_SET_ROLLOFF(p, state->dcur.rolloff);
+	DVBS_SET_PILOT(p, state->dcur.pilot);
+
+	return ret;
+}
+
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int cx24116_initfe(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+
+	dprintk("%s()\n",__FUNCTION__);
+		
+	/* Extension defaults to off */
+	state->s2_extension = 0;
+
+	/* Power on */
+	cx24116_writereg(state, 0xe0, 0);
+	cx24116_writereg(state, 0xe1, 0);
+	cx24116_writereg(state, 0xea, 0);
+	
+	/* Firmware CMD 36: Power config */
+	cmd.args[0x00] = CMD_TUNERSLEEP;
+	cmd.args[0x01] = 0;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if(ret != 0)
+		return ret;
+	
+	return cx24116_diseqc_init(fe);
+}
+
+/*
+ * Put device to sleep
+ */
+static int cx24116_sleep(struct dvb_frontend* fe)
+{
+	struct cx24116_state* state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+
+	dprintk("%s()\n",__FUNCTION__);
+
+	/* Firmware CMD 36: Power config */
+	cmd.args[0x00] = CMD_TUNERSLEEP;
+	cmd.args[0x01] = 1;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if(ret != 0)
+		return ret;
+
+	/* Power off (Shutdown clocks) */
+	cx24116_writereg(state, 0xea, 0xff);
+	cx24116_writereg(state, 0xe1, 1);
+	cx24116_writereg(state, 0xe0, 1);
+
+	return 0;
+}
+
+static struct dvb_frontend_ops cx24116_ops = {
+
+	.info = {
+		.name = "Conexant CX24116/CX24118",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
+		.frequency_tolerance = 5000,
+		.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_4_5  | FE_CAN_FEC_5_6  | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8  | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK     | FE_CAN_RECOVER  |
+			FE_CAN_NBC_QPSK | FE_CAN_NBC_8PSK | FE_CAN_FEC_3_5 |
+			FE_CAN_FEC_9_10
+	},
+
+	.release = cx24116_release,
+
+	.init = cx24116_initfe,
+	.sleep = cx24116_sleep,
+	.set_frontend = cx24116_set_frontend,
+	.enable_s2_extension = cx24116_enable_s2_extension,
+	.get_frontend = cx24116_get_frontend,
+	.read_status = cx24116_read_status,
+	.read_ber = cx24116_read_ber,
+	.read_signal_strength = cx24116_read_signal_strength,
+	.read_snr = cx24116_read_snr,
+	.read_ucblocks = cx24116_read_ucblocks,
+	.set_tone = cx24116_set_tone,
+	.set_voltage = cx24116_set_voltage,
+	.diseqc_send_master_cmd = cx24116_send_diseqc_msg,
+	.diseqc_send_burst = cx24116_diseqc_send_burst,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+module_param(toneburst, int, 0644);
+MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, 2=MESSAGE CACHE (default:1)");
+
+MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx24116_attach);
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/dvb/frontends/cx24116.h v4l-dvb/linux/drivers/media/dvb/frontends/cx24116.h
--- v4l-dvb.clean/linux/drivers/media/dvb/frontends/cx24116.h	1970-01-01 01:00:00.000000000 +0100
+++ v4l-dvb/linux/drivers/media/dvb/frontends/cx24116.h	2008-08-31 23:17:45.977800713 +0100
@@ -0,0 +1,41 @@
+/*
+    Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2006 Steven Toth <stoth@hauppauge.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 CX24116_H
+#define CX24116_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24116_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* Need to set device param for start_dma */
+	int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+
+	/* Need to reset device during firmware loading */
+	int (*reset_device)(struct dvb_frontend* fe);
+};
+
+extern struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+					struct i2c_adapter* i2c);
+
+#endif /* CX24116_H */
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/dvb/frontends/Kconfig v4l-dvb/linux/drivers/media/dvb/frontends/Kconfig
--- v4l-dvb.clean/linux/drivers/media/dvb/frontends/Kconfig	2008-08-31 23:36:14.785799498 +0100
+++ v4l-dvb/linux/drivers/media/dvb/frontends/Kconfig	2008-08-31 23:17:45.961801997 +0100
@@ -15,6 +15,13 @@
 comment "DVB-S (satellite) frontends"
 	depends on DVB_CORE
 
+config DVB_CX24116
+	tristate "Conexant CX24116 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
 config DVB_CX24110
 	tristate "Conexant CX24110 based"
 	depends on DVB_CORE && I2C
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/dvb/frontends/Makefile v4l-dvb/linux/drivers/media/dvb/frontends/Makefile
--- v4l-dvb.clean/linux/drivers/media/dvb/frontends/Makefile	2008-08-31 23:36:14.785799498 +0100
+++ v4l-dvb/linux/drivers/media/dvb/frontends/Makefile	2008-08-31 23:19:53.635255666 +0100
@@ -49,3 +49,7 @@
 obj-$(CONFIG_DVB_TDA10048) += tda10048.o
 obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
 obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
+obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
+obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
+obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_DVB_CX24116) += cx24116.o
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx23885/cx23885-dvb.c v4l-dvb/linux/drivers/media/video/cx23885/cx23885-dvb.c
--- v4l-dvb.clean/linux/drivers/media/video/cx23885/cx23885-dvb.c	2008-08-31 23:36:15.601800265 +0100
+++ v4l-dvb/linux/drivers/media/video/cx23885/cx23885-dvb.c	2008-08-31 23:38:09.366857156 +0100
@@ -315,48 +315,53 @@
 {
 	struct cx23885_dev *dev = port->dev;
 	struct cx23885_i2c *i2c_bus = NULL;
+	struct videobuf_dvb_frontend *fe0;
+
+	fe0 = videobuf_dvb_get_frontend(&port->frontends, 0);
+	if (!fe0)
+		return -EINVAL;
 
 	/* init struct videobuf_dvb */
-	port->dvb.name = dev->name;
+	fe0->dvb.name = dev->name;
 
 	/* init frontend */
 	switch (dev->board) {
 	case CX23885_BOARD_HAUPPAUGE_HVR1250:
 		i2c_bus = &dev->i2c_bus[0];
-		port->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 						&hauppauge_generic_config,
 						&i2c_bus->i2c_adap);
-		if (port->dvb.frontend != NULL) {
-			dvb_attach(mt2131_attach, port->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(mt2131_attach, fe0->dvb.frontend,
 				   &i2c_bus->i2c_adap,
 				   &hauppauge_generic_tunerconfig, 0);
 		}
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1800:
 		i2c_bus = &dev->i2c_bus[0];
-		switch (alt_tuner) {
+		switch (alt_tuner) { // XXXXXX multifrontend?
 		case 1:
-			port->dvb.frontend =
+			fe0->dvb.frontend =
 				dvb_attach(s5h1409_attach,
 					   &hauppauge_ezqam_config,
 					   &i2c_bus->i2c_adap);
-			if (port->dvb.frontend != NULL) {
-				dvb_attach(tda829x_attach, port->dvb.frontend,
+			if (fe0->dvb.frontend != NULL) {
+				dvb_attach(tda829x_attach, fe0->dvb.frontend,
 					   &dev->i2c_bus[1].i2c_adap, 0x42,
 					   &tda829x_no_probe);
-				dvb_attach(tda18271_attach, port->dvb.frontend,
+				dvb_attach(tda18271_attach, fe0->dvb.frontend,
 					   0x60, &dev->i2c_bus[1].i2c_adap,
 					   &hauppauge_tda18271_config);
 			}
 			break;
 		case 0:
 		default:
-			port->dvb.frontend =
+			fe0->dvb.frontend =
 				dvb_attach(s5h1409_attach,
 					   &hauppauge_generic_config,
 					   &i2c_bus->i2c_adap);
-			if (port->dvb.frontend != NULL)
-				dvb_attach(mt2131_attach, port->dvb.frontend,
+			if (fe0->dvb.frontend != NULL)
+				dvb_attach(mt2131_attach, fe0->dvb.frontend,
 					   &i2c_bus->i2c_adap,
 					   &hauppauge_generic_tunerconfig, 0);
 			break;
@@ -364,47 +369,47 @@
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
 		i2c_bus = &dev->i2c_bus[0];
-		port->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 						&hauppauge_hvr1800lp_config,
 						&i2c_bus->i2c_adap);
-		if (port->dvb.frontend != NULL) {
-			dvb_attach(mt2131_attach, port->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(mt2131_attach, fe0->dvb.frontend,
 				   &i2c_bus->i2c_adap,
 				   &hauppauge_generic_tunerconfig, 0);
 		}
 		break;
 	case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
 		i2c_bus = &dev->i2c_bus[0];
-		port->dvb.frontend = dvb_attach(lgdt330x_attach,
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
 						&fusionhdtv_5_express,
 						&i2c_bus->i2c_adap);
-		if (port->dvb.frontend != NULL) {
-			dvb_attach(simple_tuner_attach, port->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 				   &i2c_bus->i2c_adap, 0x61,
 				   TUNER_LG_TDVS_H06XF);
 		}
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
 		i2c_bus = &dev->i2c_bus[1];
-		port->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 						&hauppauge_hvr1500q_config,
 						&dev->i2c_bus[0].i2c_adap);
-		if (port->dvb.frontend != NULL)
-			dvb_attach(xc5000_attach, port->dvb.frontend,
+		if (fe0->dvb.frontend != NULL)
+			dvb_attach(xc5000_attach, fe0->dvb.frontend,
 				&i2c_bus->i2c_adap,
 				&hauppauge_hvr1500q_tunerconfig, port);
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1500:
 		i2c_bus = &dev->i2c_bus[1];
-		port->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 						&hauppauge_hvr1500_config,
 						&dev->i2c_bus[0].i2c_adap);
-		if (port->dvb.frontend != NULL) {
+		if (fe0->dvb.frontend != NULL) {
 			struct dvb_frontend *fe;
 			struct xc2028_config cfg = {
 				.i2c_adap  = &i2c_bus->i2c_adap,
 				.i2c_addr  = 0x61,
-				.video_dev = port,
+				.video_dev = fe0,
 				.callback  = cx23885_tuner_callback,
 			};
 			static struct xc2028_ctrl ctl = {
@@ -414,7 +419,7 @@
 			};
 
 			fe = dvb_attach(xc2028_attach,
-					port->dvb.frontend, &cfg);
+					fe0->dvb.frontend, &cfg);
 			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
 				fe->ops.tuner_ops.set_config(fe, &ctl);
 		}
@@ -422,24 +427,24 @@
 	case CX23885_BOARD_HAUPPAUGE_HVR1200:
 	case CX23885_BOARD_HAUPPAUGE_HVR1700:
 		i2c_bus = &dev->i2c_bus[0];
-		port->dvb.frontend = dvb_attach(tda10048_attach,
+		fe0->dvb.frontend = dvb_attach(tda10048_attach,
 			&hauppauge_hvr1200_config,
 			&i2c_bus->i2c_adap);
-		if (port->dvb.frontend != NULL) {
-			dvb_attach(tda829x_attach, port->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			dvb_attach(tda829x_attach, fe0->dvb.frontend,
 				&dev->i2c_bus[1].i2c_adap, 0x42,
 				&tda829x_no_probe);
-			dvb_attach(tda18271_attach, port->dvb.frontend,
+			dvb_attach(tda18271_attach, fe0->dvb.frontend,
 				0x60, &dev->i2c_bus[1].i2c_adap,
 				&hauppauge_hvr1200_tuner_config);
 		}
 		break;
 	case CX23885_BOARD_HAUPPAUGE_HVR1400:
 		i2c_bus = &dev->i2c_bus[0];
-		port->dvb.frontend = dvb_attach(dib7000p_attach,
+		fe0->dvb.frontend = dvb_attach(dib7000p_attach,
 			&i2c_bus->i2c_adap,
 			0x12, &hauppauge_hvr1400_dib7000_config);
-		if (port->dvb.frontend != NULL) {
+		if (fe0->dvb.frontend != NULL) {
 			struct dvb_frontend *fe;
 			struct xc2028_config cfg = {
 				.i2c_adap  = &dev->i2c_bus[1].i2c_adap,
@@ -455,7 +460,7 @@
 			};
 
 			fe = dvb_attach(xc2028_attach,
-					port->dvb.frontend, &cfg);
+					fe0->dvb.frontend, &cfg);
 			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
 				fe->ops.tuner_ops.set_config(fe, &ctl);
 		}
@@ -463,30 +468,30 @@
 	case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
 		i2c_bus = &dev->i2c_bus[port->nr - 1];
 
-		port->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 						&dvico_s5h1409_config,
 						&i2c_bus->i2c_adap);
-		if (port->dvb.frontend == NULL)
-			port->dvb.frontend = dvb_attach(s5h1411_attach,
+		if (fe0->dvb.frontend == NULL)
+			fe0->dvb.frontend = dvb_attach(s5h1411_attach,
 							&dvico_s5h1411_config,
 							&i2c_bus->i2c_adap);
-		if (port->dvb.frontend != NULL)
-			dvb_attach(xc5000_attach, port->dvb.frontend,
+		if (fe0->dvb.frontend != NULL)
+			dvb_attach(xc5000_attach, fe0->dvb.frontend,
 				&i2c_bus->i2c_adap,
 				&dvico_xc5000_tunerconfig, port);
 		break;
 	case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: {
 		i2c_bus = &dev->i2c_bus[port->nr - 1];
 
-		port->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 					       &dvico_fusionhdtv_xc3028,
 					       &i2c_bus->i2c_adap);
-		if (port->dvb.frontend != NULL) {
+		if (fe0->dvb.frontend != NULL) {
 			struct dvb_frontend      *fe;
 			struct xc2028_config	  cfg = {
 				.i2c_adap  = &i2c_bus->i2c_adap,
 				.i2c_addr  = 0x61,
-				.video_dev = port,
+				.video_dev = fe0,
 				.callback  = cx23885_tuner_callback,
 			};
 			static struct xc2028_ctrl ctl = {
@@ -495,7 +500,7 @@
 				.demod       = XC3028_FE_ZARLINK456,
 			};
 
-			fe = dvb_attach(xc2028_attach, port->dvb.frontend,
+			fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
 					&cfg);
 			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
 				fe->ops.tuner_ops.set_config(fe, &ctl);
@@ -505,15 +510,15 @@
 	case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
 		i2c_bus = &dev->i2c_bus[0];
 
-		port->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 			&dvico_fusionhdtv_xc3028,
 			&i2c_bus->i2c_adap);
-		if (port->dvb.frontend != NULL) {
+		if (fe0->dvb.frontend != NULL) {
 			struct dvb_frontend      *fe;
 			struct xc2028_config	  cfg = {
 				.i2c_adap  = &dev->i2c_bus[1].i2c_adap,
 				.i2c_addr  = 0x61,
-				.video_dev = port,
+				.video_dev = fe0,
 				.callback  = cx23885_tuner_callback,
 			};
 			static struct xc2028_ctrl ctl = {
@@ -522,7 +527,7 @@
 				.demod       = XC3028_FE_ZARLINK456,
 			};
 
-			fe = dvb_attach(xc2028_attach, port->dvb.frontend,
+			fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
 				&cfg);
 			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
 				fe->ops.tuner_ops.set_config(fe, &ctl);
@@ -533,7 +538,7 @@
 		       dev->name);
 		break;
 	}
-	if (NULL == port->dvb.frontend) {
+	if (NULL == fe0->dvb.frontend) {
 		printk("%s: frontend initialization failed\n", dev->name);
 		return -1;
 	}
@@ -541,19 +546,24 @@
 	/* Put the analog decoder in standby to keep it quiet */
 	cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL);
 
-	if (port->dvb.frontend->ops.analog_ops.standby)
-		port->dvb.frontend->ops.analog_ops.standby(port->dvb.frontend);
+ 	if (fe0->dvb.frontend->ops.analog_ops.standby)
+ 		fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
 
-	/* register everything */
-	return videobuf_dvb_register(&port->dvb, THIS_MODULE, port,
-				     &dev->pci->dev, adapter_nr);
+  	/* register everything */
+ 	return videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+  				     &dev->pci->dev);
 }
 
 int cx23885_dvb_register(struct cx23885_tsport *port)
 {
+ 	struct videobuf_dvb_frontend *fe0;
 	struct cx23885_dev *dev = port->dev;
 	int err;
 
+ 	fe0 = videobuf_dvb_get_frontend(&port->frontends, 0);
+ 	if (!fe0)
+ 		err = -EINVAL;
+
 	dprintk(1, "%s\n", __func__);
 	dprintk(1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
 		dev->board,
@@ -565,7 +575,7 @@
 
 	/* dvb stuff */
 	printk("%s: cx23885 based dvb card\n", dev->name);
-	videobuf_queue_sg_init(&port->dvb.dvbq, &dvb_qops, &dev->pci->dev, &port->slock,
+	videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops, &dev->pci->dev, &port->slock,
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
 			    sizeof(struct cx23885_buffer), port);
 	err = dvb_register(port);
@@ -577,9 +587,12 @@
 
 int cx23885_dvb_unregister(struct cx23885_tsport *port)
 {
+	struct videobuf_dvb_frontend *fe0;
+
+	fe0 = videobuf_dvb_get_frontend(&port->frontends, 0);
 	/* dvb */
-	if(port->dvb.frontend)
-		videobuf_dvb_unregister(&port->dvb);
+	if(fe0->dvb.frontend)
+		videobuf_dvb_unregister_bus(&port->frontends);
 
 	return 0;
 }
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx23885/cx23885.h v4l-dvb/linux/drivers/media/video/cx23885/cx23885.h
--- v4l-dvb.clean/linux/drivers/media/video/cx23885/cx23885.h	2008-08-31 23:36:15.609800510 +0100
+++ v4l-dvb/linux/drivers/media/video/cx23885/cx23885.h	2008-08-31 23:17:45.993801663 +0100
@@ -226,7 +226,7 @@
 	int                        nr;
 	int                        sram_chno;
 
-	struct videobuf_dvb        dvb;
+	struct videobuf_dvb_frontends frontends;
 
 	/* dma queues */
 	struct cx23885_dmaqueue    mpegq;
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-cards.c v4l-dvb/linux/drivers/media/video/cx88/cx88-cards.c
--- v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-cards.c	2008-08-31 23:36:15.657799257 +0100
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-cards.c	2008-08-31 23:17:45.997801761 +0100
@@ -1313,17 +1313,18 @@
 		.input          = {{
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
-			.gpio0  = 0x84bf,
+			.gpio0  = 0xe780,
 		},{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
-			.gpio0  = 0x84bf,
+			.gpio0  = 0xe780,
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
-			.gpio0  = 0x84bf,
+			.gpio0  = 0xe780,
 		}},
 		.mpeg           = CX88_MPEG_DVB,
+		.num_frontends	= 2,
 	},
 	[CX88_BOARD_NORWOOD_MICRO] = {
 		.name           = "Norwood Micro TV Tuner",
@@ -1375,6 +1376,9 @@
 		}},
 	},
 	[CX88_BOARD_HAUPPAUGE_HVR1300] = {
+		/*
+		 * gpio0 as reported by Mike Crash <mike AT mikecrash.com> on linux-dvb ML.
+		 */
 		.name		= "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
 		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
 		.radio_type	= UNSET,
@@ -1385,17 +1389,17 @@
 		.input		= {{
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
-			.gpio0	= 0xe780,
+			.gpio0	= 0xef88, /* Formerly e780 */
 			.audioroute = 1,
 		},{
 			.type	= CX88_VMUX_COMPOSITE1,
 			.vmux	= 1,
-			.gpio0	= 0xe780,
+			.gpio0	= 0xef88, /* Formerly e780 */
 			.audioroute = 2,
 		},{
 			.type	= CX88_VMUX_SVIDEO,
 			.vmux	= 2,
-			.gpio0	= 0xe780,
+			.gpio0	= 0xef88, /* Formerly e780 */
 			.audioroute = 2,
 		}},
 		/* fixme: Add radio support */
@@ -1425,6 +1429,72 @@
 			.gpio0  = 0x07fa,
 		}},
 	},
+        [CX88_BOARD_HAUPPAUGE_HVR4000] = {
+		.name           = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid",
+		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.tda9887_conf   = TDA9887_PRESENT,
+		/*
+		 * GPIO0 as found in WINTV2000 (dmb)
+		 *
+		 * Analogue     DVB-S/S2 DVB-T
+		 * Antenna      0xc4bf   0xc4bb
+		 * Composite    0xc4bf   0xc4bb
+		 * S-Video      0xc4bf   0xc4bb
+		 * Composite1   0xc4ff   0xc4fb
+		 * S-Video1     0xc4ff   0xc4fb
+		 *
+		 * BIT  VALUE	FUNCTION GP{x}_IO
+		 * 0	1	I:?
+		 * 1	1	I:?
+		 * 2	1	O:DVB-T DEMOD ENABLE LOW/ANALOG DEMOD ENABLE HIGH
+		 * 3	1	I:?
+		 * 4	1	I:?
+		 * 5	1	I:?
+		 * 6	0	O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION
+		 * 7	1	O:DVB-T DEMOD RESET LOW
+		 *
+		 * BIT  VALUE	FUNCTION GP{x}_OE
+		 * 8	0	I
+		 * 9	0	I
+		 * a	1	O
+		 * b	0	I
+		 * c	0	I
+		 * d	0	I
+		 * e	1	O
+		 * f	1	O
+		 */
+		.input          = {{
+			.type   = CX88_VMUX_TELEVISION,
+			.vmux   = 0,
+			.gpio0  = 0xc4bf,
+		},{
+			.type   = CX88_VMUX_COMPOSITE1,
+			.vmux   = 1,
+			.gpio0  = 0xc4bf,
+		},{
+			.type   = CX88_VMUX_SVIDEO,
+			.vmux   = 2,
+			.gpio0  = 0xc4bf,
+		}},
+		/* fixme: Add radio support */
+		.mpeg           = CX88_MPEG_DVB,
+		.num_frontends	= 2,
+	},
+	[CX88_BOARD_HAUPPAUGE_HVR4000LITE] = {
+		.name           = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2",
+		.tuner_type     = UNSET,
+		.radio_type     = UNSET,
+		.tuner_addr     = ADDR_UNSET,
+		.radio_addr     = ADDR_UNSET,
+		.input          = {{
+			.type   = CX88_VMUX_DVB,
+			.vmux   = 0,
+		}},
+		.mpeg           = CX88_MPEG_DVB,
+	},
 	[CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
 		.name           = "Pinnacle PCTV HD 800i",
 		.tuner_type     = TUNER_XC5000,
@@ -2015,6 +2085,26 @@
 		.subdevice = 0x0390,
 		.card      = CX88_BOARD_ADSTECH_PTV_390,
 	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x6900,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x6904,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x6902,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x6905,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+	},{
+		.subvendor = 0x0070,
+		.subdevice = 0x6906,
+		.card      = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+	},{
 		.subvendor = 0x11bd,
 		.subdevice = 0x0051,
 		.card      = CX88_BOARD_PINNACLE_PCTV_HD_800i,
@@ -2102,6 +2192,11 @@
 	case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */
 	case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
 	case 34519: /* WinTV-PCI-FM */
+	case 69009: /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */
+	case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */
+	case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */
+	case 69559: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */
+	case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */
 	case 90002: /* Nova-T-PCI (9002) */
 	case 92001: /* Nova-S-Plus (Video and IR) */
 	case 92002: /* Nova-S-Plus (Video and IR) */
@@ -2431,14 +2526,25 @@
 	case CX88_BOARD_HAUPPAUGE_HVR1300:
 		/* Bring the 702 demod up before i2c scanning/attach or devices are hidden */
 		/* We leave here with the 702 on the bus */
-		cx_write(MO_GP0_IO, 0x0000e780);
+
+		/*
+		 * "Also reset the IR receiver on GPIO[3]"
+		 *
+		 * Reported by Mike Crash <mike AT mikecrash.com> on linux-dvb ML.
+		 *
+		 */
+		cx_write(MO_GP0_IO, 0x0000ef88);
 		udelay(1000);
-		cx_clear(MO_GP0_IO, 0x00000080);
+		cx_clear(MO_GP0_IO, 0x00000088);
 		udelay(50);
-		cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
+		cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */
 		udelay(1000);
 		break;
-
+	case CX88_BOARD_HAUPPAUGE_HVR3000: /* ? */
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+		/* Init GPIO for DVB-S/S2/Analog */
+		cx_write(MO_GP0_IO, core->board.input[0].gpio0);
+		break;
 	case CX88_BOARD_PROLINK_PV_8000GT:
 		cx_write(MO_GP2_IO, 0xcf7);
 		mdelay(50);
@@ -2526,6 +2632,8 @@
 	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
 	case CX88_BOARD_HAUPPAUGE_HVR3000:
 	case CX88_BOARD_HAUPPAUGE_HVR1300:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
 		if (0 == core->i2c_rc)
 			hauppauge_eeprom(core, eeprom);
 		break;
@@ -2769,12 +2877,17 @@
 		cx88_card_list(core, pci);
 	}
 
+	memset(&core->board, 0, sizeof(core->board));
 	memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board));
 
-	info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+	if (!core->board.num_frontends)
+		core->board.num_frontends=1;
+
+	info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s], frontend(s): %d\n",
 		pci->subsystem_vendor, pci->subsystem_device, core->board.name,
 		core->boardnr, card[core->nr] == core->boardnr ?
-		"insmod option" : "autodetected");
+		"insmod option" : "autodetected",
+		core->board.num_frontends);
 
 	if (tuner[core->nr] != UNSET)
 		core->board.tuner_type = tuner[core->nr];
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-dvb.c v4l-dvb/linux/drivers/media/video/cx88/cx88-dvb.c
--- v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-dvb.c	2008-08-31 23:36:15.673799328 +0100
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-dvb.c	2008-08-31 23:17:46.009801007 +0100
@@ -49,6 +49,9 @@
 #include "tuner-simple.h"
 #include "tda9887.h"
 #include "s5h1411.h"
+#include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
+#include "cx24116.h"
 
 MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
@@ -112,13 +115,23 @@
 	struct cx8802_dev *dev= fe->dvb->priv;
 	struct cx8802_driver *drv = NULL;
 	int ret = 0;
+	int fe_id;
+
+	fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe);
+	if (!fe_id) {
+		printk(KERN_ERR "%s() No frontend found\n", __FUNCTION__);
+		return -EINVAL;
+	}
 
 	drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
 	if (drv) {
-		if (acquire)
+		if (acquire) {
+			dev->frontends.active_fe_id = fe_id;
 			ret = drv->request_acquire(drv);
-		else
+		} else {
 			ret = drv->request_release(drv);
+			dev->frontends.active_fe_id = 0;
+		}
 	}
 
 	return ret;
@@ -376,6 +389,28 @@
 	return 0;
 }
 
+static int cx24116_reset_device(struct dvb_frontend* fe)
+{
+	struct cx8802_dev *dev= fe->dvb->priv;
+	struct cx88_core *core = dev->core;
+
+	/* Put the cx24116 into reset */
+	cx_write(MO_SRST_IO, 0);
+	msleep(10);
+	
+	/* Take the cx24116 out of reset */
+	cx_write(MO_SRST_IO, 1);
+	msleep(10);
+
+	return 0;
+}
+
+static struct cx24116_config hauppauge_hvr4000_config = {
+	.demod_address = 0x05,
+	.set_ts_params = cx24123_set_ts_param,
+	.reset_device  = cx24116_reset_device,
+};
+
 static int cx88_pci_nano_callback(void *ptr, int command, int arg)
 {
 	struct cx88_core *core = ptr;
@@ -490,6 +525,7 @@
 static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
 {
 	struct dvb_frontend *fe;
+	struct videobuf_dvb_frontend *fe0 = NULL;
 	struct xc2028_ctrl ctl;
 	struct xc2028_config cfg = {
 		.i2c_adap  = &dev->core->i2c_adap,
@@ -498,7 +534,12 @@
 		.callback  = cx88_tuner_callback,
 	};
 
-	if (!dev->dvb.frontend) {
+	/* Get the first frontend */
+	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+	if (!fe0)
+		return -EINVAL;
+
+	if (!fe0->dvb.frontend) {
 		printk(KERN_ERR "%s/2: dvb frontend not attached. "
 				"Can't attach xc3028\n",
 		       dev->core->name);
@@ -512,10 +553,13 @@
 	 */
 	cx88_setup_xc3028(dev->core, &ctl);
 
-	fe = dvb_attach(xc2028_attach, dev->dvb.frontend, &cfg);
+	fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
 	if (!fe) {
 		printk(KERN_ERR "%s/2: xc3028 attach failed\n",
-		       dev->core->name);
+			dev->core->name);
+			dvb_frontend_detach(fe0->dvb.frontend);
+			dvb_unregister_frontend(fe0->dvb.frontend);
+			fe0->dvb.frontend = NULL;
 		return -EINVAL;
 	}
 
@@ -525,24 +569,26 @@
 	return 0;
 }
 
-static int dvb_register(struct cx8802_dev *dev)
-{
-	struct cx88_core *core = dev->core;
-
-	/* init struct videobuf_dvb */
-	dev->dvb.name = core->name;
-	dev->ts_gen_cntrl = 0x0c;
-
-	/* init frontend */
-	switch (core->boardnr) {
-	case CX88_BOARD_HAUPPAUGE_DVB_T1:
-		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &connexant_refboard_config,
-					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
-					0x61, &core->i2c_adap,
-					DVB_PLL_THOMSON_DTT759X))
+  static int dvb_register(struct cx8802_dev *dev)
+  {
+ 	struct cx88_core *core = dev->core;
+ 	struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+ 
+ 	/* Get the first frontend */
+ 	fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ 	if (!fe0)
+ 		return -EINVAL;
+  
+  	/* init frontend */
+  	switch (dev->core->boardnr) {
+  	case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ 		fe0->dvb.frontend = dvb_attach(cx22702_attach,
+  					       &connexant_refboard_config,
+  					       &core->i2c_adap);
+ 		if (fe0->dvb.frontend) {
+ 			dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x61,
+  				   &core->i2c_adap,
+  				   DVB_PLL_THOMSON_DTT759X);
 				goto frontend_detach;
 		}
 		break;
@@ -550,11 +596,11 @@
 	case CX88_BOARD_CONEXANT_DVB_T1:
 	case CX88_BOARD_KWORLD_DVB_T_CX22702:
 	case CX88_BOARD_WINFAST_DTV1000:
-		dev->dvb.frontend = dvb_attach(cx22702_attach,
+		fe0->dvb.frontend = dvb_attach(cx22702_attach,
 					       &connexant_refboard_config,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
 					0x60, &core->i2c_adap,
 					DVB_PLL_THOMSON_DTT7579))
 				goto frontend_detach;
@@ -564,33 +610,82 @@
 	case CX88_BOARD_HAUPPAUGE_HVR1100:
 	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
 	case CX88_BOARD_HAUPPAUGE_HVR1300:
-	case CX88_BOARD_HAUPPAUGE_HVR3000:
-		dev->dvb.frontend = dvb_attach(cx22702_attach,
+		fe0->dvb.frontend = dvb_attach(cx22702_attach,
 					       &hauppauge_hvr_config,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 				   &core->i2c_adap, 0x61,
 				   TUNER_PHILIPS_FMD1216ME_MK3))
 				goto frontend_detach;
 		}
 		break;
+  	case CX88_BOARD_HAUPPAUGE_HVR3000:
+ 		/* DVB-S init */
+ 
+ 		fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ 			       &hauppauge_novas_config,
+ 			       &core->i2c_adap);
+ 
+ 		if (fe0->dvb.frontend) {
+ 			/*
+ 			 * ISL6421_DCL turns off dynamic current protection
+ 			 * and enforces static protection.
+ 			 *
+ 			 * This is a requirement for 4x1 DiSEqC switches
+ 			 * and/or Rotors. 
+			 *
+ 			 * This is also how the Windows driver configures
+ 			 * the LNB voltage controller. (dmb).
+ 			 */
+ 			if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
+ 			&core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) {
+ 				dprintk( 1, "%s(): HVR3000 - DVB-S LNB Init: failed\n", __FUNCTION__);
+ 			}
+ 		} else {
+ 			dprintk( 1, "%s(): HVR3000 - DVB-S Init: failed\n", __FUNCTION__);
+ 		}
+ 
+ 		/* DVB-T init */
+ 
+ 		fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ 
+ 		if (fe1) {
+ 			fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ 				&hauppauge_hvr_config,
+ 				&core->i2c_adap);
+ 
+ 			if (fe1->dvb.frontend) {
+ 				fe1->dvb.frontend->id = 1;
+ 				if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend,
+ 						&core->i2c_adap, 0x61,
+ 						TUNER_PHILIPS_FMD1216ME_MK3)) {
+ 					dprintk( 1, "%s(): HVR3000 - DVB-T misc Init: failed\n", __FUNCTION__);
+ 				}
+ 			} else {
+ 				dprintk( 1, "%s(): HVR3000 - DVB-T Init: failed\n", __FUNCTION__);
+ 			}
+ 		} else {
+ 			dprintk( 1, "%s(): HVR3000 - DVB-T Init: can't find frontend 2.\n", __FUNCTION__);
+ 		}
+ 
+  		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
-		dev->dvb.frontend = dvb_attach(mt352_attach,
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
 					       &dvico_fusionhdtv,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
 					0x60, NULL, DVB_PLL_THOMSON_DTT7579))
 				goto frontend_detach;
 			break;
 		}
 		/* ZL10353 replaces MT352 on later cards */
-		dev->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 					       &dvico_fusionhdtv_plus_v1_1,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
 					0x60, NULL, DVB_PLL_THOMSON_DTT7579))
 				goto frontend_detach;
 		}
@@ -598,31 +693,31 @@
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
 		/* The tin box says DEE1601, but it seems to be DTT7579
 		 * compatible, with a slightly different MT352 AGC gain. */
-		dev->dvb.frontend = dvb_attach(mt352_attach,
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
 					       &dvico_fusionhdtv_dual,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
 					0x61, NULL, DVB_PLL_THOMSON_DTT7579))
 				goto frontend_detach;
 			break;
 		}
 		/* ZL10353 replaces MT352 on later cards */
-		dev->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 					       &dvico_fusionhdtv_plus_v1_1,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
 					0x61, NULL, DVB_PLL_THOMSON_DTT7579))
 				goto frontend_detach;
 		}
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
-		dev->dvb.frontend = dvb_attach(mt352_attach,
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
 					       &dvico_fusionhdtv,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
 					0x61, NULL, DVB_PLL_LG_Z201))
 				goto frontend_detach;
 		}
@@ -630,11 +725,11 @@
 	case CX88_BOARD_KWORLD_DVB_T:
 	case CX88_BOARD_DNTV_LIVE_DVB_T:
 	case CX88_BOARD_ADSTECH_DVB_T_PCI:
-		dev->dvb.frontend = dvb_attach(mt352_attach,
+		fe0->dvb.frontend = dvb_attach(mt352_attach,
 					       &dntv_live_dvbt_config,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(dvb_pll_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
 					0x61, NULL, DVB_PLL_UNKNOWN_1))
 				goto frontend_detach;
 		}
@@ -642,10 +737,10 @@
 	case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
 #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
 		/* MT352 is on a secondary I2C bus made from some GPIO lines */
-		dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config,
+		fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config,
 					       &dev->vp3054->adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x61,
 					TUNER_PHILIPS_FMD1216ME_MK3))
 				goto frontend_detach;
@@ -656,22 +751,22 @@
 #endif
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
-		dev->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 					       &dvico_fusionhdtv_hybrid,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 				   &core->i2c_adap, 0x61,
 				   TUNER_THOMSON_FE6600))
 				goto frontend_detach;
 		}
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
-		dev->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 					       &dvico_fusionhdtv_xc3028,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend == NULL)
-			dev->dvb.frontend = dvb_attach(mt352_attach,
+		if (fe0->dvb.frontend == NULL)
+			fe0->dvb.frontend = dvb_attach(mt352_attach,
 						&dvico_fusionhdtv_mt352_xc3028,
 						&core->i2c_adap);
 		/*
@@ -679,16 +774,16 @@
 		 * We must not permit gate_ctrl to be performed, or
 		 * the xc3028 cannot communicate on the bus.
 		 */
-		if (dev->dvb.frontend)
-			dev->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+		if (fe0->dvb.frontend)
+			fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
 		if (attach_xc3028(0x61, dev) < 0)
 			return -EINVAL;
 		break;
 	case CX88_BOARD_PCHDTV_HD3000:
-		dev->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
+		fe0->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x61,
 					TUNER_THOMSON_DTT761X))
 				goto frontend_detach;
@@ -705,11 +800,11 @@
 
 		/* Select RF connector callback */
 		fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
-		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &fusionhdtv_3_gold,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x61,
 					TUNER_MICROTUNE_4042FI5))
 				goto frontend_detach;
@@ -723,11 +818,11 @@
 		mdelay(100);
 		cx_set(MO_GP0_IO, 9);
 		mdelay(200);
-		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &fusionhdtv_3_gold,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x61,
 					TUNER_THOMSON_DTT761X))
 				goto frontend_detach;
@@ -741,15 +836,15 @@
 		mdelay(100);
 		cx_set(MO_GP0_IO, 1);
 		mdelay(200);
-		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &fusionhdtv_5_gold,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x61,
 					TUNER_LG_TDVS_H06XF))
 				goto frontend_detach;
-			if (!dvb_attach(tda9887_attach, dev->dvb.frontend,
+			if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
 				   &core->i2c_adap, 0x43))
 				goto frontend_detach;
 		}
@@ -762,25 +857,25 @@
 		mdelay(100);
 		cx_set(MO_GP0_IO, 1);
 		mdelay(200);
-		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+		fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &pchdtv_hd5500,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x61,
 					TUNER_LG_TDVS_H06XF))
 				goto frontend_detach;
-			if (!dvb_attach(tda9887_attach, dev->dvb.frontend,
+			if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
 				   &core->i2c_adap, 0x43))
 				goto frontend_detach;
 		}
 		break;
 	case CX88_BOARD_ATI_HDTVWONDER:
-		dev->dvb.frontend = dvb_attach(nxt200x_attach,
+		fe0->dvb.frontend = dvb_attach(nxt200x_attach,
 					       &ati_hdtvwonder,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
-			if (!dvb_attach(simple_tuner_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend != NULL) {
+			if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x61,
 					TUNER_PHILIPS_TUV1236D))
 				goto frontend_detach;
@@ -788,42 +883,105 @@
 		break;
 	case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
 	case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
-		dev->dvb.frontend = dvb_attach(cx24123_attach,
+		fe0->dvb.frontend = dvb_attach(cx24123_attach,
 					       &hauppauge_novas_config,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend) {
-			if (!dvb_attach(isl6421_attach, dev->dvb.frontend,
+		if (fe0->dvb.frontend) {
+			if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
 					&core->i2c_adap, 0x08, 0x00, 0x00))
 				goto frontend_detach;
 		}
 		break;
 	case CX88_BOARD_KWORLD_DVBS_100:
-		dev->dvb.frontend = dvb_attach(cx24123_attach,
+		fe0->dvb.frontend = dvb_attach(cx24123_attach,
 					       &kworld_dvbs_100_config,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend) {
-			core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
-			dev->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage;
+		if (fe0->dvb.frontend) {
+			core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage;
 		}
 		break;
 	case CX88_BOARD_GENIATECH_DVBS:
-		dev->dvb.frontend = dvb_attach(cx24123_attach,
+		fe0->dvb.frontend = dvb_attach(cx24123_attach,
 					       &geniatech_dvbs_config,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend) {
-			core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
-			dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
+		if (fe0->dvb.frontend) {
+			core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+			fe0->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
 		}
 		break;
+ 	case CX88_BOARD_HAUPPAUGE_HVR4000: 		/* DVB-S/S2 Enabled */
+ 		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ 					&hauppauge_hvr4000_config,
+ 					&core->i2c_adap);
+ 		if (fe0->dvb.frontend) {
+ 			/*
+ 			 * ISL6421_DCL turns off dynamic current protection
+ 			 * and enforces static protection.
+ 			 *
+ 			 * This is a requirement for 4x1 DiSEqC switches
+ 			 * and/or Rotors. 
+ 			 *
+ 			 * This is also how the Windows driver configures
+ 			 * the LNB voltage controller. (dmb).
+ 			 */
+ 			if(!dvb_attach(isl6421_attach, fe0->dvb.frontend,
+ 			&core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) {
+ 				dprintk( 1, "%s(): HVR4000 - DVB-S LNB Init: failed\n", __FUNCTION__);
+ 			}
+ 		} else {
+ 			dprintk( 1, "%s(): HVR4000 - DVB-S Init: failed\n", __FUNCTION__);
+ 		}
+ 
+ 		fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ 
+ 		/* DVB-T Enabled */
+ 		if (fe1) {
+ 			fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ 					       &hauppauge_hvr_config,
+ 					       &core->i2c_adap);
+ 			if (fe1->dvb.frontend) {
+ 				fe1->dvb.frontend->id = 1;
+ 				if(!dvb_attach(simple_tuner_attach, fe1->dvb.frontend,
+ 						&core->i2c_adap, 0x61,
+ 						TUNER_PHILIPS_FMD1216ME_MK3)) {
+ 					dprintk( 1, "%s(): HVR4000 - DVB-T misc Init: failed\n", __FUNCTION__);
+ 				}
+ 			} else {
+ 				dprintk( 1, "%s(): HVR4000 - DVB-T Init: failed\n", __FUNCTION__);
+ 			}
+ 		} else {
+ 			dprintk( 1, "%s(): HVR4000 - DVB-T Init: can't find frontend 2.\n", __FUNCTION__);
+ 		}
+ 		break;
+ 	case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ 		fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ 						&hauppauge_hvr4000_config,
+ 						&core->i2c_adap);
+ 		if (fe0->dvb.frontend) {
+ 			/*
+ 			 * ISL6421_DCL turns off dynamic current protection
+ 			 * and enforces static protection.
+ 			 *
+ 			 * This is a requirement for 4x1 DiSEqC switches
+ 			 * and/or Rotors. 
+ 			 *
+ 			 * This is also how the Windows driver configures
+ 			 * the LNB voltage controller. (dmb).
+ 			 */
+ 			dvb_attach(isl6421_attach, fe0->dvb.frontend,
+ 			&dev->core->i2c_adap, 0x08, ISL6421_DCL, 0x00);
+  		}
+  		break;
 	case CX88_BOARD_PINNACLE_PCTV_HD_800i:
-		dev->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 					       &pinnacle_pctv_hd_800i_config,
-					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
+				       &core->i2c_adap);
+		if (fe0->dvb.frontend != NULL) {
 			/* tuner_config.video_dev must point to
 			 * i2c_adap.algo_data
 			 */
-			if (!dvb_attach(xc5000_attach, dev->dvb.frontend,
+			if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
 					&core->i2c_adap,
 					&pinnacle_pctv_hd_800i_tuner_config,
 					core->i2c_adap.algo_data))
@@ -831,15 +989,15 @@
 		}
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
-		dev->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 						&dvico_hdtv5_pci_nano_config,
 						&core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
+		if (fe0->dvb.frontend != NULL) {
 			struct dvb_frontend *fe;
 			struct xc2028_config cfg = {
 				.i2c_adap  = &core->i2c_adap,
 				.i2c_addr  = 0x61,
-				.callback  = cx88_pci_nano_callback,
+			.callback  = cx88_pci_nano_callback,
 			};
 			static struct xc2028_ctrl ctl = {
 				.fname       = "xc3028-v27.fw",
@@ -848,13 +1006,13 @@
 			};
 
 			fe = dvb_attach(xc2028_attach,
-					dev->dvb.frontend, &cfg);
+					fe0->dvb.frontend, &cfg);
 			if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
 				fe->ops.tuner_ops.set_config(fe, &ctl);
 		}
 		break;
 	 case CX88_BOARD_PINNACLE_HYBRID_PCTV:
-		dev->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 					       &cx88_geniatech_x8000_mt,
 					       &core->i2c_adap);
 		if (attach_xc3028(0x61, dev) < 0)
@@ -863,28 +1021,28 @@
 	 case CX88_BOARD_GENIATECH_X8000_MT:
 		dev->ts_gen_cntrl = 0x00;
 
-		dev->dvb.frontend = dvb_attach(zl10353_attach,
+		fe0->dvb.frontend = dvb_attach(zl10353_attach,
 					       &cx88_geniatech_x8000_mt,
 					       &core->i2c_adap);
 		if (attach_xc3028(0x61, dev) < 0)
 			goto frontend_detach;
 		break;
 	 case CX88_BOARD_KWORLD_ATSC_120:
-		dev->dvb.frontend = dvb_attach(s5h1409_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1409_attach,
 					       &kworld_atsc_120_config,
 					       &core->i2c_adap);
 		if (attach_xc3028(0x61, dev) < 0)
 			goto frontend_detach;
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
-		dev->dvb.frontend = dvb_attach(s5h1411_attach,
+		fe0->dvb.frontend = dvb_attach(s5h1411_attach,
 					       &dvico_fusionhdtv7_config,
 					       &core->i2c_adap);
-		if (dev->dvb.frontend != NULL) {
+		if (fe0->dvb.frontend != NULL) {
 			/* tuner_config.video_dev must point to
 			 * i2c_adap.algo_data
 			 */
-			if (!dvb_attach(xc5000_attach, dev->dvb.frontend,
+			if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
 					&core->i2c_adap,
 					&dvico_fusionhdtv7_tuner_config,
 					core->i2c_adap.algo_data))
@@ -896,7 +1054,7 @@
 		       core->name);
 		break;
 	}
-	if (NULL == dev->dvb.frontend) {
+	if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
 		printk(KERN_ERR
 		       "%s/2: frontend initialization failed\n",
 		       core->name);
@@ -904,19 +1062,20 @@
 	}
 
 	/* Ensure all frontends negotiate bus access */
-	dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
-
+	fe0->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+ 	if (fe1)
+ 		fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
 	/* Put the analog decoder in standby to keep it quiet */
 	cx88_call_i2c_clients(core, TUNER_SET_STANDBY, NULL);
 
 	/* register everything */
-	return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev,
-				     &dev->pci->dev, adapter_nr);
+	return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+				     &dev->pci->dev);
 
 frontend_detach:
-	if (dev->dvb.frontend) {
-		dvb_frontend_detach(dev->dvb.frontend);
-		dev->dvb.frontend = NULL;
+	if (fe0->dvb.frontend) {
+		dvb_frontend_detach(fe0->dvb.frontend);
+		fe0->dvb.frontend = NULL;
 	}
 	return -EINVAL;
 }
@@ -940,6 +1099,64 @@
 		cx_clear(MO_GP0_IO, 0x00000004);
 		udelay(1000);
 		break;
+	case CX88_BOARD_HAUPPAUGE_HVR3000: /* ? */
+		if(core->dvbdev->frontends.active_fe_id == 1) {
+			/* DVB-S/S2 Enabled */
+
+			/* Toggle reset on cx22702 leaving i2c active */
+			cx_write(MO_GP0_IO, core->board.input[0].gpio0);
+			udelay(1000);
+			cx_clear(MO_GP0_IO, 0x00000080);
+			udelay(50);
+			cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */
+			cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */
+			udelay(1000);
+
+			cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */
+			core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
+		} else
+		if (core->dvbdev->frontends.active_fe_id == 2) {
+			/* DVB-T Enabled */
+
+			/* Put the cx24116/cx24123 into reset */
+			cx_write(MO_SRST_IO, 0);
+
+			/* cx22702 out of reset and enable it */
+			cx_set(MO_GP0_IO,   0x00000080);
+			cx_clear(MO_GP0_IO, 0x00000004);
+			core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
+			udelay(1000);
+		}
+		break;
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+		if(core->dvbdev->frontends.active_fe_id == 1) {
+			/* DVB-S/S2 Enabled */
+
+			/* Toggle reset on cx22702 leaving i2c active */
+			cx_write(MO_GP0_IO, (core->board.input[0].gpio0 & 0x0000ff00) | 0x00000080);
+			udelay(1000);
+			cx_clear(MO_GP0_IO, 0x00000080);
+			udelay(50);
+			cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */
+			cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */
+			udelay(1000);
+
+			cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */
+			core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
+		} else
+		if (core->dvbdev->frontends.active_fe_id == 2) {
+			/* DVB-T Enabled */
+
+			/* Put the cx24116/cx24123 into reset */
+			cx_write(MO_SRST_IO, 0);
+
+			/* cx22702 out of reset and enable it */
+			cx_set(MO_GP0_IO,   0x00000080);
+			cx_clear(MO_GP0_IO, 0x00000004);
+			core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
+			udelay(1000);
+		}
+		break;
 	default:
 		err = -ENODEV;
 	}
@@ -960,6 +1177,9 @@
 		 cx_set(MO_GP0_IO, 0x00000004);
 #endif
 		break;
+	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+		break;
 	default:
 		err = -ENODEV;
 	}
@@ -970,7 +1190,8 @@
 {
 	struct cx88_core *core = drv->core;
 	struct cx8802_dev *dev = drv->core->dvbdev;
-	int err;
+	int err,i;
+	struct videobuf_dvb_frontend *fe;
 
 	dprintk( 1, "%s\n", __func__);
 	dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
@@ -990,12 +1211,24 @@
 
 	/* dvb stuff */
 	printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name);
-	videobuf_queue_sg_init(&dev->dvb.dvbq, &dvb_qops,
+
+	dev->ts_gen_cntrl = 0x0c;
+
+	for (i = 1; i <= core->board.num_frontends; i++) {
+		fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
+		if (!fe) {
+			printk(KERN_ERR "%s() failed to get frontend(%d)\n", __FUNCTION__, i);
+			continue;
+		}
+		videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
 			    &dev->pci->dev, &dev->slock,
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
 			    V4L2_FIELD_TOP,
 			    sizeof(struct cx88_buffer),
 			    dev);
+		/* init struct videobuf_dvb */
+		fe->dvb.name = dev->core->name;
+	}
 	err = dvb_register(dev);
 	if (err != 0)
 		printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n",
@@ -1010,8 +1243,8 @@
 	struct cx8802_dev *dev = drv->core->dvbdev;
 
 	/* dvb */
-	if (dev->dvb.frontend)
-		videobuf_dvb_unregister(&dev->dvb);
+//	if (fe0->dvb.frontend)
+		videobuf_dvb_unregister_bus(&dev->frontends);
 
 	vp3054_i2c_remove(dev);
 
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx88/cx88.h v4l-dvb/linux/drivers/media/video/cx88/cx88.h
--- v4l-dvb.clean/linux/drivers/media/video/cx88/cx88.h	2008-08-31 23:36:15.721799751 +0100
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88.h	2008-08-31 23:17:46.017804206 +0100
@@ -222,6 +222,8 @@
 #define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
 #define CX88_BOARD_PROLINK_PV_8000GT       66
 #define CX88_BOARD_KWORLD_ATSC_120         67
+#define CX88_BOARD_HAUPPAUGE_HVR4000	   68
+#define CX88_BOARD_HAUPPAUGE_HVR4000LITE   69
 
 enum cx88_itype {
 	CX88_VMUX_COMPOSITE1 = 1,
@@ -254,6 +256,7 @@
 	struct cx88_input       radio;
 	enum cx88_board_type    mpeg;
 	unsigned int            audio_chip;
+	int			num_frontends;
 };
 
 struct cx88_subid {
@@ -351,6 +354,7 @@
 	struct cx8802_dev          *dvbdev;
 	enum cx88_board_type       active_type_id;
 	int			   active_ref;
+	int			   active_fe_id;
 };
 
 struct cx8800_dev;
@@ -502,7 +506,7 @@
 
 #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
 	/* for dvb only */
-	struct videobuf_dvb        dvb;
+	struct videobuf_dvb_frontends frontends;
 #endif
 
 #if defined(CONFIG_VIDEO_CX88_VP3054) || \
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-i2c.c v4l-dvb/linux/drivers/media/video/cx88/cx88-i2c.c
--- v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-i2c.c	2008-08-31 23:36:15.677800288 +0100
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-i2c.c	2008-08-31 23:17:46.013799848 +0100
@@ -116,18 +116,24 @@
 
 void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg)
 {
+	struct videobuf_dvb_frontend *fe0 = NULL;
+
 	if (0 != core->i2c_rc)
 		return;
 
 #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
-	if ( (core->dvbdev) && (core->dvbdev->dvb.frontend) ) {
-		if (core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl)
-			core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl(core->dvbdev->dvb.frontend, 1);
+	if (core->dvbdev) {
+		/* Get the first frontend and assume that all I2C is routed through it */
+		/* TODO: Get _THIS_FE_ then find the right i2c_gate_ctrl for it */
+		fe0 = videobuf_dvb_get_frontend(&core->dvbdev->frontends, 1);
+
+		if (fe0 && fe0->dvb.frontend && fe0->dvb.frontend->ops.i2c_gate_ctrl)
+			fe0->dvb.frontend->ops.i2c_gate_ctrl(fe0->dvb.frontend, 1);
 
 		i2c_clients_command(&core->i2c_adap, cmd, arg);
 
-		if (core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl)
-			core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl(core->dvbdev->dvb.frontend, 0);
+		if (fe0 && fe0->dvb.frontend && fe0->dvb.frontend->ops.i2c_gate_ctrl)
+			fe0->dvb.frontend->ops.i2c_gate_ctrl(fe0->dvb.frontend, 0);
 	} else
 #endif
 		i2c_clients_command(&core->i2c_adap, cmd, arg);
@@ -201,7 +207,32 @@
 
 	core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap);
 	if (0 == core->i2c_rc) {
+	        static u8 tuner_data[] =
+			{ 0x0b, 0xdc, 0x86, 0x52 };
+		static struct i2c_msg tuner_msg =
+			{ .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 };
+
 		dprintk(1, "i2c register ok\n");
+
+		switch( core->boardnr )
+		{
+		    case CX88_BOARD_HAUPPAUGE_HVR1300:
+		    case CX88_BOARD_HAUPPAUGE_HVR3000: /* ? */
+		    case CX88_BOARD_HAUPPAUGE_HVR4000:
+			/*
+	 		 * The tda9887 2-0043: tda988[5/6/7] found @ 0x43 (tuner')
+			 * is disabled after a cold boot on the HVR devices.
+			 *
+			 * We enable it here prior to probing for successful detection.
+			 *
+			 * This has been tested on the HVR1300 and HVR4000. (dmb).
+			 */
+			printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n",
+				core->name);
+			i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1);
+			break;
+		}
+
 		if (i2c_scan)
 			do_i2c_scan(core->name,&core->i2c_client);
 	} else
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-input.c v4l-dvb/linux/drivers/media/video/cx88/cx88-input.c
--- v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-input.c	2008-08-31 23:36:15.681822970 +0100
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-input.c	2008-08-31 23:17:46.013799848 +0100
@@ -237,6 +237,8 @@
 	case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
 	case CX88_BOARD_HAUPPAUGE_HVR1100:
 	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
 		ir_codes = ir_codes_hauppauge_new;
 		ir_type = IR_TYPE_RC5;
 		ir->sampling = 1;
@@ -409,7 +411,7 @@
 {
 	struct cx88_IR *ir = core->ir;
 	u32 samples, ircode;
-	int i;
+	int i, start, range, toggle, dev, code;
 
 	if (NULL == ir)
 		return;
@@ -478,12 +480,32 @@
 	case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
 	case CX88_BOARD_HAUPPAUGE_HVR1100:
 	case CX88_BOARD_HAUPPAUGE_HVR3000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000:
+	case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
 	case CX88_BOARD_PINNACLE_PCTV_HD_800i:
 		ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
 		ir_dprintk("biphase decoded: %x\n", ircode);
-		if ((ircode & 0xfffff000) != 0x3000)
+		/*
+		 * RC5 has an extension bit which adds a new range
+		 * of available codes, this is detected here. Also
+		 * hauppauge remotes (black/silver) always use
+		 * specific device ids. If we do not filter the
+		 * device ids then messages destined for devices
+		 * such as TVs (id=0) will get through. (dmb).
+		 */
+		/* split rc5 data block ... */
+		start = (ircode & 0x2000) >> 13;
+		range = (ircode & 0x1000) >> 12;
+		toggle= (ircode & 0x0800) >> 11;
+		dev   = (ircode & 0x07c0) >> 6;
+		code  = (ircode & 0x003f) | ((range << 6) ^ 0x0040);
+		if( start != 1)
+			/* no key pressed */
 			break;
-		ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode);
+		if ( dev != 0x1e && dev != 0x1f )
+			/* not a hauppauge remote */
+			break;
+		ir_input_keydown(ir->input, &ir->ir, code, ircode);
 		ir->release = jiffies + msecs_to_jiffies(120);
 		break;
 	}
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-mpeg.c v4l-dvb/linux/drivers/media/video/cx88/cx88-mpeg.c
--- v4l-dvb.clean/linux/drivers/media/video/cx88/cx88-mpeg.c	2008-08-31 23:36:15.689799469 +0100
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-mpeg.c	2008-08-31 23:17:46.017804206 +0100
@@ -808,7 +808,8 @@
 {
 	struct cx8802_dev *dev;
 	struct cx88_core  *core;
-	int err;
+	struct videobuf_dvb_frontend *demod;
+	int err, i;
 
 	/* general setup */
 	core = cx88_core_get(pci_dev);
@@ -821,6 +822,11 @@
 	if (!core->board.mpeg)
 		goto fail_core;
 
+	if (!core->board.num_frontends) {
+		printk(KERN_ERR "%s() .num_frontends should be non-zero, err = %d\n", __FUNCTION__, err);
+		goto fail_core;
+	}
+
 	err = -ENOMEM;
 	dev = kzalloc(sizeof(*dev),GFP_KERNEL);
 	if (NULL == dev)
@@ -835,6 +841,20 @@
 	INIT_LIST_HEAD(&dev->drvlist);
 	list_add_tail(&dev->devlist,&cx8802_devlist);
 
+	mutex_init(&dev->frontends.lock);
+	INIT_LIST_HEAD(&dev->frontends.frontend.felist);
+
+	printk(KERN_INFO "%s() allocating %d frontend(s)\n", __FUNCTION__, core->board.num_frontends);
+
+	for (i = 1; i <= core->board.num_frontends; i++) {
+		demod = videobuf_dvb_alloc_frontend(dev, &dev->frontends, i);
+		if(demod == NULL) {
+			printk(KERN_ERR "%s() failed to alloc\n", __FUNCTION__);
+			err = -ENOMEM;
+			goto fail_free;
+		}
+	}
+
 	/* Maintain a reference so cx88-video can query the 8802 device. */
 	core->dvbdev = dev;
 
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/cx88/Kconfig v4l-dvb/linux/drivers/media/video/cx88/Kconfig
--- v4l-dvb.clean/linux/drivers/media/video/cx88/Kconfig	2008-08-31 23:36:15.625800581 +0100
+++ v4l-dvb/linux/drivers/media/video/cx88/Kconfig	2008-08-31 23:17:45.993801663 +0100
@@ -33,8 +33,9 @@
 
 config VIDEO_CX88_BLACKBIRD
 	tristate "Blackbird MPEG encoder support (cx2388x + cx23416)"
-	depends on VIDEO_CX88
+	depends on VIDEO_CX88 && HOTPLUG
 	select VIDEO_CX2341X
+	select FW_LOADER
 	---help---
 	  This adds support for MPEG encoder cards based on the
 	  Blackbird reference design, using the Conexant 2388x
@@ -57,6 +58,7 @@
 	select DVB_CX24123 if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+	select DVB_CX24116 if !DVB_FE_CUSTOMISE
 	select DVB_S5H1411 if !DVB_FE_CUSTOMISE
 	---help---
 	  This adds support for DVB/ATSC cards based on the
diff -x .hg -dNur v4l-dvb.clean/linux/drivers/media/video/videobuf-dvb.c v4l-dvb/linux/drivers/media/video/videobuf-dvb.c
--- v4l-dvb.clean/linux/drivers/media/video/videobuf-dvb.c	2008-08-31 23:36:16.841800473 +0100
+++ v4l-dvb/linux/drivers/media/video/videobuf-dvb.c	2008-08-31 23:17:46.021801650 +0100
@@ -42,6 +42,8 @@
 #define dprintk(fmt, arg...)	if (debug)			\
 	printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg)
 
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
 /* ------------------------------------------------------------------ */
 
 static int videobuf_dvb_thread(void *data)
@@ -141,35 +143,79 @@
 
 /* ------------------------------------------------------------------ */
 
-int videobuf_dvb_register(struct videobuf_dvb *dvb,
-			  struct module *module,
-			  void *adapter_priv,
-			  struct device *device,
-			  short *adapter_nr)
-{
-	int result;
-
-	mutex_init(&dvb->lock);
-
-	/* register adapter */
-	result = dvb_register_adapter(&dvb->adapter, dvb->name, module, device,
-				      adapter_nr);
-	if (result < 0) {
-		printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
-		       dvb->name, result);
-		goto fail_adapter;
-	}
-	dvb->adapter.priv = adapter_priv;
-
-	/* register frontend */
-	result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
-	if (result < 0) {
-		printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
-		       dvb->name, result);
+ /* Register a single adapter and one or more frontends */
+ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
+ 		struct module *module,
+ 		void *adapter_priv,
+ 		struct device *device)
+ {
+ 	struct list_head *list, *q;
+ 	struct videobuf_dvb_frontend *fe;
+ 	int res = -EINVAL;
+ 
+ 	fe = videobuf_dvb_get_frontend(f, 1);
+ 	if (!fe) {
+ 		printk(KERN_WARNING "Unable to register the adapter which has no frontends\n");
+ 		goto err;
+ 	}
+ 
+ 	/* Bring up the adapter */
+ 	res = videobuf_dvb_register_adapter(f, module, adapter_priv, device, fe->dvb.name);
+ 	if (res < 0) {
+ 		printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
+ 		goto err;
+ 	}
+ 
+ 	/* Attach all of the frontends to the adapter */
+ 	mutex_lock(&f->lock);
+ 	list_for_each_safe(list, q, &f->frontend.felist) {
+ 		fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ 
+ 		res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb);
+ 		if (res < 0) {
+ 			printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n",
+ 				fe->dvb.name, res);
+ 		}
+ 	}
+ 	mutex_unlock(&f->lock);
+ 
+ err:
+ 	return res;
+ }
+ 
+ int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
+  			  struct module *module,
+  			  void *adapter_priv,
+ 			  struct device *device,
+ 			  char * adapter_name)
+  {
+  	int result;
+  
+ 	mutex_init(&fe->lock);
+  
+  	/* register adapter */
+ 	result = dvb_register_adapter(&fe->adapter, adapter_name, module, device, adapter_nr);
+  	if (result < 0) {
+  		printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+ 		       adapter_name, result);
+ 	}
+ 	fe->adapter.priv = adapter_priv;
+ 
+ 	return result;
+ }
+ 
+ int videobuf_dvb_register_frontend(struct dvb_adapter *adapter, struct videobuf_dvb *dvb)
+ {
+ 	int result;
+ 
+ 	result = dvb_register_frontend(adapter, dvb->frontend);
+  	if (result < 0) {
+  		printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+  		       dvb->name, result);
 		goto fail_frontend;
 	}
 
-	/* register demux stuff */
+/* register demux stuff */
 	dvb->demux.dmx.capabilities =
 		DMX_TS_FILTERING | DMX_SECTION_FILTERING |
 		DMX_MEMORY_BASED_FILTERING;
@@ -178,6 +224,7 @@
 	dvb->demux.feednum    = 256;
 	dvb->demux.start_feed = videobuf_dvb_start_feed;
 	dvb->demux.stop_feed  = videobuf_dvb_stop_feed;
+
 	result = dvb_dmx_init(&dvb->demux);
 	if (result < 0) {
 		printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
@@ -188,7 +235,8 @@
 	dvb->dmxdev.filternum    = 256;
 	dvb->dmxdev.demux        = &dvb->demux.dmx;
 	dvb->dmxdev.capabilities = 0;
-	result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+
+	result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
 	if (result < 0) {
 		printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
 		       dvb->name, result);
@@ -217,9 +265,8 @@
 		       dvb->name, result);
 		goto fail_fe_conn;
 	}
-
 	/* register network adapter */
-	dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+	dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
 	return 0;
 
 fail_fe_conn:
@@ -239,20 +286,98 @@
 	return result;
 }
 
-void videobuf_dvb_unregister(struct videobuf_dvb *dvb)
-{
-	dvb_net_release(&dvb->net);
-	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-	dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-	dvb_dmxdev_release(&dvb->dmxdev);
-	dvb_dmx_release(&dvb->demux);
-	dvb_unregister_frontend(dvb->frontend);
-	dvb_frontend_detach(dvb->frontend);
-	dvb_unregister_adapter(&dvb->adapter);
-}
-
-EXPORT_SYMBOL(videobuf_dvb_register);
-EXPORT_SYMBOL(videobuf_dvb_unregister);
+ void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f)
+ {
+ 	struct list_head *list, *q;
+ 	struct videobuf_dvb_frontend *fe;
+ 
+ 	mutex_lock(&f->lock);
+ 	list_for_each_safe(list, q, &f->frontend.felist) {
+ 		fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ 
+ 		dvb_net_release(&fe->dvb.net);
+ 		fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, &fe->dvb.fe_mem);
+ 		fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, &fe->dvb.fe_hw);
+ 		dvb_dmxdev_release(&fe->dvb.dmxdev);
+ 		dvb_dmx_release(&fe->dvb.demux);
+ 		dvb_unregister_frontend(fe->dvb.frontend);
+ 		dvb_frontend_detach(fe->dvb.frontend);
+ 
+ 		list_del(list);
+ 		kfree(fe);
+ 	}
+ 	mutex_unlock(&f->lock);
+ 
+ 	dvb_unregister_adapter(&f->adapter);
+ }
+ 
+ struct videobuf_dvb_frontend * videobuf_dvb_get_frontend(struct videobuf_dvb_frontends *f, int id)
+ {
+ 	struct list_head *list, *q;
+ 	struct videobuf_dvb_frontend *fe, *ret = NULL;
+ 
+ 	mutex_lock(&f->lock);
+ 
+ 	list_for_each_safe(list, q, &f->frontend.felist) {
+ 		fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ 		if (fe->id == id) {
+ 			ret = fe;
+ 			break;
+ 		}
+ 	}
+ 
+ 	mutex_unlock(&f->lock);
+ 
+ 	return ret;
+ }
+ 
+ int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f, struct dvb_frontend *p)
+ {
+ 	struct list_head *list, *q;
+ 	struct videobuf_dvb_frontend *fe = NULL;
+ 	int ret = 0;
+ 
+ 	mutex_lock(&f->lock);
+ 
+ 	list_for_each_safe(list, q, &f->frontend.felist) {
+ 		fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ 		if (fe->dvb.frontend == p) {
+ 			ret = fe->id;
+ 			break;
+ 		}
+ 	}
+ 
+ 	mutex_unlock(&f->lock);
+ 
+ 	return ret;
+ }
+ 
+ struct videobuf_dvb_frontend * videobuf_dvb_alloc_frontend(void *private, struct videobuf_dvb_frontends *f, int id)
+ {
+ 	struct videobuf_dvb_frontend *fe;
+ 
+ 	fe = kzalloc(sizeof(struct videobuf_dvb_frontend),GFP_KERNEL);
+ 	if (fe == NULL)
+ 		goto fail_alloc;
+ 
+ 	fe->dev = private;
+ 	fe->id = id;
+ 	mutex_init(&fe->dvb.lock);
+ 
+ 	mutex_lock(&f->lock);
+ 	list_add_tail(&fe->felist,&f->frontend.felist);
+ 	mutex_unlock(&f->lock);
+ 
+ fail_alloc:
+ 	return fe;
+ }
+ 
+ EXPORT_SYMBOL(videobuf_dvb_register_bus);
+ EXPORT_SYMBOL(videobuf_dvb_unregister_bus);
+ EXPORT_SYMBOL(videobuf_dvb_alloc_frontend);
+ EXPORT_SYMBOL(videobuf_dvb_get_frontend);
+ EXPORT_SYMBOL(videobuf_dvb_find_frontend);
+  
 
 /* ------------------------------------------------------------------ */
 /*
diff -x .hg -dNur v4l-dvb.clean/linux/include/linux/dvb/frontend_wrapper.h v4l-dvb/linux/include/linux/dvb/frontend_wrapper.h
--- v4l-dvb.clean/linux/include/linux/dvb/frontend_wrapper.h	1970-01-01 01:00:00.000000000 +0100
+++ v4l-dvb/linux/include/linux/dvb/frontend_wrapper.h	2008-08-31 23:17:46.021801650 +0100
@@ -0,0 +1,190 @@
+/*
+ * HVR-4000 DVB-S2 Extension Compatibility wrapper for <linux/dvb/frontend.h>
+ *
+ * Copyright (c) 2008 Darron Broad
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Darron Broad nor the names of any
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * Darron Broad BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Usage
+ * -----
+ *
+ *   #include "frontend_wrapper.h"
+ *
+ * Macros
+ * ------
+ *
+ *  DVBS_MODULATION(params)
+ *  DVBS_ROLLOFF(params)
+ *  DVBS_PILOT(params)
+ *
+ *	These macros map the extended parameter to the actual
+ *	parameter the dvb_frontend_parameters structure.
+ *	params must be a pointer to that structure. no type
+ *	casting is performed.
+ *
+ *  DVBS_GET_MODULATION(params)
+ *  DVBS_GET_ROLLOFF(params)
+ *  DVBS_GET_PILOT(params)
+ *
+ *	These macros get the extended parameter from the actual
+ *	parameter in the dvb_frontend_parameters structure.
+ *	params must be a pointer to that structure. type
+ *	casting is performed.
+ *
+ *  DVBS_SET_MODULATION(params,modulation)
+ *  DVBS_SET_ROLLOFF(params,rolloff)
+ *  DVBS_SET_PILOT(params,pilot)
+ *
+ *	These macros set the extended parameter in the actual
+ *	parameter in the dvb_frontend_parameters structure.
+ *	params must be a pointer to that structure. type
+ *	casting is performed.
+ *
+ * Methods
+ * -------
+ *
+ *  int err = dvb_get_info(int fd, struct dvb_frontend_info *)
+ *
+ *	This replaces err = ioctl(fefd, FE_GET_INFO, &fe_info)
+ *	and should be called after a device is opened. Internally
+ *	it will perform the ioctl it replaces and then initiate
+ *	DVB-S2 support on a device if it supports it.
+ *
+ *  int err = dvbs_test_modulation(int fd, int mod)
+ *
+ *	This determines if a given DVB-S/S2 modulation is
+ *	supported on the device. It will return 0 if supported
+ *	else -EOPNOTSUPP if not supported.
+ *
+ *  int err = dvb_get_frontend(int fd, struct dvb_frontend_parameters *)
+ *
+ *	This replaces err = ioctl(fefd, FE_GET_FRONTEND, &params)
+ *	This will fill-in the extension params (if an underlying
+ *	DVB-S device doesn't support them) to their default values for
+ *	DVB-S. This should always be used instead of the FE_GET_FRONTEND
+ *	ioctl call and if the device is DVB-S2 you can expect the
+ *	extension params to be set appropriately.
+ */
+#ifndef _FRONTEND_WRAPPER_H
+#define _FRONTEND_WRAPPER_H
+
+#define FE_CAN_NBC_QPSK (0x1000000)
+#define FE_CAN_NBC_8PSK (0x2000000)
+#define FE_CAN_FEC_3_5  (0x4000000)
+#define FE_CAN_FEC_9_10 (0x8000000)
+#define FE_CAN_DVBS  (FE_CAN_QPSK)
+#define FE_CAN_DVBS2 (FE_CAN_NBC_QPSK | FE_CAN_NBC_8PSK)
+
+#define FEC_3_5 (FEC_AUTO + 1)
+#define FEC_9_10 (FEC_3_5 + 1)
+
+#define NBC_QPSK (VSB_16 + 1)
+#define NBC_8PSK (NBC_QPSK + 1)
+	
+typedef enum fe_rolloff {
+	ROLLOFF_020,
+	ROLLOFF_025,
+	ROLLOFF_035
+} fe_rolloff_t;
+
+typedef enum fe_pilot {
+	PILOT_OFF,
+	PILOT_ON,
+	PILOT_AUTO
+} fe_pilot_t;
+
+#define DVBS_MODULATION(params)	((params)->u.ofdm.code_rate_LP)		/* u.qpsk.modulation */
+#define DVBS_ROLLOFF(params)	((params)->u.ofdm.constellation)	/* u.qpsk.rolloff */
+#define DVBS_PILOT(params)	((params)->u.ofdm.transmission_mode)	/* u.qpsk.pilot */
+
+#define DVBS_GET_MODULATION(params)	((fe_modulation_t)DVBS_MODULATION(params))
+#define DVBS_GET_ROLLOFF(params)	((fe_rolloff_t)DVBS_ROLLOFF(params))
+#define DVBS_GET_PILOT(params)		((fe_pilot_t)DVBS_PILOT(params))
+
+#define DVBS_SET_MODULATION(params,modulation)	(DVBS_MODULATION(params)=(fe_code_rate_t)(modulation))
+#define DVBS_SET_ROLLOFF(params,rolloff)	(DVBS_ROLLOFF(params)=(fe_modulation_t)(rolloff))
+#define DVBS_SET_PILOT(params,pilot)		(DVBS_PILOT(params)=(fe_transmit_mode_t)(pilot))
+
+#define FE_ENABLE_S2_EXTENSION _IO('o', 74)  /* int */
+
+#ifndef __KERNEL__
+int dvb_get_info(int fe_fd, struct dvb_frontend_info *fe_info)
+{
+	int ret;
+
+        ret = ioctl(fe_fd, FE_GET_INFO, fe_info);
+        if( ret != 0 )
+		return ret;
+
+	if( fe_info->caps & FE_CAN_DVBS2 )
+		ioctl(fe_fd, FE_ENABLE_S2_EXTENSION, 1);
+        return ret;
+}
+
+int dvbs_test_modulation(int fe_fd, int modulation)
+{
+	int ret;
+	struct dvb_frontend_info fe_info;
+
+        ret = dvb_get_info(fe_fd, &fe_info);
+        if( ret != 0 )
+		return ret;
+
+	if( modulation == QPSK     && ( ( fe_info.caps & FE_CAN_QPSK     ) ) )
+		return 0;
+	if( modulation == NBC_QPSK && ( ( fe_info.caps & FE_CAN_NBC_QPSK ) ) )
+		return 0;
+	if( modulation == NBC_8PSK && ( ( fe_info.caps & FE_CAN_NBC_8PSK ) ) )
+		return 0;
+	return -EOPNOTSUPP;
+}
+
+int dvb_get_frontend(int fe_fd, struct dvb_frontend_parameters *fe_params)
+{
+	int ret;
+	struct dvb_frontend_info fe_info;
+
+	ret = dvb_get_info(fe_fd, &fe_info);
+        if( ret != 0 )
+		return ret;
+
+        ret = ioctl(fe_fd, FE_GET_FRONTEND, fe_params);
+        if( ret == 0 ) {
+                if( fe_info.type == FE_QPSK && ( ( fe_info.caps & FE_CAN_DVBS2 ) == 0 ) ) {
+			DVBS_SET_MODULATION(fe_params,QPSK);
+			DVBS_SET_ROLLOFF(fe_params,ROLLOFF_035);
+			DVBS_SET_PILOT(fe_params,PILOT_OFF);
+                }
+        }
+        return ret;
+}
+#endif /* ! __KERNEL__ */
+
+#endif /* ! _FRONTEND_WRAPPER_H */
diff -x .hg -dNur v4l-dvb.clean/linux/include/media/videobuf-dvb.h v4l-dvb/linux/include/media/videobuf-dvb.h
--- v4l-dvb.clean/linux/include/media/videobuf-dvb.h	2008-08-31 23:36:17.241800577 +0100
+++ v4l-dvb/linux/include/media/videobuf-dvb.h	2008-08-31 23:17:46.021801650 +0100
@@ -24,12 +24,39 @@
 	struct dvb_net             net;
 };
 
-int videobuf_dvb_register(struct videobuf_dvb *dvb,
+struct videobuf_dvb_frontend {
+	void *dev;
+	struct list_head felist;
+	int id;
+	struct videobuf_dvb dvb;
+};
+
+struct videobuf_dvb_frontends {
+	struct mutex lock;
+	struct dvb_adapter adapter;
+	int active_fe_id; /* Indicates which frontend in the felist is in use */
+	struct videobuf_dvb_frontend frontend;
+};
+
+int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
+			  struct module *module,
+			  void *adapter_priv,
+			  struct device *device);
+void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f);
+
+int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *f,
 			  struct module *module,
 			  void *adapter_priv,
 			  struct device *device,
-			  short *adapter_nr);
-void videobuf_dvb_unregister(struct videobuf_dvb *dvb);
+			  char * adapter_name);
+
+int videobuf_dvb_register_frontend(struct dvb_adapter *adapter, struct videobuf_dvb *dvb);
+
+struct videobuf_dvb_frontend * videobuf_dvb_alloc_frontend(void *private, struct videobuf_dvb_frontends *f, int id);
+
+struct videobuf_dvb_frontend * videobuf_dvb_get_frontend(struct videobuf_dvb_frontends *f, int id);
+int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f, struct dvb_frontend *p);
+
 
 /*
  * Local variables:
