summary refs log tree commit diff
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/common/tuners/mxl5005s.c608
-rw-r--r--drivers/media/common/tuners/mxl5005s.h143
2 files changed, 371 insertions, 380 deletions
diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
index 64aa864c5dbf..45ac6a9e71a3 100644
--- a/drivers/media/common/tuners/mxl5005s.c
+++ b/drivers/media/common/tuners/mxl5005s.c
@@ -1,27 +1,62 @@
 /*
- * For the Realtek RTL chip RTL2831U
- * Realtek Release Date: 2008-03-14, ver 080314
- * Realtek version RTL2831 Linux driver version 080314
- * ver 080314
- *
- * for linux kernel version 2.6.21.4 - 2.6.22-14
- * support MXL5005s and MT2060 tuners (support tuner auto-detecting)
- * support two IR types -- RC5 and NEC
- *
- * Known boards with Realtek RTL chip RTL2821U
- *    Freecom USB stick 14aa:0160 (version 4)
- *    Conceptronic CTVDIGRCU
- *
- * Copyright (c) 2008 Realtek
- * Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
- * This code is placed under the terms of the GNU General Public License
- *
- * Released by Realtek under GPLv2.
- * Thanks to Realtek for a lot of support we received !
- *
- *  Revision: 080314 - original version
- */
+    MaxLinear MXL5005S VSB/QAM/DVBT tuner driver
+
+    Copyright (C) 2008 MaxLinear
+    Copyright (C) 2006 Steven Toth <stoth@hauppauge.com>
+      Functions:
+	mxl5005s_reset()
+	mxl5005s_writereg()
+	mxl5005s_writeregs()
+	mxl5005s_init()
+	mxl5005s_reconfigure()
+	mxl5005s_AssignTunerMode()
+	mxl5005s_set_params()
+	mxl5005s_get_frequency()
+	mxl5005s_get_bandwidth()
+	mxl5005s_release()
+	mxl5005s_attach()
+
+    Copyright (c) 2008 Realtek
+    Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
+      Functions:
+	mxl5005s_SetRfFreqHz()
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+    History of this driver (Steven Toth):
+      I was given a public release of a linux driver that included
+      support for the MaxLinear MXL5005S silicon tuner. Analysis of
+      the tuner driver showed clearly three things.
 
+      1. The tuner driver didn't support the LinuxTV tuner API
+	 so the code Realtek added had to be removed.
+
+      2. A significant amount of the driver is reference driver code
+	 from MaxLinear, I felt it was important to identify and
+	 preserve this.
+
+      3. New code has to be added to interface correctly with the
+	 LinuxTV API, as a regular kernel module.
+
+      Other than the reference driver enum's, I've clearly marked
+      sections of the code and retained the copyright of the
+      respective owners.
+*/
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -31,10 +66,10 @@
 #include "dvb_frontend.h"
 #include "mxl5005s.h"
 
-static int debug;
+static int debug = 2;
 
 #define dprintk(level, arg...) do {    \
-	if (debug >= level)            \
+	if (level <= debug)            \
 		printk(arg);    \
 	} while (0)
 
@@ -50,13 +85,6 @@ static int debug;
 #define MXLCTRL_NUM             189
 #define MASTER_CONTROL_ADDR     9
 
-/* Enumeration of AGC Mode */
-typedef enum
-{
-	MXL_DUAL_AGC = 0,
-	MXL_SINGLE_AGC
-} AGC_Mode;
-
 /* Enumeration of Master Control Register State */
 typedef enum
 {
@@ -66,51 +94,6 @@ typedef enum
 	MC_SEQ_OFF
 } Master_Control_State;
 
-/* Enumeration of MXL5005 Tuner Mode */
-typedef enum
-{
-	MXL_ANALOG_MODE = 0,
-	MXL_DIGITAL_MODE
-} Tuner_Mode;
-
-/* Enumeration of MXL5005 Tuner IF Mode */
-typedef enum
-{
-	MXL_ZERO_IF = 0,
-	MXL_LOW_IF
-} Tuner_IF_Mode;
-
-/* Enumeration of MXL5005 Tuner Clock Out Mode */
-typedef enum
-{
-	MXL_CLOCK_OUT_DISABLE = 0,
-	MXL_CLOCK_OUT_ENABLE
-} Tuner_Clock_Out;
-
-/* Enumeration of MXL5005 Tuner Div Out Mode */
-typedef enum
-{
-	MXL_DIV_OUT_1 = 0,
-	MXL_DIV_OUT_4
-
-} Tuner_Div_Out;
-
-/* Enumeration of MXL5005 Tuner Pull-up Cap Select Mode */
-typedef enum
-{
-	MXL_CAP_SEL_DISABLE = 0,
-	MXL_CAP_SEL_ENABLE
-
-} Tuner_Cap_Select;
-
-/* Enumeration of MXL5005 Tuner RSSI Mode */
-typedef enum
-{
-	MXL_RSSI_DISABLE = 0,
-	MXL_RSSI_ENABLE
-
-} Tuner_RSSI;
-
 /* Enumeration of MXL5005 Tuner Modulation Type */
 typedef enum
 {
@@ -122,22 +105,6 @@ typedef enum
 	MXL_ANALOG_OTA
 } Tuner_Modu_Type;
 
-/* Enumeration of MXL5005 Tuner Tracking Filter Type */
-typedef enum
-{
-	MXL_TF_DEFAULT = 0,
-	MXL_TF_OFF,
-	MXL_TF_C,
-	MXL_TF_C_H,
-	MXL_TF_D,
-	MXL_TF_D_L,
-	MXL_TF_E,
-	MXL_TF_F,
-	MXL_TF_E_2,
-	MXL_TF_E_NA,
-	MXL_TF_G
-} Tuner_TF_Type;
-
 /* MXL5005 Tuner Register Struct */
 typedef struct _TunerReg_struct
 {
@@ -268,33 +235,6 @@ enum
 };
 #define MXL5005S_BANDWIDTH_MODE_NUM		3
 
-/* Top modes */
-enum
-{
-	MXL5005S_TOP_5P5  =  55,
-	MXL5005S_TOP_7P2  =  72,
-	MXL5005S_TOP_9P2  =  92,
-	MXL5005S_TOP_11P0 = 110,
-	MXL5005S_TOP_12P9 = 129,
-	MXL5005S_TOP_14P7 = 147,
-	MXL5005S_TOP_16P8 = 168,
-	MXL5005S_TOP_19P4 = 194,
-	MXL5005S_TOP_21P2 = 212,
-	MXL5005S_TOP_23P2 = 232,
-	MXL5005S_TOP_25P2 = 252,
-	MXL5005S_TOP_27P1 = 271,
-	MXL5005S_TOP_29P2 = 292,
-	MXL5005S_TOP_31P7 = 317,
-	MXL5005S_TOP_34P9 = 349,
-};
-
-/* IF output load */
-enum
-{
-	MXL5005S_IF_OUTPUT_LOAD_200_OHM = 200,
-	MXL5005S_IF_OUTPUT_LOAD_300_OHM = 300,
-};
-
 /* MXL5005 Tuner Control Struct */
 typedef struct _TunerControl_struct {
 	u16 Ctrl_Num;	/* Control Number */
@@ -349,13 +289,15 @@ struct mxl5005s_state
 		TunerRegs[TUNER_REGS_NUM];	/* Tuner Register Array Pointer */
 
 	/* Linux driver framework specific */
-	const struct mxl5005s_config *config;
-
+	struct mxl5005s_config *config;
 	struct dvb_frontend *frontend;
 	struct i2c_adapter *i2c;
+
+	/* Cache values */
+	u32 current_mode;
+
 };
 
-// funcs
 u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value);
 u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value);
 u16 MXL_GetMasterControl(u8 *MasterReg, int state);
@@ -372,14 +314,26 @@ u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq);
 void MXL_SynthIFLO_Calc(struct dvb_frontend *fe);
 void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe);
 u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, int *count);
-int mxl5005s_SetRegsWithTable(struct dvb_frontend *fe, u8 *pAddrTable, u8 *pByteTable, int TableLen);
+int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, u8 *datatable, u8 len);
 u16 MXL_IFSynthInit(struct dvb_frontend *fe);
-static int mxl5005s_init2(struct dvb_frontend *fe);
+int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth);
+int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth);
+
+/* ----------------------------------------------------------------
+ * Begin: Custom code salvaged from the Realtek driver.
+ * Copyright (c) 2008 Realtek
+ * Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * Released by Realtek under GPLv2.
+ * Thanks to Realtek for a lot of support we received !
+ *
+ *  Revision: 080314 - original version
+ */
 
 int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
-	u8 AgcMasterByte = state->config->AgcMasterByte;
 	unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
 	unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
 	int TableLen;
@@ -396,7 +350,7 @@ int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
 	AddrTable[0] = MASTER_CONTROL_ADDR;
 	ByteTable[0] |= state->config->AgcMasterByte;
 
-	mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, 1);
+	mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
 
 	// Tuner RF frequency setting stage 1
 	MXL_TuneRF(fe, RfFreqHz);
@@ -410,13 +364,13 @@ int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
 
 	MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START) ;
 	AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
-	ByteTable[TableLen] = MasterControlByte | AgcMasterByte;
+	ByteTable[TableLen] = MasterControlByte | state->config->AgcMasterByte;
 	TableLen += 1;
 
-	mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, TableLen);
+	mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
 
 	// Wait 30 ms.
-	msleep(30);
+	msleep(150);
 
 	// Tuner RF frequency setting stage 2
 	MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1) ;
@@ -425,101 +379,21 @@ int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
 
 	MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START) ;
 	AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
-	ByteTable[TableLen] = MasterControlByte | AgcMasterByte ;
+	ByteTable[TableLen] = MasterControlByte | state->config->AgcMasterByte ;
 	TableLen += 1;
 
-	mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, TableLen);
+	mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
 
-	return 0;
-}
+	msleep(100);
 
-/* Write a single byte to a single reg */
-static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val)
-{
-	struct mxl5005s_state *state = fe->tuner_priv;
-	u8 buf[2] = { reg, val };
-	struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
-			       .buf = buf, .len = 2 };
-
-	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
-		printk(KERN_WARNING "mxl5005s I2C write failed\n");
-		return -EREMOTEIO;
-	}
 	return 0;
 }
+/* End: Custom code taken from the Realtek driver */
 
-/* Write a word to a single reg */
-static int mxl5005s_writereg16(struct dvb_frontend *fe, u8 reg, u16 val)
-{
-	struct mxl5005s_state *state = fe->tuner_priv;
-	u8 buf[3] = { reg, val >> 8 , val & 0xff };
-	struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
-			       .buf = buf, .len = 3 };
-
-	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
-		printk(KERN_WARNING "mxl5005s I2C write16 failed\n");
-		return -EREMOTEIO;
-	}
-	return 0;
-}
-
-int mxl5005s_SetRegsWithTable(struct dvb_frontend *fe, u8 *pAddrTable, u8 *pByteTable, int TableLen)
-{
-	int	i, ret;
-	u8	end_two_bytes_buf[]={ 0 , 0 };
-
-	for( i = 0 ; i < TableLen - 1 ; i++)
-	{
-		ret = mxl5005s_writereg(fe, pAddrTable[i], pByteTable[i]);
-		if (!ret)
-			return ret;
-	}
-
-	end_two_bytes_buf[0] = pByteTable[i];
-	end_two_bytes_buf[1] = MXL5005S_LATCH_BYTE;
-
-	ret = mxl5005s_writereg16(fe, pAddrTable[i], (end_two_bytes_buf[0] << 8) | end_two_bytes_buf[1]);
-
-	return ret;
-}
-
-int mxl5005s_SetRegMaskBits(struct dvb_frontend *fe,
-	unsigned char RegAddr,
-	unsigned char Msb,
-	unsigned char Lsb,
-	const unsigned char WritingValue
-	)
-{
-	int i;
-
-	unsigned char Mask;
-	unsigned char Shift;
-	unsigned char RegByte;
-
-	/* Generate mask and shift according to MSB and LSB. */
-	Mask = 0;
-	for(i = Lsb; i < (unsigned char)(Msb + 1); i++)
-		Mask |= 0x1 << i;
-
-	Shift = Lsb;
-
-	/* Get tuner register byte according to register adddress. */
-	MXL_RegRead(fe, RegAddr, &RegByte);
-
-	/* Reserve register byte unmask bit with mask and inlay writing value into it. */
-	RegByte &= ~Mask;
-	RegByte |= (WritingValue << Shift) & Mask;
-
-	/* Update tuner register byte table. */
-	MXL_RegWrite(fe, RegAddr, RegByte);
-
-	/* Write tuner register byte with writing byte. */
-	return mxl5005s_SetRegsWithTable(fe, &RegAddr, &RegByte, 1);
-}
-
-// The following context is source code provided by MaxLinear.
-// MaxLinear source code - MXL5005_Initialize.cpp
-// DONE
+/* ----------------------------------------------------------------
+ * Begin: Reference driver code found in the Realtek driver.
+ * Copyright (c) 2008 MaxLinear
+ */
 u16 MXL5005_RegisterInit(struct dvb_frontend *fe)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -841,7 +715,6 @@ u16 MXL5005_RegisterInit(struct dvb_frontend *fe)
 	return 0 ;
 }
 
-// DONE
 u16 MXL5005_ControlInit(struct dvb_frontend *fe)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -1785,7 +1658,6 @@ u16 MXL5005_ControlInit(struct dvb_frontend *fe)
 // MaxLinear source code - MXL5005_c.cpp
 // MXL5005.cpp : Defines the initialization routines for the DLL.
 // 2.6.12
-// DONE
 void InitTunerControls(struct dvb_frontend *fe)
 {
 	MXL5005_RegisterInit(fe);
@@ -1828,7 +1700,6 @@ void InitTunerControls(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
 		u8	Mode,		/* 0: Analog Mode ; 1: Digital Mode */
 		u8	IF_mode,	/* for Analog Mode, 0: zero IF; 1: low IF */
@@ -1898,7 +1769,6 @@ u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 void MXL_SynthIFLO_Calc(struct dvb_frontend *fe)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -1937,7 +1807,6 @@ void MXL_SynthIFLO_Calc(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -1976,7 +1845,6 @@ void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_OverwriteICDefault(struct dvb_frontend *fe)
 {
 	u16 status = 0;
@@ -2014,7 +1882,6 @@ u16 MXL_OverwriteICDefault(struct dvb_frontend *fe)
 //               > 0 : Failed                                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_BlockInit(struct dvb_frontend *fe)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -2042,6 +1909,7 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
 				status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2);
 				break;
 			case 6000000:
+				printk("%s() doing 6MHz digital\n", __func__);
 				status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 3);
 				break;
 		}
@@ -2072,7 +1940,6 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
 	else /*  Single AGC Mode Dig  Ana */
 		status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12);
 
-
 	if (state->TOP == 55) /* TOP == 5.5 */
 		status += MXL_ControlWrite(fe, AGC_IF, 0x0);
 
@@ -2302,6 +2169,8 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
 			status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
 		else /* High IF */
 			status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
+		status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
+
 	}
 	if (state->Mod_Type == MXL_ANALOG_CABLE) {
 		/* Analog Cable Mode */
@@ -2338,7 +2207,7 @@ u16 MXL_BlockInit(struct dvb_frontend *fe)
 	}
 
 	/* RSSI disable */
-	if(state->EN_RSSI==0) {
+	if(state->EN_RSSI == 0) {
 		status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
 		status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
 		status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
@@ -2547,6 +2416,7 @@ u16 MXL_IFSynthInit(struct dvb_frontend *fe)
 			Fref = 324000000UL ;
 		}
 		if (state->IF_LO == 5380000UL) {
+			printk("%s() doing 5.38\n", __func__);
 			status += MXL_ControlWrite(fe, IF_DIVVAL,   0x07) ;
 			status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C) ;
 			Fref = 322800000UL ;
@@ -3229,6 +3099,7 @@ u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
 
 	if (state->TF_Type == MXL_TF_C_H) // Tracking Filter type C-H for Hauppauge only
 	{
+		printk("%s() CH filter\n", __func__);
 		status += MXL_ControlWrite(fe, DAC_DIN_A, 0) ;
 
 		if (state->RF_IN >= 43000000 && state->RF_IN < 150000000)
@@ -3767,7 +3638,6 @@ u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
 	return status ;
 }
 
-// DONE
 u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val)
 {
 	u16 status = 0;
@@ -3834,7 +3704,6 @@ u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val)
 //                 >0 : Value exceed maximum allowed for control number      //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value)
 {
 	u16 status = 0;
@@ -3875,7 +3744,6 @@ u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value)
 //                 2 : Control name not found                                //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, u32 value, u16 controlGroup)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -3982,7 +3850,6 @@ u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, u32 value, u
 //                 -1 : Invalid Register Address                             //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_RegWrite(struct dvb_frontend *fe, u8 RegNum, u8 RegVal)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -4022,7 +3889,6 @@ u16 MXL_RegWrite(struct dvb_frontend *fe, u8 RegNum, u8 RegVal)
 //                 -1 : Invalid Register Address                             //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -4059,7 +3925,6 @@ u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal)
 //                 -1 : Invalid control name                                 //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -4131,7 +3996,6 @@ u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value)
 //                 -1 : Invalid control name                                 //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u16 MXL_ControlRegRead(struct dvb_frontend *fe, u16 controlNum, u8 *RegNum, int * count)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -4237,7 +4101,6 @@ u16 MXL_ControlRegRead(struct dvb_frontend *fe, u16 controlNum, u8 *RegNum, int
 //                 NONE                                                      //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, u8 bitVal)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -4285,7 +4148,6 @@ void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, u8 bitVal)
 //                Computed value                                             //
 //                                                                           //
 ///////////////////////////////////////////////////////////////////////////////
-// DONE
 u32 MXL_Ceiling(u32 value, u32 resolution)
 {
 	return (value/resolution + (value % resolution > 0 ? 1 : 0));
@@ -4294,7 +4156,6 @@ u32 MXL_Ceiling(u32 value, u32 resolution)
 //
 // Retrieve the Initialzation Registers
 //
-// DONE
 u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
 	u16 status = 0;
@@ -4317,7 +4178,6 @@ u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *c
 	return status;
 }
 
-// DONE
 u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
 	u16 status = 0;
@@ -4345,7 +4205,6 @@ u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *cou
 	return status;
 }
 
-// DONE
 u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
 	u16 status = 0;
@@ -4363,7 +4222,6 @@ u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, i
 	return status;
 }
 
-// DONE
 u16 MXL_GetCHRegister_LowIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, int *count)
 {
 	u16 status = 0;
@@ -4381,7 +4239,6 @@ u16 MXL_GetCHRegister_LowIF(struct dvb_frontend *fe, u8 * RegNum, u8 *RegVal, in
 	return status;
 }
 
-// DONE
 u16 MXL_GetMasterControl(u8 *MasterReg, int state)
 {
 	if (state == 1) /* Load_Start */
@@ -4526,7 +4383,6 @@ u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range)
 	return status;
 }
 
-// DONE
 u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
@@ -4537,141 +4393,224 @@ u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis)
 
 	return status;
 }
-
 #endif
+/* End: Reference driver code found in the Realtek driver that
+ * is copyright MaxLinear */
 
-/* Linux driver related functions */
-
-
-int mxl5005s_init(struct dvb_frontend *fe)
+/* ----------------------------------------------------------------
+ * Begin: Everything after here is new code to adapt the
+ * proprietary Realtek driver into a Linux API tuner.
+ * Copyright (C) 2008 Steven Toth <stoth@hauppauge.com>
+ */
+static int mxl5005s_reset(struct dvb_frontend *fe)
 {
-	int            MxlModMode;
-	int            MxlIfMode;
-	unsigned long  MxlBandwitdh;
-	unsigned long  MxlIfFreqHz;
-	unsigned long  MxlCrystalFreqHz;
-	int            MxlAgcMode;
-	unsigned short MxlTop;
-	unsigned short MxlIfOutputLoad;
-	int            MxlClockOut;
-	int            MxlDivOut;
-	int            MxlCapSel;
-	int            MxlRssiOnOff;
-	unsigned char  MxlStandard;
-	unsigned char  MxlTfType;
+	struct mxl5005s_state *state = fe->tuner_priv;
+	int ret = 0;
 
-	/* Set MxL5005S parameters. */
-	MxlModMode       = MXL_DIGITAL_MODE;
-	MxlIfMode        = MXL_ZERO_IF;
-// steve
-	//MxlBandwitdh     = MXL5005S_BANDWIDTH_8MHZ;
-	//MxlIfFreqHz      = IF_FREQ_4570000HZ;
-	MxlBandwitdh     = MXL5005S_BANDWIDTH_6MHZ; // config
-	MxlIfFreqHz      = IF_FREQ_5380000HZ; // config
-	MxlCrystalFreqHz = CRYSTAL_FREQ_16000000HZ; // config
-	MxlAgcMode       = MXL_SINGLE_AGC;
-	MxlTop           = MXL5005S_TOP_25P2;
-	MxlIfOutputLoad  = MXL5005S_IF_OUTPUT_LOAD_200_OHM;
-	MxlClockOut      = MXL_CLOCK_OUT_DISABLE;
-	MxlDivOut        = MXL_DIV_OUT_4;
-	MxlCapSel        = MXL_CAP_SEL_ENABLE;
-	MxlRssiOnOff     = MXL_RSSI_ENABLE; // config
-	MxlTfType        = MXL_TF_C_H; // config
-
-	MxlStandard = MXL_ATSC; // config
-
-	// TODO: this is bad, it trashes other configs
-	// Set MxL5005S extra module.
-	//pExtra->AgcMasterByte = (MxlAgcMode == MXL_DUAL_AGC) ? 0x4 : 0x0;
+	u8 buf[2] = { 0xff, 0x00 };
+	struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
+			       .buf = buf, .len = 2 };
 
-	MXL5005_TunerConfig(
-		fe,
-		(unsigned char)MxlModMode,
-		(unsigned char)MxlIfMode,
-		MxlBandwitdh,
-		MxlIfFreqHz,
-		MxlCrystalFreqHz,
-		(unsigned char)MxlAgcMode,
-		MxlTop,
-		MxlIfOutputLoad,
-		(unsigned char)MxlClockOut,
-		(unsigned char)MxlDivOut,
-		(unsigned char)MxlCapSel,
-		(unsigned char)MxlRssiOnOff,
-		MxlStandard, MxlTfType);
-
-	return mxl5005s_init2(fe);
-}
+	dprintk(2, "%s()\n", __func__);
 
-static int mxl5005s_set_params(struct dvb_frontend *fe,
-			       struct dvb_frontend_parameters *params)
-{
-	u32 freq;
-	u32 bw;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
 
-	if (fe->ops.info.type == FE_OFDM)
-		bw = params->u.ofdm.bandwidth;
-	else
-		bw = MXL5005S_BANDWIDTH_6MHZ;
+	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+		printk(KERN_WARNING "mxl5005s I2C reset failed\n");
+		ret = -EREMOTEIO;
+	}
 
-	freq = params->frequency; /* Hz */
-	dprintk(1, "%s() freq=%d bw=%d\n", __func__, freq, bw);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
 
-	return mxl5005s_SetRfFreqHz(fe, freq);
+	return ret;
 }
 
-static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+/* Write a single byte to a single reg, latch the value if required by
+ * following the transaction with the latch byte.
+ */
+static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
-	dprintk(1, "%s()\n", __func__);
+	u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE };
+	struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
+			       .buf = buf, .len = 3 };
 
-	*frequency = state->RF_IN;
+	if (latch == 0)
+		msg.len = 2;
 
+	dprintk(2, "%s(reg = 0x%x val = 0x%x addr = 0x%x)\n", __func__, reg, val, msg.addr);
+
+	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+		printk(KERN_WARNING "mxl5005s I2C write failed\n");
+		return -EREMOTEIO;
+	}
 	return 0;
 }
 
-static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, u8 *datatable, u8 len)
 {
-	struct mxl5005s_state *state = fe->tuner_priv;
-	dprintk(1, "%s()\n", __func__);
+	int ret = 0, i;
 
-	*bandwidth = state->Chan_Bandwidth;
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
 
-	return 0;
+	for (i = 0 ; i < len-1; i++) {
+		ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0);
+		if (ret < 0)
+			break;
+	}
+
+	ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return ret;
 }
 
-static int mxl5005s_get_status(struct dvb_frontend *fe, u32 *status)
+
+int mxl5005s_init(struct dvb_frontend *fe)
 {
 	dprintk(1, "%s()\n", __func__);
-
-	*status = 0;
-	// *status = TUNER_STATUS_LOCKED;
-
-	return 0;
+	return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ);
 }
 
-static int mxl5005s_init2(struct dvb_frontend *fe)
+int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth)
 {
 	struct mxl5005s_state *state = fe->tuner_priv;
+
 	u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
 	u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
 	int TableLen;
 
-	dprintk(1, "%s()\n", __func__);
+	dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth);
 
-	/* Initialize MxL5005S tuner according to MxL5005S tuner example code. */
+	mxl5005s_reset(fe);
 
 	/* Tuner initialization stage 0 */
 	MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET);
 	AddrTable[0] = MASTER_CONTROL_ADDR;
 	ByteTable[0] |= state->config->AgcMasterByte;
 
-	mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, 1);
+	mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
+
+	mxl5005s_AssignTunerMode(fe, mod_type, bandwidth);
 
 	/* Tuner initialization stage 1 */
 	MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen);
 
-	mxl5005s_SetRegsWithTable(fe, AddrTable, ByteTable, TableLen);
+	mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
+
+	return 0;
+}
+
+int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, u32 bandwidth)
+{
+	struct mxl5005s_state *state = fe->tuner_priv;
+	struct mxl5005s_config *c = state->config;
+
+	InitTunerControls(fe);
+
+	/* Set MxL5005S parameters. */
+	MXL5005_TunerConfig(
+		fe,
+		c->mod_mode,
+		c->if_mode,
+		bandwidth,
+		c->if_freq,
+		c->xtal_freq,
+		c->agc_mode,
+		c->top,
+		c->output_load,
+		c->clock_out,
+		c->div_out,
+		c->cap_select,
+		c->rssi_enable,
+		mod_type,
+		c->tracking_filter);
+
+	return 0;
+}
+
+static int mxl5005s_set_params(struct dvb_frontend *fe,
+			       struct dvb_frontend_parameters *params)
+{
+	struct mxl5005s_state *state = fe->tuner_priv;
+	u32 req_mode, req_bw = 0;
+	int ret;
+
+	dprintk(1, "%s()\n", __func__);
+
+	if (fe->ops.info.type == FE_ATSC) {
+		switch (params->u.vsb.modulation) {
+		case VSB_8:
+			req_mode = MXL_ATSC; break;
+		default:
+		case QAM_64:
+		case QAM_256:
+		case QAM_AUTO:
+			req_mode = MXL_QAM; break;
+		}
+	}
+	else req_mode = MXL_DVBT;
+
+	/* Change tuner for new modulation type if reqd */
+	if (req_mode != state->current_mode) {
+		switch (req_mode) {
+		case VSB_8:
+		case QAM_64:
+		case QAM_256:
+		case QAM_AUTO:
+			req_bw  = MXL5005S_BANDWIDTH_6MHZ;
+			break;
+		default:
+			/* Assume DVB-T */
+			switch (params->u.ofdm.bandwidth) {
+			case BANDWIDTH_6_MHZ:
+				req_bw  = MXL5005S_BANDWIDTH_6MHZ;
+				break;
+			case BANDWIDTH_7_MHZ:
+				req_bw  = MXL5005S_BANDWIDTH_7MHZ;
+				break;
+			case BANDWIDTH_AUTO:
+			case BANDWIDTH_8_MHZ:
+				req_bw  = MXL5005S_BANDWIDTH_8MHZ;
+				break;
+			}
+		}
+
+		state->current_mode = req_mode;
+		ret = mxl5005s_reconfigure(fe, req_mode, req_bw);
+
+	} else
+		ret = 0;
+
+	if (ret == 0) {
+		dprintk(1, "%s() freq=%d\n", __func__, params->frequency);
+		ret = mxl5005s_SetRfFreqHz(fe, params->frequency);
+	}
+
+	return ret;
+}
+
+static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct mxl5005s_state *state = fe->tuner_priv;
+	dprintk(1, "%s()\n", __func__);
+
+	*frequency = state->RF_IN;
+
+	return 0;
+}
+
+static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+	struct mxl5005s_state *state = fe->tuner_priv;
+	dprintk(1, "%s()\n", __func__);
+
+	*bandwidth = state->Chan_Bandwidth;
 
 	return 0;
 }
@@ -4698,7 +4637,6 @@ static const struct dvb_tuner_ops mxl5005s_tuner_ops = {
 	.set_params    = mxl5005s_set_params,
 	.get_frequency = mxl5005s_get_frequency,
 	.get_bandwidth = mxl5005s_get_bandwidth,
-	.get_status    = mxl5005s_get_status
 };
 
 struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
@@ -4715,6 +4653,7 @@ struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
 	state->frontend = fe;
 	state->config = config;
 	state->i2c = i2c;
+	state->current_mode = MXL_QAM;
 
 	printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n", config->i2c_address);
 
@@ -4726,8 +4665,5 @@ struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
 EXPORT_SYMBOL(mxl5005s_attach);
 
 MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver");
-MODULE_AUTHOR("Jan Hoogenraad");
-MODULE_AUTHOR("Barnaby Shearer");
-MODULE_AUTHOR("Andy Hasper");
 MODULE_AUTHOR("Steven Toth");
 MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h
index 7d0727d44536..687cf146c2a0 100644
--- a/drivers/media/common/tuners/mxl5005s.h
+++ b/drivers/media/common/tuners/mxl5005s.h
@@ -1,63 +1,118 @@
 /*
- * For the Realtek RTL chip RTL2831U
- * Realtek Release Date: 2008-03-14, ver 080314
- * Realtek version RTL2831 Linux driver version 080314
- * ver 080314
- *
- * for linux kernel version 2.6.21.4 - 2.6.22-14
- * support MXL5005s and MT2060 tuners (support tuner auto-detecting)
- * support two IR types -- RC5 and NEC
- *
- * Known boards with Realtek RTL chip RTL2821U
- *    Freecom USB stick 14aa:0160 (version 4)
- *    Conceptronic CTVDIGRCU
- *
- * Copyright (c) 2008 Realtek
- * Copyright (c) 2008 Jan Hoogenraad, Barnaby Shearer, Andy Hasper
- * This code is placed under the terms of the GNU General Public License
- *
- * Released by Realtek under GPLv2.
- * Thanks to Realtek for a lot of support we received !
- *
- *  Revision: 080314 - original version
- */
+    MaxLinear MXL5005S VSB/QAM/DVBT tuner driver
 
+    Copyright (C) 2008 MaxLinear
+    Copyright (C) 2008 Steven Toth <stoth@hauppauge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
 
 #ifndef __MXL5005S_H
 #define __MXL5005S_H
 
 #include <linux/dvb/frontend.h>
 
-/* IF frequency */
-enum IF_FREQ_HZ
-{
-	IF_FREQ_4570000HZ  =  4570000,                  ///<   IF frequency =   4.57 MHz
-	IF_FREQ_4571429HZ  =  4571429,                  ///<   IF frequency =  4.571 MHz
-	IF_FREQ_5380000HZ  =  5380000,                  ///<   IF frequency =   5.38 MHz
-	IF_FREQ_36000000HZ = 36000000,                  ///<   IF frequency = 36.000 MHz
-	IF_FREQ_36125000HZ = 36125000,                  ///<   IF frequency = 36.125 MHz
-	IF_FREQ_36166667HZ = 36166667,                  ///<   IF frequency = 36.167 MHz
-	IF_FREQ_44000000HZ = 44000000,                  ///<   IF frequency = 44.000 MHz
-};
-
-/* Crystal frequency */
-enum CRYSTAL_FREQ_HZ
-{
-	CRYSTAL_FREQ_4000000HZ  =  4000000,                     ///<   Crystal frequency =  4.0 MHz
-	CRYSTAL_FREQ_16000000HZ = 16000000,                     ///<   Crystal frequency = 16.0 MHz
-	CRYSTAL_FREQ_25000000HZ = 25000000,                     ///<   Crystal frequency = 25.0 MHz
-	CRYSTAL_FREQ_28800000HZ = 28800000,                     ///<   Crystal frequency = 28.8 MHz
-};
-
 struct mxl5005s_config
 {
+	/* 7 bit i2c address */
 	u8 i2c_address;
 
+#define IF_FREQ_4570000HZ    4570000
+#define IF_FREQ_4571429HZ    4571429
+#define IF_FREQ_5380000HZ    5380000
+#define IF_FREQ_36000000HZ  36000000
+#define IF_FREQ_36125000HZ  36125000
+#define IF_FREQ_36166667HZ  36166667
+#define IF_FREQ_44000000HZ  44000000
+	u32 if_freq;
+
+#define CRYSTAL_FREQ_4000000HZ    4000000
+#define CRYSTAL_FREQ_16000000HZ  16000000
+#define CRYSTAL_FREQ_25000000HZ  25000000
+#define CRYSTAL_FREQ_28800000HZ  28800000
+	u32 xtal_freq;
+
+#define MXL_DUAL_AGC   0
+#define MXL_SINGLE_AGC 1
+	u8 agc_mode;
+
+#define MXL_TF_DEFAULT	0
+#define MXL_TF_OFF	1
+#define MXL_TF_C	2
+#define MXL_TF_C_H	3
+#define MXL_TF_D	4
+#define MXL_TF_D_L	5
+#define MXL_TF_E	6
+#define MXL_TF_F	7
+#define MXL_TF_E_2	8
+#define MXL_TF_E_NA	9
+#define MXL_TF_G	10
+	u8 tracking_filter;
+
+#define MXL_RSSI_DISABLE	0
+#define MXL_RSSI_ENABLE		1
+	u8 rssi_enable;
+
+#define MXL_CAP_SEL_DISABLE	0
+#define MXL_CAP_SEL_ENABLE	1
+	u8 cap_select;
+
+#define MXL_DIV_OUT_1	0
+#define MXL_DIV_OUT_4	1
+	u8 div_out;
+
+#define MXL_CLOCK_OUT_DISABLE	0
+#define MXL_CLOCK_OUT_ENABLE	1
+	u8 clock_out;
+
+#define MXL5005S_IF_OUTPUT_LOAD_200_OHM 200
+#define MXL5005S_IF_OUTPUT_LOAD_300_OHM 300
+	u32 output_load;
+
+#define MXL5005S_TOP_5P5   55
+#define MXL5005S_TOP_7P2   72
+#define MXL5005S_TOP_9P2   92
+#define MXL5005S_TOP_11P0 110
+#define MXL5005S_TOP_12P9 129
+#define MXL5005S_TOP_14P7 147
+#define MXL5005S_TOP_16P8 168
+#define MXL5005S_TOP_19P4 194
+#define MXL5005S_TOP_21P2 212
+#define MXL5005S_TOP_23P2 232
+#define MXL5005S_TOP_25P2 252
+#define MXL5005S_TOP_27P1 271
+#define MXL5005S_TOP_29P2 292
+#define MXL5005S_TOP_31P7 317
+#define MXL5005S_TOP_34P9 349
+	u32 top;
+
+#define MXL_ANALOG_MODE  0
+#define MXL_DIGITAL_MODE 1
+	u8 mod_mode;
+
+#define MXL_ZERO_IF 0
+#define MXL_LOW_IF  1
+	u8 if_mode;
+
 	/* Stuff I don't know what to do with */
 	u8 AgcMasterByte;
 };
 
-#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || (defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE))
+#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || \
+	(defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE))
 extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
 					    struct i2c_adapter *i2c,
 					    struct mxl5005s_config *config);