
From: Michael Hunold <hunold@linuxtv.org>

- [DVB] reafactor or add the following frontend drivers: at76c651, cx22700,
  ves1x93, ves1820, tda80xx, tda8083, cx22702, tda1004x, tda10021, cx24110,
  dvb_dummy_fe, l64781, mt312, mt352, stv0299, sp887x, stv0297, nxt6000,
  sp8870

- [DVB] remove dib3000mb from frontends, it's handled by the hw dependent
  driver now

Signed-off-by: Michael Hunold <hunold@linuxtv.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 /dev/null                                            |  657 --------
 25-akpm/drivers/media/dvb/frontends/at76c651.c       |  468 ++----
 25-akpm/drivers/media/dvb/frontends/at76c651.h       |   47 
 25-akpm/drivers/media/dvb/frontends/cx22700.c        |  445 +++++
 25-akpm/drivers/media/dvb/frontends/cx22700.h        |   41 
 25-akpm/drivers/media/dvb/frontends/cx22702.c        |  794 ++--------
 25-akpm/drivers/media/dvb/frontends/cx22702.h        |   46 
 25-akpm/drivers/media/dvb/frontends/cx24110.c        |  699 +++------
 25-akpm/drivers/media/dvb/frontends/cx24110.h        |   45 
 25-akpm/drivers/media/dvb/frontends/dib3000-common.h |  104 +
 25-akpm/drivers/media/dvb/frontends/dib3000.h        |   53 
 25-akpm/drivers/media/dvb/frontends/dib3000mb.c      |  848 +++++------
 25-akpm/drivers/media/dvb/frontends/dib3000mb_priv.h |  467 ++++++
 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.c   |  368 ++--
 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.h   |   32 
 25-akpm/drivers/media/dvb/frontends/l64781.c         |  607 ++++++++
 25-akpm/drivers/media/dvb/frontends/l64781.h         |   43 
 25-akpm/drivers/media/dvb/frontends/mt312.c          |  722 +++------
 25-akpm/drivers/media/dvb/frontends/mt312.h          |  156 --
 25-akpm/drivers/media/dvb/frontends/mt312_priv.h     |  162 ++
 25-akpm/drivers/media/dvb/frontends/mt352.c          |  810 ++--------
 25-akpm/drivers/media/dvb/frontends/mt352.h          |  156 --
 25-akpm/drivers/media/dvb/frontends/mt352_priv.h     |  127 +
 25-akpm/drivers/media/dvb/frontends/nxt6000.c        |  632 ++------
 25-akpm/drivers/media/dvb/frontends/nxt6000.h        |  303 ----
 25-akpm/drivers/media/dvb/frontends/nxt6000_priv.h   |  265 +++
 25-akpm/drivers/media/dvb/frontends/sp8870.c         |  613 ++++++++
 25-akpm/drivers/media/dvb/frontends/sp8870.h         |   45 
 25-akpm/drivers/media/dvb/frontends/sp887x.c         |  539 +++----
 25-akpm/drivers/media/dvb/frontends/sp887x.h         |   29 
 25-akpm/drivers/media/dvb/frontends/stv0297.c        |  774 ++++++++++
 25-akpm/drivers/media/dvb/frontends/stv0297.h        |   41 
 25-akpm/drivers/media/dvb/frontends/stv0299.c        | 1431 ++++---------------
 25-akpm/drivers/media/dvb/frontends/stv0299.h        |  104 +
 25-akpm/drivers/media/dvb/frontends/tda10021.c       |  483 ++++++
 25-akpm/drivers/media/dvb/frontends/tda10021.h       |   43 
 25-akpm/drivers/media/dvb/frontends/tda1004x.c       | 1173 +++++----------
 25-akpm/drivers/media/dvb/frontends/tda1004x.h       |   53 
 25-akpm/drivers/media/dvb/frontends/tda8083.c        |  490 ++++++
 25-akpm/drivers/media/dvb/frontends/tda8083.h        |   45 
 25-akpm/drivers/media/dvb/frontends/tda80xx.c        |  749 +++++++++
 25-akpm/drivers/media/dvb/frontends/tda80xx.h        |   51 
 25-akpm/drivers/media/dvb/frontends/ves1820.c        |  607 ++------
 25-akpm/drivers/media/dvb/frontends/ves1820.h        |   52 
 25-akpm/drivers/media/dvb/frontends/ves1x93.c        |  657 +++-----
 25-akpm/drivers/media/dvb/frontends/ves1x93.h        |   50 
 46 files changed, 9503 insertions(+), 7623 deletions(-)

diff -puN drivers/media/dvb/frontends/at76c651.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/at76c651.c
--- 25/drivers/media/dvb/frontends/at76c651.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.373065136 -0800
+++ 25-akpm/drivers/media/dvb/frontends/at76c651.c	2004-11-21 17:13:23.408059816 -0800
@@ -24,9 +24,6 @@
  * AT76C651
  * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf
  * http://www.atmel.com/atmel/acrobat/doc1320.pdf
- *
- * TUA6010XS
- * http://www.infineon.com/cgi/ecrm.dll/ecrm/scripts/public_download.jsp?oid=19512
  */
 
 #include <linux/init.h>
@@ -36,48 +33,34 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/bitops.h>
-
 #include "dvb_frontend.h"
+#include "at76c651.h"
 
-#define FRONTEND_NAME "dvbfe_at76c651"
 
-#define dprintk(args...) \
-	do { \
-		if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \
-	} while (0)
+struct at76c651_state {
 
-static int debug;
+	struct i2c_adapter* i2c;
 
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+	struct dvb_frontend_ops ops;
 
+	const struct at76c651_config* config;
 
-static struct dvb_frontend_info at76c651_info = {
-	.name = "Atmel AT76C651B with TUA6010XS",
-	.type = FE_QAM,
-	.frequency_min = 48250000,
-	.frequency_max = 863250000,
-	.frequency_stepsize = 62500,
-	/*.frequency_tolerance = */	/* FIXME: 12% of SR */
-	.symbol_rate_min = 0,		/* FIXME */
-	.symbol_rate_max = 9360000,	/* FIXME */
-	.symbol_rate_tolerance = 4000,
-	.notifier_delay = 0,
-	.caps = FE_CAN_INVERSION_AUTO |
-	    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-	    FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
-	    FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
-	    FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 |
-	    FE_CAN_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER
-};
+	struct dvb_frontend frontend;
 
-struct at76c651_state {
+	/* revision of the chip */
 	u8 revision;
+
+	/* last QAM value set */
 	u8 qam;
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
 };
 
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "at76c651: " args); \
+	} while (0)
+
+
 #if ! defined(__powerpc__)
 static __inline__ int __ilog2(unsigned long x)
 {
@@ -93,14 +76,14 @@ static __inline__ int __ilog2(unsigned l
 }
 #endif
 
-static int at76c651_writereg(struct i2c_adapter *i2c, u8 reg, u8 data)
+static int at76c651_writereg(struct at76c651_state* state, u8 reg, u8 data)
 {
 	int ret;
 	u8 buf[] = { reg, data };
 	struct i2c_msg msg =
-		{ .addr = 0x1a >> 1, .flags = 0, .buf = buf, .len = 2 };
+		{ .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
 
-	ret = i2c_transfer(i2c, &msg, 1);
+	ret = i2c_transfer(state->i2c, &msg, 1);
 
 	if (ret != 1)
 		dprintk("%s: writereg error "
@@ -112,16 +95,16 @@ static int at76c651_writereg(struct i2c_
 	return (ret != 1) ? -EREMOTEIO : 0;
 }
 
-static u8 at76c651_readreg(struct i2c_adapter *i2c, u8 reg)
+static u8 at76c651_readreg(struct at76c651_state* state, u8 reg)
 {
 	int ret;
 	u8 val;
 	struct i2c_msg msg[] = {
-		{ .addr = 0x1a >> 1, .flags = 0, .buf = &reg, .len = 1 },
-		{ .addr = 0x1a >> 1, .flags = I2C_M_RD, .buf = &val, .len = 1 }
+		{ .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },
+		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = &val, .len = 1 }
 	};
 
-	ret = i2c_transfer(i2c, msg, 2);
+	ret = i2c_transfer(state->i2c, msg, 2);
 
 	if (ret != 2)
 		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
@@ -129,112 +112,64 @@ static u8 at76c651_readreg(struct i2c_ad
 	return val;
 }
 
-static int at76c651_reset(struct i2c_adapter *i2c)
+static int at76c651_reset(struct at76c651_state* state)
 {
-	return at76c651_writereg(i2c, 0x07, 0x01);
+	return at76c651_writereg(state, 0x07, 0x01);
 }
 
-static void at76c651_disable_interrupts(struct i2c_adapter *i2c)
+static void at76c651_disable_interrupts(struct at76c651_state* state)
 {
-	at76c651_writereg(i2c, 0x0b, 0x00);
+	at76c651_writereg(state, 0x0b, 0x00);
 }
 
 static int at76c651_set_auto_config(struct at76c651_state *state)
 {
-	struct i2c_adapter *i2c = state->i2c;
-
 	/*
 	 * Autoconfig
 	 */
 
-	at76c651_writereg(i2c, 0x06, 0x01);
+	at76c651_writereg(state, 0x06, 0x01);
 
 	/*
 	 * Performance optimizations, should be done after autoconfig
 	 */
 
-	at76c651_writereg(i2c, 0x10, 0x06);
-	at76c651_writereg(i2c, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10);
-	at76c651_writereg(i2c, 0x15, 0x28);
-	at76c651_writereg(i2c, 0x20, 0x09);
-	at76c651_writereg(i2c, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90);
-	at76c651_writereg(i2c, 0x30, 0x90);
+	at76c651_writereg(state, 0x10, 0x06);
+	at76c651_writereg(state, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10);
+	at76c651_writereg(state, 0x15, 0x28);
+	at76c651_writereg(state, 0x20, 0x09);
+	at76c651_writereg(state, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90);
+	at76c651_writereg(state, 0x30, 0x90);
 	if (state->qam == 5)
-		at76c651_writereg(i2c, 0x35, 0x2A);
+		at76c651_writereg(state, 0x35, 0x2A);
 
 	/*
 	 * Initialize A/D-converter
 	 */
 
 	if (state->revision == 0x11) {
-		at76c651_writereg(i2c, 0x2E, 0x38);
-		at76c651_writereg(i2c, 0x2F, 0x13);
+		at76c651_writereg(state, 0x2E, 0x38);
+		at76c651_writereg(state, 0x2F, 0x13);
 }
 
-	at76c651_disable_interrupts(i2c);
+	at76c651_disable_interrupts(state);
 
 	/*
 	 * Restart operation
 	 */
 
-	at76c651_reset(i2c);
+	at76c651_reset(state);
 
 	return 0;
 }
 
-static void at76c651_set_bbfreq(struct i2c_adapter *i2c)
+static void at76c651_set_bbfreq(struct at76c651_state* state)
 {
-	at76c651_writereg(i2c, 0x04, 0x3f);
-	at76c651_writereg(i2c, 0x05, 0xee);
+	at76c651_writereg(state, 0x04, 0x3f);
+	at76c651_writereg(state, 0x05, 0xee);
 }
 
-static int at76c651_pll_write(struct i2c_adapter *i2c, u8 *buf, size_t len)
-{
-	int ret;
-	struct i2c_msg msg =
-	    { .addr = 0xc2 >> 1, .flags = 0, .buf = buf, .len = len };
-
-	at76c651_writereg(i2c, 0x0c, 0xc3);
-
-	ret = i2c_transfer(i2c, &msg, 1);
-
-	at76c651_writereg(i2c, 0x0c, 0xc2);
-
-	if (ret < 0)
-		return ret;
-	else if (ret != 1)
-		return -EREMOTEIO;
-
-	return 0;
-}
-
-static int tua6010_setfreq(struct i2c_adapter *i2c, u32 freq)
-{
-	u32 div;
-	u8 buf[4];
-	u8 vu, p2, p1, p0;
-
-	if ((freq < 50000000) || (freq > 900000000))
-		return -EINVAL;
-
-	div = (freq + 36125000) / 62500;
-
-	if (freq > 400000000)
-		vu = 1, p2 = 1, p1 = 0, p0 = 1;
-	else if (freq > 140000000)
-		vu = 0, p2 = 1, p1 = 1, p0 = 0;
-	else
-		vu = 0, p2 = 0, p1 = 1, p0 = 1;
-
-	buf[0] = (div >> 8) & 0x7f;
-	buf[1] = (div >> 0) & 0xff;
-	buf[2] = 0x8e;
-	buf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0;
-
-	return at76c651_pll_write(i2c, buf, 4);
-}
-
-static int at76c651_set_symbol_rate(struct i2c_adapter *i2c, u32 symbol_rate)
+static int at76c651_set_symbol_rate(struct at76c651_state* state, u32 symbol_rate)
 {
 	u8 exponent;
 	u32 mantissa;
@@ -251,9 +186,9 @@ static int at76c651_set_symbol_rate(stru
 	exponent = __ilog2((symbol_rate << 4) / 903125);
 	mantissa = ((symbol_rate / 3125) * (1 << (24 - exponent))) / 289;
 
-	at76c651_writereg(i2c, 0x00, mantissa >> 13);
-	at76c651_writereg(i2c, 0x01, mantissa >> 5);
-	at76c651_writereg(i2c, 0x02, (mantissa << 3) | exponent);
+	at76c651_writereg(state, 0x00, mantissa >> 13);
+	at76c651_writereg(state, 0x01, mantissa >> 5);
+	at76c651_writereg(state, 0x02, (mantissa << 3) | exponent);
 
 	return 0;
 }
@@ -292,13 +227,12 @@ static int at76c651_set_qam(struct at76c
 
 	}
 
-	return at76c651_writereg(state->i2c, 0x03, state->qam);
+	return at76c651_writereg(state, 0x03, state->qam);
 }
 
-static int at76c651_set_inversion(struct i2c_adapter *i2c,
-		       fe_spectral_inversion_t inversion)
+static int at76c651_set_inversion(struct at76c651_state* state, fe_spectral_inversion_t inversion)
 {
-	u8 feciqinv = at76c651_readreg(i2c, 0x60);
+	u8 feciqinv = at76c651_readreg(state, 0x60);
 
 	switch (inversion) {
 	case INVERSION_OFF:
@@ -318,59 +252,63 @@ static int at76c651_set_inversion(struct
 		return -EINVAL;
 	}
 
-	return at76c651_writereg(i2c, 0x60, feciqinv);
+	return at76c651_writereg(state, 0x60, feciqinv);
 }
 
-static int at76c651_set_parameters(struct at76c651_state *state,
+
+
+
+
+
+
+
+
+static int at76c651_set_parameters(struct dvb_frontend* fe,
 			struct dvb_frontend_parameters *p)
 {
-	struct i2c_adapter *i2c = state->i2c;
 	int ret;
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
 
-	if ((ret = tua6010_setfreq(i2c, p->frequency)))
-		return ret;
+	at76c651_writereg(state, 0x0c, 0xc3);
+	state->config->pll_set(fe, p);
+	at76c651_writereg(state, 0x0c, 0xc2);
 
-	if ((ret = at76c651_set_symbol_rate(i2c, p->u.qam.symbol_rate)))
+	if ((ret = at76c651_set_symbol_rate(state, p->u.qam.symbol_rate)))
 		return ret;
 
-	if ((ret = at76c651_set_inversion(i2c, p->inversion)))
+	if ((ret = at76c651_set_inversion(state, p->inversion)))
 		return ret;
 
 	return at76c651_set_auto_config(state);
 }
 
-static int at76c651_set_defaults(struct at76c651_state *state)
+static int at76c651_set_defaults(struct dvb_frontend* fe)
 {
-	struct i2c_adapter *i2c = state->i2c;
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
 
-	at76c651_set_symbol_rate(i2c, 6900000);
+	at76c651_set_symbol_rate(state, 6900000);
 	at76c651_set_qam(state, QAM_64);
-	at76c651_set_bbfreq(i2c);
+	at76c651_set_bbfreq(state);
 	at76c651_set_auto_config(state);
 
-	return 0;
+	if (state->config->pll_init) {
+		at76c651_writereg(state, 0x0c, 0xc3);
+		state->config->pll_init(fe);
+		at76c651_writereg(state, 0x0c, 0xc2);
 }
 
-static int at76c651_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
-{
-	struct at76c651_state *state = fe->data;
-	struct i2c_adapter *i2c = state->i2c;
-
-	switch (cmd) {
-	case FE_GET_INFO:
-		memcpy(arg, &at76c651_info, sizeof (struct dvb_frontend_info));
-		break;
+	return 0;
+}
 
-	case FE_READ_STATUS:
+static int at76c651_read_status(struct dvb_frontend* fe, fe_status_t* status)
 		{
-		fe_status_t *status = arg;
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
 			u8 sync;
 
 			/*
 			 * Bits: FEC, CAR, EQU, TIM, AGC2, AGC1, ADC, PLL (PLL=0) 
 			 */
-		sync = at76c651_readreg(i2c, 0x80);
-
+	sync = at76c651_readreg(state, 0x80);
 			*status = 0;
 
 			if (sync & (0x04 | 0x10))	/* AGC1 || TIM */
@@ -388,186 +326,138 @@ static int at76c651_ioctl(struct dvb_fro
 			if ((sync & 0xF0) == 0xF0)	/* TIM && EQU && CAR && FEC */
 				*status |= FE_HAS_LOCK;
 
-			break;
-
+	return 0;
 		}
 
-	case FE_READ_BER:
+static int at76c651_read_ber(struct dvb_frontend* fe, u32* ber)
 		{
-		u32 *ber = arg;
-		*ber = (at76c651_readreg(i2c, 0x81) & 0x0F) << 16;
-		*ber |= at76c651_readreg(i2c, 0x82) << 8;
-		*ber |= at76c651_readreg(i2c, 0x83);
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+	*ber = (at76c651_readreg(state, 0x81) & 0x0F) << 16;
+	*ber |= at76c651_readreg(state, 0x82) << 8;
+	*ber |= at76c651_readreg(state, 0x83);
 			*ber *= 10;
-			break;
-		}
 
-	case FE_READ_SIGNAL_STRENGTH:
-		{
-		u8 gain = ~at76c651_readreg(i2c, 0x91);
-			*(u16 *) arg = (gain << 8) | gain;
-			break;
+	return 0;
 		}
 
-	case FE_READ_SNR:
-		*(u16 *)arg = 0xFFFF -
-		    ((at76c651_readreg(i2c, 0x8F) << 8) |
-		     at76c651_readreg(i2c, 0x90));
-		break;
-
-	case FE_READ_UNCORRECTED_BLOCKS:
-		*(u32 *)arg = at76c651_readreg(i2c, 0x82);
-		break;
-
-	case FE_SET_FRONTEND:
-		return at76c651_set_parameters(state, arg);
-
-	case FE_GET_FRONTEND:
-		break;
-
-	case FE_SLEEP:
-		break;
-
-	case FE_INIT:
-		return at76c651_set_defaults(state);
-
-	case FE_GET_TUNE_SETTINGS:
-	{
-	        struct dvb_frontend_tune_settings *fesettings = arg;
-	        fesettings->min_delay_ms = 50;
-	        fesettings->step_size = 0;
-	        fesettings->max_drift = 0;
-		break;
-	}	    
+static int at76c651_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+		{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
 
-	default:
-		return -ENOIOCTLCMD;
-	}
+	u8 gain = ~at76c651_readreg(state, 0x91);
+	*strength = (gain << 8) | gain;
 
 	return 0;
 }
 
-static struct i2c_client client_template;
-
-static int attach_adapter(struct i2c_adapter *adapter)
+static int at76c651_read_snr(struct dvb_frontend* fe, u16* snr)
 {
-	struct at76c651_state *state;
-	struct i2c_client *client;
-	int ret;
-
-	if (at76c651_readreg(adapter, 0x0E) != 0x65)
-		return -ENODEV;
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
 
-	if (!(state = kmalloc(sizeof(struct at76c651_state), GFP_KERNEL)))
-		return -ENOMEM;
+	*snr = 0xFFFF -
+	    ((at76c651_readreg(state, 0x8F) << 8) |
+	     at76c651_readreg(state, 0x90));
 
-	state->i2c = adapter;
-	state->revision = at76c651_readreg(adapter, 0x0F) & 0xFE;
-
-	switch (state->revision) {
-	case 0x10:
-		at76c651_info.name[14] = 'A';
-		break;
-	case 0x11:
-		at76c651_info.name[14] = 'B';
-		break;
-	default:
-		kfree(state);
-		return -ENODEV;
-	}
-
-	if (!(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
-		kfree(state);
-		return -ENOMEM;
-	}
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = 0x1a >> 1;
-	i2c_set_clientdata(client, state);
-
-	ret = i2c_attach_client(client);
-	if (ret) {
-		kfree(state);
-		kfree(client);
-		return ret;
-	}
-
-	BUG_ON(!state->dvb);
-
-	ret = dvb_register_frontend(at76c651_ioctl, state->dvb, state,
-					&at76c651_info, THIS_MODULE);
-	if (ret) {
-		i2c_detach_client(client);
-		kfree(client);
-		kfree(state);
-		return ret;
-		}
 
 	return 0;
-	}
+}
 
-static int detach_client(struct i2c_client *client)
-{
-	struct at76c651_state *state = i2c_get_clientdata(client);
+static int at76c651_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+	{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
 
-	dvb_unregister_frontend(at76c651_ioctl, state->dvb);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
-	kfree(state);
+	*ucblocks = at76c651_readreg(state, 0x82);
 
 	return 0;
 }
 
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int at76c651_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *fesettings)
 {
-	struct at76c651_state *state = i2c_get_clientdata(client);
-
-	switch (cmd) {
-	case FE_REGISTER:
-		state->dvb = arg;
-		break;
-	case FE_UNREGISTER:
-		state->dvb = NULL;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-
+        fesettings->min_delay_ms = 50;
+        fesettings->step_size = 0;
+        fesettings->max_drift = 0;
 	return 0;
-}
+	}
 
-static struct i2c_driver driver = {
-	.owner		= THIS_MODULE,
-	.name		= FRONTEND_NAME,
-	.id		= I2C_DRIVERID_DVBFE_AT76C651,
-	.flags		= I2C_DF_NOTIFY,
-	.attach_adapter	= attach_adapter,
-	.detach_client	= detach_client,
-	.command	= command,
-};
+static void at76c651_release(struct dvb_frontend* fe)
+{
+	struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+		kfree(state);
+	}
 
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags		= I2C_CLIENT_ALLOW_USE,
-	.driver		= &driver,
-};
+static struct dvb_frontend_ops at76c651_ops;
 
-static int __init at76c651_init(void)
+struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
+				     struct i2c_adapter* i2c)
 {
-	return i2c_add_driver(&driver);
-}
+	struct at76c651_state* state = NULL;
 
-static void __exit at76c651_exit(void)
-{
-	if (i2c_del_driver(&driver))
-		printk(KERN_ERR "at76c651: driver deregistration failed.\n");
-}
+	/* allocate memory for the internal state */
+	state = (struct at76c651_state*) kmalloc(sizeof(struct at76c651_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->qam = 0;
+
+	/* check if the demod is there */
+	if (at76c651_readreg(state, 0x0e) != 0x65) goto error;
+
+	/* finalise state setup */
+	state->i2c = i2c;
+	state->revision = at76c651_readreg(state, 0x0f) & 0xfe;
+	memcpy(&state->ops, &at76c651_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops at76c651_ops = {
+
+	.info = {
+		.name = "Atmel AT76C651B DVB-C",
+		.type = FE_QAM,
+		.frequency_min = 48250000,
+		.frequency_max = 863250000,
+		.frequency_stepsize = 62500,
+		/*.frequency_tolerance = */	/* FIXME: 12% of SR */
+		.symbol_rate_min = 0,		/* FIXME */
+		.symbol_rate_max = 9360000,	/* FIXME */
+		.symbol_rate_tolerance = 4000,
+		.caps = FE_CAN_INVERSION_AUTO |
+		    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		    FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+		    FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+		    FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 |
+		    FE_CAN_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER
+	},
+
+	.release = at76c651_release,
+
+	.init = at76c651_set_defaults,
+
+	.set_frontend = at76c651_set_parameters,
+	.get_tune_settings = at76c651_get_tune_settings,
+
+	.read_status = at76c651_read_status,
+	.read_ber = at76c651_read_ber,
+	.read_signal_strength = at76c651_read_signal_strength,
+	.read_snr = at76c651_read_snr,
+	.read_ucblocks = at76c651_read_ucblocks,
+};
 
-module_init(at76c651_init);
-module_exit(at76c651_exit);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-MODULE_DESCRIPTION("at76c651/tua6010xs dvb-c frontend driver");
+MODULE_DESCRIPTION("Atmel AT76C651 DVB-C Demodulator Driver");
 MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
 MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(at76c651_attach);
diff -puN /dev/null drivers/media/dvb/frontends/at76c651.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/at76c651.h	2004-11-21 17:13:23.408059816 -0800
@@ -0,0 +1,47 @@
+/*
+ * at76c651.c
+ *
+ * Atmel DVB-C Frontend Driver (at76c651)
+ *
+ * Copyright (C) 2001 fnbrd <fnbrd@gmx.de>
+ *             & 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *             & 2003 Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * AT76C651
+ * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf
+ * http://www.atmel.com/atmel/acrobat/doc1320.pdf
+ */
+
+#ifndef AT76C651_H
+#define AT76C651_H
+
+#include <linux/dvb/frontend.h>
+
+struct at76c651_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
+					    struct i2c_adapter* i2c);
+
+#endif // AT76C651_H
diff -puN /dev/null drivers/media/dvb/frontends/cx22700.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/cx22700.c	2004-11-21 17:13:23.410059512 -0800
@@ -0,0 +1,445 @@
+#/*
+    Conexant cx22700 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	Holger Waechtler <holger@convergence.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "cx22700.h"
+
+
+struct cx22700_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct cx22700_config* config;
+
+	struct dvb_frontend frontend;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "cx22700: " args); \
+	} while (0)
+
+static u8 init_tab [] = {
+	0x04, 0x10,
+	0x05, 0x09,
+	0x06, 0x00,
+	0x08, 0x04,
+	0x09, 0x00,
+	0x0a, 0x01,
+	0x15, 0x40,
+	0x16, 0x10,
+	0x17, 0x87,
+	0x18, 0x17,
+	0x1a, 0x10,
+	0x25, 0x04,
+	0x2e, 0x00,
+	0x39, 0x00,
+	0x3a, 0x04,
+	0x45, 0x08,
+	0x46, 0x02,
+	0x47, 0x05,
+};
+
+
+static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	ret = i2c_transfer (state->i2c, &msg, 1);
+
+	if (ret != 1)
+		printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+			__FUNCTION__, reg, data, ret);
+
+	return (ret != 1) ? -1 : 0;
+}
+
+static int cx22700_readreg (struct cx22700_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+
+	if (ret != 2) return -EIO;
+
+	return b1[0];
+}
+
+static int cx22700_set_inversion (struct cx22700_state* state, int inversion)
+{
+	u8 val;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	switch (inversion) {
+	case INVERSION_AUTO:
+		return -EOPNOTSUPP;
+	case INVERSION_ON:
+		val = cx22700_readreg (state, 0x09);
+		return cx22700_writereg (state, 0x09, val | 0x01);
+	case INVERSION_OFF:
+		val = cx22700_readreg (state, 0x09);
+		return cx22700_writereg (state, 0x09, val & 0xfe);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_parameters *p)
+{
+	static const u8 qam_tab [4] = { 0, 1, 0, 2 };
+	static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 };
+	u8 val;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8)
+		return -EINVAL;
+
+	if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8)
+
+	if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
+		return -EINVAL;
+
+	if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+	    p->guard_interval > GUARD_INTERVAL_1_4)
+		return -EINVAL;
+
+	if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+	    p->transmission_mode != TRANSMISSION_MODE_8K)
+		return -EINVAL;
+
+	if (p->constellation != QPSK &&
+	    p->constellation != QAM_16 &&
+	    p->constellation != QAM_64)
+		return -EINVAL;
+
+	if (p->hierarchy_information < HIERARCHY_NONE ||
+	    p->hierarchy_information > HIERARCHY_4)
+		return -EINVAL;
+
+	if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ)
+		return -EINVAL;
+
+	if (p->bandwidth == BANDWIDTH_7_MHZ)
+		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10));
+	else
+		cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10));
+
+	val = qam_tab[p->constellation - QPSK];
+	val |= p->hierarchy_information - HIERARCHY_NONE;
+
+	cx22700_writereg (state, 0x04, val);
+
+	val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
+	val |= fec_tab[p->code_rate_LP - FEC_1_2];
+
+	cx22700_writereg (state, 0x05, val);
+
+	val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2;
+	val |= p->transmission_mode - TRANSMISSION_MODE_2K;
+
+	cx22700_writereg (state, 0x06, val);
+
+	cx22700_writereg (state, 0x08, 0x04 | 0x02);  /* use user tps parameters */
+	cx22700_writereg (state, 0x08, 0x04);         /* restart aquisition */
+
+	return 0;
+}
+
+static int cx22700_get_tps (struct cx22700_state* state, struct dvb_ofdm_parameters *p)
+{
+	static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 };
+	static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4,
+						    FEC_5_6, FEC_7_8 };
+	u8 val;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	if (!(cx22700_readreg(state, 0x07) & 0x20))  /*  tps valid? */
+		return -EAGAIN;
+
+	val = cx22700_readreg (state, 0x01);
+
+	if ((val & 0x7) > 4)
+		p->hierarchy_information = HIERARCHY_AUTO;
+	else
+		p->hierarchy_information = HIERARCHY_NONE + (val & 0x7);
+
+	if (((val >> 3) & 0x3) > 2)
+		p->constellation = QAM_AUTO;
+	else
+		p->constellation = qam_tab[(val >> 3) & 0x3];
+
+	val = cx22700_readreg (state, 0x02);
+
+	if (((val >> 3) & 0x07) > 4)
+		p->code_rate_HP = FEC_AUTO;
+	else
+		p->code_rate_HP = fec_tab[(val >> 3) & 0x07];
+
+	if ((val & 0x07) > 4)
+		p->code_rate_LP = FEC_AUTO;
+	else
+		p->code_rate_LP = fec_tab[val & 0x07];
+
+	val = cx22700_readreg (state, 0x03);
+
+	p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3);
+	p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1);
+
+	return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+static int cx22700_init (struct dvb_frontend* fe)
+
+{	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+	int i;
+
+	dprintk("cx22700_init: init chip\n");
+
+	cx22700_writereg (state, 0x00, 0x02);   /*  soft reset */
+	cx22700_writereg (state, 0x00, 0x00);
+
+	msleep(10);
+
+	for (i=0; i<sizeof(init_tab); i+=2)
+		cx22700_writereg (state, init_tab[i], init_tab[i+1]);
+
+	cx22700_writereg (state, 0x00, 0x01);
+
+	if (state->config->pll_init) {
+		cx22700_writereg (state, 0x0a, 0x00);  /* open i2c bus switch */
+		state->config->pll_init(fe);
+		cx22700_writereg (state, 0x0a, 0x01);  /* close i2c bus switch */
+	}
+
+	return 0;
+}
+
+static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+		   | (cx22700_readreg (state, 0x0e) << 1);
+	u8 sync = cx22700_readreg (state, 0x07);
+
+	*status = 0;
+
+	if (rs_ber < 0xff00)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x20)
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_SYNC;
+
+	if (*status == 0x0f)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	*ber = cx22700_readreg (state, 0x0c) & 0x7f;
+	cx22700_writereg (state, 0x0c, 0x00);
+
+	return 0;
+}
+
+static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+		   | (cx22700_readreg (state, 0x0e) << 1);
+	*signal_strength = ~rs_ber;
+
+	return 0;
+}
+
+static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+		   | (cx22700_readreg (state, 0x0e) << 1);
+	*snr = ~rs_ber;
+
+	return 0;
+}
+
+static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	*ucblocks = cx22700_readreg (state, 0x0f);
+	cx22700_writereg (state, 0x0f, 0x00);
+
+	return 0;
+}
+
+static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+	cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/
+	cx22700_writereg (state, 0x00, 0x00);
+
+	cx22700_writereg (state, 0x0a, 0x00);  /* open i2c bus switch */
+	state->config->pll_set(fe, p);
+	cx22700_writereg (state, 0x0a, 0x01);  /* close i2c bus switch */
+	cx22700_set_inversion (state, p->inversion);
+	cx22700_set_tps (state, &p->u.ofdm);
+	cx22700_writereg (state, 0x37, 0x01);  /* PAL loop filter off */
+	cx22700_writereg (state, 0x00, 0x01);  /* restart acquire */
+
+	return 0;
+}
+
+static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+	u8 reg09 = cx22700_readreg (state, 0x09);
+
+	p->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF;
+	return cx22700_get_tps (state, &p->u.ofdm);
+}
+
+static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 150;
+        fesettings->step_size = 166667;
+        fesettings->max_drift = 166667*2;
+        return 0;
+}
+
+static void cx22700_release(struct dvb_frontend* fe)
+{
+	struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops cx22700_ops;
+
+struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct cx22700_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct cx22700_state*) kmalloc(sizeof(struct cx22700_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &cx22700_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if (cx22700_readreg(state, 0x07) < 0) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops cx22700_ops = {
+
+	.info = {
+		.name 			= "Conexant CX22700 DVB-T",
+		.type 			= FE_OFDM,
+		.frequency_min 		= 470000000,
+		.frequency_max 		= 860000000,
+		.frequency_stepsize 	= 166667,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+		      FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+	              FE_CAN_RECOVER
+	},
+
+	.release = cx22700_release,
+
+	.init = cx22700_init,
+
+	.set_frontend = cx22700_set_frontend,
+	.get_frontend = cx22700_get_frontend,
+	.get_tune_settings = cx22700_get_tune_settings,
+
+	.read_status = cx22700_read_status,
+	.read_ber = cx22700_read_ber,
+	.read_signal_strength = cx22700_read_signal_strength,
+	.read_snr = cx22700_read_snr,
+	.read_ucblocks = cx22700_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx22700_attach);
diff -puN /dev/null drivers/media/dvb/frontends/cx22700.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/cx22700.h	2004-11-21 17:13:23.411059360 -0800
@@ -0,0 +1,41 @@
+/*
+    Conexant CX22700 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	Holger Waechtler <holger@convergence.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CX22700_H
+#define CX22700_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22700_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // CX22700_H
diff -puN drivers/media/dvb/frontends/cx22702.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/cx22702.c
--- 25/drivers/media/dvb/frontends/cx22702.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.375064832 -0800
+++ 25-akpm/drivers/media/dvb/frontends/cx22702.c	2004-11-21 17:13:23.416058600 -0800
@@ -1,8 +1,8 @@
 /*
-    Conexant 22702 DVB OFDM frontend driver
+    Conexant 22702 DVB OFDM demodulator driver
 
     based on:
-        Alps TDMB7 DVB OFDM frontend driver
+        Alps TDMB7 DVB OFDM demodulator driver
 
     Copyright (C) 2001-2002 Convergence Integrated Media GmbH
 	  Holger Waechtler <holger@convergence.de>
@@ -31,44 +31,28 @@
 #include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-
 #include "dvb_frontend.h"
+#include "cx22702.h"
 
-#define FRONTEND_NAME "dvbfe_cx22702"
 
-#define I2C_EEPROM_SLAVE_ADDR 0x50
+struct cx22702_state {
 
-#define PLLTYPE_DTT7592 1
-#define PLLTYPE_DTT7595 2
-#define PLLTYPE_DTT7579 3
+	struct i2c_adapter* i2c;
 
-static int debug = 0;
+	struct dvb_frontend_ops ops;
 
-#define dprintk	if (debug) printk
+	/* configuration settings */
+	const struct cx22702_config* config;
 
-static struct dvb_frontend_info cx22702_info = {
-	.name			= "CX22702 Demod Thomson 759x/7579 PLL",
-	.type			= FE_OFDM,
-	.frequency_min		= 177000000,
-	.frequency_max		= 858000000,
-	.frequency_stepsize	= 166666,
-	.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-	FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-	FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
-	FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
-	FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
-};
+	struct dvb_frontend frontend;
 
-struct cx22702_state {
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-	struct dvb_frontend_info cx22702_info;
-   	char pll_type;
-	int pll_addr;
-	int demod_addr;
+	/* previous uncorrected block counter */
 	u8 prevUCBlocks;
 };
 
+static int debug = 0;
+#define dprintk	if (debug) printk
+
 /* Register values to initialise the demod */
 static u8 init_tab [] = {
 	0x00, 0x00, /* Stop aquisition */
@@ -99,15 +83,13 @@ static u8 init_tab [] = {
 	0xfd, 0x00,
 };
 
-static struct i2c_client client_template;
-
-static int cx22702_writereg (struct i2c_adapter *i2c, int demod_addr, u8 reg, u8 data)
+static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data)
 {
 	int ret;
 	u8 buf [] = { reg, data };
-	struct i2c_msg msg = { .addr = demod_addr, .flags = 0, .buf = buf, .len = 2 };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
 
-	ret = i2c_transfer(i2c, &msg, 1);
+	ret = i2c_transfer(state->i2c, &msg, 1);
 
 	if (ret != 1)
 		printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
@@ -116,17 +98,17 @@ static int cx22702_writereg (struct i2c_
 	return (ret != 1) ? -1 : 0;
 }
 
-static u8 cx22702_readreg (struct i2c_adapter *i2c, int demod_addr, u8 reg)
+static u8 cx22702_readreg (struct cx22702_state* state, u8 reg)
 {
 	int ret;
 	u8 b0 [] = { reg };
 	u8 b1 [] = { 0 };
 
 	struct i2c_msg msg [] = {
-		{ .addr = demod_addr, .flags = 0, .buf = b0, .len = 1 },
-		{ .addr = demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+		{ .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(i2c, msg, 2);
+	ret = i2c_transfer(state->i2c, msg, 2);
 
 	if (ret != 2)
 		printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
@@ -134,232 +116,121 @@ static u8 cx22702_readreg (struct i2c_ad
 	return b1[0];
 }
 
-static int pll_readreg(struct i2c_adapter *i2c, int pll_addr, int demod_addr, u8 reg)
+static int cx22702_set_inversion (struct cx22702_state *state, int inversion)
 {
-	u8 b0 [] = { reg };
-	u8 b1 [] = { 0 };
+	u8 val;
 
-	struct i2c_msg msg [] = {
-		{ .addr = pll_addr, .flags = 0,        .buf = b0, .len = 1 },
-		{ .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }
-	};
-
-	cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) &0xfe); // Enable PLL bus
-	if (i2c_transfer(i2c, msg, 2) != 2) {
-		printk ("%s i2c pll request failed\n", __FUNCTION__);
-		cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus
-		return -ENODEV;
-	}
-	cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus
+	switch (inversion) {
 
-	return b1[0];
-}
+		case INVERSION_AUTO:
+			return -EOPNOTSUPP;
 
-static int pll_write (struct i2c_adapter *i2c, int pll_addr, int demod_addr, u8 data [4])
-{
-	int ret=0;
-	struct i2c_msg msg = { .addr = pll_addr, .flags = 0, .buf = data, .len = 4 };
+		case INVERSION_ON:
+			val = cx22702_readreg (state, 0x0C);
+			return cx22702_writereg (state, 0x0C, val | 0x01);
 
-	cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) &0xfe);  // Enable PLL bus
-	ret = i2c_transfer(i2c, &msg, 1);
-	cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus
+		case INVERSION_OFF:
+			val = cx22702_readreg (state, 0x0C);
+			return cx22702_writereg (state, 0x0C, val & 0xfe);
 
-	if (ret != 1)
-		printk("%s: i/o error (addr == 0x%02x, ret == %i)\n", __FUNCTION__, msg.addr, ret);
+		default:
+			return -EINVAL;
+
+	}
 
-	return (ret != 1) ? -1 : 0;
 }
 
-static int pll_dtt759x_set_tv_freq (struct i2c_adapter *i2c, struct cx22702_state *state, u32 freq, int bandwidth)
+/* Retrieve the demod settings */
+static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p)
 {
-	int ret;
-
-	u32 div = (freq + 36166667) / 166666;
-
-	/* dividerhigh, dividerlow, control, bandwidth switch tuner args */
-	unsigned char buf [4] = {
-		(div >> 8) & 0x7f,
-		div & 0xff,
-		0x84,
-		0x00
-	};
+	u8 val;
 
-	if(freq < 470000000) {
-		buf[3] = 0x02;
-	} else {
-		buf[3] = 0x08;
-	}
+	/* Make sure the TPS regs are valid */
+	if (!(cx22702_readreg(state, 0x0A) & 0x20))
+		return -EAGAIN;
 
-	if(bandwidth == BANDWIDTH_7_MHZ) {
-		buf[3] |= 0x10;
+	val = cx22702_readreg (state, 0x01);
+	switch( (val&0x18)>>3) {
+		case 0: p->constellation =   QPSK; break;
+		case 1: p->constellation = QAM_16; break;
+		case 2: p->constellation = QAM_64; break;
 	}
-
-	// Now compensate for the charge pump osc
-	if(freq <= 264000000) {
-		buf[2] = buf[2] | 0x30;
-	} else if (freq <= 735000000) {
-		buf[2] = buf[2] | 0x38;
-	} else if (freq <= 835000000) {
-		buf[2] = buf[2] | 0x70;
-	} else if (freq <= 896000000) {
-		buf[2] = buf[2] | 0x78;
+	switch( val&0x07 ) {
+		case 0: p->hierarchy_information = HIERARCHY_NONE; break;
+		case 1: p->hierarchy_information =    HIERARCHY_1; break;
+		case 2: p->hierarchy_information =    HIERARCHY_2; break;
+		case 3: p->hierarchy_information =    HIERARCHY_4; break;
 	}
 
-	dprintk ("%s: freq == %i, div == 0x%04x\n", __FUNCTION__, (int) freq, (int) div);
-
-	ret= pll_write (i2c, state->pll_addr, state->demod_addr, buf);
-	if(ret<0) {
-		dprintk ("%s: first pll_write failed\n",__FUNCTION__);
-		return ret;
-	}
 
-	/* Set the AGC during search */
-	buf[2]=(buf[2] & 0xc7) | 0x18;
-	buf[3]=0xa0;
-	ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf);
-	if(ret<0) {
-		dprintk ("%s: second pll_write failed\n",__FUNCTION__);
-		return ret;
+	val = cx22702_readreg (state, 0x02);
+	switch( (val&0x38)>>3 ) {
+		case 0: p->code_rate_HP = FEC_1_2; break;
+		case 1: p->code_rate_HP = FEC_2_3; break;
+		case 2: p->code_rate_HP = FEC_3_4; break;
+		case 3: p->code_rate_HP = FEC_5_6; break;
+		case 4: p->code_rate_HP = FEC_7_8; break;
 	}
-
-	/* Tuner needs a small amount of time */
-	msleep(100);
-
-	/* Set the AGC post-search */
-	buf[3]=0x20;
-	ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf);
-	if(ret<0) {
-		dprintk ("%s: third pll_write failed\n",__FUNCTION__);
-		return ret;
+	switch( val&0x07 ) {
+		case 0: p->code_rate_LP = FEC_1_2; break;
+		case 1: p->code_rate_LP = FEC_2_3; break;
+		case 2: p->code_rate_LP = FEC_3_4; break;
+		case 3: p->code_rate_LP = FEC_5_6; break;
+		case 4: p->code_rate_LP = FEC_7_8; break;
 	}
 
-	return ret;
 
+	val = cx22702_readreg (state, 0x03);
+	switch( (val&0x0c)>>2 ) {
+		case 0: p->guard_interval = GUARD_INTERVAL_1_32; break;
+		case 1: p->guard_interval = GUARD_INTERVAL_1_16; break;
+		case 2: p->guard_interval =  GUARD_INTERVAL_1_8; break;
+		case 3: p->guard_interval =  GUARD_INTERVAL_1_4; break;
 }
-
-static int pll_dtt7579_set_tv_freq (struct i2c_adapter *i2c, struct cx22702_state *state, u32 freq, int bandwidth)
-{
-	int ret;
-
-	u32 div = (freq + 36166667) / 166666;
-
-	/* dividerhigh, dividerlow */
-	unsigned char buf [4] = {
-		div >> 8,
-		div & 0xff,
-		0x00,
-		0x00
-	};
-
-	// FIXME: bandwidth setting unknown
-
-	// Now compensate for the charge pump osc
-	if(freq <= 506000000) {
-		buf[2] = 0xb4;
-	   	buf[3] = 0x02;
-	} else if (freq <= 735000000) {
-   		buf[2] = 0xbc;
-	   	buf[3] = 0x08;
-	} else if (freq <= 835000000) {
-      		buf[2] = 0xf4;
-	   	buf[3] = 0x08;
-	} else if (freq <= 896000000) {
-		buf[2] = 0xfc;
-	   	buf[3] = 0x08;
-	}
-
-	dprintk ("%s: freq == %i, div == 0x%04x\n", __FUNCTION__, (int) freq, (int) div);
-
-	ret= pll_write (i2c, state->pll_addr, state->demod_addr, buf);
-	if(ret<0) {
-		dprintk ("%s: first pll_write failed\n",__FUNCTION__);
-		return ret;
-	}
-
-	/* Set the AGC to search */
-	buf[2]=(buf[2] & 0xdc) | 0x9c;
-	buf[3]=0xa0;
-	ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf);
-	if(ret<0) {
-		dprintk ("%s: second pll_write failed\n",__FUNCTION__);
-		return ret;
+	switch( val&0x03 ) {
+		case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break;
+		case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break;
 	}
 
-	return ret;
-
+	return 0;
 }
 
-/* Reset the demod hardware and reset all of the configuration registers
-   to a default state. */
-static int cx22702_init (struct i2c_adapter *i2c, struct cx22702_state *state)
-{
-	int i;
-
-	cx22702_writereg (i2c, state->demod_addr, 0x00, 0x02);
 
-	msleep(10);
 
-	for (i=0; i<sizeof(init_tab); i+=2)
-		cx22702_writereg (i2c, state->demod_addr, init_tab[i], init_tab[i+1]);
 
-	return 0;
-}
 
-static int cx22702_set_inversion (struct i2c_adapter *i2c, struct cx22702_state *state, int inversion)
-{
-	u8 val;
 
-	switch (inversion) {
 
-		case INVERSION_AUTO:
-			return -EOPNOTSUPP;
 
-		case INVERSION_ON:
-			val = cx22702_readreg (i2c, state->demod_addr, 0x0C);
-			return cx22702_writereg (i2c, state->demod_addr, 0x0C, val | 0x01);
 
-		case INVERSION_OFF:
-			val = cx22702_readreg (i2c, state->demod_addr, 0x0C);
-			return cx22702_writereg (i2c, state->demod_addr, 0x0C, val & 0xfe);
 
-		default:
-			return -EINVAL;
 
-	}
 
-}
 
 /* Talk to the demod, set the FEC, GUARD, QAM settings etc */
-static int cx22702_set_tps (struct i2c_adapter *i2c, struct cx22702_state *state,
-			    struct dvb_frontend_parameters *p)
+static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 {
 	u8 val;
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 
 	/* set PLL */
-	switch(state->pll_type) {
-	case PLLTYPE_DTT7592:
-	case PLLTYPE_DTT7595:
-		pll_dtt759x_set_tv_freq (i2c, state, p->frequency, p->u.ofdm.bandwidth);
-		break;
-
-	case PLLTYPE_DTT7579:
-		pll_dtt7579_set_tv_freq (i2c, state, p->frequency, p->u.ofdm.bandwidth);
-		break;
-	}
+        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+	state->config->pll_set(fe, p);
+        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
 
 	/* set inversion */
-	cx22702_set_inversion (i2c, state, p->inversion);
+	cx22702_set_inversion (state, p->inversion);
 
 	/* set bandwidth */
 	switch(p->u.ofdm.bandwidth) {
 	case BANDWIDTH_6_MHZ:
-		cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xcf) | 0x20 );
+		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 );
 		break;
 	case BANDWIDTH_7_MHZ:
-		cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xcf) | 0x10 );
+		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 );
 		break;
 	case BANDWIDTH_8_MHZ:
-		cx22702_writereg(i2c, state->demod_addr, 0x0C, cx22702_readreg(i2c, state->demod_addr, 0x0C) &0xcf );
+		cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf );
 		break;
 	default:
 		dprintk ("%s: invalid bandwidth\n",__FUNCTION__);
@@ -378,12 +249,12 @@ static int cx22702_set_tps (struct i2c_a
 	   (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) {
 
 		/* TPS Source - use hardware driven values */
-		cx22702_writereg(i2c, state->demod_addr, 0x06, 0x10);
-		cx22702_writereg(i2c, state->demod_addr, 0x07, 0x9);
-		cx22702_writereg(i2c, state->demod_addr, 0x08, 0xC1);
-		cx22702_writereg(i2c, state->demod_addr, 0x0B, cx22702_readreg(i2c, state->demod_addr, 0x0B) & 0xfc );
-		cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xBF) | 0x40 );
-		cx22702_writereg(i2c, state->demod_addr, 0x00, 0x01); /* Begin aquisition */
+		cx22702_writereg(state, 0x06, 0x10);
+		cx22702_writereg(state, 0x07, 0x9);
+		cx22702_writereg(state, 0x08, 0xC1);
+		cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc );
+		cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
+		cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */
 		printk("%s: Autodetecting\n",__FUNCTION__);
 		return 0;
 	}
@@ -407,7 +278,7 @@ static int cx22702_set_tps (struct i2c_a
 			dprintk ("%s: invalid hierarchy\n",__FUNCTION__);
 			return -EINVAL;
 	}
-	cx22702_writereg (i2c, state->demod_addr, 0x06, val);
+	cx22702_writereg (state, 0x06, val);
 
 	val=0;
 	switch(p->u.ofdm.code_rate_HP) {
@@ -432,7 +303,7 @@ static int cx22702_set_tps (struct i2c_a
 			dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__);
 			return -EINVAL;
 	}
-	cx22702_writereg (i2c, state->demod_addr, 0x07, val);
+	cx22702_writereg (state, 0x07, val);
 
 	val=0;
 	switch(p->u.ofdm.guard_interval) {
@@ -451,93 +322,52 @@ static int cx22702_set_tps (struct i2c_a
 			dprintk ("%s: invalid transmission_mode\n",__FUNCTION__);
 			return -EINVAL;
 	}
-	cx22702_writereg (i2c, state->demod_addr, 0x08, val);
-	cx22702_writereg(i2c, state->demod_addr, 0x0B, (cx22702_readreg(i2c, state->demod_addr, 0x0B) & 0xfc) | 0x02 );
-	cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xBF) | 0x40 );
+	cx22702_writereg(state, 0x08, val);
+	cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 );
+	cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
 
 	/* Begin channel aquisition */
-	cx22702_writereg(i2c, state->demod_addr, 0x00, 0x01);
+	cx22702_writereg(state, 0x00, 0x01);
 
 	return 0;
 }
 
-/* Retrieve the demod settings */
-static int cx22702_get_tps (struct i2c_adapter *i2c, struct cx22702_state *state,
-			    struct dvb_ofdm_parameters *p)
-{
-	u8 val;
 
-	/* Make sure the TPS regs are valid */
-	if (!(cx22702_readreg(i2c, state->demod_addr, 0x0A) & 0x20))
-		return -EAGAIN;
+/* Reset the demod hardware and reset all of the configuration registers
+   to a default state. */
+static int cx22702_init (struct dvb_frontend* fe)
+{
+	int i;
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 
-	val = cx22702_readreg (i2c, state->demod_addr, 0x01);
-	switch( (val&0x18)>>3) {
-		case 0: p->constellation =   QPSK; break;
-		case 1: p->constellation = QAM_16; break;
-		case 2: p->constellation = QAM_64; break;
-	}
-	switch( val&0x07 ) {
-		case 0: p->hierarchy_information = HIERARCHY_NONE; break;
-		case 1: p->hierarchy_information =    HIERARCHY_1; break;
-		case 2: p->hierarchy_information =    HIERARCHY_2; break;
-		case 3: p->hierarchy_information =    HIERARCHY_4; break;
-	}
+	cx22702_writereg (state, 0x00, 0x02);
 
+	msleep(10);
 
-	val = cx22702_readreg (i2c, state->demod_addr, 0x02);
-	switch( (val&0x38)>>3 ) {
-		case 0: p->code_rate_HP = FEC_1_2; break;
-		case 1: p->code_rate_HP = FEC_2_3; break;
-		case 2: p->code_rate_HP = FEC_3_4; break;
-		case 3: p->code_rate_HP = FEC_5_6; break;
-		case 4: p->code_rate_HP = FEC_7_8; break;
-	}
-	switch( val&0x07 ) {
-		case 0: p->code_rate_LP = FEC_1_2; break;
-		case 1: p->code_rate_LP = FEC_2_3; break;
-		case 2: p->code_rate_LP = FEC_3_4; break;
-		case 3: p->code_rate_LP = FEC_5_6; break;
-		case 4: p->code_rate_LP = FEC_7_8; break;
-	}
+	for (i=0; i<sizeof(init_tab); i+=2)
+		cx22702_writereg (state, init_tab[i], init_tab[i+1]);
 
 
-	val = cx22702_readreg (i2c, state->demod_addr, 0x03);
-	switch( (val&0x0c)>>2 ) {
-		case 0: p->guard_interval = GUARD_INTERVAL_1_32; break;
-		case 1: p->guard_interval = GUARD_INTERVAL_1_16; break;
-		case 2: p->guard_interval =  GUARD_INTERVAL_1_8; break;
-		case 3: p->guard_interval =  GUARD_INTERVAL_1_4; break;
-	}
-	switch( val&0x03 ) {
-		case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break;
-		case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break;
+	/* init PLL */
+	if (state->config->pll_init) {
+	        cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+		state->config->pll_init(fe);
+		cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
 	}
 
 	return 0;
 }
 
-static int cx22702_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status)
 {
-	struct cx22702_state *state = fe->data;
-	struct i2c_adapter *i2c = state->i2c;
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 	u8 reg0A;
 	u8 reg23;
-	u8 ucblocks;
-
-	switch (cmd) {
-		case FE_GET_INFO:
-			memcpy (arg, &state->cx22702_info, sizeof(struct dvb_frontend_info));
-			break;
-
-		case FE_READ_STATUS:
-		{
-			fe_status_t *status = (fe_status_t *) arg;
 
 			*status = 0;
 
-			reg0A = cx22702_readreg (i2c, state->demod_addr, 0x0A);
-			reg23 = cx22702_readreg (i2c, state->demod_addr, 0x23);
+	reg0A = cx22702_readreg (state, 0x0A);
+	reg23 = cx22702_readreg (state, 0x23);
 
 			dprintk ("%s: status demod=0x%02x agc=0x%02x\n"
 				,__FUNCTION__,reg0A,reg23);
@@ -554,347 +384,149 @@ static int cx22702_ioctl (struct dvb_fro
 			if(reg23 < 0xf0)
 				*status |= FE_HAS_SIGNAL;
 
-			break;
-
-		}
-
-		case FE_READ_BER:
-			if(cx22702_readreg (i2c, state->demod_addr, 0xE4) & 0x02) {
-				/* Realtime statistics */
-				*((u32*) arg) = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7
-					| (cx22702_readreg (i2c, state->demod_addr, 0xDF)&0x7F);
-			} else {
-				/* Averagtine statistics */
-				*((u32*) arg) = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7
-					| cx22702_readreg (i2c, state->demod_addr, 0xDF);
+	return 0;
 			}
-			break;
 
-		case FE_READ_SIGNAL_STRENGTH:
+static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber)
 		{
-			u16 ss = cx22702_readreg (i2c, state->demod_addr, 0x23);
-			*((u16*) arg) = ss;
-			break;
-		}
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 
-		/* We don't have an register for this */
-		/* We'll take the inverse of the BER register */
-		case FE_READ_SNR:
-		{
-			u16 rs_ber=0;
-			if(cx22702_readreg (i2c, state->demod_addr, 0xE4) & 0x02) {
+	if(cx22702_readreg (state, 0xE4) & 0x02) {
 				/* Realtime statistics */
-				rs_ber = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7
-					| (cx22702_readreg (i2c, state->demod_addr, 0xDF)& 0x7F);
+		*ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+			| (cx22702_readreg (state, 0xDF)&0x7F);
 			} else {
-				/* Averagine statistics */
-				rs_ber = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 8
-					| cx22702_readreg (i2c, state->demod_addr, 0xDF);
-			}
-			*((u16*) arg) = ~rs_ber;
-			break;
+		/* Averagtine statistics */
+		*ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+			| cx22702_readreg (state, 0xDF);
 		}
 
-		case FE_READ_UNCORRECTED_BLOCKS:
-			/* RS Uncorrectable Packet Count then reset */
-			ucblocks = cx22702_readreg (i2c, state->demod_addr, 0xE3);
-			if (state->prevUCBlocks < ucblocks) *((u32*) arg) = (ucblocks - state->prevUCBlocks);
-			else *((u32*) arg) = state->prevUCBlocks - ucblocks;
-	   		state->prevUCBlocks = ucblocks;
-			break;
-
-		case FE_SET_FRONTEND:
-		{
-			struct dvb_frontend_parameters *p = arg;
-			int ret;
-
-			if((ret=cx22702_set_tps (i2c, state, p))<0) {
-				dprintk ("%s: set_tps failed ret=%d\n",__FUNCTION__,ret);
-				return ret;
-			}
-			break;
+	return 0;
 		}
 
-		case FE_GET_FRONTEND:
+static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
 		{
-			struct dvb_frontend_parameters *p = arg;
-			u8 reg0C = cx22702_readreg (i2c, state->demod_addr, 0x0C);
-
-			p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
-			return cx22702_get_tps (i2c, state, &p->u.ofdm);
-		}
-
-		case FE_INIT:
-			return cx22702_init (i2c, state);
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 
-		default:
-			return -EOPNOTSUPP;
-	};
+	*signal_strength = cx22702_readreg (state, 0x23);
 
 	return 0;
 }
 
-/* Validate the eeprom contents, make sure content look ok.
-   Get the eeprom data. */
-static int cx22702_validate_eeprom(struct i2c_adapter *i2c, int* minfreq, int* pll_type, int* pll_addr, int* demod_addr)
-{
-	u8 b0 [] = { 0 };
-	u8 b1 [128];
-	u32 model=0;
-	u8 tuner=0;
-	int i,j;
-
-	struct i2c_msg msg [] = {
-		{ .addr = I2C_EEPROM_SLAVE_ADDR, .flags = 0,        .buf = b0, .len = 1 },
-		{ .addr = I2C_EEPROM_SLAVE_ADDR, .flags = I2C_M_RD, .buf = b1, .len = 128 }
-	};
-
-	if (i2c_transfer(i2c, msg, 2) != 2) {
-		printk ("%s i2c eeprom request failed\n", __FUNCTION__);
-		return -ENODEV;
-	}
-
-	if(debug) {
-		dprintk ("i2c eeprom content:\n");
-		j=0;
-		for(i=0;i<128;i++) {
-			dprintk("%02x ",b1[i]);
-			if(j++==16) {
-				dprintk("\n");
-				j=0;
-			}
-		}
- 		dprintk("\n");
-	}
+static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 
-	if( (b1[8]!=0x84) || (b1[10]!=0x00) ) {
-		printk ("%s eeprom content is not valid\n", __FUNCTION__);
-		return -ENODEV;
-	}
-
-	/* Make sure we support the board model */
-	model = b1[0x1f] << 24 | b1[0x1e] << 16 | b1[0x1d] << 8 | b1[0x1c];
-	switch(model) {
-		case 90002:
-		case 90500:
-		case 90501:
-			dprintk ("%s: Model #%d\n",__FUNCTION__,model);
-			break;
-		default:
-			printk ("%s: Unknown model #%d not supported\n",__FUNCTION__,model);
-			return -ENODEV;
+	u16 rs_ber=0;
+	if(cx22702_readreg (state, 0xE4) & 0x02) {
+		/* Realtime statistics */
+		rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+			| (cx22702_readreg (state, 0xDF)& 0x7F);
+	} else {
+		/* Averagine statistics */
+		rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8
+			| cx22702_readreg (state, 0xDF);
 	}
+	*snr = ~rs_ber;
 
-	/* Make sure we support the tuner */
-	tuner = b1[0x2d];
-	switch(tuner) {
-		case 0x4B:
-			dprintk ("%s: Tuner Thomson DTT 7595\n",__FUNCTION__);
-			*minfreq = 177000000;
-			*pll_type = PLLTYPE_DTT7595;
-			break;
-		case 0x4C:
-			dprintk ("%s: Tuner Thomson DTT 7592\n",__FUNCTION__);
-			*minfreq = 474000000;
-			*pll_type = PLLTYPE_DTT7592;
-			break;
-		default:
-			printk ("%s: Unknown tuner 0x%02x not supported\n",__FUNCTION__,tuner);
-			return -ENODEV;
-	}
-	*pll_addr = 0x61;
-	*demod_addr = 0x43;
 	return 0;
 }
 
-
-/* Validate the demod, make sure we understand the hardware */
-static int cx22702_validate_demod(struct i2c_adapter *i2c, int demod_addr)
+static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
 {
-	u8 b0 [] = { 0x1f };
-	u8 b1 [] = { 0 };
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 
-	struct i2c_msg msg [] = {
-		{ .addr = demod_addr, .flags = 0,        .buf = b0, .len = 1 },
-		{ .addr = demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }
-	};
-
-	if (i2c_transfer(i2c, msg, 2) != 2) {
-		printk ("%s i2c demod request failed\n", __FUNCTION__);
-		return -ENODEV;
-	}
+	u8 _ucblocks;
 
-	if( (b1[0]!=0x3) ) {
-		printk ("%s i2c demod type 0x(%02x) not known\n", __FUNCTION__,b1[0]);
-		return -ENODEV;
-	}
+	/* RS Uncorrectable Packet Count then reset */
+	_ucblocks = cx22702_readreg (state, 0xE3);
+	if (state->prevUCBlocks < _ucblocks) *ucblocks = (_ucblocks - state->prevUCBlocks);
+	else *ucblocks = state->prevUCBlocks - _ucblocks;
+	state->prevUCBlocks = _ucblocks;
 
 	return 0;
 }
 
-/* Validate the tuner PLL, make sure we understand the hardware */
-static int cx22702_validate_pll(struct i2c_adapter *i2c, int pll_addr, int demod_addr)
+static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 {
-	int result=0;
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 
-	if( (result=pll_readreg(i2c,pll_addr,demod_addr,0xc2)) < 0)
-		return result;
+	u8 reg0C = cx22702_readreg (state, 0x0C);
 
-	if( (result >= 0) && (result&0x30) )
-		return 0;
-
-	return result;
+	p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
+	return cx22702_get_tps (state, &p->u.ofdm);
 }
 
-/* Check we can see the I2c clients */
-static int cx22702_attach_adapter(struct i2c_adapter *adapter)
+static void cx22702_release(struct dvb_frontend* fe)
 {
-	struct cx22702_state *state;
-	struct i2c_client *client;
-	int ret;
-	int minfreq;
-	int pll_type;
-	int pll_addr;
-	int demod_addr;
-
-	if (0 == (adapter->class & I2C_CLASS_TV_DIGITAL)) {
-		dprintk("Ignoring adapter 0x%x:%s (no digital tv card).\n",
-			adapter->id, adapter->name);
-		return 0;
-	}
-
-	dprintk("Trying to attach to adapter 0x%x:%s.\n",
-		adapter->id, adapter->name);
-
-	if (!strcmp(adapter->name, "Conexant DVB-T reference design")) {
-	   	printk("cx22702: Detected Conexant DVB-T card - PLL Thomson DTT7579\n");
-		pll_type = PLLTYPE_DTT7579;
-		pll_addr = 0x60;
-		demod_addr = 0x43;
-		minfreq = 177000000; // guess
-	} else {
-		// default to Hauppauge Nova-T for the moment
-	   	printk("cx22702: Detected Hauppauge Nova-T DVB-T - PLL Thomson DTT759x\n");
-		ret=cx22702_validate_eeprom(adapter, &minfreq, &pll_type, &pll_addr, &demod_addr);
-		if(ret < 0)
-			return ret;
-	}
-
-	ret=cx22702_validate_demod(adapter, demod_addr);
-	if(ret < 0)
-		return ret;
-
-	ret=cx22702_validate_pll(adapter, pll_addr, demod_addr);
-	if(ret < 0)
-		return ret;
-
-	if ( !(state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL)) )
-		return -ENOMEM;
-
-	memset(state, 0, sizeof(struct cx22702_state));
-	state->i2c = adapter;
-	memcpy(&state->cx22702_info, &cx22702_info, sizeof(struct dvb_frontend_info));
-	state->cx22702_info.frequency_min = minfreq;
-	state->pll_type = pll_type;
-	state->pll_addr = pll_addr;
-	state->demod_addr = demod_addr;
-
-	if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) {
-		kfree(state);
-		return -ENOMEM;
-	}
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = state->demod_addr;
-	i2c_set_clientdata(client, state);
-
-	if ((ret = i2c_attach_client(client))) {
-		printk("cx22702: attach failed %i\n", ret);
-		kfree(client);
+	struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
 		kfree(state);
-		return ret;
 	}
-	return 0;
-}
 
-static int cx22702_detach_client(struct i2c_client *client)
-{
-	struct cx22702_state *state = i2c_get_clientdata(client);
-
-	if (NULL != state->dvb) {
-		dvb_unregister_frontend (cx22702_ioctl, state->dvb);
-		state->dvb = NULL;
-	}
-	i2c_detach_client(client);
-	kfree(client);
-	return 0;
-}
+static struct dvb_frontend_ops cx22702_ops;
 
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+				    struct i2c_adapter* i2c)
 {
-	struct cx22702_state *state = i2c_get_clientdata(client);
-	int rc;
-
-	switch(cmd) {
-	case FE_REGISTER:
-		if (NULL != state->dvb)
-			break;
-		state->dvb = arg;
-		rc = dvb_register_frontend(cx22702_ioctl, state->dvb, state,
-					   &state->cx22702_info, THIS_MODULE);
-		if (0 != rc) {
-			printk("cx22702: dvb_register_frontend failed with rc=%d\n",rc);
-			state->dvb = NULL;
-			return rc;
-		}
-		break;
-	case FE_UNREGISTER:
-		if (NULL == state->dvb)
-			break;
-		dvb_unregister_frontend (cx22702_ioctl, state->dvb);
-		state->dvb = NULL;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	return 0;
-}
+	struct cx22702_state* state = NULL;
 
-static struct i2c_driver driver = {
-	.owner = THIS_MODULE,
-	.name  = FRONTEND_NAME,
-	.id = I2C_DRIVERID_DVBFE_CX22702,
-	.flags = I2C_DF_NOTIFY,
-	.attach_adapter = cx22702_attach_adapter,
-	.detach_client = cx22702_detach_client,
-	.command = command,
+	/* allocate memory for the internal state */
+	state = (struct cx22702_state*) kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &cx22702_ops, sizeof(struct dvb_frontend_ops));
+	state->prevUCBlocks = 0;
+
+	/* check if the demod is there */
+	if (cx22702_readreg(state, 0x1f) != 0x3) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops cx22702_ops = {
+
+	.info = {
+		.name			= "Conexant CX22702 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 177000000,
+		.frequency_max		= 858000000,
+		.frequency_stepsize	= 166666,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+		FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+		FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+		FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
+	},
+
+	.release = cx22702_release,
+
+	.init = cx22702_init,
+
+	.set_frontend = cx22702_set_tps,
+	.get_frontend = cx22702_get_frontend,
+
+	.read_status = cx22702_read_status,
+	.read_ber = cx22702_read_ber,
+	.read_signal_strength = cx22702_read_signal_strength,
+	.read_snr = cx22702_read_snr,
+	.read_ucblocks = cx22702_read_ucblocks,
 };
 
-static struct i2c_client client_template = {
-	.name = FRONTEND_NAME,
-	.flags = I2C_CLIENT_ALLOW_USE,
-	.driver = &driver,
-};
-
-static int __init init_cx22702 (void)
-{
-	return i2c_add_driver(&driver);
-}
-
-static void __exit exit_cx22702 (void)
-{
-	if (i2c_del_driver(&driver))
-		printk(KERN_ERR "cx22702: driver deregistration failed.\n");
-}
-
-module_init (init_cx22702);
-module_exit (exit_cx22702);
-
-MODULE_PARM(debug,"i");
+module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Enable verbose debug messages");
-MODULE_DESCRIPTION("CX22702 / Thomson DTT 759x / Thomson DTT 7579 PLL DVB Frontend driver");
+
+MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");
 MODULE_AUTHOR("Steven Toth");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(cx22702_attach);
diff -puN /dev/null drivers/media/dvb/frontends/cx22702.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/cx22702.h	2004-11-21 17:13:23.416058600 -0800
@@ -0,0 +1,46 @@
+/*
+    Conexant 22702 DVB OFDM demodulator driver
+
+    based on:
+        Alps TDMB7 DVB OFDM demodulator driver
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	  Holger Waechtler <holger@convergence.de>
+
+    Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CX22702_H
+#define CX22702_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22702_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // CX22702_H
diff -puN drivers/media/dvb/frontends/cx24110.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/cx24110.c
--- 25/drivers/media/dvb/frontends/cx24110.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.376064680 -0800
+++ 25-akpm/drivers/media/dvb/frontends/cx24110.c	2004-11-21 17:13:23.422057688 -0800
@@ -1,6 +1,5 @@
 /*
     cx24110 - Single Chip Satellite Channel Receiver driver module
-               used on the the Pinnacle PCTV Sat cards
 
     Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
     work
@@ -23,15 +22,6 @@
 
 */
 
-/* currently drives the Conexant cx24110 and cx24106 QPSK decoder chips,
-   connected via i2c to a Conexant Fusion 878 (this uses the standard
-   linux bttv driver). The tuner chip is supposed to be the Conexant
-   cx24108 digital satellite tuner, driven through the tuner interface
-   of the cx24110. SEC is also supplied by the cx24110.
-
-   Oct-2002: Migrate to API V3 (formerly known as NEWSTRUCT)
-*/
-
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -39,44 +29,30 @@
 #include <linux/init.h>
 
 #include "dvb_frontend.h"
+#include "cx24110.h"
 
-#define FRONTEND_NAME "dvbfe_cx24110"
 
-#define dprintk(args...) \
-	do { \
-		if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \
-	} while (0)
+struct cx24110_state {
 
-static int debug;
+	struct i2c_adapter* i2c;
 
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+	struct dvb_frontend_ops ops;
 
+	const struct cx24110_config* config;
 
-static struct dvb_frontend_info cx24110_info = {
-	.name = "Conexant CX24110 with CX24108 tuner, aka HM1221/HM1811",
-	.type = FE_QPSK,
-	.frequency_min = 950000,
-	.frequency_max = 2150000,
-	.frequency_stepsize = 1011,  /* kHz for QPSK frontends, can be reduced
-					to 253kHz on the cx24108 tuner */
-	.frequency_tolerance = 29500,
-	.symbol_rate_min = 1000000,
-	.symbol_rate_max = 45000000,
-/*      .symbol_rate_tolerance = ???,*/
-	.notifier_delay = 50,                /* 1/20 s */
-	.caps = FE_CAN_INVERSION_AUTO |
-		FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-		FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-		FE_CAN_QPSK | FE_CAN_RECOVER
-};
-/* fixme: are these values correct? especially ..._tolerance and caps */
+	struct dvb_frontend frontend;
 
-struct cx24110_state {
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
+	u32 lastber;
+	u32 lastbler;
+	u32 lastesn0;
 };
 
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "cx24110: " args); \
+	} while (0)
+
 static struct {u8 reg; u8 data;} cx24110_regdata[]=
                       /* Comments beginning with @ denote this value should
                          be the default */
@@ -140,15 +116,13 @@ static struct {u8 reg; u8 data;} cx24110
 	};
 
 
-static int cx24110_writereg (struct i2c_adapter *i2c, int reg, int data)
+static int cx24110_writereg (struct cx24110_state* state, int reg, int data)
 {
         u8 buf [] = { reg, data };
-	struct i2c_msg msg = { .addr = 0x55, .flags = 0, .buf = buf, .len = 2 };
-/* fixme (medium): HW allows any i2c address. 0x55 is the default, but the
-   cx24110 might show up at any address */
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
 	int err;
 
-        if ((err = i2c_transfer(i2c, &msg, 1)) != 1) {
+        if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
 		dprintk ("%s: writereg error (err == %i, reg == 0x%02x,"
 			 " data == 0x%02x)\n", __FUNCTION__, err, reg, data);
 		return -EREMOTEIO;
@@ -158,161 +132,46 @@ static int cx24110_writereg (struct i2c_
 }
 
 
-static u8 cx24110_readreg (struct i2c_adapter *i2c, u8 reg)
+static int cx24110_readreg (struct cx24110_state* state, u8 reg)
 {
 	int ret;
 	u8 b0 [] = { reg };
 	u8 b1 [] = { 0 };
-	struct i2c_msg msg [] = { { .addr = 0x55, .flags = 0, .buf = b0, .len = 1 },
-			   { .addr = 0x55, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
-/* fixme (medium): address might be different from 0x55 */
-	ret = i2c_transfer(i2c, msg, 2);
-
-	if (ret != 2)
-		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
-
-	return b1[0];
-}
-
-
-static int cx24108_write (struct i2c_adapter *i2c, u32 data)
-{
-/* tuner data is 21 bits long, must be left-aligned in data */
-/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */
-/* FIXME (low): add error handling, avoid infinite loops if HW fails... */
-
-dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data);
-
-        cx24110_writereg(i2c,0x6d,0x30); /* auto mode at 62kHz */
-        cx24110_writereg(i2c,0x70,0x15); /* auto mode 21 bits */
-        /* if the auto tuner writer is still busy, clear it out */
-        while (cx24110_readreg(i2c,0x6d)&0x80)
-		cx24110_writereg(i2c,0x72,0);
-        /* write the topmost 8 bits */
-        cx24110_writereg(i2c,0x72,(data>>24)&0xff);
-        /* wait for the send to be completed */
-        while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80)
-		;
-        /* send another 8 bytes */
-        cx24110_writereg(i2c,0x72,(data>>16)&0xff);
-        while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80)
-		;
-        /* and the topmost 5 bits of this byte */
-        cx24110_writereg(i2c,0x72,(data>>8)&0xff);
-        while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80)
-		;
-        /* now strobe the enable line once */
-        cx24110_writereg(i2c,0x6d,0x32);
-        cx24110_writereg(i2c,0x6d,0x30);
-
-        return 0;
-}
-
-
-static int cx24108_set_tv_freq (struct i2c_adapter *i2c, u32 freq)
-{
-/* fixme (low): error handling */
-        int i, a, n, pump;
-        u32 band, pll;
+	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);
 
-        static const u32 osci[]={ 950000,1019000,1075000,1178000,
-			         1296000,1432000,1576000,1718000,
-				 1856000,2036000,2150000};
-        static const u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,
-				      0x00101000,0x00102000,0x00104000,
-				      0x00108000,0x00110000,0x00120000,
-				      0x00140000};
-
-#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */
-        dprintk("cx24110 debug: cx24108_set_tv_freq, freq=%d\n",freq);
-
-        if (freq<950000)
-		freq=950000; /* kHz */
-        if (freq>2150000)
-		freq=2150000; /* satellite IF is 950..2150MHz */
-        /* decide which VCO to use for the input frequency */
-        for (i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++)
-		;
-        dprintk("cx24110 debug: select vco #%d (f=%d)\n",i,freq);
-        band=bandsel[i];
-        /* the gain values must be set by SetSymbolrate */
-        /* compute the pll divider needed, from Conexant data sheet,
-           resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4,
-           depending on the divider bit. It is set to /4 on the 2 lowest
-           bands  */
-        n=((i<=2?2:1)*freq*10L)/(XTAL/100);
-        a=n%32; n/=32;
-	if (a==0)
-		n--;
-        pump=(freq<(osci[i-1]+osci[i])/2);
-        pll=0xf8000000|
-            ((pump?1:2)<<(14+11))|
-            ((n&0x1ff)<<(5+11))|
-            ((a&0x1f)<<11);
-        /* everything is shifted left 11 bits to left-align the bits in the
-           32bit word. Output to the tuner goes MSB-aligned, after all */
-        dprintk("cx24110 debug: pump=%d, n=%d, a=%d\n",pump,n,a);
-        cx24108_write(i2c,band);
-        /* set vga and vca to their widest-band settings, as a precaution.
-           SetSymbolrate might not be called to set this up */
-        cx24108_write(i2c,0x500c0000);
-        cx24108_write(i2c,0x83f1f800);
-        cx24108_write(i2c,pll);
-        cx24110_writereg(i2c,0x56,0x7f);
-
-	msleep(10); /* wait a moment for the tuner pll to lock */
-
-	/* tuner pll lock can be monitored on GPIO pin 4 of cx24110 */
-        while (!(cx24110_readreg(i2c,0x66)&0x20)&&i<1000)
-		i++;
-        dprintk("cx24110 debug: GPIO IN=%2.2x(loop=%d)\n",
-                cx24110_readreg(i2c,0x66),i);
-        return 0;
-
-}
-
-
-static int cx24110_initfe(struct i2c_adapter *i2c)
-{
-/* fixme (low): error handling */
-        int i;
+	if (ret != 2) return ret;
 
-	dprintk("%s: init chip\n", __FUNCTION__);
-
-        for(i=0;i<sizeof(cx24110_regdata)/sizeof(cx24110_regdata[0]);i++) {
-		cx24110_writereg(i2c,cx24110_regdata[i].reg,cx24110_regdata[i].data);
-        };
-
-	return 0;
+	return b1[0];
 }
 
-
-static int cx24110_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion_t inversion)
+static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion)
 {
 /* fixme (low): error handling */
 
 	switch (inversion) {
 	case INVERSION_OFF:
-                cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)|0x1);
+                cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
                 /* AcqSpectrInvDis on. No idea why someone should want this */
-                cx24110_writereg(i2c,0x5,cx24110_readreg(i2c,0x5)&0xf7);
+                cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7);
                 /* Initial value 0 at start of acq */
-                cx24110_writereg(i2c,0x22,cx24110_readreg(i2c,0x22)&0xef);
+                cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef);
                 /* current value 0 */
                 /* The cx24110 manual tells us this reg is read-only.
                    But what the heck... set it ayways */
                 break;
 	case INVERSION_ON:
-                cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)|0x1);
+                cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
                 /* AcqSpectrInvDis on. No idea why someone should want this */
-                cx24110_writereg(i2c,0x5,cx24110_readreg(i2c,0x5)|0x08);
+                cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08);
                 /* Initial value 1 at start of acq */
-                cx24110_writereg(i2c,0x22,cx24110_readreg(i2c,0x22)|0x10);
+                cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10);
                 /* current value 1 */
                 break;
 	case INVERSION_AUTO:
-                cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)&0xfe);
+                cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe);
                 /* AcqSpectrInvDis off. Leave initial & current states as is */
                 break;
 	default:
@@ -323,7 +182,7 @@ static int cx24110_set_inversion (struct
 }
 
 
-static int cx24110_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec)
+static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec)
 {
 /* fixme (low): error handling */
 
@@ -339,27 +198,27 @@ static int cx24110_set_fec (struct i2c_a
                 fec=FEC_AUTO;
 
         if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
-		cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)&0xdf);
+		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf);
 		/* clear AcqVitDis bit */
-		cx24110_writereg(i2c,0x18,0xae);
+		cx24110_writereg(state,0x18,0xae);
 		/* allow all DVB standard code rates */
-		cx24110_writereg(i2c,0x05,(cx24110_readreg(i2c,0x05)&0xf0)|0x3);
+		cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3);
 		/* set nominal Viterbi rate 3/4 */
-		cx24110_writereg(i2c,0x22,(cx24110_readreg(i2c,0x22)&0xf0)|0x3);
+		cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3);
 		/* set current Viterbi rate 3/4 */
-		cx24110_writereg(i2c,0x1a,0x05); cx24110_writereg(i2c,0x1b,0x06);
+		cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06);
 		/* set the puncture registers for code rate 3/4 */
 		return 0;
         } else {
-		cx24110_writereg(i2c,0x37,cx24110_readreg(i2c,0x37)|0x20);
+		cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20);
 		/* set AcqVitDis bit */
 		if(rate[fec]>0) {
-			cx24110_writereg(i2c,0x05,(cx24110_readreg(i2c,0x05)&0xf0)|rate[fec]);
+			cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]);
 			/* set nominal Viterbi rate */
-			cx24110_writereg(i2c,0x22,(cx24110_readreg(i2c,0x22)&0xf0)|rate[fec]);
+			cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]);
 			/* set current Viterbi rate */
-			cx24110_writereg(i2c,0x1a,g1[fec]);
-			cx24110_writereg(i2c,0x1b,g2[fec]);
+			cx24110_writereg(state,0x1a,g1[fec]);
+			cx24110_writereg(state,0x1b,g2[fec]);
 			/* not sure if this is the right way: I always used AutoAcq mode */
            } else
 		   return -EOPNOTSUPP;
@@ -369,11 +228,11 @@ static int cx24110_set_fec (struct i2c_a
 }
 
 
-static fe_code_rate_t cx24110_get_fec (struct i2c_adapter *i2c)
+static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state)
 {
 	int i;
 
-	i=cx24110_readreg(i2c,0x22)&0x0f;
+	i=cx24110_readreg(state,0x22)&0x0f;
 	if(!(i&0x08)) {
 		return FEC_1_2 + i - 1;
 	} else {
@@ -386,16 +245,13 @@ static fe_code_rate_t cx24110_get_fec (s
 }
 
 
-static int cx24110_set_symbolrate (struct i2c_adapter *i2c, u32 srate)
+static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
 {
 /* fixme (low): add error handling */
         u32 ratio;
         u32 tmp, fclk, BDRI;
 
         static const u32 bands[]={5000000UL,15000000UL,90999000UL/2};
-        static const u32 vca[]={0x80f03800,0x81f0f800,0x83f1f800};
-        static const u32 vga[]={0x5f8fc000,0x580f0000,0x500c0000};
-        static const u8  filtune[]={0xa2,0xcc,0x66};
         int i;
 
 dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate);
@@ -409,22 +265,22 @@ dprintk("cx24110 debug: entering %s(%d)\
         /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz,
            and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult,
            R06[3:0] PLLphaseDetGain */
-        tmp=cx24110_readreg(i2c,0x07)&0xfc;
+        tmp=cx24110_readreg(state,0x07)&0xfc;
         if(srate<90999000UL/4) { /* sample rate 45MHz*/
-		cx24110_writereg(i2c,0x07,tmp);
-		cx24110_writereg(i2c,0x06,0x78);
+		cx24110_writereg(state,0x07,tmp);
+		cx24110_writereg(state,0x06,0x78);
 		fclk=90999000UL/2;
         } else if(srate<60666000UL/2) { /* sample rate 60MHz */
-		cx24110_writereg(i2c,0x07,tmp|0x1);
-		cx24110_writereg(i2c,0x06,0xa5);
+		cx24110_writereg(state,0x07,tmp|0x1);
+		cx24110_writereg(state,0x06,0xa5);
 		fclk=60666000UL;
         } else if(srate<80888000UL/2) { /* sample rate 80MHz */
-		cx24110_writereg(i2c,0x07,tmp|0x2);
-		cx24110_writereg(i2c,0x06,0x87);
+		cx24110_writereg(state,0x07,tmp|0x2);
+		cx24110_writereg(state,0x06,0x87);
 		fclk=80888000UL;
         } else { /* sample rate 90MHz */
-		cx24110_writereg(i2c,0x07,tmp|0x3);
-		cx24110_writereg(i2c,0x06,0x78);
+		cx24110_writereg(state,0x07,tmp|0x3);
+		cx24110_writereg(state,0x06,0x78);
 		fclk=90999000UL;
         };
         dprintk("cx24110 debug: fclk %d Hz\n",fclk);
@@ -453,64 +309,123 @@ dprintk("cx24110 debug: entering %s(%d)\
         dprintk("fclk = %d\n", fclk);
         dprintk("ratio= %08x\n", ratio);
 
-        cx24110_writereg(i2c, 0x1, (ratio>>16)&0xff);
-        cx24110_writereg(i2c, 0x2, (ratio>>8)&0xff);
-        cx24110_writereg(i2c, 0x3, (ratio)&0xff);
-
-        /* please see the cx24108 data sheet, this controls tuner gain
-           and bandwidth settings depending on the symbol rate */
-        cx24108_write(i2c,vga[i]);
-        cx24108_write(i2c,vca[i]); /* gain is set on tuner chip */
-        cx24110_writereg(i2c,0x56,filtune[i]); /* bw is contolled by filtune voltage */
+        cx24110_writereg(state, 0x1, (ratio>>16)&0xff);
+        cx24110_writereg(state, 0x2, (ratio>>8)&0xff);
+        cx24110_writereg(state, 0x3, (ratio)&0xff);
 
         return 0;
 
 }
 
 
-static int cx24110_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage)
+
+
+
+
+
+
+
+
+
+
+int cx24110_pll_write (struct dvb_frontend* fe, u32 data)
 {
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+/* tuner data is 21 bits long, must be left-aligned in data */
+/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */
+/* FIXME (low): add error handling, avoid infinite loops if HW fails... */
+
+	dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data);
+
+        cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */
+        cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */
+
+        /* if the auto tuner writer is still busy, clear it out */
+        while (cx24110_readreg(state,0x6d)&0x80)
+		cx24110_writereg(state,0x72,0);
+
+        /* write the topmost 8 bits */
+        cx24110_writereg(state,0x72,(data>>24)&0xff);
+
+        /* wait for the send to be completed */
+        while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+		;
+
+        /* send another 8 bytes */
+        cx24110_writereg(state,0x72,(data>>16)&0xff);
+        while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+		;
+
+        /* and the topmost 5 bits of this byte */
+        cx24110_writereg(state,0x72,(data>>8)&0xff);
+        while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+		;
+
+        /* now strobe the enable line once */
+        cx24110_writereg(state,0x6d,0x32);
+        cx24110_writereg(state,0x6d,0x30);
+
+        return 0;
+}
+
+
+
+static int cx24110_initfe(struct dvb_frontend* fe)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+/* fixme (low): error handling */
+        int i;
+
+	dprintk("%s: init chip\n", __FUNCTION__);
+
+        for(i=0;i<sizeof(cx24110_regdata)/sizeof(cx24110_regdata[0]);i++) {
+		cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data);
+        };
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	return 0;
+}
+
+
+static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
 	switch (voltage) {
 	case SEC_VOLTAGE_13:
-		return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0xc0);
+		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0);
 	case SEC_VOLTAGE_18:
-		return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0x40);
+		return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40);
 	default:
 		return -EINVAL;
 	};
 }
 
-static void cx24110_send_diseqc_msg(struct i2c_adapter *i2c,
+static int cx24110_send_diseqc_msg(struct dvb_frontend* fe,
 				    struct dvb_diseqc_master_cmd *cmd)
 {
 	int i, rv;
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
 
 	for (i = 0; i < cmd->msg_len; i++)
-		cx24110_writereg(i2c, 0x79 + i, cmd->msg[i]);
+		cx24110_writereg(state, 0x79 + i, cmd->msg[i]);
 
-	rv = cx24110_readreg(i2c, 0x76);
+	rv = cx24110_readreg(state, 0x76);
 
-	cx24110_writereg(i2c, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3));
-	for (i=500; i-- > 0 && !(cx24110_readreg(i2c,0x76)&0x40);)
+	cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3));
+	for (i=500; i-- > 0 && !(cx24110_readreg(state,0x76)&0x40);)
 		; /* wait for LNB ready */
-}
 
+	return 0;
+}
 
-static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status)
 {
-	struct cx24110_state *state = fe->data;
-	struct i2c_adapter *i2c = state->i2c;
-	static int lastber=0, lastbyer=0,lastbler=0, lastesn0=0, sum_bler=0;
-
-        switch (cmd) {
-        case FE_GET_INFO:
-		memcpy (arg, &cx24110_info, sizeof(struct dvb_frontend_info));
-		break;
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
 
-        case FE_READ_STATUS:
-	{
-		fe_status_t *status = arg;
-		int sync = cx24110_readreg (i2c, 0x55);
+	int sync = cx24110_readreg (state, 0x55);
 
 		*status = 0;
 
@@ -520,7 +435,7 @@ static int cx24110_ioctl (struct dvb_fro
 		if (sync & 0x08)
 			*status |= FE_HAS_CARRIER;
 
-		sync = cx24110_readreg (i2c, 0x08);
+	sync = cx24110_readreg (state, 0x08);
 
 		if (sync & 0x40)
 			*status |= FE_HAS_VITERBI;
@@ -531,87 +446,97 @@ static int cx24110_ioctl (struct dvb_fro
 		if ((sync & 0x60) == 0x60)
 			*status |= FE_HAS_LOCK;
 
-		if(cx24110_readreg(i2c,0x10)&0x40) {
-			/* the RS error counter has finished one counting window */
-			cx24110_writereg(i2c,0x10,0x60); /* select the byer reg */
-			lastbyer=cx24110_readreg(i2c,0x12)|
-				(cx24110_readreg(i2c,0x13)<<8)|
-				(cx24110_readreg(i2c,0x14)<<16);
-			cx24110_writereg(i2c,0x10,0x70); /* select the bler reg */
-			lastbler=cx24110_readreg(i2c,0x12)|
-				(cx24110_readreg(i2c,0x13)<<8)|
-				(cx24110_readreg(i2c,0x14)<<16);
-			cx24110_writereg(i2c,0x10,0x20); /* start new count window */
-			sum_bler += lastbler;
-		}
-		if(cx24110_readreg(i2c,0x24)&0x10) {
-			/* the Viterbi error counter has finished one counting window */
-			cx24110_writereg(i2c,0x24,0x04); /* select the ber reg */
-			lastber=cx24110_readreg(i2c,0x25)|
-				(cx24110_readreg(i2c,0x26)<<8);
-			cx24110_writereg(i2c,0x24,0x04); /* start new count window */
-			cx24110_writereg(i2c,0x24,0x14);
-		}
-		if(cx24110_readreg(i2c,0x6a)&0x80) {
-			/* the Es/N0 error counter has finished one counting window */
-			lastesn0=cx24110_readreg(i2c,0x69)|
-				(cx24110_readreg(i2c,0x68)<<8);
-			cx24110_writereg(i2c,0x6a,0x84); /* start new count window */
-		}
-		break;
+	return 0;
 	}
 
-        case FE_READ_BER:
+static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber)
 	{
-		u32 *ber = (u32 *) arg;
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
 
-		*ber = lastber;
 /* fixme (maybe): value range is 16 bit. Scale? */
-		break;
+	if(cx24110_readreg(state,0x24)&0x10) {
+		/* the Viterbi error counter has finished one counting window */
+		cx24110_writereg(state,0x24,0x04); /* select the ber reg */
+		state->lastber=cx24110_readreg(state,0x25)|
+			(cx24110_readreg(state,0x26)<<8);
+		cx24110_writereg(state,0x24,0x04); /* start new count window */
+		cx24110_writereg(state,0x24,0x14);
 	}
+	*ber = state->lastber;
 
-        case FE_READ_SIGNAL_STRENGTH:
+	return 0;
+	}
+
+static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
 	{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
 /* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */
-		u8 signal = cx24110_readreg (i2c, 0x27)+128;
-		*((u16*) arg) = (signal << 8) | signal;
-		break;
+	u8 signal = cx24110_readreg (state, 0x27)+128;
+	*signal_strength = (signal << 8) | signal;
+
+	return 0;
 	}
 
-        case FE_READ_SNR:
+static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr)
 	{
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
 /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */
-		*(u16*) arg = lastesn0;
-		break;
+	if(cx24110_readreg(state,0x6a)&0x80) {
+		/* the Es/N0 error counter has finished one counting window */
+		state->lastesn0=cx24110_readreg(state,0x69)|
+			(cx24110_readreg(state,0x68)<<8);
+		cx24110_writereg(state,0x6a,0x84); /* start new count window */
+	}
+	*snr = state->lastesn0;
+
+	return 0;
 	}
 
-	case FE_READ_UNCORRECTED_BLOCKS:
+static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
 	{
-		*(u16*) arg = sum_bler&0xffff;
-		sum_bler=0;
-		break;
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+	u32 lastbyer;
+
+	if(cx24110_readreg(state,0x10)&0x40) {
+		/* the RS error counter has finished one counting window */
+		cx24110_writereg(state,0x10,0x60); /* select the byer reg */
+		lastbyer=cx24110_readreg(state,0x12)|
+			(cx24110_readreg(state,0x13)<<8)|
+			(cx24110_readreg(state,0x14)<<16);
+		cx24110_writereg(state,0x10,0x70); /* select the bler reg */
+		state->lastbler=cx24110_readreg(state,0x12)|
+			(cx24110_readreg(state,0x13)<<8)|
+			(cx24110_readreg(state,0x14)<<16);
+		cx24110_writereg(state,0x10,0x20); /* start new count window */
 	}
+	*ucblocks = state->lastbler;
 
-        case FE_SET_FRONTEND:
+	return 0;
+}
+
+static int cx24110_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
         {
-		struct dvb_frontend_parameters *p = arg;
+	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
 
-		cx24108_set_tv_freq (i2c, p->frequency);
-		cx24110_set_inversion (i2c, p->inversion);
-		cx24110_set_fec (i2c, p->u.qpsk.fec_inner);
-		cx24110_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
-		cx24110_writereg(i2c,0x04,0x05); /* start aquisition */
-                break;
+	state->config->pll_set(fe, p);
+	cx24110_set_inversion (state, p->inversion);
+	cx24110_set_fec (state, p->u.qpsk.fec_inner);
+	cx24110_set_symbolrate (state, p->u.qpsk.symbol_rate);
+	cx24110_writereg(state,0x04,0x05); /* start aquisition */
+
+	return 0;
         }
 
-	case FE_GET_FRONTEND:
+static int cx24110_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 	{
-		struct dvb_frontend_parameters *p = arg;
+   	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
 		s32 afc; unsigned sclk;
 
 /* cannot read back tuner settings (freq). Need to have some private storage */
 
-		sclk = cx24110_readreg (i2c, 0x07) & 0x03;
+	sclk = cx24110_readreg (state, 0x07) & 0x03;
 /* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz.
  * Need 64 bit arithmetic. Is thiss possible in the kernel? */
 		if (sclk==0) sclk=90999000L/2L;
@@ -619,152 +544,104 @@ static int cx24110_ioctl (struct dvb_fro
 		else if (sclk==2) sclk=80888000L;
 		else sclk=90999000L;
 		sclk>>=8;
-		afc = sclk*(cx24110_readreg (i2c, 0x44)&0x1f)+
-		      ((sclk*cx24110_readreg (i2c, 0x45))>>8)+
-		      ((sclk*cx24110_readreg (i2c, 0x46))>>16);
+	afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+
+	      ((sclk*cx24110_readreg (state, 0x45))>>8)+
+	      ((sclk*cx24110_readreg (state, 0x46))>>16);
 
 		p->frequency += afc;
-		p->inversion = (cx24110_readreg (i2c, 0x22) & 0x10) ?
+	p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ?
 					INVERSION_ON : INVERSION_OFF;
-		p->u.qpsk.fec_inner = cx24110_get_fec (i2c);
-		break;
-	}
-
-        case FE_SLEEP:
-/* cannot do this from the FE end. How to communicate this to the place where it can be done? */
-		break;
-        case FE_INIT:
-		return cx24110_initfe(i2c);
-
-	case FE_SET_TONE:
-		return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&~0x10)|((((fe_sec_tone_mode_t) arg)==SEC_TONE_ON)?0x10:0));
-	case FE_SET_VOLTAGE:
-		return cx24110_set_voltage (i2c, (fe_sec_voltage_t) arg);
-
-	case FE_DISEQC_SEND_MASTER_CMD:
-		// FIXME Status?
-		cx24110_send_diseqc_msg(i2c, (struct dvb_diseqc_master_cmd*) arg);
-		return 0;
-
-	default:
-		return -EOPNOTSUPP;
-        };
+	p->u.qpsk.fec_inner = cx24110_get_fec (state);
 
         return 0;
 }
 
-static struct i2c_client client_template;
-
-static int attach_adapter (struct i2c_adapter *adapter)
+static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
 {
-	struct cx24110_state *state;
-	struct i2c_client *client;
-	int ret = 0;
-	u8 sig;
-
-	dprintk("Trying to attach to adapter 0x%x:%s.\n",
-		adapter->id, adapter->name);
-
-	sig = cx24110_readreg (adapter, 0x00);
-	if ( sig != 0x5a && sig != 0x69 )
-		return -ENODEV;
+   	struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
 
-	if ( !(state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL)) )
-		return -ENOMEM;
-
-	memset(state, 0, sizeof(struct cx24110_state));
-	state->i2c = adapter;
-
-	if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) {
-		kfree(state);
-		return -ENOMEM;
+	return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0));
 }
 
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = 0x55;
-	i2c_set_clientdata(client, state);
-
-	if ((ret = i2c_attach_client(client))) {
-		kfree(client);
-		kfree(state);
-		return ret;
-}
-
-	BUG_ON(!state->dvb);
-
-	if ((ret = dvb_register_frontend(cx24110_ioctl, state->dvb, state,
-					     &cx24110_info, THIS_MODULE))) {
-		i2c_detach_client(client);
-		kfree(client);
+static void cx24110_release(struct dvb_frontend* fe)
+{
+	struct cx24110_state* state = (struct cx24110_state*) fe->demodulator_priv;
 		kfree(state);
-		return ret;
-}
-
-	return 0;
 }
 
-static int detach_client (struct i2c_client *client)
-{
-	struct cx24110_state *state = i2c_get_clientdata(client);
-
-	dvb_unregister_frontend(cx24110_ioctl, state->dvb);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
-	kfree(state);
-	return 0;
-}
+static struct dvb_frontend_ops cx24110_ops;
 
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
+struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+				    struct i2c_adapter* i2c)
 {
-	struct cx24110_state *state = i2c_get_clientdata(client);
-
-	switch(cmd) {
-	case FE_REGISTER:
-		state->dvb = arg;
-		break;
-	case FE_UNREGISTER:
-		state->dvb = NULL;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	return 0;
-}
-
-static struct i2c_driver driver = {
-	.owner		= THIS_MODULE,
-	.name		= FRONTEND_NAME,
-	.id		= I2C_DRIVERID_DVBFE_CX24110,
-	.flags		= I2C_DF_NOTIFY,
-	.attach_adapter	= attach_adapter,
-	.detach_client	= detach_client,
-	.command	= command,
-};
+	struct cx24110_state* state = NULL;
+	int ret;
 
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags		= I2C_CLIENT_ALLOW_USE,
-	.driver		= &driver,
+	/* allocate memory for the internal state */
+	state = (struct cx24110_state*) kmalloc(sizeof(struct cx24110_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &cx24110_ops, sizeof(struct dvb_frontend_ops));
+	state->lastber = 0;
+	state->lastbler = 0;
+   	state->lastesn0 = 0;
+
+	/* check if the demod is there */
+	ret = cx24110_readreg(state, 0x00);
+	if ((ret != 0x5a) && (ret != 0x69)) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops cx24110_ops = {
+
+	.info = {
+		.name = "Conexant CX24110 DVB-S",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1011,  /* kHz for QPSK frontends */
+		.frequency_tolerance = 29500,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_RECOVER
+	},
+
+	.release = cx24110_release,
+
+	.init = cx24110_initfe,
+	.set_frontend = cx24110_set_frontend,
+	.get_frontend = cx24110_get_frontend,
+	.read_status = cx24110_read_status,
+	.read_ber = cx24110_read_ber,
+	.read_signal_strength = cx24110_read_signal_strength,
+	.read_snr = cx24110_read_snr,
+	.read_ucblocks = cx24110_read_ucblocks,
+
+	.diseqc_send_master_cmd = cx24110_send_diseqc_msg,
+	.set_tone = cx24110_set_tone,
+	.set_voltage = cx24110_set_voltage,
 };
 
-static int __init cx24110_init(void)
-{
-	return i2c_add_driver(&driver);
-}
-
-static void __exit cx24110_exit(void)
-{
-	if (i2c_del_driver(&driver))
-		printk(KERN_ERR "cx24110: driver deregistration failed.\n");
-}
-
-module_init(cx24110_init);
-module_exit(cx24110_exit);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-MODULE_DESCRIPTION("DVB Frontend driver module for the Conexant cx24108/cx24110 chipset");
+MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver");
 MODULE_AUTHOR("Peter Hettkamp");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(cx24110_attach);
+EXPORT_SYMBOL(cx24110_pll_write);
diff -puN /dev/null drivers/media/dvb/frontends/cx24110.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/cx24110.h	2004-11-21 17:13:23.422057688 -0800
@@ -0,0 +1,45 @@
+/*
+    cx24110 - Single Chip Satellite Channel Receiver driver module
+
+    Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
+    work
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CX24110_H
+#define CX24110_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24110_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+					   struct i2c_adapter* i2c);
+
+extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data);
+
+#endif // CX24110_H
diff -puN /dev/null drivers/media/dvb/frontends/dib3000-common.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/dib3000-common.h	2004-11-21 17:13:23.423057536 -0800
@@ -0,0 +1,104 @@
+/*
+ * .h-files for the common use of the frontend drivers made by DiBcom
+ * DiBcom 3000-MB/MC/P
+ *
+ * DiBcom (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#ifndef DIB3000_COMMON_H
+#define DIB3000_COMMON_H
+
+
+/* info and err, taken from usb.h, if there is anything available like by default,
+ * please change !
+ */
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg)
+
+/* a PID for the pid_filter list, when in use */
+struct dib3000_pid
+{
+	u16 pid;
+	int active;
+};
+
+/* mask for enabling a specific pid for the pid_filter */
+#define DIB3000_ACTIVATE_PID_FILTERING	(0x2000)
+
+/* common values for tuning */
+#define DIB3000_ALPHA_0					(     0)
+#define DIB3000_ALPHA_1					(     1)
+#define DIB3000_ALPHA_2					(     2)
+#define DIB3000_ALPHA_4					(     4)
+
+#define DIB3000_CONSTELLATION_QPSK		(     0)
+#define DIB3000_CONSTELLATION_16QAM		(     1)
+#define DIB3000_CONSTELLATION_64QAM		(     2)
+
+#define DIB3000_GUARD_TIME_1_32			(     0)
+#define DIB3000_GUARD_TIME_1_16			(     1)
+#define DIB3000_GUARD_TIME_1_8			(     2)
+#define DIB3000_GUARD_TIME_1_4			(     3)
+
+#define DIB3000_TRANSMISSION_MODE_2K	(     0)
+#define DIB3000_TRANSMISSION_MODE_8K	(     1)
+
+#define DIB3000_SELECT_LP				(     0)
+#define DIB3000_SELECT_HP				(     1)
+
+#define DIB3000_FEC_1_2					(     1)
+#define DIB3000_FEC_2_3					(     2)
+#define DIB3000_FEC_3_4					(     3)
+#define DIB3000_FEC_5_6					(     5)
+#define DIB3000_FEC_7_8					(     7)
+
+#define DIB3000_HRCH_OFF				(     0)
+#define DIB3000_HRCH_ON					(     1)
+
+#define DIB3000_DDS_INVERSION_OFF		(     0)
+#define DIB3000_DDS_INVERSION_ON		(     1)
+
+#define DIB3000_TUNER_WRITE_ENABLE(a)	(0xffff & (a << 7))
+#define DIB3000_TUNER_WRITE_DISABLE(a)	(0xffff & ((a << 7) | (1 << 7)))
+
+/* for auto search */
+static u16 dib3000_seq[2][2][2] =     /* fft,gua,   inv   */
+	{ /* fft */
+		{ /* gua */
+			{ 0, 1 },                   /*  0   0   { 0,1 } */
+			{ 3, 9 },                   /*  0   1   { 0,1 } */
+		},
+		{
+			{ 2, 5 },                   /*  1   0   { 0,1 } */
+			{ 6, 11 },                  /*  1   1   { 0,1 } */
+		}
+	};
+
+#define DIB3000_REG_MANUFACTOR_ID		(  1025)
+#define DIB3000_I2C_ID_DIBCOM			(0x01b3)
+
+#define DIB3000_REG_DEVICE_ID			(  1026)
+#define DIB3000MB_DEVICE_ID				(0x3000)
+#define DIB3000MC_DEVICE_ID				(0x3001)
+#define DIB3000P_DEVICE_ID				(0x3002)
+
+#endif // DIB3000_COMMON_H
diff -puN /dev/null drivers/media/dvb/frontends/dib3000.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/dib3000.h	2004-11-21 17:13:23.423057536 -0800
@@ -0,0 +1,53 @@
+/*
+ * public header file of the frontend drivers for mobile DVB-T demodulators
+ * DiBcom 3000-MB and DiBcom 3000-MC/P (http://www.dibcom.fr/)
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ * based on GPL code from DibCom, which has
+ *
+ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ *
+ * Acknowledgements
+ *
+ *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
+ *  sources, on which this driver (and the dvb-dibusb) are based.
+ *
+ * see Documentation/dvb/README.dibusb for more information
+ *
+ */
+
+#ifndef DIB3000_H
+#define DIB3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct dib3000_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* The i2c address of the PLL */
+	u8 pll_addr;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend *fe);
+	int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params);
+};
+
+struct dib3000_xfer_ops
+{
+	/* pid and transfer handling is done in the demodulator */
+	int (*pid_filter)(struct dvb_frontend *fe, int onoff);
+	int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff);
+	int (*pid_ctrl)(struct dvb_frontend *fe, int pid, int onoff);
+};
+
+extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+					     struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops);
+
+#endif // DIB3000_H
diff -puN drivers/media/dvb/frontends/dib3000mb.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/dib3000mb.c
--- 25/drivers/media/dvb/frontends/dib3000mb.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.378064376 -0800
+++ 25-akpm/drivers/media/dvb/frontends/dib3000mb.c	2004-11-21 17:13:23.430056472 -0800
@@ -17,8 +17,6 @@
  *  Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
  *  sources, on which this driver (and the dvb-dibusb) are based.
  *
- * 
- * 
  * see Documentation/dvb/README.dibusb for more information
  *
  */
@@ -32,8 +30,26 @@
 #include <linux/delay.h>
 
 #include "dvb_frontend.h"
+#include "dib3000-common.h"
+#include "dib3000mb_priv.h"
+#include "dib3000.h"
+
+struct dib3000mb_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct dib3000_config* config;
+
+	spinlock_t pid_list_lock;
+	struct dib3000_pid pid_list[DIB3000MB_NUM_PIDS];
+	int feedcount;
+
+	struct dvb_frontend frontend;
+};
 
-#include "dib3000mb.h"
 
 /* debug */
 
@@ -56,34 +72,13 @@ MODULE_PARM_DESC(debug, "set debugging l
 
 /* Version information */
 #define DRIVER_VERSION "0.1"
-#define DRIVER_DESC "DiBcom 3000-MB DVB-T frontend"
+#define DRIVER_DESC "DiBcom 3000-MB DVB-T demodulator driver"
 #define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
 
-struct dib3000mb_state {
-	struct i2c_client *i2c;
-	struct dvb_adapter *dvb;
-	u16 manufactor_id;
-	u16 device_id;
-};
-
-static struct dvb_frontend_info dib3000mb_info = {
-	.name			= "DiBcom 3000-MB DVB-T",
-	.type 			= FE_OFDM,
-	.frequency_min 		= 44250000,
-	.frequency_max 		= 867250000,
-	.frequency_stepsize	= 62500,
-	.caps = FE_CAN_INVERSION_AUTO |
-			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
-			FE_CAN_GUARD_INTERVAL_AUTO |
-			FE_CAN_TRANSMISSION_MODE_AUTO | 
-			FE_CAN_HIERARCHY_AUTO,
-};
 
-
-#define rd(reg) dib3000mb_read_reg(state->i2c,reg)
-#define wr(reg,val) if (dib3000mb_write_reg(state->i2c,reg,val)) \
+/* handy shortcuts */
+#define rd(reg) dib3000mb_read_reg(state,reg)
+#define wr(reg,val) if (dib3000mb_write_reg(state,reg,val)) \
 	{ err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
 #define wr_foreach(a,v) { int i; \
 	deb_alot("sizeof: %d %d\n",sizeof(a),sizeof(v));\
@@ -91,228 +86,53 @@ static struct dvb_frontend_info dib3000m
 		wr(a[i],v[i]); \
 }
 
-static u16 dib3000mb_read_reg(struct i2c_client *i2c, u16 reg)
+static int dib3000mb_read_reg(struct dib3000mb_state *state, u16 reg)
 {
 	u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
 	u8 rb[2];
 	struct i2c_msg msg[] = {
-		{ .addr = i2c->addr, .flags = 0,        .buf = wb, .len = 2 },
-		{ .addr = i2c->addr, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+		{ .addr = state->config->demod_address, .flags = 0,        .buf = wb, .len = 2 },
+		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
 	};
 	deb_alot("reading from i2c bus (reg: %d)\n",reg);
 
-	if (i2c_transfer(i2c->adapter,msg,2) != 2)
+	if (i2c_transfer(state->i2c, msg, 2) != 2)
 		deb_alot("i2c read error\n");
 
 	return (rb[0] << 8) | rb[1];
 }
 
-static int dib3000mb_write_reg(struct i2c_client *i2c,u16 reg, u16 val)
+static int dib3000mb_write_reg(struct dib3000mb_state *state, u16 reg, u16 val)
 {
 	u8 b[] = {
 		(reg >> 8) & 0xff, reg & 0xff,
 		(val >> 8) & 0xff, val & 0xff,
 	};
-	struct i2c_msg msg[] = { { .addr = i2c->addr, .flags = 0, .buf = b, .len = 4 } };
+	struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 4 } };
 	deb_alot("writing to i2c bus (reg: %d, val: %d)\n",reg,val);
 
-	return i2c_transfer(i2c->adapter,msg,1) != 1 ? -EREMOTEIO : 0 ;
+	return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0;
 }
 
-static int dib3000mb_tuner_thomson_cable_eu(struct dib3000mb_state *state,
-		u32 freq)
-{
-	u32 tfreq = (freq + 36125000) / 62500;
-	unsigned int addr;
-	int vu,p0,p1,p2;
-
-	if (freq > 403250000)
-		vu = 1, p2 = 1, p1 = 0, p0 = 1;
-	else if (freq > 115750000)
-		vu = 0, p2 = 1, p1 = 1, p0 = 0;
-	else if (freq > 44250000)
-		vu = 0, p2 = 0, p1 = 1, p0 = 1;
-	else
-		return -EINVAL;
-	/* TODO better solution for i2c->addr handling */
-	addr = state->i2c->addr;
-	state->i2c->addr = DIB3000MB_TUNER_ADDR_DEFAULT;
-	wr(tfreq & 0x7fff,(0x8e << 8) + ((vu << 7) | (p2 << 2) | (p1 << 1) | p0) );
-	state->i2c->addr = addr;
-
-	return 0;
-}
-
-static int dib3000mb_get_frontend(struct dib3000mb_state *state,
-		struct dvb_frontend_parameters *fep)
-{
-	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
-	fe_code_rate_t *cr;
-	u16 tps_val;
-	int inv_test1,inv_test2;
-	u32 dds_val, threshold = 0x800000;
-
-	if (!rd(DIB3000MB_REG_TPS_LOCK))
-		return 0;
-
-	dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB);
-	if (dds_val & threshold)
-		inv_test1 = 0;
-	else if (dds_val == threshold)
-		inv_test1 = 1;
-	else
-		inv_test1 = 2;
-
-	dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB);
-	if (dds_val & threshold)
-		inv_test2 = 0;
-	else if (dds_val == threshold)
-		inv_test2 = 1;
-	else
-		inv_test2 = 2;
-
-	fep->inversion =
-		((inv_test2 == 2) && (inv_test1==1 || inv_test1==0))
-					||
-		((inv_test2 == 0) && (inv_test1==1 || inv_test1==2));
-
-	deb_getf("inversion %d %d, %d\n",inv_test2,inv_test1, fep->inversion);
-
-	switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) {
-		case DIB3000MB_QAM_QPSK:
-			deb_getf("QPSK ");
-			ofdm->constellation = QPSK;
-			break;
-		case DIB3000MB_QAM_QAM16:
-			deb_getf("QAM16 ");
-			ofdm->constellation = QAM_16;
-			break;
-		case DIB3000MB_QAM_QAM64:
-			deb_getf("QAM64 ");
-			ofdm->constellation = QAM_64;
-			break;
-		default:
-			err("Unexpected constellation returned by TPS (%d)",tps_val);
-			break;
- 	}
-	deb_getf("TPS: %d\n",tps_val);
-
-	if (rd(DIB3000MB_REG_TPS_HRCH)) {
-		deb_getf("HRCH ON\n");
-		tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP);
-		cr = &ofdm->code_rate_LP;
-		ofdm->code_rate_HP = FEC_NONE;
-
-		switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) {
-			case DIB3000MB_VIT_ALPHA_OFF:
-				deb_getf("HIERARCHY_NONE ");
-				ofdm->hierarchy_information = HIERARCHY_NONE;
-				break;
-			case DIB3000MB_VIT_ALPHA_1:
-				deb_getf("HIERARCHY_1 ");
-				ofdm->hierarchy_information = HIERARCHY_1;
-				break;
-			case DIB3000MB_VIT_ALPHA_2:
-				deb_getf("HIERARCHY_2 ");
-				ofdm->hierarchy_information = HIERARCHY_2;
-				break;
-			case DIB3000MB_VIT_ALPHA_4:
-				deb_getf("HIERARCHY_4 ");
-				ofdm->hierarchy_information = HIERARCHY_4;
-				break;
-			default:
-				err("Unexpected ALPHA value returned by TPS (%d)",tps_val);
-		}
-		deb_getf("TPS: %d\n",tps_val);
-	} else {
-		deb_getf("HRCH OFF\n");
-		tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP);
-		cr = &ofdm->code_rate_HP;
-		ofdm->code_rate_LP = FEC_NONE;
-		ofdm->hierarchy_information = HIERARCHY_NONE;
-	}
-
-	switch (tps_val) {
-		case DIB3000MB_FEC_1_2:
-			deb_getf("FEC_1_2 ");
-			*cr = FEC_1_2;
-			break;
-		case DIB3000MB_FEC_2_3:
-			deb_getf("FEC_2_3 ");
-			*cr = FEC_2_3;
-			break;
-		case DIB3000MB_FEC_3_4:
-			deb_getf("FEC_3_4 ");
-			*cr = FEC_3_4;
-			break;
-		case DIB3000MB_FEC_5_6:
-			deb_getf("FEC_5_6 ");
-			*cr = FEC_4_5;
-			break;
-		case DIB3000MB_FEC_7_8:
-			deb_getf("FEC_7_8 ");
-			*cr = FEC_7_8;
-			break;
-		default:
-			err("Unexpected FEC returned by TPS (%d)",tps_val);
-			break;
-	}
-	deb_getf("TPS: %d\n",tps_val);
-
-	switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) {
-		case DIB3000MB_GUARD_TIME_1_32:
-			deb_getf("GUARD_INTERVAL_1_32 ");
-			ofdm->guard_interval = GUARD_INTERVAL_1_32;
-			break;
-		case DIB3000MB_GUARD_TIME_1_16:
-			deb_getf("GUARD_INTERVAL_1_16 ");
-			ofdm->guard_interval = GUARD_INTERVAL_1_16;
-			break;
-		case DIB3000MB_GUARD_TIME_1_8:
-			deb_getf("GUARD_INTERVAL_1_8 ");
-			ofdm->guard_interval = GUARD_INTERVAL_1_8;
-			break;
-		case DIB3000MB_GUARD_TIME_1_4:
-			deb_getf("GUARD_INTERVAL_1_4 ");
-			ofdm->guard_interval = GUARD_INTERVAL_1_4;
-			break;
-		default:
-			err("Unexpected Guard Time returned by TPS (%d)",tps_val);
-			break;
-	}
-	deb_getf("TPS: %d\n",tps_val);
-
-	switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) {
-		case DIB3000MB_FFT_2K:
-			deb_getf("TRANSMISSION_MODE_2K ");
-			ofdm->transmission_mode = TRANSMISSION_MODE_2K;
-			break;
-		case DIB3000MB_FFT_8K:
-			deb_getf("TRANSMISSION_MODE_8K ");
-			ofdm->transmission_mode = TRANSMISSION_MODE_8K;
-			break;
-		default:
-			err("unexpected transmission mode return by TPS (%d)",tps_val);
-	}
-	deb_getf("TPS: %d\n",tps_val);
-	return 0;
-}
-
-static int dib3000mb_set_frontend(struct dib3000mb_state *state,
+static int dib3000mb_set_frontend(struct dvb_frontend* fe,
 		struct dvb_frontend_parameters *fep, int tuner);
 
-static int dib3000mb_fe_read_search_status(struct dib3000mb_state *state)
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep);
+
+static int dib3000mb_fe_read_search_status(struct dvb_frontend* fe)
 {
 	u16 irq;
 	struct dvb_frontend_parameters fep;
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
 
 	irq = rd(DIB3000MB_REG_AS_IRQ_PENDING);
 
 	if (irq & 0x02) {
 		if (rd(DIB3000MB_REG_LOCK2_VALUE) & 0x01) {
-			if (dib3000mb_get_frontend(state,&fep) == 0) {
+			if (dib3000mb_get_frontend(fe, &fep) == 0) {
 				deb_setf("reading tuning data from frontend succeeded.\n");
-				return dib3000mb_set_frontend(state,&fep,0) == 0;
+				return dib3000mb_set_frontend(fe, &fep, 0) == 0;
 			} else {
 				deb_setf("reading tuning data failed -> tuning failed.\n");
 				return 0;
@@ -329,22 +149,20 @@ static int dib3000mb_fe_read_search_stat
 	return -1;
 }
 
-static int dib3000mb_set_frontend(struct dib3000mb_state *state,
+static int dib3000mb_set_frontend(struct dvb_frontend* fe,
 		struct dvb_frontend_parameters *fep, int tuner)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
 	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
 	fe_code_rate_t fe_cr = FEC_NONE;
 	int search_state,seq;
 
 	if (tuner) {
 		wr(DIB3000MB_REG_TUNER,
-				DIB3000MB_ACTIVATE_TUNER_XFER( DIB3000MB_TUNER_ADDR_DEFAULT ) );
-		dib3000mb_tuner_thomson_cable_eu(state,fep->frequency);
-
-		/* wait for tuner */
-		msleep(1);
+				DIB3000_TUNER_WRITE_ENABLE(state->config->pll_addr));
+		state->config->pll_set(fe, fep);
 		wr(DIB3000MB_REG_TUNER,
-				DIB3000MB_DEACTIVATE_TUNER_XFER( DIB3000MB_TUNER_ADDR_DEFAULT ) );
+				DIB3000_TUNER_WRITE_DISABLE(state->config->pll_addr));
 
 		deb_setf("bandwidth: ");
 		switch (ofdm->bandwidth) {
@@ -376,15 +194,14 @@ static int dib3000mb_set_frontend(struct
 	switch (ofdm->transmission_mode) {
 		case TRANSMISSION_MODE_2K:
 			deb_setf("2k\n");
-			wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_2K);
+			wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K);
 			break;
 		case TRANSMISSION_MODE_8K:
 			deb_setf("8k\n");
-			wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_8K);
+			wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K);
 			break;
 		case TRANSMISSION_MODE_AUTO:
 			deb_setf("auto\n");
-			wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_AUTO);
 			break;
 		default:
 			return -EINVAL;
@@ -394,40 +211,39 @@ static int dib3000mb_set_frontend(struct
 	switch (ofdm->guard_interval) {
 		case GUARD_INTERVAL_1_32:
 			deb_setf("1_32\n");
-			wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_32);
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32);
 			break;
 		case GUARD_INTERVAL_1_16:
 			deb_setf("1_16\n");
-			wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_16);
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16);
 			break;
 		case GUARD_INTERVAL_1_8:
 			deb_setf("1_8\n");
-			wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_8);
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8);
 			break;
 		case GUARD_INTERVAL_1_4:
 			deb_setf("1_4\n");
-			wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_4);
+			wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4);
 			break;
 		case GUARD_INTERVAL_AUTO:
 			deb_setf("auto\n");
-			wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_AUTO);
 			break;
 		default:
 			return -EINVAL;
 	}
 
-	deb_setf("invsersion: ");
+	deb_setf("inversion: ");
 	switch (fep->inversion) {
-		case INVERSION_AUTO:
-			deb_setf("auto\n");
-			break;
 		case INVERSION_OFF:
-			deb_setf("on\n");
-			wr(DIB3000MB_REG_DDS_INV,DIB3000MB_DDS_INV_OFF);
+			deb_setf("off\n");
+			wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF);
+			break;
+		case INVERSION_AUTO:
+			deb_setf("auto ");
 			break;
 		case INVERSION_ON:
 			deb_setf("on\n");
-			wr(DIB3000MB_REG_DDS_INV,DIB3000MB_DDS_INV_ON);
+			wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON);
 			break;
 		default:
 			return -EINVAL;
@@ -437,15 +253,15 @@ static int dib3000mb_set_frontend(struct
 	switch (ofdm->constellation) {
 		case QPSK:
 			deb_setf("qpsk\n");
-			wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QPSK);
+			wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK);
 			break;
 		case QAM_16:
 			deb_setf("qam16\n");
-			wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QAM16);
+			wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM);
 			break;
 		case QAM_64:
 			deb_setf("qam64\n");
-			wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QAM64);
+			wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM);
 			break;
 		case QAM_AUTO:
 			break;
@@ -456,22 +272,21 @@ static int dib3000mb_set_frontend(struct
 	switch (ofdm->hierarchy_information) {
 		case HIERARCHY_NONE:
 			deb_setf("none ");
-			/* fall through alpha is 1, even when HIERARCHY is NONE */ 
+			/* fall through */
 		case HIERARCHY_1:
 			deb_setf("alpha=1\n");	
-			wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_1);
+			wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1);
 			break;
 		case HIERARCHY_2:
 			deb_setf("alpha=2\n");	
-			wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_2);
+			wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2);
 			break;
 		case HIERARCHY_4:
 			deb_setf("alpha=4\n");	
-			wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_4);
+			wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4);
 			break;
 		case HIERARCHY_AUTO:
 			deb_setf("alpha=auto\n");	
-			wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_AUTO);
 			break;
 		default:
 			return -EINVAL;
@@ -480,39 +295,40 @@ static int dib3000mb_set_frontend(struct
 	deb_setf("hierarchy: ");
 	if (ofdm->hierarchy_information == HIERARCHY_NONE) {
 		deb_setf("none\n");
-		wr(DIB3000MB_REG_VIT_HRCH,DIB3000MB_VIT_HRCH_OFF);
-		wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_HP);
+		wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF);
+		wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP);
 		fe_cr = ofdm->code_rate_HP;
 	} else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
 		deb_setf("on\n");
-		wr(DIB3000MB_REG_VIT_HRCH,DIB3000MB_VIT_HRCH_ON);
-		wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_LP);
+		wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON);
+		wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP);
 		fe_cr = ofdm->code_rate_LP;
 	}
 	deb_setf("fec: ");
 	switch (fe_cr) {
 		case FEC_1_2:
 			deb_setf("1_2\n");
-			wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_1_2);
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2);
 			break;
 		case FEC_2_3:
 			deb_setf("2_3\n");
-			wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_2_3);
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3);
 			break;
 		case FEC_3_4:
 			deb_setf("3_4\n");
-			wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_3_4);
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4);
 			break;
 		case FEC_5_6:
 			deb_setf("5_6\n");
-			wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_5_6);
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6);
 			break;
 		case FEC_7_8:
 			deb_setf("7_8\n");
-			wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_7_8);
+			wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8);
 			break;
 		case FEC_NONE:
 			deb_setf("none ");
+			break;
 		case FEC_AUTO:
 			deb_setf("auto\n");
 			break;
@@ -520,7 +336,7 @@ static int dib3000mb_set_frontend(struct
 			return -EINVAL;
 	}
 
-	seq = dib3000mb_seq
+	seq = dib3000_seq
 		[ofdm->transmission_mode == TRANSMISSION_MODE_AUTO]
 		[ofdm->guard_interval == GUARD_INTERVAL_AUTO]
 		[fep->inversion == INVERSION_AUTO];
@@ -564,6 +380,7 @@ static int dib3000mb_set_frontend(struct
 		ofdm->hierarchy_information == HIERARCHY_AUTO ||
 		fe_cr == FEC_AUTO ||
 		fep->inversion == INVERSION_AUTO) {
+		int as_count=0;
 
 		deb_setf("autosearch enabled.\n");	
 
@@ -572,20 +389,22 @@ static int dib3000mb_set_frontend(struct
 		wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_AUTO_SEARCH);
 		wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF);
 
-		while ((search_state = dib3000mb_fe_read_search_status(state)) < 0);
-		deb_info("search_state after autosearch %d\n",search_state);
-		return search_state ? 0 : -EINVAL;
+		while ((search_state = dib3000mb_fe_read_search_status(fe)) < 0 && as_count++ < 100)
+			msleep(1);
+
+		deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count);
 	} else {
 		wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_CTRL);
 		wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF);
-		msleep(70);
 	}
+
 	return 0;
 }
 
-
-static int dib3000mb_fe_init(struct dib3000mb_state *state,int mobile_mode)
+static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
+
 	wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_UP);
 
 	wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC);
@@ -597,9 +416,6 @@ static int dib3000mb_fe_init(struct dib3
 
 	wr(DIB3000MB_REG_ELECT_OUT_MODE,DIB3000MB_ELECT_OUT_MODE_ON);
 
-	wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_RESERVED);
-	wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_AUTO);
-
 	wr(DIB3000MB_REG_DDS_FREQ_MSB,DIB3000MB_DDS_FREQ_MSB);
 	wr(DIB3000MB_REG_DDS_FREQ_LSB,DIB3000MB_DDS_FREQ_LSB);
 
@@ -621,7 +437,7 @@ static int dib3000mb_fe_init(struct dib3
 	wr(DIB3000MB_REG_LOCK0_MASK,DIB3000MB_LOCK0_DEFAULT);
 	wr(DIB3000MB_REG_LOCK1_MASK,DIB3000MB_LOCK1_SEARCH_4);
 	wr(DIB3000MB_REG_LOCK2_MASK,DIB3000MB_LOCK2_DEFAULT);
-	wr(DIB3000MB_REG_SEQ,dib3000mb_seq[1][1][1]);
+	wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]);
 
 	wr_foreach(dib3000mb_reg_bandwidth,dib3000mb_bandwidth_8mhz);
 
@@ -639,8 +455,6 @@ static int dib3000mb_fe_init(struct dib3
 	wr(DIB3000MB_REG_UNK_108,DIB3000MB_UNK_108);
 	wr(DIB3000MB_REG_UNK_122,DIB3000MB_UNK_122);
 	wr(DIB3000MB_REG_MOBILE_MODE_QAM,DIB3000MB_MOBILE_MODE_QAM_OFF);
-	wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_1_2);
-	wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_HP);
 	wr(DIB3000MB_REG_BERLEN,DIB3000MB_BERLEN_DEFAULT);
 
 	wr_foreach(dib3000mb_reg_filter_coeffs,dib3000mb_filter_coeffs);
@@ -653,17 +467,186 @@ static int dib3000mb_fe_init(struct dib3
 
 	wr(DIB3000MB_REG_FIFO_142,DIB3000MB_FIFO_142);
 	wr(DIB3000MB_REG_MPEG2_OUT_MODE,DIB3000MB_MPEG2_OUT_MODE_188);
-	wr(DIB3000MB_REG_FIFO_144,DIB3000MB_FIFO_144);
+	wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE);
 	wr(DIB3000MB_REG_FIFO,DIB3000MB_FIFO_INHIBIT);
 	wr(DIB3000MB_REG_FIFO_146,DIB3000MB_FIFO_146);
 	wr(DIB3000MB_REG_FIFO_147,DIB3000MB_FIFO_147);
 
 	wr(DIB3000MB_REG_DATA_IN_DIVERSITY,DIB3000MB_DATA_DIVERSITY_IN_OFF);
+
+	if (state->config->pll_init) {
+		wr(DIB3000MB_REG_TUNER,
+			DIB3000_TUNER_WRITE_ENABLE(state->config->pll_addr));
+		state->config->pll_init(fe);
+		wr(DIB3000MB_REG_TUNER,
+			DIB3000_TUNER_WRITE_DISABLE(state->config->pll_addr));
+	}
+
 	return 0;
 }
 
-static int dib3000mb_read_status(struct dib3000mb_state *state,fe_status_t *stat)
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+				  struct dvb_frontend_parameters *fep)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
+	struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+	fe_code_rate_t *cr;
+	u16 tps_val;
+	int inv_test1,inv_test2;
+	u32 dds_val, threshold = 0x800000;
+
+	if (!rd(DIB3000MB_REG_TPS_LOCK))
+		return 0;
+
+	dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB);
+	if (dds_val & threshold)
+		inv_test1 = 0;
+	else if (dds_val == threshold)
+		inv_test1 = 1;
+	else
+		inv_test1 = 2;
+
+	dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB);
+	if (dds_val & threshold)
+		inv_test2 = 0;
+	else if (dds_val == threshold)
+		inv_test2 = 1;
+	else
+		inv_test2 = 2;
+
+	fep->inversion =
+		((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
+		((inv_test2 == 0) && (inv_test1==1 || inv_test1==2));
+
+	deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+
+	switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) {
+		case DIB3000_CONSTELLATION_QPSK:
+			deb_getf("QPSK ");
+			ofdm->constellation = QPSK;
+			break;
+		case DIB3000_CONSTELLATION_16QAM:
+			deb_getf("QAM16 ");
+			ofdm->constellation = QAM_16;
+			break;
+		case DIB3000_CONSTELLATION_64QAM:
+			deb_getf("QAM64 ");
+			ofdm->constellation = QAM_64;
+			break;
+		default:
+			err("Unexpected constellation returned by TPS (%d)", tps_val);
+			break;
+ 	}
+	deb_getf("TPS: %d\n", tps_val);
+
+	if (rd(DIB3000MB_REG_TPS_HRCH)) {
+		deb_getf("HRCH ON\n");
+		tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP);
+		cr = &ofdm->code_rate_LP;
+		ofdm->code_rate_HP = FEC_NONE;
+
+		switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) {
+			case DIB3000_ALPHA_0:
+				deb_getf("HIERARCHY_NONE ");
+				ofdm->hierarchy_information = HIERARCHY_NONE;
+				break;
+			case DIB3000_ALPHA_1:
+				deb_getf("HIERARCHY_1 ");
+				ofdm->hierarchy_information = HIERARCHY_1;
+				break;
+			case DIB3000_ALPHA_2:
+				deb_getf("HIERARCHY_2 ");
+				ofdm->hierarchy_information = HIERARCHY_2;
+				break;
+			case DIB3000_ALPHA_4:
+				deb_getf("HIERARCHY_4 ");
+				ofdm->hierarchy_information = HIERARCHY_4;
+				break;
+			default:
+				err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
+				break;
+		}
+		deb_getf("TPS: %d\n", tps_val);
+	} else {
+		deb_getf("HRCH OFF\n");
+		tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP);
+		cr = &ofdm->code_rate_HP;
+		ofdm->code_rate_LP = FEC_NONE;
+		ofdm->hierarchy_information = HIERARCHY_NONE;
+	}
+
+	switch (tps_val) {
+		case DIB3000_FEC_1_2:
+			deb_getf("FEC_1_2 ");
+			*cr = FEC_1_2;
+			break;
+		case DIB3000_FEC_2_3:
+			deb_getf("FEC_2_3 ");
+			*cr = FEC_2_3;
+			break;
+		case DIB3000_FEC_3_4:
+			deb_getf("FEC_3_4 ");
+			*cr = FEC_3_4;
+			break;
+		case DIB3000_FEC_5_6:
+			deb_getf("FEC_5_6 ");
+			*cr = FEC_4_5;
+			break;
+		case DIB3000_FEC_7_8:
+			deb_getf("FEC_7_8 ");
+			*cr = FEC_7_8;
+			break;
+		default:
+			err("Unexpected FEC returned by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("TPS: %d\n",tps_val);
+
+	switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) {
+		case DIB3000_GUARD_TIME_1_32:
+			deb_getf("GUARD_INTERVAL_1_32 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_32;
+			break;
+		case DIB3000_GUARD_TIME_1_16:
+			deb_getf("GUARD_INTERVAL_1_16 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_16;
+			break;
+		case DIB3000_GUARD_TIME_1_8:
+			deb_getf("GUARD_INTERVAL_1_8 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_8;
+			break;
+		case DIB3000_GUARD_TIME_1_4:
+			deb_getf("GUARD_INTERVAL_1_4 ");
+			ofdm->guard_interval = GUARD_INTERVAL_1_4;
+			break;
+		default:
+			err("Unexpected Guard Time returned by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("TPS: %d\n", tps_val);
+
+	switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) {
+		case DIB3000_TRANSMISSION_MODE_2K:
+			deb_getf("TRANSMISSION_MODE_2K ");
+			ofdm->transmission_mode = TRANSMISSION_MODE_2K;
+			break;
+		case DIB3000_TRANSMISSION_MODE_8K:
+			deb_getf("TRANSMISSION_MODE_8K ");
+			ofdm->transmission_mode = TRANSMISSION_MODE_8K;
+			break;
+		default:
+			err("unexpected transmission mode return by TPS (%d)", tps_val);
+			break;
+	}
+	deb_getf("TPS: %d\n", tps_val);
+
+	return 0;
+}
+
+static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
+
 	*stat = 0;
 
 	if (rd(DIB3000MB_REG_AGC_LOCK))
@@ -699,8 +682,10 @@ static int dib3000mb_read_status(struct 
 	return 0;
 }
 
-static int dib3000mb_read_ber(struct dib3000mb_state *state,u32 *ber)
+static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
+
 	*ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB) );
 	return 0;
 }
@@ -714,8 +699,10 @@ static int dib3000mb_read_ber(struct dib
 #define DIB3000MB_AGC_REF_dBm		-14
 #define DIB3000MB_GAIN_SLOPE_dBm	100
 #define DIB3000MB_GAIN_DELTA_dBm	-2
-static int dib3000mb_read_signal_strength(struct dib3000mb_state *state, u16 *strength)
+static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
+
 /* TODO log10 
 	u16 sigpow = rd(DIB3000MB_REG_SIGNAL_POWER), 
 		n_agc_power = rd(DIB3000MB_REG_AGC_POWER),
@@ -748,8 +735,9 @@ static int dib3000mb_read_signal_strengt
  * If SNR is above 20dB, BER should be always 0.
  * choose 0dB as the minimum
  */
-static int dib3000mb_read_snr(struct dib3000mb_state *state,u16 *snr)
+static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
 	short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER);
 	int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) |
 		rd(DIB3000MB_REG_NOISE_POWER_LSB);
@@ -763,24 +751,27 @@ static int dib3000mb_read_snr(struct dib
 	
 	*snr = (u16) ((snr_dBm / 35) * 0xffff);
 */
-	*snr = (sigpow<<8) / (icipow > 0 ? icipow : 1);
+	*snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1);
 	return 0;
 }
 
-static int dib3000mb_read_unc_blocks(struct dib3000mb_state *state,u32 *unc)
+static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
+
 	*unc = rd(DIB3000MB_REG_UNC);
 	return 0;
 }
 
-static int dib3000mb_sleep(struct dib3000mb_state *state)
+static int dib3000mb_sleep(struct dvb_frontend* fe)
 {
+	struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv;
+
 	wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_DOWN);
 	return 0;
 }
 
-static int dib3000mb_fe_get_tune_settings(struct dib3000mb_state *state, 
-		struct dvb_frontend_tune_settings *tune)
+static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
 {
 	tune->min_delay_ms = 800;
 	tune->step_size = 166667;
@@ -789,205 +780,176 @@ static int dib3000mb_fe_get_tune_setting
 	return 0;
 }
 
-static int dib3000mb_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe)
 {
-	struct dib3000mb_state *state = fe->data;
-	switch (cmd) {
-		case FE_GET_INFO:
-			deb_info("FE_GET_INFO\n");
-			memcpy(arg, &dib3000mb_info, sizeof(struct dvb_frontend_info));
-			return 0;
-			break;
-
-		case FE_READ_STATUS:
-			deb_info("FE_READ_STATUS\n");
-			return dib3000mb_read_status(state,(fe_status_t *)arg);
-			break;
-
-		case FE_READ_BER:
-			deb_info("FE_READ_BER\n");
-			return dib3000mb_read_ber(state,(u32 *)arg);
-			break;
-
-		case FE_READ_SIGNAL_STRENGTH:
-			deb_info("FE_READ_SIG_STRENGTH\n");
-			return dib3000mb_read_signal_strength(state,(u16 *) arg);
-			break;
-
-		case FE_READ_SNR:
-			deb_info("FE_READ_SNR\n");
-			return dib3000mb_read_snr(state,(u16 *) arg);
-			break;
-
-		case FE_READ_UNCORRECTED_BLOCKS:
-			deb_info("FE_READ_UNCORRECTED_BLOCKS\n");
-			return dib3000mb_read_unc_blocks(state,(u32 *) arg);
-			break;
-
-		case FE_SET_FRONTEND:
-			deb_info("FE_SET_FRONTEND\n");
-			return dib3000mb_set_frontend(state,(struct dvb_frontend_parameters *) arg,1);
-			break;
-
-		case FE_GET_FRONTEND:
-			deb_info("FE_GET_FRONTEND\n");
-			return dib3000mb_get_frontend(state,(struct dvb_frontend_parameters *) arg);
-			break;
+	return dib3000mb_fe_init(fe, 0);
+}
 
-		case FE_SLEEP:
-			deb_info("FE_SLEEP\n");
-			return dib3000mb_sleep(state);
-			break;
+static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+{
+	return dib3000mb_set_frontend(fe, fep, 1);
+}
 
-		case FE_INIT:
-			deb_info("FE_INIT\n");
-			return dib3000mb_fe_init(state,0);
-			break;
+static void dib3000mb_release(struct dvb_frontend* fe)
+{
+	struct dib3000mb_state *state = (struct dib3000mb_state*) fe->demodulator_priv;
+	kfree(state);
+}
 
-		case FE_GET_TUNE_SETTINGS:
-			deb_info("GET_TUNE_SETTINGS");
-			return dib3000mb_fe_get_tune_settings(state, (struct
-						dvb_frontend_tune_settings *) arg);
+/* pid filter and transfer stuff */
 
+/* fetch a pid from pid_list */
+static int dib3000_get_pid_index(struct dib3000_pid pid_list[],
+		int num_pids, int pid, spinlock_t *pid_list_lock,int onoff)
+{
+	int i,ret = -1;
+	unsigned long flags;
+
+	spin_lock_irqsave(pid_list_lock,flags);
+	for (i=0; i < num_pids; i++)
+		if (onoff) {
+			if (!pid_list[i].active) {
+				pid_list[i].pid = pid;
+				pid_list[i].active = 1;
+				ret = i;
 			break;
-		case FE_SET_TONE:
-		case FE_SET_VOLTAGE:
-		default:
-			return -EOPNOTSUPP;
+			}
+		} else {
+			if (pid_list[i].active && pid_list[i].pid == pid) {
+				pid_list[i].pid = 0;
+				pid_list[i].active = 0;
+				ret = i;
 			break;
 	}
-	return 0;
 }
 
-static struct i2c_client client_template;
+	spin_unlock_irqrestore(pid_list_lock,flags);
+	return ret;
+}
 
-static int dib3000mb_attach_adapter(struct i2c_adapter *adapter)
+static int dib3000mb_pid_control(struct dvb_frontend *fe,int pid,int onoff)
 {
-	struct i2c_client *client;
-	struct dib3000mb_state *state;
-	int ret = -ENOMEM;
+	struct dib3000mb_state *state = fe->demodulator_priv;
+	int index = dib3000_get_pid_index(state->pid_list, DIB3000MB_NUM_PIDS, pid, &state->pid_list_lock,onoff);
+	pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
 
-	deb_info("i2c probe with adapter '%s'.\n",adapter->name);
+	deb_info("setting pid 0x%x on index %d\n",pid,index);
 
-	if ((state = kmalloc(sizeof(struct dib3000mb_state),GFP_KERNEL)) == NULL)
+	if (index >= 0) {
+		wr(index+DIB3000MB_REG_FIRST_PID,pid);
+	} else {
+		err("no more pids for filtering.");
 		return -ENOMEM;
+	}
+	return 0;
+}
 
+static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+	struct dib3000mb_state *state = (struct dib3000mb_state*) fe->demodulator_priv;
 
-	if ((client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL)
-		goto i2c_kmalloc_err;
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-
-	client->adapter = adapter;
-	client->addr = 0x10;
-	state->i2c = client;
-
-	i2c_set_clientdata(client,state);
-
-	state->manufactor_id = dib3000mb_read_reg(client, DIB3000MB_REG_MANUFACTOR_ID);
-	if (state->manufactor_id != 0x01b3) {
-		ret = -ENODEV;
-		goto probe_err;
+	if (onoff) {
+		wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE);
+	} else {
+		wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT);
 	}
-	
-	state->device_id = dib3000mb_read_reg(client,DIB3000MB_REG_DEVICE_ID);
-	if (state->device_id != 0x3000) {
-		ret = -ENODEV;
-		goto probe_err;
+	return 0;
 	}
 
-	info("found a DiBCom (0x%04x) 3000-MB DVB-T frontend (ver: %x).",
-			state->manufactor_id, state->device_id);
-	
-	if ((ret = i2c_attach_client(client)))
-		goto i2c_attach_err;
-
-	if (state->dvb == NULL)
-		goto i2c_attach_err;
-
-	if ((ret = dvb_register_frontend(dib3000mb_ioctl, state->dvb, state,
-					     &dib3000mb_info, THIS_MODULE)))
-		goto dvb_fe_err;
-
-
-	goto success;
-dvb_fe_err:
-	i2c_detach_client(client);
-i2c_attach_err:
-probe_err:
-	kfree(client);
-i2c_kmalloc_err:
-	kfree(state);
-	return ret;
-success:
+static int dib3000mb_pid_filter(struct dvb_frontend *fe, int onoff)
+{
+	//struct dib3000mb_state *state = fe->demodulator_priv;
+	/* switch it off and on */
 	return 0;
-}
+	}
 
+static struct dvb_frontend_ops dib3000mb_ops;
 
-static int dib3000mb_detach_client(struct i2c_client *client)
+struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+				      struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops)
 {
-	struct dib3000mb_state *state = i2c_get_clientdata(client);
+	struct dib3000mb_state* state = NULL;
+	int i;
 
-	deb_info("i2c detach\n");
+	/* allocate memory for the internal state */
+	state = (struct dib3000mb_state*) kmalloc(sizeof(struct dib3000mb_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
 
-	dvb_unregister_frontend(dib3000mb_ioctl, state->dvb);
-	i2c_detach_client(client);
-	kfree(client);
-	kfree(state);
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops));
 
-	return 0;
-}
+	/* check for the correct demod */
+	if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+		goto error;
 
-static int dib3000mb_command(struct i2c_client *client,
-			      unsigned int cmd, void *arg)
-{
-	struct dib3000mb_state *state = i2c_get_clientdata(client);
-	deb_info("i2c command.\n");
-	switch(cmd) {
-		case FE_REGISTER:
-			state->dvb = arg;
-			break;
-		case FE_UNREGISTER:
-			state->dvb = NULL;
-			break;
-		default:
-			return -EOPNOTSUPP;
-	}
+	if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID)
+		goto error;
 
-	return 0;
+	/* initialize the id_list */
+	deb_info("initializing %d pids for the pid_list.\n",DIB3000MB_NUM_PIDS);
+	state->pid_list_lock = SPIN_LOCK_UNLOCKED;
+	memset(state->pid_list,0,DIB3000MB_NUM_PIDS*(sizeof(struct dib3000_pid)));
+	for (i=0; i < DIB3000MB_NUM_PIDS; i++) {
+		state->pid_list[i].pid = 0;
+		state->pid_list[i].active = 0;
 }
+	state->feedcount = 0;
 
-static struct i2c_driver driver = {
-	.owner		= THIS_MODULE,
-	.name		= "dib3000mb",
-	.id			= I2C_DRIVERID_DVBFE_DIB3000MB,
-	.flags		= I2C_DF_NOTIFY,
-	.attach_adapter	= dib3000mb_attach_adapter,
-	.detach_client	= dib3000mb_detach_client,
-	.command	= dib3000mb_command,
-};
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
 
-static struct i2c_client client_template = {
-	.name		= "dib3000mb",
-	.flags		= I2C_CLIENT_ALLOW_USE,
-	.driver		= &driver,
-};
+	/* set the xfer operations */
+	xfer_ops->pid_filter = dib3000mb_pid_filter;
+	xfer_ops->fifo_ctrl = dib3000mb_fifo_control;
+	xfer_ops->pid_ctrl = dib3000mb_pid_control;
 
-/* module stuff */
-static int __init dib3000mb_init(void)
-{
-	deb_info("debugging level: %d\n",debug);
-	return i2c_add_driver(&driver);
-}
+	return &state->frontend;
 
-static void __exit dib3000mb_exit(void)
-{
-	i2c_del_driver(&driver);
-}
+error:
+	if (state)
+	kfree(state);
+	return NULL;
+	}
 
-module_init (dib3000mb_init);
-module_exit (dib3000mb_exit);
+static struct dvb_frontend_ops dib3000mb_ops = {
+
+	.info = {
+		.name			= "DiBcom 3000-MB DVB-T",
+		.type 			= FE_OFDM,
+		.frequency_min 		= 44250000,
+		.frequency_max 		= 867250000,
+		.frequency_stepsize	= 62500,
+		.caps = FE_CAN_INVERSION_AUTO |
+				FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+				FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = dib3000mb_release,
+
+	.init = dib3000mb_fe_init_nonmobile,
+	.sleep = dib3000mb_sleep,
+
+	.set_frontend = dib3000mb_set_frontend_and_tuner,
+	.get_frontend = dib3000mb_get_frontend,
+	.get_tune_settings = dib3000mb_fe_get_tune_settings,
+
+	.read_status = dib3000mb_read_status,
+	.read_ber = dib3000mb_read_ber,
+	.read_signal_strength = dib3000mb_read_signal_strength,
+	.read_snr = dib3000mb_read_snr,
+	.read_ucblocks = dib3000mb_read_unc_blocks,
+};
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000mb_attach);
diff -L drivers/media/dvb/frontends/dib3000mb.h -puN drivers/media/dvb/frontends/dib3000mb.h~dvb-frontend-driver-refactoring /dev/null
--- 25/drivers/media/dvb/frontends/dib3000mb.h
+++ /dev/null	2003-09-15 06:40:47.000000000 -0700
@@ -1,657 +0,0 @@
-/*
- * dib3000mb.h
- *
- * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
- *
- *	This program is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU General Public License as
- *	published by the Free Software Foundation, version 2.
- *
- * 
- *
- * for more information see dib3000mb.c .
- */
-
-#ifndef __DIB3000MB_H_INCLUDED__
-#define __DIB3000MB_H_INCLUDED__
-
-/* info and err, taken from usb.h, if there is anything available like by default,
- * please change !
- */
-#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg)
-#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg)
-#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg)
-
-/* register addresses and some of their default values */
-
-/* restart subsystems */
-#define DIB3000MB_REG_RESTART			(     0)
-
-#define DIB3000MB_RESTART_OFF			(     0)
-#define DIB3000MB_RESTART_AUTO_SEARCH		(1 << 1)
-#define DIB3000MB_RESTART_CTRL				(1 << 2)
-#define DIB3000MB_RESTART_AGC				(1 << 3)
-
-/* FFT size */
-#define DIB3000MB_REG_FFT				(     1)
-#define DIB3000MB_FFT_2K					(     0)
-#define DIB3000MB_FFT_8K					(     1)
-#define DIB3000MB_FFT_AUTO					(     1)
-
-/* Guard time */
-#define DIB3000MB_REG_GUARD_TIME		(     2)
-#define DIB3000MB_GUARD_TIME_1_32			(     0)
-#define DIB3000MB_GUARD_TIME_1_16			(     1)
-#define DIB3000MB_GUARD_TIME_1_8			(     2)
-#define DIB3000MB_GUARD_TIME_1_4			(     3)
-#define DIB3000MB_GUARD_TIME_AUTO			(     0)
-
-/* QAM */
-#define DIB3000MB_REG_QAM				(     3)
-#define DIB3000MB_QAM_QPSK					(     0)
-#define DIB3000MB_QAM_QAM16					(     1)
-#define DIB3000MB_QAM_QAM64					(     2)
-#define DIB3000MB_QAM_RESERVED				(     3)
-
-/* Alpha coefficient high priority Viterbi algorithm */
-#define DIB3000MB_REG_VIT_ALPHA			(     4)
-#define DIB3000MB_VIT_ALPHA_OFF				(     0)
-#define DIB3000MB_VIT_ALPHA_1				(     1)
-#define DIB3000MB_VIT_ALPHA_2				(     2)
-#define DIB3000MB_VIT_ALPHA_4				(     4)
-#define DIB3000MB_VIT_ALPHA_AUTO			(     7)
-
-/* spectrum inversion */
-#define DIB3000MB_REG_DDS_INV			(     5)
-#define DIB3000MB_DDS_INV_OFF				(     0)
-#define DIB3000MB_DDS_INV_ON				(     1)
-
-/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */
-#define DIB3000MB_REG_DDS_FREQ_MSB		(     6)
-#define DIB3000MB_REG_DDS_FREQ_LSB		(     7)
-#define DIB3000MB_DDS_FREQ_MSB				(   178)
-#define DIB3000MB_DDS_FREQ_LSB				(  8990)
-
-/* timing frequency (carrier spacing) */
-#define DIB3000MB_REG_TIMING_FREQ_MSB	(     8)
-#define DIB3000MB_REG_TIMING_FREQ_LSB	(     9)
-
-static u16 dib3000mb_reg_timing_freq[] = {
-	DIB3000MB_REG_TIMING_FREQ_MSB, DIB3000MB_REG_TIMING_FREQ_LSB
-};
-static u16 dib3000mb_timing_freq[][2] = {
-	{ 126 , 48873 }, /* 6 MHz */
-	{ 147 , 57019 }, /* 7 MHz */
-	{ 168 , 65164 }, /* 8 MHz */
-};
-
-/* impulse noise parameter */
-#define DIB3000MB_REG_IMPNOISE_10		(    10)
-#define DIB3000MB_REG_IMPNOISE_11		(    11)
-#define DIB3000MB_REG_IMPNOISE_12		(    12)
-#define DIB3000MB_REG_IMPNOISE_13		(    13)
-#define DIB3000MB_REG_IMPNOISE_14		(    14)
-#define DIB3000MB_REG_IMPNOISE_15		(    15)
-/* 36 ??? */
-#define DIB3000MB_REG_IMPNOISE_36		(    36)
-
-enum dib3000mb_impulse_noise_type {
-	DIB3000MB_IMPNOISE_OFF,
-	DIB3000MB_IMPNOISE_MOBILE,
-	DIB3000MB_IMPNOISE_FIXED,
-	DIB3000MB_IMPNOISE_DEFAULT
-};
-
-static u16 dib3000mb_reg_impulse_noise[] = {
- DIB3000MB_REG_IMPNOISE_10, DIB3000MB_REG_IMPNOISE_11,
- DIB3000MB_REG_IMPNOISE_12, DIB3000MB_REG_IMPNOISE_15,
- DIB3000MB_REG_IMPNOISE_36
-};
-
-static u16 dib3000mb_impulse_noise_values[][5] = {
-	{ 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */
-	{ 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */
-	{ 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */
-	{ 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */
-};
-
-/*
- * Dual Automatic-Gain-Control
- * - gains RF in tuner (AGC1)
- * - gains IF after filtering (AGC2)
- */
-
-/* also from 16 to 18 */
-#define DIB3000MB_REG_AGC_GAIN_19		(    19)
-#define DIB3000MB_REG_AGC_GAIN_20		(    20)
-#define DIB3000MB_REG_AGC_GAIN_21		(    21)
-#define DIB3000MB_REG_AGC_GAIN_22		(    22)
-#define DIB3000MB_REG_AGC_GAIN_23		(    23)
-#define DIB3000MB_REG_AGC_GAIN_24		(    24)
-#define DIB3000MB_REG_AGC_GAIN_25		(    25)
-#define DIB3000MB_REG_AGC_GAIN_26		(    26)
-#define DIB3000MB_REG_AGC_GAIN_27		(    27)
-#define DIB3000MB_REG_AGC_GAIN_28		(    28)
-#define DIB3000MB_REG_AGC_GAIN_29		(    29)
-#define DIB3000MB_REG_AGC_GAIN_30		(    30)
-#define DIB3000MB_REG_AGC_GAIN_31		(    31)
-#define DIB3000MB_REG_AGC_GAIN_32		(    32)
-
-static u16 dib3000mb_reg_agc_gain[] = {
-  DIB3000MB_REG_AGC_GAIN_19, DIB3000MB_REG_AGC_GAIN_20, DIB3000MB_REG_AGC_GAIN_21,
-  DIB3000MB_REG_AGC_GAIN_22, DIB3000MB_REG_AGC_GAIN_23, DIB3000MB_REG_AGC_GAIN_24,
-  DIB3000MB_REG_AGC_GAIN_25, DIB3000MB_REG_AGC_GAIN_26, DIB3000MB_REG_AGC_GAIN_27,
-  DIB3000MB_REG_AGC_GAIN_28, DIB3000MB_REG_AGC_GAIN_29, DIB3000MB_REG_AGC_GAIN_30,
-  DIB3000MB_REG_AGC_GAIN_31, DIB3000MB_REG_AGC_GAIN_32 };
-
-static u16 dib3000mb_default_agc_gain[] =
-	{ 0x0001, 52429,   623, 128, 166, 195, 61,   /* RF ??? */
-	  0x0001, 53766, 38011,   0,  90,  33, 23 }; /* IF ??? */
-
-/* phase noise */
-#define DIB3000MB_REG_PHASE_NOISE_33		(    33)
-#define DIB3000MB_REG_PHASE_NOISE_34		(    34)
-#define DIB3000MB_REG_PHASE_NOISE_35		(    35)
-#define DIB3000MB_REG_PHASE_NOISE_36		(    36)
-#define DIB3000MB_REG_PHASE_NOISE_37		(    37)
-#define DIB3000MB_REG_PHASE_NOISE_38		(    38)
-
-/* DIB3000MB_REG_PHASE_NOISE_36 is set when setting the impulse noise */
-static u16 dib3000mb_reg_phase_noise[] = {
-	DIB3000MB_REG_PHASE_NOISE_33, DIB3000MB_REG_PHASE_NOISE_34, DIB3000MB_REG_PHASE_NOISE_35,
-	DIB3000MB_REG_PHASE_NOISE_37, DIB3000MB_REG_PHASE_NOISE_38
-};
-
-static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 };
-
-/* lock duration */
-#define DIB3000MB_REG_LOCK_DURATION_39	(    39)
-#define DIB3000MB_REG_LOCK_DURATION_40	(    40)
-
-static u16 dib3000mb_reg_lock_duration[] = {
-	DIB3000MB_REG_LOCK_DURATION_39, DIB3000MB_REG_LOCK_DURATION_40
-};
-
-static u16 dib3000mb_default_lock_duration[] = { 135, 135 };
-
-/* AGC loop bandwidth */
-
-#define DIB3000MB_REG_AGC_BW_43			(    43)
-#define DIB3000MB_REG_AGC_BW_44			(    44)
-#define DIB3000MB_REG_AGC_BW_45			(    45)
-#define DIB3000MB_REG_AGC_BW_46			(    46)
-#define DIB3000MB_REG_AGC_BW_47			(    47)
-#define DIB3000MB_REG_AGC_BW_48			(    48)
-#define DIB3000MB_REG_AGC_BW_49			(    49)
-#define DIB3000MB_REG_AGC_BW_50			(    50)
-
-static u16 dib3000mb_reg_agc_bandwidth[] = {
-	DIB3000MB_REG_AGC_BW_43, DIB3000MB_REG_AGC_BW_44, DIB3000MB_REG_AGC_BW_45,
-	DIB3000MB_REG_AGC_BW_46, DIB3000MB_REG_AGC_BW_47, DIB3000MB_REG_AGC_BW_48,
-	DIB3000MB_REG_AGC_BW_49, DIB3000MB_REG_AGC_BW_50
-};
-
-static u16 dib3000mb_agc_bandwidth_low[]  =
-	{ 2088, 10, 2088, 10, 3448, 5, 3448, 5 };
-static u16 dib3000mb_agc_bandwidth_high[] =
-	{ 2349,  5, 2349,  5, 2586, 2, 2586, 2 };
-
-/*
- * lock0 definition (coff_lock)
- */
-#define DIB3000MB_REG_LOCK0_MASK		(    51)
-#define DIB3000MB_LOCK0_DEFAULT				(     4)
-
-/*
- * lock1 definition (cpil_lock)
- * for auto search
- * which values hide behind the lock masks
- */
-#define DIB3000MB_REG_LOCK1_MASK		(    52)
-#define DIB3000MB_LOCK1_SEARCH_4			(0x0004)
-#define DIB3000MB_LOCK1_SEARCH_2048			(0x0800)
-#define DIB3000MB_LOCK1_DEFAULT				(0x0001)
-
-/*
- * lock2 definition (fec_lock) */
-#define DIB3000MB_REG_LOCK2_MASK		(    53)
-#define DIB3000MB_LOCK2_DEFAULT				(0x0080)
-
-/*
- * SEQ ? what was that again ... :)
- * changes when, inversion, guard time and fft is
- * either automatically detected or not
- */
-#define DIB3000MB_REG_SEQ				(    54)
-
-/* all values have been set manually */
-static u16 dib3000mb_seq[2][2][2] = 	/* fft,gua,   inv   */
-	{ /* fft */
-		{ /* gua */
-			{ 0, 1 },					/*  0   0   { 0,1 } */
-			{ 3, 9 },					/*  0   1   { 0,1 } */
-		},
-		{
-			{ 2, 5 },					/*  1   0   { 0,1 } */
-			{ 6, 11 },					/*  1   1   { 0,1 } */
-		}
-	};
-
-/* bandwidth */
-#define DIB3000MB_REG_BW_55				(    55)
-#define DIB3000MB_REG_BW_56				(    56)
-#define DIB3000MB_REG_BW_57				(    57)
-#define DIB3000MB_REG_BW_58				(    58)
-#define DIB3000MB_REG_BW_59				(    59)
-#define DIB3000MB_REG_BW_60				(    60)
-#define DIB3000MB_REG_BW_61				(    61)
-#define DIB3000MB_REG_BW_62				(    62)
-#define DIB3000MB_REG_BW_63				(    63)
-#define DIB3000MB_REG_BW_64				(    64)
-#define DIB3000MB_REG_BW_65				(    65)
-#define DIB3000MB_REG_BW_66				(    66)
-#define DIB3000MB_REG_BW_67				(    67)
-
-static u16 dib3000mb_reg_bandwidth[] = {
-	DIB3000MB_REG_BW_55, DIB3000MB_REG_BW_56, DIB3000MB_REG_BW_57,
-	DIB3000MB_REG_BW_58, DIB3000MB_REG_BW_59, DIB3000MB_REG_BW_60,
-	DIB3000MB_REG_BW_61, DIB3000MB_REG_BW_62, DIB3000MB_REG_BW_63,
-	DIB3000MB_REG_BW_64, DIB3000MB_REG_BW_65, DIB3000MB_REG_BW_66,
-	DIB3000MB_REG_BW_67
-};
-
-static u16 dib3000mb_bandwidth_6mhz[] =
-	{ 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 };
-
-static u16 dib3000mb_bandwidth_7mhz[] =
-	{ 0, 28, 64421,  96, 39973, 483,  3255, 0, 1000, 0, 1010, 1, 45264 };
-
-static u16 dib3000mb_bandwidth_8mhz[] =
-	{ 0, 25, 23600,  84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 };
-
-#define DIB3000MB_REG_UNK_68				(    68)
-#define DIB3000MB_UNK_68						(     0)
-
-#define DIB3000MB_REG_UNK_69				(    69)
-#define DIB3000MB_UNK_69						(     0)
-
-#define DIB3000MB_REG_UNK_71				(    71)
-#define DIB3000MB_UNK_71						(     0)
-
-#define DIB3000MB_REG_UNK_77				(    77)
-#define DIB3000MB_UNK_77						(     6)
-
-#define DIB3000MB_REG_UNK_78				(    78)
-#define DIB3000MB_UNK_78						(0x0080)
-
-/* isi */
-#define DIB3000MB_REG_ISI				(    79)
-#define DIB3000MB_ISI_ACTIVATE				(     0)
-#define DIB3000MB_ISI_INHIBIT				(     1)
-
-/* sync impovement */
-#define DIB3000MB_REG_SYNC_IMPROVEMENT	(    84)
-#define DIB3000MB_SYNC_IMPROVE_2K_1_8		(     3)
-#define DIB3000MB_SYNC_IMPROVE_DEFAULT		(     0)
-
-/* phase noise compensation inhibition */
-#define DIB3000MB_REG_PHASE_NOISE		(    87)
-#define DIB3000MB_PHASE_NOISE_DEFAULT	(     0)
-
-#define DIB3000MB_REG_UNK_92				(    92)
-#define DIB3000MB_UNK_92						(0x0080)
-
-#define DIB3000MB_REG_UNK_96				(    96)
-#define DIB3000MB_UNK_96						(0x0010)
-
-#define DIB3000MB_REG_UNK_97				(    97)
-#define DIB3000MB_UNK_97						(0x0009)
-
-/* mobile mode ??? */
-#define DIB3000MB_REG_MOBILE_MODE		(   101)
-#define DIB3000MB_MOBILE_MODE_ON				(     1)
-#define DIB3000MB_MOBILE_MODE_OFF			(     0)
-
-#define DIB3000MB_REG_UNK_106			(   106)
-#define DIB3000MB_UNK_106					(0x0080)
-
-#define DIB3000MB_REG_UNK_107			(   107)
-#define DIB3000MB_UNK_107					(0x0080)
-
-#define DIB3000MB_REG_UNK_108			(   108)
-#define DIB3000MB_UNK_108					(0x0080)
-
-/* fft */
-#define DIB3000MB_REG_UNK_121			(   121)
-#define DIB3000MB_UNK_121_2K				(     7)
-#define DIB3000MB_UNK_121_DEFAULT			(     5)
-
-#define DIB3000MB_REG_UNK_122			(   122)
-#define DIB3000MB_UNK_122					(  2867)
-
-/* QAM for mobile mode */
-#define DIB3000MB_REG_MOBILE_MODE_QAM	(   126)
-#define DIB3000MB_MOBILE_MODE_QAM_64			(     3)
-#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16	(     1)
-#define DIB3000MB_MOBILE_MODE_QAM_OFF		(     0)
-
-/*
- * data diversity when having more than one chip on-board
- * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY
- */
-#define DIB3000MB_REG_DATA_IN_DIVERSITY		(   127)
-#define DIB3000MB_DATA_DIVERSITY_IN_OFF			(     0)
-#define DIB3000MB_DATA_DIVERSITY_IN_ON			(     2)
-
-/* vit hrch */
-#define DIB3000MB_REG_VIT_HRCH			(   128)
-#define DIB3000MB_VIT_HRCH_ON				(     1)
-#define DIB3000MB_VIT_HRCH_OFF				(     0)
-
-/* vit code rate */
-#define DIB3000MB_REG_VIT_CODE_RATE		(   129)
-
-/* forward error correction code rates */
-#define DIB3000MB_FEC_1_2					(     1)
-#define DIB3000MB_FEC_2_3					(     2)
-#define DIB3000MB_FEC_3_4					(     3)
-#define DIB3000MB_FEC_5_6					(     5)
-#define DIB3000MB_FEC_7_8					(     7)
-
-/* vit select hp */
-#define DIB3000MB_REG_VIT_HP			(   130)
-#define DIB3000MB_VIT_LP					(     0)
-#define DIB3000MB_VIT_HP					(     1)
-
-/* time frame for Bit-Error-Rate calculation */
-#define DIB3000MB_REG_BERLEN			(   135)
-#define DIB3000MB_BERLEN_LONG				(     0)
-#define DIB3000MB_BERLEN_DEFAULT			(     1)
-#define DIB3000MB_BERLEN_MEDIUM				(     2)
-#define DIB3000MB_BERLEN_SHORT				(     3)
-
-/* 142 - 152 FIFO parameters
- * which is what ?
- */
-
-#define DIB3000MB_REG_FIFO_142			(   142)
-#define DIB3000MB_FIFO_142					(     0)
-
-/* MPEG2 TS output mode */
-#define DIB3000MB_REG_MPEG2_OUT_MODE	(   143)
-#define DIB3000MB_MPEG2_OUT_MODE_204		(     0)
-#define DIB3000MB_MPEG2_OUT_MODE_188		(     1)
-
-#define DIB3000MB_REG_FIFO_144			(   144)
-#define DIB3000MB_FIFO_144					(     1)
-
-#define DIB3000MB_REG_FIFO				(   145)
-#define DIB3000MB_FIFO_INHIBIT				(     1)
-#define DIB3000MB_FIFO_ACTIVATE				(     0)
-
-#define DIB3000MB_REG_FIFO_146			(   146)
-#define DIB3000MB_FIFO_146					(     3)
-
-#define DIB3000MB_REG_FIFO_147			(   147)
-#define DIB3000MB_FIFO_147					(0x0100)
-
-/*
- * pidfilter
- * it is not a hardware pidfilter but a filter which drops all pids
- * except the ones set. Necessary because of the limited USB1.1 bandwidth.
- */
-#define DIB3000MB_REG_FILTER_PID_0		(   153)
-#define DIB3000MB_REG_FILTER_PID_1		(   154)
-#define DIB3000MB_REG_FILTER_PID_2		(   155)
-#define DIB3000MB_REG_FILTER_PID_3		(   156)
-#define DIB3000MB_REG_FILTER_PID_4		(   157)
-#define DIB3000MB_REG_FILTER_PID_5		(   158)
-#define DIB3000MB_REG_FILTER_PID_6		(   159)
-#define DIB3000MB_REG_FILTER_PID_7		(   160)
-#define DIB3000MB_REG_FILTER_PID_8		(   161)
-#define DIB3000MB_REG_FILTER_PID_9		(   162)
-#define DIB3000MB_REG_FILTER_PID_10		(   163)
-#define DIB3000MB_REG_FILTER_PID_11		(   164)
-#define DIB3000MB_REG_FILTER_PID_12		(   165)
-#define DIB3000MB_REG_FILTER_PID_13		(   166)
-#define DIB3000MB_REG_FILTER_PID_14		(   167)
-#define DIB3000MB_REG_FILTER_PID_15		(   168)
-
-#define DIB3000MB_ACTIVATE_FILTERING			(0x2000)
-
-/*
- * output mode
- * USB devices have to use 'slave'-mode
- * see also DIB3000MB_REG_ELECT_OUT_MODE
- */
-#define DIB3000MB_REG_OUTPUT_MODE		(   169)
-#define DIB3000MB_OUTPUT_MODE_GATED_CLK		(     0)
-#define DIB3000MB_OUTPUT_MODE_CONT_CLK		(     1)
-#define DIB3000MB_OUTPUT_MODE_SERIAL		(     2)
-#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY	(     5)
-#define DIB3000MB_OUTPUT_MODE_SLAVE			(     6)
-
-/* irq event mask */
-#define DIB3000MB_REG_IRQ_EVENT_MASK		(   170)
-#define DIB3000MB_IRQ_EVENT_MASK				(     0)
-
-/* filter coefficients */
-#define DIB3000MB_REG_FILTER_COEF_171	(   171)
-#define DIB3000MB_REG_FILTER_COEF_172	(   172)
-#define DIB3000MB_REG_FILTER_COEF_173	(   173)
-#define DIB3000MB_REG_FILTER_COEF_174	(   174)
-#define DIB3000MB_REG_FILTER_COEF_175	(   175)
-#define DIB3000MB_REG_FILTER_COEF_176	(   176)
-#define DIB3000MB_REG_FILTER_COEF_177	(   177)
-#define DIB3000MB_REG_FILTER_COEF_178	(   178)
-#define DIB3000MB_REG_FILTER_COEF_179	(   179)
-#define DIB3000MB_REG_FILTER_COEF_180	(   180)
-#define DIB3000MB_REG_FILTER_COEF_181	(   181)
-#define DIB3000MB_REG_FILTER_COEF_182	(   182)
-#define DIB3000MB_REG_FILTER_COEF_183	(   183)
-#define DIB3000MB_REG_FILTER_COEF_184	(   184)
-#define DIB3000MB_REG_FILTER_COEF_185	(   185)
-#define DIB3000MB_REG_FILTER_COEF_186	(   186)
-#define DIB3000MB_REG_FILTER_COEF_187	(   187)
-#define DIB3000MB_REG_FILTER_COEF_188	(   188)
-#define DIB3000MB_REG_FILTER_COEF_189	(   189)
-#define DIB3000MB_REG_FILTER_COEF_190	(   190)
-#define DIB3000MB_REG_FILTER_COEF_191	(   191)
-#define DIB3000MB_REG_FILTER_COEF_192	(   192)
-#define DIB3000MB_REG_FILTER_COEF_193	(   193)
-#define DIB3000MB_REG_FILTER_COEF_194	(   194)
-
-static u16 dib3000mb_reg_filter_coeffs[] = {
-	DIB3000MB_REG_FILTER_COEF_171, DIB3000MB_REG_FILTER_COEF_172, DIB3000MB_REG_FILTER_COEF_173,
-	DIB3000MB_REG_FILTER_COEF_174, DIB3000MB_REG_FILTER_COEF_175, DIB3000MB_REG_FILTER_COEF_176,
-	DIB3000MB_REG_FILTER_COEF_177, DIB3000MB_REG_FILTER_COEF_178, DIB3000MB_REG_FILTER_COEF_179,
-	DIB3000MB_REG_FILTER_COEF_180, DIB3000MB_REG_FILTER_COEF_181, DIB3000MB_REG_FILTER_COEF_182,
-	DIB3000MB_REG_FILTER_COEF_183, DIB3000MB_REG_FILTER_COEF_184, DIB3000MB_REG_FILTER_COEF_185,
-	DIB3000MB_REG_FILTER_COEF_186,                                DIB3000MB_REG_FILTER_COEF_188,
-	DIB3000MB_REG_FILTER_COEF_189, DIB3000MB_REG_FILTER_COEF_190, DIB3000MB_REG_FILTER_COEF_191,
-	DIB3000MB_REG_FILTER_COEF_192,                                DIB3000MB_REG_FILTER_COEF_194
-};
-
-static u16 dib3000mb_filter_coeffs[] = {
-	 226,  160,   29,
- 	 979,  998,   19,
-	  22, 1019, 1006,
-	1022,   12,    6,
-	1017, 1017,    3,
-	   6,       1019,
-	1021,    2,    3,
-	   1,          0,
-};
-
-/*
- * mobile algorithm (when you are moving with your device)
- * but not faster than 90 km/h
- */
-#define DIB3000MB_REG_MOBILE_ALGO		(   195)
-#define DIB3000MB_MOBILE_ALGO_ON			(     0)
-#define DIB3000MB_MOBILE_ALGO_OFF			(     1)
-
-/* multiple demodulators algorithm */
-#define DIB3000MB_REG_MULTI_DEMOD_MSB	(   206)
-#define DIB3000MB_REG_MULTI_DEMOD_LSB	(   207)
-
-/* terminator, no more demods */
-#define DIB3000MB_MULTI_DEMOD_MSB			( 32767)
-#define DIB3000MB_MULTI_DEMOD_LSB			(  4095)
-
-/* bring the device into a known  */
-#define DIB3000MB_REG_RESET_DEVICE		(  1024)
-#define DIB3000MB_RESET_DEVICE				(0x812c)
-#define DIB3000MB_RESET_DEVICE_RST			(     0)
-
-/* identification registers, manufactor an the device */
-#define DIB3000MB_REG_MANUFACTOR_ID		(  1025)
-#define DIB3000MB_MANUFACTOR_ID_DIBCOM		(0x01B3)
-
-#define DIB3000MB_REG_DEVICE_ID			(  1026)
-#define DIB3000MB_DEVICE_ID					(0x3000)
-
-/* hardware clock configuration */
-#define DIB3000MB_REG_CLOCK				(  1027)
-#define DIB3000MB_CLOCK_DEFAULT				(0x9000)
-#define DIB3000MB_CLOCK_DIVERSITY			(0x92b0)
-
-/* power down config */
-#define DIB3000MB_REG_POWER_CONTROL		(  1028)
-#define DIB3000MB_POWER_DOWN				(     1)
-#define DIB3000MB_POWER_UP					(     0)
-
-/* electrical output mode */
-#define DIB3000MB_REG_ELECT_OUT_MODE	(  1029)
-#define DIB3000MB_ELECT_OUT_MODE_OFF		(     0)
-#define DIB3000MB_ELECT_OUT_MODE_ON			(     1)
-
-/* set the tuner i2c address */
-#define DIB3000MB_REG_TUNER				(  1089)
-#define DIB3000MB_TUNER_ADDR_DEFAULT		(   194)
-#define DIB3000MB_ACTIVATE_TUNER_XFER(a)	(0xffff & (a << 7))
-#define DIB3000MB_DEACTIVATE_TUNER_XFER(a)	(0xffff & ((a << 7) + 0x80))
-
-/* monitoring registers (read only) */
-
-/* agc loop locked (size: 1) */
-#define DIB3000MB_REG_AGC_LOCK			(   324)
-
-/* agc power (size: 16) */
-#define DIB3000MB_REG_AGC_POWER			(   325)
-
-/* agc1 value (16) */
-#define DIB3000MB_REG_AGC1_VALUE		(   326)
-
-/* agc2 value (16) */
-#define DIB3000MB_REG_AGC2_VALUE		(   327)
-
-/* total RF power (16), can be used for signal strength */
-#define DIB3000MB_REG_RF_POWER			(   328)
-
-/* dds_frequency with offset (24) */
-#define DIB3000MB_REG_DDS_VALUE_MSB		(   339)
-#define DIB3000MB_REG_DDS_VALUE_LSB		(   340)
-
-/* timing offset signed (24) */
-#define DIB3000MB_REG_TIMING_OFFSET_MSB	(   341)
-#define DIB3000MB_REG_TIMING_OFFSET_LSB	(   342)
-
-/* fft start position (13) */
-#define DIB3000MB_REG_FFT_WINDOW_POS	(   353)
-
-/* carriers locked (1) */
-#define DIB3000MB_REG_CARRIER_LOCK		(   355)
-
-/* noise power (24) */
-#define DIB3000MB_REG_NOISE_POWER_MSB	(   372)
-#define DIB3000MB_REG_NOISE_POWER_LSB	(   373)
-
-#define DIB3000MB_REG_MOBILE_NOISE_MSB	(   374)
-#define DIB3000MB_REG_MOBILE_NOISE_LSB	(   375)
-
-/*
- * signal power (16), this and the above can be
- * used to calculate the signal/noise - ratio
- */
-#define DIB3000MB_REG_SIGNAL_POWER		(   380)
-
-/* mer (24) */
-#define DIB3000MB_REG_MER_MSB			(   381)
-#define DIB3000MB_REG_MER_LSB			(   382)
-
-/*
- * Transmission Parameter Signalling (TPS)
- * the following registers can be used to get TPS-information.
- * The values are according to the DVB-T standard.
- */
-
-/* TPS locked (1) */
-#define DIB3000MB_REG_TPS_LOCK			(   394)
-
-/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */
-#define DIB3000MB_REG_TPS_QAM			(   398)
-
-/* hierarchy from TPS (1) */
-#define DIB3000MB_REG_TPS_HRCH			(   399)
-
-/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */
-#define DIB3000MB_REG_TPS_VIT_ALPHA		(   400)
-
-/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */
-#define DIB3000MB_REG_TPS_CODE_RATE_HP	(   401)
-
-/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */
-#define DIB3000MB_REG_TPS_CODE_RATE_LP	(   402)
-
-/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */
-#define DIB3000MB_REG_TPS_GUARD_TIME	(   403)
-
-/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */
-#define DIB3000MB_REG_TPS_FFT			(   404)
-
-/* cell id from TPS (16) */
-#define DIB3000MB_REG_TPS_CELL_ID		(   406)
-
-/* TPS (68) */
-#define DIB3000MB_REG_TPS_1				(   408)
-#define DIB3000MB_REG_TPS_2				(   409)
-#define DIB3000MB_REG_TPS_3				(   410)
-#define DIB3000MB_REG_TPS_4				(   411)
-#define DIB3000MB_REG_TPS_5				(   412)
-
-/* bit error rate (before RS correction) (21) */
-#define DIB3000MB_REG_BER_MSB			(   414)
-#define DIB3000MB_REG_BER_LSB			(   415)
-
-/* packet error rate (uncorrected TS packets) (16) */
-#define DIB3000MB_REG_PACKET_ERROR_RATE	(   417)
-
-/* uncorrected packet count (16) */
-#define DIB3000MB_REG_UNC				(   420)
-
-/* viterbi locked (1) */
-#define DIB3000MB_REG_VIT_LCK			(   421)
-
-/* viterbi inidcator (16) */
-#define DIB3000MB_REG_VIT_INDICATOR		(   422)
-
-/* transport stream sync lock (1) */
-#define DIB3000MB_REG_TS_SYNC_LOCK		(   423)
-
-/* transport stream RS lock (1) */
-#define DIB3000MB_REG_TS_RS_LOCK		(   424)
-
-/* lock mask 0 value (1) */
-#define DIB3000MB_REG_LOCK0_VALUE		(   425)
-
-/* lock mask 1 value (1) */
-#define DIB3000MB_REG_LOCK1_VALUE		(   426)
-
-/* lock mask 2 value (1) */
-#define DIB3000MB_REG_LOCK2_VALUE		(   427)
-
-/* interrupt pending for auto search */
-#define DIB3000MB_REG_AS_IRQ_PENDING	(   434)
-
-#endif
diff -puN /dev/null drivers/media/dvb/frontends/dib3000mb_priv.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/dib3000mb_priv.h	2004-11-21 17:13:23.436055560 -0800
@@ -0,0 +1,467 @@
+/*
+ * dib3000mb_priv.h
+ *
+ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License as
+ *	published by the Free Software Foundation, version 2.
+ *
+ * for more information see dib3000mb.c .
+ */
+
+#ifndef __DIB3000MB_PRIV_H_INCLUDED__
+#define __DIB3000MB_PRIV_H_INCLUDED__
+
+/* register addresses and some of their default values */
+
+/* restart subsystems */
+#define DIB3000MB_REG_RESTART			(     0)
+
+#define DIB3000MB_RESTART_OFF			(     0)
+#define DIB3000MB_RESTART_AUTO_SEARCH		(1 << 1)
+#define DIB3000MB_RESTART_CTRL				(1 << 2)
+#define DIB3000MB_RESTART_AGC				(1 << 3)
+
+/* FFT size */
+#define DIB3000MB_REG_FFT				(     1)
+
+/* Guard time */
+#define DIB3000MB_REG_GUARD_TIME		(     2)
+
+/* QAM */
+#define DIB3000MB_REG_QAM				(     3)
+
+/* Alpha coefficient high priority Viterbi algorithm */
+#define DIB3000MB_REG_VIT_ALPHA			(     4)
+
+/* spectrum inversion */
+#define DIB3000MB_REG_DDS_INV			(     5)
+
+/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */
+#define DIB3000MB_REG_DDS_FREQ_MSB		(     6)
+#define DIB3000MB_REG_DDS_FREQ_LSB		(     7)
+#define DIB3000MB_DDS_FREQ_MSB				(   178)
+#define DIB3000MB_DDS_FREQ_LSB				(  8990)
+
+/* timing frequency (carrier spacing) */
+static u16 dib3000mb_reg_timing_freq[] = { 8,9 };
+static u16 dib3000mb_timing_freq[][2] = {
+	{ 126 , 48873 }, /* 6 MHz */
+	{ 147 , 57019 }, /* 7 MHz */
+	{ 168 , 65164 }, /* 8 MHz */
+};
+
+/* impulse noise parameter */
+/* 36 ??? */
+
+static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 };
+
+enum dib3000mb_impulse_noise_type {
+	DIB3000MB_IMPNOISE_OFF,
+	DIB3000MB_IMPNOISE_MOBILE,
+	DIB3000MB_IMPNOISE_FIXED,
+	DIB3000MB_IMPNOISE_DEFAULT
+};
+
+static u16 dib3000mb_impulse_noise_values[][5] = {
+	{ 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */
+	{ 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */
+	{ 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */
+	{ 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */
+};
+
+/*
+ * Dual Automatic-Gain-Control
+ * - gains RF in tuner (AGC1)
+ * - gains IF after filtering (AGC2)
+ */
+
+/* also from 16 to 18 */
+static u16 dib3000mb_reg_agc_gain[] = {
+	19,20,21,22,23,24,25,26,27,28,29,30,31,32
+};
+
+static u16 dib3000mb_default_agc_gain[] =
+	{ 0x0001, 52429,   623, 128, 166, 195, 61,   /* RF ??? */
+	  0x0001, 53766, 38011,   0,  90,  33, 23 }; /* IF ??? */
+
+/* phase noise */
+/* 36 is set when setting the impulse noise */
+static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 };
+
+static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 };
+
+/* lock duration */
+static u16 dib3000mb_reg_lock_duration[] = { 39,40 };
+static u16 dib3000mb_default_lock_duration[] = { 135, 135 };
+
+/* AGC loop bandwidth */
+static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 };
+
+static u16 dib3000mb_agc_bandwidth_low[]  =
+	{ 2088, 10, 2088, 10, 3448, 5, 3448, 5 };
+static u16 dib3000mb_agc_bandwidth_high[] =
+	{ 2349,  5, 2349,  5, 2586, 2, 2586, 2 };
+
+/*
+ * lock0 definition (coff_lock)
+ */
+#define DIB3000MB_REG_LOCK0_MASK		(    51)
+#define DIB3000MB_LOCK0_DEFAULT				(     4)
+
+/*
+ * lock1 definition (cpil_lock)
+ * for auto search
+ * which values hide behind the lock masks
+ */
+#define DIB3000MB_REG_LOCK1_MASK		(    52)
+#define DIB3000MB_LOCK1_SEARCH_4			(0x0004)
+#define DIB3000MB_LOCK1_SEARCH_2048			(0x0800)
+#define DIB3000MB_LOCK1_DEFAULT				(0x0001)
+
+/*
+ * lock2 definition (fec_lock) */
+#define DIB3000MB_REG_LOCK2_MASK		(    53)
+#define DIB3000MB_LOCK2_DEFAULT				(0x0080)
+
+/*
+ * SEQ ? what was that again ... :)
+ * changes when, inversion, guard time and fft is
+ * either automatically detected or not
+ */
+#define DIB3000MB_REG_SEQ				(    54)
+
+/* bandwidth */
+static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 };
+static u16 dib3000mb_bandwidth_6mhz[] =
+	{ 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_7mhz[] =
+	{ 0, 28, 64421,  96, 39973, 483,  3255, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_8mhz[] =
+	{ 0, 25, 23600,  84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 };
+
+#define DIB3000MB_REG_UNK_68				(    68)
+#define DIB3000MB_UNK_68						(     0)
+
+#define DIB3000MB_REG_UNK_69				(    69)
+#define DIB3000MB_UNK_69						(     0)
+
+#define DIB3000MB_REG_UNK_71				(    71)
+#define DIB3000MB_UNK_71						(     0)
+
+#define DIB3000MB_REG_UNK_77				(    77)
+#define DIB3000MB_UNK_77						(     6)
+
+#define DIB3000MB_REG_UNK_78				(    78)
+#define DIB3000MB_UNK_78						(0x0080)
+
+/* isi */
+#define DIB3000MB_REG_ISI				(    79)
+#define DIB3000MB_ISI_ACTIVATE				(     0)
+#define DIB3000MB_ISI_INHIBIT				(     1)
+
+/* sync impovement */
+#define DIB3000MB_REG_SYNC_IMPROVEMENT	(    84)
+#define DIB3000MB_SYNC_IMPROVE_2K_1_8		(     3)
+#define DIB3000MB_SYNC_IMPROVE_DEFAULT		(     0)
+
+/* phase noise compensation inhibition */
+#define DIB3000MB_REG_PHASE_NOISE		(    87)
+#define DIB3000MB_PHASE_NOISE_DEFAULT	(     0)
+
+#define DIB3000MB_REG_UNK_92				(    92)
+#define DIB3000MB_UNK_92						(0x0080)
+
+#define DIB3000MB_REG_UNK_96				(    96)
+#define DIB3000MB_UNK_96						(0x0010)
+
+#define DIB3000MB_REG_UNK_97				(    97)
+#define DIB3000MB_UNK_97						(0x0009)
+
+/* mobile mode ??? */
+#define DIB3000MB_REG_MOBILE_MODE		(   101)
+#define DIB3000MB_MOBILE_MODE_ON			(     1)
+#define DIB3000MB_MOBILE_MODE_OFF			(     0)
+
+#define DIB3000MB_REG_UNK_106			(   106)
+#define DIB3000MB_UNK_106					(0x0080)
+
+#define DIB3000MB_REG_UNK_107			(   107)
+#define DIB3000MB_UNK_107					(0x0080)
+
+#define DIB3000MB_REG_UNK_108			(   108)
+#define DIB3000MB_UNK_108					(0x0080)
+
+/* fft */
+#define DIB3000MB_REG_UNK_121			(   121)
+#define DIB3000MB_UNK_121_2K				(     7)
+#define DIB3000MB_UNK_121_DEFAULT			(     5)
+
+#define DIB3000MB_REG_UNK_122			(   122)
+#define DIB3000MB_UNK_122					(  2867)
+
+/* QAM for mobile mode */
+#define DIB3000MB_REG_MOBILE_MODE_QAM	(   126)
+#define DIB3000MB_MOBILE_MODE_QAM_64		(     3)
+#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16	(     1)
+#define DIB3000MB_MOBILE_MODE_QAM_OFF		(     0)
+
+/*
+ * data diversity when having more than one chip on-board
+ * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY
+ */
+#define DIB3000MB_REG_DATA_IN_DIVERSITY		(   127)
+#define DIB3000MB_DATA_DIVERSITY_IN_OFF			(     0)
+#define DIB3000MB_DATA_DIVERSITY_IN_ON			(     2)
+
+/* vit hrch */
+#define DIB3000MB_REG_VIT_HRCH			(   128)
+
+/* vit code rate */
+#define DIB3000MB_REG_VIT_CODE_RATE		(   129)
+
+/* vit select hp */
+#define DIB3000MB_REG_VIT_HP			(   130)
+
+/* time frame for Bit-Error-Rate calculation */
+#define DIB3000MB_REG_BERLEN			(   135)
+#define DIB3000MB_BERLEN_LONG				(     0)
+#define DIB3000MB_BERLEN_DEFAULT			(     1)
+#define DIB3000MB_BERLEN_MEDIUM				(     2)
+#define DIB3000MB_BERLEN_SHORT				(     3)
+
+/* 142 - 152 FIFO parameters
+ * which is what ?
+ */
+
+#define DIB3000MB_REG_FIFO_142			(   142)
+#define DIB3000MB_FIFO_142					(     0)
+
+/* MPEG2 TS output mode */
+#define DIB3000MB_REG_MPEG2_OUT_MODE	(   143)
+#define DIB3000MB_MPEG2_OUT_MODE_204		(     0)
+#define DIB3000MB_MPEG2_OUT_MODE_188		(     1)
+
+#define DIB3000MB_REG_PID_PARSE			(   144)
+#define DIB3000MB_PID_PARSE_INHIBIT		(     0)
+#define DIB3000MB_PID_PARSE_ACTIVATE	(     1)
+
+#define DIB3000MB_REG_FIFO				(   145)
+#define DIB3000MB_FIFO_INHIBIT				(     1)
+#define DIB3000MB_FIFO_ACTIVATE				(     0)
+
+#define DIB3000MB_REG_FIFO_146			(   146)
+#define DIB3000MB_FIFO_146					(     3)
+
+#define DIB3000MB_REG_FIFO_147			(   147)
+#define DIB3000MB_FIFO_147					(0x0100)
+
+/*
+ * pidfilter
+ * it is not a hardware pidfilter but a filter which drops all pids
+ * except the ones set. Necessary because of the limited USB1.1 bandwidth.
+ * regs 153-168
+ */
+
+#define DIB3000MB_REG_FIRST_PID			(   153)
+#define DIB3000MB_NUM_PIDS				(    16)
+
+/*
+ * output mode
+ * USB devices have to use 'slave'-mode
+ * see also DIB3000MB_REG_ELECT_OUT_MODE
+ */
+#define DIB3000MB_REG_OUTPUT_MODE		(   169)
+#define DIB3000MB_OUTPUT_MODE_GATED_CLK		(     0)
+#define DIB3000MB_OUTPUT_MODE_CONT_CLK		(     1)
+#define DIB3000MB_OUTPUT_MODE_SERIAL		(     2)
+#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY	(     5)
+#define DIB3000MB_OUTPUT_MODE_SLAVE			(     6)
+
+/* irq event mask */
+#define DIB3000MB_REG_IRQ_EVENT_MASK		(   170)
+#define DIB3000MB_IRQ_EVENT_MASK				(     0)
+
+/* filter coefficients */
+static u16 dib3000mb_reg_filter_coeffs[] = {
+	171, 172, 173, 174, 175, 176, 177, 178,
+	179, 180, 181, 182, 183, 184, 185, 186,
+	188, 189, 190, 191, 192, 194
+};
+
+static u16 dib3000mb_filter_coeffs[] = {
+	 226,  160,   29,
+ 	 979,  998,   19,
+	  22, 1019, 1006,
+	1022,   12,    6,
+	1017, 1017,    3,
+	   6,       1019,
+	1021,    2,    3,
+	   1,          0,
+};
+
+/*
+ * mobile algorithm (when you are moving with your device)
+ * but not faster than 90 km/h
+ */
+#define DIB3000MB_REG_MOBILE_ALGO		(   195)
+#define DIB3000MB_MOBILE_ALGO_ON			(     0)
+#define DIB3000MB_MOBILE_ALGO_OFF			(     1)
+
+/* multiple demodulators algorithm */
+#define DIB3000MB_REG_MULTI_DEMOD_MSB	(   206)
+#define DIB3000MB_REG_MULTI_DEMOD_LSB	(   207)
+
+/* terminator, no more demods */
+#define DIB3000MB_MULTI_DEMOD_MSB			( 32767)
+#define DIB3000MB_MULTI_DEMOD_LSB			(  4095)
+
+/* bring the device into a known  */
+#define DIB3000MB_REG_RESET_DEVICE		(  1024)
+#define DIB3000MB_RESET_DEVICE				(0x812c)
+#define DIB3000MB_RESET_DEVICE_RST			(     0)
+
+/* hardware clock configuration */
+#define DIB3000MB_REG_CLOCK				(  1027)
+#define DIB3000MB_CLOCK_DEFAULT				(0x9000)
+#define DIB3000MB_CLOCK_DIVERSITY			(0x92b0)
+
+/* power down config */
+#define DIB3000MB_REG_POWER_CONTROL		(  1028)
+#define DIB3000MB_POWER_DOWN				(     1)
+#define DIB3000MB_POWER_UP					(     0)
+
+/* electrical output mode */
+#define DIB3000MB_REG_ELECT_OUT_MODE	(  1029)
+#define DIB3000MB_ELECT_OUT_MODE_OFF		(     0)
+#define DIB3000MB_ELECT_OUT_MODE_ON			(     1)
+
+/* set the tuner i2c address */
+#define DIB3000MB_REG_TUNER				(  1089)
+
+/* monitoring registers (read only) */
+
+/* agc loop locked (size: 1) */
+#define DIB3000MB_REG_AGC_LOCK			(   324)
+
+/* agc power (size: 16) */
+#define DIB3000MB_REG_AGC_POWER			(   325)
+
+/* agc1 value (16) */
+#define DIB3000MB_REG_AGC1_VALUE		(   326)
+
+/* agc2 value (16) */
+#define DIB3000MB_REG_AGC2_VALUE		(   327)
+
+/* total RF power (16), can be used for signal strength */
+#define DIB3000MB_REG_RF_POWER			(   328)
+
+/* dds_frequency with offset (24) */
+#define DIB3000MB_REG_DDS_VALUE_MSB		(   339)
+#define DIB3000MB_REG_DDS_VALUE_LSB		(   340)
+
+/* timing offset signed (24) */
+#define DIB3000MB_REG_TIMING_OFFSET_MSB	(   341)
+#define DIB3000MB_REG_TIMING_OFFSET_LSB	(   342)
+
+/* fft start position (13) */
+#define DIB3000MB_REG_FFT_WINDOW_POS	(   353)
+
+/* carriers locked (1) */
+#define DIB3000MB_REG_CARRIER_LOCK		(   355)
+
+/* noise power (24) */
+#define DIB3000MB_REG_NOISE_POWER_MSB	(   372)
+#define DIB3000MB_REG_NOISE_POWER_LSB	(   373)
+
+#define DIB3000MB_REG_MOBILE_NOISE_MSB	(   374)
+#define DIB3000MB_REG_MOBILE_NOISE_LSB	(   375)
+
+/*
+ * signal power (16), this and the above can be
+ * used to calculate the signal/noise - ratio
+ */
+#define DIB3000MB_REG_SIGNAL_POWER		(   380)
+
+/* mer (24) */
+#define DIB3000MB_REG_MER_MSB			(   381)
+#define DIB3000MB_REG_MER_LSB			(   382)
+
+/*
+ * Transmission Parameter Signalling (TPS)
+ * the following registers can be used to get TPS-information.
+ * The values are according to the DVB-T standard.
+ */
+
+/* TPS locked (1) */
+#define DIB3000MB_REG_TPS_LOCK			(   394)
+
+/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */
+#define DIB3000MB_REG_TPS_QAM			(   398)
+
+/* hierarchy from TPS (1) */
+#define DIB3000MB_REG_TPS_HRCH			(   399)
+
+/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */
+#define DIB3000MB_REG_TPS_VIT_ALPHA		(   400)
+
+/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */
+#define DIB3000MB_REG_TPS_CODE_RATE_HP	(   401)
+
+/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */
+#define DIB3000MB_REG_TPS_CODE_RATE_LP	(   402)
+
+/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */
+#define DIB3000MB_REG_TPS_GUARD_TIME	(   403)
+
+/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */
+#define DIB3000MB_REG_TPS_FFT			(   404)
+
+/* cell id from TPS (16) */
+#define DIB3000MB_REG_TPS_CELL_ID		(   406)
+
+/* TPS (68) */
+#define DIB3000MB_REG_TPS_1				(   408)
+#define DIB3000MB_REG_TPS_2				(   409)
+#define DIB3000MB_REG_TPS_3				(   410)
+#define DIB3000MB_REG_TPS_4				(   411)
+#define DIB3000MB_REG_TPS_5				(   412)
+
+/* bit error rate (before RS correction) (21) */
+#define DIB3000MB_REG_BER_MSB			(   414)
+#define DIB3000MB_REG_BER_LSB			(   415)
+
+/* packet error rate (uncorrected TS packets) (16) */
+#define DIB3000MB_REG_PACKET_ERROR_RATE	(   417)
+
+/* uncorrected packet count (16) */
+#define DIB3000MB_REG_UNC				(   420)
+
+/* viterbi locked (1) */
+#define DIB3000MB_REG_VIT_LCK			(   421)
+
+/* viterbi inidcator (16) */
+#define DIB3000MB_REG_VIT_INDICATOR		(   422)
+
+/* transport stream sync lock (1) */
+#define DIB3000MB_REG_TS_SYNC_LOCK		(   423)
+
+/* transport stream RS lock (1) */
+#define DIB3000MB_REG_TS_RS_LOCK		(   424)
+
+/* lock mask 0 value (1) */
+#define DIB3000MB_REG_LOCK0_VALUE		(   425)
+
+/* lock mask 1 value (1) */
+#define DIB3000MB_REG_LOCK1_VALUE		(   426)
+
+/* lock mask 2 value (1) */
+#define DIB3000MB_REG_LOCK2_VALUE		(   427)
+
+/* interrupt pending for auto search */
+#define DIB3000MB_REG_AS_IRQ_PENDING	(   434)
+
+#endif
diff -puN drivers/media/dvb/frontends/dvb_dummy_fe.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/dvb_dummy_fe.c
--- 25/drivers/media/dvb/frontends/dvb_dummy_fe.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.381063920 -0800
+++ 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.c	2004-11-21 17:13:23.438055256 -0800
@@ -24,244 +24,258 @@
 #include <linux/init.h>
 
 #include "dvb_frontend.h"
+#include "dvb_dummy_fe.h"
 
-#define FRONTEND_NAME "dvbfe_dummy"
 
-static int frontend_type;
-module_param(frontend_type, int, 0444);
-MODULE_PARM_DESC(frontend_type, "0 == DVB-S, 1 == DVB-C, 2 == DVB-T");
-
-/* depending on module parameter sct deliver different infos
- */
-
-static struct dvb_frontend_info dvb_s_dummyfe_info = {
-	.name 			= "DVB-S dummy frontend",
-	.type 			= FE_QPSK,
-	.frequency_min 		= 950000,
-	.frequency_max 		= 2150000,
-	.frequency_stepsize 	= 250,           /* kHz for QPSK frontends */
-	.frequency_tolerance 	= 29500,
-	.symbol_rate_min 	= 1000000,
-	.symbol_rate_max 	= 45000000,
-/*      .symbol_rate_tolerance 	= ???,*/
-	.notifier_delay		 = 50,                /* 1/20 s */
-	.caps = FE_CAN_INVERSION_AUTO | 
-	FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-	FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-	FE_CAN_QPSK
-};
-
-static struct dvb_frontend_info dvb_c_dummyfe_info = {
-	.name = "DVB-C dummy frontend",
-	.type = FE_QAM,
-	.frequency_stepsize = 62500,
-	.frequency_min = 51000000,
-	.frequency_max = 858000000,
-	.symbol_rate_min = (57840000/2)/64,     /* SACLK/64 == (XIN/2)/64 */
-	.symbol_rate_max = (57840000/2)/4,      /* SACLK/4 */
-#if 0
-	.frequency_tolerance	= ???,
-	.symbol_rate_tolerance	= ???,  /* ppm */  /* == 8% (spec p. 5) */
-	.notifier_delay		= ?,
-#endif
-	.caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
-		FE_CAN_QAM_128 | FE_CAN_QAM_256 | 
-		FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO
-};
 
-static struct dvb_frontend_info dvb_t_dummyfe_info = {
-	.name = "DVB-T dummy frontend",
-	.type = FE_OFDM,
-	.frequency_min = 0,
-	.frequency_max = 863250000,
-	.frequency_stepsize = 62500,
-	/*.frequency_tolerance = */	/* FIXME: 12% of SR */
-	.symbol_rate_min = 0,		/* FIXME */
-	.symbol_rate_max = 9360000,	/* FIXME */
-	.symbol_rate_tolerance = 4000,
-	.notifier_delay = 0,
-	.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
-			FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
-			FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
-			FE_CAN_TRANSMISSION_MODE_AUTO |
-			FE_CAN_GUARD_INTERVAL_AUTO |
-			FE_CAN_HIERARCHY_AUTO,
-};
+struct dvb_dummy_fe_state {
 
-struct dvb_frontend_info *frontend_info(void)
-{
-	switch(frontend_type) {
-	case 2:
-		return &dvb_t_dummyfe_info;
-	case 1:
-		return &dvb_c_dummyfe_info;
-	case 0:
-	default:
-	        return &dvb_s_dummyfe_info;
-	}
-}
+	struct dvb_frontend_ops ops;
 
+	struct dvb_frontend frontend;
+};
 
-static int dvbdummyfe_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
-{
-        switch (cmd) {
-        case FE_GET_INFO:
-		memcpy (arg, frontend_info(), 
-			sizeof(struct dvb_frontend_info));
-		break;
-
-        case FE_READ_STATUS:
+static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status)
 	{
-		fe_status_t *status = arg;
 		*status = FE_HAS_SIGNAL
 			| FE_HAS_CARRIER
 			| FE_HAS_VITERBI
 			| FE_HAS_SYNC
 			| FE_HAS_LOCK;
-		break;
+
+	return 0;
 	}
 
-        case FE_READ_BER:
+static int dvb_dummy_fe_read_ber(struct dvb_frontend* fe, u32* ber)
 	{
-		u32 *ber = (u32 *) arg;
 		*ber = 0;
-		break;
+	return 0;
 	}
 
-        case FE_READ_SIGNAL_STRENGTH:
+static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 	{
-		u8 signal = 0xff;
-		*((u16*) arg) = (signal << 8) | signal;
-		break;
+	*strength = 0;
+	return 0;
 	}
 
-        case FE_READ_SNR:
+static int dvb_dummy_fe_read_snr(struct dvb_frontend* fe, u16* snr)
 	{
-		u8 snr = 0xf0;
-		*(u16*) arg = (snr << 8) | snr;
-		break;
+	*snr = 0;
+	return 0;
 	}
 
-	case FE_READ_UNCORRECTED_BLOCKS: 
-		*(u32*) arg = 0;
-		break;
-
-        case FE_SET_FRONTEND:
-                break;
-
-	case FE_GET_FRONTEND:
-		break;
-
-        case FE_SLEEP:
+static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	*ucblocks = 0;
 		return 0;
+}
 
-        case FE_INIT:
+static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
 		return 0;
+}
 
-	case FE_SET_TONE:
-		return -EOPNOTSUPP;
+static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	return 0;
+}
 
-	case FE_SET_VOLTAGE:
+static int dvb_dummy_fe_sleep(struct dvb_frontend* fe)
+{
 		return 0;
+}
 
-	default:
-		return -EOPNOTSUPP;
+static int dvb_dummy_fe_init(struct dvb_frontend* fe)
+{
+	return 0;
         }
+
+static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
         return 0;
 } 
 
-static struct i2c_client client_template;
-
-static int dvbdummyfe_attach_adapter(struct i2c_adapter *adapter)
+static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
 {
-	struct dvb_adapter *dvb;
-	struct i2c_client *client;
-	int ret;
-
-	if ((client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
+	return 0;
+}
 
-	if ((ret = i2c_attach_client(client))) {
-		kfree(client);
-		return ret;
+static void dvb_dummy_fe_release(struct dvb_frontend* fe)
+{
+	struct dvb_dummy_fe_state* state = (struct dvb_dummy_fe_state*) fe->demodulator_priv;
+	kfree(state);
 }
 
-	dvb = i2c_get_clientdata(client);
-	BUG_ON(!dvb);
+static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops;
 
-	if ((ret = dvb_register_frontend(dvbdummyfe_ioctl, dvb, NULL,
-					     frontend_info(), THIS_MODULE))) {
-		kfree(client);
-		return ret;
-	}
+struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void)
+{
+	struct dvb_dummy_fe_state* state = NULL;
 
-	return 0;
+	/* allocate memory for the internal state */
+	state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	memcpy(&state->ops, &dvb_dummy_fe_ofdm_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
 }
 
+static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops;
 
-static int dvbdummyfe_detach_client(struct i2c_client *client)
+struct dvb_frontend* dvb_dummy_fe_qpsk_attach()
 {
-	struct dvb_adapter *dvb = i2c_get_clientdata(client);
+	struct dvb_dummy_fe_state* state = NULL;
 
-	dvb_unregister_frontend(dvbdummyfe_ioctl, dvb);
-	i2c_detach_client(client);
-	kfree(client);
-	return 0;
+	/* allocate memory for the internal state */
+	state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	memcpy(&state->ops, &dvb_dummy_fe_qpsk_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
 }
 
-static int dvbdummyfe_command(struct i2c_client *client,
-			      unsigned int cmd, void *arg)
+static struct dvb_frontend_ops dvb_dummy_fe_qam_ops;
+
+struct dvb_frontend* dvb_dummy_fe_qam_attach()
 {
-	switch(cmd) {
-	case FE_REGISTER:
-		i2c_set_clientdata(client, arg);
-		break;
-	case FE_UNREGISTER:
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
+	struct dvb_dummy_fe_state* state = NULL;
 
-	return 0;
+	/* allocate memory for the internal state */
+	state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	memcpy(&state->ops, &dvb_dummy_fe_qam_ops, sizeof(struct dvb_frontend_ops));
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
 }
 
-static struct i2c_driver driver = {
-	.owner		= THIS_MODULE,
-	.name		= FRONTEND_NAME,
-	.id		= I2C_DRIVERID_DVBFE_DUMMY,
-	.flags		= I2C_DF_NOTIFY,
-	.attach_adapter	= dvbdummyfe_attach_adapter,
-	.detach_client	= dvbdummyfe_detach_client,
-	.command	= dvbdummyfe_command,
-};
+static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = {
 
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags		= I2C_CLIENT_ALLOW_USE,
-	.driver		= &driver,
+	.info = {
+		.name			= "Dummy DVB-T",
+		.type 			= FE_OFDM,
+		.frequency_min 		= 0,
+		.frequency_max 		= 863250000,
+		.frequency_stepsize	= 62500,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+				FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+				FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+				FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+				FE_CAN_TRANSMISSION_MODE_AUTO |
+				FE_CAN_GUARD_INTERVAL_AUTO |
+				FE_CAN_HIERARCHY_AUTO,
+	},
+
+	.release = dvb_dummy_fe_release,
+
+	.init = dvb_dummy_fe_init,
+	.sleep = dvb_dummy_fe_sleep,
+
+	.set_frontend = dvb_dummy_fe_set_frontend,
+	.get_frontend = dvb_dummy_fe_get_frontend,
+
+	.read_status = dvb_dummy_fe_read_status,
+	.read_ber = dvb_dummy_fe_read_ber,
+	.read_signal_strength = dvb_dummy_fe_read_signal_strength,
+	.read_snr = dvb_dummy_fe_read_snr,
+	.read_ucblocks = dvb_dummy_fe_read_ucblocks,
 };
 
-static int __init init_dvbdummyfe (void)
-{
-	return i2c_add_driver(&driver);
-}
+static struct dvb_frontend_ops dvb_dummy_fe_qam_ops = {
 
-static void __exit exit_dvbdummyfe (void)
-{
-	if (i2c_del_driver(&driver))
-		printk(KERN_ERR "dummyfe: driver deregistration failed.\n");
-}
+	.info = {
+		.name			= "Dummy DVB-C",
+		.type 			= FE_QAM,
+		.frequency_stepsize	= 62500,
+		.frequency_min 		= 51000000,
+		.frequency_max 		= 858000000,
+		.symbol_rate_min 	= (57840000/2)/64,     /* SACLK/64 == (XIN/2)/64 */
+		.symbol_rate_max 	= (57840000/2)/4,      /* SACLK/4 */
+		.caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+			FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO
+	},
+
+	.release = dvb_dummy_fe_release,
+
+	.init = dvb_dummy_fe_init,
+	.sleep = dvb_dummy_fe_sleep,
+
+	.set_frontend = dvb_dummy_fe_set_frontend,
+	.get_frontend = dvb_dummy_fe_get_frontend,
+
+	.read_status = dvb_dummy_fe_read_status,
+	.read_ber = dvb_dummy_fe_read_ber,
+	.read_signal_strength = dvb_dummy_fe_read_signal_strength,
+	.read_snr = dvb_dummy_fe_read_snr,
+	.read_ucblocks = dvb_dummy_fe_read_ucblocks,
+};
 
+static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = {
 
-module_init(init_dvbdummyfe);
-module_exit(exit_dvbdummyfe);
+	.info = {
+		.name 			= "Dummy DVB-S",
+		.type 			= FE_QPSK,
+		.frequency_min 		= 950000,
+		.frequency_max 		= 2150000,
+		.frequency_stepsize 	= 250,           /* kHz for QPSK frontends */
+		.frequency_tolerance 	= 29500,
+		.symbol_rate_min 	= 1000000,
+		.symbol_rate_max 	= 45000000,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK
+	},
+
+	.release = dvb_dummy_fe_release,
+
+	.init = dvb_dummy_fe_init,
+	.sleep = dvb_dummy_fe_sleep,
+
+	.set_frontend = dvb_dummy_fe_set_frontend,
+	.get_frontend = dvb_dummy_fe_get_frontend,
+
+	.read_status = dvb_dummy_fe_read_status,
+	.read_ber = dvb_dummy_fe_read_ber,
+	.read_signal_strength = dvb_dummy_fe_read_signal_strength,
+	.read_snr = dvb_dummy_fe_read_snr,
+	.read_ucblocks = dvb_dummy_fe_read_ucblocks,
 
+	.set_voltage = dvb_dummy_fe_set_voltage,
+     	.set_tone = dvb_dummy_fe_set_tone,
+};
 
 MODULE_DESCRIPTION("DVB DUMMY Frontend");
 MODULE_AUTHOR("Emard");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach);
+EXPORT_SYMBOL(dvb_dummy_fe_qam_attach);
+EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach);
diff -puN /dev/null drivers/media/dvb/frontends/dvb_dummy_fe.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.h	2004-11-21 17:13:23.438055256 -0800
@@ -0,0 +1,32 @@
+/*
+ *  Driver for Dummy Frontend
+ *
+ *  Written by Emard <emard@softhome.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef DVB_DUMMY_FE_H
+#define DVB_DUMMY_FE_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void);
+
+#endif // DVB_DUMMY_FE_H
diff -puN /dev/null drivers/media/dvb/frontends/l64781.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/l64781.c	2004-11-21 17:13:23.442054648 -0800
@@ -0,0 +1,607 @@
+/*
+    driver for LSI L64781 COFDM demodulator
+
+    Copyright (C) 2001 Holger Waechtler <holger@convergence.de>
+                       for Convergence Integrated Media GmbH
+                       Marko Kohtala <marko.kohtala@nokia.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "l64781.h"
+
+
+struct l64781_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct l64781_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* private demodulator data */
+   	int first:1;
+};
+
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "l64781: " args); \
+	} while (0)
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+
+static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
+		dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
+			 __FUNCTION__, reg, ret);
+
+	return (ret != 1) ? -1 : 0;
+}
+
+static int l64781_readreg (struct l64781_state* state, u8 reg)
+{
+	int ret;
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) return ret;
+
+	return b1[0];
+}
+
+static void apply_tps (struct l64781_state* state)
+{
+	l64781_writereg (state, 0x2a, 0x00);
+	l64781_writereg (state, 0x2a, 0x01);
+
+	/* This here is a little bit questionable because it enables
+	   the automatic update of TPS registers. I think we'd need to
+	   handle the IRQ from FE to update some other registers as
+	   well, or at least implement some magic to tuning to correct
+	   to the TPS received from transmission. */
+	l64781_writereg (state, 0x2a, 0x02);
+}
+
+
+static void reset_afc (struct l64781_state* state)
+{
+	/* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
+	   timing offset */
+	l64781_writereg (state, 0x07, 0x9e); /* stall AFC */
+	l64781_writereg (state, 0x08, 0);    /* AFC INIT FREQ */
+	l64781_writereg (state, 0x09, 0);
+	l64781_writereg (state, 0x0a, 0);
+	l64781_writereg (state, 0x07, 0x8e);
+	l64781_writereg (state, 0x0e, 0);    /* AGC gain to zero in beginning */
+	l64781_writereg (state, 0x11, 0x80); /* stall TIM */
+	l64781_writereg (state, 0x10, 0);    /* TIM_OFFSET_LSB */
+	l64781_writereg (state, 0x12, 0);
+	l64781_writereg (state, 0x13, 0);
+	l64781_writereg (state, 0x11, 0x00);
+}
+
+static int reset_and_configure (struct l64781_state* state)
+{
+	u8 buf [] = { 0x06 };
+	struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 };
+	// NOTE: this is correct in writing to address 0x00
+
+	return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV;
+}
+
+static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_parameters *param)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	/* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */
+	static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 };
+	/* QPSK, QAM_16, QAM_64 */
+	static const u8 qam_tab [] = { 2, 4, 0, 6 };
+	static const u8 bw_tab [] = { 8, 7, 6 };  /* 8Mhz, 7MHz, 6MHz */
+	static const u8 guard_tab [] = { 1, 2, 4, 8 };
+	/* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */
+	static const u32 ppm = 8000;
+	struct dvb_ofdm_parameters *p = &param->u.ofdm;
+	u32 ddfs_offset_fixed;
+/*	u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */
+/*			bw_tab[p->bandWidth]<<10)/15625; */
+	u32 init_freq;
+	u32 spi_bias;
+	u8 val0x04;
+	u8 val0x05;
+	u8 val0x06;
+	int bw = p->bandwidth - BANDWIDTH_8_MHZ;
+
+	state->config->pll_set(fe, param);
+
+	if (param->inversion != INVERSION_ON &&
+	    param->inversion != INVERSION_OFF)
+		return -EINVAL;
+
+	if (bw < 0 || bw > 2)
+		return -EINVAL;
+
+	if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 &&
+	    p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 &&
+	    p->code_rate_HP != FEC_7_8)
+		return -EINVAL;
+
+	if (p->hierarchy_information != HIERARCHY_NONE &&
+	    (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 &&
+	     p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 &&
+	     p->code_rate_LP != FEC_7_8))
+		return -EINVAL;
+
+	if (p->constellation != QPSK && p->constellation != QAM_16 &&
+	    p->constellation != QAM_64)
+		return -EINVAL;
+
+	if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+	    p->transmission_mode != TRANSMISSION_MODE_8K)
+		return -EINVAL;
+
+	if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+	    p->guard_interval > GUARD_INTERVAL_1_4)
+		return -EINVAL;
+
+	if (p->hierarchy_information < HIERARCHY_NONE ||
+	    p->hierarchy_information > HIERARCHY_4)
+		return -EINVAL;
+
+	ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000;
+
+	/* This works up to 20000 ppm, it overflows if too large ppm! */
+	init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) /
+			bw_tab[p->bandwidth] & 0xFFFFFF);
+
+	/* SPI bias calculation is slightly modified to fit in 32bit */
+	/* will work for high ppm only... */
+	spi_bias = 378 * (1 << 10);
+	spi_bias *= 16;
+	spi_bias *= bw_tab[p->bandwidth];
+	spi_bias *= qam_tab[p->constellation];
+	spi_bias /= p->code_rate_HP + 1;
+	spi_bias /= (guard_tab[p->guard_interval] + 32);
+	spi_bias *= 1000ULL;
+	spi_bias /= 1000ULL + ppm/1000;
+	spi_bias *= p->code_rate_HP;
+
+	val0x04 = (p->transmission_mode << 2) | p->guard_interval;
+	val0x05 = fec_tab[p->code_rate_HP];
+
+	if (p->hierarchy_information != HIERARCHY_NONE)
+		val0x05 |= (p->code_rate_LP - FEC_1_2) << 3;
+
+	val0x06 = (p->hierarchy_information << 2) | p->constellation;
+
+	l64781_writereg (state, 0x04, val0x04);
+	l64781_writereg (state, 0x05, val0x05);
+	l64781_writereg (state, 0x06, val0x06);
+
+	reset_afc (state);
+
+	/* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */
+	l64781_writereg (state, 0x15,
+			 p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3);
+	l64781_writereg (state, 0x16, init_freq & 0xff);
+	l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff);
+	l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff);
+
+	l64781_writereg (state, 0x1b, spi_bias & 0xff);
+	l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff);
+	l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) |
+		(param->inversion == INVERSION_ON ? 0x80 : 0x00));
+
+	l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff);
+	l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f);
+
+	l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
+	l64781_readreg (state, 0x01);  /*  dto. */
+
+	apply_tps (state);
+
+	return 0;
+}
+
+static int get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* param)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	int tmp;
+
+
+	tmp = l64781_readreg(state, 0x04);
+	switch(tmp & 3) {
+	case 0:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+		break;
+	case 1:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+		break;
+	case 2:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+		break;
+	case 3:
+		param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+		break;
+	}
+	switch((tmp >> 2) & 3) {
+	case 0:
+		param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+		break;
+	case 1:
+		param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+		break;
+	default:
+		printk("Unexpected value for transmission_mode\n");
+	}
+
+
+
+	tmp = l64781_readreg(state, 0x05);
+	switch(tmp & 7) {
+	case 0:
+		param->u.ofdm.code_rate_HP = FEC_1_2;
+		break;
+	case 1:
+		param->u.ofdm.code_rate_HP = FEC_2_3;
+		break;
+	case 2:
+		param->u.ofdm.code_rate_HP = FEC_3_4;
+		break;
+	case 3:
+		param->u.ofdm.code_rate_HP = FEC_5_6;
+		break;
+	case 4:
+		param->u.ofdm.code_rate_HP = FEC_7_8;
+		break;
+	default:
+		printk("Unexpected value for code_rate_HP\n");
+	}
+	switch((tmp >> 3) & 7) {
+	case 0:
+		param->u.ofdm.code_rate_LP = FEC_1_2;
+		break;
+	case 1:
+		param->u.ofdm.code_rate_LP = FEC_2_3;
+		break;
+	case 2:
+		param->u.ofdm.code_rate_LP = FEC_3_4;
+		break;
+	case 3:
+		param->u.ofdm.code_rate_LP = FEC_5_6;
+		break;
+	case 4:
+		param->u.ofdm.code_rate_LP = FEC_7_8;
+		break;
+	default:
+		printk("Unexpected value for code_rate_LP\n");
+	}
+
+
+	tmp = l64781_readreg(state, 0x06);
+	switch(tmp & 3) {
+	case 0:
+		param->u.ofdm.constellation = QPSK;
+		break;
+	case 1:
+		param->u.ofdm.constellation = QAM_16;
+		break;
+	case 2:
+		param->u.ofdm.constellation = QAM_64;
+		break;
+	default:
+		printk("Unexpected value for constellation\n");
+	}
+	switch((tmp >> 2) & 7) {
+	case 0:
+		param->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+		break;
+	case 1:
+		param->u.ofdm.hierarchy_information = HIERARCHY_1;
+		break;
+	case 2:
+		param->u.ofdm.hierarchy_information = HIERARCHY_2;
+		break;
+	case 3:
+		param->u.ofdm.hierarchy_information = HIERARCHY_4;
+		break;
+	default:
+		printk("Unexpected value for hierarchy\n");
+	}
+
+
+	tmp = l64781_readreg (state, 0x1d);
+	param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF;
+
+	tmp = (int) (l64781_readreg (state, 0x08) |
+		     (l64781_readreg (state, 0x09) << 8) |
+		     (l64781_readreg (state, 0x0a) << 16));
+	param->frequency += tmp;
+
+	return 0;
+}
+
+static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	int sync = l64781_readreg (state, 0x32);
+	int gain = l64781_readreg (state, 0x0e);
+
+	l64781_readreg (state, 0x00);  /*  clear interrupt registers... */
+	l64781_readreg (state, 0x01);  /*  dto. */
+
+	*status = 0;
+
+	if (gain > 5)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x02) /* VCXO locked, this criteria should be ok */
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 0x20)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x40)
+		*status |= FE_HAS_SYNC;
+
+	if (sync == 0x7f)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int l64781_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	/*   XXX FIXME: set up counting period (reg 0x26...0x28)
+	 */
+	*ber = l64781_readreg (state, 0x39)
+	    | (l64781_readreg (state, 0x3a) << 8);
+
+	return 0;
+}
+
+static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	u8 gain = l64781_readreg (state, 0x0e);
+	*signal_strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int l64781_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	u8 avg_quality = 0xff - l64781_readreg (state, 0x33);
+	*snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/
+
+	return 0;
+}
+
+static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	*ucblocks = l64781_readreg (state, 0x37)
+	   | (l64781_readreg (state, 0x38) << 8);
+
+	return 0;
+}
+
+static int l64781_sleep(struct dvb_frontend* fe)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+	/* Power down */
+	return l64781_writereg (state, 0x3e, 0x5a);
+}
+
+static int l64781_init(struct dvb_frontend* fe)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+        reset_and_configure (state);
+
+	/* Power up */
+	l64781_writereg (state, 0x3e, 0xa5);
+
+	/* Reset hard */
+	l64781_writereg (state, 0x2a, 0x04);
+	l64781_writereg (state, 0x2a, 0x00);
+
+	/* Set tuner specific things */
+	/* AFC_POL, set also in reset_afc */
+	l64781_writereg (state, 0x07, 0x8e);
+
+	/* Use internal ADC */
+	l64781_writereg (state, 0x0b, 0x81);
+
+	/* AGC loop gain, and polarity is positive */
+	l64781_writereg (state, 0x0c, 0x84);
+
+	/* Internal ADC outputs two's complement */
+	l64781_writereg (state, 0x0d, 0x8c);
+
+	/* With ppm=8000, it seems the DTR_SENSITIVITY will result in
+           value of 2 with all possible bandwidths and guard
+           intervals, which is the initial value anyway. */
+        /*l64781_writereg (state, 0x19, 0x92);*/
+
+	/* Everything is two's complement, soft bit and CSI_OUT too */
+	l64781_writereg (state, 0x1e, 0x09);
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	/* delay a bit after first init attempt */
+	if (state->first) {
+		state->first = 0;
+		msleep(200);
+	}
+
+	return 0;
+}
+
+static int l64781_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 200;
+        fesettings->step_size = 166667;
+        fesettings->max_drift = 166667*2;
+        return 0;
+}
+
+static void l64781_release(struct dvb_frontend* fe)
+{
+	struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops l64781_ops;
+
+struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+				   struct i2c_adapter* i2c)
+{
+	struct l64781_state* state = NULL;
+	int reg0x3e = -1;
+	u8 b0 [] = { 0x1a };
+	u8 b1 [] = { 0x00 };
+	struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+			   { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+	/* allocate memory for the internal state */
+	state = (struct l64781_state*) kmalloc(sizeof(struct l64781_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &l64781_ops, sizeof(struct dvb_frontend_ops));
+	state->first = 1;
+
+	/**
+	 *  the L64781 won't show up before we send the reset_and_configure()
+	 *  broadcast. If nothing responds there is no L64781 on the bus...
+	 */
+	if (reset_and_configure(state) < 0) {
+		dprintk("No response to reset and configure broadcast...\n");
+		goto error;
+	}
+
+	/* The chip always responds to reads */
+	if (i2c_transfer(state->i2c, msg, 2) != 2) {
+	        dprintk("No response to read on I2C bus\n");
+		goto error;
+	}
+
+	/* Save current register contents for bailout */
+	reg0x3e = l64781_readreg(state, 0x3e);
+
+	/* Reading the POWER_DOWN register always returns 0 */
+	if (reg0x3e != 0) {
+	        dprintk("Device doesn't look like L64781\n");
+		goto error;
+	}
+
+	/* Turn the chip off */
+	l64781_writereg (state, 0x3e, 0x5a);
+
+	/* Responds to all reads with 0 */
+	if (l64781_readreg(state, 0x1a) != 0) {
+ 	        dprintk("Read 1 returned unexpcted value\n");
+		goto error;
+	}
+
+	/* Turn the chip on */
+	l64781_writereg (state, 0x3e, 0xa5);
+
+	/* Responds with register default value */
+	if (l64781_readreg(state, 0x1a) != 0xa1) {
+ 	        dprintk("Read 2 returned unexpcted value\n");
+		goto error;
+	}
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (reg0x3e >= 0) l64781_writereg (state, 0x3e, reg0x3e);  /* restore reg 0x3e */
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops l64781_ops = {
+
+	.info = {
+		.name = "LSI L64781 DVB-T",
+		.type = FE_OFDM,
+	/*	.frequency_min = ???,*/
+	/*	.frequency_max = ???,*/
+		.frequency_stepsize = 166666,
+	/*      .frequency_tolerance = ???,*/
+	/*      .symbol_rate_tolerance = ???,*/
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+		      FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+        	      FE_CAN_MUTE_TS
+	},
+
+     	.release = l64781_release,
+
+	.init = l64781_init,
+	.sleep = l64781_sleep,
+
+	.set_frontend = apply_frontend_param,
+	.get_frontend = get_frontend,
+	.get_tune_settings = l64781_get_tune_settings,
+
+	.read_status = l64781_read_status,
+	.read_ber = l64781_read_ber,
+	.read_signal_strength = l64781_read_signal_strength,
+	.read_snr = l64781_read_snr,
+	.read_ucblocks = l64781_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(l64781_attach);
diff -puN /dev/null drivers/media/dvb/frontends/l64781.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/l64781.h	2004-11-21 17:13:23.442054648 -0800
@@ -0,0 +1,43 @@
+/*
+    driver for LSI L64781 COFDM demodulator
+
+    Copyright (C) 2001 Holger Waechtler <holger@convergence.de>
+                       for Convergence Integrated Media GmbH
+                       Marko Kohtala <marko.kohtala@nokia.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef L64781_H
+#define L64781_H
+
+#include <linux/dvb/frontend.h>
+
+struct l64781_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+
+extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+					  struct i2c_adapter* i2c);
+
+#endif // L64781_H
diff -puN drivers/media/dvb/frontends/mt312.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt312.c
--- 25/drivers/media/dvb/frontends/mt312.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.383063616 -0800
+++ 25-akpm/drivers/media/dvb/frontends/mt312.c	2004-11-21 17:13:23.447053888 -0800
@@ -1,5 +1,5 @@
 /* 
-    Driver for Zarlink MT312 Satellite Channel Decoder
+    Driver for Zarlink VP310/MT312 Satellite Channel Decoder
 
     Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
 
@@ -31,71 +31,52 @@
 #include <linux/moduleparam.h>
 
 #include "dvb_frontend.h"
+#include "mt312_priv.h"
 #include "mt312.h"
 
-#define FRONTEND_NAME "dvbfe_mt312"
 
-#define dprintk(args...) \
-	do { \
-		if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \
-	} while (0)
+struct mt312_state {
 
-static int debug;
+	struct i2c_adapter* i2c;
 
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct mt312_config* config;
 
+	struct dvb_frontend frontend;
 
-#define I2C_ADDR_MT312		0x0e
-#define I2C_ADDR_SL1935		0x61
-#define I2C_ADDR_TSA5059	0x61
+	u8 id;
+	u8 frequency;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "mt312: " args); \
+	} while (0)
 
 #define MT312_SYS_CLK		90000000UL	/* 90 MHz */
 #define MT312_LPOWER_SYS_CLK	60000000UL	/* 60 MHz */
 #define MT312_PLL_CLK		10000000UL	/* 10 MHz */
 
-static struct dvb_frontend_info mt312_info = {
-	.name = "Zarlink MT312",
-	.type = FE_QPSK,
-	.frequency_min = 950000,
-	.frequency_max = 2150000,
-	.frequency_stepsize = (MT312_PLL_CLK / 1000) / 128,
-	/*.frequency_tolerance = 29500,         FIXME: binary compatibility waste? */
-	.symbol_rate_min = MT312_SYS_CLK / 128,
-	.symbol_rate_max = MT312_SYS_CLK / 2,
-	/*.symbol_rate_tolerance = 500,         FIXME: binary compatibility waste? 2% */
-	.notifier_delay = 0,
-	.caps =
-	    FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
-	    FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
-	    FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS | 
-            FE_CAN_RECOVER
-};
-
-struct mt312_state {
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-	int id;
-};
-
-static int mt312_read(struct i2c_adapter *i2c,
-		      const enum mt312_reg_addr reg, void *buf,
-		      const size_t count)
+static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
+		      void *buf, const size_t count)
 {
 	int ret;
 	struct i2c_msg msg[2];
 	u8 regbuf[1] = { reg };
 
-	msg[0].addr = I2C_ADDR_MT312;
+	msg[0].addr = state->config->demod_address;
 	msg[0].flags = 0;
 	msg[0].buf = regbuf;
 	msg[0].len = 1;
-	msg[1].addr = I2C_ADDR_MT312;
+	msg[1].addr = state->config->demod_address;
 	msg[1].flags = I2C_M_RD;
 	msg[1].buf = buf;
 	msg[1].len = count;
 
-	ret = i2c_transfer(i2c, msg, 2);
+	ret = i2c_transfer(state->i2c, msg, 2);
 
 	if (ret != 2) {
 		printk(KERN_ERR "%s: ret == %d\n", __FUNCTION__, ret);
@@ -113,9 +94,8 @@ static int mt312_read(struct i2c_adapter
 	return 0;
 }
 
-static int mt312_write(struct i2c_adapter *i2c,
-		       const enum mt312_reg_addr reg, const void *src,
-		       const size_t count)
+static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg,
+		       const void *src, const size_t count)
 {
 	int ret;
 	u8 buf[count + 1];
@@ -132,12 +112,12 @@ static int mt312_write(struct i2c_adapte
 	buf[0] = reg;
 	memcpy(&buf[1], src, count);
 
-	msg.addr = I2C_ADDR_MT312;
+	msg.addr = state->config->demod_address;
 	msg.flags = 0;
 	msg.buf = buf;
 	msg.len = count + 1;
 
-	ret = i2c_transfer(i2c, &msg, 1);
+	ret = i2c_transfer(state->i2c, &msg, 1);
 
 	if (ret != 1) {
 		dprintk("%s: ret == %d\n", __FUNCTION__, ret);
@@ -147,123 +127,136 @@ static int mt312_write(struct i2c_adapte
 	return 0;
 }
 
-static inline int mt312_readreg(struct i2c_adapter *i2c,
+static inline int mt312_readreg(struct mt312_state* state,
 				const enum mt312_reg_addr reg, u8 * val)
 {
-	return mt312_read(i2c, reg, val, 1);
+	return mt312_read(state, reg, val, 1);
 }
 
-static inline int mt312_writereg(struct i2c_adapter *i2c,
+static inline int mt312_writereg(struct mt312_state* state,
 				 const enum mt312_reg_addr reg, const u8 val)
 {
-	return mt312_write(i2c, reg, &val, 1);
+	return mt312_write(state, reg, &val, 1);
 }
 
-static int mt312_pll_write(struct i2c_adapter *i2c, const u8 addr,
-			   u8 * buf, const u8 len)
+static inline u32 mt312_div(u32 a, u32 b)
+{
+	return (a + (b / 2)) / b;
+}
+
+static int mt312_reset(struct mt312_state* state, const u8 full)
+{
+	return mt312_writereg(state, RESET, full ? 0x80 : 0x40);
+}
+
+static int mt312_get_inversion(struct mt312_state* state,
+			       fe_spectral_inversion_t *i)
 {
 	int ret;
-	struct i2c_msg msg;
+	u8 vit_mode;
 
-	msg.addr = addr;
-	msg.flags = 0;
-	msg.buf = buf;
-	msg.len = len;
+	if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0)
+		return ret;
+
+	if (vit_mode & 0x80)	/* auto inversion was used */
+		*i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF;
+
+	return 0;
+}
+
+static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
+{
+	int ret;
+	u8 sym_rate_h;
+	u8 dec_ratio;
+	u16 sym_rat_op;
+	u16 monitor;
+	u8 buf[2];
 
-	if ((ret = mt312_writereg(i2c, GPP_CTRL, 0x40)) < 0)
+	if ((ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h)) < 0)
 		return ret;
 
-	if ((ret = i2c_transfer(i2c, &msg, 1)) != 1)
-		printk(KERN_ERR "%s: i/o error (ret == %d)\n", __FUNCTION__, ret);
+	if (sym_rate_h & 0x80) {	/* symbol rate search was used */
+		if ((ret = mt312_writereg(state, MON_CTRL, 0x03)) < 0)
+			return ret;
 
-	if ((ret = mt312_writereg(i2c, GPP_CTRL, 0x00)) < 0)
+		if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
 		return ret;
 
-	return 0;
+		monitor = (buf[0] << 8) | buf[1];
+
+		dprintk(KERN_DEBUG "sr(auto) = %u\n",
+		       mt312_div(monitor * 15625, 4));
+	} else {
+		if ((ret = mt312_writereg(state, MON_CTRL, 0x05)) < 0)
+			return ret;
+
+		if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+			return ret;
+
+		dec_ratio = ((buf[0] >> 5) & 0x07) * 32;
+
+		if ((ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf))) < 0)
+			return ret;
+
+		sym_rat_op = (buf[0] << 8) | buf[1];
+
+		dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n",
+		       sym_rat_op, dec_ratio);
+		dprintk(KERN_DEBUG "*sr(manual) = %lu\n",
+		       (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) *
+			2) - dec_ratio);
 }
 
-static inline u32 mt312_div(u32 a, u32 b)
-{
-	return (a + (b / 2)) / b;
+	return 0;
 }
 
-static int sl1935_set_tv_freq(struct i2c_adapter *i2c, u32 freq, u32 sr)
+static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
 {
-	/* 155 uA, Baseband Path B */
-	u8 buf[4] = { 0x00, 0x00, 0x80, 0x00 };
+	const fe_code_rate_t fec_tab[8] =
+	    { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8,
+		FEC_AUTO, FEC_AUTO };
 
-	u8 exp;
-	u32 ref;
-	u32 div;
-
-	if (sr < 10000000) {	/* 1-10 MSym/s: ratio 2 ^ 3 */
-		exp = 3;
-		buf[2] |= 0x40;	/* 690 uA */
-	} else if (sr < 15000000) {	/* 10-15 MSym/s: ratio 2 ^ 4 */
-		exp = 4;
-		buf[2] |= 0x20;	/* 330 uA */
-	} else {		/* 15-45 MSym/s: ratio 2 ^ 7 */
-		exp = 7;
-		buf[3] |= 0x08;	/* Baseband Path A */
+	int ret;
+	u8 fec_status;
+
+	if ((ret = mt312_readreg(state, FEC_STATUS, &fec_status)) < 0)
+		return ret;
+
+	*cr = fec_tab[(fec_status >> 4) & 0x07];
+
+	return 0;
 	}
 
-	div = mt312_div(MT312_PLL_CLK, 1 << exp);
-	ref = mt312_div(freq * 1000, div);
-	mt312_info.frequency_stepsize = mt312_div(div, 1000);
 
-	buf[0] = (ref >> 8) & 0x7f;
-	buf[1] = (ref >> 0) & 0xff;
-	buf[2] |= (exp - 1);
 
-	if (freq < 1550000)
-		buf[3] |= 0x10;
 
-	dprintk(KERN_INFO "synth dword = %02x%02x%02x%02x\n", buf[0],
-	       buf[1], buf[2], buf[3]);
 
-	return mt312_pll_write(i2c, I2C_ADDR_SL1935, buf, sizeof(buf));
-}
 
-static int tsa5059_set_tv_freq(struct i2c_adapter *i2c, u32 freq, u32 sr)
-{
-	u8 buf[4];
 
-	u32 ref = mt312_div(freq, 125);
 
-	buf[0] = (ref >> 8) & 0x7f;
-	buf[1] = (ref >> 0) & 0xff;
-	buf[2] = 0x84 | ((ref >> 10) & 0x60);
-	buf[3] = 0x80;
 	
-	if (freq < 1550000)
-		buf[3] |= 0x02;
 
-	dprintk(KERN_INFO "synth dword = %02x%02x%02x%02x\n", buf[0],
-	       buf[1], buf[2], buf[3]);
 
-	return mt312_pll_write(i2c, I2C_ADDR_TSA5059, buf, sizeof(buf));
-}
 
-static int mt312_reset(struct i2c_adapter *i2c, const u8 full)
-{
-	return mt312_writereg(i2c, RESET, full ? 0x80 : 0x40);
-}
 
-static int mt312_initfe(struct mt312_state *state, u8 pll)
+
+
+static int mt312_initfe(struct dvb_frontend* fe)
 {
-	struct i2c_adapter *i2c = state->i2c;
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 buf[2];
 
 	/* wake up */
-	if ((ret = mt312_writereg(i2c, CONFIG, (pll == 60 ? 0x88 : 0x8c))) < 0)
+	if ((ret = mt312_writereg(state, CONFIG, (state->frequency == 60 ? 0x88 : 0x8c))) < 0)
 		return ret;
 
 	/* wait at least 150 usec */
 	udelay(150);
 
 	/* full reset */
-	if ((ret = mt312_reset(i2c, 1)) < 0)
+	if ((ret = mt312_reset(state, 1)) < 0)
 		return ret;
 
 // Per datasheet, write correct values. 09/28/03 ACCJr.
@@ -271,56 +264,63 @@ static int mt312_initfe(struct mt312_sta
 	{
 		u8 buf_def[8]={0x14, 0x12, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00};
 
-		if ((ret = mt312_write(i2c, VIT_SETUP, buf_def, sizeof(buf_def))) < 0)
+		if ((ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def))) < 0)
 			return ret;
 	}
 
 	/* SYS_CLK */
-	buf[0] = mt312_div((pll == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000);
+	buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000);
 
 	/* DISEQC_RATIO */
 	buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4);
 
-	if ((ret = mt312_write(i2c, SYS_CLK, buf, sizeof(buf))) < 0)
+	if ((ret = mt312_write(state, SYS_CLK, buf, sizeof(buf))) < 0)
 		return ret;
 
-	if ((ret = mt312_writereg(i2c, SNR_THS_HIGH, 0x32)) < 0)
+	if ((ret = mt312_writereg(state, SNR_THS_HIGH, 0x32)) < 0)
 		return ret;
 
-	if ((ret = mt312_writereg(i2c, OP_CTRL, 0x53)) < 0)
+	if ((ret = mt312_writereg(state, OP_CTRL, 0x53)) < 0)
 		return ret;
 
 	/* TS_SW_LIM */
 	buf[0] = 0x8c;
 	buf[1] = 0x98;
 
-	if ((ret = mt312_write(i2c, TS_SW_LIM_L, buf, sizeof(buf))) < 0)
+	if ((ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf))) < 0)
 		return ret;
 
-	if ((ret = mt312_writereg(i2c, CS_SW_LIM, 0x69)) < 0)
+	if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0)
 		return ret;
 
+	if (state->config->pll_init) {
+		mt312_writereg(state, GPP_CTRL, 0x40);
+		state->config->pll_init(fe);
+		mt312_writereg(state, GPP_CTRL, 0x00);
+	}
+
 	return 0;
 }
 
-static int mt312_send_master_cmd(struct i2c_adapter *i2c,
-				 const struct dvb_diseqc_master_cmd *c)
+static int mt312_send_master_cmd(struct dvb_frontend* fe,
+				 struct dvb_diseqc_master_cmd *c)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 diseqc_mode;
 
 	if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg)))
 		return -EINVAL;
 
-	if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0)
+	if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
 		return ret;
 
 	if ((ret =
-	     mt312_write(i2c, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0)
+	     mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0)
 		return ret;
 
 	if ((ret =
-	     mt312_writereg(i2c, DISEQC_MODE,
+	     mt312_writereg(state, DISEQC_MODE,
 			    (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3)
 			    | 0x04)) < 0)
 		return ret;
@@ -328,21 +328,15 @@ static int mt312_send_master_cmd(struct 
 	/* set DISEQC_MODE[2:0] to zero if a return message is expected */
 	if (c->msg[0] & 0x02)
 		if ((ret =
-		     mt312_writereg(i2c, DISEQC_MODE, (diseqc_mode & 0x40))) < 0)
+		     mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40))) < 0)
 			return ret;
 
 	return 0;
 }
 
-static int mt312_recv_slave_reply(struct i2c_adapter *i2c,
-				  struct dvb_diseqc_slave_reply *r)
-{
-	/* TODO */
-	return -EOPNOTSUPP;
-}
-
-static int mt312_send_burst(struct i2c_adapter *i2c, const fe_sec_mini_cmd_t c)
+static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	const u8 mini_tab[2] = { 0x02, 0x03 };
 
 	int ret;
@@ -351,19 +345,20 @@ static int mt312_send_burst(struct i2c_a
 	if (c > SEC_MINI_B)
 		return -EINVAL;
 
-	if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0)
+	if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
 		return ret;
 
 	if ((ret =
-	     mt312_writereg(i2c, DISEQC_MODE,
+	     mt312_writereg(state, DISEQC_MODE,
 			    (diseqc_mode & 0x40) | mini_tab[c])) < 0)
 		return ret;
 
 	return 0;
 }
 
-static int mt312_set_tone(struct i2c_adapter *i2c, const fe_sec_tone_mode_t t)
+static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	const u8 tone_tab[2] = { 0x01, 0x00 };
 
 	int ret;
@@ -372,36 +367,37 @@ static int mt312_set_tone(struct i2c_ada
 	if (t > SEC_TONE_OFF)
 		return -EINVAL;
 
-	if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0)
+	if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
 		return ret;
 
 	if ((ret =
-	     mt312_writereg(i2c, DISEQC_MODE,
+	     mt312_writereg(state, DISEQC_MODE,
 			    (diseqc_mode & 0x40) | tone_tab[t])) < 0)
 		return ret;
 
 	return 0;
 }
 
-static int mt312_set_voltage(struct i2c_adapter *i2c, const fe_sec_voltage_t v)
+static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	const u8 volt_tab[3] = { 0x00, 0x40, 0x00 };
 
 	if (v > SEC_VOLTAGE_OFF)
 		return -EINVAL;
 
-	return mt312_writereg(i2c, DISEQC_MODE, volt_tab[v]);
+	return mt312_writereg(state, DISEQC_MODE, volt_tab[v]);
 }
 
-static int mt312_read_status(struct mt312_state *state, fe_status_t *s)
+static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
 {
-	struct i2c_adapter *i2c = state->i2c;
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 status[3], vit_mode;
 
 	*s = 0;
 
-	if ((ret = mt312_read(i2c, QPSK_STAT_H, status, sizeof(status))) < 0)
+	if ((ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status))) < 0)
 		return ret;
 
 	dprintk(KERN_DEBUG "QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
@@ -419,24 +415,25 @@ static int mt312_read_status(struct mt31
 
 	// VP310 doesn't have AUTO, so we "implement it here" ACCJr
 	if ((state->id == ID_VP310) && !(status[0] & 0x01)) {
-		if ((ret = mt312_readreg(i2c, VIT_MODE, &vit_mode)) < 0)
+		if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0)
 			return ret;
 		vit_mode ^= 0x40;
-		if ((ret = mt312_writereg(i2c, VIT_MODE, vit_mode)) < 0)
+		if ((ret = mt312_writereg(state, VIT_MODE, vit_mode)) < 0)
                 	return ret;
-		if ((ret = mt312_writereg(i2c, GO, 0x01)) < 0)
+		if ((ret = mt312_writereg(state, GO, 0x01)) < 0)
                 	return ret;
 	}
 
 	return 0;
 }
 
-static int mt312_read_bercnt(struct i2c_adapter *i2c, u32 *ber)
+static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 buf[3];
 
-	if ((ret = mt312_read(i2c, RS_BERCNT_H, buf, 3)) < 0)
+	if ((ret = mt312_read(state, RS_BERCNT_H, buf, 3)) < 0)
 		return ret;
 
 	*ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64;
@@ -444,14 +441,15 @@ static int mt312_read_bercnt(struct i2c_
 	return 0;
 }
 
-static int mt312_read_agc(struct i2c_adapter *i2c, u16 *signal_strength)
+static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_strength)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 buf[3];
 	u16 agc;
 	s16 err_db;
 
-	if ((ret = mt312_read(i2c, AGC_H, buf, sizeof(buf))) < 0)
+	if ((ret = mt312_read(state, AGC_H, buf, sizeof(buf))) < 0)
 		return ret;
 
 	agc = (buf[0] << 6) | (buf[1] >> 2);
@@ -464,12 +462,13 @@ static int mt312_read_agc(struct i2c_ada
 	return 0;
 }
 
-static int mt312_read_snr(struct i2c_adapter *i2c, u16 *snr)
+static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 buf[2];
 
-	if ((ret = mt312_read(i2c, M_SNR_H, &buf, sizeof(buf))) < 0)
+	if ((ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf))) < 0)
 		return ret;
 
 	*snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1);
@@ -477,12 +476,13 @@ static int mt312_read_snr(struct i2c_ada
 	return 0;
 }
 
-static int mt312_read_ubc(struct i2c_adapter *i2c, u32 *ubc)
+static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 buf[2];
 
-	if ((ret = mt312_read(i2c, RS_UBC_H, &buf, sizeof(buf))) < 0)
+	if ((ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf))) < 0)
 		return ret;
 
 	*ubc = (buf[0] << 8) | buf[1];
@@ -490,10 +490,10 @@ static int mt312_read_ubc(struct i2c_ada
 	return 0;
 }
 
-static int mt312_set_frontend(struct mt312_state *state,
-			      const struct dvb_frontend_parameters *p)
+static int mt312_set_frontend(struct dvb_frontend* fe,
+			      struct dvb_frontend_parameters *p)
 {
-	struct i2c_adapter *i2c = state->i2c;
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 buf[5], config_val;
 	u16 sr;
@@ -502,20 +502,18 @@ static int mt312_set_frontend(struct mt3
 	    { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f };
 	const u8 inv_tab[3] = { 0x00, 0x40, 0x80 };
 
-	int (*set_tv_freq)(struct i2c_adapter *i2c, u32 freq, u32 sr);
-
 	dprintk("%s: Freq %d\n", __FUNCTION__, p->frequency);
 
-	if ((p->frequency < mt312_info.frequency_min)
-	    || (p->frequency > mt312_info.frequency_max))
+	if ((p->frequency < fe->ops->info.frequency_min)
+	    || (p->frequency > fe->ops->info.frequency_max))
 		return -EINVAL;
 
 	if ((p->inversion < INVERSION_OFF)
-	    || (p->inversion > INVERSION_AUTO))
+	    || (p->inversion > INVERSION_ON))
 		return -EINVAL;
 
-	if ((p->u.qpsk.symbol_rate < mt312_info.symbol_rate_min)
-	    || (p->u.qpsk.symbol_rate > mt312_info.symbol_rate_max))
+	if ((p->u.qpsk.symbol_rate < fe->ops->info.symbol_rate_min)
+	    || (p->u.qpsk.symbol_rate > fe->ops->info.symbol_rate_max))
 		return -EINVAL;
 
 	if ((p->u.qpsk.fec_inner < FEC_NONE)
@@ -530,31 +528,36 @@ static int mt312_set_frontend(struct mt3
 	case ID_VP310:
 	// For now we will do this only for the VP310.
 	// It should be better for the mt312 as well, but tunning will be slower. ACCJr 09/29/03
-		if ((ret = mt312_readreg(i2c, CONFIG, &config_val) < 0))
+		if ((ret = mt312_readreg(state, CONFIG, &config_val) < 0))
 			return ret;
 		if (p->u.qpsk.symbol_rate >= 30000000) //Note that 30MS/s should use 90MHz
 		{
-			if ((config_val & 0x0c) == 0x08) //We are running 60MHz
-				if ((ret = mt312_initfe(state, (u8) 90)) < 0)
+			if ((config_val & 0x0c) == 0x08) { //We are running 60MHz
+				state->frequency = 90;
+				if ((ret = mt312_initfe(fe)) < 0)
 					return ret;
 		}
+		}
 		else
 		{
-			if ((config_val & 0x0c) == 0x0C) //We are running 90MHz
-				if ((ret = mt312_initfe(state, (u8) 60)) < 0)
+			if ((config_val & 0x0c) == 0x0C) { //We are running 90MHz
+				state->frequency = 60;
+				if ((ret = mt312_initfe(fe)) < 0)
 					return ret;
 		}
-		set_tv_freq = tsa5059_set_tv_freq;
+		}
 		break;
+
 	case ID_MT312:
-		set_tv_freq = sl1935_set_tv_freq;
 		break;
+
 	default:
 		return -EINVAL;
 	}
 
-	if ((ret = set_tv_freq(i2c, p->frequency, p->u.qpsk.symbol_rate)) < 0)
-		return ret;
+	mt312_writereg(state, GPP_CTRL, 0x40);
+	state->config->pll_set(fe, p);
+	mt312_writereg(state, GPP_CTRL, 0x00);
 
 	/* sr = (u16)(sr * 256.0 / 1000000.0) */
 	sr = mt312_div(p->u.qpsk.symbol_rate * 4, 15625);
@@ -575,333 +578,154 @@ static int mt312_set_frontend(struct mt3
 	/* GO */
 	buf[4] = 0x01;
 
-	if ((ret = mt312_write(i2c, SYM_RATE_H, buf, sizeof(buf))) < 0)
-		return ret;
-
-        mt312_reset(i2c, 0);
-
-	return 0;
-}
-
-static int mt312_get_inversion(struct i2c_adapter *i2c,
-			       fe_spectral_inversion_t * i)
-{
-	int ret;
-	u8 vit_mode;
-
-	if ((ret = mt312_readreg(i2c, VIT_MODE, &vit_mode)) < 0)
-		return ret;
-
-	if (vit_mode & 0x80)	/* auto inversion was used */
-		*i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF;
-
-	return 0;
-}
-
-static int mt312_get_symbol_rate(struct i2c_adapter *i2c, u32 *sr)
-{
-	int ret;
-	u8 sym_rate_h;
-	u8 dec_ratio;
-	u16 sym_rat_op;
-	u16 monitor;
-	u8 buf[2];
-
-	if ((ret = mt312_readreg(i2c, SYM_RATE_H, &sym_rate_h)) < 0)
+	if ((ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf))) < 0)
 		return ret;
 
-	if (sym_rate_h & 0x80) {	/* symbol rate search was used */
-		if ((ret = mt312_writereg(i2c, MON_CTRL, 0x03)) < 0)
-			return ret;
-
-		if ((ret = mt312_read(i2c, MONITOR_H, buf, sizeof(buf))) < 0)
-			return ret;
-
-		monitor = (buf[0] << 8) | buf[1];
-
-		dprintk(KERN_DEBUG "sr(auto) = %u\n",
-		       mt312_div(monitor * 15625, 4));
-	} else {
-		if ((ret = mt312_writereg(i2c, MON_CTRL, 0x05)) < 0)
-			return ret;
-
-		if ((ret = mt312_read(i2c, MONITOR_H, buf, sizeof(buf))) < 0)
-			return ret;
-
-		dec_ratio = ((buf[0] >> 5) & 0x07) * 32;
-
-		if ((ret = mt312_read(i2c, SYM_RAT_OP_H, buf, sizeof(buf))) < 0)
-			return ret;
-
-		sym_rat_op = (buf[0] << 8) | buf[1];
-
-		dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n",
-		       sym_rat_op, dec_ratio);
-		dprintk(KERN_DEBUG "*sr(manual) = %lu\n",
-		       (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) *
-			2) - dec_ratio);
-	}
-
-	return 0;
-}
-
-static int mt312_get_code_rate(struct i2c_adapter *i2c, fe_code_rate_t *cr)
-{
-	const fe_code_rate_t fec_tab[8] =
-	    { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8,
-		FEC_AUTO, FEC_AUTO };
-
-	int ret;
-	u8 fec_status;
-
-	if ((ret = mt312_readreg(i2c, FEC_STATUS, &fec_status)) < 0)
-		return ret;
-
-	*cr = fec_tab[(fec_status >> 4) & 0x07];
+        mt312_reset(state, 0);
 
 	return 0;
 }
 
-static int mt312_get_frontend(struct i2c_adapter *i2c,
+static int mt312_get_frontend(struct dvb_frontend* fe,
 			      struct dvb_frontend_parameters *p)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 
-	if ((ret = mt312_get_inversion(i2c, &p->inversion)) < 0)
+	if ((ret = mt312_get_inversion(state, &p->inversion)) < 0)
 		return ret;
 
-	if ((ret = mt312_get_symbol_rate(i2c, &p->u.qpsk.symbol_rate)) < 0)
+	if ((ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate)) < 0)
 		return ret;
 
-	if ((ret = mt312_get_code_rate(i2c, &p->u.qpsk.fec_inner)) < 0)
+	if ((ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner)) < 0)
 		return ret;
 
 	return 0;
 }
 
-static int mt312_sleep(struct i2c_adapter *i2c)
+static int mt312_sleep(struct dvb_frontend* fe)
 {
+	struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
 	int ret;
 	u8 config;
 
 	/* reset all registers to defaults */
-	if ((ret = mt312_reset(i2c, 1)) < 0)
+	if ((ret = mt312_reset(state, 1)) < 0)
 		return ret;
 
-	if ((ret = mt312_readreg(i2c, CONFIG, &config)) < 0)
+	if ((ret = mt312_readreg(state, CONFIG, &config)) < 0)
 		return ret;
 
 	/* enter standby */
-	if ((ret = mt312_writereg(i2c, CONFIG, config & 0x7f)) < 0)
+	if ((ret = mt312_writereg(state, CONFIG, config & 0x7f)) < 0)
 		return ret;
 
 	return 0;
 }
 
-static int mt312_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
-{
-	struct mt312_state *state = fe->data;
-	struct i2c_adapter *i2c = state->i2c;
-
-	switch (cmd) {
-	case FE_GET_INFO:
-		memcpy(arg, &mt312_info, sizeof(struct dvb_frontend_info));
-		break;
-
-	case FE_DISEQC_RESET_OVERLOAD:
-		return -EOPNOTSUPP;
-
-	case FE_DISEQC_SEND_MASTER_CMD:
-		return mt312_send_master_cmd(i2c, arg);
-
-	case FE_DISEQC_RECV_SLAVE_REPLY:
-		if (state->id == ID_MT312)
-			return mt312_recv_slave_reply(i2c, arg);
-		else
-			return -EOPNOTSUPP;
-
-	case FE_DISEQC_SEND_BURST:
-		return mt312_send_burst(i2c, (fe_sec_mini_cmd_t) arg);
-
-	case FE_SET_TONE:
-		return mt312_set_tone(i2c, (fe_sec_tone_mode_t) arg);
-
-	case FE_SET_VOLTAGE:
-		return mt312_set_voltage(i2c, (fe_sec_voltage_t) arg);
-
-	case FE_ENABLE_HIGH_LNB_VOLTAGE:
-		return -EOPNOTSUPP;
-
-	case FE_READ_STATUS:
-		return mt312_read_status(state, arg);
-
-	case FE_READ_BER:
-		return mt312_read_bercnt(i2c, arg);
-
-	case FE_READ_SIGNAL_STRENGTH:
-		return mt312_read_agc(i2c, arg);
-
-	case FE_READ_SNR:
-		return mt312_read_snr(i2c, arg);
-
-	case FE_READ_UNCORRECTED_BLOCKS:
-		return mt312_read_ubc(i2c, arg);
-
-	case FE_SET_FRONTEND:
-		return mt312_set_frontend(state, arg);
-
-	case FE_GET_FRONTEND:
-		return mt312_get_frontend(i2c, arg);
-
-	case FE_GET_EVENT:
-		return -EOPNOTSUPP;
-
-	case FE_SLEEP:
-		return mt312_sleep(i2c);
-
-	case FE_INIT:
-		/* For the VP310 we should run at 60MHz when ever possible.
-		 * It should be better to run the mt312 ar lower speed when
-		 * ever possible, but tunning will be slower. ACCJr 09/29/03
-		 */
-		if (state->id == ID_MT312)
-			return mt312_initfe(state, (u8) 90);
-		else
-			return mt312_initfe(state, (u8) 60);
-
-	case FE_GET_TUNE_SETTINGS:
+static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
 	{
-	        struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
 	        fesettings->min_delay_ms = 50;
 	        fesettings->step_size = 0;
 	        fesettings->max_drift = 0;
 	        return 0;
 	}	    
 
-	default:
-		return -ENOIOCTLCMD;
-	}
-
-	return 0;
-}
-
-static struct i2c_client client_template;
-
-static int mt312_attach_adapter(struct i2c_adapter *adapter)
+static void mt312_release(struct dvb_frontend* fe)
 {
-	struct mt312_state *state;
-	struct i2c_client *client;
-	int ret;
-	u8 id;
-
-	dprintk("Trying to attach to adapter 0x%x:%s.\n",
-		adapter->id, adapter->name);
-
-	if (mt312_readreg(adapter, ID, &id) < 0)
-		return -ENODEV;
-
-	if ((id != ID_VP310) && (id != ID_MT312))
-		return -ENODEV;
-
-	if ( !(state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL)) )
-		return -ENOMEM;
-
-	memset(state, 0, sizeof(struct mt312_state));
-	state->i2c = adapter;
-	state->id = id;
-
-	if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) {
-		kfree(state);
-		return -ENOMEM;
-	}
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = I2C_ADDR_MT312;
-	i2c_set_clientdata(client, state);
-
-	if ((ret = i2c_attach_client(client))) {
-		kfree(client);
-		kfree(state);
-		return ret;
-	}
-
-	BUG_ON(!state->dvb);
-
-	if ((ret = dvb_register_frontend(mt312_ioctl, state->dvb, state,
-					     &mt312_info, THIS_MODULE))) {
-		i2c_detach_client(client);
-		kfree(client);
-		kfree(state);
-		return ret;
-	}
-
-	return 0;
-}
-
-static int mt312_detach_client(struct i2c_client *client)
-{
-	struct mt312_state *state = i2c_get_clientdata(client);
-
-	dprintk ("%s\n", __FUNCTION__);
-
-	dvb_unregister_frontend (mt312_ioctl, state->dvb);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
+	struct mt312_state* state = (struct mt312_state*) fe->demodulator_priv;
 	kfree(state);
-	return 0;
 }
 
-static int mt312_command (struct i2c_client *client, unsigned int cmd, void *arg)
+static struct dvb_frontend_ops mt312_ops;
+
+struct dvb_frontend* mt312_attach(const struct mt312_config* config,
+				  struct i2c_adapter* i2c)
 {
-	struct mt312_state *state = i2c_get_clientdata(client);
+	struct mt312_state* state = NULL;
 
-	dprintk ("%s\n", __FUNCTION__);
+	/* allocate memory for the internal state */
+	state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+	if (state == NULL) goto error;
 
-	switch (cmd) {
-	case FE_REGISTER:
-		state->dvb = arg;
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &mt312_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if (mt312_readreg(state, ID, &state->id) < 0) goto error;
+	switch(state->id) {
+	case ID_VP310:
+		state->frequency = 90;
+		printk("mt312: Detected Zarlink VP310\n");
 		break;
-	case FE_UNREGISTER:
-		state->dvb = NULL;
+
+	case ID_MT312:
+		state->frequency = 60;
+		printk("mt312: Detected Zarlink MT312\n");
 		break;
+
 	default:
-		return -EOPNOTSUPP;
-	}
-	return 0;
+		goto error;
 }
 
-static struct i2c_driver driver = {
-	.owner 		= THIS_MODULE,
-	.name 		= FRONTEND_NAME,
-	.id 		= I2C_DRIVERID_DVBFE_MT312,
-	.flags 		= I2C_DF_NOTIFY,
-	.attach_adapter = mt312_attach_adapter,
-	.detach_client 	= mt312_detach_client,
-	.command 	= mt312_command,
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops mt312_ops = {
+
+	.info = {
+		.name = "Zarlink VP310/MT312 DVB-S",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = (MT312_PLL_CLK / 1000) / 128,
+		/*.frequency_tolerance = 29500,         FIXME: binary compatibility waste? */
+		.symbol_rate_min = MT312_SYS_CLK / 128,
+		.symbol_rate_max = MT312_SYS_CLK / 2,
+		/*.symbol_rate_tolerance = 500,         FIXME: binary compatibility waste? 2% */
+		.caps =
+		    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+		    FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+		    FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS |
+	            FE_CAN_RECOVER
+	},
+
+     	.release = mt312_release,
+
+	.init = mt312_initfe,
+	.sleep = mt312_sleep,
+
+	.set_frontend = mt312_set_frontend,
+	.get_frontend = mt312_get_frontend,
+	.get_tune_settings = mt312_get_tune_settings,
+
+	.read_status = mt312_read_status,
+	.read_ber = mt312_read_ber,
+	.read_signal_strength = mt312_read_signal_strength,
+	.read_snr = mt312_read_snr,
+	.read_ucblocks = mt312_read_ucblocks,
+
+	.diseqc_send_master_cmd = mt312_send_master_cmd,
+	.diseqc_send_burst = mt312_send_burst,
+	.set_tone = mt312_set_tone,
+	.set_voltage = mt312_set_voltage,
 };
 
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags 		= I2C_CLIENT_ALLOW_USE,
-	.driver  	= &driver,
-};
-
-static int __init mt312_module_init(void)
-{
-	return i2c_add_driver(&driver);
-}
-
-static void __exit mt312_module_exit(void)
-{
-	if (i2c_del_driver(&driver))
-		printk(KERN_ERR "mt312: driver deregistration failed.\n");
-}
-
-module_init(mt312_module_init);
-module_exit(mt312_module_exit);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-MODULE_DESCRIPTION("MT312 Satellite Channel Decoder Driver");
+MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver");
 MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
 MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(mt312_attach);
diff -puN drivers/media/dvb/frontends/mt312.h~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt312.h
--- 25/drivers/media/dvb/frontends/mt312.h~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.384063464 -0800
+++ 25-akpm/drivers/media/dvb/frontends/mt312.h	2004-11-21 17:13:23.448053736 -0800
@@ -1,5 +1,5 @@
 /* 
-    Driver for Zarlink MT312 QPSK Frontend
+    Driver for Zarlink MT312 Satellite Channel Decoder
 
     Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
 
@@ -18,145 +18,27 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+    References:
+    http://products.zarlink.com/product_profiles/MT312.htm
+    http://products.zarlink.com/product_profiles/SL1935.htm
 */
 
-#ifndef _DVB_FRONTENDS_MT312
-#define _DVB_FRONTENDS_MT312
+#ifndef MT312_H
+#define MT312_H
 
-enum mt312_reg_addr {
-	QPSK_INT_H = 0,
-	QPSK_INT_M = 1,
-	QPSK_INT_L = 2,
-	FEC_INT = 3,
-	QPSK_STAT_H = 4,
-	QPSK_STAT_L = 5,
-	FEC_STATUS = 6,
-	LNB_FREQ_H = 7,
-	LNB_FREQ_L = 8,
-	M_SNR_H = 9,
-	M_SNR_L = 10,
-	VIT_ERRCNT_H = 11,
-	VIT_ERRCNT_M = 12,
-	VIT_ERRCNT_L = 13,
-	RS_BERCNT_H = 14,
-	RS_BERCNT_M = 15,
-	RS_BERCNT_L = 16,
-	RS_UBC_H = 17,
-	RS_UBC_L = 18,
-	SIG_LEVEL = 19,
-	GPP_CTRL = 20,
-	RESET = 21,
-	DISEQC_MODE = 22,
-	SYM_RATE_H = 23,
-	SYM_RATE_L = 24,
-	VIT_MODE = 25,
-	QPSK_CTRL = 26,
-	GO = 27,
-	IE_QPSK_H = 28,
-	IE_QPSK_M = 29,
-	IE_QPSK_L = 30,
-	IE_FEC = 31,
-	QPSK_STAT_EN = 32,
-	FEC_STAT_EN = 33,
-	SYS_CLK = 34,
-	DISEQC_RATIO = 35,
-	DISEQC_INSTR = 36,
-	FR_LIM = 37,
-	FR_OFF = 38,
-	AGC_CTRL = 39,
-	AGC_INIT = 40,
-	AGC_REF = 41,
-	AGC_MAX = 42,
-	AGC_MIN = 43,
-	AGC_LK_TH = 44,
-	TS_AGC_LK_TH = 45,
-	AGC_PWR_SET = 46,
-	QPSK_MISC = 47,
-	SNR_THS_LOW = 48,
-	SNR_THS_HIGH = 49,
-	TS_SW_RATE = 50,
-	TS_SW_LIM_L = 51,
-	TS_SW_LIM_H = 52,
-	CS_SW_RATE_1 = 53,
-	CS_SW_RATE_2 = 54,
-	CS_SW_RATE_3 = 55,
-	CS_SW_RATE_4 = 56,
-	CS_SW_LIM = 57,
-	TS_LPK = 58,
-	TS_LPK_M = 59,
-	TS_LPK_L = 60,
-	CS_KPROP_H = 61,
-	CS_KPROP_L = 62,
-	CS_KINT_H = 63,
-	CS_KINT_L = 64,
-	QPSK_SCALE = 65,
-	TLD_OUTCLK_TH = 66,
-	TLD_INCLK_TH = 67,
-	FLD_TH = 68,
-	PLD_OUTLK3 = 69,
-	PLD_OUTLK2 = 70,
-	PLD_OUTLK1 = 71,
-	PLD_OUTLK0 = 72,
-	PLD_INLK3 = 73,
-	PLD_INLK2 = 74,
-	PLD_INLK1 = 75,
-	PLD_INLK0 = 76,
-	PLD_ACC_TIME = 77,
-	SWEEP_PAR = 78,
-	STARTUP_TIME = 79,
-	LOSSLOCK_TH = 80,
-	FEC_LOCK_TM = 81,
-	LOSSLOCK_TM = 82,
-	VIT_ERRPER_H = 83,
-	VIT_ERRPER_M = 84,
-	VIT_ERRPER_L = 85,
-	VIT_SETUP = 86,
-	VIT_REF0 = 87,
-	VIT_REF1 = 88,
-	VIT_REF2 = 89,
-	VIT_REF3 = 90,
-	VIT_REF4 = 91,
-	VIT_REF5 = 92,
-	VIT_REF6 = 93,
-	VIT_MAXERR = 94,
-	BA_SETUPT = 95,
-	OP_CTRL = 96,
-	FEC_SETUP = 97,
-	PROG_SYNC = 98,
-	AFC_SEAR_TH = 99,
-	CSACC_DIF_TH = 100,
-	QPSK_LK_CT = 101,
-	QPSK_ST_CT = 102,
-	MON_CTRL = 103,
-	QPSK_RESET = 104,
-	QPSK_TST_CT = 105,
-	QPSK_TST_ST = 106,
-	TEST_R = 107,
-	AGC_H = 108,
-	AGC_M = 109,
-	AGC_L = 110,
-	FREQ_ERR1_H = 111,
-	FREQ_ERR1_M = 112,
-	FREQ_ERR1_L = 113,
-	FREQ_ERR2_H = 114,
-	FREQ_ERR2_L = 115,
-	SYM_RAT_OP_H = 116,
-	SYM_RAT_OP_L = 117,
-	DESEQC2_INT = 118,
-	DISEQC2_STAT = 119,
-	DISEQC2_FIFO = 120,
-	DISEQC2_CTRL1 = 121,
-	DISEQC2_CTRL2 = 122,
-	MONITOR_H = 123,
-	MONITOR_L = 124,
-	TEST_MODE = 125,
-	ID = 126,
-	CONFIG = 127
-};
+#include <linux/dvb/frontend.h>
 
-enum mt312_model_id {
-	ID_VP310 = 1,
-	ID_MT312 = 3
+struct mt312_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
 };
 
-#endif				/* DVB_FRONTENDS_MT312 */
+extern struct dvb_frontend* mt312_attach(const struct mt312_config* config,
+					 struct i2c_adapter* i2c);
+
+#endif // MT312_H
diff -puN /dev/null drivers/media/dvb/frontends/mt312_priv.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/mt312_priv.h	2004-11-21 17:13:23.449053584 -0800
@@ -0,0 +1,162 @@
+/*
+    Driver for Zarlink MT312 QPSK Frontend
+
+    Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _DVB_FRONTENDS_MT312_PRIV
+#define _DVB_FRONTENDS_MT312_PRIV
+
+enum mt312_reg_addr {
+	QPSK_INT_H = 0,
+	QPSK_INT_M = 1,
+	QPSK_INT_L = 2,
+	FEC_INT = 3,
+	QPSK_STAT_H = 4,
+	QPSK_STAT_L = 5,
+	FEC_STATUS = 6,
+	LNB_FREQ_H = 7,
+	LNB_FREQ_L = 8,
+	M_SNR_H = 9,
+	M_SNR_L = 10,
+	VIT_ERRCNT_H = 11,
+	VIT_ERRCNT_M = 12,
+	VIT_ERRCNT_L = 13,
+	RS_BERCNT_H = 14,
+	RS_BERCNT_M = 15,
+	RS_BERCNT_L = 16,
+	RS_UBC_H = 17,
+	RS_UBC_L = 18,
+	SIG_LEVEL = 19,
+	GPP_CTRL = 20,
+	RESET = 21,
+	DISEQC_MODE = 22,
+	SYM_RATE_H = 23,
+	SYM_RATE_L = 24,
+	VIT_MODE = 25,
+	QPSK_CTRL = 26,
+	GO = 27,
+	IE_QPSK_H = 28,
+	IE_QPSK_M = 29,
+	IE_QPSK_L = 30,
+	IE_FEC = 31,
+	QPSK_STAT_EN = 32,
+	FEC_STAT_EN = 33,
+	SYS_CLK = 34,
+	DISEQC_RATIO = 35,
+	DISEQC_INSTR = 36,
+	FR_LIM = 37,
+	FR_OFF = 38,
+	AGC_CTRL = 39,
+	AGC_INIT = 40,
+	AGC_REF = 41,
+	AGC_MAX = 42,
+	AGC_MIN = 43,
+	AGC_LK_TH = 44,
+	TS_AGC_LK_TH = 45,
+	AGC_PWR_SET = 46,
+	QPSK_MISC = 47,
+	SNR_THS_LOW = 48,
+	SNR_THS_HIGH = 49,
+	TS_SW_RATE = 50,
+	TS_SW_LIM_L = 51,
+	TS_SW_LIM_H = 52,
+	CS_SW_RATE_1 = 53,
+	CS_SW_RATE_2 = 54,
+	CS_SW_RATE_3 = 55,
+	CS_SW_RATE_4 = 56,
+	CS_SW_LIM = 57,
+	TS_LPK = 58,
+	TS_LPK_M = 59,
+	TS_LPK_L = 60,
+	CS_KPROP_H = 61,
+	CS_KPROP_L = 62,
+	CS_KINT_H = 63,
+	CS_KINT_L = 64,
+	QPSK_SCALE = 65,
+	TLD_OUTCLK_TH = 66,
+	TLD_INCLK_TH = 67,
+	FLD_TH = 68,
+	PLD_OUTLK3 = 69,
+	PLD_OUTLK2 = 70,
+	PLD_OUTLK1 = 71,
+	PLD_OUTLK0 = 72,
+	PLD_INLK3 = 73,
+	PLD_INLK2 = 74,
+	PLD_INLK1 = 75,
+	PLD_INLK0 = 76,
+	PLD_ACC_TIME = 77,
+	SWEEP_PAR = 78,
+	STARTUP_TIME = 79,
+	LOSSLOCK_TH = 80,
+	FEC_LOCK_TM = 81,
+	LOSSLOCK_TM = 82,
+	VIT_ERRPER_H = 83,
+	VIT_ERRPER_M = 84,
+	VIT_ERRPER_L = 85,
+	VIT_SETUP = 86,
+	VIT_REF0 = 87,
+	VIT_REF1 = 88,
+	VIT_REF2 = 89,
+	VIT_REF3 = 90,
+	VIT_REF4 = 91,
+	VIT_REF5 = 92,
+	VIT_REF6 = 93,
+	VIT_MAXERR = 94,
+	BA_SETUPT = 95,
+	OP_CTRL = 96,
+	FEC_SETUP = 97,
+	PROG_SYNC = 98,
+	AFC_SEAR_TH = 99,
+	CSACC_DIF_TH = 100,
+	QPSK_LK_CT = 101,
+	QPSK_ST_CT = 102,
+	MON_CTRL = 103,
+	QPSK_RESET = 104,
+	QPSK_TST_CT = 105,
+	QPSK_TST_ST = 106,
+	TEST_R = 107,
+	AGC_H = 108,
+	AGC_M = 109,
+	AGC_L = 110,
+	FREQ_ERR1_H = 111,
+	FREQ_ERR1_M = 112,
+	FREQ_ERR1_L = 113,
+	FREQ_ERR2_H = 114,
+	FREQ_ERR2_L = 115,
+	SYM_RAT_OP_H = 116,
+	SYM_RAT_OP_L = 117,
+	DESEQC2_INT = 118,
+	DISEQC2_STAT = 119,
+	DISEQC2_FIFO = 120,
+	DISEQC2_CTRL1 = 121,
+	DISEQC2_CTRL2 = 122,
+	MONITOR_H = 123,
+	MONITOR_L = 124,
+	TEST_MODE = 125,
+	ID = 126,
+	CONFIG = 127
+};
+
+enum mt312_model_id {
+	ID_VP310 = 1,
+	ID_MT312 = 3
+};
+
+#endif				/* DVB_FRONTENDS_MT312_PRIV */
diff -puN drivers/media/dvb/frontends/mt352.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt352.c
--- 25/drivers/media/dvb/frontends/mt352.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.386063160 -0800
+++ 25-akpm/drivers/media/dvb/frontends/mt352.c	2004-11-21 17:13:23.454052824 -0800
@@ -37,438 +37,90 @@
 #include <linux/delay.h>
 
 #include "dvb_frontend.h"
+#include "mt352_priv.h"
 #include "mt352.h"
 
-#define FRONTEND_NAME "dvbfe_mt352"
+struct mt352_state {
 
-#define dprintk(args...) \
-	do { \
-		if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \
-	} while (0)
+	struct i2c_adapter* i2c;
 
-static int debug;
-#define MAX_CARDS	4
-static int force_card[MAX_CARDS] = { -1, -1, -1, -1 };
-static int force_card_count = 0;
+	struct dvb_frontend_ops ops;
 
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-module_param_array(force_card, int, &force_card_count, 0444);
-MODULE_PARM_DESC(force_card, "Forces the type of each attached mt352 frontend.\n\t"
-		 "If your card is not autodetected, then you must specify its type here.\n\t"
-		 "Valid card types are: 0 == AVDVBT771, 1 == TUA6034, 2 == TDTC9251DH01C,\n\t"
-		 "3 == DVICO FusionHDTV DVB-T1, 4 == DVICO FusionHDTV DVB-T Lite.");
+	/* configuration settings */
+	const struct mt352_config* config;
 
-struct mt352_state {
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-	struct dvb_frontend_info fe_info;
-	int card_type;
+	struct dvb_frontend frontend;
 };
 
-#define mt352_write(ibuf, ilen)						\
+static int debug;
+#define dprintk(args...) \
 do {									\
-	struct i2c_msg msg = { .addr = I2C_MT352_ADDR, .flags = 0,	\
-			       .buf = ibuf, .len = ilen };		\
-	int err = i2c_transfer(i2c, &msg, 1);				\
-	if (err != 1) {							\
-		printk(KERN_WARNING					\
-		       "mt352_write() failed (err = %d)!\n", err);	\
-		return err;						\
-	}								\
+		if (debug) printk(KERN_DEBUG "mt352: " args); \
 } while (0)
 
-static struct _tuner_info tuner_info [] = {
-	{
-	  .fe_name = "AverMedia DVB-T 771",
-	  .fe_frequency_min = 174000000,
-	  .fe_frequency_max = 862000000,
-	  .fe_frequency_stepsize = 166667,
-	  .pll_i2c_addr = 0xc2,
-	  .mt352_init = mt352_init_AVERMEDIA771,
-	  .mt352_charge_pump = mt352_cp_AVERMEDIA771,
-	  .mt352_band_select = mt352_bs_AVERMEDIA771
-	},
-	{
-	  .fe_name = "Zarlink MT352 + TUA6034 DVB-T",
-	  .fe_frequency_min = 174000000,
-	  .fe_frequency_max = 862000000,
-	  .fe_frequency_stepsize = 166667,
-	  .pll_i2c_addr = 0xc2,
-	  .mt352_init = mt352_init_TUA6034,
-	  .mt352_charge_pump = mt352_cp_TUA6034,
-	  .mt352_band_select = mt352_bs_TUA6034
-	},
-	{
-	  .fe_name = "Zarlink MT352 + Samsung TDTC9251DH01C DVB-T",
-	  .fe_frequency_min = 474000000,
-	  .fe_frequency_max = 858000000,
-	  .fe_frequency_stepsize = 166667,
-	  .pll_i2c_addr = 0xc2,
-	  .mt352_init = mt352_init_TDTC9251DH01C,
-	  .mt352_charge_pump = mt352_cp_TDTC9251DH01C,
-	  .mt352_band_select = mt352_bs_TDTC9251DH01C
-	},
-	{
-	  .fe_name = "DVICO FusionHDTV DVB-T1",
-	  .fe_frequency_min = 174000000,
-	  .fe_frequency_max = 862000000,
-	  .fe_frequency_stepsize = 166667,
-	  .pll_i2c_addr = 0xc2,
-	  .mt352_init = mt352_init_DVICODVBT1,
-	  .mt352_charge_pump = mt352_cp_DVICODVBT1,
-	  .mt352_band_select = mt352_bs_DVICODVBT1,
-	},
-	{
-	  .fe_name = "DVICO FusionHDTV DVB-T Lite",
-	  .fe_frequency_min = 174000000,
-	  .fe_frequency_max = 862000000,
-	  .fe_frequency_stepsize = 166667,
-	  .pll_i2c_addr = 0xc0,
-	  .mt352_init = mt352_init_DVICODVBTLITE,
-	  .mt352_charge_pump = mt352_cp_DVICODVBTLITE,
-	  .mt352_band_select = mt352_bs_DVICODVBTLITE,
-	}
-};
-
-static struct dvb_frontend_info mt352_info_template = {
-	.name			= "DVB-T Zarlink MT352 demodulator driver",
-	.type			= FE_OFDM,
-/*
-	.frequency_min		= 0,
-	.frequency_max		= 0,
-	.frequency_stepsize	= 0,
-	.frequency_tolerance	= 0,
-	.symbol_rate_min	= 1000000,
-	.symbol_rate_max	= 45000000,
-	.symbol_rate_tolerance	= ???,
-*/
-	.notifier_delay		 = 0,
-	.caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
-		FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
-		FE_CAN_FEC_AUTO |
-		FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
-		FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
-		FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER |
-		FE_CAN_MUTE_TS
-};
-
-static u8 mt352_reset [] = { RESET, 0x80 };
-static u8 mt352_adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
-static u8 mt352_capt_range_cfg[] = { CAPT_RANGE, 0x32 };
-
-static int mt352_init_TUA6034(struct i2c_adapter *i2c)
-{
-	static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
-	static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x19, 0xa0 };
-
-	mt352_write(mt352_clock_config, sizeof(mt352_clock_config));
-	udelay(2000);
-	mt352_write(mt352_reset, sizeof(mt352_reset));
-	mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
-
-	mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg));
-	mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
-
-	return 0;
-}
-
-static int mt352_init_AVERMEDIA771(struct i2c_adapter *i2c)
-{
-	static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
-	static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x10, 0x23, 0x00, 0xFF, 0xFF,
-				       0x00, 0xFF, 0x00, 0x40, 0x40 };
-	static u8 mt352_av771_extra[] = { 0xB5, 0x7A };
-	static u8 mt352_capt_range_cfg[] = { CAPT_RANGE, 0x32 };
-
-	mt352_write(mt352_clock_config, sizeof(mt352_clock_config));
-	udelay(2000);
-	mt352_write(mt352_reset, sizeof(mt352_reset));
-	mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
-
-	mt352_write(mt352_agc_cfg,sizeof(mt352_agc_cfg));
-	udelay(2000);
-	mt352_write(mt352_av771_extra,sizeof(mt352_av771_extra));
-	mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
-
-	return 0;
-}
-
-static int mt352_init_TDTC9251DH01C(struct i2c_adapter *i2c)
+int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
 {
-	static u8 mt352_clock_config [] = { CLOCK_CTL, 0x10, 0x2d };
-	static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x28, 0xa1 };
-
-	mt352_write(mt352_clock_config, sizeof(mt352_clock_config));
-	udelay(2000);
-	mt352_write(mt352_reset, sizeof(mt352_reset));
-	mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
-
-	mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg));
-	mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
-
-	return 0;
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0,
+			       .buf = ibuf, .len = ilen };
+	int err = i2c_transfer(state->i2c, &msg, 1);
+	if (err != 1) {
+		printk(KERN_WARNING
+		       "mt352_write() failed (err = %d)!\n", err);
+		return err;
 }
 
-static int mt352_init_DVICODVBT1(struct i2c_adapter *i2c)
-{
-	static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x39 };
-	static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x24, 0x20 };
-	static u8 mt352_gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
-
-	mt352_write(mt352_clock_config, sizeof(mt352_clock_config));
-	udelay(200);
-	mt352_write(mt352_reset, sizeof(mt352_reset));
-	mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
-
-	mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg));
-	mt352_write(mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
-	mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
-
 	return 0;
 }
 
-static int mt352_init_DVICODVBTLITE(struct i2c_adapter *i2c)
-{
-	static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x38 };
-	static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x28, 0x20 };
-	static u8 mt352_gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
-
-	mt352_write(mt352_clock_config, sizeof(mt352_clock_config));
-	udelay(200);
-	mt352_write(mt352_reset, sizeof(mt352_reset));
-	mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
-
-	mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg));
-	mt352_write(mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg));
-	mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
-
-	return 0;
-}
-
-static unsigned char mt352_cp_TUA6034(u32 freq)
-{
-	unsigned char cp = 0;
-
-	if (freq < 542000000)
-		cp = 0xbe;
-	else if (freq < 830000000)
-		cp = 0xf6;
-	else
-		cp = 0xfe;
-
-	return cp;
-}
-
-static unsigned char mt352_cp_AVERMEDIA771(u32 freq)
-{
-	unsigned char cp = 0;
-
-	if (freq < 150000000)
-		cp = 0xB4;
-	else if (freq < 173000000)
-		cp = 0xBC;
-	else if (freq < 250000000)
-		cp = 0xB4;
-	else if (freq < 400000000)
-		cp = 0xBC;
-	else if (freq < 420000000)
-		cp = 0xF4;
-	else if (freq < 470000000)
-		cp = 0xFC;
-	else if (freq < 600000000)
-		cp = 0xBC;
-	else if (freq < 730000000)
-		cp = 0xF4;
-	else
-		cp = 0xFC;
-
-	return cp;
-}
-
-static unsigned char mt352_cp_TDTC9251DH01C(u32 freq)
-{
-	return(0xcc);
-}
-
-static unsigned char mt352_cp_DVICODVBT1(u32 freq)
+static u8 mt352_read_register(struct mt352_state* state, u8 reg)
 {
-	unsigned char cp = 0;
-
-	if (freq < 542000000)
-		cp = 0xbc;
-	else if (freq < 830000000)
-		cp = 0xf4;
-	else
-		cp = 0xfc;
-
-	return cp;
-}
-
-static unsigned char mt352_cp_DVICODVBTLITE(u32 freq)
-{
-	unsigned char cp = 0;
-
-	if (freq < 542000000)
-		cp = 0xb4;
-	else if (freq < 771000000)
-		cp = 0xbc;
-	else 
-		cp = 0xf4;
-
-	return cp;
-}
+	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 } };
 
-static unsigned char mt352_bs_TUA6034(u32 freq)
-{
-	unsigned char bs = 0;
+	ret = i2c_transfer(state->i2c, msg, 2);
 
-	if (freq < 250000000)
-		bs = 0x01;
-	else
-		bs = 0x08;
+	if (ret != 2)
+		printk(KERN_WARNING
+		       "%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
 
-	return bs;
+	return b1[0];
 }
 
-static unsigned char mt352_bs_AVERMEDIA771(u32 freq)
-{
-	unsigned char bs = 0;
 
-	if (freq < 150000000)
-		bs = 0x01;
-	else if (freq < 173000000)
-		bs = 0x01;
-	else if (freq < 250000000)
-		bs = 0x02;
-	else if (freq < 400000000)
-		bs = 0x02;
-	else if (freq < 420000000)
-		bs = 0x02;
-	else if (freq < 470000000)
-		bs = 0x02;
-	else if (freq < 600000000)
-		bs = 0x08;
-	else if (freq < 730000000)
-		bs = 0x08;
-	else
-		bs = 0x08;
 
-	return bs;
-}
 
-static unsigned char mt352_bs_TDTC9251DH01C(u32 freq)
-{
-	unsigned char bs = 0;
 
-	if (freq >= 48000000 && freq <= 154000000)      /* low band */
-		bs = 0x09;
 
-	if (freq >= 161000000 && freq <= 439000000)     /* medium band */
-		bs = 0x0a;
 
-	if (freq >= 447000000 && freq <= 863000000)     /* high band */
-		bs = 0x08;
 
-	return bs;
-}
 
-static unsigned char mt352_bs_DVICODVBT1(u32 freq)
-{
-	unsigned char bs = 0;
 
-	if (freq == 0)			/* power down PLL */
-		bs = 0x03;
-	else if (freq < 157500000)	/* low band */
-		bs = 0x01;
-	else if (freq < 443250000)	/* mid band */
-		bs = 0x02;
-	else				/* high band */
-		bs = 0x04;
 
-	return bs;
-}
 
-static unsigned char mt352_bs_DVICODVBTLITE(u32 freq)
-{
-	unsigned char bs = 0;
-
-	if (freq == 0)			/* power down PLL */
-		bs = 0x03;
-	else if (freq < 443250000)	/* mid band */
-		bs = 0x02;
-	else				/* high band */
-		bs = 0x08;
-
-	return bs;
-}
-
-static u32 mt352_read_eeprom_dword(struct i2c_adapter *i2c, int dword_base)
-	{
-	int i;
-	u32 dword = 0;
-	u8 reg, val;
-	struct i2c_msg msg[2] = {
-		{
-			.addr = 0x50,
-			.flags = 0,
-			.buf = &reg,
-			.len = 1
-		},
-		{
-			.addr = 0x50,
-			.flags = I2C_M_RD,
-			.buf = &val,
-			.len = 1
-		}
-	};
-
-	for (i = 0; i < 4; i++) {
-		reg = dword_base + i;
-		if (i2c_transfer(i2c,msg,2) != 2)
-			return 0;
-		dword = (dword << 8) | val;
-	}
-
-	return dword;
-}
-
-static int mt352_init(struct i2c_adapter *i2c, int card_type)
-{
-	/**
-	 *  all register write sequence have the register address of the
-	 *  first register in the first byte, thenafter the value to write
-	 *  into this and the following registers.
-	 *
-	 *
-	 *  We only write non-default settings, all default settings are
-	 *  restored by the full mt352_reset sequence.
-	 *
-	 *
-	 *  The optimal AGC target value and slope might vary from tuner
-	 *  type to tuner type, so check whether you need to adjust this one...
-	 **/
-
-	return(MT352_INIT(i2c));
-}
-
-static int mt352_sleep(struct i2c_adapter *i2c)
+static int mt352_sleep(struct dvb_frontend* fe)
 {
 	static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 };
 
-	mt352_write(mt352_softdown, sizeof(mt352_softdown));
+	mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
 
 	return 0;
 }
 
-static int mt352_set_parameters(struct i2c_adapter *i2c,
-				struct dvb_frontend_parameters *param,
-				int card_type)
+static int mt352_set_parameters(struct dvb_frontend* fe,
+				struct dvb_frontend_parameters *param)
 {
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
 	unsigned char buf[14];
 	unsigned int tps = 0;
 	struct dvb_ofdm_parameters *op = &param->u.ofdm;
-	uint16_t tmp;
 	int i;
 
 	switch (op->code_rate_HP) {
@@ -600,22 +252,7 @@ static int mt352_set_parameters(struct i
 	buf[6] = 0x31;  /* INPUT_FREQ_(1|0), 20.48MHz clock, 36.166667MHz IF */
 	buf[7] = 0x05;  /* see MT352 Design Manual page 32 for details */
 
-	buf[8] = PLL_I2C_ADDR;
-
-	/**
-	 *  All the following settings are tuner module dependent,
-	 *  check the datasheet...
-	 */
-
-	/* here we assume 1/6MHz == 166.66kHz stepsize */
-	#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
-	tmp = (((param->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
-
-	buf[9] = msb(tmp);      /* CHAN_START_(1|0) */
-	buf[10] = lsb(tmp);
-
-	buf[11] = MT352_CHARGE_PUMP(param->frequency);
-	buf[12] = MT352_BAND_SELECT(param->frequency);
+	state->config->pll_set(fe, param, buf+8);
 
 	buf[13] = 0x01; /* TUNER_GO!! */
 
@@ -623,39 +260,18 @@ static int mt352_set_parameters(struct i
 	 * parameters already set.  Enhances tuning time and prevents stream
 	 * breakup when retuning the same transponder. */
 	for (i = 1; i < 13; i++)
-		if (buf[i] != mt352_read_register(i2c, i + 0x50)) {
-	mt352_write(buf, sizeof(buf));
+		if (buf[i] != mt352_read_register(state, i + 0x50)) {
+			mt352_write(fe, buf, sizeof(buf));
 			break;
 		}
 
 	return 0;
 }
 
-static u8 mt352_read_register(struct i2c_adapter *i2c, u8 reg)
-{
-	int ret;
-	u8 b0 [] = { reg };
-	u8 b1 [] = { 0 };
-	struct i2c_msg msg [] = { { .addr = I2C_MT352_ADDR,
-				    .flags = 0,
-				    .buf = b0, .len = 1 },
-				  { .addr = I2C_MT352_ADDR,
-				    .flags = I2C_M_RD,
-				    .buf = b1, .len = 1 } };
-
-	ret = i2c_transfer(i2c, msg, 2);
-
-	if (ret != 2)
-		printk(KERN_WARNING
-		       "%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
-
-	return b1[0];
-}
-
-
-static int mt352_get_parameters(struct i2c_adapter *i2c,
+static int mt352_get_parameters(struct dvb_frontend* fe,
 				struct dvb_frontend_parameters *param)
 {
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
 	u16 tps;
 	u16 div;
 	u8 trl;
@@ -672,7 +288,7 @@ static int mt352_get_parameters(struct i
 		FEC_AUTO
 	};
 
-	if ( (mt352_read_register(i2c,0x00) & 0xC0) != 0xC0 )
+	if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 )
 	{
 		return -EINVAL;
 	}
@@ -680,9 +296,9 @@ static int mt352_get_parameters(struct i
 	/* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because
 	 * the mt352 sometimes works with the wrong parameters
 	 */
-	tps = (mt352_read_register(i2c,	TPS_RECEIVED_1) << 8) | mt352_read_register(i2c, TPS_RECEIVED_0);
-	div = (mt352_read_register(i2c, CHAN_START_1) << 8) | mt352_read_register(i2c, CHAN_START_0);
-	trl = mt352_read_register(i2c, TRL_NOMINAL_RATE_1);
+	tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0);
+	div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0);
+	trl = mt352_read_register(state, TRL_NOMINAL_RATE_1);
 
 	op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7];
 	op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7];
@@ -759,7 +375,7 @@ static int mt352_get_parameters(struct i
 	}
 
 
-	if (mt352_read_register(i2c, STATUS_2) & 0x02)
+	if (mt352_read_register(state, STATUS_2) & 0x02)
 		param->inversion = INVERSION_OFF;
 	else
 		param->inversion = INVERSION_ON;
@@ -767,26 +383,13 @@ static int mt352_get_parameters(struct i
 	return 0;
 }
 
-
-static int mt352_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
+static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status)
 {
-	struct mt352_state *state = fe->data;
-	struct i2c_adapter *i2c = state->i2c;
-	int card_type = state->card_type;
-	u8 r,snr;
-	fe_status_t *status;
-	u16 signal;
-	struct dvb_frontend_tune_settings *fe_tune_settings;
-
-	switch (cmd) {
-	case FE_GET_INFO:
-		memcpy(arg, &state->fe_info, sizeof(struct dvb_frontend_info));
-		break;
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+	u8 r;
 
-	case FE_READ_STATUS:
-		status = arg;
 		*status = 0;
-		r = mt352_read_register (i2c, STATUS_0);
+	r = mt352_read_register (state, STATUS_0);
 		if (r & (1 << 4))
 			*status = FE_HAS_CARRIER;
 		if (r & (1 << 1))
@@ -794,243 +397,164 @@ static int mt352_ioctl(struct dvb_fronte
 		if (r & (1 << 5))
 			*status |= FE_HAS_LOCK;
 
-		r = mt352_read_register (i2c, STATUS_1);
+	r = mt352_read_register (state, STATUS_1);
 		if (r & (1 << 1))
 			*status |= FE_HAS_SYNC;
 
-		r = mt352_read_register (i2c, STATUS_3);
+	r = mt352_read_register (state, STATUS_3);
 		if (r & (1 << 6))
 			*status |= FE_HAS_SIGNAL;
 
-		break;
+	if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
+		      (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
+		*status &= ~FE_HAS_LOCK;
 
-	case FE_READ_BER:
-		*((u32 *) arg) = (mt352_read_register (i2c, RS_ERR_CNT_2) << 16) |
-		       (mt352_read_register (i2c, RS_ERR_CNT_1) << 8) |
-		       (mt352_read_register (i2c, RS_ERR_CNT_0));
-		break;
-
-	case FE_READ_SIGNAL_STRENGTH:
-		signal = (mt352_read_register (i2c, AGC_GAIN_3) << 8) |
-			     (mt352_read_register (i2c, AGC_GAIN_2));
-		*((u16*) arg) = ~signal;
-		break;
-
-	case FE_READ_SNR:
-		snr = mt352_read_register (i2c, SNR);
-		*((u16*) arg) = (snr << 8) | snr;
-		break;
-
-	case FE_READ_UNCORRECTED_BLOCKS:
-		*(u32*) arg = (mt352_read_register (i2c,  RS_UBC_1) << 8) |
-			      (mt352_read_register (i2c,  RS_UBC_0));
-		break;
-
-	case FE_SET_FRONTEND:
-		return mt352_set_parameters (i2c,
-				 (struct dvb_frontend_parameters *) arg,
-				 card_type);
-
-	case FE_GET_FRONTEND:
-		return mt352_get_parameters (i2c,
-				 (struct dvb_frontend_parameters *) arg);
-
-	case FE_GET_TUNE_SETTINGS:
-		fe_tune_settings = (struct dvb_frontend_tune_settings *) arg;
-		fe_tune_settings->min_delay_ms = 800;
-		fe_tune_settings->step_size = 0;
-		fe_tune_settings->max_drift = 0;
-		break;
-
-	case FE_SLEEP:
-		return mt352_sleep(i2c);
-
-	case FE_INIT:
-		/* Only send the initialisation command if the demodulator
-		 * isn't already enabled.  Greatly enhances tuning time. */
-		if ((mt352_read_register(i2c, CLOCK_CTL) & 0x10) == 0 ||
-		    (mt352_read_register(i2c, CONFIG) & 0x20) == 0)
-			return mt352_init(i2c, card_type);
-		else
-			return 0;
+	return 0;
+}
+
+static int mt352_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
 
-	default:
-		return -EOPNOTSUPP;
+	*ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) |
+	       (mt352_read_register (state, RS_ERR_CNT_1) << 8) |
+	       (mt352_read_register (state, RS_ERR_CNT_0));
+
+			return 0;
 	}
 
+static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
+
+	u16 signal = (mt352_read_register (state, AGC_GAIN_3) << 8) |
+		     (mt352_read_register (state, AGC_GAIN_2));
+
+	*strength = ~signal;
 	return 0;
 }
 
-static struct i2c_client client_template;
-
-static int mt352_attach_adapter(struct i2c_adapter *i2c)
+static int mt352_read_snr(struct dvb_frontend* fe, u16* snr)
 {
-	static int num_cards_probed;
-	struct mt352_state *state;
-	struct i2c_client *client;
-	static u8 mt352_reset_attach [] = { RESET, 0xC0 };
-	int ret;
-	int card_type, forced_card = -1;
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
 
-	dprintk("Trying to attach to adapter 0x%x:%s.\n",
-		i2c->id, i2c->name);
+	u8 _snr = mt352_read_register (state, SNR);
+	*snr = (_snr << 8) | _snr;
 
-	if (mt352_read_register(i2c, CHIP_ID) != ID_MT352)
-		return -ENODEV;
+	return 0;
+}
 
-	if ( !(state = kmalloc(sizeof(struct mt352_state), GFP_KERNEL)) )
-		return -ENOMEM;
+static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
 
-	memset(state, 0, sizeof(struct mt352_state));
-	state->i2c = i2c;
-	state->card_type = -1;
-	memcpy(&state->fe_info, &mt352_info_template, sizeof(struct dvb_frontend_info));
+	*ucblocks = (mt352_read_register (state,  RS_UBC_1) << 8) |
+		    (mt352_read_register (state,  RS_UBC_0));
 
-	/* Attempt autodetection of card type based on PCI ID information
-	 * stored in any on-board EEPROM. */
-	switch (mt352_read_eeprom_dword(i2c, 0xFC)) {	/* BT878A chipset */
-	case 0x07711461:
-		state->card_type = CARD_AVDVBT771;
-		break;
-	case 0xdb1018ac:
-		state->card_type = CARD_DVICODVBTLITE;
-		break;
-	default:
-		break;
-	}
-
-	switch (mt352_read_eeprom_dword(i2c, 0x04)) {	/* CX2388x chipset */
-	case 0xac1800db:
-		state->card_type = CARD_DVICODVBT1;
-		break;
-	default:
-		break;
-	}
-
-	if (num_cards_probed < force_card_count)
-		forced_card = force_card[num_cards_probed++];
-
-	if (state->card_type == -1 && forced_card < 0) {
-		dprintk("Card type not automatically detected.  You "
-			"must use the 'force_card' module parameter.\n");
-		kfree(state);
-		return -ENODEV;
+	return 0;
 	}
 
-	if (forced_card >= 0) {
-		if (state->card_type >= 0 && forced_card != state->card_type)
-			printk(KERN_WARNING FRONTEND_NAME ": Warning, overriding"
-					    " detected card type.\n");
-		state->card_type = forced_card;
+static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+	fe_tune_settings->min_delay_ms = 800;
+	fe_tune_settings->step_size = 0;
+	fe_tune_settings->max_drift = 0;
+
+	return 0;
 	}
 
-	card_type = state->card_type;
-	printk(KERN_INFO FRONTEND_NAME ": Setup for %s\n", FE_NAME);
+static int mt352_init(struct dvb_frontend* fe)
+{
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
 
-	/* set the frontend name and card-specific frequency info */
-	strlcpy(state->fe_info.name, FE_NAME, sizeof(state->fe_info.name));
-	state->fe_info.frequency_min = FE_FREQ_MIN;
-	state->fe_info.frequency_max = FE_FREQ_MAX;
-	state->fe_info.frequency_stepsize = FE_FREQ_STEPSIZE;
+	static u8 mt352_reset_attach [] = { RESET, 0xC0 };
 
-	/* Do a "hard" reset */
-	mt352_write(mt352_reset_attach, sizeof(mt352_reset_attach));
+	if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 ||
+	    (mt352_read_register(state, CONFIG) & 0x20) == 0) {
 
-	/* Try to intiialise the device */
-	if (mt352_init(i2c, card_type) != 0) {
-		kfree(state);
-		return -ENODEV;
+	/* Do a "hard" reset */
+		mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
+		return state->config->demod_init(fe);
 	}
 
-	if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) {
-		kfree(state);
-		return -ENOMEM;
+	return 0;
 	}
 
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = i2c;
-	client->addr = 0; // XXX
-	i2c_set_clientdata(client, state);
-
-	if ((ret = i2c_attach_client(client))) {
-		kfree(client);
+static void mt352_release(struct dvb_frontend* fe)
+{
+	struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv;
 		kfree(state);
-		return ret;
 	}
 
-	return 0;
-}
+static struct dvb_frontend_ops mt352_ops;
 
-static int mt352_detach_client(struct i2c_client *client)
+struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+				  struct i2c_adapter* i2c)
 {
-	struct mt352_state *state = i2c_get_clientdata(client);
+	struct mt352_state* state = NULL;
 
-	if (state->dvb)
-	dvb_unregister_frontend (mt352_ioctl, state->dvb);
-	i2c_detach_client(client);
-	kfree(client);
-	kfree(state);
-	return 0;
-}
+	/* allocate memory for the internal state */
+	state = (struct mt352_state*) kmalloc(sizeof(struct mt352_state), GFP_KERNEL);
+	if (state == NULL) goto error;
 
-static int mt352_command (struct i2c_client *client, unsigned int cmd, void *arg)
-{
-	struct mt352_state *state = i2c_get_clientdata(client);
-	int ret;
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &mt352_ops, sizeof(struct dvb_frontend_ops));
 
-	switch (cmd) {
-	case FE_REGISTER:
-		if (!state->dvb) {
-			if ((ret = dvb_register_frontend(mt352_ioctl, arg,
-							 state, &state->fe_info,
-							 THIS_MODULE)))
-				return ret;
-		state->dvb = arg;
-		}
-		break;
-	case FE_UNREGISTER:
-		if (state->dvb == arg) {
-			dvb_unregister_frontend(mt352_ioctl, state->dvb);
-		state->dvb = NULL;
-		}
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-	return 0;
-}
+	/* check if the demod is there */
+	if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error;
 
-static struct i2c_driver driver = {
-	.owner 		= THIS_MODULE,
-	.name 		= FRONTEND_NAME,
-	.id 		= I2C_DRIVERID_DVBFE_MT352,
-	.flags 		= I2C_DF_NOTIFY,
-	.attach_adapter = mt352_attach_adapter,
-	.detach_client 	= mt352_detach_client,
-	.command 	= mt352_command,
-};
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops mt352_ops = {
+
+	.info = {
+		.name			= "Zarlink MT352 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 174000000,
+		.frequency_max		= 862000000,
+		.frequency_stepsize	= 166667,
+		.frequency_tolerance	= 0,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+			FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+			FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER |
+			FE_CAN_MUTE_TS
+	},
 
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags 		= I2C_CLIENT_ALLOW_USE,
-	.driver  	= &driver,
-};
+	.release = mt352_release,
 
-static int __init mt352_module_init(void)
-{
-	return i2c_add_driver(&driver);
-}
+	.init = mt352_init,
+	.sleep = mt352_sleep,
 
-static void __exit mt352_module_exit(void)
-{
-	if (i2c_del_driver(&driver))
-		printk(KERN_ERR "mt352: driver deregistration failed.\n");
-}
+	.set_frontend = mt352_set_parameters,
+	.get_frontend = mt352_get_parameters,
+	.get_tune_settings = mt352_get_tune_settings,
+
+	.read_status = mt352_read_status,
+	.read_ber = mt352_read_ber,
+	.read_signal_strength = mt352_read_signal_strength,
+	.read_snr = mt352_read_snr,
+	.read_ucblocks = mt352_read_ucblocks,
+};
 
-module_init(mt352_module_init);
-module_exit(mt352_module_exit);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-MODULE_DESCRIPTION("DVB-T MT352 Zarlink");
+MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver");
 MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(mt352_attach);
+EXPORT_SYMBOL(mt352_write);
diff -puN drivers/media/dvb/frontends/mt352.h~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt352.h
--- 25/drivers/media/dvb/frontends/mt352.h~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.387063008 -0800
+++ 25-akpm/drivers/media/dvb/frontends/mt352.h	2004-11-21 17:13:23.455052672 -0800
@@ -30,145 +30,29 @@
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
  */
 
-#ifndef _MT352_
-#define _MT352_
+#ifndef MT352_H
+#define MT352_H
 
-#define I2C_MT352_ADDR  0x0f
-#define ID_MT352        0x13
+#include <linux/dvb/frontend.h>
 
-#define CARD_AVDVBT771	    0x00
-#define CARD_TUA6034	    0x01
-#define CARD_TDTC9251DH01C  0x02
-#define CARD_DVICODVBT1	    0x03
-#define CARD_DVICODVBTLITE  0x04
-
-#define msb(x) (((x) >> 8) & 0xff)
-#define lsb(x) ((x) & 0xff)
-
-enum mt352_reg_addr {
-	STATUS_0           = 0x00,
-	STATUS_1           = 0x01,
-	STATUS_2           = 0x02,
-	STATUS_3           = 0x03,
-	STATUS_4           = 0x04,
-	INTERRUPT_0        = 0x05,
-	INTERRUPT_1        = 0x06,
-	INTERRUPT_2        = 0x07,
-	INTERRUPT_3        = 0x08,
-	SNR                = 0x09,
-	VIT_ERR_CNT_2      = 0x0A,
-	VIT_ERR_CNT_1      = 0x0B,
-	VIT_ERR_CNT_0      = 0x0C,
-	RS_ERR_CNT_2       = 0x0D,
-	RS_ERR_CNT_1       = 0x0E,
-	RS_ERR_CNT_0       = 0x0F,
-	RS_UBC_1           = 0x10,
-	RS_UBC_0           = 0x11,
-	AGC_GAIN_3         = 0x12,
-	AGC_GAIN_2         = 0x13,
-	AGC_GAIN_1         = 0x14,
-	AGC_GAIN_0         = 0x15,
-	FREQ_OFFSET_2      = 0x17,
-	FREQ_OFFSET_1      = 0x18,
-	FREQ_OFFSET_0      = 0x19,
-	TIMING_OFFSET_1    = 0x1A,
-	TIMING_OFFSET_0    = 0x1B,
-	CHAN_FREQ_1        = 0x1C,
-	CHAN_FREQ_0        = 0x1D,
-	TPS_RECEIVED_1     = 0x1E,
-	TPS_RECEIVED_0     = 0x1F,
-	TPS_CURRENT_1      = 0x20,
-	TPS_CURRENT_0      = 0x21,
-	TPS_CELL_ID_1      = 0x22,
-	TPS_CELL_ID_0      = 0x23,
-	TPS_MISC_DATA_2    = 0x24,
-	TPS_MISC_DATA_1    = 0x25,
-	TPS_MISC_DATA_0    = 0x26,
-	RESET              = 0x50,
-	TPS_GIVEN_1        = 0x51,
-	TPS_GIVEN_0        = 0x52,
-	ACQ_CTL            = 0x53,
-	TRL_NOMINAL_RATE_1 = 0x54,
-	TRL_NOMINAL_RATE_0 = 0x55,
-	INPUT_FREQ_1       = 0x56,
-	INPUT_FREQ_0       = 0x57,
-	TUNER_ADDR         = 0x58,
-	CHAN_START_1       = 0x59,
-	CHAN_START_0       = 0x5A,
-	CONT_1             = 0x5B,
-	CONT_0             = 0x5C,
-	TUNER_GO           = 0x5D,
-	STATUS_EN_0        = 0x5F,
-	STATUS_EN_1        = 0x60,
-	INTERRUPT_EN_0     = 0x61,
-	INTERRUPT_EN_1     = 0x62,
-	INTERRUPT_EN_2     = 0x63,
-	INTERRUPT_EN_3     = 0x64,
-	AGC_TARGET         = 0x67,
-	AGC_CTL            = 0x68,
-	CAPT_RANGE         = 0x75,
-	SNR_SELECT_1       = 0x79,
-	SNR_SELECT_0       = 0x7A,
-	RS_ERR_PER_1       = 0x7C,
-	RS_ERR_PER_0       = 0x7D,
-	CHIP_ID            = 0x7F,
-	CHAN_STOP_1        = 0x80,
-	CHAN_STOP_0        = 0x81,
-	CHAN_STEP_1        = 0x82,
-	CHAN_STEP_0        = 0x83,
-	FEC_LOCK_TIME      = 0x85,
-	OFDM_LOCK_TIME     = 0x86,
-	ACQ_DELAY          = 0x87,
-	SCAN_CTL           = 0x88,
-	CLOCK_CTL          = 0x89,
-	CONFIG             = 0x8A,
-	MCLK_RATIO         = 0x8B,
-	GPP_CTL            = 0x8C,
-	ADC_CTL_1          = 0x8E,
-	ADC_CTL_0          = 0x8F
+struct mt352_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* Initialise the demodulator and PLL. Cannot be NULL */
+	int (*demod_init)(struct dvb_frontend* fe);
+
+	/* PLL setup - fill out the supplied 5 byte buffer with your PLL settings.
+	 * byte0: Set to pll i2c address (nonlinux; left shifted by 1)
+	 * byte1-4: PLL configuration.
+	 */
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf);
 };
 
-struct _tuner_info {
-	char *fe_name;
-#define FE_NAME tuner_info[card_type].fe_name
+extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+					 struct i2c_adapter* i2c);
 
-	__u32 fe_frequency_min;
-#define FE_FREQ_MIN tuner_info[card_type].fe_frequency_min
+extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen);
 
-	__u32 fe_frequency_max;
-#define FE_FREQ_MAX tuner_info[card_type].fe_frequency_max
-
-	__u32 fe_frequency_stepsize; //verificare se u32 e' corretto
-#define FE_FREQ_STEPSIZE  tuner_info[card_type].fe_frequency_stepsize
-
-	u8 pll_i2c_addr;
-#define PLL_I2C_ADDR tuner_info[card_type].pll_i2c_addr
-
-	int (* mt352_init) (struct i2c_adapter *i2c);
-#define MT352_INIT tuner_info[card_type].mt352_init
-
-	unsigned char (* mt352_charge_pump) (u32 freq);
-#define MT352_CHARGE_PUMP tuner_info[card_type].mt352_charge_pump
-
-	unsigned char (* mt352_band_select) (u32 freq);
-#define MT352_BAND_SELECT tuner_info[card_type].mt352_band_select
-};
-
-static int mt352_init_TUA6034(struct i2c_adapter *i2c);
-static int mt352_init_AVERMEDIA771(struct i2c_adapter *i2c);
-static int mt352_init_TDTC9251DH01C(struct i2c_adapter *i2c);
-static int mt352_init_DVICODVBT1(struct i2c_adapter *i2c);
-static int mt352_init_DVICODVBTLITE(struct i2c_adapter *i2c);
-static unsigned char mt352_cp_TUA6034(u32 freq);
-static unsigned char mt352_cp_AVERMEDIA771(u32 freq);
-static unsigned char mt352_cp_TDTC9251DH01C(u32 freq);
-static unsigned char mt352_cp_DVICODVBT1(u32 freq);
-static unsigned char mt352_cp_DVICODVBTLITE(u32 freq);
-static unsigned char mt352_bs_TUA6034(u32 freq);
-static unsigned char mt352_bs_AVERMEDIA771(u32 freq);
-static unsigned char mt352_bs_TDTC9251DH01C(u32 freq);
-static unsigned char mt352_bs_DVICODVBT1(u32 freq);
-static unsigned char mt352_bs_DVICODVBTLITE(u32 freq);
-static u8 mt352_read_register(struct i2c_adapter *i2c, u8 reg);
-
-#endif                          /* _MT352_ */
+#endif // MT352_H
diff -puN /dev/null drivers/media/dvb/frontends/mt352_priv.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/mt352_priv.h	2004-11-21 17:13:23.456052520 -0800
@@ -0,0 +1,127 @@
+/*
+ *  Driver for Zarlink DVB-T MT352 demodulator
+ *
+ *  Written by Holger Waechtler <holger@qanu.de>
+ *	 and Daniel Mack <daniel@qanu.de>
+ *
+ *  AVerMedia AVerTV DVB-T 771 support by
+ *       Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ *  Support for Samsung TDTC9251DH01C(M) tuner
+ *  Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ *                     Amauri  Celani  <acelani@essegi.net>
+ *
+ *  DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ *       Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef _MT352_PRIV_
+#define _MT352_PRIV_
+
+#define ID_MT352        0x13
+
+#define msb(x) (((x) >> 8) & 0xff)
+#define lsb(x) ((x) & 0xff)
+
+enum mt352_reg_addr {
+	STATUS_0           = 0x00,
+	STATUS_1           = 0x01,
+	STATUS_2           = 0x02,
+	STATUS_3           = 0x03,
+	STATUS_4           = 0x04,
+	INTERRUPT_0        = 0x05,
+	INTERRUPT_1        = 0x06,
+	INTERRUPT_2        = 0x07,
+	INTERRUPT_3        = 0x08,
+	SNR                = 0x09,
+	VIT_ERR_CNT_2      = 0x0A,
+	VIT_ERR_CNT_1      = 0x0B,
+	VIT_ERR_CNT_0      = 0x0C,
+	RS_ERR_CNT_2       = 0x0D,
+	RS_ERR_CNT_1       = 0x0E,
+	RS_ERR_CNT_0       = 0x0F,
+	RS_UBC_1           = 0x10,
+	RS_UBC_0           = 0x11,
+	AGC_GAIN_3         = 0x12,
+	AGC_GAIN_2         = 0x13,
+	AGC_GAIN_1         = 0x14,
+	AGC_GAIN_0         = 0x15,
+	FREQ_OFFSET_2      = 0x17,
+	FREQ_OFFSET_1      = 0x18,
+	FREQ_OFFSET_0      = 0x19,
+	TIMING_OFFSET_1    = 0x1A,
+	TIMING_OFFSET_0    = 0x1B,
+	CHAN_FREQ_1        = 0x1C,
+	CHAN_FREQ_0        = 0x1D,
+	TPS_RECEIVED_1     = 0x1E,
+	TPS_RECEIVED_0     = 0x1F,
+	TPS_CURRENT_1      = 0x20,
+	TPS_CURRENT_0      = 0x21,
+	TPS_CELL_ID_1      = 0x22,
+	TPS_CELL_ID_0      = 0x23,
+	TPS_MISC_DATA_2    = 0x24,
+	TPS_MISC_DATA_1    = 0x25,
+	TPS_MISC_DATA_0    = 0x26,
+	RESET              = 0x50,
+	TPS_GIVEN_1        = 0x51,
+	TPS_GIVEN_0        = 0x52,
+	ACQ_CTL            = 0x53,
+	TRL_NOMINAL_RATE_1 = 0x54,
+	TRL_NOMINAL_RATE_0 = 0x55,
+	INPUT_FREQ_1       = 0x56,
+	INPUT_FREQ_0       = 0x57,
+	TUNER_ADDR         = 0x58,
+	CHAN_START_1       = 0x59,
+	CHAN_START_0       = 0x5A,
+	CONT_1             = 0x5B,
+	CONT_0             = 0x5C,
+	TUNER_GO           = 0x5D,
+	STATUS_EN_0        = 0x5F,
+	STATUS_EN_1        = 0x60,
+	INTERRUPT_EN_0     = 0x61,
+	INTERRUPT_EN_1     = 0x62,
+	INTERRUPT_EN_2     = 0x63,
+	INTERRUPT_EN_3     = 0x64,
+	AGC_TARGET         = 0x67,
+	AGC_CTL            = 0x68,
+	CAPT_RANGE         = 0x75,
+	SNR_SELECT_1       = 0x79,
+	SNR_SELECT_0       = 0x7A,
+	RS_ERR_PER_1       = 0x7C,
+	RS_ERR_PER_0       = 0x7D,
+	CHIP_ID            = 0x7F,
+	CHAN_STOP_1        = 0x80,
+	CHAN_STOP_0        = 0x81,
+	CHAN_STEP_1        = 0x82,
+	CHAN_STEP_0        = 0x83,
+	FEC_LOCK_TIME      = 0x85,
+	OFDM_LOCK_TIME     = 0x86,
+	ACQ_DELAY          = 0x87,
+	SCAN_CTL           = 0x88,
+	CLOCK_CTL          = 0x89,
+	CONFIG             = 0x8A,
+	MCLK_RATIO         = 0x8B,
+	GPP_CTL            = 0x8C,
+	ADC_CTL_1          = 0x8E,
+	ADC_CTL_0          = 0x8F
+};
+
+/* here we assume 1/6MHz == 166.66kHz stepsize */
+#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
+
+#endif                          /* _MT352_PRIV_ */
diff -puN drivers/media/dvb/frontends/nxt6000.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/nxt6000.c
--- 25/drivers/media/dvb/frontends/nxt6000.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.389062704 -0800
+++ 25-akpm/drivers/media/dvb/frontends/nxt6000.c	2004-11-21 17:13:23.460051912 -0800
@@ -1,13 +1,6 @@
 /* 
-
 	NxtWave Communications - NXT6000 demodulator driver
 	
-	This driver currently supports:
-	
-	Alps TDME7 (Tuner: MITEL SP5659)
-	Alps TDED4 (Tuner: TI ALP510, external Nxt6000)
-	Comtech DVBT-6k07 (PLL IC: SP5730)
-
     Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
     Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au>
 
@@ -34,217 +27,68 @@
 #include <linux/slab.h>
 
 #include "dvb_frontend.h"
+#include "nxt6000_priv.h"
 #include "nxt6000.h"
 
-MODULE_DESCRIPTION("NxtWave NXT6000 DVB demodulator driver");
-MODULE_AUTHOR("Florian Schirmer");
-MODULE_LICENSE("GPL");
-
-static int debug = 0;
-MODULE_PARM(debug, "i");
 
-static struct dvb_frontend_info nxt6000_info = {
 
-	.name = "NxtWave NXT6000",
-	.type = FE_OFDM,
-	.frequency_min = 0,
-	.frequency_max = 863250000,
-	.frequency_stepsize = 62500,
-	/*.frequency_tolerance = */	/* FIXME: 12% of SR */
-	.symbol_rate_min = 0,		/* FIXME */
-	.symbol_rate_max = 9360000,	/* FIXME */
-	.symbol_rate_tolerance = 4000,
-	.notifier_delay = 0,
-	.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | 
-                FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | 
-                FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | 
-                FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | 
-                FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | 
-                FE_CAN_HIERARCHY_AUTO,
-};
+struct nxt6000_state {
 
-struct nxt6000_config {
-	u8 demod_addr;
-	u8 tuner_addr;
-	u8 tuner_type;
-	u8 clock_inversion;
 	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-};
 
-#define TUNER_TYPE_ALP510	0
-#define TUNER_TYPE_SP5659	1
-#define TUNER_TYPE_SP5730	2
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct nxt6000_config* config;
 
-// #define FE2NXT(fe) ((struct nxt6000_config *)((fe)->data))
-#define FREQ2DIV(freq) ((freq + 36166667) / 166667)
+	struct dvb_frontend frontend;
 
+};
+
+static int debug = 0;
 #define dprintk if (debug) printk
 
-static int nxt6000_write(struct i2c_adapter *i2c, u8 addr, u8 reg, u8 data)
+static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data)
 {
 	u8 buf[] = {reg, data};
-	struct i2c_msg msg = {.addr = addr >> 1, .flags = 0, .buf = buf, .len = 2};
+	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 };
 	int ret;
 	
-	if ((ret = i2c_transfer(i2c, &msg, 1)) != 1)
-		dprintk("nxt6000: nxt6000_write error (.addr = 0x%02X, reg: 0x%02X, data: 0x%02X, ret: %d)\n", addr, reg, data, ret);
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
+		dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret);
 
 	return (ret != 1) ? -EFAULT : 0;
 }
 
-static u8 nxt6000_writereg(struct nxt6000_config *nxt, u8 reg, u8 data)
-{
-	return nxt6000_write(nxt->i2c, nxt->demod_addr, reg, data);
-}
-
-static u8 nxt6000_read(struct i2c_adapter *i2c, u8 addr, u8 reg)
+static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg)
 {
 	int ret;
 	u8 b0[] = {reg};
 	u8 b1[] = {0};
 	struct i2c_msg msgs[] = {
-		{.addr = addr >> 1,.flags = 0,.buf = b0,.len = 1},
-		{.addr = addr >> 1,.flags = I2C_M_RD,.buf = b1,.len = 1}
+		{.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(i2c, msgs, 2);
+	ret = i2c_transfer(state->i2c, msgs, 2);
 	
 	if (ret != 2)
-		dprintk("nxt6000: nxt6000_read error (.addr = 0x%02X, reg: 0x%02X, ret: %d)\n", addr, reg, ret);
+		dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret);
 	
 	return b1[0];
 }
 
-static u8 nxt6000_readreg(struct nxt6000_config *nxt, u8 reg)
-{
-	return nxt6000_read(nxt->i2c, nxt->demod_addr, reg);
-}
-
-static int pll_test(struct i2c_adapter *i2c, u8 demod_addr, u8 tuner_addr)
-{
-	u8 buf [1];
-	struct i2c_msg msg = {.addr = tuner_addr >> 1,.flags = I2C_M_RD,.buf = buf,.len = 1 };
-	int ret;
-
-	nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x01);	/* open i2c bus switch */
-	ret = i2c_transfer(i2c, &msg, 1);
-	nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x00);	/* close i2c bus switch */
-
-	return (ret != 1) ? -EFAULT : 0;
-}
-
-static int pll_write(struct i2c_adapter *i2c, u8 demod_addr, u8 tuner_addr, u8 * buf, u8 len)
-{
-	struct i2c_msg msg = {.addr = tuner_addr >> 1, .flags = 0, .buf = buf, .len = len};
-	int ret;
-				
-	nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x01);		/* open i2c bus switch */
-	ret = i2c_transfer(i2c, &msg, 1);
-	nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x00);		/* close i2c bus switch */
-										
-	if (ret != 1)
-		dprintk("nxt6000: pll_write error %d\n", ret);
-																
-	return (ret != 1) ? -EFAULT : 0;
-}
-
-static int sp5659_set_tv_freq(struct nxt6000_config *nxt, u32 freq)
-{
-	u8 buf[4];
-
-	buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F;
-	buf[1] = FREQ2DIV(freq) & 0xFF;
-	buf[2] = (((FREQ2DIV(freq) >> 15) & 0x03) << 5) | 0x85;
-
-	if ((freq >= 174000000) && (freq < 230000000))
-		buf[3] = 0x82;
-	else if ((freq >= 470000000) && (freq < 782000000))
-		buf[3] = 0x85;
-	else if ((freq >= 782000000) && (freq < 863000000))
-		buf[3] = 0xC5;
-	else
-		return -EINVAL;
-
-	return pll_write(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4);
-}
-
-static int alp510_set_tv_freq(struct nxt6000_config *nxt, u32 freq)
-{
-	u8 buf[4];
-
-	buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F;
-	buf[1] = FREQ2DIV(freq) & 0xFF;
-	buf[2] = 0x85;
-
-#if 0
-	if ((freq >= 47000000) && (freq < 153000000))
-		buf[3] = 0x01;
-	else if ((freq >= 153000000) && (freq < 430000000))
-		buf[3] = 0x02;
-	else if ((freq >= 430000000) && (freq < 824000000))
-		buf[3] = 0x08;
-	else if ((freq >= 824000000) && (freq < 863000000))
-		buf[3] = 0x88;
-	else
-		return -EINVAL;
-#else
-	if ((freq >= 47000000) && (freq < 153000000))
-		buf[3] = 0x01;
-	else if ((freq >= 153000000) && (freq < 430000000))
-		buf[3] = 0x02;
-	else if ((freq >= 430000000) && (freq < 824000000))
-		buf[3] = 0x0C;
-	else if ((freq >= 824000000) && (freq < 863000000))
-		buf[3] = 0x8C;
-	else
-		return -EINVAL;
-#endif
-
-	return pll_write(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4);
-}
-
-static int sp5730_set_tv_freq(struct nxt6000_config *nxt, u32 freq)
-{
-	u8 buf[4];
-
-	buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F;
-	buf[1] = FREQ2DIV(freq) & 0xFF;
-	buf[2] = 0x93;
-
-	if ((freq >= 51000000) && (freq < 132100000))
-		buf[3] = 0x05;
-	else if ((freq >= 132100000) && (freq < 143000000))
-		buf[3] = 0x45;
-	else if ((freq >= 146000000) && (freq < 349100000))
-		buf[3] = 0x06;
-	else if ((freq >= 349100000) && (freq < 397100000))
-		buf[3] = 0x46;
-	else if ((freq >= 397100000) && (freq < 426000000))
-		buf[3] = 0x86;
-	else if ((freq >= 430000000) && (freq < 659100000))
-		buf[3] = 0x03;
-	else if ((freq >= 659100000) && (freq < 759100000))
-		buf[3] = 0x43;
-	else if ((freq >= 759100000) && (freq < 858000000))
-		buf[3] = 0x83;
-	else
-		return -EINVAL;
-
-	return pll_write(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4);
-}
-
-static void nxt6000_reset(struct nxt6000_config *fe)
+static void nxt6000_reset(struct nxt6000_state* state)
 {
 	u8 val;
 
-	val = nxt6000_readreg(fe, OFDM_COR_CTL);
+	val = nxt6000_readreg(state, OFDM_COR_CTL);
 	
-	nxt6000_writereg(fe, OFDM_COR_CTL, val & ~COREACT);
-	nxt6000_writereg(fe, OFDM_COR_CTL, val | COREACT);
+	nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT);
+	nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT);
 }
 
-static int nxt6000_set_bandwidth(struct nxt6000_config *fe, fe_bandwidth_t bandwidth)
+static int nxt6000_set_bandwidth(struct nxt6000_state* state, fe_bandwidth_t bandwidth)
 {
 	u16 nominal_rate;
 	int result;
@@ -270,119 +114,115 @@ static int nxt6000_set_bandwidth(struct 
 			break;
 
 		default:
-			
 			return -EINVAL;
-			
 	}
 
-	if ((result = nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0)
+	if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0)
 		return result;
 		
-	return nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF);
+	return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF);
 }
 
-static int nxt6000_set_guard_interval(struct nxt6000_config *fe, fe_guard_interval_t guard_interval)
+static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval)
 {
 	switch(guard_interval) {
 	
 		case GUARD_INTERVAL_1_32:
-
-			return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
 
 		case GUARD_INTERVAL_1_16:
-
-			return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
 
 		case GUARD_INTERVAL_AUTO:
 		case GUARD_INTERVAL_1_8:
-
-			return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
 
 		case GUARD_INTERVAL_1_4:
-
-			return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
 			
 		default:
 			return -EINVAL;
 	}
 }
 
-static int nxt6000_set_inversion(struct nxt6000_config *fe, fe_spectral_inversion_t inversion)
+static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion)
 {
 	switch(inversion) {
 	
 		case INVERSION_OFF:
-		
-			return nxt6000_writereg(fe, OFDM_ITB_CTL, 0x00);
+		return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00);
 			
 		case INVERSION_ON:
-		
-			return nxt6000_writereg(fe, OFDM_ITB_CTL, ITBINV);
+		return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV);
 
 		default:
-		
 			return -EINVAL;	
 	
 	}
 }
 
-static int nxt6000_set_transmission_mode(struct nxt6000_config *fe, fe_transmit_mode_t transmission_mode)
+static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode)
 {
 	int result;
 
 	switch(transmission_mode) {
 
 		case TRANSMISSION_MODE_2K:	
-
-			if ((result = nxt6000_writereg(fe, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(fe, EN_DMD_RACQ) & ~0x03))) < 0)
+		if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0)
 				return result;
 				
-			return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04));
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04));
 
 		case TRANSMISSION_MODE_8K:	
 		case TRANSMISSION_MODE_AUTO:	
-
-			if ((result = nxt6000_writereg(fe, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(fe, EN_DMD_RACQ) & ~0x03))) < 0)
+		if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0)
 				return result;
 
-			return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04));
+		return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04));
 
 		default:
-			
 			return -EINVAL;
 	
 	}
 }
 
-static void nxt6000_setup(struct nxt6000_config *fe)
+static void nxt6000_setup(struct dvb_frontend* fe)
 {
-	nxt6000_writereg(fe, RS_COR_SYNC_PARAM, SYNC_PARAM);
-	nxt6000_writereg(fe, BER_CTRL, /*(1 << 2) |*/ (0x01 << 1) | 0x01);
-	nxt6000_writereg(fe, VIT_COR_CTL, VIT_COR_RESYNC);
-	nxt6000_writereg(fe, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(fe, OFDM_COR_CTL) & 0x0F));
-	nxt6000_writereg(fe, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02);
-	nxt6000_writereg(fe, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW);
-	nxt6000_writereg(fe, OFDM_ITB_FREQ_1, 0x06);
-	nxt6000_writereg(fe, OFDM_ITB_FREQ_2, 0x31);
-	nxt6000_writereg(fe, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04);
-	nxt6000_writereg(fe, CAS_FREQ, 0xBB);	/* CHECKME */
-	nxt6000_writereg(fe, OFDM_SYR_CTL, 1 << 2);
-	nxt6000_writereg(fe, OFDM_PPM_CTL_1, PPM256);
-	nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_1, 0x49);
-	nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_2, 0x72);
-	nxt6000_writereg(fe, ANALOG_CONTROL_0, 1 << 5);
-	nxt6000_writereg(fe, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2);
-	nxt6000_writereg(fe, DIAG_CONFIG, TB_SET);
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
 	
-	if (fe->clock_inversion)
-		nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, CLKINVERSION);
+	nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM);
+	nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01);
+	nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC);
+	nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F));
+	nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02);
+	nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW);
+	nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06);
+	nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31);
+	nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04);
+	nxt6000_writereg(state, CAS_FREQ, 0xBB);	/* CHECKME */
+	nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2);
+	nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256);
+	nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49);
+	nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72);
+	nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5);
+	nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2);
+	nxt6000_writereg(state, DIAG_CONFIG, TB_SET);
+
+	if (state->config->clock_inversion)
+		nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION);
 	else
-		nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, 0);
+		nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0);
+
+	nxt6000_writereg(state, TS_FORMAT, 0);
 		
-	nxt6000_writereg(fe, TS_FORMAT, 0);
+	if (state->config->pll_init) {
+		nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01);	/* open i2c bus switch */
+		state->config->pll_init(fe);
+		nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00);	/* close i2c bus switch */
+	}
 }
 
-static void nxt6000_dump_status(struct nxt6000_config *fe)
+static void nxt6000_dump_status(struct nxt6000_state *state)
 {
 	u8 val;
 
@@ -400,12 +240,12 @@ static void nxt6000_dump_status(struct n
 */
 	printk("NXT6000 status:");
 
-	val = nxt6000_readreg(fe, RS_COR_STAT);
+	val = nxt6000_readreg(state, RS_COR_STAT);
 	
 	printk(" DATA DESCR LOCK: %d,", val & 0x01);
 	printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01);
 
-	val = nxt6000_readreg(fe, VIT_SYNC_STATUS);
+	val = nxt6000_readreg(state, VIT_SYNC_STATUS);
 
 	printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01);
 
@@ -443,7 +283,7 @@ static void nxt6000_dump_status(struct n
 			
 	}
 
-	val = nxt6000_readreg(fe, OFDM_COR_STAT);
+	val = nxt6000_readreg(state, OFDM_COR_STAT);
 	
 	printk(" CHCTrack: %d,", (val >> 7) & 0x01);
 	printk(" TPSLock: %d,", (val >> 6) & 0x01);
@@ -496,7 +336,7 @@ static void nxt6000_dump_status(struct n
 			
 	}
 
-	val = nxt6000_readreg(fe, OFDM_SYR_STAT);
+	val = nxt6000_readreg(state, OFDM_SYR_STAT);
 
 	printk(" SYRLock: %d,", (val >> 4) & 0x01);
 	printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K");
@@ -522,14 +362,11 @@ static void nxt6000_dump_status(struct n
 			break;
 	
 		case 0x03: 
-		
 			printk(" SYRGuard: 1/4,");
-			
 			break;
-			
 	}
 
-	val = nxt6000_readreg(fe, OFDM_TPS_RCVD_3);
+	val = nxt6000_readreg(state, OFDM_TPS_RCVD_3);
 	
 	switch((val >> 4) & 0x07) {
 	
@@ -599,7 +436,7 @@ static void nxt6000_dump_status(struct n
 			
 	}
 
-	val = nxt6000_readreg(fe, OFDM_TPS_RCVD_4);
+	val = nxt6000_readreg(state, OFDM_TPS_RCVD_4);
 	
 	printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K");
 	
@@ -632,299 +469,156 @@ static void nxt6000_dump_status(struct n
 	}
 	
 	/* Strange magic required to gain access to RF_AGC_STATUS */
-	nxt6000_readreg(fe, RF_AGC_VAL_1);
-	val = nxt6000_readreg(fe, RF_AGC_STATUS);
-	val = nxt6000_readreg(fe, RF_AGC_STATUS);
+	nxt6000_readreg(state, RF_AGC_VAL_1);
+	val = nxt6000_readreg(state, RF_AGC_STATUS);
+	val = nxt6000_readreg(state, RF_AGC_STATUS);
 
 	printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01);
 	printk("\n");
 }
 
-static int nxt6000_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg)
-{
-	struct nxt6000_config *fe = (struct nxt6000_config *) f->data;
 
-	switch (cmd) {
 
-		case FE_GET_INFO:
 
-			memcpy(arg, &nxt6000_info, sizeof (struct dvb_frontend_info));
 
-			return 0;
 
-		case FE_READ_STATUS:
-		{
-			fe_status_t *status = (fe_status_t *)arg;
 
+
+
+
+
+static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
 			u8 core_status;
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
 
 			*status = 0;
 			
-			core_status = nxt6000_readreg(fe, OFDM_COR_STAT);
+	core_status = nxt6000_readreg(state, OFDM_COR_STAT);
 
 			if (core_status & AGCLOCKED)
 				*status |= FE_HAS_SIGNAL;
 
-			if (nxt6000_readreg(fe, OFDM_SYR_STAT) & GI14_SYR_LOCK)
+	if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK)
 				*status |= FE_HAS_CARRIER;
 
-			if (nxt6000_readreg(fe, VIT_SYNC_STATUS) & VITINSYNC)
+	if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC)
 				*status |= FE_HAS_VITERBI;
 
-			if (nxt6000_readreg(fe, RS_COR_STAT) & RSCORESTATUS)
+	if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS)
 				*status |= FE_HAS_SYNC;
 				
 			if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)))
 				*status |= FE_HAS_LOCK;
 				
 			if (debug)
-				nxt6000_dump_status(fe);
+		nxt6000_dump_status(state);
 
 			return 0;
-			
 		}
 	
-		case FE_READ_BER:
+static int nxt6000_init(struct dvb_frontend* fe)
 		{
-			u32 *ber = (u32 *)arg;
-
-			*ber=0;
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
 
-			return 0;
-			
-		}
+	nxt6000_reset(state);
+	nxt6000_setup(fe);
 	
-		case FE_READ_SIGNAL_STRENGTH:
-		{
-			s16 *signal = (s16 *) arg;
-/*
-			*signal=(((signed char)readreg(client, 0x16))+128)<<8;
-*/
-			*signal = 0;
 			return 0;
-			
-		}
-	
-		case FE_READ_SNR:
-		{
-			s16 *snr = (s16 *) arg;
-/*
-			*snr=readreg(client, 0x24)<<8;
-			*snr|=readreg(client, 0x25);
-*/
-			*snr = 0;
-			break;
-		}
-	
-		case FE_READ_UNCORRECTED_BLOCKS: 
-		{
-			u32 *ublocks = (u32 *)arg;
-
-			*ublocks = 0;
-
-			break;
 		}
 	
-		case FE_INIT:
-			nxt6000_reset(fe);
-			nxt6000_setup(fe);
-		break;
 
-		case FE_SET_FRONTEND:
+static int nxt6000_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *param)
 		{
-			struct dvb_frontend_parameters *param = (struct dvb_frontend_parameters *)arg;
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
 			int result;
 
-			switch (fe->tuner_type) {
-			
-				case TUNER_TYPE_ALP510:
-					if ((result = alp510_set_tv_freq(fe, param->frequency)) < 0)
-						return result;
-					break;
-
-				case TUNER_TYPE_SP5659:
-
-					if ((result = sp5659_set_tv_freq(fe, param->frequency)) < 0)
-						return result;
-						
-					break;
-					
-				case TUNER_TYPE_SP5730:
+	nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01);	/* open i2c bus switch */
+	state->config->pll_set(fe, param);
+	nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00);	/* close i2c bus switch */
 
-					if ((result = sp5730_set_tv_freq(fe, param->frequency)) < 0)
+	if ((result = nxt6000_set_bandwidth(state, param->u.ofdm.bandwidth)) < 0)
 						return result;
-
-					break;
-
-				default:
-				
-					return -EFAULT;
-					
-			}
-
-			if ((result = nxt6000_set_bandwidth(fe, param->u.ofdm.bandwidth)) < 0)
-				return result;
-			if ((result = nxt6000_set_guard_interval(fe, param->u.ofdm.guard_interval)) < 0)
+	if ((result = nxt6000_set_guard_interval(state, param->u.ofdm.guard_interval)) < 0)
 				return result;
-			if ((result = nxt6000_set_transmission_mode(fe, param->u.ofdm.transmission_mode)) < 0)
+	if ((result = nxt6000_set_transmission_mode(state, param->u.ofdm.transmission_mode)) < 0)
 				return result;
-			if ((result = nxt6000_set_inversion(fe, param->inversion)) < 0)
+	if ((result = nxt6000_set_inversion(state, param->inversion)) < 0)
 				return result;
-			
-			break;
-		}
-
-		default:
-
-			return -EOPNOTSUPP;
-
-	}
 
 	return 0;
-	
 } 
 
-static u8 demod_addr_tbl[] = {0x14, 0x18, 0x24, 0x28};
-
-static struct i2c_client client_template;
 
-static int attach_adapter(struct i2c_adapter *adapter)
+static void nxt6000_release(struct dvb_frontend* fe)
 {
-	struct i2c_client *client;
-	struct nxt6000_config *nxt;
-	u8 addr_nr;
-	int ret;
-	
-	if ((nxt = kmalloc(sizeof(struct nxt6000_config), GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	memset(nxt, 0, sizeof(*nxt));
-	nxt->i2c = adapter;
-
-	for (addr_nr = 0; addr_nr < sizeof(demod_addr_tbl); addr_nr++) {
-	
-		if (nxt6000_read(adapter, demod_addr_tbl[addr_nr], OFDM_MSC_REV) != NXT6000ASICDEVICE)
-			continue;
-
-		if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC0) == 0) {
-			nxt->tuner_addr = 0xC0;
-			nxt->tuner_type = TUNER_TYPE_ALP510;
-			nxt->clock_inversion = 1;
-	
-			dprintk("nxt6000: detected TI ALP510 tuner at 0x%02X\n", nxt->tuner_addr);
-		
-		} else if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC2) == 0) {
-			nxt->tuner_addr = 0xC2;
-			nxt->tuner_type = TUNER_TYPE_SP5659;
-			nxt->clock_inversion = 0;
-
-			dprintk("nxt6000: detected MITEL SP5659 tuner at 0x%02X\n", nxt->tuner_addr);
-		
-		} else if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC0) == 0) {
-			nxt->tuner_addr = 0xC0;
-			nxt->tuner_type = TUNER_TYPE_SP5730;
-			nxt->clock_inversion = 0;
-
-			dprintk("nxt6000: detected SP5730 tuner at 0x%02X\n", nxt->tuner_addr);
-		
-		} else {
-			printk("nxt6000: unable to detect tuner\n");
-			continue;	
-		}
+	struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+	kfree(state);
 	}
 	
-	if (addr_nr == sizeof(demod_addr_tbl)) {
-		kfree(nxt);
-		return -ENODEV;
-	}
+static struct dvb_frontend_ops nxt6000_ops;
 	
-	nxt->demod_addr = demod_addr_tbl[addr_nr];
-
-	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
-		kfree(nxt);
-		return -ENOMEM;
-}
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = demod_addr_tbl[addr_nr];
-	i2c_set_clientdata(client, (void *) nxt);
-
-	ret = i2c_attach_client(client);
-	if (ret)
-		goto out;
-
-	BUG_ON(!nxt->dvb);
-
-	ret = dvb_register_frontend(nxt6000_ioctl, nxt->dvb, nxt, &nxt6000_info, THIS_MODULE);
-	if (ret) {
-		i2c_detach_client(client);
-		goto out;
-	}
-
-	ret = 0;
-out:
-	kfree(client);
-	kfree(nxt);
-	return ret;
-}
-
-static int detach_client(struct i2c_client *client)
+struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+				    struct i2c_adapter* i2c)
 {
-	struct nxt6000_config *state = (struct nxt6000_config *) i2c_get_clientdata(client);
-	dvb_unregister_frontend(nxt6000_ioctl, state->dvb);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
-	kfree(state);
-	return 0;
-}
+	struct nxt6000_state* state = NULL;
 
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
-	struct nxt6000_config *state = (struct nxt6000_config *) i2c_get_clientdata(client);
+	/* allocate memory for the internal state */
+	state = (struct nxt6000_state*) kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+	}
+
+static struct dvb_frontend_ops nxt6000_ops = {
+
+	.info = {
+		.name = "NxtWave NXT6000 DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 0,
+		.frequency_max = 863250000,
+		.frequency_stepsize = 62500,
+		/*.frequency_tolerance = *//* FIXME: 12% of SR */
+		.symbol_rate_min = 0,	/* FIXME */
+		.symbol_rate_max = 9360000,	/* FIXME */
+		.symbol_rate_tolerance = 4000,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+	                FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+	                FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+	                FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+	                FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+	                FE_CAN_HIERARCHY_AUTO,
+	},
 
-	switch (cmd) {
-	case FE_REGISTER:{
-			state->dvb = (struct dvb_adapter *) arg;
-			break;
-		}
-	case FE_UNREGISTER:{
-			state->dvb = NULL;
-			break;
-		}
-	default:
-		return -EOPNOTSUPP;
-	}
-	return 0;
-}
+	.release = nxt6000_release,
+
+	.init = nxt6000_init,
 	
-static struct i2c_driver driver = {
-	.owner = THIS_MODULE,
-	.name = "nxt6000",
-	.id = I2C_DRIVERID_DVBFE_NXT6000,
-	.flags = I2C_DF_NOTIFY,
-	.attach_adapter = attach_adapter,
-	.detach_client = detach_client,
-	.command = command,
-};
+	.set_frontend = nxt6000_set_frontend,
 	
-static struct i2c_client client_template = {
-	I2C_DEVNAME("nxt6000"),
-	.flags = I2C_CLIENT_ALLOW_USE,
-	.driver = &driver,
+	.read_status = nxt6000_read_status,
 };
 
-static __init int nxt6000_init(void)
-{
-	return i2c_add_driver(&driver);
-}
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-static __exit void nxt6000_exit(void)
-{
-	if (i2c_del_driver(&driver))
-		printk("nxt6000: driver deregistration failed\n");
-}
+MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver");
+MODULE_AUTHOR("Florian Schirmer");
+MODULE_LICENSE("GPL");
 
-module_init(nxt6000_init);
-module_exit(nxt6000_exit);
+EXPORT_SYMBOL(nxt6000_attach);
diff -puN drivers/media/dvb/frontends/nxt6000.h~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/nxt6000.h
--- 25/drivers/media/dvb/frontends/nxt6000.h~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.390062552 -0800
+++ 25-akpm/drivers/media/dvb/frontends/nxt6000.h	2004-11-21 17:13:23.462051608 -0800
@@ -1,266 +1,43 @@
 /*
- * Public Include File for DRV6000 users
- * (ie. NxtWave Communications - NXT6000 demodulator driver)
- *
- * Copyright (C) 2001 NxtWave Communications, Inc.
- *
- */
-
-/*  Nxt6000 Register Addresses and Bit Masks */
-
-/* Maximum Register Number */
-#define MAXNXT6000REG          (0x9A)
-
-/* 0x1B A_VIT_BER_0  aka 0x3A */
-#define A_VIT_BER_0            (0x1B)
-
-/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */
-#define A_VIT_BER_TIMER_0      (0x1D)
-
-/* 0x21 RS_COR_STAT */
-#define RS_COR_STAT            (0x21)
-#define RSCORESTATUS           (0x03)
-
-/* 0x22 RS_COR_INTEN */
-#define RS_COR_INTEN           (0x22)
-
-/* 0x23 RS_COR_INSTAT */
-#define RS_COR_INSTAT          (0x23)
-#define INSTAT_ERROR           (0x04)
-#define LOCK_LOSS_BITS         (0x03)
-
-/* 0x24 RS_COR_SYNC_PARAM */
-#define RS_COR_SYNC_PARAM      (0x24)
-#define SYNC_PARAM             (0x03)
-
-/* 0x25 BER_CTRL */
-#define BER_CTRL               (0x25)
-#define BER_ENABLE             (0x02)
-#define BER_RESET              (0x01)
-
-/* 0x26 BER_PAY */
-#define BER_PAY                (0x26)
-
-/* 0x27 BER_PKT_L */
-#define BER_PKT_L              (0x27)
-#define BER_PKTOVERFLOW        (0x80)
-
-/* 0x30 VIT_COR_CTL */
-#define VIT_COR_CTL            (0x30)
-#define BER_CONTROL            (0x02)
-#define VIT_COR_MASK           (0x82)
-#define VIT_COR_RESYNC         (0x80)
-
-
-/* 0x32 VIT_SYNC_STATUS */
-#define VIT_SYNC_STATUS        (0x32)
-#define VITINSYNC              (0x80)
-
-/* 0x33 VIT_COR_INTEN */
-#define VIT_COR_INTEN          (0x33)
-#define GLOBAL_ENABLE          (0x80)
-
-/* 0x34 VIT_COR_INTSTAT */
-#define VIT_COR_INTSTAT        (0x34)
-#define BER_DONE               (0x08)
-#define BER_OVERFLOW           (0x10)
-
-/* 0x38 OFDM_BERTimer */     /* Use the alias registers */
-#define A_VIT_BER_TIMER_0      (0x1D)
-
-/* 0x3A VIT_BER_TIMER_0 */   /* Use the alias registers */
-#define A_VIT_BER_0            (0x1B)
-
-/* 0x40 OFDM_COR_CTL */
-#define OFDM_COR_CTL           (0x40)
-#define COREACT                (0x20)
-#define HOLDSM                 (0x10)
-#define WAIT_AGC               (0x02)
-#define WAIT_SYR               (0x03)
-
-/* 0x41 OFDM_COR_STAT */
-#define OFDM_COR_STAT          (0x41)
-#define COR_STATUS             (0x0F)
-#define MONITOR_TPS            (0x06)
-#define TPSLOCKED              (0x40)
-#define AGCLOCKED              (0x10)
-
-/* 0x42 OFDM_COR_INTEN */
-#define OFDM_COR_INTEN         (0x42)
-#define TPSRCVBAD              (0x04)
-#define TPSRCVCHANGED         (0x02)
-#define TPSRCVUPDATE           (0x01)
-
-/* 0x43 OFDM_COR_INSTAT */
-#define OFDM_COR_INSTAT        (0x43)
-
-/* 0x44 OFDM_COR_MODEGUARD */
-#define OFDM_COR_MODEGUARD     (0x44)
-#define FORCEMODE              (0x08)
-#define FORCEMODE8K			   (0x04)
-
-/* 0x45 OFDM_AGC_CTL */
-#define OFDM_AGC_CTL           (0x45)
-#define INITIAL_AGC_BW		   (0x08)
-#define AGCNEG                 (0x02)
-#define AGCLAST				   (0x10)
-
-/* 0x48 OFDM_AGC_TARGET */
-#define OFDM_AGC_TARGET		   (0x48)
-#define OFDM_AGC_TARGET_DEFAULT (0x28)
-#define OFDM_AGC_TARGET_IMPULSE (0x38)
-
-/* 0x49 OFDM_AGC_GAIN_1 */
-#define OFDM_AGC_GAIN_1        (0x49)
-
-/* 0x4B OFDM_ITB_CTL */
-#define OFDM_ITB_CTL           (0x4B)
-#define ITBINV                 (0x01)
-
-/* 0x4C OFDM_ITB_FREQ_1 */
-#define OFDM_ITB_FREQ_1        (0x4C)
-
-/* 0x4D OFDM_ITB_FREQ_2 */
-#define OFDM_ITB_FREQ_2        (0x4D)
-
-/* 0x4E  OFDM_CAS_CTL */       
-#define OFDM_CAS_CTL           (0x4E)
-#define ACSDIS                 (0x40)
-#define CCSEN                  (0x80)
-
-/* 0x4F CAS_FREQ */
-#define CAS_FREQ               (0x4F)
-
-/* 0x51 OFDM_SYR_CTL */
-#define OFDM_SYR_CTL           (0x51)
-#define SIXTH_ENABLE           (0x80)
-#define SYR_TRACKING_DISABLE   (0x01)
-
-/* 0x52 OFDM_SYR_STAT */
-#define OFDM_SYR_STAT		   (0x52)
-#define GI14_2K_SYR_LOCK	   (0x13)
-#define GI14_8K_SYR_LOCK	   (0x17)
-#define GI14_SYR_LOCK		   (0x10)
-
-/* 0x55 OFDM_SYR_OFFSET_1 */
-#define OFDM_SYR_OFFSET_1      (0x55)
-
-/* 0x56 OFDM_SYR_OFFSET_2 */
-#define OFDM_SYR_OFFSET_2      (0x56)
-
-/* 0x58 OFDM_SCR_CTL */
-#define OFDM_SCR_CTL           (0x58)
-#define SYR_ADJ_DECAY_MASK     (0x70)
-#define SYR_ADJ_DECAY          (0x30)
- 
-/* 0x59 OFDM_PPM_CTL_1 */
-#define OFDM_PPM_CTL_1         (0x59)
-#define PPMMAX_MASK            (0x30)
-#define PPM256				   (0x30)
-
-/* 0x5B OFDM_TRL_NOMINALRATE_1 */
-#define OFDM_TRL_NOMINALRATE_1 (0x5B)
-
-/* 0x5C OFDM_TRL_NOMINALRATE_2 */
-#define OFDM_TRL_NOMINALRATE_2 (0x5C)
-
-/* 0x5D OFDM_TRL_TIME_1 */
-#define OFDM_TRL_TIME_1        (0x5D)
-
-/* 0x60 OFDM_CRL_FREQ_1 */
-#define OFDM_CRL_FREQ_1        (0x60)
-
-/* 0x63 OFDM_CHC_CTL_1 */
-#define OFDM_CHC_CTL_1         (0x63)
-#define MANMEAN1               (0xF0);
-#define CHCFIR                 (0x01)
-
-/* 0x64 OFDM_CHC_SNR */
-#define OFDM_CHC_SNR           (0x64)
-
-/* 0x65 OFDM_BDI_CTL */
-#define OFDM_BDI_CTL           (0x65)
-#define LP_SELECT              (0x02)
-
-/* 0x67 OFDM_TPS_RCVD_1 */
-#define OFDM_TPS_RCVD_1        (0x67)
-#define TPSFRAME               (0x03)
-
-/* 0x68 OFDM_TPS_RCVD_2 */
-#define OFDM_TPS_RCVD_2        (0x68)
-
-/* 0x69 OFDM_TPS_RCVD_3 */
-#define OFDM_TPS_RCVD_3        (0x69)
-
-/* 0x6A OFDM_TPS_RCVD_4 */
-#define OFDM_TPS_RCVD_4        (0x6A)
-
-/* 0x6B OFDM_TPS_RESERVED_1 */
-#define OFDM_TPS_RESERVED_1    (0x6B)
-
-/* 0x6C OFDM_TPS_RESERVED_2 */
-#define OFDM_TPS_RESERVED_2    (0x6C)
-
-/* 0x73 OFDM_MSC_REV */
-#define OFDM_MSC_REV           (0x73)
-
-/* 0x76 OFDM_SNR_CARRIER_2 */
-#define OFDM_SNR_CARRIER_2     (0x76)
-#define MEAN_MASK              (0x80)
-#define MEANBIT                (0x80)
-
-/* 0x80 ANALOG_CONTROL_0 */
-#define ANALOG_CONTROL_0       (0x80)
-#define POWER_DOWN_ADC         (0x40)
-
-/* 0x81 ENABLE_TUNER_IIC */
-#define ENABLE_TUNER_IIC       (0x81)
-#define ENABLE_TUNER_BIT       (0x01)
-
-/* 0x82 EN_DMD_RACQ */
-#define EN_DMD_RACQ            (0x82)
-#define EN_DMD_RACQ_REG_VAL    (0x81) 
-#define EN_DMD_RACQ_REG_VAL_14 (0x01)
-
-/* 0x84 SNR_COMMAND */
-#define SNR_COMMAND            (0x84)
-#define SNRStat                (0x80)
-
-/* 0x85 SNRCARRIERNUMBER_LSB */
-#define SNRCARRIERNUMBER_LSB   (0x85)
-
-/* 0x87 SNRMINTHRESHOLD_LSB */
-#define SNRMINTHRESHOLD_LSB    (0x87)
-
-/* 0x89 SNR_PER_CARRIER_LSB */
-#define SNR_PER_CARRIER_LSB    (0x89)
-
-/* 0x8B SNRBELOWTHRESHOLD_LSB */
-#define SNRBELOWTHRESHOLD_LSB  (0x8B)
-
-/* 0x91 RF_AGC_VAL_1 */
-#define RF_AGC_VAL_1           (0x91)
-
-/* 0x92 RF_AGC_STATUS */
-#define RF_AGC_STATUS          (0x92)
-
-/* 0x98 DIAG_CONFIG */
-#define DIAG_CONFIG            (0x98)
-#define DIAG_MASK              (0x70)
-#define TB_SET                 (0x10)
-#define TRAN_SELECT            (0x07)
-#define SERIAL_SELECT          (0x01)
-
-/* 0x99 SUB_DIAG_MODE_SEL */
-#define SUB_DIAG_MODE_SEL      (0x99)
-#define CLKINVERSION           (0x01)
-
-/* 0x9A TS_FORMAT */
-#define TS_FORMAT              (0x9A)
-#define ERROR_SENSE            (0x08)
-#define VALID_SENSE            (0x04)
-#define SYNC_SENSE             (0x02)
-#define GATED_CLOCK            (0x01)
+	NxtWave Communications - NXT6000 demodulator driver
 
-#define NXT6000ASICDEVICE      (0x0b)
+    Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
+    Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au>
 
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef NXT6000_H
+#define NXT6000_H
+
+#include <linux/dvb/frontend.h>
+
+struct nxt6000_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* should clock inversion be used? */
+	u8 clock_inversion:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // NXT6000_H
diff -puN /dev/null drivers/media/dvb/frontends/nxt6000_priv.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/nxt6000_priv.h	2004-11-21 17:13:23.463051456 -0800
@@ -0,0 +1,265 @@
+/*
+ * Public Include File for DRV6000 users
+ * (ie. NxtWave Communications - NXT6000 demodulator driver)
+ *
+ * Copyright (C) 2001 NxtWave Communications, Inc.
+ *
+ */
+
+/*  Nxt6000 Register Addresses and Bit Masks */
+
+/* Maximum Register Number */
+#define MAXNXT6000REG          (0x9A)
+
+/* 0x1B A_VIT_BER_0  aka 0x3A */
+#define A_VIT_BER_0            (0x1B)
+
+/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */
+#define A_VIT_BER_TIMER_0      (0x1D)
+
+/* 0x21 RS_COR_STAT */
+#define RS_COR_STAT            (0x21)
+#define RSCORESTATUS           (0x03)
+
+/* 0x22 RS_COR_INTEN */
+#define RS_COR_INTEN           (0x22)
+
+/* 0x23 RS_COR_INSTAT */
+#define RS_COR_INSTAT          (0x23)
+#define INSTAT_ERROR           (0x04)
+#define LOCK_LOSS_BITS         (0x03)
+
+/* 0x24 RS_COR_SYNC_PARAM */
+#define RS_COR_SYNC_PARAM      (0x24)
+#define SYNC_PARAM             (0x03)
+
+/* 0x25 BER_CTRL */
+#define BER_CTRL               (0x25)
+#define BER_ENABLE             (0x02)
+#define BER_RESET              (0x01)
+
+/* 0x26 BER_PAY */
+#define BER_PAY                (0x26)
+
+/* 0x27 BER_PKT_L */
+#define BER_PKT_L              (0x27)
+#define BER_PKTOVERFLOW        (0x80)
+
+/* 0x30 VIT_COR_CTL */
+#define VIT_COR_CTL            (0x30)
+#define BER_CONTROL            (0x02)
+#define VIT_COR_MASK           (0x82)
+#define VIT_COR_RESYNC         (0x80)
+
+
+/* 0x32 VIT_SYNC_STATUS */
+#define VIT_SYNC_STATUS        (0x32)
+#define VITINSYNC              (0x80)
+
+/* 0x33 VIT_COR_INTEN */
+#define VIT_COR_INTEN          (0x33)
+#define GLOBAL_ENABLE          (0x80)
+
+/* 0x34 VIT_COR_INTSTAT */
+#define VIT_COR_INTSTAT        (0x34)
+#define BER_DONE               (0x08)
+#define BER_OVERFLOW           (0x10)
+
+			     /* 0x38 OFDM_BERTimer *//* Use the alias registers */
+#define A_VIT_BER_TIMER_0      (0x1D)
+
+			     /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */
+#define A_VIT_BER_0            (0x1B)
+
+/* 0x40 OFDM_COR_CTL */
+#define OFDM_COR_CTL           (0x40)
+#define COREACT                (0x20)
+#define HOLDSM                 (0x10)
+#define WAIT_AGC               (0x02)
+#define WAIT_SYR               (0x03)
+
+/* 0x41 OFDM_COR_STAT */
+#define OFDM_COR_STAT          (0x41)
+#define COR_STATUS             (0x0F)
+#define MONITOR_TPS            (0x06)
+#define TPSLOCKED              (0x40)
+#define AGCLOCKED              (0x10)
+
+/* 0x42 OFDM_COR_INTEN */
+#define OFDM_COR_INTEN         (0x42)
+#define TPSRCVBAD              (0x04)
+#define TPSRCVCHANGED         (0x02)
+#define TPSRCVUPDATE           (0x01)
+
+/* 0x43 OFDM_COR_INSTAT */
+#define OFDM_COR_INSTAT        (0x43)
+
+/* 0x44 OFDM_COR_MODEGUARD */
+#define OFDM_COR_MODEGUARD     (0x44)
+#define FORCEMODE              (0x08)
+#define FORCEMODE8K			   (0x04)
+
+/* 0x45 OFDM_AGC_CTL */
+#define OFDM_AGC_CTL           (0x45)
+#define INITIAL_AGC_BW		   (0x08)
+#define AGCNEG                 (0x02)
+#define AGCLAST				   (0x10)
+
+/* 0x48 OFDM_AGC_TARGET */
+#define OFDM_AGC_TARGET		   (0x48)
+#define OFDM_AGC_TARGET_DEFAULT (0x28)
+#define OFDM_AGC_TARGET_IMPULSE (0x38)
+
+/* 0x49 OFDM_AGC_GAIN_1 */
+#define OFDM_AGC_GAIN_1        (0x49)
+
+/* 0x4B OFDM_ITB_CTL */
+#define OFDM_ITB_CTL           (0x4B)
+#define ITBINV                 (0x01)
+
+/* 0x4C OFDM_ITB_FREQ_1 */
+#define OFDM_ITB_FREQ_1        (0x4C)
+
+/* 0x4D OFDM_ITB_FREQ_2 */
+#define OFDM_ITB_FREQ_2        (0x4D)
+
+/* 0x4E  OFDM_CAS_CTL */
+#define OFDM_CAS_CTL           (0x4E)
+#define ACSDIS                 (0x40)
+#define CCSEN                  (0x80)
+
+/* 0x4F CAS_FREQ */
+#define CAS_FREQ               (0x4F)
+
+/* 0x51 OFDM_SYR_CTL */
+#define OFDM_SYR_CTL           (0x51)
+#define SIXTH_ENABLE           (0x80)
+#define SYR_TRACKING_DISABLE   (0x01)
+
+/* 0x52 OFDM_SYR_STAT */
+#define OFDM_SYR_STAT		   (0x52)
+#define GI14_2K_SYR_LOCK	   (0x13)
+#define GI14_8K_SYR_LOCK	   (0x17)
+#define GI14_SYR_LOCK		   (0x10)
+
+/* 0x55 OFDM_SYR_OFFSET_1 */
+#define OFDM_SYR_OFFSET_1      (0x55)
+
+/* 0x56 OFDM_SYR_OFFSET_2 */
+#define OFDM_SYR_OFFSET_2      (0x56)
+
+/* 0x58 OFDM_SCR_CTL */
+#define OFDM_SCR_CTL           (0x58)
+#define SYR_ADJ_DECAY_MASK     (0x70)
+#define SYR_ADJ_DECAY          (0x30)
+
+/* 0x59 OFDM_PPM_CTL_1 */
+#define OFDM_PPM_CTL_1         (0x59)
+#define PPMMAX_MASK            (0x30)
+#define PPM256				   (0x30)
+
+/* 0x5B OFDM_TRL_NOMINALRATE_1 */
+#define OFDM_TRL_NOMINALRATE_1 (0x5B)
+
+/* 0x5C OFDM_TRL_NOMINALRATE_2 */
+#define OFDM_TRL_NOMINALRATE_2 (0x5C)
+
+/* 0x5D OFDM_TRL_TIME_1 */
+#define OFDM_TRL_TIME_1        (0x5D)
+
+/* 0x60 OFDM_CRL_FREQ_1 */
+#define OFDM_CRL_FREQ_1        (0x60)
+
+/* 0x63 OFDM_CHC_CTL_1 */
+#define OFDM_CHC_CTL_1         (0x63)
+#define MANMEAN1               (0xF0);
+#define CHCFIR                 (0x01)
+
+/* 0x64 OFDM_CHC_SNR */
+#define OFDM_CHC_SNR           (0x64)
+
+/* 0x65 OFDM_BDI_CTL */
+#define OFDM_BDI_CTL           (0x65)
+#define LP_SELECT              (0x02)
+
+/* 0x67 OFDM_TPS_RCVD_1 */
+#define OFDM_TPS_RCVD_1        (0x67)
+#define TPSFRAME               (0x03)
+
+/* 0x68 OFDM_TPS_RCVD_2 */
+#define OFDM_TPS_RCVD_2        (0x68)
+
+/* 0x69 OFDM_TPS_RCVD_3 */
+#define OFDM_TPS_RCVD_3        (0x69)
+
+/* 0x6A OFDM_TPS_RCVD_4 */
+#define OFDM_TPS_RCVD_4        (0x6A)
+
+/* 0x6B OFDM_TPS_RESERVED_1 */
+#define OFDM_TPS_RESERVED_1    (0x6B)
+
+/* 0x6C OFDM_TPS_RESERVED_2 */
+#define OFDM_TPS_RESERVED_2    (0x6C)
+
+/* 0x73 OFDM_MSC_REV */
+#define OFDM_MSC_REV           (0x73)
+
+/* 0x76 OFDM_SNR_CARRIER_2 */
+#define OFDM_SNR_CARRIER_2     (0x76)
+#define MEAN_MASK              (0x80)
+#define MEANBIT                (0x80)
+
+/* 0x80 ANALOG_CONTROL_0 */
+#define ANALOG_CONTROL_0       (0x80)
+#define POWER_DOWN_ADC         (0x40)
+
+/* 0x81 ENABLE_TUNER_IIC */
+#define ENABLE_TUNER_IIC       (0x81)
+#define ENABLE_TUNER_BIT       (0x01)
+
+/* 0x82 EN_DMD_RACQ */
+#define EN_DMD_RACQ            (0x82)
+#define EN_DMD_RACQ_REG_VAL    (0x81)
+#define EN_DMD_RACQ_REG_VAL_14 (0x01)
+
+/* 0x84 SNR_COMMAND */
+#define SNR_COMMAND            (0x84)
+#define SNRStat                (0x80)
+
+/* 0x85 SNRCARRIERNUMBER_LSB */
+#define SNRCARRIERNUMBER_LSB   (0x85)
+
+/* 0x87 SNRMINTHRESHOLD_LSB */
+#define SNRMINTHRESHOLD_LSB    (0x87)
+
+/* 0x89 SNR_PER_CARRIER_LSB */
+#define SNR_PER_CARRIER_LSB    (0x89)
+
+/* 0x8B SNRBELOWTHRESHOLD_LSB */
+#define SNRBELOWTHRESHOLD_LSB  (0x8B)
+
+/* 0x91 RF_AGC_VAL_1 */
+#define RF_AGC_VAL_1           (0x91)
+
+/* 0x92 RF_AGC_STATUS */
+#define RF_AGC_STATUS          (0x92)
+
+/* 0x98 DIAG_CONFIG */
+#define DIAG_CONFIG            (0x98)
+#define DIAG_MASK              (0x70)
+#define TB_SET                 (0x10)
+#define TRAN_SELECT            (0x07)
+#define SERIAL_SELECT          (0x01)
+
+/* 0x99 SUB_DIAG_MODE_SEL */
+#define SUB_DIAG_MODE_SEL      (0x99)
+#define CLKINVERSION           (0x01)
+
+/* 0x9A TS_FORMAT */
+#define TS_FORMAT              (0x9A)
+#define ERROR_SENSE            (0x08)
+#define VALID_SENSE            (0x04)
+#define SYNC_SENSE             (0x02)
+#define GATED_CLOCK            (0x01)
+
+#define NXT6000ASICDEVICE      (0x0b)
diff -puN /dev/null drivers/media/dvb/frontends/sp8870.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/sp8870.c	2004-11-21 17:13:23.466051000 -0800
@@ -0,0 +1,613 @@
+/*
+    Driver for Spase SP8870 demodulator
+
+    Copyright (C) 1999 Juergen Peitz
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "sp8870.h"
+
+
+struct sp8870_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct sp8870_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* demodulator private data */
+	u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "sp8870: " args); \
+	} while (0)
+
+/* firmware size for sp8870 */
+#define SP8870_FIRMWARE_SIZE 16382
+
+/* starting point for firmware in file 'Sc_main.mc' */
+#define SP8870_FIRMWARE_OFFSET 0x0A
+
+static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data)
+{
+        u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 };
+	int err;
+
+        if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+		dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+        return 0;
+}
+
+static int sp8870_readreg (struct sp8870_state* state, u16 reg)
+{
+	int ret;
+	u8 b0 [] = { reg >> 8 , reg & 0xff };
+	u8 b1 [] = { 0, 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+
+	if (ret != 2) {
+		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+		return -1;
+	}
+
+	return (b1[0] << 8 | b1[1]);
+}
+
+static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw)
+{
+	struct i2c_msg msg;
+	char *fw_buf = fw->data;
+	int fw_pos;
+	u8 tx_buf[255];
+	int tx_len;
+	int err = 0;
+
+	dprintk ("%s: ...\n", __FUNCTION__);
+
+	if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET)
+		return -EINVAL;
+
+	// system controller stop
+	sp8870_writereg(state, 0x0F00, 0x0000);
+
+	// instruction RAM register hiword
+	sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF));
+
+	// instruction RAM MWR
+	sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16));
+
+	// do firmware upload
+	fw_pos = SP8870_FIRMWARE_OFFSET;
+	while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){
+		tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos;
+		// write register 0xCF0A
+		tx_buf[0] = 0xCF;
+		tx_buf[1] = 0x0A;
+		memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len);
+		msg.addr = state->config->demod_address;
+		msg.flags = 0;
+		msg.buf = tx_buf;
+		msg.len = tx_len + 2;
+        	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+			printk("%s: firmware upload failed!\n", __FUNCTION__);
+			printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
+			return err;
+		}
+		fw_pos += tx_len;
+	}
+
+	dprintk ("%s: done!\n", __FUNCTION__);
+	return 0;
+};
+
+static void sp8870_microcontroller_stop (struct sp8870_state* state)
+{
+	sp8870_writereg(state, 0x0F08, 0x000);
+	sp8870_writereg(state, 0x0F09, 0x000);
+
+	// microcontroller STOP
+	sp8870_writereg(state, 0x0F00, 0x000);
+}
+
+static void sp8870_microcontroller_start (struct sp8870_state* state)
+{
+	sp8870_writereg(state, 0x0F08, 0x000);
+	sp8870_writereg(state, 0x0F09, 0x000);
+
+	// microcontroller START
+	sp8870_writereg(state, 0x0F00, 0x001);
+	// not documented but if we don't read 0x0D01 out here
+	// we don't get a correct data valid signal
+	sp8870_readreg(state, 0x0D01);
+}
+
+static int sp8870_read_data_valid_signal(struct sp8870_state* state)
+{
+	return (sp8870_readreg(state, 0x0D02) > 0);
+}
+
+static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
+{
+	int known_parameters = 1;
+
+	*reg0xc05 = 0x000;
+
+	switch (p->u.ofdm.constellation) {
+	case QPSK:
+		break;
+	case QAM_16:
+		*reg0xc05 |= (1 << 10);
+		break;
+	case QAM_64:
+		*reg0xc05 |= (2 << 10);
+		break;
+	case QAM_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (p->u.ofdm.hierarchy_information) {
+	case HIERARCHY_NONE:
+		break;
+	case HIERARCHY_1:
+		*reg0xc05 |= (1 << 7);
+		break;
+	case HIERARCHY_2:
+		*reg0xc05 |= (2 << 7);
+		break;
+	case HIERARCHY_4:
+		*reg0xc05 |= (3 << 7);
+		break;
+	case HIERARCHY_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (p->u.ofdm.code_rate_HP) {
+	case FEC_1_2:
+		break;
+	case FEC_2_3:
+		*reg0xc05 |= (1 << 3);
+		break;
+	case FEC_3_4:
+		*reg0xc05 |= (2 << 3);
+		break;
+	case FEC_5_6:
+		*reg0xc05 |= (3 << 3);
+		break;
+	case FEC_7_8:
+		*reg0xc05 |= (4 << 3);
+		break;
+	case FEC_AUTO:
+		known_parameters = 0;
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	if (known_parameters)
+		*reg0xc05 |= (2 << 1);	/* use specified parameters */
+	else
+		*reg0xc05 |= (1 << 1);	/* enable autoprobing */
+
+	return 0;
+}
+
+static int sp8870_wake_up(struct sp8870_state* state)
+{
+	// enable TS output and interface pins
+	return sp8870_writereg(state, 0xC18, 0x00D);
+}
+
+static int sp8870_set_frontend_parameters (struct dvb_frontend* fe,
+					   struct dvb_frontend_parameters *p)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int  err;
+	u16 reg0xc05;
+
+	if ((err = configure_reg0xc05(p, &reg0xc05)))
+		return err;
+
+	// system controller stop
+	sp8870_microcontroller_stop(state);
+
+	// set tuner parameters
+	sp8870_writereg(state, 0x206, 0x001);
+	state->config->pll_set(fe, p);
+	sp8870_writereg(state, 0x206, 0x000);
+
+	// sample rate correction bit [23..17]
+	sp8870_writereg(state, 0x0319, 0x000A);
+
+	// sample rate correction bit [16..0]
+	sp8870_writereg(state, 0x031A, 0x0AAB);
+
+	// integer carrier offset
+	sp8870_writereg(state, 0x0309, 0x0400);
+
+	// fractional carrier offset
+	sp8870_writereg(state, 0x030A, 0x0000);
+
+	// filter for 6/7/8 Mhz channel
+	if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+		sp8870_writereg(state, 0x0311, 0x0002);
+	else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+		sp8870_writereg(state, 0x0311, 0x0001);
+	else
+		sp8870_writereg(state, 0x0311, 0x0000);
+
+	// scan order: 2k first = 0x0000, 8k first = 0x0001
+	if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
+		sp8870_writereg(state, 0x0338, 0x0000);
+	else
+		sp8870_writereg(state, 0x0338, 0x0001);
+
+	sp8870_writereg(state, 0xc05, reg0xc05);
+
+	// read status reg in order to clear pending irqs
+	sp8870_readreg(state, 0x200);
+
+	// system controller start
+	sp8870_microcontroller_start(state);
+
+	return 0;
+}
+
+static int sp8870_init (struct dvb_frontend* fe)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+        const struct firmware *fw = NULL;
+
+	sp8870_wake_up(state);
+	if (state->initialised) return 0;
+	state->initialised = 1;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+
+	/* request the firmware, this will block until someone uploads it */
+	printk("sp8870: waiting for firmware upload...\n");
+	if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) {
+		printk("sp8870: no firmware upload (timeout or file not found?)\n");
+		release_firmware(fw);
+		return -EIO;
+	}
+
+	if (sp8870_firmware_upload(state, fw)) {
+		printk("sp8870: writing firmware to device failed\n");
+		release_firmware(fw);
+		return -EIO;
+	}
+
+	/* enable TS output and interface pins */
+	sp8870_writereg(state, 0xc18, 0x00d);
+
+	// system controller stop
+	sp8870_microcontroller_stop(state);
+
+	// ADC mode
+	sp8870_writereg(state, 0x0301, 0x0003);
+
+	// Reed Solomon parity bytes passed to output
+	sp8870_writereg(state, 0x0C13, 0x0001);
+
+	// MPEG clock is suppressed if no valid data
+	sp8870_writereg(state, 0x0C14, 0x0001);
+
+	/* bit 0x010: enable data valid signal */
+	sp8870_writereg(state, 0x0D00, 0x010);
+	sp8870_writereg(state, 0x0D01, 0x000);
+
+	/* setup PLL */
+	if (state->config->pll_init) {
+		sp8870_writereg(state, 0x206, 0x001);
+		state->config->pll_init(fe);
+		sp8870_writereg(state, 0x206, 0x000);
+	}
+
+	return 0;
+}
+
+static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int status;
+	int signal;
+
+	*fe_status = 0;
+
+	status = sp8870_readreg (state, 0x0200);
+	if (status < 0)
+		return -EIO;
+
+	signal = sp8870_readreg (state, 0x0303);
+	if (signal < 0)
+		return -EIO;
+
+	if (signal > 0x0F)
+		*fe_status |= FE_HAS_SIGNAL;
+	if (status & 0x08)
+		*fe_status |= FE_HAS_SYNC;
+	if (status & 0x04)
+		*fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI;
+
+	return 0;
+}
+
+static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int ret;
+	u32 tmp;
+
+	*ber = 0;
+
+	ret = sp8870_readreg(state, 0xC08);
+	if (ret < 0)
+		return -EIO;
+
+	tmp = ret & 0x3F;
+
+	ret = sp8870_readreg(state, 0xC07);
+	if (ret < 0)
+		return -EIO;
+
+	 tmp = ret << 6;
+
+	if (tmp >= 0x3FFF0)
+		tmp = ~0;
+
+	*ber = tmp;
+
+	return 0;
+}
+
+static int sp8870_read_signal_strength(struct dvb_frontend* fe,  u16 * signal)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int ret;
+	u16 tmp;
+
+	*signal = 0;
+
+	ret = sp8870_readreg (state, 0x306);
+	if (ret < 0)
+		return -EIO;
+
+	tmp = ret << 8;
+
+	ret = sp8870_readreg (state, 0x303);
+	if (ret < 0)
+		return -EIO;
+
+	tmp |= ret;
+
+	if (tmp)
+		*signal = 0xFFFF - tmp;
+
+	return 0;
+}
+
+static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	int ret;
+
+	*ublocks = 0;
+
+	ret = sp8870_readreg(state, 0xC0C);
+	if (ret < 0)
+		return -EIO;
+
+	if (ret == 0xFFFF)
+		ret = ~0;
+
+	*ublocks = ret;
+
+	return 0;
+}
+
+// number of trials to recover from lockup
+#define MAXTRIALS 5
+// maximum checks for data valid signal
+#define MAXCHECKS 100
+
+// only for debugging: counter for detected lockups
+static int lockups = 0;
+// only for debugging: counter for channel switches
+static int switches = 0;
+
+static int sp8870_set_frontend (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+	/*
+	    The firmware of the sp8870 sometimes locks up after setting frontend parameters.
+	    We try to detect this by checking the data valid signal.
+	    If it is not set after MAXCHECKS we try to recover the lockup by setting
+	    the frontend parameters again.
+	*/
+
+	int err = 0;
+	int valid = 0;
+	int trials = 0;
+	int check_count = 0;
+
+	dprintk("%s: frequency = %i\n", __FUNCTION__, p->frequency);
+
+	for (trials = 1; trials <= MAXTRIALS; trials++) {
+
+		if ((err = sp8870_set_frontend_parameters(fe, p)))
+			return err;
+
+		for (check_count = 0; check_count < MAXCHECKS; check_count++) {
+//			valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0);
+			valid = sp8870_read_data_valid_signal(state);
+			if (valid) {
+				dprintk("%s: delay = %i usec\n",
+					__FUNCTION__, check_count * 10);
+				break;
+			}
+			udelay(10);
+		}
+		if (valid)
+			break;
+	}
+
+	if (!valid) {
+		printk("%s: firmware crash!!!!!!\n", __FUNCTION__);
+		return -EIO;
+	}
+
+	if (debug) {
+		if (valid) {
+			if (trials > 1) {
+				printk("%s: firmware lockup!!!\n", __FUNCTION__);
+				printk("%s: recovered after %i trial(s))\n",  __FUNCTION__, trials - 1);
+				lockups++;
+			}
+		}
+		switches++;
+		printk("%s: switches = %i lockups = %i\n", __FUNCTION__, switches, lockups);
+	}
+
+	return 0;
+}
+
+static int sp8870_sleep(struct dvb_frontend* fe)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+	// tristate TS output and disable interface pins
+	return sp8870_writereg(state, 0xC18, 0x000);
+}
+
+static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+        fesettings->min_delay_ms = 350;
+        fesettings->step_size = 0;
+        fesettings->max_drift = 0;
+        return 0;
+}
+
+static void sp8870_release(struct dvb_frontend* fe)
+{
+	struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops sp8870_ops;
+
+struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+				   struct i2c_adapter* i2c)
+{
+	struct sp8870_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct sp8870_state*) kmalloc(sizeof(struct sp8870_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &sp8870_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+
+	/* check if the demod is there */
+	if (sp8870_readreg(state, 0x0200) < 0) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops sp8870_ops = {
+
+	.info = {
+		.name			= "Spase SP8870 DVB-T",
+		.type			= FE_OFDM,
+		.frequency_min		= 470000000,
+		.frequency_max		= 860000000,
+		.frequency_stepsize	= 166666,
+		.caps			= FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
+					  FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 |
+					  FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+					  FE_CAN_QPSK | FE_CAN_QAM_16 |
+					  FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+					  FE_CAN_HIERARCHY_AUTO |  FE_CAN_RECOVER
+	},
+
+	.release = sp8870_release,
+
+	.init = sp8870_init,
+	.sleep = sp8870_sleep,
+
+	.set_frontend = sp8870_set_frontend,
+	.get_tune_settings = sp8870_get_tune_settings,
+
+	.read_status = sp8870_read_status,
+	.read_ber = sp8870_read_ber,
+	.read_signal_strength = sp8870_read_signal_strength,
+	.read_ucblocks = sp8870_read_uncorrected_blocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver");
+MODULE_AUTHOR("Juergen Peitz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sp8870_attach);
diff -puN /dev/null drivers/media/dvb/frontends/sp8870.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/sp8870.h	2004-11-21 17:13:23.466051000 -0800
@@ -0,0 +1,45 @@
+/*
+    Driver for Spase SP8870 demodulator
+
+    Copyright (C) 1999 Juergen Peitz
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef SP8870_H
+#define SP8870_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp8870_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+	/* request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+					  struct i2c_adapter* i2c);
+
+#endif // SP8870_H
diff -puN drivers/media/dvb/frontends/sp887x.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/sp887x.c
--- 25/drivers/media/dvb/frontends/sp887x.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.392062248 -0800
+++ 25-akpm/drivers/media/dvb/frontends/sp887x.c	2004-11-21 17:13:23.470050392 -0800
@@ -1,5 +1,5 @@
 /*
-   Driver for the Microtune 7202D Frontend
+   Driver for the Spase sp887x demodulator
 */
 
 /*
@@ -16,74 +16,50 @@
 #include <linux/firmware.h>
 
 #include "dvb_frontend.h"
+#include "sp887x.h"
 
-#define FRONTEND_NAME "dvbfe_sp887x"
 
-#define dprintk(args...) \
-	do { \
-		if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \
-	} while (0)
+struct sp887x_state {
 
-static int debug;
+	struct i2c_adapter* i2c;
 
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+	struct dvb_frontend_ops ops;
 
-#if 0
-#define LOG(dir,addr,buf,len) 					\
-	do {							\
-		int i;						\
-		printk("%s (%02x):", dir, addr & 0xff);		\
-		for (i=0; i<len; i++)				\
-			printk(" 0x%02x,", buf[i] & 0xff);	\
-		printk("\n");					\
-	} while (0)
-#else
-#define LOG(dir,addr,buf,len)
-#endif
-
-static struct dvb_frontend_info sp887x_info = {
-	.name = "Microtune MT7202DTF",
-	.type = FE_OFDM,
-	.frequency_min =  50500000,
-	.frequency_max = 858000000,
-	.frequency_stepsize = 166666,
-	.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-		FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-		FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
-                FE_CAN_RECOVER
-};
+	const struct sp887x_config* config;
 
-struct sp887x_state {
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
+	struct dvb_frontend frontend;
+
+	/* demodulator private data */
+	u8 initialised:1;
 };
 
-static int i2c_writebytes (struct i2c_adapter *i2c, u8 addr, u8 *buf, u8 len)
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "sp887x: " args); \
+	} while (0)
+
+static int i2c_writebytes (struct sp887x_state* state, u8 *buf, u8 len)
 {
-	struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = len };
 	int err;
 
-	LOG("i2c_writebytes", msg.addr, msg.buf, msg.len);
-
-	if ((err = i2c_transfer (i2c, &msg, 1)) != 1) {
+	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
 		printk ("%s: i2c write error (addr %02x, err == %i)\n",
-			__FUNCTION__, addr, err);
+			__FUNCTION__, state->config->demod_address, err);
 		return -EREMOTEIO;
 	}
 
 	return 0;
 }
 
-static int sp887x_writereg (struct i2c_adapter *i2c, u16 reg, u16 data)
+static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data)
 {
 	u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff };
-	struct i2c_msg msg = { .addr = 0x70, .flags = 0, .buf = b0, .len = 4 };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 };
 	int ret;
 
-	LOG("sp887x_writereg", msg.addr, msg.buf, msg.len);
-
-	if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) {
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) {
 		/**
 		 *  in case of soft reset we ignore ACK errors...
 		 */
@@ -100,61 +76,60 @@ static int sp887x_writereg (struct i2c_a
 	return 0;
 }
 
-static u16 sp887x_readreg (struct i2c_adapter *i2c, u16 reg)
+static int sp887x_readreg (struct sp887x_state* state, u16 reg)
 {
 	u8 b0 [] = { reg >> 8 , reg & 0xff };
 	u8 b1 [2];
 	int ret;
-	struct i2c_msg msg[] = {{ .addr = 0x70, .flags = 0, .buf = b0, .len = 2 },
-		         { .addr = 0x70, .flags = I2C_M_RD, .buf = b1, .len = 2 }};
-
-	LOG("sp887x_readreg (w)", msg[0].addr, msg[0].buf, msg[0].len);
-	LOG("sp887x_readreg (r)", msg[1].addr, msg[1].buf, msg[1].len);
+	struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+		         { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }};
 
-	if ((ret = i2c_transfer(i2c, msg, 2)) != 2)
+	if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) {
 		printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+		return -1;
+	}
 
 	return (((b1[0] << 8) | b1[1]) & 0xfff);
 }
 
-static void sp887x_microcontroller_stop (struct i2c_adapter *fe)
+static void sp887x_microcontroller_stop (struct sp887x_state* state)
 {
 	dprintk("%s\n", __FUNCTION__);
-	sp887x_writereg(fe, 0xf08, 0x000);
-	sp887x_writereg(fe, 0xf09, 0x000);
+	sp887x_writereg(state, 0xf08, 0x000);
+	sp887x_writereg(state, 0xf09, 0x000);
 
 	/* microcontroller STOP */
-	sp887x_writereg(fe, 0xf00, 0x000);
+	sp887x_writereg(state, 0xf00, 0x000);
 }
 
-static void sp887x_microcontroller_start (struct i2c_adapter *fe)
+static void sp887x_microcontroller_start (struct sp887x_state* state)
 {
 	dprintk("%s\n", __FUNCTION__);
-	sp887x_writereg(fe, 0xf08, 0x000);
-	sp887x_writereg(fe, 0xf09, 0x000);
+	sp887x_writereg(state, 0xf08, 0x000);
+	sp887x_writereg(state, 0xf09, 0x000);
 
 	/* microcontroller START */
-	sp887x_writereg(fe, 0xf00, 0x001);
+	sp887x_writereg(state, 0xf00, 0x001);
 }
 
-static void sp887x_setup_agc (struct i2c_adapter *fe)
+static void sp887x_setup_agc (struct sp887x_state* state)
 {
 	/* setup AGC parameters */
 	dprintk("%s\n", __FUNCTION__);
-	sp887x_writereg(fe, 0x33c, 0x054);
-	sp887x_writereg(fe, 0x33b, 0x04c);
-	sp887x_writereg(fe, 0x328, 0x000);
-	sp887x_writereg(fe, 0x327, 0x005);
-	sp887x_writereg(fe, 0x326, 0x001);
-	sp887x_writereg(fe, 0x325, 0x001);
-	sp887x_writereg(fe, 0x324, 0x001);
-	sp887x_writereg(fe, 0x318, 0x050);
-	sp887x_writereg(fe, 0x317, 0x3fe);
-	sp887x_writereg(fe, 0x316, 0x001);
-	sp887x_writereg(fe, 0x313, 0x005);
-	sp887x_writereg(fe, 0x312, 0x002);
-	sp887x_writereg(fe, 0x306, 0x000);
-	sp887x_writereg(fe, 0x303, 0x000);
+	sp887x_writereg(state, 0x33c, 0x054);
+	sp887x_writereg(state, 0x33b, 0x04c);
+	sp887x_writereg(state, 0x328, 0x000);
+	sp887x_writereg(state, 0x327, 0x005);
+	sp887x_writereg(state, 0x326, 0x001);
+	sp887x_writereg(state, 0x325, 0x001);
+	sp887x_writereg(state, 0x324, 0x001);
+	sp887x_writereg(state, 0x318, 0x050);
+	sp887x_writereg(state, 0x317, 0x3fe);
+	sp887x_writereg(state, 0x316, 0x001);
+	sp887x_writereg(state, 0x313, 0x005);
+	sp887x_writereg(state, 0x312, 0x002);
+	sp887x_writereg(state, 0x306, 0x000);
+	sp887x_writereg(state, 0x303, 0x000);
 }
 
 #define BLOCKSIZE 30
@@ -162,8 +137,9 @@ static void sp887x_setup_agc (struct i2c
 /**
  *  load firmware and setup MPEG interface...
  */
-static int sp887x_initial_setup (struct i2c_adapter *fe, const struct firmware *fw)
+static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw)
 {
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
 	u8 buf [BLOCKSIZE+2];
 	int i;
 	int fw_size = fw->size;
@@ -178,18 +154,18 @@ static int sp887x_initial_setup (struct 
 	mem = fw->data + 10;
 
 	/* soft reset */
-	sp887x_writereg(fe, 0xf1a, 0x000);
+	sp887x_writereg(state, 0xf1a, 0x000);
 
-	sp887x_microcontroller_stop (fe);
+	sp887x_microcontroller_stop (state);
 
 	printk ("%s: firmware upload... ", __FUNCTION__);
 
 	/* setup write pointer to -1 (end of memory) */
 	/* bit 0x8000 in address is set to enable 13bit mode */
-	sp887x_writereg(fe, 0x8f08, 0x1fff);
+	sp887x_writereg(state, 0x8f08, 0x1fff);
 
 	/* dummy write (wrap around to start of memory) */
-	sp887x_writereg(fe, 0x8f0a, 0x0000);
+	sp887x_writereg(state, 0x8f0a, 0x0000);
 
 	for (i = 0; i < FW_SIZE; i += BLOCKSIZE) {
 		int c = BLOCKSIZE;
@@ -206,7 +182,7 @@ static int sp887x_initial_setup (struct 
 
 		memcpy(&buf[2], mem + i, c);
 
-		if ((err = i2c_writebytes (fe, 0x70, buf, c+2)) < 0) {
+		if ((err = i2c_writebytes (state, buf, c+2)) < 0) {
 			printk ("failed.\n");
 			printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
 			return err;
@@ -214,61 +190,37 @@ static int sp887x_initial_setup (struct 
 	}
 
 	/* don't write RS bytes between packets */
-	sp887x_writereg(fe, 0xc13, 0x001);
+	sp887x_writereg(state, 0xc13, 0x001);
 
 	/* suppress clock if (!data_valid) */
-	sp887x_writereg(fe, 0xc14, 0x000);
+	sp887x_writereg(state, 0xc14, 0x000);
 
 	/* setup MPEG interface... */
-	sp887x_writereg(fe, 0xc1a, 0x872);
-	sp887x_writereg(fe, 0xc1b, 0x001);
-	sp887x_writereg(fe, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */
-	sp887x_writereg(fe, 0xc1a, 0x871);
+	sp887x_writereg(state, 0xc1a, 0x872);
+	sp887x_writereg(state, 0xc1b, 0x001);
+	sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */
+	sp887x_writereg(state, 0xc1a, 0x871);
 
 	/* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */
-	sp887x_writereg(fe, 0x301, 0x002);
+	sp887x_writereg(state, 0x301, 0x002);
 
-	sp887x_setup_agc(fe);
+	sp887x_setup_agc(state);
 
 	/* bit 0x010: enable data valid signal */
-	sp887x_writereg(fe, 0xd00, 0x010);
-	sp887x_writereg(fe, 0x0d1, 0x000);
+	sp887x_writereg(state, 0xd00, 0x010);
+	sp887x_writereg(state, 0x0d1, 0x000);
+
+	/* setup the PLL */
+	if (state->config->pll_init) {
+		sp887x_writereg(state, 0x206, 0x001);
+		state->config->pll_init(fe);
+		sp887x_writereg(state, 0x206, 0x000);
+	}
 
 	printk ("done.\n");
 	return 0;
 };
 
-/**
- *  returns the actual tuned center frequency which can be used
- *  to initialise the AFC registers
- */
-static int tsa5060_setup_pll (struct i2c_adapter *fe, int freq)
-{
-	u8 cfg, cpump, band_select;
-	u8 buf [4];
-	u32 div;
-
-	div = (36000000 + freq + 83333) / 166666;
-	cfg = 0x88;
-
-	cpump = freq < 175000000 ? 2 : freq < 390000000 ? 1 :
-		freq < 470000000 ? 2 : freq < 750000000 ? 2 : 3;
-
-	band_select = freq < 175000000 ? 0x0e : freq < 470000000 ? 0x05 : 0x03;
-
-	buf [0] = (div >> 8) & 0x7f;
-	buf [1] = div & 0xff;
-	buf [2] = ((div >> 10) & 0x60) | cfg;
-	buf [3] = cpump | band_select;
-
-	/* open i2c gate for PLL message transmission... */
-	sp887x_writereg(fe, 0x206, 0x001);
-	i2c_writebytes(fe, 0x60, buf, 4);
-	sp887x_writereg(fe, 0x206, 0x000);
-
-	return (div * 166666 - 36000000);
-}
-
 static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
 {
 	int known_parameters = 1;
@@ -362,7 +314,7 @@ static void divide (int n, int d, int *q
 	}
 }
 
-static void sp887x_correct_offsets (struct i2c_adapter *fe,
+static void sp887x_correct_offsets (struct sp887x_state* state,
 			     struct dvb_frontend_parameters *p,
 			     int actual_freq)
 {
@@ -385,17 +337,31 @@ static void sp887x_correct_offsets (stru
 		frequency_shift = -frequency_shift;
 
 	/* sample rate correction */
-	sp887x_writereg(fe, 0x319, srate_correction[bw_index] >> 12);
-	sp887x_writereg(fe, 0x31a, srate_correction[bw_index] & 0xfff);
+	sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12);
+	sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff);
 
 	/* carrier offset correction */
-	sp887x_writereg(fe, 0x309, frequency_shift >> 12);
-	sp887x_writereg(fe, 0x30a, frequency_shift & 0xfff);
+	sp887x_writereg(state, 0x309, frequency_shift >> 12);
+	sp887x_writereg(state, 0x30a, frequency_shift & 0xfff);
 }
 
-static int sp887x_setup_frontend_parameters (struct i2c_adapter *fe,
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe,
 				      struct dvb_frontend_parameters *p)
 {
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
 	int actual_freq, err;
 	u16 val, reg0xc05;
 
@@ -407,14 +373,17 @@ static int sp887x_setup_frontend_paramet
 	if ((err = configure_reg0xc05(p, &reg0xc05)))
 		return err;
 
-	sp887x_microcontroller_stop(fe);
+	sp887x_microcontroller_stop(state);
 
-	actual_freq = tsa5060_setup_pll(fe, p->frequency);
+	/* setup the PLL */
+	sp887x_writereg(state, 0x206, 0x001);
+	actual_freq = state->config->pll_set(fe, p);
+	sp887x_writereg(state, 0x206, 0x000);
 
-	/* read status reg in order to clear pending irqs */
-	sp887x_readreg(fe, 0x200);
+	/* read status reg in order to clear <pending irqs */
+	sp887x_readreg(state, 0x200);
 
-	sp887x_correct_offsets(fe, p, actual_freq);
+	sp887x_correct_offsets(state, p, actual_freq);
 
 	/* filter for 6/7/8 Mhz channel */
 	if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
@@ -424,15 +393,15 @@ static int sp887x_setup_frontend_paramet
 	else
 		val = 0;
 
-	sp887x_writereg(fe, 0x311, val);
+	sp887x_writereg(state, 0x311, val);
 
 	/* scan order: 2k first = 0, 8k first = 1 */
 	if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
-		sp887x_writereg(fe, 0x338, 0x000);
+		sp887x_writereg(state, 0x338, 0x000);
 	else
-		sp887x_writereg(fe, 0x338, 0x001);
+		sp887x_writereg(state, 0x338, 0x001);
 
-	sp887x_writereg(fe, 0xc05, reg0xc05);
+	sp887x_writereg(state, 0xc05, reg0xc05);
 
 	if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
 		val = 2 << 3;
@@ -444,29 +413,19 @@ static int sp887x_setup_frontend_paramet
 	/* enable OFDM and SAW bits as lock indicators in sync register 0xf17,
 	 * optimize algorithm for given bandwidth...
 	 */
-	sp887x_writereg(fe, 0xf14, 0x160 | val);
-	sp887x_writereg(fe, 0xf15, 0x000);
+	sp887x_writereg(state, 0xf14, 0x160 | val);
+	sp887x_writereg(state, 0xf15, 0x000);
 
-	sp887x_microcontroller_start(fe);
+	sp887x_microcontroller_start(state);
 	return 0;
 }
 
-static int sp887x_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg)
-{
-	struct sp887x_state *state = (struct sp887x_state *) f->data;
-	struct i2c_adapter *fe = state->i2c;
-
-        switch (cmd) {
-        case FE_GET_INFO:
-		memcpy (arg, &sp887x_info, sizeof(struct dvb_frontend_info));
-		break;
-
-        case FE_READ_STATUS:
+static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status)
 	{
-		u16 snr12 = sp887x_readreg(fe, 0xf16);
-		u16 sync0x200 = sp887x_readreg(fe, 0x200);
-		u16 sync0xf17 = sp887x_readreg(fe, 0xf17);
-		fe_status_t *status = arg;
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+	u16 snr12 = sp887x_readreg(state, 0xf16);
+	u16 sync0x200 = sp887x_readreg(state, 0x200);
+	u16 sync0xf17 = sp887x_readreg(state, 0xf17);
 
 		*status = 0;
 
@@ -492,212 +451,172 @@ static int sp887x_ioctl(struct dvb_front
 			       steps);
 		}
 
-		break;
-
+	return 0;
 	}
 
-        case FE_READ_BER:
+static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber)
 	{
-		u32* ber = arg;
-		*ber = (sp887x_readreg(fe, 0xc08) & 0x3f) |
-		       (sp887x_readreg(fe, 0xc07) << 6);
-		sp887x_writereg(fe, 0xc08, 0x000);
-		sp887x_writereg(fe, 0xc07, 0x000);
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	*ber = (sp887x_readreg(state, 0xc08) & 0x3f) |
+	       (sp887x_readreg(state, 0xc07) << 6);
+	sp887x_writereg(state, 0xc08, 0x000);
+	sp887x_writereg(state, 0xc07, 0x000);
 		if (*ber >= 0x3fff0)
 			*ber = ~0;
-		break;
 
+	return 0;
 	}
 
-        case FE_READ_SIGNAL_STRENGTH:		// FIXME: correct registers ?
+static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 	{
-		u16 snr12 = sp887x_readreg(fe, 0xf16);
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	u16 snr12 = sp887x_readreg(state, 0xf16);
 		u32 signal = 3 * (snr12 << 4);
-		*((u16*) arg) = (signal < 0xffff) ? signal : 0xffff;
-		break;
-	}
+	*strength = (signal < 0xffff) ? signal : 0xffff;
 
-        case FE_READ_SNR:
-	{
-		u16 snr12 = sp887x_readreg(fe, 0xf16);
-		*(u16*) arg = (snr12 << 4) | (snr12 >> 8);
-		break;
+	return 0;
 	}
 
-	case FE_READ_UNCORRECTED_BLOCKS:
+static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr)
 	{
-		u32 *ublocks = (u32 *) arg;
-		*ublocks = sp887x_readreg(fe, 0xc0c);
-		if (*ublocks == 0xfff)
-			*ublocks = ~0;
-		break;
-	}
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
 
-        case FE_SET_FRONTEND:
-		return sp887x_setup_frontend_parameters(fe, arg);
-
-	case FE_GET_FRONTEND:  // FIXME: read known values back from Hardware...
-		break;
+	u16 snr12 = sp887x_readreg(state, 0xf16);
+	*snr = (snr12 << 4) | (snr12 >> 8);
 
-        case FE_SLEEP:
-		/* tristate TS output and disable interface pins */
-		sp887x_writereg(fe, 0xc18, 0x000);
-		break;
-
-        case FE_INIT:
-		/* enable TS output and interface pins */
-		sp887x_writereg(fe, 0xc18, 0x00d);
-		break;
-
-	case FE_GET_TUNE_SETTINGS:
-	{
-	        struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
-	        fesettings->min_delay_ms = 350;
-	        fesettings->step_size = 166666*2;
-	        fesettings->max_drift = (166666*2)+1;
 	        return 0;
 	}
 
-	default:
-		return -EOPNOTSUPP;
-        };
+static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+	*ucblocks = sp887x_readreg(state, 0xc0c);
+	if (*ucblocks == 0xfff)
+		*ucblocks = ~0;
 
         return 0;
 }
 
-static struct i2c_client client_template;
-
-static int attach_adapter(struct i2c_adapter *adapter)
+static int sp887x_sleep(struct dvb_frontend* fe)
 {
-	struct i2c_client *client;
-	struct sp887x_state *state;
-	const struct firmware *fw;
-	int ret;
-
-	struct i2c_msg msg = {.addr = 0x70, .flags = 0, .buf = NULL, .len = 0 };
-
-	dprintk ("%s\n", __FUNCTION__);
-
-	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
-		return -ENOMEM;
-	}
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
 
-	if (NULL == (state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL))) {
-		kfree(client);
-		return -ENOMEM;
-	}
-	state->i2c = adapter;
+	/* tristate TS output and disable interface pins */
+	sp887x_writereg(state, 0xc18, 0x000);
 
-	if (i2c_transfer (adapter, &msg, 1) != 1) {
-		kfree(state);
-		kfree(client);
-                return -ENODEV;
+	return 0;
 	}
 
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	i2c_set_clientdata(client, (void*)state);
-
-	ret = i2c_attach_client(client);
-	if (ret) {
-		kfree(client);
-		kfree(state);
-		return ret;
-	}
+static int sp887x_init(struct dvb_frontend* fe)
+{
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+        const struct firmware *fw = NULL;
+	int ret;
 
+	if (!state->initialised) {
 	/* request the firmware, this will block until someone uploads it */
 	printk("sp887x: waiting for firmware upload...\n");
-	ret = request_firmware(&fw, SP887X_DEFAULT_FIRMWARE, &client->dev);
+		ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE);
 	if (ret) {
 		printk("sp887x: no firmware upload (timeout or file not found?)\n");
-		goto out;
+			return ret;
 	}
 
-	ret = sp887x_initial_setup(adapter, fw);
+		ret = sp887x_initial_setup(fe, fw);
 	if (ret) {
 		printk("sp887x: writing firmware to device failed\n");
-		goto out;
+			release_firmware(fw);
+			return ret;
 	}
-
-	ret = dvb_register_frontend(sp887x_ioctl, state->dvb, state,
-					&sp887x_info, THIS_MODULE);
-	if (ret) {
-		printk("sp887x: registering frontend to dvb-core failed.\n");
-		goto out;
+		state->initialised = 1;
 	}
 
+	/* enable TS output and interface pins */
+	sp887x_writereg(state, 0xc18, 0x00d);
+
 	return 0;
-out:
-	release_firmware(fw);
-	i2c_detach_client(client);
-	kfree(client);
-	kfree(state);
-	return ret;
 }
 
-static int detach_client(struct i2c_client *client)
+static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
 {
-	struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client);
-
-	dprintk ("%s\n", __FUNCTION__);
-
-	dvb_unregister_frontend (sp887x_ioctl, state->dvb);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
-	kfree(state);
-	return 0;
+        fesettings->min_delay_ms = 350;
+        fesettings->step_size = 166666*2;
+        fesettings->max_drift = (166666*2)+1;
+        return 0;
 }
 
-static int command (struct i2c_client *client, unsigned int cmd, void *arg)
+static void sp887x_release(struct dvb_frontend* fe)
 {
-	struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client);
-
-	dprintk ("%s\n", __FUNCTION__);
-
-	switch (cmd) {
-	case FE_REGISTER:
-		state->dvb = (struct dvb_adapter*)arg;
-		break;
-	case FE_UNREGISTER:
-		state->dvb = NULL;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-	return 0;
+	struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+	kfree(state);
 }
 
-static struct i2c_driver driver = {
-	.owner 		= THIS_MODULE,
-	.name 		= FRONTEND_NAME,
-	.id 		= I2C_DRIVERID_DVBFE_SP887X,
-	.flags 		= I2C_DF_NOTIFY,
-	.attach_adapter = attach_adapter,
-	.detach_client 	= detach_client,
-	.command 	= command,
-};
-
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags 		= I2C_CLIENT_ALLOW_USE,
-	.driver  	= &driver,
-};
+static struct dvb_frontend_ops sp887x_ops;
 
-static int __init init_sp887x(void)
+struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+				   struct i2c_adapter* i2c)
 {
-	return i2c_add_driver(&driver);
-}
+	struct sp887x_state* state = NULL;
 
-static void __exit exit_sp887x(void)
-{
-	if (i2c_del_driver(&driver))
-		printk("sp887x: driver deregistration failed\n");
-}
+	/* allocate memory for the internal state */
+	state = (struct sp887x_state*) kmalloc(sizeof(struct sp887x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &sp887x_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+
+	/* check if the demod is there */
+	if (sp887x_readreg(state, 0x0200) < 0) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops sp887x_ops = {
+
+	.info = {
+		.name = "Spase SP887x DVB-T",
+		.type = FE_OFDM,
+		.frequency_min =  50500000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166666,
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+	                FE_CAN_RECOVER
+	},
+
+	.release = sp887x_release,
+
+	.init = sp887x_init,
+	.sleep = sp887x_sleep,
+
+	.set_frontend = sp887x_setup_frontend_parameters,
+	.get_tune_settings = sp887x_get_tune_settings,
+
+	.read_status = sp887x_read_status,
+	.read_ber = sp887x_read_ber,
+	.read_signal_strength = sp887x_read_signal_strength,
+	.read_snr = sp887x_read_snr,
+	.read_ucblocks = sp887x_read_ucblocks,
+};
 
-module_init(init_sp887x);
-module_exit(exit_sp887x);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-MODULE_DESCRIPTION("sp887x DVB-T demodulator driver");
+MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(sp887x_attach);
diff -puN /dev/null drivers/media/dvb/frontends/sp887x.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/sp887x.h	2004-11-21 17:13:23.471050240 -0800
@@ -0,0 +1,29 @@
+/*
+   Driver for the Spase sp887x demodulator
+*/
+
+#ifndef SP887X_H
+#define SP887X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp887x_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+
+	/* this should return the actual frequency tuned to */
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+	/* request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+					  struct i2c_adapter* i2c);
+
+#endif // SP887X_H
diff -puN /dev/null drivers/media/dvb/frontends/stv0297.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/stv0297.c	2004-11-21 17:13:23.474049784 -0800
@@ -0,0 +1,774 @@
+/*
+    Driver for STV0297 demodulator
+
+    Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+    Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "stv0297.h"
+
+struct stv0297_state {
+
+        struct i2c_adapter* i2c;
+
+        struct dvb_frontend_ops ops;
+
+        const struct stv0297_config* config;
+
+        struct dvb_frontend frontend;
+
+        int freq_off;
+
+	unsigned long base_freq;
+
+	u8 pwm;
+};
+
+#if 1
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+#define STV0297_CLOCK   28900
+
+static u8 init_tab [] = {
+  0x00, 0x09,
+  0x01, 0x69,
+  0x03, 0x00,
+  0x04, 0x00,
+  0x07, 0x00,
+  0x08, 0x00,
+  0x20, 0x00,
+  0x21, 0x40,
+  0x22, 0x00,
+  0x23, 0x00,
+  0x24, 0x40,
+  0x25, 0x88,
+  0x30, 0xff,
+  0x31, 0x00,
+  0x32, 0xff,
+  0x33, 0x00,
+  0x34, 0x50,
+  0x35, 0x7f,
+  0x36, 0x00,
+  0x37, 0x20,
+  0x38, 0x00,
+  0x40, 0x1c,
+  0x41, 0xff,
+  0x42, 0x29,
+  0x43, 0x00,// check
+  0x44, 0xff,
+  0x45, 0x00,
+  0x46, 0x00,
+  0x49, 0x04,
+  0x4a, 0xff,
+  0x4b, 0x7f,
+  0x52, 0x30,
+  0x55, 0xae,
+  0x56, 0x47,
+  0x57, 0xe1,
+  0x58, 0x3a,
+  0x5a, 0x1e,
+  0x5b, 0x34,
+  0x60, 0x00,
+  0x63, 0x00,
+  0x64, 0x00,
+  0x65, 0x00,
+  0x66, 0x00,
+  0x67, 0x00,
+  0x68, 0x00,
+  0x69, 0x00,
+  0x6a, 0x02,
+  0x6b, 0x00,
+  0x70, 0xff,
+  0x71, 0x00,
+  0x72, 0x00,
+  0x73, 0x00,
+  0x74, 0x0c,
+  0x80, 0x00,
+  0x81, 0x00,
+  0x82, 0x00,
+  0x83, 0x00,
+  0x84, 0x04,
+  0x85, 0x80,
+  0x86, 0x24,
+  0x87, 0x78,
+  0x88, 0x00,
+  0x89, 0x00,
+  0x90, 0x01,
+  0x91, 0x01,
+  0xa0, 0x00,
+  0xa1, 0x00,
+  0xa2, 0x00,
+  0xb0, 0x91,
+  0xb1, 0x0b,
+  0xc0, 0x53,
+  0xc1, 0x70,
+  0xc2, 0x12,
+  0xd0, 0x00,
+  0xd1, 0x00,
+  0xd2, 0x00,
+  0xd3, 0x00,
+  0xd4, 0x00,
+  0xd5, 0x00,
+  0xde, 0x00,
+  0xdf, 0x00,
+  0x61, 0x49,
+  0x62, 0x0b,
+  0x53, 0x08,
+  0x59, 0x08,
+};
+
+
+static int stv0297_writereg (struct stv0297_state* state, u8 reg, u8 data)
+{
+        int ret;
+        u8 buf [] = { reg, data };
+        struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+        ret = i2c_transfer (state->i2c, &msg, 1);
+
+        if (ret != 1)
+                dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+                  "ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+        return (ret != 1) ? -1 : 0;
+}
+
+static int stv0297_readreg (struct stv0297_state* state, u8 reg)
+{
+        int ret;
+        u8 b0[] = { reg };
+        u8 b1[] = { 0 };
+        struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+                                  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+        // this device needs a STOP between the register and data
+        if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) {
+                dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+                        __FUNCTION__, reg, ret);
+                return -1;
+        }
+        if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) {
+                dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+                        __FUNCTION__, reg, ret);
+                return -1;
+        }
+
+        return b1[0];
+}
+
+static int stv0297_writereg_mask (struct stv0297_state* state, u8 reg, u8 mask, u8 data)
+{
+        int val;
+
+        val = stv0297_readreg(state, reg);
+        val &= ~mask;
+        val |= (data & mask);
+        stv0297_writereg(state, reg, val);
+
+        return 0;
+}
+
+static int stv0297_readregs (struct stv0297_state* state, u8 reg1, u8 *b, u8 len)
+{
+        int ret;
+        struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+                                  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+        // this device needs a STOP between the register and data
+        if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) {
+                dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+                        __FUNCTION__, reg1, ret);
+                return -1;
+        }
+        if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) {
+                dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+                        __FUNCTION__, reg1, ret);
+                return -1;
+        }
+
+        return 0;
+}
+
+static int stv0297_set_symbolrate (struct stv0297_state* state, u32 srate)
+{
+        u64 tmp;
+
+        tmp = srate;
+        tmp <<= 32;
+        do_div(tmp, STV0297_CLOCK);
+
+        stv0297_writereg (state, 0x55,(unsigned char)(tmp & 0xFF));
+        stv0297_writereg (state, 0x56,(unsigned char)(tmp>> 8));
+        stv0297_writereg (state, 0x57,(unsigned char)(tmp>>16));
+        stv0297_writereg (state, 0x58,(unsigned char)(tmp>>24));
+
+        return 0;
+}
+
+static u32 stv0297_get_symbolrate (struct stv0297_state* state)
+{
+        u64 tmp;
+
+        tmp = stv0297_readreg(state, 0x55);
+        tmp |= (stv0297_readreg(state, 0x56) << 8);
+        tmp |= (stv0297_readreg(state, 0x57) << 16);
+        tmp |= (stv0297_readreg(state, 0x57) << 24);
+
+        tmp *= STV0297_CLOCK;
+        tmp >>= 32;
+        return tmp;
+}
+
+static void stv0297_set_sweeprate(struct stv0297_state* state, short fshift)
+{
+        s64 tmp;
+        u32 symrate;
+
+        symrate = stv0297_get_symbolrate(state);
+
+        // cannot use shifts - it is signed
+        tmp = fshift * (1<<28);
+        do_div(tmp, symrate);
+
+        // adjust
+        if (tmp >= 0) {
+                tmp += 500000;
+        } else {
+                tmp -= 500000;
+        }
+        do_div(tmp, 1000000);
+
+        stv0297_writereg(state, 0x60, tmp & 0xFF);
+        stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0);
+
+        return;
+}
+
+static void stv0297_set_carrieroffset(struct stv0297_state* state, long offset)
+{
+        long long_tmp;
+
+        // symrate is hardcoded to 10000 here - don't ask me why
+        long_tmp = offset * 26844L ; /* (2**28)/10000 */
+        if(long_tmp < 0) long_tmp += 0x10000000 ;
+        long_tmp &= 0x0FFFFFFF ;
+
+        stv0297_writereg (state,0x66,(unsigned char)(long_tmp & 0xFF));    // iphase0
+        stv0297_writereg (state,0x67,(unsigned char)(long_tmp>>8));        // iphase1
+        stv0297_writereg (state,0x68,(unsigned char)(long_tmp>>16));       // iphase2
+        stv0297_writereg_mask(state, 0x69, 0x0F, (long_tmp >> 24) & 0x0f);
+
+        return;
+}
+
+static long stv0297_get_carrieroffset(struct stv0297_state* state)
+{
+        s32 raw;
+        s64 tmp;
+        u32 symbol_rate;
+
+        stv0297_writereg(state,0x6B, 0x00);
+
+        symbol_rate = stv0297_get_symbolrate(state);
+
+        raw =   stv0297_readreg(state,0x66);
+        raw |= (stv0297_readreg(state,0x67) << 8);
+        raw |= (stv0297_readreg(state,0x68) << 16);
+        raw |= (stv0297_readreg(state,0x69) & 0x0F) << 24;
+
+        // cannot just use a shift here 'cos it is signed
+        tmp = raw;
+        tmp *= symbol_rate;
+        do_div(tmp, 1<<28);
+
+        return (s32) tmp;
+}
+
+static void stv0297_set_initialdemodfreq(struct stv0297_state* state, long freq)
+{
+        u64 tmp;
+
+        if (freq > 10000) freq -= STV0297_CLOCK;
+        if (freq < 0) freq = 0;
+
+        tmp = freq << 16;
+        do_div(tmp, STV0297_CLOCK);
+        if (tmp > 0xffff) tmp = 0xffff;
+
+        stv0297_writereg_mask(state, 0x25, 0x80, 0x80);
+        stv0297_writereg(state, 0x21, tmp >> 8);
+        stv0297_writereg(state, 0x20, tmp);
+}
+
+static int stv0297_set_qam(struct stv0297_state* state, fe_modulation_t modulation)
+{
+        int val = 0;
+
+        switch(modulation) {
+        case QAM_16:
+                val = 0;
+                break;
+
+        case QAM_32:
+                val = 1;
+                break;
+
+        case QAM_64:
+                val = 4;
+                break;
+
+        case QAM_128:
+                val = 2;
+                break;
+
+        case QAM_256:
+                val = 3;
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        stv0297_writereg_mask(state, 0x00, 0x70, val << 4);
+
+        return 0;
+}
+
+static int stv0297_set_inversion(struct stv0297_state* state, fe_spectral_inversion_t inversion)
+{
+        int val = 0;
+
+        switch(inversion) {
+        case INVERSION_OFF:
+                val = 0;
+                break;
+
+        case INVERSION_ON:
+                val = 1;
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        stv0297_writereg_mask(state, 0x83, 0x08, val << 3);
+
+        return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+int stv0297_enable_plli2c(struct dvb_frontend* fe)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+
+        stv0297_writereg(state, 0x87, 0x78);
+        stv0297_writereg(state, 0x86, 0xc8);
+
+        return 0;
+}
+
+static int stv0297_init (struct dvb_frontend* fe)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        int i;
+
+        stv0297_writereg_mask(state, 0x80, 1, 1);
+        stv0297_writereg_mask(state, 0x80, 1, 0);
+        stv0297_writereg_mask(state, 0x81, 1, 1);
+        stv0297_writereg_mask(state, 0x81, 1, 0);
+
+        for (i=0; i<sizeof(init_tab); i+=2) {
+                stv0297_writereg (state, init_tab[i], init_tab[i+1]);
+        }
+
+        stv0297_set_symbolrate(state, 6900);
+        stv0297_writereg_mask(state, 0x88, 0x10, 0x10);
+        stv0297_writereg_mask(state, 0xA0, 0x80, 0x00);
+        stv0297_writereg_mask(state, 0xA0, 0x10, 0x00);
+        stv0297_writereg_mask(state, 0xA0, 0x08, 0x00);
+        stv0297_writereg_mask(state, 0xA0, 0x07, 0x04);
+        stv0297_writereg(state, 0x4a, 0x00);
+        stv0297_writereg(state, 0x4b, state->pwm);
+        msleep(200);
+
+        if (state->config->pll_init) state->config->pll_init(fe);
+
+        return 0;
+}
+
+static int stv0297_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+
+        u8 sync = stv0297_readreg (state, 0xDF);
+
+        *status = 0;
+        if (sync & 0x80)
+                *status |= FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK;
+        return 0;
+}
+
+static int stv0297_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        u8 BER[3];
+
+        stv0297_writereg (state, 0xA0, 0x80);	// Start Counting bit errors for 4096 Bytes
+        mdelay(25);				// Hopefully got 4096 Bytes
+        stv0297_readregs (state, 0xA0, BER, 3);
+        mdelay(25);
+        *ber = (BER[2] << 8 | BER[1]) / ( 8 * 4096);
+
+        return 0;
+}
+
+
+static int stv0297_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        u8 STRENGTH[2];
+
+        stv0297_readregs (state, 0x41, STRENGTH, 2);
+        *strength = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0];
+
+        return 0;
+}
+
+static int stv0297_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        u8 SNR[2];
+
+        stv0297_readregs (state, 0x07, SNR, 2);
+        *snr = SNR[1] << 8 | SNR[0];
+
+        return 0;
+}
+
+static int stv0297_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+
+        *ucblocks = (stv0297_readreg (state, 0xD5) << 8)
+                   | stv0297_readreg (state, 0xD4);
+
+        return 0;
+}
+
+static int stv0297_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        int u_threshold;
+        int initial_u;
+        int blind_u;
+        int delay;
+        int locked;
+        int sweeprate;
+        int carrieroffset;
+        unsigned long starttime;
+        unsigned long timeout;
+
+        switch(p->u.qam.modulation) {
+        case QAM_16:
+        case QAM_32:
+        case QAM_64:
+          delay = 100;
+          sweeprate = 1500;
+          break;
+
+        case QAM_128:
+          delay = 150;
+          sweeprate = 1000;
+          break;
+
+        case QAM_256:
+          delay = 200;
+          sweeprate = 500;
+          break;
+
+        default:
+          return -EINVAL;
+        }
+
+        // determine inversion dependant parameters
+        carrieroffset = -330;
+        switch(p->inversion) {
+        case INVERSION_OFF:
+          break;
+
+        case INVERSION_ON:
+          sweeprate = -sweeprate;
+          carrieroffset = -carrieroffset;
+          break;
+
+        default:
+          return -EINVAL;
+        }
+
+        state->config->pll_set(fe, p);
+
+        // reset everything
+        stv0297_writereg_mask(state, 0x82, 0x4, 0x4);
+        stv0297_set_initialdemodfreq(state, state->freq_off + 7250);
+        stv0297_writereg_mask(state, 0x43, 0x10, 0x00);
+        stv0297_writereg(state, 0x41, 0x00);
+        stv0297_writereg_mask(state, 0x42, 0x03, 0x01);
+        stv0297_writereg_mask(state, 0x36, 0x60, 0x00);
+        stv0297_writereg_mask(state, 0x36, 0x18, 0x00);
+        stv0297_writereg_mask(state, 0x71, 0x80, 0x80);
+        stv0297_writereg(state, 0x72, 0x00);
+        stv0297_writereg(state, 0x73, 0x00);
+        stv0297_writereg_mask(state, 0x74, 0x0F, 0x00);
+        stv0297_writereg_mask(state, 0x43, 0x08, 0x00);
+        stv0297_writereg_mask(state, 0x71, 0x80, 0x00);
+        stv0297_writereg_mask(state, 0x5a, 0x20, 0x20);
+        stv0297_writereg_mask(state, 0x5b, 0x02, 0x02);
+        stv0297_writereg_mask(state, 0x5b, 0x02, 0x00);
+        stv0297_writereg_mask(state, 0x5b, 0x01, 0x00);
+        stv0297_writereg_mask(state, 0x5a, 0x40, 0x40);
+        stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+        stv0297_writereg_mask(state, 0x81, 0x01, 0x01);
+        stv0297_writereg_mask(state, 0x81, 0x01, 0x00);
+        stv0297_writereg_mask(state, 0x83, 0x20, 0x20);
+        stv0297_writereg_mask(state, 0x83, 0x20, 0x00);
+        u_threshold = stv0297_readreg(state, 0x00) & 0xf;
+        initial_u = stv0297_readreg(state, 0x01) >> 4;
+        blind_u = stv0297_readreg(state, 0x01) & 0xf;
+        stv0297_writereg_mask(state, 0x84, 0x01, 0x01);
+        stv0297_writereg_mask(state, 0x84, 0x01, 0x00);
+        stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold);
+        stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4);
+        stv0297_writereg_mask(state, 0x01, 0x0f, blind_u);
+        stv0297_writereg_mask(state, 0x87, 0x80, 0x00);
+        stv0297_writereg(state, 0x63, 0x00);
+        stv0297_writereg(state, 0x64, 0x00);
+        stv0297_writereg(state, 0x65, 0x00);
+        stv0297_writereg(state, 0x66, 0x00);
+        stv0297_writereg(state, 0x67, 0x00);
+        stv0297_writereg(state, 0x68, 0x00);
+        stv0297_writereg_mask(state, 0x69, 0x0f, 0x00);
+
+        // set parameters
+        stv0297_set_qam(state, p->u.qam.modulation);
+        stv0297_set_symbolrate(state, p->u.qam.symbol_rate/1000);
+        stv0297_set_sweeprate(state, sweeprate);
+        stv0297_set_carrieroffset(state, carrieroffset);
+        stv0297_set_inversion(state, p->inversion);
+
+        // kick off lock
+        stv0297_writereg_mask(state, 0x88, 0x08, 0x08);
+        stv0297_writereg_mask(state, 0x5a, 0x20, 0x00);
+        stv0297_writereg_mask(state, 0x6a, 0x01, 0x01);
+        stv0297_writereg_mask(state, 0x43, 0x40, 0x40);
+        stv0297_writereg_mask(state, 0x5b, 0x30, 0x00);
+        stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c);
+        stv0297_writereg_mask(state, 0x03, 0x03, 0x03);
+        stv0297_writereg_mask(state, 0x43, 0x10, 0x10);
+
+        // wait for WGAGC lock
+        starttime = jiffies;
+        timeout = jiffies + (200*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+                if (stv0297_readreg(state, 0x43) & 0x08) break;
+        }
+        if (time_after(jiffies, timeout)) {
+                goto timeout;
+        }
+        msleep(20);
+
+        // wait for equaliser partial convergence
+        locked = 0;
+        timeout = jiffies + (50*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+
+                if (stv0297_readreg(state, 0x82) & 0x04) {
+                        locked = 1;
+                }
+        }
+        if (time_after(jiffies, timeout) && (!locked)) {
+                goto timeout;
+        }
+
+        // wait for equaliser full convergence
+        timeout = jiffies + (delay*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+
+                if (stv0297_readreg(state, 0x82) & 0x08) {
+                        break;
+                }
+        }
+        if (time_after(jiffies, timeout)) {
+                goto timeout;
+        }
+
+        // disable sweep
+        stv0297_writereg_mask(state, 0x6a, 1, 0);
+        stv0297_writereg_mask(state, 0x88, 8, 0);
+
+        // wait for main lock
+        timeout = jiffies + (20*HZ)/1000;
+        while(time_before(jiffies, timeout)) {
+                msleep(10);
+
+                if (stv0297_readreg(state, 0xDF) & 0x80) {
+                        break;
+                }
+        }
+        if (time_after(jiffies, timeout)) {
+                goto timeout;
+        }
+        msleep(100);
+
+        // is it still locked after that delay?
+        if (!(stv0297_readreg(state, 0xDF) & 0x80)) {
+                goto timeout;
+        }
+
+        // success!!
+        stv0297_writereg_mask(state, 0x5a, 0x40, 0x00);
+        state->freq_off = stv0297_get_carrieroffset(state);
+        state->base_freq = p->frequency;
+        return 0;
+
+timeout:
+        stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+        return 0;
+}
+
+static int stv0297_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        int reg_00, reg_83;
+
+        reg_00 = stv0297_readreg(state, 0x00);
+        reg_83 = stv0297_readreg(state, 0x83);
+
+        p->frequency = state->base_freq + state->freq_off;
+        p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF;
+        p->u.qam.symbol_rate = stv0297_get_symbolrate(state);
+        p->u.qam.fec_inner = 0;
+
+        switch((reg_00 >> 4) & 0x7) {
+        case 0: p->u.qam.modulation = QAM_16; break;
+        case 1: p->u.qam.modulation = QAM_32; break;
+        case 2: p->u.qam.modulation = QAM_128; break;
+        case 3: p->u.qam.modulation = QAM_256; break;
+        case 4: p->u.qam.modulation = QAM_64; break;
+        }
+
+        return 0;
+}
+
+static void stv0297_release(struct dvb_frontend* fe)
+{
+        struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv;
+        kfree(state);
+}
+
+static struct dvb_frontend_ops stv0297_ops;
+
+struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+                                    struct i2c_adapter* i2c,
+                                    int pwm)
+{
+        struct stv0297_state* state = NULL;
+
+        /* allocate memory for the internal state */
+        state = (struct stv0297_state*) kmalloc(sizeof(struct stv0297_state), GFP_KERNEL);
+        if (state == NULL) goto error;
+
+        /* setup the state */
+        state->config = config;
+        state->i2c = i2c;
+        memcpy(&state->ops, &stv0297_ops, sizeof(struct dvb_frontend_ops));
+        state->freq_off = 0;
+        state->base_freq = 0;
+        state->pwm = pwm;
+
+        /* check if the demod is there */
+        if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20) goto error;
+
+        /* create dvb_frontend */
+        state->frontend.ops = &state->ops;
+        state->frontend.demodulator_priv = state;
+        return &state->frontend;
+
+error:
+        if (state) kfree(state);
+        return NULL;
+}
+
+static struct dvb_frontend_ops stv0297_ops = {
+
+        .info = {
+                .name			= "ST STV0297 DVB-C",
+                .type			= FE_QAM,
+                .frequency_min		= 64000000,
+                .frequency_max		= 1300000000,
+                .frequency_stepsize	= 62500,
+                .symbol_rate_min	= 870000,
+                .symbol_rate_max	= 11700000,
+                .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+                        FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+                        FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO |
+                        FE_CAN_RECOVER
+        },
+
+        .release = stv0297_release,
+
+        .init = stv0297_init,
+
+        .set_frontend = stv0297_set_frontend,
+        .get_frontend = stv0297_get_frontend,
+
+        .read_status = stv0297_read_status,
+        .read_ber = stv0297_read_ber,
+        .read_signal_strength = stv0297_read_signal_strength,
+        .read_snr = stv0297_read_snr,
+        .read_ucblocks = stv0297_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver");
+MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(stv0297_attach);
+EXPORT_SYMBOL(stv0297_enable_plli2c);
diff -puN /dev/null drivers/media/dvb/frontends/stv0297.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/stv0297.h	2004-11-21 17:13:23.475049632 -0800
@@ -0,0 +1,41 @@
+/*
+    Driver for STV0297 demodulator
+
+    Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef STV0297_H
+#define STV0297_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0297_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+					   struct i2c_adapter* i2c, int pwm);
+extern int stv0297_enable_plli2c(struct dvb_frontend* fe);
+
+#endif // STV0297_H
diff -puN drivers/media/dvb/frontends/stv0299.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/stv0299.c
--- 25/drivers/media/dvb/frontends/stv0299.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.394061944 -0800
+++ 25-akpm/drivers/media/dvb/frontends/stv0299.c	2004-11-21 17:13:23.483048416 -0800
@@ -1,8 +1,5 @@
 /*
-    Universal driver for STV0299/TDA5059/SL1935 based
-    DVB QPSK frontends
-
-    Alps BSRU6, LG TDQB-S00x
+    Driver for ST STV0299 demodulator
 
     Copyright (C) 2001-2002 Convergence Integrated Media GmbH
 	<ralph@convergence.de>,
@@ -54,236 +51,42 @@
 #include <asm/div64.h>
 
 #include "dvb_frontend.h"
+#include "stv0299.h"
 
-#define FRONTEND_NAME "dvbfe_stv0299"
-
-#define dprintk(args...) \
-	do { \
-		if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \
-	} while (0)
-
-static int debug;
-static int stv0299_status;
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-module_param(stv0299_status, int, 0444);
-MODULE_PARM_DESC(stv0299_status, "Which status value to support "
-		 "(0 == BER (default), 1 == UCBLOCKS)");
+struct stv0299_state {
 
-#define STATUS_BER 0
-#define STATUS_UCBLOCKS 1
+	struct i2c_adapter* i2c;
 
+	struct dvb_frontend_ops ops;
 
-/* frontend types */
-#define UNKNOWN_FRONTEND  -1
-#define PHILIPS_SU1278_TSA	0 // SU1278 with TSA5059 synth and datasheet recommended settings
-#define ALPS_BSRU6         1
-#define LG_TDQF_S001F      2
-#define PHILIPS_SU1278_TUA      3 // SU1278 with TUA6100 synth
-#define SAMSUNG_TBMU24112IMB    4
-#define PHILIPS_SU1278_TSA_TT	5 // SU1278 with TSA5059 synth and TechnoTrend settings
-#define PHILIPS_SU1278_TSA_TY	6 // SU1278 with TUA5059 synth and Typhoon wiring
-#define PHILIPS_SU1278_TSA_CI	7 // SU1278 with TUA5059 synth and TerraTec Cinergy wiring
-
-/* Master Clock = 88 MHz */
-#define M_CLK (88000000UL) 
-
-/* Master Clock for TT cards = 64 MHz */
-#define M_CLK_SU1278_TSA_TT (64000000UL)
-
-static struct dvb_frontend_info uni0299_info = {
-	.name			= "STV0299/TSA5059/SL1935 based",
-	.type			= FE_QPSK,
-	.frequency_min		= 950000,
-	.frequency_max		= 2150000,
-	.frequency_stepsize	= 125,   /* kHz for QPSK frontends */
-	.frequency_tolerance	= M_CLK/2000,
-	.symbol_rate_min	= 1000000,
-	.symbol_rate_max	= 45000000,
-	.symbol_rate_tolerance	= 500,  /* ppm */
-	.notifier_delay		= 0,
-	.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-	      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
-	      FE_CAN_QPSK |
-	      FE_CAN_FEC_AUTO
-};
+	const struct stv0299_config* config;
 
+	struct dvb_frontend frontend;
 
-struct stv0299_state {
-	u8 tuner_type;
 	u8 initialised:1;
 	u32 tuner_frequency;
 	u32 symbol_rate;
 	fe_code_rate_t fec_inner;
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-};
-
-
-static u8 init_tab [] = {
-	0x04, 0x7d,   /* F22FR = 0x7d                                 */
-		      /* F22 = f_VCO / 128 / 0x7d = 22 kHz            */
-
-        /* I2C bus repeater */
-	0x05, 0x35,   /* I2CT = 0, SCLT = 1, SDAT = 1                 */
-
- 	/* general purpose DAC registers */
-	0x06, 0x40,   /* DAC not used, set to high impendance mode    */
-	0x07, 0x00,   /* DAC LSB                                      */
-
-	/* DiSEqC registers */
-	0x08, 0x40,   /* DiSEqC off, LNB power on OP2/LOCK pin on     */
-	0x09, 0x00,   /* FIFO                                         */
-
-        /* Input/Output configuration register */
-	0x0c, 0x51,   /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */
-	              /* OP0 ctl = Normal, OP0 val = 1 (18 V)         */
-                      /* Nyquist filter = 00, QPSK reverse = 0        */
-                   
-        /* AGC1 control register */
-	0x0d, 0x82,   /* DC offset compensation = ON, beta_agc1 = 2   */
-
-        /* Timing loop register */
-	0x0e, 0x23,   /* alpha_tmg = 2, beta_tmg = 3                  */
-
-	0x10, 0x3f,   // AGC2  0x3d
-
-	0x11, 0x84,
-	0x12, 0xb5,   // Lock detect: -64  Carrier freq detect:on
-
-	0x15, 0xc9,   // lock detector threshold
-
-	0x16, 0x00,
-	0x17, 0x00,
-	0x18, 0x00,
-	0x19, 0x00,
-	0x1a, 0x00,
-
-	0x1f, 0x50,
-
-	0x20, 0x00,
-	0x21, 0x00,
-	0x22, 0x00,
-	0x23, 0x00,
-
-	0x28, 0x00,  // out imp: normal  out type: parallel FEC mode:0
-
-	0x29, 0x1e,  // 1/2 threshold
-	0x2a, 0x14,  // 2/3 threshold
-	0x2b, 0x0f,  // 3/4 threshold
-	0x2c, 0x09,  // 5/6 threshold
-	0x2d, 0x05,  // 7/8 threshold
-	0x2e, 0x01,
-
-	0x31, 0x1f,  // test all FECs
-
-	0x32, 0x19,  // viterbi and synchro search
-	0x33, 0xfc,  // rs control
-	0x34, 0x93,  // error control
-};
-
-
-static u8 init_tab_samsung [] = {
-	0x01, 0x15,
-	0x02, 0x00,
-	0x03, 0x00,
-	0x04, 0x7D,
-	0x05, 0x35,
-	0x06, 0x02,
-	0x07, 0x00,
-	0x08, 0xC3,
-	0x0C, 0x00,
-	0x0D, 0x81,
-	0x0E, 0x23,
-	0x0F, 0x12,
-	0x10, 0x7E,
-	0x11, 0x84,
-	0x12, 0xB9,
-	0x13, 0x88,
-	0x14, 0x89,
-	0x15, 0xC9,
-	0x16, 0x00,
-	0x17, 0x5C,
-	0x18, 0x00,
-	0x19, 0x00,
-	0x1A, 0x00,
-	0x1C, 0x00,
-	0x1D, 0x00,
-	0x1E, 0x00,
-	0x1F, 0x3A,
-	0x20, 0x2E,
-	0x21, 0x80,
-	0x22, 0xFF,
-	0x23, 0xC1,
-	0x28, 0x00,
-	0x29, 0x1E,
-	0x2A, 0x14,
-	0x2B, 0x0F,
-	0x2C, 0x09,
-	0x2D, 0x05,
-	0x31, 0x1F,
-	0x32, 0x19,
-	0x33, 0xFE,
-	0x34, 0x93
+	int errmode;
 };
 
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
 
-static u8 init_tab_su1278_tsa_tt [] = {
-        0x01, 0x0f,
-        0x02, 0x30,
-        0x03, 0x00,
-        0x04, 0x5b,
-        0x05, 0x85,
-        0x06, 0x02,
-        0x07, 0x00,
-        0x08, 0x02,
-        0x09, 0x00,
-        0x0C, 0x01,
-        0x0D, 0x81,
-        0x0E, 0x44,
-        0x0f, 0x14,
-        0x10, 0x3c,
-        0x11, 0x84,
-        0x12, 0xda,
-        0x13, 0x97,
-        0x14, 0x95,
-        0x15, 0xc9,
-        0x16, 0x19,
-        0x17, 0x8c,
-        0x18, 0x59,
-        0x19, 0xf8,
-        0x1a, 0xfe,
-        0x1c, 0x7f,
-        0x1d, 0x00,
-        0x1e, 0x00,
-        0x1f, 0x50,
-        0x20, 0x00,
-        0x21, 0x00,
-        0x22, 0x00,
-        0x23, 0x00,
-        0x28, 0x00,
-        0x29, 0x28,
-        0x2a, 0x14,
-        0x2b, 0x0f,
-        0x2c, 0x09,
-        0x2d, 0x09,
-        0x31, 0x1f,
-        0x32, 0x19,
-        0x33, 0xfc,
-        0x34, 0x13
-};
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "stv0299: " args); \
+	} while (0)
 
-static int stv0299_set_FEC (struct i2c_adapter *i2c, fe_code_rate_t fec);
-static int stv0299_set_symbolrate (struct i2c_adapter *i2c, u32 srate, int tuner_type);
 
-static int stv0299_writereg (struct i2c_adapter *i2c, u8 reg, u8 data)
+static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data)
 {
 	int ret;
 	u8 buf [] = { reg, data };
-	struct i2c_msg msg = { .addr = 0x68, .flags = 0, .buf = buf, .len = 2 };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
 
-	ret = i2c_transfer (i2c, &msg, 1);
+	ret = i2c_transfer (state->i2c, &msg, 1);
 
 	if (ret != 1) 
 		dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
@@ -292,16 +95,23 @@ static int stv0299_writereg (struct i2c_
 	return (ret != 1) ? -EREMOTEIO : 0;
 }
 
+int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	return stv0299_writeregI(state, reg, data);
+}
+
 
-static u8 stv0299_readreg (struct i2c_adapter *i2c, u8 reg)
+static u8 stv0299_readreg (struct stv0299_state* state, u8 reg)
 {
 	int ret;
 	u8 b0 [] = { reg };
 	u8 b1 [] = { 0 };
-	struct i2c_msg msg [] = { { .addr = 0x68, .flags = 0, .buf = b0, .len = 1 },
-			   { .addr = 0x68, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+	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 (i2c, msg, 2);
+	ret = i2c_transfer (state->i2c, msg, 2);
         
 	if (ret != 2) 
 		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
@@ -311,13 +121,13 @@ static u8 stv0299_readreg (struct i2c_ad
 }
 
 
-static int stv0299_readregs (struct i2c_adapter *i2c, u8 reg1, u8 *b, u8 len)
+static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len)
 {
         int ret;
-        struct i2c_msg msg [] = { { .addr = 0x68, .flags = 0, .buf = &reg1, .len = 1 },
-                           { .addr = 0x68, .flags = I2C_M_RD, .buf = b, .len = len } };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
 
-	ret = i2c_transfer (i2c, msg, 2);
+	ret = i2c_transfer (state->i2c, msg, 2);
 
         if (ret != 2)
                 dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
@@ -326,373 +136,44 @@ static int stv0299_readregs (struct i2c_
 }
 
 
-static int pll_write (struct i2c_adapter *i2c, u8 addr, u8 *data, int len)
-{
-	int ret;
-	struct i2c_msg msg = { .addr = addr, .buf = data, .len = len };
-
-
-	stv0299_writereg(i2c, 0x05, 0xb5);	/*  enable i2c repeater on stv0299  */
-
-	ret =  i2c_transfer (i2c, &msg, 1);
-
-	stv0299_writereg(i2c, 0x05, 0x35);	/*  disable i2c repeater on stv0299  */
-
-	if (ret != 1)
-		dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
-
-	return (ret != 1) ? -1 : 0;
-}
-
-
-static int sl1935_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype)
-{
-	u8 buf[4];
-	u32 div;
-
-	div = freq / 125;
-
-	dprintk("%s : freq = %i, div = %i\n", __FUNCTION__, freq, div);
-
-	buf[0] = (div >> 8) & 0x7f;
-	buf[1] = div & 0xff;
-	buf[2] = 0x84;	// 0xC4
-	buf[3] = 0x08;
-
-	if (freq < 1500000) buf[3] |= 0x10;
-
-	return pll_write (i2c, 0x61, buf, sizeof(buf));
-}
-
-/**
- *   set up the downconverter frequency divisor for a 
- *   reference clock comparision frequency of 125 kHz.
- */
-static int tsa5059_set_tv_freq	(struct i2c_adapter *i2c, u32 freq, int ftype, int srate)
-{
-	u8 addr;
-	u32 div;
-	u8 buf[4];
-	int divisor, regcode;
-
-	dprintk ("%s: freq %i, ftype %i\n", __FUNCTION__, freq, ftype);
-
-	if ((freq < 950000) || (freq > 2150000)) return -EINVAL;
-
-	if (ftype == PHILIPS_SU1278_TSA_TT) {
-        divisor = 500;
-        regcode = 2;
-	} else {
-		divisor = 125;
-		regcode = 4;
-	}
-
-	// setup frequency divisor
-	div = (freq + (divisor - 1)) / divisor; // round correctly
-	buf[0] = (div >> 8) & 0x7f;
-	buf[1] = div & 0xff;
-	buf[2] = 0x80 | ((div & 0x18000) >> 10) | regcode;
-	buf[3] = 0;
-
-	// tuner-specific settings
-	switch(ftype) {
-	case PHILIPS_SU1278_TSA:
-	case PHILIPS_SU1278_TSA_TT:
-	case PHILIPS_SU1278_TSA_TY:
-	case PHILIPS_SU1278_TSA_CI:
-		if (ftype == PHILIPS_SU1278_TSA_TY || ftype == PHILIPS_SU1278_TSA_CI)
-			addr = 0x61;
-		else
-		addr = 0x60;
-
-		buf[3] |= 0x20;
-
-		if (srate < 4000000) buf[3] |= 1;
-	   
-		if (freq < 1250000) buf[3] |= 0;
-		else if (freq < 1550000) buf[3] |= 0x40;
-		else if (freq < 2050000) buf[3] |= 0x80;
-		else if (freq < 2150000) buf[3] |= 0xC0;
-		break;
-
-	case ALPS_BSRU6:
-		addr = 0x61;
-		buf[3] = 0xC4;
-		if (freq > 1530000) buf[3] = 0xc0;
-	 	break;
-
-	default:
-		return -EINVAL;
-	}
-
-	return pll_write (i2c, addr, buf, sizeof(buf));
-}
-
-
-#define MIN2(a,b) ((a) < (b) ? (a) : (b))
-#define MIN3(a,b,c) MIN2(MIN2(a,b),c)
-
-static int tua6100_set_tv_freq	(struct i2c_adapter *i2c, u32 freq,
-			 int ftype, int srate)
-{
-	u8 reg0 [2] = { 0x00, 0x00 };
-	u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 };
-	u8 reg2 [3] = { 0x02, 0x00, 0x00 };
-	int _fband;
-	int first_ZF;
-	int R, A, N, P, M;
-	int err;
-
-	first_ZF = (freq) / 1000;
-
-	if (abs(MIN2(abs(first_ZF-1190),abs(first_ZF-1790))) <
-	    abs(MIN3(abs(first_ZF-1202),abs(first_ZF-1542),abs(first_ZF-1890))))
-		_fband = 2;
-	else
-		_fband = 3;
-
-	if (_fband == 2) {
-		if (((first_ZF >= 950) && (first_ZF < 1350)) ||
-		    ((first_ZF >= 1430) && (first_ZF < 1950)))
-			reg0[1] = 0x07;
-		else if (((first_ZF >= 1350) && (first_ZF < 1430)) ||
-			    ((first_ZF >= 1950) && (first_ZF < 2150)))
-			reg0[1] = 0x0B;
-	}
-
-	if(_fband == 3) {
-		if (((first_ZF >= 950) && (first_ZF < 1350)) ||
-		     ((first_ZF >= 1455) && (first_ZF < 1950)))
-			reg0[1] = 0x07;
-		else if (((first_ZF >= 1350) && (first_ZF < 1420)) ||
-			 ((first_ZF >= 1950) && (first_ZF < 2150)))
-			reg0[1] = 0x0B;
-		else if ((first_ZF >= 1420) && (first_ZF < 1455))
-			reg0[1] = 0x0F;
-}
-
-	if (first_ZF > 1525)
-		reg1[1] |= 0x80;
-	else
-		reg1[1] &= 0x7F;
-
-	if (_fband == 2) {
-	        if (first_ZF > 1430) { /* 1430MHZ */
-	                reg1[1] &= 0xCF; /* N2 */
-			reg2[1] &= 0xCF; /* R2 */
-			reg2[1] |= 0x10;
-	        } else {
-        		reg1[1] &= 0xCF; /* N2 */
-			reg1[1] |= 0x20;
-			reg2[1] &= 0xCF; /* R2 */
-			reg2[1] |= 0x10;
-		}
-}
-
-	if (_fband == 3) {
-        	if ((first_ZF >= 1455) &&
-		    (first_ZF < 1630)) {
-			reg1[1] &= 0xCF; /* N2 */
-			reg1[1] |= 0x20;
-			reg2[1] &= 0xCF; /* R2 */
-	        } else {
-			if (first_ZF < 1455) {
-	                        reg1[1] &= 0xCF; /* N2 */
-				reg1[1] |= 0x20;
-                	        reg2[1] &= 0xCF; /* R2 */
-                        	reg2[1] |= 0x10;
-	                } else {
-	                        if (first_ZF >= 1630) {
-        	                        reg1[1] &= 0xCF; /* N2 */
-                        	        reg2[1] &= 0xCF; /* R2 */
-                                	reg2[1] |= 0x10;
-	                        }
-        	        }
-	        }
-	}
-
-        /* set ports, enable P0 for symbol rates > 4Ms/s */
-	if (srate >= 4000000)
-		reg1[1] |= 0x0c;
-	else
-		reg1[1] |= 0x04;
-
-	reg2[1] |= 0x0c;
-
-	R = 64;
-	A = 64;
-	P = 64;  //32
-
-	M = (freq * R) / 4;		/* in Mhz */
-	N = (M - A * 1000) / (P * 1000);
-
-	reg1[1] |= (N >> 9) & 0x03;
-	reg1[2]  = (N >> 1) & 0xff;
-	reg1[3]  = (N << 7) & 0x80;
-
-	reg2[1] |= (R >> 8) & 0x03;
-	reg2[2]  = R & 0xFF;	/* R */
-
-	reg1[3] |= A & 0x7f;	/* A */
-
-	if (P == 64)
-		reg1[1] |= 0x40; /* Prescaler 64/65 */
-
-	reg0[1] |= 0x03;
-
-	if ((err = pll_write(i2c, 0x60, reg0, sizeof(reg0))))
-		return err;
-
-	if ((err = pll_write(i2c, 0x60, reg1, sizeof(reg1))))
-		return err;
-
-	if ((err = pll_write(i2c, 0x60, reg2, sizeof(reg2))))
-		return err;
-
-	return 0;
-}
-
-
-static int pll_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype, int srate)
-{
-	switch(ftype) {
-	case SAMSUNG_TBMU24112IMB:
-		return sl1935_set_tv_freq(i2c, freq, ftype);
-
-	case LG_TDQF_S001F:
-		return sl1935_set_tv_freq(i2c, freq, ftype);
-
-	case PHILIPS_SU1278_TUA:
-		return tua6100_set_tv_freq(i2c, freq, ftype, srate);
-
-	default:
-		return tsa5059_set_tv_freq(i2c, freq, ftype, srate);
-}
-}
-
-#if 0
-static int tsa5059_read_status	(struct i2c_adapter *i2c)
-{
-	int ret;
-	u8 rpt1 [] = { 0x05, 0xb5 };
-	u8 stat [] = { 0 };
-
-	struct i2c_msg msg [] = {{ .addr = 0x68, .flags = 0, .buf = rpt1, .len = 2 },
-			  { .addr = 0x60, .flags = I2C_M_RD, .buf = stat, .len = 1 }};
-
-	dprintk ("%s\n", __FUNCTION__);
-
-	ret = i2c_transfer (i2c, msg, 2);
-
-	if (ret != 2)
-		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
-
-	return stat[0];
-}
-#endif
-
-
-static int stv0299_init (struct i2c_adapter *i2c, int ftype)
-{
-	int i;
-
-	dprintk("stv0299: init chip\n");
-
-	switch(ftype) {
-	case SAMSUNG_TBMU24112IMB:
-		dprintk("%s: init stv0299 chip for Samsung TBMU24112IMB\n", __FUNCTION__);
-
-		for (i=0; i<sizeof(init_tab_samsung); i+=2)
-		{
-			dprintk("%s: reg == 0x%02x, val == 0x%02x\n", __FUNCTION__, init_tab_samsung[i], init_tab_samsung[i+1]);
-
-			stv0299_writereg (i2c, init_tab_samsung[i], init_tab_samsung[i+1]);
-		}
-		break;
-
-	case PHILIPS_SU1278_TSA_TT:
-	        for (i=0; i<sizeof(init_tab_su1278_tsa_tt); i+=2) {
-			stv0299_writereg (i2c, init_tab_su1278_tsa_tt[i], init_tab_su1278_tsa_tt[i+1]);
-		}
-	        break;
-
-	default:
-	stv0299_writereg (i2c, 0x01, 0x15);
-		stv0299_writereg (i2c, 0x02, ftype == PHILIPS_SU1278_TUA ? 0x00 : 0x30);
-	stv0299_writereg (i2c, 0x03, 0x00);
-
-	for (i=0; i<sizeof(init_tab); i+=2)
-		stv0299_writereg (i2c, init_tab[i], init_tab[i+1]);
-
-        /* AGC1 reference register setup */
-		if (ftype == PHILIPS_SU1278_TSA || ftype == PHILIPS_SU1278_TSA_TY || ftype == PHILIPS_SU1278_TSA_CI)
-		  stv0299_writereg (i2c, 0x0f, 0x92);  /* Iagc = Inverse, m1 = 18 */
-		else if (ftype == PHILIPS_SU1278_TUA)
-		  stv0299_writereg (i2c, 0x0f, 0x94);  /* Iagc = Inverse, m1 = 20 */
-	else
-	  stv0299_writereg (i2c, 0x0f, 0x52);  /* Iagc = Normal,  m1 = 18 */
-		break;
-	}
-	
-	switch(stv0299_status) {
-	case STATUS_BER:
-		stv0299_writereg(i2c, 0x34, 0x93);
-		break;
-	
-	case STATUS_UCBLOCKS:
-		stv0299_writereg(i2c, 0x34, 0xB3);
-		break;
-	}
-
-	return 0;
-}
-
-
-static int stv0299_set_FEC (struct i2c_adapter *i2c, fe_code_rate_t fec)
+static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec)
 {
 	dprintk ("%s\n", __FUNCTION__);
 
 	switch (fec) {
 	case FEC_AUTO:
 	{
-		dprintk ("%s : FEC_AUTO\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x31, 0x1f);
+		return stv0299_writeregI (state, 0x31, 0x1f);
 	}
 	case FEC_1_2:
 	{
-		dprintk ("%s : FEC_1_2\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x31, 0x01);
+		return stv0299_writeregI (state, 0x31, 0x01);
 	}
 	case FEC_2_3:
 	{
-		dprintk ("%s : FEC_2_3\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x31, 0x02);
+		return stv0299_writeregI (state, 0x31, 0x02);
 	}
 	case FEC_3_4:
 	{
-		dprintk ("%s : FEC_3_4\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x31, 0x04);
+		return stv0299_writeregI (state, 0x31, 0x04);
 	}
 	case FEC_5_6:
 	{
-		dprintk ("%s : FEC_5_6\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x31, 0x08);
+		return stv0299_writeregI (state, 0x31, 0x08);
 	}
 	case FEC_7_8:
 	{
-		dprintk ("%s : FEC_7_8\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x31, 0x10);
+		return stv0299_writeregI (state, 0x31, 0x10);
 	}
 	default:
 	{
-		dprintk ("%s : FEC invalid\n", __FUNCTION__);
 		return -EINVAL;
 	}
 }
 }
 
 
-static fe_code_rate_t stv0299_get_fec (struct i2c_adapter *i2c)
+static fe_code_rate_t stv0299_get_fec (struct stv0299_state* state)
 {
 	static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6,
 					     FEC_7_8, FEC_1_2 };
@@ -700,7 +181,7 @@ static fe_code_rate_t stv0299_get_fec (s
 
 	dprintk ("%s\n", __FUNCTION__);
 
-	index = stv0299_readreg (i2c, 0x1b);
+	index = stv0299_readreg (state, 0x1b);
 	index &= 0x7;
 
 	if (index > 4)
@@ -710,13 +191,13 @@ static fe_code_rate_t stv0299_get_fec (s
 }
 
 
-static int stv0299_wait_diseqc_fifo (struct i2c_adapter *i2c, int timeout)
+static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout)
 {
 	unsigned long start = jiffies;
 
 	dprintk ("%s\n", __FUNCTION__);
 
-	while (stv0299_readreg(i2c, 0x0a) & 1) {
+	while (stv0299_readreg(state, 0x0a) & 1) {
 		if (jiffies - start > timeout) {
 			dprintk ("%s: timeout!!\n", __FUNCTION__);
 			return -ETIMEDOUT;
@@ -728,13 +209,13 @@ static int stv0299_wait_diseqc_fifo (str
 }
 
 
-static int stv0299_wait_diseqc_idle (struct i2c_adapter *i2c, int timeout)
+static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout)
 {
 	unsigned long start = jiffies;
 
 	dprintk ("%s\n", __FUNCTION__);
 
-	while ((stv0299_readreg(i2c, 0x0a) & 3) != 2 ) {
+	while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) {
 		if (jiffies - start > timeout) {
 			dprintk ("%s: timeout!!\n", __FUNCTION__);
 			return -ETIMEDOUT;
@@ -745,101 +226,156 @@ static int stv0299_wait_diseqc_idle (str
 	return 0;
 }
 
+static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	u64 big = srate;
+	u32 ratio;
+
+	// check rate is within limits
+	if ((srate < 1000000) || (srate > 45000000)) return -EINVAL;
+
+	// calculate value to program
+	big = big << 20;
+	big += (state->config->mclk-1); // round correctly
+	do_div(big, state->config->mclk);
+	ratio = big << 4;
+
+	return state->config->set_symbol_rate(fe, srate, ratio);
+}
+
+
+static int stv0299_get_symbolrate (struct stv0299_state* state)
+{
+	u32 Mclk = state->config->mclk / 4096L;
+	u32 srate;
+	s32 offset;
+	u8 sfr[3];
+	s8 rtf;
+
+	dprintk ("%s\n", __FUNCTION__);
+
+	stv0299_readregs (state, 0x1f, sfr, 3);
+	stv0299_readregs (state, 0x1a, &rtf, 1);
+
+	srate = (sfr[0] << 8) | sfr[1];
+	srate *= Mclk;
+	srate /= 16;
+	srate += (sfr[2] >> 4) * Mclk / 256;
+	offset = (s32) rtf * (srate / 4096L);
+	offset /= 128;
+
+	dprintk ("%s : srate = %i\n", __FUNCTION__, srate);
+	dprintk ("%s : ofset = %i\n", __FUNCTION__, offset);
+
+	srate += offset;
+
+	srate += 1000;
+	srate /= 2000;
+	srate *= 2000;
+
+	return srate;
+}
+
+
+
+
+
+
+
+
+
+
 
-static int stv0299_send_diseqc_msg (struct i2c_adapter *i2c,
+
+
+
+static int stv0299_send_diseqc_msg (struct dvb_frontend* fe,
 			     struct dvb_diseqc_master_cmd *m)
 {
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 	u8 val;
 	int i;
 
 	dprintk ("%s\n", __FUNCTION__);
 
-	if (stv0299_wait_diseqc_idle (i2c, 100) < 0)
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
 		return -ETIMEDOUT;
 
-	val = stv0299_readreg (i2c, 0x08);
+	val = stv0299_readreg (state, 0x08);
 
-	if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x6))  /* DiSEqC mode */
+	if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6))  /* DiSEqC mode */
 		return -EREMOTEIO;
 
 	for (i=0; i<m->msg_len; i++) {
-		if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
+		if (stv0299_wait_diseqc_fifo (state, 100) < 0)
 			return -ETIMEDOUT;
 
-		if (stv0299_writereg (i2c, 0x09, m->msg[i]))
+		if (stv0299_writeregI (state, 0x09, m->msg[i]))
 			return -EREMOTEIO;
 	}
 
-	if (stv0299_wait_diseqc_idle (i2c, 100) < 0)
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
 		return -ETIMEDOUT;
 
 	return 0;
 }
 
 
-static int stv0299_send_diseqc_burst (struct i2c_adapter *i2c, fe_sec_mini_cmd_t burst)
+static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
 {
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 	u8 val;
 
 	dprintk ("%s\n", __FUNCTION__);
 
-	if (stv0299_wait_diseqc_idle (i2c, 100) < 0)
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
 		return -ETIMEDOUT;
 
-	val = stv0299_readreg (i2c, 0x08);
+	val = stv0299_readreg (state, 0x08);
 
-	if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x2))   /* burst mode */
+	if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2))	/* burst mode */
 		return -EREMOTEIO;
 
-	if (stv0299_writereg (i2c, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff))
+	if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff))
 		return -EREMOTEIO;
 
-	if (stv0299_wait_diseqc_idle (i2c, 100) < 0)
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
 		return -ETIMEDOUT;
 
-	if (stv0299_writereg (i2c, 0x08, val))
+	if (stv0299_writeregI (state, 0x08, val))
 		return -EREMOTEIO;
 
 	return 0;
 }
 
 
-static int stv0299_set_tone (struct i2c_adapter *i2c, fe_sec_tone_mode_t tone)
+static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
 {
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 	u8 val;
 
-	dprintk("%s: %s\n", __FUNCTION__,
-		tone == SEC_TONE_ON ? "SEC_TONE_ON" : 
-		tone == SEC_TONE_OFF ? "SEC_TONE_OFF" : "??");
-
-	if (stv0299_wait_diseqc_idle (i2c, 100) < 0)
+	if (stv0299_wait_diseqc_idle (state, 100) < 0)
 		return -ETIMEDOUT;
 
-	val = stv0299_readreg (i2c, 0x08);
+	val = stv0299_readreg (state, 0x08);
 
 	switch (tone) {
 	case SEC_TONE_ON:
-	{
-	    	dprintk("%s: TONE_ON\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x08, val | 0x3);
-	}	
+		return stv0299_writeregI (state, 0x08, val | 0x3);
+
 	case SEC_TONE_OFF:
-	{
-	    	dprintk("%s: TONE_OFF\n", __FUNCTION__);
-		return stv0299_writereg (i2c, 0x08, (val & ~0x3) | 0x02);
-	}
+		return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02);
+
 	default:
-	{
-	    	dprintk("%s: TONE INVALID\n", __FUNCTION__);
 		return -EINVAL;
 	}
-	};
 }
 
 
-static int stv0299_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage,
-				int tuner_type)
+static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
 {
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 	u8 reg0x08;
 	u8 reg0x0c;
 
@@ -847,8 +383,8 @@ static int stv0299_set_voltage (struct i
 		voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : 
 		voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
 
-	reg0x08 = stv0299_readreg (i2c, 0x08);
-	reg0x0c = stv0299_readreg (i2c, 0x0c);
+	reg0x08 = stv0299_readreg (state, 0x08);
+	reg0x0c = stv0299_readreg (state, 0x0c);
 
 	/**
 	 *  H/V switching over OP0, OP1 and OP2 are LNB power enable bits
@@ -856,37 +392,28 @@ static int stv0299_set_voltage (struct i
 	reg0x0c &= 0x0f;
 
 	if (voltage == SEC_VOLTAGE_OFF) {
-		stv0299_writereg (i2c, 0x0c, 0x00); /*	LNB power off! */
-		return stv0299_writereg (i2c, 0x08, 0x00); /*	LNB power off! */
+		stv0299_writeregI (state, 0x0c, 0x00); /*	LNB power off! */
+		return stv0299_writeregI (state, 0x08, 0x00); /*	LNB power off! */
 	}
 	
-	if (tuner_type == PHILIPS_SU1278_TSA_CI)
-	{
-		stv0299_writereg (i2c, 0x08, reg0x08 & 0xBF); // switch LNB power on OP2/LOCK pin off
-	}
-	else
-	{
-		stv0299_writereg (i2c, 0x08, reg0x08 | 0x40);
-	}
+	stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6));
 
 	switch (voltage) {
 	case SEC_VOLTAGE_13:
-		if (tuner_type == PHILIPS_SU1278_TSA_TY || tuner_type == PHILIPS_SU1278_TSA_CI)
-			return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x10);
-		else
-			return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x40);
+		if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) reg0x0c |= 0x10;
+		else reg0x0c |= 0x40;
 
-	case SEC_VOLTAGE_18:
-		return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x50);
+		return stv0299_writeregI(state, 0x0c, reg0x0c);
 
+	case SEC_VOLTAGE_18:
+		return stv0299_writeregI(state, 0x0c, reg0x0c | 0x50);
 	default:
 		return -EINVAL;
 	};
 }
 
 
-static int stv0299_send_legacy_dish_cmd(struct i2c_adapter *i2c, u32 cmd, 
-                                        int tuner_type)
+static int stv0299_send_legacy_dish_cmd(struct dvb_frontend* fe, u32 cmd)
 {
 	u8 last = 1;
 	int i;
@@ -901,15 +428,14 @@ static int stv0299_send_legacy_dish_cmd(
 	cmd = cmd << 1;
 	dprintk("%s switch command: 0x%04x\n",__FUNCTION__, cmd);
 
-	stv0299_set_voltage(i2c,SEC_VOLTAGE_18,tuner_type);
+	stv0299_set_voltage(fe,SEC_VOLTAGE_18);
 	msleep(32);
 
 	for (i=0; i<9; i++) {
 		if((cmd & 0x01) != last) {
-			stv0299_set_voltage(i2c,
+			stv0299_set_voltage(fe,
 					    last ? SEC_VOLTAGE_13 :
-					    	   SEC_VOLTAGE_18,
-					    tuner_type);
+					    	   SEC_VOLTAGE_18);
 			last = (last) ? 0 : 1;
 		}
 
@@ -922,160 +448,35 @@ static int stv0299_send_legacy_dish_cmd(
 	return 0;
 }
 
-static int stv0299_set_symbolrate (struct i2c_adapter *i2c, u32 srate, int tuner_type)
+
+static int stv0299_init (struct dvb_frontend* fe)
 {
-	u64 big = srate;
-	u32 ratio;
-	u8 aclk = 0;
-	u8 bclk = 0;
-	u8 m1;
-        int Mclk = M_CLK;
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	int i;
 
-        // check rate is within limits
-	if ((srate < 1000000) || (srate > 45000000)) return -EINVAL;
+	dprintk("stv0299: init chip\n");
 
-        // calculate value to program
-	if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT;
-        big = big << 20;
-	big += (Mclk-1); // round correctly
-        do_div(big, Mclk);
-        ratio = big << 4;
-
-        // program registers
-	switch(tuner_type) {
-	case PHILIPS_SU1278_TSA_TT:
-	        stv0299_writereg (i2c, 0x0e, 0x44);
-	        if (srate >= 10000000) {
-		        stv0299_writereg (i2c, 0x13, 0x97);
-		        stv0299_writereg (i2c, 0x14, 0x95);
-		        stv0299_writereg (i2c, 0x15, 0xc9);
-		        stv0299_writereg (i2c, 0x17, 0x8c);
-		        stv0299_writereg (i2c, 0x1a, 0xfe);
-		        stv0299_writereg (i2c, 0x1c, 0x7f);
-		        stv0299_writereg (i2c, 0x2d, 0x09);
-		} else {
-		        stv0299_writereg (i2c, 0x13, 0x99);
-		        stv0299_writereg (i2c, 0x14, 0x8d);
-		        stv0299_writereg (i2c, 0x15, 0xce);
-		        stv0299_writereg (i2c, 0x17, 0x43);
-		        stv0299_writereg (i2c, 0x1a, 0x1d);
-		        stv0299_writereg (i2c, 0x1c, 0x12);
-		        stv0299_writereg (i2c, 0x2d, 0x05);
-		}
-	        stv0299_writereg (i2c, 0x0e, 0x23);
-	        stv0299_writereg (i2c, 0x0f, 0x94);
-	        stv0299_writereg (i2c, 0x10, 0x39);
-	        stv0299_writereg (i2c, 0x15, 0xc9);
-
-	        stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff);
-	        stv0299_writereg (i2c, 0x20, (ratio >>  8) & 0xff);
-	        stv0299_writereg (i2c, 0x21, (ratio      ) & 0xf0);
-	        break;
-
-	case PHILIPS_SU1278_TSA_TY:
-	case PHILIPS_SU1278_TSA_CI:
-	case PHILIPS_SU1278_TSA:
-		aclk = 0xb5;
-		if (srate < 2000000) bclk = 0x86;
-		else if (srate < 5000000) bclk = 0x89;
-		else if (srate < 15000000) bclk = 0x8f;
-		else if (srate < 45000000) bclk = 0x95;
-
-		m1 = 0x14;
-		if (srate < 4000000) m1 = 0x10;
-
-	    	stv0299_writereg (i2c, 0x13, aclk);
-  	        stv0299_writereg (i2c, 0x14, bclk);
-	        stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff);
-	        stv0299_writereg (i2c, 0x20, (ratio >>  8) & 0xff);
-	        stv0299_writereg (i2c, 0x21, (ratio      ) & 0xf0);
-	        stv0299_writereg (i2c, 0x0f, (stv0299_readreg(i2c, 0x0f) & 0xc0) | m1);
-		break;
+	for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2)
+		stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]);
 
-	case ALPS_BSRU6:
-	default:
-		if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
-		else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
-		else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
-		else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
-		else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
-		else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; }
-  
-	stv0299_writereg (i2c, 0x13, aclk);
-	stv0299_writereg (i2c, 0x14, bclk);
-	stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff);
-	stv0299_writereg (i2c, 0x20, (ratio >>  8) & 0xff);
-	stv0299_writereg (i2c, 0x21, (ratio      ) & 0xf0);
-		break;
+	if (state->config->pll_init) {
+		stv0299_writeregI(state, 0x05, 0xb5);	/*  enable i2c repeater on stv0299  */
+		state->config->pll_init(fe);
+		stv0299_writeregI(state, 0x05, 0x35);	/*  disable i2c repeater on stv0299  */
 	}
 
-
 	return 0;
 }
 
 
-static int stv0299_get_symbolrate (struct i2c_adapter *i2c, int tuner_type)
-{
-	u32 Mclk = M_CLK / 4096L;
-	u32 srate;
-	s32 offset;
-	u8 sfr[3];
-	s8 rtf;
-
-	dprintk ("%s\n", __FUNCTION__);
-
-    	if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT / 4096L;
-
-	stv0299_readregs (i2c, 0x1f, sfr, 3);
-	stv0299_readregs (i2c, 0x1a, &rtf, 1);
-
-	srate = (sfr[0] << 8) | sfr[1];
-	srate *= Mclk;
-	srate /= 16;
-	srate += (sfr[2] >> 4) * Mclk / 256;
-
-	offset = (s32) rtf * (srate / 4096L);
-	offset /= 128;
-
-	dprintk ("%s : srate = %i\n", __FUNCTION__, srate);
-	dprintk ("%s : ofset = %i\n", __FUNCTION__, offset);
-
-	srate += offset;
-
-	srate += 1000;
-	srate /= 2000;
-	srate *= 2000;
-
-	return srate;
-}
-
-static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
-{
-	struct stv0299_state *state = (struct stv0299_state *) fe->data;
-	struct i2c_adapter *i2c = state->i2c;
-
-	dprintk ("%s\n", __FUNCTION__);
-
-	switch (cmd) {
-	case FE_GET_INFO:
+static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status)
 	{
-	        struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) arg;
-		memcpy (arg, &uni0299_info, sizeof(struct dvb_frontend_info));
-
-		if (state->tuner_type == PHILIPS_SU1278_TSA_TT) {
-		        tmp->frequency_tolerance = M_CLK_SU1278_TSA_TT / 2000;
-		}
-		break;
-	}
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 
-	case FE_READ_STATUS:
-	{
-		fe_status_t *status = (fe_status_t *) arg;
-		u8 signal = 0xff - stv0299_readreg (i2c, 0x18);
-		u8 sync = stv0299_readreg (i2c, 0x1b);
+	u8 signal = 0xff - stv0299_readreg (state, 0x18);
+	u8 sync = stv0299_readreg (state, 0x1b);
 
 		dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __FUNCTION__, sync);
-
 		*status = 0;
 
 		if (signal > 10)
@@ -1093,53 +494,61 @@ static int uni0299_ioctl (struct dvb_fro
 		if ((sync & 0x98) == 0x98)
 			*status |= FE_HAS_LOCK;
 
-		break;
+	return 0;
 	}
 
-        case FE_READ_BER:
-		if (stv0299_status == STATUS_BER) {
-		*((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8)
-			       | stv0299_readreg (i2c, 0x1e);
-		} else {
-			*((u32*) arg) = 0;
+static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	if (state->errmode != STATUS_BER) return 0;
+	*ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e);
+
+	return 0;
 		}
-		break;
 
-	case FE_READ_SIGNAL_STRENGTH:
+static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 	{
-		s32 signal =  0xffff - ((stv0299_readreg (i2c, 0x18) << 8)
-			               | stv0299_readreg (i2c, 0x19));
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	s32 signal =  0xffff - ((stv0299_readreg (state, 0x18) << 8)
+			       | stv0299_readreg (state, 0x19));
 
 		dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __FUNCTION__,
-			 stv0299_readreg (i2c, 0x18),
-			 stv0299_readreg (i2c, 0x19), (int) signal);
+		 stv0299_readreg (state, 0x18),
+		 stv0299_readreg (state, 0x19), (int) signal);
 
 		signal = signal * 5 / 4;
-		*((u16*) arg) = (signal > 0xffff) ? 0xffff :
-				(signal < 0) ? 0 : signal;
-		break;
-	}
-        case FE_READ_SNR:
-	{
-		s32 snr = 0xffff - ((stv0299_readreg (i2c, 0x24) << 8)
-			           | stv0299_readreg (i2c, 0x25));
-		snr = 3 * (snr - 0xa100);
-		*((u16*) arg) = (snr > 0xffff) ? 0xffff :
-				(snr < 0) ? 0 : snr;
-		break;
-	}
-	case FE_READ_UNCORRECTED_BLOCKS: 
-		if (stv0299_status == STATUS_UCBLOCKS) {
-			*((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8)
-			               | stv0299_readreg (i2c, 0x1e);
-		} else {
-			*((u32*) arg) = 0;
+	*strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
+
+	return 0;
 		}
-		break;
 
-        case FE_SET_FRONTEND:
+static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr)
         {
-		struct dvb_frontend_parameters *p = arg;
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8)
+			   | stv0299_readreg (state, 0x25));
+	xsnr = 3 * (xsnr - 0xa100);
+	*snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+
+	return 0;
+}
+
+static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+	if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0;
+	else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e);
+
+	return 0;
+}
+
+static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 		int invval = 0;
 
 		dprintk ("%s : FE_SET_FRONTEND\n", __FUNCTION__);
@@ -1151,12 +560,10 @@ static int uni0299_ioctl (struct dvb_fro
 			printk("stv0299 does not support auto-inversion\n");
 			return -EINVAL;
 		}
-		if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1;
-		stv0299_writereg(i2c, 0x0c, (stv0299_readreg(i2c, 0x0c) & 0xfe) | invval);
+	if (state->config->invert) invval = (~invval) & 1;
+	stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval);
 
-		switch(state->tuner_type) {
-		case PHILIPS_SU1278_TSA_TT: 
-		{
+	if (state->config->enhanced_tuning) {
 			/* check if we should do a finetune */
 			int frequency_delta = p->frequency - state->tuner_frequency;
 			int minmax = p->u.qpsk.symbol_rate / 2000;
@@ -1165,130 +572,88 @@ static int uni0299_ioctl (struct dvb_fro
 			if ((frequency_delta > -minmax) && (frequency_delta < minmax) && (frequency_delta != 0) &&
 			    (state->fec_inner == p->u.qpsk.fec_inner) && 
 			    (state->symbol_rate == p->u.qpsk.symbol_rate)) {
-				int Drot_freq = (frequency_delta << 16) / (M_CLK_SU1278_TSA_TT / 1000);
+			int Drot_freq = (frequency_delta << 16) / (state->config->mclk / 1000);
 
 				// zap the derotator registers first
-				stv0299_writereg (i2c, 0x22, 0x00); 
-				stv0299_writereg (i2c, 0x23, 0x00);
+			stv0299_writeregI(state, 0x22, 0x00);
+			stv0299_writeregI(state, 0x23, 0x00);
 
 				// now set them as we want
-				stv0299_writereg (i2c, 0x22, Drot_freq >> 8);
-				stv0299_writereg (i2c, 0x23, Drot_freq);
+			stv0299_writeregI(state, 0x22, Drot_freq >> 8);
+			stv0299_writeregI(state, 0x23, Drot_freq);
 			} else {
 				/* A "normal" tune is requested */
-				pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate);
-				stv0299_writereg (i2c, 0x32, 0x80);
-				stv0299_writereg (i2c, 0x22, 0x00);
-				stv0299_writereg (i2c, 0x23, 0x00);
-				stv0299_writereg (i2c, 0x32, 0x19);
-				stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type);
-                stv0299_set_FEC (i2c, p->u.qpsk.fec_inner);
-			}
-			break;
+			stv0299_writeregI(state, 0x05, 0xb5);	/*  enable i2c repeater on stv0299  */
+			state->config->pll_set(fe, p);
+			stv0299_writeregI(state, 0x05, 0x35);	/*  disable i2c repeater on stv0299  */
+
+			stv0299_writeregI(state, 0x32, 0x80);
+			stv0299_writeregI(state, 0x22, 0x00);
+			stv0299_writeregI(state, 0x23, 0x00);
+			stv0299_writeregI(state, 0x32, 0x19);
+			stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate);
+			stv0299_set_FEC (state, p->u.qpsk.fec_inner);
 		}
-		    
-		default:
-			pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate);
-			stv0299_set_FEC (i2c, p->u.qpsk.fec_inner);
-			stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type);
-		stv0299_writereg (i2c, 0x22, 0x00);
-		stv0299_writereg (i2c, 0x23, 0x00);
-		stv0299_readreg (i2c, 0x23);
-		stv0299_writereg (i2c, 0x12, 0xb9);
-			break;
+	} else {
+		stv0299_writeregI(state, 0x05, 0xb5);	/*  enable i2c repeater on stv0299  */
+		state->config->pll_set(fe, p);
+		stv0299_writeregI(state, 0x05, 0x35);	/*  disable i2c repeater on stv0299  */
+
+		stv0299_set_FEC (state, p->u.qpsk.fec_inner);
+		stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate);
+		stv0299_writeregI(state, 0x22, 0x00);
+		stv0299_writeregI(state, 0x23, 0x00);
+		stv0299_readreg (state, 0x23);
+		stv0299_writeregI(state, 0x12, 0xb9);
 		}
 
 		state->tuner_frequency = p->frequency;
 		state->fec_inner = p->u.qpsk.fec_inner;
 		state->symbol_rate = p->u.qpsk.symbol_rate;
-                break;
+
+	return 0;
         }
 
-        case FE_GET_FRONTEND:
+static int stv0299_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
         {
-		struct dvb_frontend_parameters *p = arg;
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 		s32 derot_freq;
-	        int Mclk = M_CLK;
 		int invval;
 
-		if (state->tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT;
+	derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8)
+				| stv0299_readreg (state, 0x23));
 
-		derot_freq = (s32)(s16) ((stv0299_readreg (i2c, 0x22) << 8)
-					| stv0299_readreg (i2c, 0x23));
-
-		derot_freq *= (Mclk >> 16);
+	derot_freq *= (state->config->mclk >> 16);
 		derot_freq += 500;
 		derot_freq /= 1000;
 
 		p->frequency += derot_freq;
 
-		invval = stv0299_readreg (i2c, 0x0c) & 1;
-		if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1;
+	invval = stv0299_readreg (state, 0x0c) & 1;
+	if (state->config->invert) invval = (~invval) & 1;
 		p->inversion = invval ? INVERSION_ON : INVERSION_OFF;
 
-		p->u.qpsk.fec_inner = stv0299_get_fec (i2c);
-		p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c, state->tuner_type);
-                break;
-        }
-
-        case FE_SLEEP:
-		stv0299_writereg (i2c, 0x0c, 0x00);  /*  LNB power off! */
-		stv0299_writereg (i2c, 0x08, 0x00); /*  LNB power off! */
-		stv0299_writereg (i2c, 0x02, 0x80);
-		state->initialised = 0;
-		break;
-
-        case FE_INIT:
-		switch(state->tuner_type) {
-		case PHILIPS_SU1278_TSA_TT:
-			state->tuner_frequency = 0;
-			if (!state->initialised) {
-				state->initialised = 1;
-				return stv0299_init (i2c, state->tuner_type);
-			}
-			break;
+	p->u.qpsk.fec_inner = stv0299_get_fec (state);
+	p->u.qpsk.symbol_rate = stv0299_get_symbolrate (state);
 
-		default:
-			return stv0299_init (i2c, state->tuner_type);
+	return 0;
 		}
-		break;
-
-	case FE_DISEQC_SEND_MASTER_CMD:
-		return stv0299_send_diseqc_msg (i2c, arg);
 
-	case FE_DISEQC_SEND_BURST:
-		return stv0299_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg);
-
-	case FE_SET_TONE:
-		return stv0299_set_tone (i2c, (fe_sec_tone_mode_t) arg);
+static int stv0299_sleep(struct dvb_frontend* fe)
+{
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 
-	case FE_SET_VOLTAGE:
-		return stv0299_set_voltage (i2c, (fe_sec_voltage_t) arg,
-					    state->tuner_type);
+	stv0299_writeregI(state, 0x02, 0x80);
+	state->initialised = 0;
 
-	case FE_DISHNETWORK_SEND_LEGACY_CMD:
-		return stv0299_send_legacy_dish_cmd (i2c,
-						    (u32)(unsigned long)arg,
-						     state->tuner_type);
+	return 0;
+}
 
-	case FE_GET_TUNE_SETTINGS:
+static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
 	{
-		struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
-	    
-		switch(state->tuner_type) {
-		case PHILIPS_SU1278_TSA_TT:
-			fesettings->min_delay_ms = 50;
-			if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) {
-				fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000;
-				fesettings->max_drift = 5000;
-			} else {
-				fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000;
-				fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000;
-			}
-			break;
+        struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
 
-		default:
-			fesettings->min_delay_ms = 100;
+	fesettings->min_delay_ms = state->config->min_delay_ms;
 			if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) {
 				fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000;
 				fesettings->max_drift = 5000;
@@ -1296,225 +661,103 @@ static int uni0299_ioctl (struct dvb_fro
 				fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000;
 				fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000;
 			}
-			break;		    
-		}
-
-		return 0;
-	}
-
-	default:
-		return -EOPNOTSUPP;
-	};
-
 	return 0;
 }
 
-static long probe_tuner (struct i2c_adapter *adapter)
-{
-	struct i2c_adapter *i2c = adapter; /* superfluous */
-
-        /* read the status register of TSA5059 */
-	u8 rpt[] = { 0x05, 0xb5 };
-        u8 stat [] = { 0 };
-	u8 tda6100_buf [] = { 0, 0 };
-	int ret;
-	struct i2c_msg msg1 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt,  .len = 2 },
-			   { .addr = 0x60, .flags = I2C_M_RD, .buf = stat, .len = 1 }};
-	struct i2c_msg msg2 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt,  .len = 2 },
-			   { .addr = 0x61, .flags = I2C_M_RD, .buf = stat, .len = 1 }};
-	struct i2c_msg msg3 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt,  .len = 2 },
-			   { .addr = 0x60, .flags = 0, .buf = tda6100_buf, .len = 2 }};
-
-	stv0299_writereg (i2c, 0x01, 0x15);
-	stv0299_writereg (i2c, 0x02, 0x30);
-	stv0299_writereg (i2c, 0x03, 0x00);
-
-
-	printk("stv0299: try to attach to %s\n", adapter->name);
-
-	if (!strcmp(adapter->name, "SkyStar2")) {
-	    printk ("stv0299: setup for tuner Samsung TBMU24112IMB\n");
-    	    return SAMSUNG_TBMU24112IMB;
-	}
-
-	if ((ret = i2c_transfer(i2c, msg1, 2)) == 2) {
-	        if ( strcmp(adapter->name, "TT-Budget/WinTV-NOVA-CI PCI") == 0 ) {
-		        // technotrend cards require non-datasheet settings
-			printk ("stv0299: setup for tuner SU1278 (TSA5059 synth) on TechnoTrend hardware\n");
-		        return PHILIPS_SU1278_TSA_TT;
-		}  else {
-		        // fall back to datasheet-recommended settings
-			printk ("stv0299: setup for tuner SU1278 (TSA5059 synth)\n");
-		        return PHILIPS_SU1278_TSA;
-		}
-		}
-
-	if ((ret = i2c_transfer(i2c, msg2, 2)) == 2) {
-		if ( strcmp(adapter->name, "KNC1 DVB-S") == 0 )
+static void stv0299_release(struct dvb_frontend* fe)
 		{
-			// Typhoon cards have unusual wiring.
-			printk ("stv0299: setup for tuner SU1278 (TSA5059 synth) on Typhoon hardware\n");
-			return PHILIPS_SU1278_TSA_TY;
-		}
-		else if ( strcmp(adapter->name, "TerraTec Cinergy 1200 DVB-S") == 0 )
-		{
-			// Cinergy cards have unusual wiring.
-			printk ("%s: setup for tuner SU1278 (TSA5059 synth) on"
-				" TerraTec hardware\n", __FILE__);
-			return PHILIPS_SU1278_TSA_CI;
-		}
-		//else if ((stat[0] & 0x3f) == 0) {
-		else if (0) {
-			printk ("stv0299: setup for tuner TDQF-S001F\n");
-			return LG_TDQF_S001F;
-	} else {
-			printk ("stv0299: setup for tuner BSRU6, TDQB-S00x\n");
-			return ALPS_BSRU6;
-	}
-	}
-
-	/**
-	 *  setup i2c timing for SU1278...
-	 */
-	stv0299_writereg (i2c, 0x02, 0x00);
-
-	if ((ret = i2c_transfer(i2c, msg3, 2)) == 2) {
-		printk ("stv0299: setup for tuner Philips SU1278 (TUA6100 synth)\n");
-		return PHILIPS_SU1278_TUA;
-	}
-
-	printk ("stv0299: unknown PLL synthesizer (ret == %i), please report to <linuxdvb@linuxtv.org>!!\n", ret);
-
-	return UNKNOWN_FRONTEND;
+	struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+	kfree(state);
 }
 
-static struct i2c_client client_template;
+static struct dvb_frontend_ops stv0299_ops;
 
-static int attach_adapter(struct i2c_adapter *adapter)
+struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+				    struct i2c_adapter* i2c)
 {
-	struct i2c_client *client;
-	struct stv0299_state* state;
-	int tuner_type;
-	int ret;
-	u8 id;
+	struct stv0299_state* state = NULL;
+	int id;
  
-	stv0299_writereg(adapter, 0x02, 0x34); /* standby off */
-	msleep(200);
-	id = stv0299_readreg(adapter, 0x00);
+	/* allocate memory for the internal state */
+	state = (struct stv0299_state*) kmalloc(sizeof(struct stv0299_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &stv0299_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+	state->tuner_frequency = 0;
+	state->symbol_rate = 0;
+	state->fec_inner = 0;
+	state->errmode = STATUS_BER;
 
-	dprintk ("%s: id == 0x%02x\n", __FUNCTION__, id);
+	/* check if the demod is there */
+	stv0299_writeregI(state, 0x02, 0x34); /* standby off */
+	msleep(200);
+	id = stv0299_readreg(state, 0x00);
 
 	/* register 0x00 contains 0xa1 for STV0299 and STV0299B */
 	/* register 0x00 might contain 0x80 when returning from standby */
-	if (id != 0xa1 && id != 0x80)
-		return -ENODEV;
-
-	if ((tuner_type = probe_tuner(adapter)) < 0)
-		return -ENODEV;
-
-	if ((state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL)) == NULL) {
-		return -ENOMEM;
-	}
-
-	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
-		kfree(state);
-		return -ENOMEM;
-	}
-
-	state->tuner_type = tuner_type;
-	state->tuner_frequency = 0;
-	state->initialised = 0;
-	state->i2c = adapter;
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = (0x68>>1);
-	i2c_set_clientdata(client, (void*)state);
-
-	ret = i2c_attach_client(client);
-	if (ret) {
-		kfree(client);
-		kfree(state);
-		return -EFAULT;
-	}
-
-	BUG_ON(!state->dvb);
-
-	ret = dvb_register_frontend(uni0299_ioctl, state->dvb, state,
-					&uni0299_info, THIS_MODULE);
-	if (ret) {
-		i2c_detach_client(client);
-		kfree(client);
-		kfree(state);
-		return -EFAULT;
-}
+	if (id != 0xa1 && id != 0x80) goto error;
 
-	return 0;
-}
-
-static int detach_client(struct i2c_client *client)
-{
-	struct stv0299_state *state = (struct stv0299_state*)i2c_get_clientdata(client);
-
-	dvb_unregister_frontend (uni0299_ioctl, state->dvb);
-	i2c_detach_client(client);
-	kfree(client);
-	kfree(state);
-	return 0;
-}
-
-static int command (struct i2c_client *client, unsigned int cmd, void *arg)
-{
-	struct stv0299_state *data = (struct stv0299_state*)i2c_get_clientdata(client);
-	dprintk ("%s\n", __FUNCTION__);
-
-	switch (cmd) {
-	case FE_REGISTER: {
-		data->dvb = (struct dvb_adapter*)arg;
-		break;
-	}
-	case FE_UNREGISTER: {
-		data->dvb = NULL;
-		break;
-	}
-	default:
-		return -EOPNOTSUPP;
-	}
-	return 0;
-}
-
-static struct i2c_driver driver = {
-	.owner 		= THIS_MODULE,
-	.name 		= FRONTEND_NAME,
-	.id 		= I2C_DRIVERID_DVBFE_STV0299,
-	.flags 		= I2C_DF_NOTIFY,
-	.attach_adapter = attach_adapter,
-	.detach_client 	= detach_client,
-	.command 	= command,
-};
-
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags 		= I2C_CLIENT_ALLOW_USE,
-	.driver  	= &driver,
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+        state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops stv0299_ops = {
+
+	.info = {
+		.name			= "ST STV0299 DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 125,	 /* kHz for QPSK frontends */
+		.frequency_tolerance	= 0,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+		.symbol_rate_tolerance	= 500,	/* ppm */
+		.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		      FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+		      FE_CAN_QPSK |
+		      FE_CAN_FEC_AUTO
+	},
+
+	.release = stv0299_release,
+
+	.init = stv0299_init,
+	.sleep = stv0299_sleep,
+
+	.set_frontend = stv0299_set_frontend,
+	.get_frontend = stv0299_get_frontend,
+	.get_tune_settings = stv0299_get_tune_settings,
+
+	.read_status = stv0299_read_status,
+	.read_ber = stv0299_read_ber,
+	.read_signal_strength = stv0299_read_signal_strength,
+	.read_snr = stv0299_read_snr,
+	.read_ucblocks = stv0299_read_ucblocks,
+
+	.diseqc_send_master_cmd = stv0299_send_diseqc_msg,
+	.diseqc_send_burst = stv0299_send_diseqc_burst,
+	.set_tone = stv0299_set_tone,
+	.set_voltage = stv0299_set_voltage,
+	.dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd,
 };
 
-static int __init init_uni0299 (void)
-{
-	return i2c_add_driver(&driver);
-}
-
-static void __exit exit_uni0299 (void)
-{
-	if (i2c_del_driver(&driver))
-		printk("stv0299: driver deregistration failed\n");
-}
-
-module_init (init_uni0299);
-module_exit (exit_uni0299);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-MODULE_DESCRIPTION("Universal STV0299/TSA5059/SL1935 DVB Frontend driver");
+MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver");
 MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, "
               "Andreas Oberritter, Andrew de Quincey, Kenneth Aafløy");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(stv0299_writereg);
+EXPORT_SYMBOL(stv0299_attach);
diff -puN /dev/null drivers/media/dvb/frontends/stv0299.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/stv0299.h	2004-11-21 17:13:23.484048264 -0800
@@ -0,0 +1,104 @@
+/*
+    Driver for ST STV0299 demodulator
+
+    Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+	<ralph@convergence.de>,
+	<holger@convergence.de>,
+	<js@convergence.de>
+
+
+    Philips SU1278/SH
+
+    Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de>
+
+
+    LG TDQF-S001F
+
+    Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net>
+		     & Andreas Oberritter <obi@linuxtv.org>
+
+
+    Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B
+
+    Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>:
+
+    Support for Philips SU1278 on Technotrend hardware
+
+    Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef STV0299_H
+#define STV0299_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#define STV0229_LOCKOUTPUT_0  0
+#define STV0229_LOCKOUTPUT_1  1
+#define STV0229_LOCKOUTPUT_CF 2
+#define STV0229_LOCKOUTPUT_LK 3
+
+#define STV0299_VOLT13_OP0 0
+#define STV0299_VOLT13_OP1 1
+
+struct stv0299_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* inittab - array of pairs of values.
+	 * First of each pair is the register, second is the value.
+	 * List should be terminated with an 0xff, 0xff pair.
+	 */
+	u8* inittab;
+
+	/* master clock to use */
+	u32 mclk;
+
+	/* does the inversion require inversion? */
+	u8 invert:1;
+
+	/* Should the enhanced tuning code be used? */
+	u8 enhanced_tuning:1;
+
+	/* Skip reinitialisation? */
+	u8 skip_reinit:1;
+
+	/* LOCK OUTPUT setting */
+	u8 lock_output:2;
+
+	/* Is 13v controlled by OP0 or OP1? */
+	u8 volt13_op0_op1:1;
+
+	/* minimum delay before retuning */
+	int min_delay_ms;
+
+	/* Set the symbol rate */
+	int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data);
+
+extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // STV0299_H
diff -puN /dev/null drivers/media/dvb/frontends/tda10021.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/tda10021.c	2004-11-21 17:13:23.486047960 -0800
@@ -0,0 +1,483 @@
+/*
+    TDA10021  - Single Chip Cable Channel Receiver driver module
+               used on the the Siemens DVB-C cards
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+                   Suppport for TDA10021
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda10021.h"
+
+
+struct tda10021_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct tda10021_config* config;
+
+	struct dvb_frontend frontend;
+
+	u8 pwm;
+	u8 reg0;
+};
+
+
+#if 0
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+static int verbose;
+
+#define XIN 57840000UL
+#define DISABLE_INVERSION(reg0)		do { reg0 |= 0x20; } while (0)
+#define ENABLE_INVERSION(reg0)		do { reg0 &= ~0x20; } while (0)
+#define HAS_INVERSION(reg0)		(!(reg0 & 0x20))
+
+#define FIN (XIN >> 4)
+
+int tda10021_inittab_size = 0x40;
+static u8 tda10021_inittab[0x40]=
+{
+	0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
+	0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
+	0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
+	0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
+	0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
+	0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
+	0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
+	0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
+};
+
+static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
+{
+        u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+        int ret;
+
+	ret = i2c_transfer (state->i2c, &msg, 1);
+	if (ret != 1)
+		printk("DVB: TDA10021(%d): %s, writereg error "
+			"(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+			state->frontend.dvb->num, __FUNCTION__, reg, data, ret);
+
+	msleep(10);
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+
+static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
+{
+	u8 b0 [] = { reg };
+	u8 b1 [] = { 0 };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+	                          { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+	int ret;
+
+	ret = i2c_transfer (state->i2c, msg, 2);
+	if (ret != 2)
+		printk("DVB: TDA10021(%d): %s: readreg error (ret == %i)\n",
+				state->frontend.dvb->num, __FUNCTION__, ret);
+	return b1[0];
+}
+
+//get access to tuner
+static int lock_tuner(struct tda10021_state* state)
+{
+	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
+	struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+	if(i2c_transfer(state->i2c, &msg, 1) != 1)
+	{
+		printk("tda10021: lock tuner fails\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+//release access from tuner
+static int unlock_tuner(struct tda10021_state* state)
+{
+	u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
+	struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+	if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
+	{
+		printk("tda10021: unlock tuner fails\n");
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0,
+				fe_spectral_inversion_t inversion)
+{
+	reg0 |= state->reg0 & 0x63;
+
+	if (INVERSION_ON == inversion)
+		ENABLE_INVERSION(reg0);
+	else if (INVERSION_OFF == inversion)
+		DISABLE_INVERSION(reg0);
+
+	tda10021_writereg (state, 0x00, reg0 & 0xfe);
+	tda10021_writereg (state, 0x00, reg0 | 0x01);
+
+	state->reg0 = reg0;
+	return 0;
+}
+
+static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate)
+{
+	s32 BDR;
+	s32 BDRI;
+	s16 SFIL=0;
+	u16 NDEC = 0;
+	u32 tmp, ratio;
+
+	if (symbolrate > XIN/2)
+		symbolrate = XIN/2;
+	if (symbolrate < 500000)
+		symbolrate = 500000;
+
+	if (symbolrate < XIN/16) NDEC = 1;
+	if (symbolrate < XIN/32) NDEC = 2;
+	if (symbolrate < XIN/64) NDEC = 3;
+
+	if (symbolrate < (u32)(XIN/12.3)) SFIL = 1;
+	if (symbolrate < (u32)(XIN/16))	 SFIL = 0;
+	if (symbolrate < (u32)(XIN/24.6)) SFIL = 1;
+	if (symbolrate < (u32)(XIN/32))	 SFIL = 0;
+	if (symbolrate < (u32)(XIN/49.2)) SFIL = 1;
+	if (symbolrate < (u32)(XIN/64))	 SFIL = 0;
+	if (symbolrate < (u32)(XIN/98.4)) SFIL = 1;
+
+	symbolrate <<= NDEC;
+	ratio = (symbolrate << 4) / FIN;
+	tmp =  ((symbolrate << 4) % FIN) << 8;
+	ratio = (ratio << 8) + tmp / FIN;
+	tmp = (tmp % FIN) << 8;
+	ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
+
+	BDR = ratio;
+	BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
+
+	if (BDRI > 0xFF)
+		BDRI = 0xFF;
+
+	SFIL = (SFIL << 4) | tda10021_inittab[0x0E];
+
+	NDEC = (NDEC << 6) | tda10021_inittab[0x03];
+
+	tda10021_writereg (state, 0x03, NDEC);
+	tda10021_writereg (state, 0x0a, BDR&0xff);
+	tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
+	tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
+
+	tda10021_writereg (state, 0x0d, BDRI);
+	tda10021_writereg (state, 0x0e, SFIL);
+
+	return 0;
+}
+
+
+
+
+
+
+
+
+
+
+static int tda10021_init (struct dvb_frontend *fe)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	int i;
+
+	dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
+
+	//tda10021_writereg (fe, 0, 0);
+
+	for (i=0; i<tda10021_inittab_size; i++)
+		tda10021_writereg (state, i, tda10021_inittab[i]);
+
+	tda10021_writereg (state, 0x34, state->pwm);
+
+	//Comment by markus
+	//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
+	//0x2A[4] == BYPPLL -> Power down mode (default 1)
+	//0x2A[5] == LCK -> PLL Lock Flag
+	//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
+
+	//Activate PLL
+	tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
+
+	if (state->config->pll_init) {
+		lock_tuner(state);
+		state->config->pll_init(fe);
+		unlock_tuner(state);
+	}
+
+	return 0;
+}
+
+static int tda10021_set_parameters (struct dvb_frontend *fe,
+			    struct dvb_frontend_parameters *p)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	//table for QAM4-QAM256 ready  QAM4  QAM16 QAM32 QAM64 QAM128 QAM256
+	//CONF
+	static const u8 reg0x00 [] = { 0x14, 0x00, 0x04, 0x08, 0x0c,  0x10 };
+	//AGCREF value
+	static const u8 reg0x01 [] = { 0x78, 0x8c, 0x8c, 0x6a, 0x78,  0x5c };
+	//LTHR value
+	static const u8 reg0x05 [] = { 0x78, 0x87, 0x64, 0x46, 0x36,  0x26 };
+	//MSETH
+	static const u8 reg0x08 [] = { 0x8c, 0xa2, 0x74, 0x43, 0x34,  0x23 };
+	//AREF
+	static const u8 reg0x09 [] = { 0x96, 0x91, 0x96, 0x6a, 0x7e,  0x6b };
+
+	int qam = p->u.qam.modulation;
+
+	if (qam < 0 || qam > 5)
+		return -EINVAL;
+
+	//printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate);
+
+	lock_tuner(state);
+	state->config->pll_set(fe, p);
+	unlock_tuner(state);
+
+	tda10021_set_symbolrate (state, p->u.qam.symbol_rate);
+	tda10021_writereg (state, 0x34, state->pwm);
+
+	tda10021_writereg (state, 0x01, reg0x01[qam]);
+	tda10021_writereg (state, 0x05, reg0x05[qam]);
+	tda10021_writereg (state, 0x08, reg0x08[qam]);
+	tda10021_writereg (state, 0x09, reg0x09[qam]);
+
+	tda10021_setup_reg0 (state, reg0x00[qam], p->inversion);
+
+	return 0;
+}
+
+static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	int sync;
+
+	*status = 0;
+	//0x11[0] == EQALGO -> Equalizer algorithms state
+	//0x11[1] == CARLOCK -> Carrier locked
+	//0x11[2] == FSYNC -> Frame synchronisation
+	//0x11[3] == FEL -> Front End locked
+	//0x11[6] == NODVB -> DVB Mode Information
+	sync = tda10021_readreg (state, 0x11);
+
+	if (sync & 2)
+		*status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
+
+	if (sync & 4)
+		*status |= FE_HAS_SYNC|FE_HAS_VITERBI;
+
+	if (sync & 8)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	u32 _ber = tda10021_readreg(state, 0x14) |
+     		  (tda10021_readreg(state, 0x15) << 8) |
+		 ((tda10021_readreg(state, 0x16) & 0x0f) << 16);
+	*ber = 10 * _ber;
+
+	return 0;
+}
+
+static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	u8 gain = tda10021_readreg(state, 0x17);
+	*strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	u8 quality = ~tda10021_readreg(state, 0x18);
+	*snr = (quality << 8) | quality;
+
+	return 0;
+}
+
+static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	*ucblocks = tda10021_readreg (state, 0x13) & 0x7f;
+	if (*ucblocks == 0x7f)
+		*ucblocks = 0xffffffff;
+
+	/* reset uncorrected block counter */
+	tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
+	tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
+
+	return 0;
+}
+
+static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	int sync;
+	s8 afc = 0;
+
+	sync = tda10021_readreg(state, 0x11);
+	afc = tda10021_readreg(state, 0x19);
+	if (verbose) {
+		/* AFC only valid when carrier has been recovered */
+		printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" :
+				  "DVB: TDA10021(%d): [AFC (%d) %dHz]\n",
+			state->frontend.dvb->num, afc,
+		       -((s32)p->u.qam.symbol_rate * afc) >> 10);
+	}
+
+	p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
+	p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+	p->u.qam.fec_inner = FEC_NONE;
+	p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+
+	if (sync & 2)
+		p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10;
+
+	return 0;
+}
+
+static int tda10021_sleep(struct dvb_frontend* fe)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+	tda10021_writereg (state, 0x1b, 0x02);  /* pdown ADC */
+	tda10021_writereg (state, 0x00, 0x80);  /* standby */
+
+	return 0;
+}
+
+static void tda10021_release(struct dvb_frontend* fe)
+{
+	struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda10021_ops;
+
+struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+				     struct i2c_adapter* i2c,
+				     u8 pwm)
+{
+	struct tda10021_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda10021_state*) kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
+	state->pwm = pwm;
+	state->reg0 = tda10021_inittab[0];
+
+	/* check if the demod is there */
+	if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda10021_ops = {
+
+	.info = {
+		.name = "Philips TDA10021 DVB-C",
+		.type = FE_QAM,
+		.frequency_stepsize = 62500,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.symbol_rate_min = (XIN/2)/64,     /* SACLK/64 == (XIN/2)/64 */
+		.symbol_rate_max = (XIN/2)/4,      /* SACLK/4 */
+	#if 0
+		.frequency_tolerance = ???,
+		.symbol_rate_tolerance = ???,  /* ppm */  /* == 8% (spec p. 5) */
+	#endif
+		.caps = 0x400 | //FE_CAN_QAM_4
+			FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+			FE_CAN_FEC_AUTO
+	},
+
+	.release = tda10021_release,
+
+	.init = tda10021_init,
+	.sleep = tda10021_sleep,
+
+	.set_frontend = tda10021_set_parameters,
+	.get_frontend = tda10021_get_frontend,
+
+	.read_status = tda10021_read_status,
+	.read_ber = tda10021_read_ber,
+	.read_signal_strength = tda10021_read_signal_strength,
+	.read_snr = tda10021_read_snr,
+	.read_ucblocks = tda10021_read_ucblocks,
+};
+
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
+
+MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10021_attach);
diff -puN /dev/null drivers/media/dvb/frontends/tda10021.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/tda10021.h	2004-11-21 17:13:23.487047808 -0800
@@ -0,0 +1,43 @@
+/*
+    TDA10021  - Single Chip Cable Channel Receiver driver module
+               used on the the Siemens DVB-C cards
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+                   Suppport for TDA10021
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef TDA10021_H
+#define TDA10021_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda10021_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+					    struct i2c_adapter* i2c,
+					    u8 pwm);
+
+#endif // TDA10021_H
diff -puN drivers/media/dvb/frontends/tda1004x.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/tda1004x.c
--- 25/drivers/media/dvb/frontends/tda1004x.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.396061640 -0800
+++ 25-akpm/drivers/media/dvb/frontends/tda1004x.c	2004-11-21 17:13:23.497046288 -0800
@@ -1,5 +1,5 @@
   /*
-     Driver for Philips tda1004xh OFDM Frontend
+     Driver for Philips tda1004xh OFDM Demodulator
 
      (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach
 
@@ -32,23 +32,35 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/device.h>
-#include <linux/firmware.h>
-
 #include "dvb_frontend.h"
+#include "tda1004x.h"
 
-#define FRONTEND_NAME "dvbfe_tda1004x"
+#define TDA1004X_DEMOD_TDA10045 0
+#define TDA1004X_DEMOD_TDA10046 1
 
-#define dprintk(args...) \
-	do { \
-		if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \
-	} while (0)
 
-static int debug;
+struct tda1004x_state
+{
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	const struct tda1004x_config* config;
+
+	struct dvb_frontend frontend;
+
+	/* private demod data */
+	u8 initialised:1;
+
+	u8 demod_type;
+};
 
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-#define MC44BC374_ADDRESS	 0x65
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "tda1004x: " args); \
+	} while (0)
 
 #define TDA1004X_CHIPID		 0x00
 #define TDA1004X_AUTO		 0x01
@@ -128,53 +140,7 @@ MODULE_PARM_DESC(debug, "Turn on/off fro
 #define TDA10046H_CODE_IN	 0x58
 
 
-#define FE_TYPE_TDA10045H     0
-#define FE_TYPE_TDA10046H     1
-
-#define TUNER_TYPE_TD1344     0
-#define TUNER_TYPE_TD1316     1
-
-static struct dvb_frontend_info tda10045h_info = {
-	.name = "Philips TDA10045H",
-	.type = FE_OFDM,
-	.frequency_min = 51000000,
-	.frequency_max = 858000000,
-	.frequency_stepsize = 166667,
-	.caps =
-	    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-	    FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-	    FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
-	    FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
-};
-
-static struct dvb_frontend_info tda10046h_info = {
-	.name = "Philips TDA10046H",
-	.type = FE_OFDM,
-	.frequency_min = 51000000,
-	.frequency_max = 858000000,
-	.frequency_stepsize = 166667,
-	.caps =
-	    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-	    FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-	    FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
-	    FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
-};
-
-struct tda1004x_state {
-	u8 tda1004x_address;
-	u8 tuner_address;
-	u8 initialised;
-	u8 tuner_type;
-	u8 fe_type;
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-
-	int dspCodeCounterReg;
-	int dspCodeInReg;
-	int dspVersion;
-};
-
-static int tda1004x_write_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int data)
+static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data)
 {
 	int ret;
 	u8 buf[] = { reg, data };
@@ -182,8 +148,8 @@ static int tda1004x_write_byte(struct i2
 
 	dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data);
 
-	msg.addr = tda_state->tda1004x_address;
-	ret = i2c_transfer(i2c, &msg, 1);
+	msg.addr = state->config->demod_address;
+	ret = i2c_transfer(state->i2c, &msg, 1);
 
 	if (ret != 1)
 		dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
@@ -194,7 +160,7 @@ static int tda1004x_write_byte(struct i2
 	return (ret != 1) ? -1 : 0;
 }
 
-static int tda1004x_read_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg)
+static int tda1004x_read_byte(struct tda1004x_state *state, int reg)
 {
 	int ret;
 	u8 b0[] = { reg };
@@ -204,9 +170,9 @@ static int tda1004x_read_byte(struct i2c
 
 	dprintk("%s: reg=0x%x\n", __FUNCTION__, reg);
 
-	msg[0].addr = tda_state->tda1004x_address;
-	msg[1].addr = tda_state->tda1004x_address;
-	ret = i2c_transfer(i2c, msg, 2);
+	msg[0].addr = state->config->demod_address;
+	msg[1].addr = state->config->demod_address;
+	ret = i2c_transfer(state->i2c, msg, 2);
 
 	if (ret != 2) {
 		dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg,
@@ -219,14 +185,14 @@ static int tda1004x_read_byte(struct i2c
 	return b1[0];
 }
 
-static int tda1004x_write_mask(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int mask, int data)
+static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data)
 {
 	int val;
 	dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg,
 		mask, data);
 
 	// read a byte and check
-	val = tda1004x_read_byte(i2c, tda_state, reg);
+	val = tda1004x_read_byte(state, reg);
 	if (val < 0)
 		return val;
 
@@ -235,10 +201,10 @@ static int tda1004x_write_mask(struct i2
 	val |= data & 0xff;
 
 	// write it out again
-	return tda1004x_write_byte(i2c, tda_state, reg, val);
+	return tda1004x_write_byteI(state, reg, val);
 }
 
-static int tda1004x_write_buf(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, unsigned char *buf, int len)
+static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len)
 {
 	int i;
 	int result;
@@ -247,7 +213,7 @@ static int tda1004x_write_buf(struct i2c
 
 	result = 0;
 	for (i = 0; i < len; i++) {
-		result = tda1004x_write_byte(i2c, tda_state, reg + i, buf[i]);
+		result = tda1004x_write_byteI(state, reg + i, buf[i]);
 		if (result != 0)
 			break;
 	}
@@ -255,25 +221,24 @@ static int tda1004x_write_buf(struct i2c
 	return result;
 }
 
-static int tda1004x_enable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
+static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state)
 {
 	int result;
 	dprintk("%s\n", __FUNCTION__);
 
-	result = tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 2);
+	result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2);
 	msleep(1);
 	return result;
 }
 
-static int tda1004x_disable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
+static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state)
 {
 	dprintk("%s\n", __FUNCTION__);
 
-	return tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 0);
+	return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0);
 }
 
-static int tda10045h_set_bandwidth(struct i2c_adapter *i2c,
-				   struct tda1004x_state *tda_state,
+static int tda10045h_set_bandwidth(struct tda1004x_state *state,
 				   fe_bandwidth_t bandwidth)
 {
 	static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f };
@@ -282,31 +247,27 @@ static int tda10045h_set_bandwidth(struc
 
 	switch (bandwidth) {
 	case BANDWIDTH_6_MHZ:
-		tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14);
-		tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
 		break;
 
 	case BANDWIDTH_7_MHZ:
-		tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x80);
-		tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
 		break;
 
 	case BANDWIDTH_8_MHZ:
-		tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14);
-		tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+		tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
 		break;
 
 	default:
 		return -EINVAL;
 	}
 
-	tda1004x_write_byte(i2c, tda_state, TDA10045H_IOFFSET, 0);
+	tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0);
 
 	return 0;
 }
 
-static int tda10046h_set_bandwidth(struct i2c_adapter *i2c,
-				   struct tda1004x_state *tda_state,
+static int tda10046h_set_bandwidth(struct tda1004x_state *state,
 				   fe_bandwidth_t bandwidth)
 {
 	static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e };
@@ -315,18 +276,15 @@ static int tda10046h_set_bandwidth(struc
 
 	switch (bandwidth) {
 	case BANDWIDTH_6_MHZ:
-		tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));
-		tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0);
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));
 		break;
 
 	case BANDWIDTH_7_MHZ:
-		tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));
-		tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0);
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));
 		break;
 
 	case BANDWIDTH_8_MHZ:
-		tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));
-		tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0xFF);
+		tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));
 		break;
 
 	default:
@@ -336,7 +294,9 @@ static int tda10046h_set_bandwidth(struc
 	return 0;
 }
 
-static int tda1004x_do_upload(struct i2c_adapter *i2c, struct tda1004x_state *state, unsigned char *mem, unsigned int len)
+static int tda1004x_do_upload(struct tda1004x_state *state,
+			      unsigned char *mem, unsigned int len,
+			      u8 dspCodeCounterReg, u8 dspCodeInReg)
 {
 	u8 buf[65];
 	struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 };
@@ -344,10 +304,10 @@ static int tda1004x_do_upload(struct i2c
 	int pos = 0;
 
 	/* clear code counter */
-	tda1004x_write_byte(i2c, state, state->dspCodeCounterReg, 0);
-	fw_msg.addr = state->tda1004x_address;
+	tda1004x_write_byteI(state, dspCodeCounterReg, 0);
+	fw_msg.addr = state->config->demod_address;
 
-	buf[0] = state->dspCodeInReg;
+	buf[0] = dspCodeInReg;
 	while (pos != len) {
 
 		// work out how much to send this time
@@ -359,7 +319,7 @@ static int tda1004x_do_upload(struct i2c
 		// send the chunk
 		memcpy(buf + 1, mem + pos, tx_size);
 		fw_msg.len = tx_size + 1;
-		if (i2c_transfer(i2c, &fw_msg, 1) != 1) {
+		if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
 			printk("tda1004x: Error during firmware upload\n");
 			return -EIO;
 		}
@@ -370,18 +330,17 @@ static int tda1004x_do_upload(struct i2c
 	return 0;
 }
 
-static int tda1004x_check_upload_ok(struct i2c_adapter *i2c, struct tda1004x_state *state)
+static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion)
 {
 	u8 data1, data2;
 
 	// check upload was OK
-	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
-	tda1004x_write_byte(i2c, state, TDA1004X_DSP_CMD, 0x67);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
+	tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67);
 
-	data1 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA1);
-	data2 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA2);
-	if (data1 != 0x67 || data2 != state->dspVersion) {
-		printk("tda1004x: firmware upload failed!\n");
+	data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);
+	data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);
+	if (data1 != 0x67 || data2 != dspVersion) {
 		return -EIO;
 	}
 
@@ -389,31 +348,34 @@ static int tda1004x_check_upload_ok(stru
 }
 
 
-static int tda10045_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, struct i2c_client *client)
+static int tda10045_fwupload(struct dvb_frontend* fe)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	int ret;
 	const struct firmware *fw;
 
+
+	/* don't re-upload unless necessary */
+	if (tda1004x_check_upload_ok(state, 0x2c) == 0) return 0;
+
 	/* request the firmware, this will block until someone uploads it */
 	printk("tda1004x: waiting for firmware upload...\n");
-	ret = request_firmware(&fw, TDA10045_DEFAULT_FIRMWARE, &client->dev);
+	ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
 	if (ret) {
 		printk("tda1004x: no firmware upload (timeout or file not found?)\n");
 	   	return ret;
 	}
 
-	/* set some valid bandwith parameters before uploading */
-
 	/* reset chip */
-	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0);
-	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8);
-	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 0);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
 	msleep(10);
 
 	/* set parameters */
-	tda10045h_set_bandwidth(i2c, state, BANDWIDTH_8_MHZ);
+	tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ);
 
-	ret = tda1004x_do_upload(i2c, state, fw->data, fw->size);
+	ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);
 	if (ret)
 		return ret;
 
@@ -421,49 +383,47 @@ static int tda10045_fwupload(struct i2c_
 	/* DSPREADY doesn't seem to work on the TDA10045H */
 	msleep(100);
 
-	ret = tda1004x_check_upload_ok(i2c, state);
-	if (ret)
-		return ret;
-
-	return 0;
+	return tda1004x_check_upload_ok(state, 0x2c);
 }
 
-static int tda10046_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, struct i2c_client *client)
+static int tda10046_fwupload(struct dvb_frontend* fe)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	unsigned long timeout;
 	int ret;
 	const struct firmware *fw;
 
+	/* don't re-upload unless necessary */
+	if (tda1004x_check_upload_ok(state, 0x20) == 0) return 0;
+
 	/* request the firmware, this will block until someone uploads it */
 	printk("tda1004x: waiting for firmware upload...\n");
-	ret = request_firmware(&fw, TDA10046_DEFAULT_FIRMWARE, &client->dev);
+	ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
 	if (ret) {
 		printk("tda1004x: no firmware upload (timeout or file not found?)\n");
    	   	return ret;
 	}
 
-	/* set some valid bandwith parameters before uploading */
-
 	/* reset chip */
-	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 1, 0);
-	tda1004x_write_mask(i2c, state, TDA10046H_CONF_TRISTATE1, 1, 0);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0);
+	tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
 	msleep(10);
 
 	/* set parameters */
-	tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL2, 10);
-	tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL3, 0);
-	tda1004x_write_byte(i2c, state, TDA10046H_FREQ_OFFSET, 99);
-	tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
-	tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
-	tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10);
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0);
+	tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99);
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
 
-	ret = tda1004x_do_upload(i2c, state, fw->data, fw->size);
+	ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
 	if (ret)
 		return ret;
 
 	/* wait for DSP to initialise */
 	timeout = jiffies + HZ;
-	while(!(tda1004x_read_byte(i2c, state, TDA1004X_STATUS_CD) & 0x20)) {
+	while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
 		if (time_after(jiffies, timeout)) {
 			printk("tda1004x: DSP failed to initialised.\n");
 			return -EIO;
@@ -471,95 +431,7 @@ static int tda10046_fwupload(struct i2c_
 		msleep(1);
 	}
 
-	ret = tda1004x_check_upload_ok(i2c, state);
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
-static int tda10045h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
-{
-	struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = NULL,.len = 0 };
-	static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
-
-	dprintk("%s\n", __FUNCTION__);
-
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
-
-	// Disable the MC44BC374C
-	tda1004x_enable_tuner_i2c(i2c, tda_state);
-	tuner_msg.addr = MC44BC374_ADDRESS;
-	tuner_msg.buf = disable_mc44BC374c;
-	tuner_msg.len = sizeof(disable_mc44BC374c);
-	if (i2c_transfer(i2c, &tuner_msg, 1) != 1) {
-		i2c_transfer(i2c, &tuner_msg, 1);
-	}
-	tda1004x_disable_tuner_i2c(i2c, tda_state);
-
-	// tda setup
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0); // no frequency inversion
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
-	tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
-	tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
-	tda1004x_write_byte(i2c, tda_state, TDA1004X_CONFADC1, 0x2e);
-
-	return 0;
-}
-
-static int tda10046h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state)
-{
-	struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = NULL,.len = 0 };
-	static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
-
-	dprintk("%s\n", __FUNCTION__);
-
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 0); // wake up the chip
-
-	// Disable the MC44BC374C
-	tda1004x_enable_tuner_i2c(i2c, tda_state);
-	tuner_msg.addr = MC44BC374_ADDRESS;
-	tuner_msg.buf = disable_mc44BC374c;
-	tuner_msg.len = sizeof(disable_mc44BC374c);
-	if (i2c_transfer(i2c, &tuner_msg, 1) != 1) {
-		i2c_transfer(i2c, &tuner_msg, 1);
-	}
-	tda1004x_disable_tuner_i2c(i2c, tda_state);
-
-	// tda setup
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0x40); // TT TDA10046H needs inversion ON
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10); // PLL M = 10
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // }
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_CONF, 0); // AGC setup
-	tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MIN, 0);	  // }
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MIN, 0);	  // }
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MAX, 0xff);  // }
-	tda1004x_write_mask(i2c, tda_state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm
-	tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
-	tda1004x_write_mask(i2c, tda_state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config
-	tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup
-	tda1004x_write_byte(i2c, tda_state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config
-	tda1004x_write_mask(i2c, tda_state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select
-	tda10046h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
-
-	return 0;
+	return tda1004x_check_upload_ok(state, 0x20);
 }
 
 static int tda1004x_encode_fec(int fec)
@@ -602,174 +474,147 @@ static int tda1004x_decode_fec(int tdafe
 	return -1;
 }
 
-static int tda1004x_set_frequency(struct i2c_adapter *i2c,
-				  struct tda1004x_state *tda_state,
-				  struct dvb_frontend_parameters *fe_params)
-{
-	u8 tuner_buf[4];
-	struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) };
-	int tuner_frequency = 0;
-	u8 band, cp, filter;
-	int counter, counter2;
 
-	dprintk("%s\n", __FUNCTION__);
 
-	// setup the frequency buffer
-	switch (tda_state->tuner_type) {
-	case TUNER_TYPE_TD1344:
-
-		// setup tuner buffer
-		// ((Fif+((1000000/6)/2)) + Finput)/(1000000/6)
-		tuner_frequency =
-			(((fe_params->frequency / 1000) * 6) + 217502) / 1000;
-		tuner_buf[0] = tuner_frequency >> 8;
-		tuner_buf[1] = tuner_frequency & 0xff;
-		tuner_buf[2] = 0x88;
-		if (fe_params->frequency < 550000000) {
-			tuner_buf[3] = 0xab;
-		} else {
-			tuner_buf[3] = 0xeb;
-		}
 
-		// tune it
-		tda1004x_enable_tuner_i2c(i2c, tda_state);
-		tuner_msg.addr = tda_state->tuner_address;
-		tuner_msg.len = 4;
-		i2c_transfer(i2c, &tuner_msg, 1);
-
-		// wait for it to finish
-		tuner_msg.len = 1;
-		tuner_msg.flags = I2C_M_RD;
-		counter = 0;
-		counter2 = 0;
-		while (counter++ < 100) {
-			if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
-				if (tuner_buf[0] & 0x40) {
-					counter2++;
-				} else {
-					counter2 = 0;
-				}
-			}
-
-			if (counter2 > 10) {
-				break;
-			}
-		}
-		tda1004x_disable_tuner_i2c(i2c, tda_state);
-		break;
 
-	case TUNER_TYPE_TD1316:
-		// determine charge pump
-		tuner_frequency = fe_params->frequency + 36130000;
-		if (tuner_frequency < 87000000) {
-			return -EINVAL;
-		} else if (tuner_frequency < 130000000) {
-			cp = 3;
-		} else if (tuner_frequency < 160000000) {
-			cp = 5;
-		} else if (tuner_frequency < 200000000) {
-			cp = 6;
-		} else if (tuner_frequency < 290000000) {
-			cp = 3;
-		} else if (tuner_frequency < 420000000) {
-			cp = 5;
-		} else if (tuner_frequency < 480000000) {
-			cp = 6;
-		} else if (tuner_frequency < 620000000) {
-			cp = 3;
-		} else if (tuner_frequency < 830000000) {
-			cp = 5;
-		} else if (tuner_frequency < 895000000) {
-			cp = 7;
-		} else {
-			return -EINVAL;
-		}
 
-		// determine band
-		if (fe_params->frequency < 49000000) {
-			return -EINVAL;
-		} else if (fe_params->frequency < 159000000) {
-			band = 1;
-		} else if (fe_params->frequency < 444000000) {
-			band = 2;
-		} else if (fe_params->frequency < 861000000) {
-			band = 4;
-		} else {
-			return -EINVAL;
+
+
+
+
+
+
+
+
+
+
+
+
+int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+
+	return tda1004x_write_byteI(state, reg, data);
 		}
 
-		// work out filter
-		switch (fe_params->u.ofdm.bandwidth) {
-		case BANDWIDTH_6_MHZ:
-			filter = 0;
-			break;
+static int tda10045_init(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
 
-		case BANDWIDTH_7_MHZ:
-			filter = 0;
-			break;
+	dprintk("%s\n", __FUNCTION__);
 
-		case BANDWIDTH_8_MHZ:
-			filter = 1;
-			break;
+	if (state->initialised) return 0;
 
-		default:
-			return -EINVAL;
+	if (tda10045_fwupload(fe)) {
+		printk("tda1004x: firmware upload failed\n");
+		return -EIO;
 		}
 
-		// calculate divisor
-		// ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
-		tuner_frequency =
-		      (((fe_params->frequency / 1000) * 6) + 217280) / 1000;
-
-		// setup tuner buffer
-		tuner_buf[0] = tuner_frequency >> 8;
-		tuner_buf[1] = tuner_frequency & 0xff;
-		tuner_buf[2] = 0xca;
-		tuner_buf[3] = (cp << 5) | (filter << 3) | band;
-
-		// tune it
-		if (tda_state->fe_type == FE_TYPE_TDA10046H) {
-			// setup auto offset
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10);
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x80, 0);
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0);
+	tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
 
-			// disable agc_conf[2]
-			tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 0);
+	// Init the PLL
+	if (state->config->pll_init) {
+		tda1004x_enable_tuner_i2c(state);
+		state->config->pll_init(fe);
+		tda1004x_disable_tuner_i2c(state);
+	}
+
+	// tda setup
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+	tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
+	tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
+	tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
+	tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
+	tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e);
+
+	state->initialised = 1;
+	return 0;
 		}
-		tda1004x_enable_tuner_i2c(i2c, tda_state);
-		tuner_msg.addr = tda_state->tuner_address;
-		tuner_msg.len = 4;
-		if (i2c_transfer(i2c, &tuner_msg, 1) != 1) {
+
+static int tda10046_init(struct dvb_frontend* fe)
+{
+	struct tda1004x_state* state = fe->demodulator_priv;
+	dprintk("%s\n", __FUNCTION__);
+
+	if (state->initialised) return 0;
+
+	if (tda10046_fwupload(fe)) {
+		printk("tda1004x: firmware upload failed\n");
 			return -EIO;
 		}
-		msleep(1);
-		tda1004x_disable_tuner_i2c(i2c, tda_state);
-		if (tda_state->fe_type == FE_TYPE_TDA10046H)
-			tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 4);
-		break;
 
-	default:
-		return -EINVAL;
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); // wake up the chip
+
+	// Init the PLL
+	if (state->config->pll_init) {
+		tda1004x_enable_tuner_i2c(state);
+		state->config->pll_init(fe);
+		tda1004x_disable_tuner_i2c(state);
 	}
 
-	dprintk("%s: success\n", __FUNCTION__);
+	// tda setup
+	tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0x40);
+	tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
+	tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); // PLL M = 10
+	tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
+	tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221
+	tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0); // AGC setup
+	tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities
+	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0);	  // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
+	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0);	  // }
+	tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff);  // }
+	tda1004x_write_mask(state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits
+	tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1
+	tda1004x_write_mask(state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm
+	tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
+	tda1004x_write_mask(state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config
+	tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN
+	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup
+	tda1004x_write_byteI(state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config
+	tda1004x_write_mask(state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select
+	tda10046h_set_bandwidth(state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
 
+	state->initialised = 1;
 	return 0;
 }
 
-static int tda1004x_set_fe(struct i2c_adapter *i2c,
-			   struct tda1004x_state *tda_state,
+static int tda1004x_set_fe(struct dvb_frontend* fe,
 			   struct dvb_frontend_parameters *fe_params)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	int tmp;
 	int inversion;
 
 	dprintk("%s\n", __FUNCTION__);
 
+	if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+		// setup auto offset
+		tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0);
+
+		// disable agc_conf[2]
+		tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0);
+	}
+
 	// set frequency
-	if ((tmp = tda1004x_set_frequency(i2c, tda_state, fe_params)) < 0)
-		return tmp;
+	tda1004x_enable_tuner_i2c(state);
+	state->config->pll_set(fe, fe_params);
+	tda1004x_disable_tuner_i2c(state);
+
+	if (state->demod_type == TDA1004X_DEMOD_TDA10046)
+		tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 4);
 
 	// Hardcoded to use auto as much as possible
 	// The TDA10045 is very unreliable if AUTO mode is _not_ used. I have not
@@ -783,35 +628,35 @@ static int tda1004x_set_fe(struct i2c_ad
 	    (fe_params->u.ofdm.code_rate_LP == FEC_AUTO) ||
 	    (fe_params->u.ofdm.constellation == QAM_AUTO) ||
 	    (fe_params->u.ofdm.hierarchy_information == HIERARCHY_AUTO)) {
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 1, 1);	// enable auto
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x03, 0);	// turn off constellation bits
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 0);	// turn off hierarchy bits
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0x3f, 0);	// turn off FEC bits
+		tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1);	// enable auto
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0);	// turn off constellation bits
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0);	// turn off hierarchy bits
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0);	// turn off FEC bits
 	} else {
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 1, 0);	// disable auto
+		tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0);	// disable auto
 
 		// set HP FEC
 		tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_HP);
 		if (tmp < 0) return tmp;
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 7, tmp);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp);
 
 		// set LP FEC
 		tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_LP);
 		if (tmp < 0) return tmp;
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0x38, tmp << 3);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3);
 
 		// set constellation
 		switch (fe_params->u.ofdm.constellation) {
 		case QPSK:
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 0);
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0);
 			break;
 
 		case QAM_16:
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 1);
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1);
 			break;
 
 		case QAM_64:
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 2);
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2);
 			break;
 
 		default:
@@ -821,19 +666,19 @@ static int tda1004x_set_fe(struct i2c_ad
 		// set hierarchy
 		switch (fe_params->u.ofdm.hierarchy_information) {
 		case HIERARCHY_NONE:
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 0 << 5);
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5);
 			break;
 
 		case HIERARCHY_1:
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 1 << 5);
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5);
 			break;
 
 		case HIERARCHY_2:
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 2 << 5);
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5);
 			break;
 
 		case HIERARCHY_4:
-			tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 3 << 5);
+			tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5);
 			break;
 
 		default:
@@ -842,30 +687,26 @@ static int tda1004x_set_fe(struct i2c_ad
 	}
 
 	// set bandwidth
-	switch(tda_state->fe_type) {
-	case FE_TYPE_TDA10045H:
-		tda10045h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth);
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda10045h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
 		break;
 
-	case FE_TYPE_TDA10046H:
-		tda10046h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth);
+	case TDA1004X_DEMOD_TDA10046:
+		tda10046h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
 		break;
 	}
 
-	// need to invert the inversion for TT TDA10046H
-	inversion = fe_params->inversion;
-	if (tda_state->fe_type == FE_TYPE_TDA10046H) {
-		inversion = inversion ? INVERSION_OFF : INVERSION_ON;
-	}
-
 	// set inversion
+	inversion = fe_params->inversion;
+	if (state->config->invert) inversion = inversion ? INVERSION_OFF : INVERSION_ON;
 	switch (inversion) {
 	case INVERSION_OFF:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0);
+		tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0);
 		break;
 
 	case INVERSION_ON:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0x20);
+		tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20);
 		break;
 
 	default:
@@ -875,28 +716,28 @@ static int tda1004x_set_fe(struct i2c_ad
 	// set guard interval
 	switch (fe_params->u.ofdm.guard_interval) {
 	case GUARD_INTERVAL_1_32:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
 		break;
 
 	case GUARD_INTERVAL_1_16:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 1 << 2);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2);
 		break;
 
 	case GUARD_INTERVAL_1_8:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 2 << 2);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2);
 		break;
 
 	case GUARD_INTERVAL_1_4:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 3 << 2);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2);
 		break;
 
 	case GUARD_INTERVAL_AUTO:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 2);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
 		break;
 
 	default:
@@ -906,18 +747,18 @@ static int tda1004x_set_fe(struct i2c_ad
 	// set transmission mode
 	switch (fe_params->u.ofdm.transmission_mode) {
 	case TRANSMISSION_MODE_2K:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 0 << 4);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4);
 		break;
 
 	case TRANSMISSION_MODE_8K:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 1 << 4);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4);
 		break;
 
 	case TRANSMISSION_MODE_AUTO:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 4);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 0);
+		tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4);
+		tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0);
 		break;
 
 	default:
@@ -925,15 +766,15 @@ static int tda1004x_set_fe(struct i2c_ad
 	}
 
 	// start the lock
-	switch(tda_state->fe_type) {
-	case FE_TYPE_TDA10045H:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0);
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
 		msleep(10);
 		break;
 
-	case FE_TYPE_TDA10046H:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x40, 0x40);
+	case TDA1004X_DEMOD_TDA10046:
+		tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40);
 		msleep(10);
 		break;
 	}
@@ -941,25 +782,22 @@ static int tda1004x_set_fe(struct i2c_ad
 	return 0;
 }
 
-static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, struct dvb_frontend_parameters *fe_params)
+static int tda1004x_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	dprintk("%s\n", __FUNCTION__);
 
 	// inversion status
 	fe_params->inversion = INVERSION_OFF;
-	if (tda1004x_read_byte(i2c, tda_state, TDA1004X_CONFC1) & 0x20) {
+	if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) {
 		fe_params->inversion = INVERSION_ON;
 	}
-
-	// need to invert the inversion for TT TDA10046H
-	if (tda_state->fe_type == FE_TYPE_TDA10046H) {
-		fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
-	}
+	if (state->config->invert) fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
 
 	// bandwidth
-	switch(tda_state->fe_type) {
-	case FE_TYPE_TDA10045H:
-		switch (tda1004x_read_byte(i2c, tda_state, TDA10045H_WREF_LSB)) {
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) {
 		case 0x14:
 			fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
 			break;
@@ -972,8 +810,8 @@ static int tda1004x_get_fe(struct i2c_ad
 		}
 		break;
 
-	case FE_TYPE_TDA10046H:
-		switch (tda1004x_read_byte(i2c, tda_state, TDA10046H_TIME_WREF1)) {
+	case TDA1004X_DEMOD_TDA10046:
+		switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) {
 		case 0x60:
 			fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
 			break;
@@ -989,12 +827,12 @@ static int tda1004x_get_fe(struct i2c_ad
 
 	// FEC
 	fe_params->u.ofdm.code_rate_HP =
-	    tda1004x_decode_fec(tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF2) & 7);
+	    tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7);
 	fe_params->u.ofdm.code_rate_LP =
-	    tda1004x_decode_fec((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF2) >> 3) & 7);
+	    tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7);
 
 	// constellation
-	switch (tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 3) {
+	switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) {
 	case 0:
 		fe_params->u.ofdm.constellation = QPSK;
 		break;
@@ -1008,12 +846,12 @@ static int tda1004x_get_fe(struct i2c_ad
 
 	// transmission mode
 	fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
-	if (tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x10) {
+	if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) {
 		fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
 	}
 
 	// guard interval
-	switch ((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) {
+	switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) {
 	case 0:
 		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
 		break;
@@ -1029,7 +867,7 @@ static int tda1004x_get_fe(struct i2c_ad
 	}
 
 	// hierarchy
-	switch ((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x60) >> 5) {
+	switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) {
 	case 0:
 		fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;
 		break;
@@ -1047,8 +885,9 @@ static int tda1004x_get_fe(struct i2c_ad
 	return 0;
 }
 
-static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, fe_status_t * fe_status)
+static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	int status;
 	int cber;
 	int vber;
@@ -1056,7 +895,7 @@ static int tda1004x_read_status(struct i
 	dprintk("%s\n", __FUNCTION__);
 
 	// read status
-	status = tda1004x_read_byte(i2c, tda_state, TDA1004X_STATUS_CD);
+	status = tda1004x_read_byte(state, TDA1004X_STATUS_CD);
 	if (status == -1) {
 		return -EIO;
 	}
@@ -1071,12 +910,12 @@ static int tda1004x_read_status(struct i
 	// is getting anything valid
 	if (!(*fe_status & FE_HAS_VITERBI)) {
 		// read the CBER
-		cber = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_LSB);
+		cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
 		if (cber == -1) return -EIO;
-		status = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_MSB);
+		status = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
 		if (status == -1) return -EIO;
 		cber |= (status << 8);
-		tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_RESET);
+		tda1004x_read_byte(state, TDA1004X_CBER_RESET);
 
 		if (cber != 65535) {
 			*fe_status |= FE_HAS_VITERBI;
@@ -1087,15 +926,15 @@ static int tda1004x_read_status(struct i
 	// bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid.
 	if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) {
 		// read the VBER
-		vber = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_LSB);
+		vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB);
 		if (vber == -1) return -EIO;
-		status = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_MID);
+		status = tda1004x_read_byte(state, TDA1004X_VBER_MID);
 		if (status == -1) return -EIO;
 		vber |= (status << 8);
-		status = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_MSB);
+		status = tda1004x_read_byte(state, TDA1004X_VBER_MSB);
 		if (status == -1) return -EIO;
 		vber |= ((status << 16) & 0x0f);
-		tda1004x_read_byte(i2c, tda_state, TDA1004X_CVBER_LUT);
+		tda1004x_read_byte(state, TDA1004X_CVBER_LUT);
 
 		// if RS has passed some valid TS packets, then we must be
 		// getting some SYNC bytes
@@ -1109,26 +948,27 @@ static int tda1004x_read_status(struct i
 	return 0;
 }
 
-static int tda1004x_read_signal_strength(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * signal)
+static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	int tmp;
 	int reg = 0;
 
 	dprintk("%s\n", __FUNCTION__);
 
 	// determine the register to use
-	switch(tda_state->fe_type) {
-	case FE_TYPE_TDA10045H:
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
 		reg = TDA10045H_S_AGC;
 		break;
 
-	case FE_TYPE_TDA10046H:
+	case TDA1004X_DEMOD_TDA10046:
 		reg = TDA10046H_AGC_IF_LEVEL;
 		break;
 	}
 
 	// read it
-	tmp = tda1004x_read_byte(i2c, tda_state, reg);
+	tmp = tda1004x_read_byte(state, reg);
 	if (tmp < 0)
 		return -EIO;
 
@@ -1137,14 +977,15 @@ static int tda1004x_read_signal_strength
 	return 0;
 }
 
-static int tda1004x_read_snr(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * snr)
+static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	int tmp;
 
 	dprintk("%s\n", __FUNCTION__);
 
 	// read it
-	tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_SNR);
+	tmp = tda1004x_read_byte(state, TDA1004X_SNR);
 	if (tmp < 0)
 		return -EIO;
 	if (tmp) {
@@ -1156,8 +997,9 @@ static int tda1004x_read_snr(struct i2c_
 	return 0;
 }
 
-static int tda1004x_read_ucblocks(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ucblocks)
+static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	int tmp;
 	int tmp2;
 	int counter;
@@ -1166,16 +1008,16 @@ static int tda1004x_read_ucblocks(struct
 
 	// read the UCBLOCKS and reset
 	counter = 0;
-	tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_UNCOR);
+	tmp = tda1004x_read_byte(state, TDA1004X_UNCOR);
 	if (tmp < 0)
 		return -EIO;
 	tmp &= 0x7f;
 	while (counter++ < 5) {
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0);
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+		tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
 
-		tmp2 = tda1004x_read_byte(i2c, tda_state, TDA1004X_UNCOR);
+		tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR);
 		if (tmp2 < 0)
 			return -EIO;
 		tmp2 &= 0x7f;
@@ -1192,373 +1034,188 @@ static int tda1004x_read_ucblocks(struct
 	return 0;
 }
 
-static int tda1004x_read_ber(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ber)
+static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber)
 {
+	struct tda1004x_state* state = fe->demodulator_priv;
 	int tmp;
 
 	dprintk("%s\n", __FUNCTION__);
 
 	// read it in
-	tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_LSB);
+	tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
 	if (tmp < 0) return -EIO;
 	*ber = tmp << 1;
-	tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_MSB);
+	tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
 	if (tmp < 0) return -EIO;
 	*ber |= (tmp << 9);
-	tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_RESET);
+	tda1004x_read_byte(state, TDA1004X_CBER_RESET);
 
 	dprintk("%s: ber=0x%x\n", __FUNCTION__, *ber);
 	return 0;
 }
 
-static int tda1004x_sleep(struct i2c_adapter *i2c, struct tda1004x_state* tda_state)
+static int tda1004x_sleep(struct dvb_frontend* fe)
 {
-	switch(tda_state->fe_type) {
-	case FE_TYPE_TDA10045H:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0x10);
-		break;
-
-	case FE_TYPE_TDA10046H:
-		tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 1);
-		break;
-	}
+	struct tda1004x_state* state = fe->demodulator_priv;
 
-	return 0;
-}
-
-static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
-{
-	struct tda1004x_state *tda_state = (struct tda1004x_state *) fe->data;
-	struct i2c_adapter *i2c = tda_state->i2c;
-	int status = 0;
-
-	dprintk("%s: cmd=0x%x\n", __FUNCTION__, cmd);
-
-	switch (cmd) {
-	case FE_GET_INFO:
-		switch(tda_state->fe_type) {
-		case FE_TYPE_TDA10045H:
-			memcpy(arg, &tda10045h_info, sizeof(struct dvb_frontend_info));
+	switch(state->demod_type) {
+	case TDA1004X_DEMOD_TDA10045:
+		tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10);
 			break;
 
-		case FE_TYPE_TDA10046H:
-			memcpy(arg, &tda10046h_info, sizeof(struct dvb_frontend_info));
+	case TDA1004X_DEMOD_TDA10046:
+		tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
 			break;
 		}
-		break;
-
-	case FE_READ_STATUS:
-		return tda1004x_read_status(i2c, tda_state, (fe_status_t *) arg);
-
-	case FE_READ_BER:
-		return tda1004x_read_ber(i2c, tda_state, (u32 *) arg);
-
-	case FE_READ_SIGNAL_STRENGTH:
-		return tda1004x_read_signal_strength(i2c, tda_state, (u16 *) arg);
-
-	case FE_READ_SNR:
-		return tda1004x_read_snr(i2c, tda_state, (u16 *) arg);
-
-	case FE_READ_UNCORRECTED_BLOCKS:
-		return tda1004x_read_ucblocks(i2c, tda_state, (u32 *) arg);
-
-	case FE_SET_FRONTEND:
-		return tda1004x_set_fe(i2c, tda_state, (struct dvb_frontend_parameters*) arg);
-
-	case FE_GET_FRONTEND:
-		return tda1004x_get_fe(i2c, tda_state, (struct dvb_frontend_parameters*) arg);
-
-	case FE_SLEEP:
-		tda_state->initialised = 0;
-		return tda1004x_sleep(i2c, tda_state);
-
-	case FE_INIT:
+	state->initialised = 0;
 
-		// don't bother reinitialising
-		if (tda_state->initialised)
 			return 0;
-
-		// OK, perform initialisation
-		switch(tda_state->fe_type) {
-		case FE_TYPE_TDA10045H:
-			status = tda10045h_init(i2c, tda_state);
-			break;
-
-		case FE_TYPE_TDA10046H:
-			status = tda10046h_init(i2c, tda_state);
-			break;
 		}
-		if (status == 0)
-			tda_state->initialised = 1;
-		return status;
 
-	case FE_GET_TUNE_SETTINGS:
+static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
 	{
-		struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg;
 		fesettings->min_delay_ms = 800;
 		fesettings->step_size = 166667;
 		fesettings->max_drift = 166667*2;
 		return 0;
 	}
 
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	return 0;
-}
-
-static int tda1004x_attach(struct i2c_adapter *i2c, struct tda1004x_state* state)
+static void tda1004x_release(struct dvb_frontend* fe)
 {
-	int tda1004x_address = -1;
-	int tuner_address = -1;
-	int fe_type = -1;
-	int tuner_type = -1;
-	struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=NULL, .len=0 };
-	static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab };
-	static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
-	static u8 td1316_init_tda10046h[] = { 0x0b, 0xf5, 0x80, 0xab };
-
-	dprintk("%s\n", __FUNCTION__);
-
-	// probe for tda10045h
-	if (tda1004x_address == -1) {
-		state->tda1004x_address = 0x08;
-		if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x25) {
-			tda1004x_address = 0x08;
-			fe_type = FE_TYPE_TDA10045H;
-			printk("tda1004x: Detected Philips TDA10045H.\n");
-		}
-	}
-
-	// probe for tda10046h
-	if (tda1004x_address == -1) {
-		state->tda1004x_address = 0x08;
-		if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x46) {
-			tda1004x_address = 0x08;
-			fe_type = FE_TYPE_TDA10046H;
-			printk("tda1004x: Detected Philips TDA10046H.\n");
-		}
-	}
-
-	// did we find a frontend?
-	if (tda1004x_address == -1) {
-		return -ENODEV;
-	}
-
-	// enable access to the tuner
-	tda1004x_enable_tuner_i2c(i2c, state);
-
-	// check for a TD1344 first
-	if (tuner_address == -1) {
-		tuner_msg.addr = 0x61;
-		tuner_msg.buf = td1344_init;
-		tuner_msg.len = sizeof(td1344_init);
-		if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
-			msleep(1);
-			tuner_address = 0x61;
-			tuner_type = TUNER_TYPE_TD1344;
-			printk("tda1004x: Detected Philips TD1344 tuner.\n");
-		}
-	}
-
-	// OK, try a TD1316 on address 0x63
-	if (tuner_address == -1) {
-		tuner_msg.addr = 0x63;
-		tuner_msg.buf = td1316_init;
-		tuner_msg.len = sizeof(td1316_init);
-		if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
-			msleep(1);
-			tuner_address = 0x63;
-			tuner_type = TUNER_TYPE_TD1316;
-			printk("tda1004x: Detected Philips TD1316 tuner.\n");
-		}
-	}
-
-	// OK, TD1316 again, on address 0x60 (TDA10046H)
-	if (tuner_address == -1) {
-		tuner_msg.addr = 0x60;
-		tuner_msg.buf = td1316_init_tda10046h;
-		tuner_msg.len = sizeof(td1316_init_tda10046h);
-		if (i2c_transfer(i2c, &tuner_msg, 1) == 1) {
-			msleep(1);
-			tuner_address = 0x60;
-			tuner_type = TUNER_TYPE_TD1316;
-			printk("tda1004x: Detected Philips TD1316 tuner.\n");
-		}
-	}
-	tda1004x_disable_tuner_i2c(i2c, state);
-
-	// did we find a tuner?
-	if (tuner_address == -1) {
-		printk("tda1004x: Detected, but with unknown tuner.\n");
-		return -ENODEV;
+	struct tda1004x_state* state = (struct tda1004x_state*) fe->demodulator_priv;
+	kfree(state);
 	}
 
-	// create state
-	state->tda1004x_address = tda1004x_address;
-	state->fe_type = fe_type;
-	state->tuner_address = tuner_address;
-	state->tuner_type = tuner_type;
-	state->initialised = 0;
-
-	return 0;
-}
-
-static struct i2c_client client_template;
+static struct dvb_frontend_ops tda10045_ops;
 
-static int attach_adapter(struct i2c_adapter *adapter)
+struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+				     struct i2c_adapter* i2c)
 {
-	struct i2c_client *client;
-	struct tda1004x_state *state;
-	int ret;
+	struct tda1004x_state* state = NULL;
 
-	dprintk ("%s\n", __FUNCTION__);
-
-	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
-		return -ENOMEM;
-	}
+	/* allocate memory for the internal state */
+	state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
 
-	if (NULL == (state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL))) {
-		kfree(client);
-		return -ENOMEM;
-	}
-	state->i2c = adapter;
-
-	ret = tda1004x_attach(adapter, state);
-	if (ret) {
-		kfree(state);
-		kfree(client);
-		return -ENODEV;
-	}
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = state->tda1004x_address;
-	i2c_set_clientdata(client, (void*)state);
-
-	ret = i2c_attach_client(client);
-	if (ret) {
-		kfree(client);
-		kfree(state);
-		return ret;
-	}
-
-	// upload firmware
-	BUG_ON(!state->dvb);
-
-	switch(state->fe_type) {
-	case FE_TYPE_TDA10045H:
-		state->dspCodeCounterReg = TDA10045H_FWPAGE;
-		state->dspCodeInReg =  TDA10045H_CODE_IN;
-		state->dspVersion = 0x2c;
-
-		ret = tda10045_fwupload(adapter, state, client);
-		if (ret) {
-			printk("tda1004x: firmware upload failed\n");
-			goto out;
-		}
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+	state->demod_type = TDA1004X_DEMOD_TDA10045;
 
-		ret = dvb_register_frontend(tda1004x_ioctl, state->dvb,
-						state, &tda10045h_info,
-						THIS_MODULE);
-		break;
-	case FE_TYPE_TDA10046H:
-		state->dspCodeCounterReg = TDA10046H_CODE_CPT;
-		state->dspCodeInReg =  TDA10046H_CODE_IN;
-		state->dspVersion = 0x20;
-
-		ret = tda10046_fwupload(adapter, state, client);
-		if (ret) {
-			printk("tda1004x: firmware upload failed\n");
-			goto out;
-		}
+	/* check if the demod is there */
+	if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) goto error;
 
-		ret = dvb_register_frontend(tda1004x_ioctl, state->dvb,
-						state, &tda10046h_info,
-						THIS_MODULE);
-		break;
-	default:
-		BUG_ON(1);
-	}
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
 
-	if (ret) {
-		printk("tda1004x: registering frontend failed\n");
-		goto out;
+error:
+	if (state) kfree(state);
+	return NULL;
 	}
 
-	return 0;
-out:
-	i2c_detach_client(client);
-	kfree(client);
-	kfree(state);
-	return ret;
-}
-
-static int detach_client(struct i2c_client *client)
-{
-	struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client);
-
-	dprintk ("%s\n", __FUNCTION__);
+static struct dvb_frontend_ops tda10046_ops;
 
-	dvb_unregister_frontend (tda1004x_ioctl, state->dvb);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
-	kfree(state);
-	return 0;
-}
-
-static int command (struct i2c_client *client, unsigned int cmd, void *arg)
+struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+				     struct i2c_adapter* i2c)
 {
-	struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client);
+	struct tda1004x_state* state = NULL;
 
-	dprintk ("%s\n", __FUNCTION__);
+	/* allocate memory for the internal state */
+	state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+	if (state == NULL) goto error;
 
-	switch (cmd) {
-	case FE_REGISTER:
-		state->dvb = (struct dvb_adapter*)arg;
-		break;
-	case FE_UNREGISTER:
-		state->dvb = NULL;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-	return 0;
-}
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops));
+	state->initialised = 0;
+	state->demod_type = TDA1004X_DEMOD_TDA10046;
 
-static struct i2c_driver driver = {
-	.owner 		= THIS_MODULE,
-	.name 		= FRONTEND_NAME,
-	.id 		= I2C_DRIVERID_DVBFE_TDA1004X,
-	.flags 		= I2C_DF_NOTIFY,
-	.attach_adapter = attach_adapter,
-	.detach_client 	= detach_client,
-	.command 	= command,
-};
+	/* check if the demod is there */
+	if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) goto error;
 
-static struct i2c_client client_template = {
-	.name		= FRONTEND_NAME,
-	.flags 		= I2C_CLIENT_ALLOW_USE,
-	.driver  	= &driver,
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+		}
+
+static struct dvb_frontend_ops tda10045_ops = {
+
+	.info = {
+		.name = "Philips TDA10045H DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166667,
+		.caps =
+		    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		    FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+		    FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+		    FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+	},
+
+	.release = tda1004x_release,
+
+	.init = tda10045_init,
+	.sleep = tda1004x_sleep,
+
+	.set_frontend = tda1004x_set_fe,
+	.get_frontend = tda1004x_get_fe,
+
+	.read_status = tda1004x_read_status,
+	.read_ber = tda1004x_read_ber,
+	.read_signal_strength = tda1004x_read_signal_strength,
+	.read_snr = tda1004x_read_snr,
+	.read_ucblocks = tda1004x_read_ucblocks,
 };
 
-static int __init init_tda1004x(void)
-{
-	return i2c_add_driver(&driver);
-}
+static struct dvb_frontend_ops tda10046_ops = {
 
-static void __exit exit_tda1004x(void)
-{
-	if (i2c_del_driver(&driver))
-		printk("tda1004x: driver deregistration failed\n");
-}
+	.info = {
+		.name = "Philips TDA10046H DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.frequency_stepsize = 166667,
+		.caps =
+		    FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+		    FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+		    FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+		    FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+	},
+
+	.release = tda1004x_release,
+
+	.init = tda10046_init,
+	.sleep = tda1004x_sleep,
+
+	.set_frontend = tda1004x_set_fe,
+	.get_frontend = tda1004x_get_fe,
+	.get_tune_settings = tda1004x_get_tune_settings,
+
+	.read_status = tda1004x_read_status,
+	.read_ber = tda1004x_read_ber,
+	.read_signal_strength = tda1004x_read_signal_strength,
+	.read_snr = tda1004x_read_snr,
+	.read_ucblocks = tda1004x_read_ucblocks,
+};
 
-module_init(init_tda1004x);
-module_exit(exit_tda1004x);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
 
-MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Frontend");
+MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator");
 MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(tda10045_attach);
+EXPORT_SYMBOL(tda10046_attach);
+EXPORT_SYMBOL(tda1004x_write_byte);
diff -puN /dev/null drivers/media/dvb/frontends/tda1004x.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/tda1004x.h	2004-11-21 17:13:23.497046288 -0800
@@ -0,0 +1,53 @@
+  /*
+     Driver for Philips tda1004xh OFDM Frontend
+
+     (c) 2004 Andrew de Quincey
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+     GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   */
+
+#ifndef TDA1004X_H
+#define TDA1004X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct tda1004x_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* does the "inversion" need inverted? */
+	u8 invert:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+	/* request firmware for device */
+	int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+					    struct i2c_adapter* i2c);
+
+extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+					    struct i2c_adapter* i2c);
+
+extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data);
+
+#endif // TDA1004X_H
diff -puN /dev/null drivers/media/dvb/frontends/tda8083.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/tda8083.c	2004-11-21 17:13:23.500045832 -0800
@@ -0,0 +1,490 @@
+/*
+    Driver for Philips TDA8083 based QPSK Demodulator
+
+    Copyright (C) 2001 Convergence Integrated Media GmbH
+
+    written by Ralph Metzler <ralph@convergence.de>
+
+    adoption to the new DVB frontend API and diagnostic ioctl's
+    by Holger Waechtler <holger@convergence.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "tda8083.h"
+
+
+struct tda8083_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct tda8083_config* config;
+
+	struct dvb_frontend frontend;
+};
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk(KERN_DEBUG "tda8083: " args); \
+	} while (0)
+
+
+static u8 tda8083_init_tab [] = {
+	0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+	0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+	0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+	0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+	0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+
+static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf [] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+        ret = i2c_transfer(state->i2c, &msg, 1);
+
+        if (ret != 1)
+                dprintk ("%s: writereg error (reg %02x, ret == %i)\n",
+			__FUNCTION__, reg, ret);
+
+        return (ret != 1) ? -1 : 0;
+}
+
+
+static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len)
+{
+	int ret;
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk ("%s: readreg error (reg %02x, ret == %i)\n",
+			__FUNCTION__, reg1, ret);
+
+        return ret == 2 ? 0 : -1;
+}
+
+
+static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg)
+{
+	u8 val;
+
+	tda8083_readregs (state, reg, &val, 1);
+
+	return val;
+}
+
+
+
+static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion)
+{
+	/*  XXX FIXME: implement other modes than FEC_AUTO */
+	if (inversion == INVERSION_AUTO)
+		return 0;
+
+	return -EINVAL;
+}
+
+
+static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec)
+{
+	if (fec == FEC_AUTO)
+		return tda8083_writereg (state, 0x07, 0xff);
+
+	if (fec >= FEC_1_2 && fec <= FEC_8_9)
+		return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec));
+
+	return -EINVAL;
+}
+
+
+static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state)
+{
+	u8 index;
+	static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+				       FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 };
+
+	index = tda8083_readreg(state, 0x0e) & 0x07;
+
+	return fec_tab [index];
+}
+
+
+static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate)
+{
+        u32 ratio;
+	u32 tmp;
+	u8 filter;
+
+	if (srate > 32000000)
+                srate = 32000000;
+        if (srate < 500000)
+                srate = 500000;
+
+	filter = 0;
+	if (srate < 24000000)
+		filter = 2;
+	if (srate < 16000000)
+		filter = 3;
+
+	tmp = 31250 << 16;
+	ratio = tmp / srate;
+
+	tmp = (tmp % srate) << 8;
+	ratio = (ratio << 8) + tmp / srate;
+
+	tmp = (tmp % srate) << 8;
+	ratio = (ratio << 8) + tmp / srate;
+
+	dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio);
+
+	tda8083_writereg (state, 0x05, filter);
+	tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff);
+	tda8083_writereg (state, 0x03, (ratio >>  8) & 0xff);
+	tda8083_writereg (state, 0x04, (ratio      ) & 0xff);
+
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 1;
+}
+
+
+static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout)
+{
+	unsigned long start = jiffies;
+
+	while (jiffies - start < timeout &&
+               !(tda8083_readreg(state, 0x02) & 0x80))
+	{
+		msleep(50);
+	};
+}
+
+static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone)
+{
+	tda8083_writereg (state, 0x26, 0xf1);
+
+	switch (tone) {
+	case SEC_TONE_OFF:
+		return tda8083_writereg (state, 0x29, 0x00);
+	case SEC_TONE_ON:
+		return tda8083_writereg (state, 0x29, 0x80);
+	default:
+		return -EINVAL;
+	};
+}
+
+
+static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage)
+{
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		return tda8083_writereg (state, 0x20, 0x00);
+	case SEC_VOLTAGE_18:
+		return tda8083_writereg (state, 0x20, 0x11);
+	default:
+		return -EINVAL;
+	};
+}
+
+static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst)
+{
+	switch (burst) {
+	case SEC_MINI_A:
+		tda8083_writereg (state, 0x29, (5 << 2));  /* send burst A */
+		break;
+	case SEC_MINI_B:
+		tda8083_writereg (state, 0x29, (7 << 2));  /* send B */
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	tda8083_wait_diseqc_fifo (state, 100);
+
+	return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int tda8083_send_diseqc_msg (struct dvb_frontend* fe,
+				    struct dvb_diseqc_master_cmd *m)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+	int i;
+
+	tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */
+
+	for (i=0; i<m->msg_len; i++)
+		tda8083_writereg (state, 0x23 + i, m->msg[i]);
+
+	tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */
+
+	tda8083_wait_diseqc_fifo (state, 100);
+
+	return 0;
+}
+
+static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	u8 signal = ~tda8083_readreg (state, 0x01);
+	u8 sync = tda8083_readreg (state, 0x02);
+
+	*status = 0;
+
+	if (signal > 10)
+		*status |= FE_HAS_SIGNAL;
+
+	if (sync & 0x01)
+		*status |= FE_HAS_CARRIER;
+
+	if (sync & 0x02)
+		*status |= FE_HAS_VITERBI;
+
+	if (sync & 0x10)
+		*status |= FE_HAS_SYNC;
+
+	if ((sync & 0x1f) == 0x1f)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	u8 signal = ~tda8083_readreg (state, 0x01);
+	*strength = (signal << 8) | signal;
+
+	return 0;
+}
+
+static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	u8 _snr = tda8083_readreg (state, 0x08);
+	*snr = (_snr << 8) | _snr;
+
+	return 0;
+}
+
+static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	state->config->pll_set(fe, p);
+	tda8083_set_inversion (state, p->inversion);
+	tda8083_set_fec (state, p->u.qpsk.fec_inner);
+	tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate);
+
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	/*  FIXME: get symbolrate & frequency offset...*/
+	/*p->frequency = ???;*/
+	p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ?
+			INVERSION_ON : INVERSION_OFF;
+	p->u.qpsk.fec_inner = tda8083_get_fec (state);
+	/*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (state);*/
+
+	return 0;
+}
+
+static int tda8083_sleep(struct dvb_frontend* fe)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_writereg (state, 0x00, 0x02);
+	return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+	int i;
+
+	for (i=0; i<44; i++)
+		tda8083_writereg (state, i, tda8083_init_tab[i]);
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_send_diseqc_burst (state, burst);
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_set_tone (state, tone);
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+	tda8083_set_voltage (state, voltage);
+	tda8083_writereg (state, 0x00, 0x3c);
+	tda8083_writereg (state, 0x00, 0x04);
+
+	return 0;
+}
+
+static void tda8083_release(struct dvb_frontend* fe)
+{
+	struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda8083_ops;
+
+struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct tda8083_state* state = NULL;
+
+	/* allocate memory for the internal state */
+	state = (struct tda8083_state*) kmalloc(sizeof(struct tda8083_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda8083_ops, sizeof(struct dvb_frontend_ops));
+
+	/* check if the demod is there */
+	if ((tda8083_readreg(state, 0x00)) != 0x05) goto error;
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda8083_ops = {
+
+	.info = {
+		.name			= "Philips TDA8083 DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,     /* FIXME: guessed! */
+		.frequency_max		= 1400000,    /* FIXME: guessed! */
+		.frequency_stepsize	= 125,   /* kHz for QPSK frontends */
+	/*      .frequency_tolerance	= ???,*/
+		.symbol_rate_min	= 1000000,   /* FIXME: guessed! */
+		.symbol_rate_max	= 45000000,  /* FIXME: guessed! */
+	/*      .symbol_rate_tolerance	= ???,*/
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_MUTE_TS
+	},
+
+	.release = tda8083_release,
+
+	.init = tda8083_init,
+	.sleep = tda8083_sleep,
+
+	.set_frontend = tda8083_set_frontend,
+	.get_frontend = tda8083_get_frontend,
+
+	.read_status = tda8083_read_status,
+	.read_signal_strength = tda8083_read_signal_strength,
+	.read_snr = tda8083_read_snr,
+
+	.diseqc_send_master_cmd = tda8083_send_diseqc_msg,
+     	.diseqc_send_burst = tda8083_diseqc_send_burst,
+     	.set_tone = tda8083_diseqc_set_tone,
+     	.set_voltage = tda8083_diseqc_set_voltage,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda8083_attach);
diff -puN /dev/null drivers/media/dvb/frontends/tda8083.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/tda8083.h	2004-11-21 17:13:23.500045832 -0800
@@ -0,0 +1,45 @@
+/*
+    Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend
+
+    Copyright (C) 2001 Convergence Integrated Media GmbH
+
+    written by Ralph Metzler <ralph@convergence.de>
+
+    adoption to the new DVB frontend API and diagnostic ioctl's
+    by Holger Waechtler <holger@convergence.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef TDA8083_H
+#define TDA8083_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda8083_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // TDA8083_H
diff -puN /dev/null drivers/media/dvb/frontends/tda80xx.c
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/tda80xx.c	2004-11-21 17:13:23.503045376 -0800
@@ -0,0 +1,749 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "tda80xx.h"
+
+enum {
+	ID_TDA8044 = 0x04,
+	ID_TDA8083 = 0x05,
+};
+
+
+struct tda80xx_state {
+
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct tda80xx_config* config;
+
+	struct dvb_frontend frontend;
+
+	u32 clk;
+	int afc_loop;
+	struct work_struct worklet;
+	fe_code_rate_t code_rate;
+	fe_spectral_inversion_t spectral_inversion;
+	fe_status_t status;
+	u8 id;
+};
+
+static int debug = 1;
+#define dprintk	if (debug) printk
+
+static u8 tda8044_inittab_pre[] = {
+	0x02, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+	0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x58,
+	0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+	0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x00,
+	0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00
+};
+
+static u8 tda8044_inittab_post[] = {
+	0x04, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+	0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x50,
+	0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+	0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x6c,
+	0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00
+};
+
+static u8 tda8083_inittab[] = {
+	0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+	0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+	0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+	0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+	0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00
+};
+
+static __inline__ u32 tda80xx_div(u32 a, u32 b)
+{
+	return (a + (b / 2)) / b;
+}
+
+static __inline__ u32 tda80xx_gcd(u32 a, u32 b)
+{
+	u32 r;
+
+	while ((r = a % b)) {
+		a = b;
+		b = r;
+	}
+
+	return b;
+}
+
+static int tda80xx_read(struct tda80xx_state* state, u8 reg, u8 *buf, u8 len)
+{
+	int ret;
+	struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },
+			  { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (reg %02x, ret == %i)\n",
+				__FUNCTION__, reg, ret);
+
+	mdelay(10);
+
+	return (ret == 2) ? 0 : -EREMOTEIO;
+}
+
+static int tda80xx_write(struct tda80xx_state* state, u8 reg, const u8 *buf, u8 len)
+{
+	int ret;
+	u8 wbuf[len + 1];
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = wbuf, .len = len + 1 };
+
+	wbuf[0] = reg;
+	memcpy(&wbuf[1], buf, len);
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: i2c xfer error (ret == %i)\n", __FUNCTION__, ret);
+
+	mdelay(10);
+
+	return (ret == 1) ? 0 : -EREMOTEIO;
+}
+
+static __inline__ u8 tda80xx_readreg(struct tda80xx_state* state, u8 reg)
+{
+	u8 val;
+
+	tda80xx_read(state, reg, &val, 1);
+
+	return val;
+}
+
+static __inline__ int tda80xx_writereg(struct tda80xx_state* state, u8 reg, u8 data)
+{
+	return tda80xx_write(state, reg, &data, 1);
+}
+
+static int tda80xx_set_parameters(struct tda80xx_state* state,
+				  fe_spectral_inversion_t inversion,
+				  u32 symbol_rate,
+				  fe_code_rate_t fec_inner)
+{
+	u8 buf[15];
+	u64 ratio;
+	u32 clk;
+	u32 k;
+	u32 sr = symbol_rate;
+	u32 gcd;
+	u8 scd;
+
+	if (symbol_rate > (state->clk * 3) / 16)
+		scd = 0;
+	else if (symbol_rate > (state->clk * 3) / 32)
+		scd = 1;
+	else if (symbol_rate > (state->clk * 3) / 64)
+		scd = 2;
+	else
+		scd = 3;
+
+	clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+	/*
+	 * Viterbi decoder:
+	 * Differential decoding off
+	 * Spectral inversion unknown
+	 * QPSK modulation
+	 */
+	if (inversion == INVERSION_ON)
+		buf[0] = 0x60;
+	else if (inversion == INVERSION_OFF)
+		buf[0] = 0x20;
+	else
+		buf[0] = 0x00;
+
+	/*
+	 * CLK ratio:
+	 * system clock frequency is up to 64 or 96 MHz
+	 *
+	 * formula:
+	 * r = k * clk / symbol_rate
+	 *
+	 * k:	2^21 for caa 0..3,
+	 * 	2^20 for caa 4..5,
+	 * 	2^19 for caa 6..7
+	 */
+	if (symbol_rate <= (clk * 3) / 32)
+		k = (1 << 19);
+	else if (symbol_rate <= (clk * 3) / 16)
+		k = (1 << 20);
+	else
+		k = (1 << 21);
+
+	gcd = tda80xx_gcd(clk, sr);
+	clk /= gcd;
+	sr /= gcd;
+
+	gcd = tda80xx_gcd(k, sr);
+	k /= gcd;
+	sr /= gcd;
+
+	ratio = (u64)k * (u64)clk;
+	do_div(ratio, sr);
+
+	buf[1] = ratio >> 16;
+	buf[2] = ratio >> 8;
+	buf[3] = ratio;
+
+	/* nyquist filter roll-off factor 35% */
+	buf[4] = 0x20;
+
+	clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+	/* Anti Alias Filter */
+	if (symbol_rate < (clk * 3) / 64)
+		printk("tda80xx: unsupported symbol rate: %u\n", symbol_rate);
+	else if (symbol_rate <= clk / 16)
+		buf[4] |= 0x07;
+	else if (symbol_rate <= (clk * 3) / 32)
+		buf[4] |= 0x06;
+	else if (symbol_rate <= clk / 8)
+		buf[4] |= 0x05;
+	else if (symbol_rate <= (clk * 3) / 16)
+		buf[4] |= 0x04;
+	else if (symbol_rate <= clk / 4)
+		buf[4] |= 0x03;
+	else if (symbol_rate <= (clk * 3) / 8)
+		buf[4] |= 0x02;
+	else if (symbol_rate <= clk / 2)
+		buf[4] |= 0x01;
+	else
+		buf[4] |= 0x00;
+
+	/* Sigma Delta converter */
+	buf[5] = 0x00;
+
+	/* FEC: Possible puncturing rates */
+	if (fec_inner == FEC_NONE)
+		buf[6] = 0x00;
+	else if ((fec_inner >= FEC_1_2) && (fec_inner <= FEC_8_9))
+		buf[6] = (1 << (8 - fec_inner));
+	else if (fec_inner == FEC_AUTO)
+		buf[6] = 0xff;
+	else
+		return -EINVAL;
+
+	/* carrier lock detector threshold value */
+	buf[7] = 0x30;
+	/* AFC1: proportional part settings */
+	buf[8] = 0x42;
+	/* AFC1: integral part settings */
+	buf[9] = 0x98;
+	/* PD: Leaky integrator SCPC mode */
+	buf[10] = 0x28;
+	/* AFC2, AFC1 controls */
+	buf[11] = 0x30;
+	/* PD: proportional part settings */
+	buf[12] = 0x42;
+	/* PD: integral part settings */
+	buf[13] = 0x99;
+	/* AGC */
+	buf[14] = 0x50 | scd;
+
+	printk("symbol_rate=%u clk=%u\n", symbol_rate, clk);
+
+	return tda80xx_write(state, 0x01, buf, sizeof(buf));
+}
+
+static int tda80xx_set_clk(struct tda80xx_state* state)
+{
+	u8 buf[2];
+
+	/* CLK proportional part */
+	buf[0] = (0x06 << 5) | 0x08;	/* CMP[2:0], CSP[4:0] */
+	/* CLK integral part */
+	buf[1] = (0x04 << 5) | 0x1a;	/* CMI[2:0], CSI[4:0] */
+
+	return tda80xx_write(state, 0x17, buf, sizeof(buf));
+}
+
+#if 0
+static int tda80xx_set_scpc_freq_offset(struct tda80xx_state* state)
+{
+	/* a constant value is nonsense here imho */
+	return tda80xx_writereg(state, 0x22, 0xf9);
+}
+#endif
+
+static int tda80xx_close_loop(struct tda80xx_state* state)
+{
+	u8 buf[2];
+
+	/* PD: Loop closed, LD: lock detect enable, SCPC: Sweep mode - AFC1 loop closed */
+	buf[0] = 0x68;
+	/* AFC1: Loop closed, CAR Feedback: 8192 */
+	buf[1] = 0x70;
+
+	return tda80xx_write(state, 0x0b, buf, sizeof(buf));
+}
+
+static irqreturn_t tda80xx_irq(int irq, void *priv, struct pt_regs *pt)
+{
+	schedule_work(priv);
+
+	return IRQ_HANDLED;
+}
+
+static void tda80xx_read_status_int(struct tda80xx_state* state)
+{
+	u8 val;
+
+	static const fe_spectral_inversion_t inv_tab[] = {
+		INVERSION_OFF, INVERSION_ON
+	};
+
+	static const fe_code_rate_t fec_tab[] = {
+		FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+		FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8,
+	};
+
+	val = tda80xx_readreg(state, 0x02);
+
+	state->status = 0;
+
+	if (val & 0x01) /* demodulator lock */
+		state->status |= FE_HAS_SIGNAL;
+	if (val & 0x02) /* clock recovery lock */
+		state->status |= FE_HAS_CARRIER;
+	if (val & 0x04) /* viterbi lock */
+		state->status |= FE_HAS_VITERBI;
+	if (val & 0x08) /* deinterleaver lock (packet sync) */
+		state->status |= FE_HAS_SYNC;
+	if (val & 0x10) /* derandomizer lock (frame sync) */
+		state->status |= FE_HAS_LOCK;
+	if (val & 0x20) /* frontend can not lock */
+		state->status |= FE_TIMEDOUT;
+
+	if ((state->status & (FE_HAS_CARRIER)) && (state->afc_loop)) {
+		printk("tda80xx: closing loop\n");
+		tda80xx_close_loop(state);
+		state->afc_loop = 0;
+	}
+
+	if (state->status & (FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK)) {
+		val = tda80xx_readreg(state, 0x0e);
+		state->code_rate = fec_tab[val & 0x07];
+		if (state->status & (FE_HAS_SYNC | FE_HAS_LOCK))
+			state->spectral_inversion = inv_tab[(val >> 7) & 0x01];
+		else
+			state->spectral_inversion = INVERSION_AUTO;
+	}
+	else {
+		state->code_rate = FEC_AUTO;
+	}
+}
+
+static void tda80xx_worklet(void *priv)
+{
+	struct tda80xx_state *state = priv;
+
+	tda80xx_writereg(state, 0x00, 0x04);
+	enable_irq(state->config->irq);
+
+	tda80xx_read_status_int(state);
+}
+
+static void tda80xx_wait_diseqc_fifo(struct tda80xx_state* state)
+{
+	size_t i;
+
+	for (i = 0; i < 100; i++) {
+		if (tda80xx_readreg(state, 0x02) & 0x80)
+			break;
+		msleep(10);
+	}
+}
+
+static int tda8044_init(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+	int ret;
+
+	/*
+	 * this function is a mess...
+	 */
+
+	if ((ret = tda80xx_write(state, 0x00, tda8044_inittab_pre, sizeof(tda8044_inittab_pre))))
+		return ret;
+
+	tda80xx_writereg(state, 0x0f, 0x50);
+#if 1
+	tda80xx_writereg(state, 0x20, 0x8F);		/* FIXME */
+	tda80xx_writereg(state, 0x20, state->config->volt18setting);	/* FIXME */
+	//tda80xx_writereg(state, 0x00, 0x04);
+	tda80xx_writereg(state, 0x00, 0x0C);
+#endif
+	//tda80xx_writereg(state, 0x00, 0x08); /* Reset AFC1 loop filter */
+
+	tda80xx_write(state, 0x00, tda8044_inittab_post, sizeof(tda8044_inittab_post));
+
+	if (state->config->pll_init) {
+	   	tda80xx_writereg(state, 0x1c, 0x80);
+		state->config->pll_init(fe);
+		tda80xx_writereg(state, 0x1c, 0x00);
+	}
+
+	return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	tda80xx_write(state, 0x00, tda8083_inittab, sizeof(tda8083_inittab));
+
+	if (state->config->pll_init) {
+	   	tda80xx_writereg(state, 0x1c, 0x80);
+		state->config->pll_init(fe);
+		tda80xx_writereg(state, 0x1c, 0x00);
+	}
+
+	return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int tda80xx_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch (voltage) {
+	case SEC_VOLTAGE_13:
+		return tda80xx_writereg(state, 0x20, state->config->volt13setting);
+	case SEC_VOLTAGE_18:
+		return tda80xx_writereg(state, 0x20, state->config->volt18setting);
+	case SEC_VOLTAGE_OFF:
+		return tda80xx_writereg(state, 0x20, 0);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int tda80xx_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch (tone) {
+	case SEC_TONE_OFF:
+		return tda80xx_writereg(state, 0x29, 0x00);
+	case SEC_TONE_ON:
+		return tda80xx_writereg(state, 0x29, 0x80);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int tda80xx_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
+{
+   	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (cmd->msg_len > 6)
+		return -EINVAL;
+
+	tda80xx_writereg(state, 0x29, 0x08 | (cmd->msg_len - 3));
+	tda80xx_write(state, 0x23, cmd->msg, cmd->msg_len);
+	tda80xx_writereg(state, 0x29, 0x0c | (cmd->msg_len - 3));
+	tda80xx_wait_diseqc_fifo(state);
+
+	return 0;
+}
+
+static int tda80xx_send_diseqc_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t cmd)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch (cmd) {
+	case SEC_MINI_A:
+		tda80xx_writereg(state, 0x29, 0x14);
+		break;
+	case SEC_MINI_B:
+		tda80xx_writereg(state, 0x29, 0x1c);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	tda80xx_wait_diseqc_fifo(state);
+
+	return 0;
+}
+
+static int tda80xx_sleep(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	tda80xx_writereg(state, 0x00, 0x02);	/* enter standby */
+
+	return 0;
+}
+
+static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+   	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	tda80xx_writereg(state, 0x1c, 0x80);
+	state->config->pll_set(fe, p);
+	tda80xx_writereg(state, 0x1c, 0x00);
+
+	tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner);
+	tda80xx_set_clk(state);
+	//tda80xx_set_scpc_freq_offset(state);
+	state->afc_loop = 1;
+
+	return 0;
+}
+
+static int tda80xx_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (!state->config->irq)
+		tda80xx_read_status_int(state);
+
+	p->inversion = state->spectral_inversion;
+	p->u.qpsk.fec_inner = state->code_rate;
+
+	return 0;
+}
+
+static int tda80xx_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (!state->config->irq)
+		tda80xx_read_status_int(state);
+	*status = state->status;
+
+	return 0;
+}
+
+static int tda80xx_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+	int ret;
+	u8 buf[3];
+
+	if ((ret = tda80xx_read(state, 0x0b, buf, sizeof(buf))))
+		return ret;
+
+	*ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2];
+
+	return 0;
+}
+
+static int tda80xx_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	u8 gain = ~tda80xx_readreg(state, 0x01);
+	*strength = (gain << 8) | gain;
+
+	return 0;
+}
+
+static int tda80xx_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	u8 quality = tda80xx_readreg(state, 0x08);
+	*snr = (quality << 8) | quality;
+
+	return 0;
+}
+
+static int tda80xx_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	*ucblocks = tda80xx_readreg(state, 0x0f);
+	if (*ucblocks == 0xff)
+		*ucblocks = 0xffffffff;
+
+	return 0;
+}
+
+static int tda80xx_init(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	switch(state->id) {
+	case ID_TDA8044:
+		return tda8044_init(fe);
+
+	case ID_TDA8083:
+		return tda8083_init(fe);
+	}
+	return 0;
+}
+
+static void tda80xx_release(struct dvb_frontend* fe)
+{
+	struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+	if (state->config->irq)
+		free_irq(state->config->irq, &state->worklet);
+
+	kfree(state);
+}
+
+static struct dvb_frontend_ops tda80xx_ops;
+
+struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct tda80xx_state* state = NULL;
+	int ret;
+
+	/* allocate memory for the internal state */
+	state = (struct tda80xx_state*) kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &tda80xx_ops, sizeof(struct dvb_frontend_ops));
+	state->spectral_inversion = INVERSION_AUTO;
+	state->code_rate = FEC_AUTO;
+	state->status = 0;
+	state->afc_loop = 0;
+
+	/* check if the demod is there */
+	if (tda80xx_writereg(state, 0x89, 0x00) < 0) goto error;
+	state->id = tda80xx_readreg(state, 0x00);
+
+	switch (state->id) {
+	case ID_TDA8044:
+		state->clk = 96000000;
+		printk("tda80xx: Detected tda8044\n");
+		break;
+
+	case ID_TDA8083:
+		state->clk = 64000000;
+		printk("tda80xx: Detected tda8083\n");
+		break;
+
+	default:
+		goto error;
+	}
+
+	/* setup IRQ */
+	if (state->config->irq) {
+		INIT_WORK(&state->worklet, tda80xx_worklet, state);
+		if ((ret = request_irq(state->config->irq, tda80xx_irq, SA_ONESHOT, "tda80xx", &state->worklet)) < 0) {
+			printk(KERN_ERR "tda80xx: request_irq failed (%d)\n", ret);
+			goto error;
+		}
+	}
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+}
+
+static struct dvb_frontend_ops tda80xx_ops = {
+
+	.info = {
+		.name = "Philips TDA80xx DVB-S",
+		.type = FE_QPSK,
+		.frequency_min = 500000,
+		.frequency_max = 2700000,
+		.frequency_stepsize = 125,
+		.symbol_rate_min = 4500000,
+		.symbol_rate_max = 45000000,
+		.caps =	FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK |
+			FE_CAN_MUTE_TS
+	},
+
+	.release = tda80xx_release,
+
+	.init = tda80xx_init,
+	.sleep = tda80xx_sleep,
+
+	.set_frontend = tda80xx_set_frontend,
+	.get_frontend = tda80xx_get_frontend,
+
+	.read_status = tda80xx_read_status,
+	.read_ber = tda80xx_read_ber,
+	.read_signal_strength = tda80xx_read_signal_strength,
+	.read_snr = tda80xx_read_snr,
+	.read_ucblocks = tda80xx_read_ucblocks,
+
+	.diseqc_send_master_cmd = tda80xx_send_diseqc_msg,
+     	.diseqc_send_burst = tda80xx_send_diseqc_burst,
+     	.set_tone = tda80xx_set_tone,
+     	.set_voltage = tda80xx_set_voltage,
+};
+
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("Philips TDA8044 / TDA8083 DVB-S Demodulator driver");
+MODULE_AUTHOR("Felix Domke, Andreas Oberritter");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda80xx_attach);
diff -puN /dev/null drivers/media/dvb/frontends/tda80xx.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/tda80xx.h	2004-11-21 17:13:23.504045224 -0800
@@ -0,0 +1,51 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef TDA80XX_H
+#define TDA80XX_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda80xx_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* IRQ to use (0=>no IRQ used) */
+	u32 irq;
+
+	/* Register setting to use for 13v */
+	u8 volt13setting;
+
+	/* Register setting to use for 18v */
+	u8 volt18setting;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // TDA80XX_H
diff -puN drivers/media/dvb/frontends/ves1820.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/ves1820.c
--- 25/drivers/media/dvb/frontends/ves1820.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.398061336 -0800
+++ 25-akpm/drivers/media/dvb/frontends/ves1820.c	2004-11-21 17:13:23.507044768 -0800
@@ -1,6 +1,5 @@
 /* 
     VES1820  - Single Chip Cable Channel Receiver driver module
-               used on the the Siemens DVB-C cards
 
     Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
 
@@ -27,70 +26,34 @@
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/slab.h>
+#include <asm/div64.h>
 
 #include "dvb_frontend.h"
+#include "ves1820.h"
 
-/* I2C_DRIVERID_VES1820 is already defined in i2c-id.h */
 
-#if 0
-static int debug = 0;
-#define dprintk	if (debug) printk
-#endif
-
-static int verbose;
 
 struct ves1820_state {
-	int pwm;
-	u8 reg0;
-	int tuner;
-	u8 demod_addr;
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-};
 
-/* possible ves1820 adresses */
-static u8 addr[] = { 0x61, 0x62 };
+	struct i2c_adapter *i2c;
 
-#if defined(CONFIG_DBOX2)
-#define XIN 69600000UL
-#define DISABLE_INVERSION(reg0)		do { reg0 &= ~0x20; } while (0)
-#define ENABLE_INVERSION(reg0)		do { reg0 |= 0x20; } while (0)
-#define HAS_INVERSION(reg0)		(reg0 & 0x20)
-#else	/* PCI cards */
-#define XIN 57840000UL
-#define DISABLE_INVERSION(reg0)		do { reg0 |= 0x20; } while (0)
-#define ENABLE_INVERSION(reg0)		do { reg0 &= ~0x20; } while (0)
-#define HAS_INVERSION(reg0)		(!(reg0 & 0x20))
-#endif
-
-#define FIN (XIN >> 4)
+	struct dvb_frontend_ops ops;
 
+	/* configuration settings */
+	const struct ves1820_config* config;
 
+	struct dvb_frontend frontend;
 
-static struct dvb_frontend_info ves1820_info = {
-	.name = "VES1820 based DVB-C frontend",
-	.type = FE_QAM,
-	.frequency_stepsize = 62500,
-	.frequency_min = 51000000,
-	.frequency_max = 858000000,
-	.symbol_rate_min = (XIN/2)/64,     /* SACLK/64 == (XIN/2)/64 */
-	.symbol_rate_max = (XIN/2)/4,      /* SACLK/4 */
-#if 0
-	.frequency_tolerance = ???,
-	.symbol_rate_tolerance = ???,  /* ppm */  /* == 8% (spec p. 5) */
-	.notifier_delay = ?,
-#endif
-	.caps = FE_CAN_QAM_16 |
-		FE_CAN_QAM_32 |
-		FE_CAN_QAM_64 |
-		FE_CAN_QAM_128 |
-		FE_CAN_QAM_256 |
-		FE_CAN_FEC_AUTO |
-		FE_CAN_INVERSION_AUTO,
+	/* private demodulator data */
+	u8 reg0;
+	u8 pwm;
 };
 
+
+static int verbose;
+
 static u8 ves1820_inittab[] = {
-	0x69, 0x6A, 0x9B, 0x12, 0x12, 0x46, 0x26, 0x1A,
+	0x69, 0x6A, 0x93, 0x12, 0x12, 0x46, 0x26, 0x1A,
 	0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
 	0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
@@ -102,7 +65,7 @@ static u8 ves1820_inittab[] = {
 static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
 {
         u8 buf[] = { 0x00, reg, data };
-	struct i2c_msg msg = {.addr = state->demod_addr,.flags = 0,.buf = buf,.len = 3 };
+	struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 };
         int ret;
 
 	ret = i2c_transfer(state->i2c, &msg, 1);
@@ -120,8 +83,8 @@ static u8 ves1820_readreg(struct ves1820
 	u8 b0 [] = { 0x00, reg };
 	u8 b1 [] = { 0 };
 	struct i2c_msg msg[] = {
-		{.addr = state->demod_addr,.flags = 0,.buf = b0,.len = 2},
-		{.addr = state->demod_addr,.flags = I2C_M_RD,.buf = b1,.len = 1}
+		{.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2},
+		{.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
 	};
 	int ret;
 
@@ -134,148 +97,88 @@ static u8 ves1820_readreg(struct ves1820
 	return b1[0];
 }
 
-static int tuner_write(struct ves1820_state *state, u8 addr, u8 data[4])
-{
-        int ret;
-        struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 };
-
-	ret = i2c_transfer(state->i2c, &msg, 1);
-
-        if (ret != 1)
-		printk("ves1820: %s(): i/o error (ret == %i)\n", __FUNCTION__, ret);
-
-        return (ret != 1) ? -EREMOTEIO : 0;
-}
-
-
-/**
- *   set up the downconverter frequency divisor for a
- *   reference clock comparision frequency of 62.5 kHz.
- */
-static int tuner_set_tv_freq(struct ves1820_state *state, u32 freq)
-{
-        u32 div, ifreq;
-	static u8 byte3 [] = { 0x8e, 0x85 };
-	int tuner_type = state->tuner;
-        u8 buf [4];
-
-	if (tuner_type == 0xff)     /*  PLL not reachable over i2c ...  */
-		return 0;
-
-	if (strstr(state->i2c->name, "Technotrend")
-	 || strstr(state->i2c->name, "TT-Budget"))
-		ifreq = 35937500;
-	else
-		ifreq = 36125000;
-
-	div = (freq + ifreq + 31250) / 62500;
-
-	buf[0] = (div >> 8) & 0x7f;
-	buf[1] = div & 0xff;
-	buf[2] = byte3[tuner_type];
-
-	if (tuner_type == 1) {
-		buf[2] |= (div >> 10) & 0x60;
-		buf[3] = (freq < 174000000 ? 0x88 : freq < 470000000 ? 0x84 : 0x81);
-	} else {
-		buf[3] = (freq < 174000000 ? 0xa1 : freq < 454000000 ? 0x92 : 0x34);
-	}
-
-	return tuner_write(state, addr[tuner_type], buf);
-}
 
 static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion)
 {
 	reg0 |= state->reg0 & 0x62;
 	
-	if (INVERSION_ON == inversion)
-		ENABLE_INVERSION(reg0);
-	else if (INVERSION_OFF == inversion)
-		DISABLE_INVERSION(reg0);
+	if (INVERSION_ON == inversion) {
+		if (!state->config->invert) reg0 |= 0x20;
+		else reg0 &= ~0x20;
 	
-	ves1820_writereg(state, 0x00, reg0 & 0xfe);
-	ves1820_writereg(state, 0x00, reg0 | 0x01);
+	} else if (INVERSION_OFF == inversion) {
+
+     		if (!state->config->invert) reg0 &= ~0x20;
+		else reg0 |= 0x20;
+	}
 
-	/**
-	 *  check lock and toggle inversion bit if required...
-	 */
-	if (INVERSION_AUTO == inversion && !(ves1820_readreg(state, 0x11) & 0x08)) {
-		mdelay(50);
-		if (!(ves1820_readreg(state, 0x11) & 0x08)) {
-			reg0 ^= 0x20;
 			ves1820_writereg(state, 0x00, reg0 & 0xfe);
 			ves1820_writereg(state, 0x00, reg0 | 0x01);
-		}
-	}
 
 	state->reg0 = reg0;
 
 	return 0;
 }
 
-static int ves1820_init(struct ves1820_state *state)
-{
-	int i;
-        
-	ves1820_writereg(state, 0, 0);
-
-#if defined(CONFIG_DBOX2)
-	ves1820_inittab[2] &= ~0x08;
-#endif
-
-	for (i=0; i<53; i++)
-		ves1820_writereg(state, i, ves1820_inittab[i]);
-
-	ves1820_writereg(state, 0x34, state->pwm);
-
-	return 0;
-}
-
 static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
 {
         s32 BDR; 
         s32 BDRI;
         s16 SFIL=0;
         u16 NDEC = 0;
-        u32 tmp, ratio;
+	u32 ratio;
+	u32 fin;
+	u32 tmp;
+	u64 fptmp;
+	u64 fpxin;
 
-        if (symbolrate > XIN/2) 
-                symbolrate = XIN/2;
+	if (symbolrate > state->config->xin / 2)
+		symbolrate = state->config->xin / 2;
 
 	if (symbolrate < 500000)
                 symbolrate = 500000;
 
-	if (symbolrate < XIN / 16)
+	if (symbolrate < state->config->xin / 16)
 		NDEC = 1;
-	if (symbolrate < XIN / 32)
+	if (symbolrate < state->config->xin / 32)
 		NDEC = 2;
-	if (symbolrate < XIN / 64)
+	if (symbolrate < state->config->xin / 64)
 		NDEC = 3;
 
-	if (symbolrate < (u32) (XIN / 12.3))
+	/* yeuch! */
+	fpxin = state->config->xin * 10;
+	fptmp = fpxin; do_div(fptmp, 123);
+	if (symbolrate < fptmp);
 		SFIL = 1;
-	if (symbolrate < (u32) (XIN / 16))
+	fptmp = fpxin; do_div(fptmp, 160);
+	if (symbolrate < fptmp);
 		SFIL = 0;
-	if (symbolrate < (u32) (XIN / 24.6))
+	fptmp = fpxin; do_div(fptmp, 246);
+	if (symbolrate < fptmp);
 		SFIL = 1;
-	if (symbolrate < (u32) (XIN / 32))
+	fptmp = fpxin; do_div(fptmp, 320);
+	if (symbolrate < fptmp);
 		SFIL = 0;
-	if (symbolrate < (u32) (XIN / 49.2))
+	fptmp = fpxin; do_div(fptmp, 492);
+	if (symbolrate < fptmp);
 		SFIL = 1;
-	if (symbolrate < (u32) (XIN / 64))
+	fptmp = fpxin; do_div(fptmp, 640);
+	if (symbolrate < fptmp);
 		SFIL = 0;
-	if (symbolrate < (u32) (XIN / 98.4))
+	fptmp = fpxin; do_div(fptmp, 984);
+	if (symbolrate < fptmp);
 		SFIL = 1;
         
+	fin = state->config->xin >> 4;
         symbolrate <<= NDEC;
-        ratio = (symbolrate << 4) / FIN;
-        tmp =  ((symbolrate << 4) % FIN) << 8;
-        ratio = (ratio << 8) + tmp / FIN;
-        tmp = (tmp % FIN) << 8;
-        ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
+	ratio = (symbolrate << 4) / fin;
+	tmp = ((symbolrate << 4) % fin) << 8;
+	ratio = (ratio << 8) + tmp / fin;
+	tmp = (tmp % fin) << 8;
+	ratio = (ratio << 8) + (tmp + fin / 2) / fin;
         
         BDR = ratio;
-        BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
+	BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
         
         if (BDRI > 0xFF) 
                 BDRI = 0xFF;
@@ -295,8 +198,42 @@ static int ves1820_set_symbolrate(struct
         return 0;
 }
 
-static int ves1820_set_parameters(struct ves1820_state *state, struct dvb_frontend_parameters *p)
+
+
+
+
+
+
+
+
+
+
+
+
+static int ves1820_init(struct dvb_frontend* fe)
 {
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+	int i;
+	int val;
+
+	ves1820_writereg(state, 0, 0);
+
+	for (i = 0; i < 53; i++) {
+		val = ves1820_inittab[i];
+		if ((i == 2) && (state->config->selagc)) val |= 0x08;
+		ves1820_writereg(state, i, val);
+	}
+
+	ves1820_writereg(state, 0x34, state->pwm);
+
+	if (state->config->pll_init) state->config->pll_init(fe);
+
+	return 0;
+}
+
+static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
 	static const u8 reg0x00 [] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
 	static const u8 reg0x01 [] = {  140,  140,  106,  100,   92 };
 	static const u8 reg0x05 [] = {  135,  100,   70,   54,   38 };
@@ -307,7 +244,7 @@ static int ves1820_set_parameters(struct
 	if (real_qam < 0 || real_qam > 4)
 		return -EINVAL;
 
-	tuner_set_tv_freq(state, p->frequency);
+	state->config->pll_set(fe, p);
 	ves1820_set_symbolrate(state, p->u.qam.symbol_rate);
 	ves1820_writereg(state, 0x34, state->pwm);
 
@@ -318,25 +255,12 @@ static int ves1820_set_parameters(struct
 
 	ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
 
-	/* yes, this speeds things up: userspace reports lock in about 8 ms
-	   instead of 500 to 1200 ms after calling FE_SET_FRONTEND. */
-	mdelay(50);
-
 	return 0;
 }
 
-static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status)
 {
-	struct ves1820_state *state = (struct ves1820_state *) fe->data;
-
-        switch (cmd) {
-	case FE_GET_INFO:
-		memcpy (arg, &ves1820_info, sizeof(struct dvb_frontend_info));
-		break;
-
-        case FE_READ_STATUS:
-	{
-		fe_status_t *status = (fe_status_t *) arg;
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
 		int sync;
 
 		*status = 0;
@@ -358,46 +282,59 @@ static int ves1820_ioctl (struct dvb_fro
 		if (sync & 8)
 			*status |= FE_HAS_LOCK;
 
-		break;
+	return 0;
 	}
 
-	case FE_READ_BER:
+static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
 	{
-			u32 ber = ves1820_readreg(state, 0x14) |
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+	u32 _ber = ves1820_readreg(state, 0x14) |
 					(ves1820_readreg(state, 0x15) << 8) |
 					((ves1820_readreg(state, 0x16) & 0x0f) << 16);
-		*((u32*) arg) = 10 * ber;
-		break;
+	*ber = 10 * _ber;
+
+	return 0;
 	}
-	case FE_READ_SIGNAL_STRENGTH:
+
+static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 	{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
 			u8 gain = ves1820_readreg(state, 0x17);
-		*((u16*) arg) = (gain << 8) | gain;
-		break;
+	*strength = (gain << 8) | gain;
+
+	return 0;
 	}
 
-	case FE_READ_SNR:
+static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
 	{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
 			u8 quality = ~ves1820_readreg(state, 0x18);
-		*((u16*) arg) = (quality << 8) | quality;
-		break;
+	*snr = (quality << 8) | quality;
+
+	return 0;
 	}
 
-	case FE_READ_UNCORRECTED_BLOCKS:
-		*((u32 *) arg) = ves1820_readreg(state, 0x13) & 0x7f;
-		if (*((u32*) arg) == 0x7f)
-			*((u32*) arg) = 0xffffffff;
+static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+	*ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
+	if (*ucblocks == 0x7f)
+		*ucblocks = 0xffffffff;
+
 		/* reset uncorrected block counter */
 		ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
 		ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
-		break;
 
-        case FE_SET_FRONTEND:
-		return ves1820_set_parameters(state, arg);
+	return 0;
+}
 
-	case FE_GET_FRONTEND:
+static int ves1820_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 	{
-		struct dvb_frontend_parameters *p = (struct dvb_frontend_parameters *)arg;
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
 		int sync;
 		s8 afc = 0;
                 
@@ -409,7 +346,12 @@ static int ves1820_ioctl (struct dvb_fro
 					"ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10);
 		}
 
-			p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
+	if (!state->config->invert) {
+		p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
+	} else {
+		p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
+	}
+
 			p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
 
 		p->u.qam.fec_inner = FEC_NONE;
@@ -417,229 +359,112 @@ static int ves1820_ioctl (struct dvb_fro
 		p->frequency = ((p->frequency + 31250) / 62500) * 62500;
 		if (sync & 2)
 			p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10;
-		break;
-	}
-	case FE_SLEEP:
-		ves1820_writereg(state, 0x1b, 0x02);	/* pdown ADC */
-		ves1820_writereg(state, 0x00, 0x80);	/* standby */
-		break;
-
-        case FE_INIT:
-		return ves1820_init(state);
-
-        default:
-                return -EINVAL;
-        }
 
         return 0;
 } 
 
-static long probe_tuner(struct i2c_adapter *i2c)
+static int ves1820_sleep(struct dvb_frontend* fe)
 {
-	struct i2c_msg msg1 = {.addr = 0x61,.flags = 0,.buf = NULL,.len = 0 };
-	struct i2c_msg msg2 = {.addr = 0x62,.flags = 0,.buf = NULL,.len = 0 };
-	int type;
-
-	if (i2c_transfer(i2c, &msg1, 1) == 1) {
-		type = 0;
-		printk("ves1820: setup for tuner spXXXX\n");
-	} else if (i2c_transfer(i2c, &msg2, 1) == 1) {
-		type = 1;
-		printk("ves1820: setup for tuner sp5659c\n");
-	} else {
-		type = -1;
-	}
-
-	return type;
-}
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
 
-static u8 read_pwm(struct i2c_adapter *i2c)
-{
-	u8 b = 0xff;
-	u8 pwm;
-	struct i2c_msg msg [] = { { .addr = 0x50, .flags = 0, .buf = &b, .len = 1 },
-	{.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
-	};
-
-	if ((i2c_transfer(i2c, msg, 2) != 2) || (pwm == 0xff))
-		pwm = 0x48;
-
-	printk("ves1820: pwm=0x%02x\n", pwm);
-
-	return pwm;
-}
-
-static long probe_demod_addr(struct i2c_adapter *i2c)
-{
-	u8 b [] = { 0x00, 0x1a };
-	u8 id;
-	struct i2c_msg msg [] = { { .addr = 0x08, .flags = 0, .buf = b, .len = 2 },
-	{.addr = 0x08,.flags = I2C_M_RD,.buf = &id,.len = 1}
-	};
-
-	if (i2c_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70)
-		return msg[0].addr;
-
-	msg[0].addr = msg[1].addr = 0x09;
-
-	if (i2c_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70)
-		return msg[0].addr;
-
-	return -1;
-}
-
-static ssize_t attr_read_pwm(struct device *dev, char *buf)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client);
-	return sprintf(buf, "0x%02x\n", state->pwm);
-}
+	ves1820_writereg(state, 0x1b, 0x02);	/* pdown ADC */
+	ves1820_writereg(state, 0x00, 0x80);	/* standby */
 
-static ssize_t attr_write_pwm(struct device *dev, const char *buf, size_t count)
-{
-	struct i2c_client *client = to_i2c_client(dev);
-	struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client);
-	unsigned long pwm;
-	pwm = simple_strtoul(buf, NULL, 0);
-	state->pwm = pwm & 0xff;
-	return strlen(buf)+1;
+	return 0;
 }
 
-static struct device_attribute dev_attr_client_name = {
-	.attr	= { .name = "pwm", .mode = S_IRUGO|S_IWUGO, .owner = THIS_MODULE },
-	.show	= &attr_read_pwm,
-	.store  = &attr_write_pwm,
-};
-
-static struct i2c_client client_template;
-
-static int attach_adapter(struct i2c_adapter *adapter)
+static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
 {
-	struct i2c_client *client;
-	struct ves1820_state *state;
-	long demod_addr;
-	int tuner_type;
-	int ret;
-
-	demod_addr = probe_demod_addr(adapter);
-	if (demod_addr < 0)
-		return -ENODEV;
-
-	tuner_type = probe_tuner(adapter);
-	if (tuner_type < 0) {
-		printk("ves1820: demod found, but unknown tuner type.\n");
-		return -ENODEV;
-	}
-
-	if ((state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL)) == NULL) {
-		return -ENOMEM;
-	}
-
-	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
-		kfree(state);
-		return -ENOMEM;
-	}
-
-	memset(state, 0, sizeof(*state));
-	state->i2c = adapter;
-	state->tuner = tuner_type;
-	state->pwm = read_pwm(adapter);
-	state->reg0 = ves1820_inittab[0];
-	state->demod_addr = demod_addr;
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = addr[tuner_type];
-
-	i2c_set_clientdata(client, (void *) state);
-
-	ret = i2c_attach_client(client);
-	if (ret) {
-		kfree(client);
-		kfree(state);
-		return ret;
-}
-
-	BUG_ON(!state->dvb);
-
-	device_create_file(&client->dev, &dev_attr_client_name);
-
-	ret = dvb_register_frontend(ves1820_ioctl, state->dvb, state, &ves1820_info, THIS_MODULE);
-	if (ret) {
-		i2c_detach_client(client);
-		kfree(client);
-		kfree(state);
-		return ret;
-	}
 
+	fesettings->min_delay_ms = 200;
+	fesettings->step_size = 0;
+	fesettings->max_drift = 0;
 	return 0;
 }
 
-static int detach_client(struct i2c_client *client)
+static void ves1820_release(struct dvb_frontend* fe)
 {
-	struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client);
-	dvb_unregister_frontend(ves1820_ioctl, state->dvb);
-	device_remove_file(&client->dev, &dev_attr_client_name);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
+	struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
 	kfree(state);
-	return 0;
-}
-
-static int command(struct i2c_client *client, unsigned int cmd, void *arg)
-{
-	struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client);
-
-	switch (cmd) {
-	case FE_REGISTER:{
-			state->dvb = (struct dvb_adapter *) arg;
-			break;
-		}
-	case FE_UNREGISTER:{
-			state->dvb = NULL;
-			break;
-		}
-	default:
-		return -EOPNOTSUPP;
-	}
-	return 0;
 }
 
-static struct i2c_driver driver = {
-	.owner = THIS_MODULE,
-	.name = "ves1820",
-	.id = I2C_DRIVERID_VES1820,
-	.flags = I2C_DF_NOTIFY,
-	.attach_adapter = attach_adapter,
-	.detach_client = detach_client,
-	.command = command,
-};
+static struct dvb_frontend_ops ves1820_ops;
 
-static struct i2c_client client_template = {
-	I2C_DEVNAME("ves1820"),
-	.flags = I2C_CLIENT_ALLOW_USE,
-	.driver = &driver,
-};
-
-static int __init init_ves1820 (void)
+struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+				    struct i2c_adapter* i2c,
+				    u8 pwm)
 {
-	return i2c_add_driver(&driver);
-}
+	struct ves1820_state* state = NULL;
 
-static void __exit exit_ves1820 (void)
-{
-	if (i2c_del_driver(&driver))
-		printk("ves1820: driver deregistration failed\n");
-}
+	/* allocate memory for the internal state */
+	state = (struct ves1820_state*) kmalloc(sizeof(struct ves1820_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
 
-module_init(init_ves1820);
-module_exit(exit_ves1820);
+	/* setup the state */
+	memcpy(&state->ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
+	state->reg0 = ves1820_inittab[0];
+	state->config = config;
+	state->i2c = i2c;
+	state->pwm = pwm;
+
+	/* check if the demod is there */
+	if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
+		goto error;
+
+	if (verbose)
+		printk("ves1820: pwm=0x%02x\n", state->pwm);
+
+	state->ops.info.symbol_rate_min = (state->config->xin / 2) / 64;      /* SACLK/64 == (XIN/2)/64 */
+	state->ops.info.symbol_rate_max = (state->config->xin / 2) / 4;       /* SACLK/4 */
+
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+	}
+
+static struct dvb_frontend_ops ves1820_ops = {
+
+	.info = {
+		.name = "VLSI VES1820 DVB-C",
+		.type = FE_QAM,
+		.frequency_stepsize = 62500,
+		.frequency_min = 51000000,
+		.frequency_max = 858000000,
+		.caps = FE_CAN_QAM_16 |
+			FE_CAN_QAM_32 |
+			FE_CAN_QAM_64 |
+			FE_CAN_QAM_128 |
+			FE_CAN_QAM_256 |
+			FE_CAN_FEC_AUTO
+	},
+
+	.release = ves1820_release,
+
+	.init = ves1820_init,
+	.sleep = ves1820_sleep,
+
+	.set_frontend = ves1820_set_parameters,
+	.get_frontend = ves1820_get_frontend,
+	.get_tune_settings = ves1820_get_tune_settings,
+
+	.read_status = ves1820_read_status,
+	.read_ber = ves1820_read_ber,
+	.read_signal_strength = ves1820_read_signal_strength,
+	.read_snr = ves1820_read_snr,
+	.read_ucblocks = ves1820_read_ucblocks,
+};
 
-MODULE_PARM(verbose, "i");
+module_param(verbose, int, 0644);
 MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
 
-MODULE_DESCRIPTION("VES1820 DVB-C frontend driver");
+MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
 MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
 MODULE_LICENSE("GPL");
 
+EXPORT_SYMBOL(ves1820_attach);
diff -puN /dev/null drivers/media/dvb/frontends/ves1820.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/ves1820.h	2004-11-21 17:13:23.508044616 -0800
@@ -0,0 +1,52 @@
+/*
+    VES1820  - Single Chip Cable Channel Receiver driver module
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef VES1820_H
+#define VES1820_H
+
+#include <linux/dvb/frontend.h>
+
+#define VES1820_SELAGC_PWM 0
+#define VES1820_SELAGC_SIGNAMPERR 1
+
+struct ves1820_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* value of XIN to use */
+	u32 xin;
+
+	/* does inversion need inverted? */
+	u8 invert:1;
+
+	/* SELAGC control */
+	u8 selagc:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+					   struct i2c_adapter* i2c,
+					   u8 pwm);
+
+#endif // VES1820_H
diff -puN drivers/media/dvb/frontends/ves1x93.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/ves1x93.c
--- 25/drivers/media/dvb/frontends/ves1x93.c~dvb-frontend-driver-refactoring	2004-11-21 17:13:23.400061032 -0800
+++ 25-akpm/drivers/media/dvb/frontends/ves1x93.c	2004-11-21 17:13:23.512044008 -0800
@@ -1,5 +1,5 @@
 /* 
-    Driver for VES1893 and VES1993 QPSK Frontends
+    Driver for VES1893 and VES1993 QPSK Demodulators
 
     Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
     Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de>
@@ -31,41 +31,33 @@
 #include <linux/delay.h>
 
 #include "dvb_frontend.h"
+#include "ves1x93.h"
 
-static int debug = 0;
-#define dprintk	if (debug) printk
 
-static int board_type = 0;
-#define BOARD_SIEMENS_PCI	0
-#define BOARD_NOKIA_DBOX2	1
-#define BOARD_SAGEM_DBOX2	2
+struct ves1x93_state {
 
-static int demod_type = 0;
-#define DEMOD_VES1893		0
-#define DEMOD_VES1993		1
+	struct i2c_adapter* i2c;
+
+	struct dvb_frontend_ops ops;
+
+	/* configuration settings */
+	const struct ves1x93_config* config;
 
-static struct dvb_frontend_info ves1x93_info = {
-	.name			= "VES1x93",
-	.type			= FE_QPSK,
-	.frequency_min		= 950000,
-	.frequency_max		= 2150000,
-	.frequency_stepsize	= 125,		 /* kHz for QPSK frontends */
-	.frequency_tolerance	= 29500,
-	.symbol_rate_min	= 1000000,
-	.symbol_rate_max	= 45000000,
-/*      .symbol_rate_tolerance	=	???,*/
-	.notifier_delay		= 50,                /* 1/20 s */
-	.caps = FE_CAN_INVERSION_AUTO |
-		FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
-		FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
-		FE_CAN_QPSK
+	struct dvb_frontend frontend;
+
+	/* previous uncorrected block counter */
+	fe_spectral_inversion_t inversion;
+	u8 *init_1x93_tab;
+	u8 *init_1x93_wtab;
+	u8 tab_size;
+	u8 demod_type;
 };
 
+static int debug = 0;
+#define dprintk	if (debug) printk
 
-/**
- * nokia dbox2 (ves1893) and sagem dbox2 (ves1993)
- * need bit AGCR[PWMS] set to 1
- */
+#define DEMOD_VES1893		0
+#define DEMOD_VES1993		1
 
 static u8 init_1893_tab [] = {
 	0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4,
@@ -89,10 +81,6 @@ static u8 init_1993_tab [] = {
 	0x00, 0x00, 0x0e, 0x80, 0x00
 };
 
-
-static u8 * init_1x93_tab;
-
-
 static u8 init_1893_wtab[] =
 {
         1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0,
@@ -110,21 +98,13 @@ static u8 init_1993_wtab[] =
 	1,1,1,0,1,1,1,1, 1,1,1,1,1
 };
 
-struct ves1x93_state {
-	fe_spectral_inversion_t inversion;
-	struct i2c_adapter *i2c;
-	struct dvb_adapter *dvb;
-};
-
-
-
-static int ves1x93_writereg (struct i2c_adapter *i2c, u8 reg, u8 data)
+static int ves1x93_writereg (struct ves1x93_state* state, u8 reg, u8 data)
 {
         u8 buf [] = { 0x00, reg, data };
-	struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = buf, .len = 3 };
+	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 };
 	int err;
 
-	if ((err = i2c_transfer (i2c, &msg, 1)) != 1) {
+	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
 		dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
 		return -EREMOTEIO;
 	}
@@ -133,151 +113,31 @@ static int ves1x93_writereg (struct i2c_
 }
 
 
-static u8 ves1x93_readreg (struct i2c_adapter *i2c, u8 reg)
+static u8 ves1x93_readreg (struct ves1x93_state* state, u8 reg)
 {
 	int ret;
 	u8 b0 [] = { 0x00, reg };
 	u8 b1 [] = { 0 };
-	struct i2c_msg msg [] = { { .addr = 0x08, .flags = 0, .buf = b0, .len = 2 },
-			   { .addr = 0x08, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+	struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+			   { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
 
-	ret = i2c_transfer (i2c, msg, 2);
+	ret = i2c_transfer (state->i2c, msg, 2);
 
-	if (ret != 2)
-		dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+	if (ret != 2) return ret;
 
 	return b1[0];
 }
 
-
-static int tuner_write (struct i2c_adapter *i2c, u8 *data, u8 len)
-{
-        int ret;
-        struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = len };
-
-	ves1x93_writereg(i2c, 0x00, 0x11);
-	ret = i2c_transfer (i2c, &msg, 1);
-	ves1x93_writereg(i2c, 0x00, 0x01);
-
-        if (ret != 1)
-                printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
-
-        return (ret != 1) ? -1 : 0;
-}
-
-
-
-/**
- *   set up the downconverter frequency divisor for a
- *   reference clock comparision frequency of 125 kHz.
- */
-static int sp5659_set_tv_freq (struct i2c_adapter *i2c, u32 freq)
-{
-	u8 pwr = 0;
-   	u8 buf[4];
-        u32 div = (freq + 479500) / 125;
-
-	if (freq > 2000000) pwr = 3;
-	else if (freq > 1800000) pwr = 2;
-	else if (freq > 1600000) pwr = 1;
-	else if (freq > 1200000) pwr = 0;
-	else if (freq >= 1100000) pwr = 1;
-	else pwr = 2;
-
-   	buf[0] = (div >> 8) & 0x7f;
-   	buf[1] = div & 0xff;
-   	buf[2] = ((div & 0x18000) >> 10) | 0x95;
-   	buf[3] = (pwr << 6) | 0x30;
-
-   	// NOTE: since we're using a prescaler of 2, we set the
-	// divisor frequency to 62.5kHz and divide by 125 above
-
-	return tuner_write (i2c, buf, sizeof(buf));
-}
-
-
-static int tsa5059_set_tv_freq (struct i2c_adapter *i2c, u32 freq)
-{
-	int ret;
-	u8 buf [2];
-
-	freq /= 1000;
-
-	buf[0] = (freq >> 8) & 0x7F;
-	buf[1] = freq & 0xFF;
-
-	ret = tuner_write(i2c, buf, sizeof(buf));
-
-	return ret;
-}
-
-
-static int tuner_set_tv_freq (struct i2c_adapter *i2c, u32 freq)
-{
-	if ((demod_type == DEMOD_VES1893) && (board_type == BOARD_SIEMENS_PCI))
-		return sp5659_set_tv_freq (i2c, freq);
-	else if (demod_type == DEMOD_VES1993)
-		return tsa5059_set_tv_freq (i2c, freq);
-
-	return -EINVAL;
-}
-
-
-static int ves1x93_init (struct i2c_adapter *i2c)
-{
-	int i;
-	int size;
-	u8 *init_1x93_wtab;
- 
-	dprintk("%s: init chip\n", __FUNCTION__);
-
-	switch (demod_type) {
-	case DEMOD_VES1893:
-		init_1x93_tab = init_1893_tab;
-		init_1x93_wtab = init_1893_wtab;
-		size = sizeof(init_1893_tab);
-		if (board_type == BOARD_NOKIA_DBOX2)
-			init_1x93_tab[0x05] |= 0x20; /* invert PWM */
-		break;
-
-	case DEMOD_VES1993:
-		init_1x93_tab = init_1993_tab;
-		init_1x93_wtab = init_1993_wtab;
-		size = sizeof(init_1993_tab);
-		if (board_type == BOARD_SAGEM_DBOX2)
-			init_1x93_tab[0x05] |= 0x20; /* invert PWM */
-		break;
-
-	default:
-		return -EINVAL;
-	}
-
-	for (i = 0; i < size; i++)
-		if (init_1x93_wtab[i])
-			ves1x93_writereg (i2c, i, init_1x93_tab[i]);
-
-	if (demod_type == DEMOD_VES1993) {
-		if (board_type == BOARD_NOKIA_DBOX2)
-			tuner_write(i2c, "\x06\x5c\x83\x60", 4);
-		else if (board_type == BOARD_SAGEM_DBOX2)
-			tuner_write(i2c, "\x25\x70\x92\x40", 4);
-	}
-
-	return 0;
-}
-
-
-static int ves1x93_clr_bit (struct i2c_adapter *i2c)
+static int ves1x93_clr_bit (struct ves1x93_state* state)
 {
 	msleep(10);
-        ves1x93_writereg (i2c, 0, init_1x93_tab[0] & 0xfe);
-        ves1x93_writereg (i2c, 0, init_1x93_tab[0]);
+	ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe);
+	ves1x93_writereg (state, 0, state->init_1x93_tab[0]);
 	msleep(50);
 	return 0;
 }
 
-
-static int ves1x93_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion_t inversion)
+static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion)
 {
 	u8 val;
 
@@ -300,66 +160,47 @@ static int ves1x93_set_inversion (struct
 		return -EINVAL;
 	}
 
-	return ves1x93_writereg (i2c, 0x0c, (init_1x93_tab[0x0c] & 0x3f) | val);
+	return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val);
 }
 
 
-static int ves1x93_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec)
+static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec)
 {
 	if (fec == FEC_AUTO)
-		return ves1x93_writereg (i2c, 0x0d, 0x08);
+		return ves1x93_writereg (state, 0x0d, 0x08);
 	else if (fec < FEC_1_2 || fec > FEC_8_9)
 		return -EINVAL;
 	else
-		return ves1x93_writereg (i2c, 0x0d, fec - FEC_1_2);
+		return ves1x93_writereg (state, 0x0d, fec - FEC_1_2);
 }
 
 
-static fe_code_rate_t ves1x93_get_fec (struct i2c_adapter *i2c)
+static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state)
 {
-	return FEC_1_2 + ((ves1x93_readreg (i2c, 0x0d) >> 4) & 0x7);
+	return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7);
 }
 
 
-static int ves1x93_set_symbolrate (struct i2c_adapter *i2c, u32 srate)
+static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate)
 {
 	u32 BDR;
         u32 ratio;
 	u8  ADCONF, FCONF, FNR;
 	u32 BDRI;
 	u32 tmp;
-	u32 XIN, FIN;
+	u32 FIN;
 
 	dprintk("%s: srate == %d\n", __FUNCTION__, (unsigned int) srate);
 
-	switch (board_type) {
-	case BOARD_SIEMENS_PCI:
-		XIN = 90100000UL;
-		break;
-	case BOARD_NOKIA_DBOX2:
-		if (demod_type == DEMOD_VES1893)
-			XIN = 91000000UL;
-		else if (demod_type == DEMOD_VES1993)
-			XIN = 96000000UL;
-		else
-			return -EINVAL;
-		break;
-	case BOARD_SAGEM_DBOX2:
-		XIN = 92160000UL;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	if (srate > XIN/2)
-		srate = XIN/2;
+	if (srate > state->config->xin/2)
+		srate = state->config->xin/2;
 
 	if (srate < 500000)
 		srate = 500000;
 
 #define MUL (1UL<<26)
 
-	FIN = (XIN + 6000) >> 4;
+	FIN = (state->config->xin + 6000) >> 4;
 
 	tmp = srate << 6;
 	ratio = tmp / FIN;
@@ -404,55 +245,87 @@ static int ves1x93_set_symbolrate (struc
 	if (BDRI > 0xff)
 		BDRI = 0xff;
 
-	ves1x93_writereg (i2c, 0x06, 0xff & BDR);
-	ves1x93_writereg (i2c, 0x07, 0xff & (BDR >> 8));
-	ves1x93_writereg (i2c, 0x08, 0x0f & (BDR >> 16));
-
-	ves1x93_writereg (i2c, 0x09, BDRI);
-	ves1x93_writereg (i2c, 0x20, ADCONF);
-	ves1x93_writereg (i2c, 0x21, FCONF);
+	ves1x93_writereg (state, 0x06, 0xff & BDR);
+	ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8));
+	ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16));
+
+	ves1x93_writereg (state, 0x09, BDRI);
+	ves1x93_writereg (state, 0x20, ADCONF);
+	ves1x93_writereg (state, 0x21, FCONF);
 
 	if (srate < 6000000) 
-		ves1x93_writereg (i2c, 0x05, init_1x93_tab[0x05] | 0x80);
+		ves1x93_writereg (state, 0x05, state->init_1x93_tab[0x05] | 0x80);
 	else
-		ves1x93_writereg (i2c, 0x05, init_1x93_tab[0x05] & 0x7f);
+		ves1x93_writereg (state, 0x05, state->init_1x93_tab[0x05] & 0x7f);
 
 	/* ves1993 hates this, will lose lock */
-	if (demod_type != DEMOD_VES1993)
-		ves1x93_clr_bit (i2c);
+	if (state->demod_type != DEMOD_VES1993)
+		ves1x93_clr_bit (state);
 
 	return 0;
 }
 
-static int ves1x93_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static int ves1x93_init (struct dvb_frontend* fe)
 {
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+	int i;
+	int val;
+
+	dprintk("%s: init chip\n", __FUNCTION__);
+
+	for (i = 0; i < state->tab_size; i++) {
+		if (state->init_1x93_wtab[i]) {
+			val = state->init_1x93_tab[i];
+
+			if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */
+			ves1x93_writereg (state, i, val);
+		}
+	}
+
+	if (state->config->pll_init) {
+		ves1x93_writereg(state, 0x00, 0x11);
+		state->config->pll_init(fe);
+		ves1x93_writereg(state, 0x00, 0x01);
+	}
+
+	return 0;
+}
+
+static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
 	switch (voltage) {
 	case SEC_VOLTAGE_13:
-		return ves1x93_writereg (i2c, 0x1f, 0x20);
+		return ves1x93_writereg (state, 0x1f, 0x20);
 	case SEC_VOLTAGE_18:
-		return ves1x93_writereg (i2c, 0x1f, 0x30);
+		return ves1x93_writereg (state, 0x1f, 0x30);
 	case SEC_VOLTAGE_OFF:
-		return ves1x93_writereg (i2c, 0x1f, 0x00);
+		return ves1x93_writereg (state, 0x1f, 0x00);
 	default:
 		return -EINVAL;
 	}
 }
 
-
-static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status)
 {
-	struct ves1x93_state *state = (struct ves1x93_state*) fe->data;
-	struct i2c_adapter *i2c = state->i2c;
-
-        switch (cmd) {
-        case FE_GET_INFO:
-		memcpy (arg, &ves1x93_info, sizeof(struct dvb_frontend_info));
-		break;
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
 
-        case FE_READ_STATUS:
-	{
-		fe_status_t *status = arg;
-		u8 sync = ves1x93_readreg (i2c, 0x0e);
+	u8 sync = ves1x93_readreg (state, 0x0e);
 
 		/*
 		 * The ves1893 sometimes returns sync values that make no sense,
@@ -466,7 +339,7 @@ static int ves1x93_ioctl (struct dvb_fro
 		int maxtry = 10; /* just for safety - let's not get stuck here */
 		while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) {
 			msleep(10);
-			sync = ves1x93_readreg (i2c, 0x0e);
+		sync = ves1x93_readreg (state, 0x0e);
 		}
 
 		*status = 0;
@@ -486,64 +359,78 @@ static int ves1x93_ioctl (struct dvb_fro
 		if ((sync & 0x1f) == 0x1f)
 			*status |= FE_HAS_LOCK;
 
-		break;
+	return 0;
 	}
 
-        case FE_READ_BER:
+
+static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber)
 	{
-		u32 *ber = (u32 *) arg;
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
 
-		*ber = ves1x93_readreg (i2c, 0x15);
-                *ber |= (ves1x93_readreg (i2c, 0x16) << 8);
-                *ber |= ((ves1x93_readreg (i2c, 0x17) & 0x0F) << 16);
+	*ber = ves1x93_readreg (state, 0x15);
+	*ber |= (ves1x93_readreg (state, 0x16) << 8);
+	*ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16);
 		*ber *= 10;
-		break;
+
+	return 0;
 	}
 
-        case FE_READ_SIGNAL_STRENGTH:
+static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 	{
-		u8 signal = ~ves1x93_readreg (i2c, 0x0b);
-		*((u16*) arg) = (signal << 8) | signal;
-		break;
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	u8 signal = ~ves1x93_readreg (state, 0x0b);
+	*strength = (signal << 8) | signal;
+
+	return 0;
 	}
 
-        case FE_READ_SNR:
+static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr)
 	{
-		u8 snr = ~ves1x93_readreg (i2c, 0x1c);
-		*(u16*) arg = (snr << 8) | snr;
-		break;
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+	u8 _snr = ~ves1x93_readreg (state, 0x1c);
+	*snr = (_snr << 8) | _snr;
+
+	return 0;
 	}
 
-	case FE_READ_UNCORRECTED_BLOCKS: 
+static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
 	{
-		*(u32*) arg = ves1x93_readreg (i2c, 0x18) & 0x7f;
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
 
-		if (*(u32*) arg == 0x7f)
-			*(u32*) arg = 0xffffffff;   /* counter overflow... */
+	*ucblocks = ves1x93_readreg (state, 0x18) & 0x7f;
 		
-		ves1x93_writereg (i2c, 0x18, 0x00);  /* reset the counter */
-		ves1x93_writereg (i2c, 0x18, 0x80);  /* dto. */
-		break;
+	if (*ucblocks == 0x7f)
+		*ucblocks = 0xffffffff;   /* counter overflow... */
+
+	ves1x93_writereg (state, 0x18, 0x00);  /* reset the counter */
+	ves1x93_writereg (state, 0x18, 0x80);  /* dto. */
+
+	return 0;
 	}
 
-        case FE_SET_FRONTEND:
+static int ves1x93_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
         {
-		struct dvb_frontend_parameters *p = arg;
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
 
-		tuner_set_tv_freq (i2c, p->frequency);
-		ves1x93_set_inversion (i2c, p->inversion);
-		ves1x93_set_fec (i2c, p->u.qpsk.fec_inner);
-		ves1x93_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
+	ves1x93_writereg(state, 0x00, 0x11);
+	state->config->pll_set(fe, p);
+	ves1x93_writereg(state, 0x00, 0x01);
+	ves1x93_set_inversion (state, p->inversion);
+	ves1x93_set_fec (state, p->u.qpsk.fec_inner);
+	ves1x93_set_symbolrate (state, p->u.qpsk.symbol_rate);
 		state->inversion = p->inversion;
-                break;
+
+	return 0;
         }
 
-	case FE_GET_FRONTEND:
+static int ves1x93_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 	{
-		struct dvb_frontend_parameters *p = arg;
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
 		int afc;
 
-		afc = ((int)((char)(ves1x93_readreg (i2c, 0x0a) << 1)))/2;
+	afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2;
 		afc = (afc * (int)(p->u.qpsk.symbol_rate/1000/8))/16;
 
 		p->frequency -= afc;
@@ -553,185 +440,125 @@ static int ves1x93_ioctl (struct dvb_fro
 		 * if auto inversion was used
 		 */
 		if (state->inversion == INVERSION_AUTO)
-			p->inversion = (ves1x93_readreg (i2c, 0x0f) & 2) ? 
+		p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ?
 					INVERSION_OFF : INVERSION_ON;
-		p->u.qpsk.fec_inner = ves1x93_get_fec (i2c);
+	p->u.qpsk.fec_inner = ves1x93_get_fec (state);
 	/*  XXX FIXME: timing offset !! */
-		break;
-	}
 
-        case FE_SLEEP:
-		if (board_type == BOARD_SIEMENS_PCI)
-			ves1x93_writereg (i2c, 0x1f, 0x00);    /*  LNB power off  */
-		return ves1x93_writereg (i2c, 0x00, 0x08);
-
-        case FE_INIT:
-		return ves1x93_init (i2c);
-
-	case FE_SET_TONE:
-		return -EOPNOTSUPP;  /* the ves1893 can generate the 22k */
-		                     /* let's implement this when we have */
-		                     /* a box that uses the 22K_0 pin... */
+	return 0;
+}
 
-	case FE_SET_VOLTAGE:
-		return ves1x93_set_voltage (i2c, (fe_sec_voltage_t) arg);
+static int ves1x93_sleep(struct dvb_frontend* fe)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
 
-	default:
-		return -EOPNOTSUPP;
-        };
+	return ves1x93_writereg (state, 0x00, 0x08);
+}
         
-        return 0;
+static void ves1x93_release(struct dvb_frontend* fe)
+{
+	struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+	kfree(state);
 } 
 
-static struct i2c_client client_template;
+static struct dvb_frontend_ops ves1x93_ops;
 
-static int attach_adapter(struct i2c_adapter *adapter)
+struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+				    struct i2c_adapter* i2c)
 {
-	struct i2c_client *client;
-	struct ves1x93_state* state;
-	u8 identity = ves1x93_readreg(adapter, 0x1e);
-	int ret;
+	struct ves1x93_state* state = NULL;
+	u8 identity;
 
+	/* allocate memory for the internal state */
+	state = (struct ves1x93_state*) kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
+	if (state == NULL) goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	memcpy(&state->ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops));
+	state->inversion = INVERSION_OFF;
+
+	/* check if the demod is there + identify it */
+	identity = ves1x93_readreg(state, 0x1e);
 	switch (identity) {
 	case 0xdc: /* VES1893A rev1 */
 		printk("ves1x93: Detected ves1893a rev1\n");
-		demod_type = DEMOD_VES1893;
-		ves1x93_info.name[4] = '8';
+		state->demod_type = DEMOD_VES1893;
+		state->init_1x93_tab = init_1893_tab;
+		state->init_1x93_wtab = init_1893_wtab;
+		state->tab_size = sizeof(init_1893_tab);
 		break;
+
 	case 0xdd: /* VES1893A rev2 */
 		printk("ves1x93: Detected ves1893a rev2\n");
-		demod_type = DEMOD_VES1893;
-		ves1x93_info.name[4] = '8';
+		state->demod_type = DEMOD_VES1893;
+		state->init_1x93_tab = init_1893_tab;
+		state->init_1x93_wtab = init_1893_wtab;
+		state->tab_size = sizeof(init_1893_tab);
 		break;
+
 	case 0xde: /* VES1993 */
 		printk("ves1x93: Detected ves1993\n");
-		demod_type = DEMOD_VES1993;
-		ves1x93_info.name[4] = '9';
-		break;
-	default:
-		dprintk("VES1x93 not found (identity %02x)\n", identity);
-		return -ENODEV;
-	}
-
-	if ((state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL)) == NULL) {
-		return -ENOMEM;
-	}
-
-	if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
-		kfree(state);
-		return -ENOMEM;
-	}
-
-	state->inversion = INVERSION_OFF;
-	state->i2c = adapter;
-
-	memcpy(client, &client_template, sizeof(struct i2c_client));
-	client->adapter = adapter;
-	client->addr = (0x08>>1);
-	i2c_set_clientdata(client, (void*)state);
-
-	ret = i2c_attach_client(client);
-	if (ret) {
-		kfree(client);
-		kfree(state);
-		return -EFAULT;
-	}
-
-	BUG_ON(!state->dvb);
-
-	ret = dvb_register_frontend(ves1x93_ioctl, state->dvb, state,
-					&ves1x93_info, THIS_MODULE);
-	if (ret) {
-		i2c_detach_client(client);
-		kfree(client);
-		kfree(state);
-		return -EFAULT;
-}
-
-	return 0;
-}
-
-static int detach_client(struct i2c_client *client)
-{
-	struct ves1x93_state *state = (struct ves1x93_state*)i2c_get_clientdata(client);
-	dvb_unregister_frontend(ves1x93_ioctl, state->dvb);
-	i2c_detach_client(client);
-	BUG_ON(state->dvb);
-	kfree(client);
-	kfree(state);
-	return 0;
-}
-
-static int command (struct i2c_client *client, unsigned int cmd, void *arg)
-{
-	struct ves1x93_state *state = (struct ves1x93_state*)i2c_get_clientdata(client);
-
-	dprintk ("%s\n", __FUNCTION__);
-
-	switch (cmd) {
-	case FE_REGISTER: {
-		state->dvb = (struct dvb_adapter*)arg;
+		state->demod_type = DEMOD_VES1993;
+		state->init_1x93_tab = init_1993_tab;
+		state->init_1x93_wtab = init_1993_wtab;
+		state->tab_size = sizeof(init_1993_tab);
 		break;
-	}
-	case FE_UNREGISTER: {
-		state->dvb = NULL;
-		break;
-	}
-	default:
-		return -EOPNOTSUPP;
-}
-	return 0;
-}
-
-static struct i2c_driver driver = {
-	.owner 		= THIS_MODULE,
-	.name 		= "ves1x93",
-	.id 		= I2C_DRIVERID_DVBFE_VES1X93,
-	.flags 		= I2C_DF_NOTIFY,
-	.attach_adapter = attach_adapter,
-	.detach_client 	= detach_client,
-	.command 	= command,
-};
 
-static struct i2c_client client_template = {
-	I2C_DEVNAME("ves1x93"),
-	.flags 		= I2C_CLIENT_ALLOW_USE,
-	.driver  	= &driver,
-};
-
-static int __init init_ves1x93 (void)
-{
-	switch (board_type) {
-	case BOARD_NOKIA_DBOX2:
-		dprintk("%s: NOKIA_DBOX2\n", __FILE__);
-		break;
-	case BOARD_SAGEM_DBOX2:
-		dprintk("%s: SAGEM_DBOX2\n", __FILE__);
-		break;
-	case BOARD_SIEMENS_PCI:
-		dprintk("%s: SIEMENS_PCI\n", __FILE__);
-		break;
 	default:
-		return -EIO;
+		goto error;
 	}
 
-	return i2c_add_driver(&driver);
-}
-
-
-static void __exit exit_ves1x93 (void)
-{
-	if (i2c_del_driver(&driver))
-		printk("vex1x93: driver deregistration failed\n");
-}
+	/* create dvb_frontend */
+	state->frontend.ops = &state->ops;
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	if (state) kfree(state);
+	return NULL;
+	}
+
+static struct dvb_frontend_ops ves1x93_ops = {
+
+	.info = {
+		.name			= "VLSI VES1x93 DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 125,		 /* kHz for QPSK frontends */
+		.frequency_tolerance	= 29500,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+	/*	.symbol_rate_tolerance	=	???,*/
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK
+	},
+
+	.release = ves1x93_release,
+
+	.init = ves1x93_init,
+	.sleep = ves1x93_sleep,
+
+	.set_frontend = ves1x93_set_frontend,
+	.get_frontend = ves1x93_get_frontend,
+
+	.read_status = ves1x93_read_status,
+	.read_ber = ves1x93_read_ber,
+	.read_signal_strength = ves1x93_read_signal_strength,
+	.read_snr = ves1x93_read_snr,
+	.read_ucblocks = ves1x93_read_ucblocks,
 
-module_init(init_ves1x93);
-module_exit(exit_ves1x93);
+	.set_voltage = ves1x93_set_voltage,
+};
 
+module_param(debug, int, 0644);
 
-MODULE_DESCRIPTION("VES1x93 DVB-S Frontend");
+MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver");
 MODULE_AUTHOR("Ralph Metzler");
 MODULE_LICENSE("GPL");
-MODULE_PARM(debug,"i");
-MODULE_PARM(board_type,"i");
 
+EXPORT_SYMBOL(ves1x93_attach);
diff -puN /dev/null drivers/media/dvb/frontends/ves1x93.h
--- /dev/null	2003-09-15 06:40:47.000000000 -0700
+++ 25-akpm/drivers/media/dvb/frontends/ves1x93.h	2004-11-21 17:13:23.513043856 -0800
@@ -0,0 +1,50 @@
+/*
+    Driver for VES1893 and VES1993 QPSK Demodulators
+
+    Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+    Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de>
+    Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de>
+    Copyright (C) 2002-2003 Andreas Oberritter <obi@linuxtv.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef VES1X93_H
+#define VES1X93_H
+
+#include <linux/dvb/frontend.h>
+
+struct ves1x93_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* value of XIN to use */
+	u32 xin;
+
+	/* should PWM be inverted? */
+	u8 invert_pwm:1;
+
+	/* PLL maintenance */
+	int (*pll_init)(struct dvb_frontend* fe);
+	int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+					   struct i2c_adapter* i2c);
+
+#endif // VES1X93_H
_
