Patchwork af901x: NXP TDA18218

login
register
mail settings
Submitter Bert Massop
Date Feb. 26, 2010, 11:01 p.m.
Message ID <41ef408f1002261501q17922555wc5ae022fecf4a63c@mail.gmail.com>
Download mbox | patch
Permalink /patch/82494/
State RFC
Headers show

Comments

Patch

diff -r 0f41fd7df85d linux/drivers/media/common/tuners/Kconfig
--- a/linux/drivers/media/common/tuners/Kconfig	Thu Feb 11 02:33:12 2010 +0200
+++ b/linux/drivers/media/common/tuners/Kconfig	Thu Feb 25 17:21:11 2010 +0100
@@ -179,4 +179,11 @@ 
 	help
 	  A driver for the silicon tuner MAX2165 from Maxim.
 
+config MEDIA_TUNER_TDA18218
+	tristate "NXP TDA18218 silicon tuner"
+	depends on VIDEO_MEDIA && I2C
+	default m if MEDIA_TUNER_CUSTOMISE
+	help
+	  A driver for the silicon tuner TDA18218 from NXP.
+
 endif # MEDIA_TUNER_CUSTOMISE
diff -r 0f41fd7df85d linux/drivers/media/common/tuners/Makefile
--- a/linux/drivers/media/common/tuners/Makefile	Thu Feb 11 02:33:12 2010 +0200
+++ b/linux/drivers/media/common/tuners/Makefile	Thu Feb 25 17:21:11 2010 +0100
@@ -24,6 +24,7 @@ 
 obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
 obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
 obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
+obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff -r 0f41fd7df85d linux/drivers/media/common/tuners/tda18218.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/common/tuners/tda18218.c	Thu Feb 25 17:21:11 2010 +0100
@@ -0,0 +1,471 @@ 
+/*
+ *  Driver for NXP TDA18218 silicon tuner
+ *
+ *  Copyright (C) 2010 Lauris Ding <lding@gmx.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 "tda18218.h"
+#include "compat.h"
+#include "tda18218_priv.h"
+
+static int tda18218_write_reg(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	u8 buf[2] = { reg, val };
+	struct i2c_msg msg = { .addr = priv->cfg->i2c_address, .flags = 0,
+			       .buf = buf, .len = 2 };
+	int ret;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	/* write register */
+	ret = i2c_transfer(priv->i2c, &msg, 1);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	if (ret != 1)
+		printk(KERN_WARNING "I2C write failed ret: %d reg: %02x\n", ret, reg);
+
+	return (ret == 1 ? 0 : ret);
+}
+
+static int tda18218_write_regs(struct dvb_frontend *fe, u8 reg,
+	u8 *val, u8 len)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	u8 buf[1+len];
+	struct i2c_msg msg = {
+		.addr = priv->cfg->i2c_address,
+		.flags = 0,
+		.len = sizeof(buf),
+		.buf = buf };
+		
+	int ret;
+
+	buf[0] = reg;
+	memcpy(&buf[1], val, len);
+	
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	ret = i2c_transfer(priv->i2c, &msg, 1);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	if (ret != 1)
+		printk(KERN_WARNING "I2C write failed ret: %d reg: %02x len: %d\n", ret, reg, len);
+	
+	return (ret == 1 ? 0 : ret);
+}
+
+/*static int tda18218_read_reg(struct tda18218_priv *priv, u16 reg, u8 *val)
+{
+	u8 obuf[3] = { reg >> 8, reg & 0xff, 0 };
+	u8 ibuf[1];
+	struct i2c_msg msg[2] = {
+		{
+			.addr = 0x3a,
+			.flags = 0,
+			.len = sizeof(obuf),
+			.buf = obuf
+		}, {
+			.addr = 0x3a,
+			.flags = I2C_M_RD,
+			.len = sizeof(ibuf),
+			.buf = ibuf
+		}
+	};
+
+	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+		printk(KERN_WARNING "I2C read failed reg:%04x\n", reg);
+		return -EREMOTEIO;
+	}
+	*val = ibuf[0];
+	return 0;
+}*/
+
+static int tda18218_read_regs(struct dvb_frontend *fe)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	u8 *regs = priv->tda18218_regs;
+	u8 buf = 0x00;
+	int ret;
+	//int i;
+	struct i2c_msg msg[] = {
+		{ .addr = 0xc0, .flags = 0,
+		  .buf = &buf, .len = 1 },
+		{ .addr = 0xc0, .flags = I2C_M_RD,
+		  .buf = regs, .len = 59 }
+	};
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	/* read all registers */
+	ret = i2c_transfer(priv->i2c, msg, 2);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	if (ret != 2)
+		printk(KERN_WARNING "I2C read failed ret: %d\n", ret);
+	
+	/*for(i = 0; i <= 58; i++)
+		printk("Register %d: %02x\n", i, 0xff & regs[i]);*/
+
+	return (ret == 2 ? 0 : ret);
+}
+
+static int tda18218_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	u8 *regs = priv->tda18218_regs;
+	u8 Fc, BP;
+	int i, ret;
+	u16 if1, bw;
+	u32 freq;
+	
+	u8 paramsbuf[4][6] = {
+		{ 0x03, 0x1a },
+		{ 0x04, 0x0a },
+		{ 0x01, 0x0f },
+		{ 0x01, 0x0f },
+	};
+	
+	u8 agcbuf[][2] = {
+		{ 0x1a, 0x0e },
+		{ 0x20, 0x60 },
+		{ 0x23, 0x02 },
+		{ 0x20, 0xa0 },
+		{ 0x23, 0x09 },
+		{ 0x20, 0xe0 },
+		{ 0x23, 0x0c },
+		{ 0x20, 0x40 },
+		{ 0x23, 0x01 },
+		{ 0x20, 0x80 },
+		{ 0x23, 0x08 },
+		{ 0x20, 0xc0 },
+		{ 0x23, 0x0b },
+		{ 0x24, 0x1c },
+		{ 0x24, 0x0c },
+	};
+	
+	switch (params->u.ofdm.bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		bw = 6000;
+		Fc = 0;
+		break;
+	case BANDWIDTH_7_MHZ:
+		bw = 7000;
+		Fc = 1;
+		break;
+	case BANDWIDTH_8_MHZ:
+		bw = 8000;
+		Fc = 2;
+		break;
+	default:
+		printk(KERN_WARNING "Invalid bandwidth");
+		return -EINVAL;
+	}
+	
+	if1 = bw / 2;
+	
+	if((params->frequency >= 174000000) && (params->frequency < 188000000)) {
+		BP = 3;
+	}
+	else if((params->frequency >= 188000000) && (params->frequency < 253000000)) {
+		BP = 4;
+	}
+	else if((params->frequency >= 253000000) && (params->frequency < 343000000)) {
+		BP = 5;
+	}
+	else if((params->frequency >= 343000000) && (params->frequency <= 870000000)) {
+		BP = 6;
+	}
+	else {
+		printk(KERN_WARNING "Frequency out of range");
+		return -EINVAL;
+	}
+	
+	freq = params->frequency;
+	freq /= 1000;
+	freq +=if1;
+	freq *= 16;
+	
+	tda18218_read_regs(fe);
+	
+	paramsbuf[0][2] = regs[0x1a] | BP;
+	paramsbuf[0][3] = regs[0x1b] & ~3;
+	paramsbuf[0][3] = regs[0x1b] | Fc;
+	paramsbuf[0][4] = regs[0x1c] | 0x0a;
+	
+	paramsbuf[1][2] = freq >> 16;
+	paramsbuf[1][3] = freq >> 8;
+	paramsbuf[1][4] = (freq & 0xf0) | (regs[0x0c] & 0x0f);
+	paramsbuf[1][5] = 0xff;
+	paramsbuf[2][2] = regs[0x0f] | 0x40;
+	paramsbuf[3][2] = 0x09;
+	
+	tda18218_write_reg(fe, 0x04, 0x03);
+
+	for(i = 0; i < ARRAY_SIZE(paramsbuf); i++) {
+
+		/* write registers */
+		ret = tda18218_write_regs(fe, paramsbuf[i][1], &paramsbuf[i][2], paramsbuf[i][0]);
+
+		if (ret)
+			goto error;
+	}
+	for(i = 0; i < ARRAY_SIZE(agcbuf); i++) {
+		tda18218_write_reg(fe, agcbuf[i][0], agcbuf[i][1]);
+	}
+	
+	//tda18218_write_reg(fe, 0x03, 0x00);
+	//tda18218_write_reg(fe, 0x04, 0x00);
+	//tda18218_write_reg(fe, 0x20, 0xc7);
+	
+	msleep(60);
+	i = 0;
+	while(i < 10) {
+		tda18218_read_regs(fe);
+		if((regs[0x01] & 0x60) == 0x60)
+			printk(KERN_INFO "We've got a lock!"); break;
+		msleep(20);
+		i++;
+	}
+	
+	priv->bandwidth = params->u.ofdm.bandwidth;
+	priv->frequency = params->frequency;
+	return 0;
+error:
+	return ret;
+}
+
+static int tda18218_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	*frequency = priv->frequency;
+	return 0;
+}
+
+static int tda18218_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+	struct tda18218_priv *priv = fe->tuner_priv;
+	*bandwidth = priv->bandwidth;
+	return 0;
+}
+
+static int tda18218_init(struct dvb_frontend *fe)
+{
+	//struct tda18218_priv *priv = fe->tuner_priv;
+	//u8 *regs = priv->tda18218_regs;
+	int i;
+	int ret;
+	
+	u8 initbuf[][18] = {
+		{ 0x10, 0x05, 0x00, 0x00, 0xd0, 0x00, 0x40, 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00, 0x01 },
+		{ 0x0b, 0x15, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x0e, 0x29, 0x98, 0x00, 0x00, 0x58 },
+		{ 0x10, 0x24, 0x0c, 0x48, 0x85, 0xc9, 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00, 0x8a, 0x00 },
+		{ 0x07, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6 },
+	};
+	
+	u8 initbuf2[4];
+
+	for(i = 0; i < ARRAY_SIZE(initbuf); i++) {
+		
+		/* write registers */
+		ret = tda18218_write_regs(fe, initbuf[i][1], &initbuf[i][2], initbuf[i][0]);
+
+		if (ret != 0) {
+			printk(KERN_ERR "init: ERROR: i2c_transfer returned: %d\n", ret);
+			return -EREMOTEIO;
+		}
+		if(i == 1) {
+			tda18218_write_reg(fe, 0x22, 0x8c);
+		}
+	}
+	
+	tda18218_write_reg(fe, 0x05, 0x80);
+	tda18218_write_reg(fe, 0x05, 0x00);
+	tda18218_write_reg(fe, 0x05, 0x20);
+	tda18218_write_reg(fe, 0x05, 0x00);
+	tda18218_write_reg(fe, 0x27, 0xde);
+	tda18218_write_reg(fe, 0x17, 0xf8);
+	tda18218_write_reg(fe, 0x18, 0x0f);
+	tda18218_write_reg(fe, 0x1c, 0x8b);
+	tda18218_write_reg(fe, 0x29, 0x02);
+	tda18218_write_reg(fe, 0x19, 0x1a);
+	tda18218_write_reg(fe, 0x11, 0x13);
+
+	initbuf2[0] = 0x0a;
+	initbuf2[1] = 0x5c;
+	initbuf2[2] = 0xc6;
+	initbuf2[3] = 0x07;
+	tda18218_write_regs(fe, initbuf2[0], &initbuf2[1], 3);
+	tda18218_write_reg(fe, 0x0f, 0x49);
+	tda18218_write_reg(fe, 0x05, 0x40);
+	tda18218_write_reg(fe, 0x05, 0x00);
+	tda18218_write_reg(fe, 0x05, 0x20);
+	tda18218_write_reg(fe, 0x11, 0xed);
+	tda18218_write_reg(fe, 0x0f, 0x49);
+	tda18218_write_reg(fe, 0x19, 0x2a);
+	tda18218_write_reg(fe, 0x05, 0x58);
+	tda18218_write_reg(fe, 0x05, 0x18);
+	tda18218_write_reg(fe, 0x05, 0x38);
+	tda18218_write_reg(fe, 0x29, 0x03);
+	tda18218_write_reg(fe, 0x19, 0x1a);
+	tda18218_write_reg(fe, 0x11, 0x13);
+	initbuf2[0] = 0x0a;
+	initbuf2[1] = 0xbe;
+	initbuf2[2] = 0x6e;
+	initbuf2[3] = 0x07;
+	tda18218_write_regs(fe, initbuf2[0], &initbuf2[1], 3);
+	tda18218_write_reg(fe, 0x0f, 0x49);
+	tda18218_write_reg(fe, 0x05, 0x58);
+	tda18218_write_reg(fe, 0x05, 0x18);
+	tda18218_write_reg(fe, 0x05, 0x38);
+	tda18218_write_reg(fe, 0x11, 0xed);
+	tda18218_write_reg(fe, 0x0f, 0x49);
+	tda18218_write_reg(fe, 0x19, 0x2a);
+	tda18218_write_reg(fe, 0x05, 0x58);
+	tda18218_write_reg(fe, 0x05, 0x18);
+	tda18218_write_reg(fe, 0x05, 0x38);
+	tda18218_write_reg(fe, 0x19, 0x0a);
+	tda18218_write_reg(fe, 0x27, 0xc9);
+	tda18218_write_reg(fe, 0x11, 0x13);
+	initbuf2[0] = 0x17;
+	initbuf2[1] = 0xf0;
+	initbuf2[2] = 0x19;
+	initbuf2[3] = 0x00;
+	tda18218_write_regs(fe, initbuf2[0], &initbuf2[1], 2);
+	tda18218_write_reg(fe, 0x1c, 0x98);
+	tda18218_write_reg(fe, 0x29, 0x03);
+	tda18218_write_reg(fe, 0x2a, 0x00);
+	tda18218_write_reg(fe, 0x2a, 0x01);
+	tda18218_write_reg(fe, 0x2a, 0x02);
+	tda18218_write_reg(fe, 0x2a, 0x03);
+	tda18218_write_reg(fe, 0x1c, 0x98);
+	tda18218_write_reg(fe, 0x18, 0x19);
+	tda18218_write_reg(fe, 0x22, 0x9c);
+	tda18218_write_reg(fe, 0x1f, 0x58);
+	tda18218_write_reg(fe, 0x24, 0x0c);
+	tda18218_write_reg(fe, 0x1c, 0x88);
+	tda18218_write_reg(fe, 0x20, 0x10);
+	tda18218_write_reg(fe, 0x21, 0x4c);
+	tda18218_write_reg(fe, 0x20, 0x00);
+	tda18218_write_reg(fe, 0x21, 0x48);
+	tda18218_write_reg(fe, 0x1f, 0x5b);
+	tda18218_write_reg(fe, 0x20, 0x00);
+	tda18218_write_reg(fe, 0x1f, 0x59);
+	tda18218_write_reg(fe, 0x20, 0x00);
+	tda18218_write_reg(fe, 0x1f, 0x5a);
+	tda18218_write_reg(fe, 0x20, 0x00);
+	tda18218_write_reg(fe, 0x1f, 0x5f);
+	tda18218_write_reg(fe, 0x20, 0x00);
+	tda18218_write_reg(fe, 0x1f, 0x5d);
+	tda18218_write_reg(fe, 0x20, 0x00);
+	tda18218_write_reg(fe, 0x1f, 0x5e);
+	tda18218_write_reg(fe, 0x20, 0x00);
+	tda18218_write_reg(fe, 0x20, 0x60);
+	tda18218_write_reg(fe, 0x23, 0x02);
+	tda18218_write_reg(fe, 0x20, 0xa0);
+	tda18218_write_reg(fe, 0x23, 0x09);
+	tda18218_write_reg(fe, 0x20, 0xe0);
+	tda18218_write_reg(fe, 0x23, 0x0c);
+	tda18218_write_reg(fe, 0x20, 0x40);
+	tda18218_write_reg(fe, 0x23, 0x01);
+	tda18218_write_reg(fe, 0x20, 0x80);
+	tda18218_write_reg(fe, 0x23, 0x08);
+	tda18218_write_reg(fe, 0x20, 0xc0);
+	tda18218_write_reg(fe, 0x23, 0x0b);
+	tda18218_write_reg(fe, 0x1c, 0x98);
+	tda18218_write_reg(fe, 0x22, 0x8c);
+	initbuf2[0] = 0x17;
+	initbuf2[1] = 0xb0;
+	initbuf2[2] = 0x59;
+	initbuf2[3] = 0x00;
+	//tda18218_write_regs(fe, initbuf2[0], &initbuf2[1], 2);
+	initbuf2[0] = 0x1a;
+	initbuf2[1] = 0x0e;
+	initbuf2[2] = 0x2a;
+	initbuf2[3] = 0x98;
+	tda18218_write_regs(fe, initbuf2[0], &initbuf2[1], 3);
+	initbuf2[0] = 0x17;
+	initbuf2[1] = 0xb0;
+	initbuf2[2] = 0x59;
+	initbuf2[3] = 0x00;
+	tda18218_write_regs(fe, initbuf2[0], &initbuf2[1], 2);
+	tda18218_write_reg(fe, 0x2d, 0x81);
+	tda18218_write_reg(fe, 0x29, 0x02);
+	
+	return 0;
+}
+	
+static int tda18218_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static const struct dvb_tuner_ops tda18218_tuner_ops = {
+	.info = {
+		.name           = "NXP TDA18218",
+		.frequency_min  = TDA18218_MIN_FREQ,
+		.frequency_max  = TDA18218_MAX_FREQ,
+		.frequency_step = TDA18218_STEP,
+	},
+
+	.release       = tda18218_release,
+	.init          = tda18218_init,
+	
+	.set_params = tda18218_set_params,
+	.get_frequency = tda18218_get_frequency,
+	.get_bandwidth = tda18218_get_bandwidth,
+};
+
+struct dvb_frontend * tda18218_attach(struct dvb_frontend *fe,
+				    struct i2c_adapter *i2c,
+				    struct tda18218_config *cfg)
+{
+	struct tda18218_priv *priv = NULL;
+
+	priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->cfg = cfg;
+	priv->i2c = i2c;
+
+	fe->tuner_priv = priv;
+	
+	tda18218_read_regs(fe);
+	if (priv->tda18218_regs[0x00] != 0xc0) {
+		printk(KERN_WARNING "Device is not a TDA18218!\n");
+		kfree(priv);
+		return NULL;
+	}
+	
+	printk(KERN_INFO "NXP TDA18218 successfully identified.\n");
+	memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
+	       sizeof(struct dvb_tuner_ops));
+	
+	return fe;
+}
+EXPORT_SYMBOL(tda18218_attach);
+
+MODULE_DESCRIPTION("NXP TDA18218 silicon tuner driver");
+MODULE_AUTHOR("Lauris Ding <lding@gmx.de>");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff -r 0f41fd7df85d linux/drivers/media/common/tuners/tda18218.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/common/tuners/tda18218.h	Thu Feb 25 17:21:11 2010 +0100
@@ -0,0 +1,44 @@ 
+/*
+ *  Driver for  NXP TDA18218 silicon tuner
+ *
+ *  Copyright (C) 2010 Lauris Ding <lding@gmx.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 TDA18218_H
+#define TDA18218_H
+
+#include "dvb_frontend.h"
+
+struct tda18218_config {
+	u8 i2c_address;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_TDA18218) || (defined(CONFIG_MEDIA_TUNER_TDA18218_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
+					  struct i2c_adapter *i2c,
+					  struct tda18218_config *cfg);
+#else
+static inline struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
+						 struct i2c_adapter *i2c,
+						 struct tda18218_config *cfg)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif // CONFIG_MEDIA_TUNER_TDA18218
+
+#endif
\ No newline at end of file
diff -r 0f41fd7df85d linux/drivers/media/common/tuners/tda18218_priv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/common/tuners/tda18218_priv.h	Thu Feb 25 17:21:11 2010 +0100
@@ -0,0 +1,36 @@ 
+/*
+ *  Driver for NXP TDA18218 silicon tuner
+ *
+ *  Copyright (C) 2010 Lauris Ding <lding@gmx.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 TDA18218_PRIV_H
+#define TDA18218_PRIV_H
+
+#define TDA18218_STEP         1000 /* 1 kHz */
+#define TDA18218_MIN_FREQ   174000000 /*   174 MHz */
+#define TDA18218_MAX_FREQ  864000000 /*  864 MHz */
+
+struct tda18218_priv {
+	u8 tda18218_regs[0x3b];
+	struct tda18218_config *cfg;
+	struct i2c_adapter *i2c;
+
+	u32 frequency;
+	u32 bandwidth;
+};
+
+#endif
diff -r 0f41fd7df85d linux/drivers/media/dvb/dvb-usb/af9015.c
--- a/linux/drivers/media/dvb/dvb-usb/af9015.c	Thu Feb 11 02:33:12 2010 +0200
+++ b/linux/drivers/media/dvb/dvb-usb/af9015.c	Thu Feb 25 17:21:11 2010 +0100
@@ -20,11 +20,7 @@ 
  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
 
-#include <linux/hash.h>
-
-#endif
 #include "af9015.h"
 #include "af9013.h"
 #include "mt2060.h"
@@ -32,6 +28,7 @@ 
 #include "tda18271.h"
 #include "mxl5005s.h"
 #include "mc44s803.h"
+#include "tda18218.h"
 
 static int dvb_usb_af9015_debug;
 module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
@@ -273,7 +270,8 @@ 
 
 	while (i < num) {
 		if (msg[i].addr == af9015_af9013_config[0].demod_address ||
-		    msg[i].addr == af9015_af9013_config[1].demod_address) {
+		    msg[i].addr == af9015_af9013_config[1].demod_address  ||
+		    msg[i].addr == 0x3a) {
 			addr = msg[i].buf[0] << 8;
 			addr += msg[i].buf[1];
 			mbox = msg[i].buf[2];
@@ -286,7 +284,8 @@ 
 
 		if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
 			if (msg[i].addr ==
-				af9015_af9013_config[0].demod_address)
+				af9015_af9013_config[0].demod_address ||
+			    msg[i].addr == 0x3a)
 				req.cmd = READ_MEMORY;
 			else
 				req.cmd = READ_I2C;
@@ -301,7 +300,8 @@ 
 		} else if (msg[i].flags & I2C_M_RD) {
 			ret = -EINVAL;
 			if (msg[i].addr ==
-				af9015_af9013_config[0].demod_address)
+				af9015_af9013_config[0].demod_address ||
+			    msg[i].addr == 0x3a)
 				goto error;
 			else
 				req.cmd = READ_I2C;
@@ -315,7 +315,8 @@ 
 			i += 1;
 		} else {
 			if (msg[i].addr ==
-				af9015_af9013_config[0].demod_address)
+				af9015_af9013_config[0].demod_address ||
+			    msg[i].addr == 0x3a)
 				req.cmd = WRITE_MEMORY;
 			else
 				req.cmd = WRITE_I2C;
@@ -560,24 +561,11 @@ 
 	return ret;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+/* dump eeprom */
 static int af9015_eeprom_dump(struct dvb_usb_device *d)
-#else
-/* hash (and dump) eeprom */
-static int af9015_eeprom_hash(struct usb_device *udev)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
 	u8 reg, val;
-#else
-	static const unsigned int eeprom_size = 256;
-	unsigned int reg;
-	int ret;
-	u8 val, *eeprom;
-	struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
-#endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
 	for (reg = 0; ; reg++) {
 		if (reg % 16 == 0) {
 			if (reg)
@@ -590,43 +578,9 @@ 
 			deb_info(KERN_CONT " --");
 		if (reg == 0xff)
 			break;
-#else
-	eeprom = kmalloc(eeprom_size, GFP_KERNEL);
-	if (eeprom == NULL)
-		return -ENOMEM;
-
-	for (reg = 0; reg < eeprom_size; reg++) {
-		req.addr = reg;
-		ret = af9015_rw_udev(udev, &req);
-		if (ret)
-			goto free;
-		eeprom[reg] = val;
-#endif
 	}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
 	deb_info(KERN_CONT "\n");
 	return 0;
-#else
-	if (dvb_usb_af9015_debug & 0x01)
-		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, eeprom,
-				eeprom_size);
-
-	BUG_ON(eeprom_size % 4);
-
-	af9015_config.eeprom_sum = 0;
-	for (reg = 0; reg < eeprom_size / sizeof(u32); reg++) {
-		af9015_config.eeprom_sum *= GOLDEN_RATIO_PRIME_32;
-		af9015_config.eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]);
-	}
-
-	deb_info("%s: eeprom sum=%.8x\n", __func__, af9015_config.eeprom_sum);
-
-	ret = 0;
-free:
-	kfree(eeprom);
-	return ret;
-#endif
 }
 
 static int af9015_download_ir_table(struct dvb_usb_device *d)
@@ -765,132 +719,12 @@ 
 	return ret;
 }
 
-struct af9015_setup {
-	unsigned int id;
-	struct dvb_usb_rc_key *rc_key_map;
-	unsigned int rc_key_map_size;
-	u8 *ir_table;
-	unsigned int ir_table_size;
-};
-
-static const struct af9015_setup *af9015_setup_match(unsigned int id,
-		const struct af9015_setup *table)
-{
-	for (; table->rc_key_map; table++)
-		if (table->id == id)
-			return table;
-	return NULL;
-}
-
-static const struct af9015_setup af9015_setup_modparam[] = {
-	{ AF9015_REMOTE_A_LINK_DTU_M,
-		af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link),
-		af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) },
-	{ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
-		af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi),
-		af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) },
-	{ AF9015_REMOTE_MYGICTV_U718,
-		af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv),
-		af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) },
-	{ AF9015_REMOTE_DIGITTRADE_DVB_T,
-		af9015_rc_keys_digittrade, ARRAY_SIZE(af9015_rc_keys_digittrade),
-		af9015_ir_table_digittrade, ARRAY_SIZE(af9015_ir_table_digittrade) },
-	{ AF9015_REMOTE_AVERMEDIA_KS,
-		af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia),
-		af9015_ir_table_avermedia_ks, ARRAY_SIZE(af9015_ir_table_avermedia_ks) },
-	{ }
-};
-
-/* don't add new entries here anymore, use hashes instead */
-static const struct af9015_setup af9015_setup_usbids[] = {
-	{ USB_VID_LEADTEK,
-		af9015_rc_keys_leadtek, ARRAY_SIZE(af9015_rc_keys_leadtek),
-		af9015_ir_table_leadtek, ARRAY_SIZE(af9015_ir_table_leadtek) },
-	{ USB_VID_VISIONPLUS,
-		af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan),
-		af9015_ir_table_twinhan, ARRAY_SIZE(af9015_ir_table_twinhan) },
-	{ USB_VID_KWORLD_2, /* TODO: use correct rc keys */
-		af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan),
-		af9015_ir_table_kworld, ARRAY_SIZE(af9015_ir_table_kworld) },
-	{ USB_VID_AVERMEDIA,
-		af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia),
-		af9015_ir_table_avermedia, ARRAY_SIZE(af9015_ir_table_avermedia) },
-	{ USB_VID_MSI_2,
-		af9015_rc_keys_msi_digivox_iii, ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii),
-		af9015_ir_table_msi_digivox_iii, ARRAY_SIZE(af9015_ir_table_msi_digivox_iii) },
-	{ }
-};
-
-static const struct af9015_setup af9015_setup_hashes[] = {
-	{ 0xb8feb708,
-		af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi),
-		af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) },
-	{ 0xa3703d00,
-		af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link),
-		af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) },
-	{ 0x9b7dc64e,
-		af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv),
-		af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) },
-	{ }
-};
-
-static void af9015_set_remote_config(struct usb_device *udev,
-		struct dvb_usb_device_properties *props)
-{
-	const struct af9015_setup *table = NULL;
-
-	if (dvb_usb_af9015_remote) {
-		/* load remote defined as module param */
-		table = af9015_setup_match(dvb_usb_af9015_remote,
-				af9015_setup_modparam);
-	} else {
-		u16 vendor = le16_to_cpu(udev->descriptor.idVendor);
-
-		table = af9015_setup_match(af9015_config.eeprom_sum,
-				af9015_setup_hashes);
-
-		if (!table && vendor == USB_VID_AFATECH) {
-			/* Check USB manufacturer and product strings and try
-			   to determine correct remote in case of chip vendor
-			   reference IDs are used.
-			   DO NOT ADD ANYTHING NEW HERE. Use hashes instead.
-			 */
-			char manufacturer[10];
-			memset(manufacturer, 0, sizeof(manufacturer));
-			usb_string(udev, udev->descriptor.iManufacturer,
-				manufacturer, sizeof(manufacturer));
-			if (!strcmp("MSI", manufacturer)) {
-				/* iManufacturer 1 MSI
-				   iProduct      2 MSI K-VOX */
-				table = af9015_setup_match(
-					AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
-					af9015_setup_modparam);
-			} else if (udev->descriptor.idProduct ==
-				cpu_to_le16(USB_PID_TREKSTOR_DVBT)) {
-				table = &(const struct af9015_setup){ 0,
-					af9015_rc_keys_trekstor,
-					ARRAY_SIZE(af9015_rc_keys_trekstor),
-					af9015_ir_table_trekstor,
-					ARRAY_SIZE(af9015_ir_table_trekstor)
-				};
-			}
-		} else if (!table)
-			table = af9015_setup_match(vendor, af9015_setup_usbids);
-	}
-
-	if (table) {
-		props->rc_key_map = table->rc_key_map;
-		props->rc_key_map_size = table->rc_key_map_size;
-		af9015_config.ir_table = table->ir_table;
-		af9015_config.ir_table_size = table->ir_table_size;
-	}
-}
-
 static int af9015_read_config(struct usb_device *udev)
 {
 	int ret;
 	u8 val, i, offset = 0;
 	struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
+	char manufacturer[10];
 
 	/* IR remote controller */
 	req.addr = AF9015_EEPROM_IR_MODE;
@@ -902,20 +736,158 @@ 
 	}
 	if (ret)
 		goto error;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
-	ret = af9015_eeprom_hash(udev);
-	if (ret)
-		goto error;
-
-#endif
 	deb_info("%s: IR mode:%d\n", __func__, val);
 	for (i = 0; i < af9015_properties_count; i++) {
 		if (val == AF9015_IR_MODE_DISABLED) {
 			af9015_properties[i].rc_key_map = NULL;
 			af9015_properties[i].rc_key_map_size  = 0;
-		} else
-			af9015_set_remote_config(udev, &af9015_properties[i]);
+		} else if (dvb_usb_af9015_remote) {
+			/* load remote defined as module param */
+			switch (dvb_usb_af9015_remote) {
+			case AF9015_REMOTE_A_LINK_DTU_M:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_a_link;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_a_link);
+				af9015_config.ir_table = af9015_ir_table_a_link;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_a_link);
+				break;
+			case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_msi;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_msi);
+				af9015_config.ir_table = af9015_ir_table_msi;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_msi);
+				break;
+			case AF9015_REMOTE_MYGICTV_U718:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_mygictv;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_mygictv);
+				af9015_config.ir_table =
+				  af9015_ir_table_mygictv;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_mygictv);
+				break;
+			case AF9015_REMOTE_DIGITTRADE_DVB_T:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_digittrade;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_digittrade);
+				af9015_config.ir_table =
+				  af9015_ir_table_digittrade;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_digittrade);
+				break;
+			case AF9015_REMOTE_AVERMEDIA_KS:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_avermedia;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_avermedia);
+				af9015_config.ir_table =
+				  af9015_ir_table_avermedia_ks;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_avermedia_ks);
+				break;
+			}
+		} else {
+			switch (le16_to_cpu(udev->descriptor.idVendor)) {
+			case USB_VID_LEADTEK:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_leadtek;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_leadtek);
+				af9015_config.ir_table =
+				  af9015_ir_table_leadtek;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_leadtek);
+				break;
+			case USB_VID_VISIONPLUS:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_twinhan;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_twinhan);
+				af9015_config.ir_table =
+				  af9015_ir_table_twinhan;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_twinhan);
+				break;
+			case USB_VID_KWORLD_2:
+				/* TODO: use correct rc keys */
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_twinhan;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_twinhan);
+				af9015_config.ir_table = af9015_ir_table_kworld;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_kworld);
+				break;
+			/* Check USB manufacturer and product strings and try
+			   to determine correct remote in case of chip vendor
+			   reference IDs are used. */
+			case USB_VID_AFATECH:
+				memset(manufacturer, 0, sizeof(manufacturer));
+				usb_string(udev, udev->descriptor.iManufacturer,
+					manufacturer, sizeof(manufacturer));
+				if (!strcmp("Geniatech", manufacturer)) {
+					/* iManufacturer 1 Geniatech
+					   iProduct      2 AF9015 */
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_mygictv;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_mygictv);
+					af9015_config.ir_table =
+					  af9015_ir_table_mygictv;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_mygictv);
+				} else if (!strcmp("MSI", manufacturer)) {
+					/* iManufacturer 1 MSI
+					   iProduct      2 MSI K-VOX */
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_msi;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_msi);
+					af9015_config.ir_table =
+					  af9015_ir_table_msi;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_msi);
+				} else if (udev->descriptor.idProduct ==
+					cpu_to_le16(USB_PID_TREKSTOR_DVBT)) {
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_trekstor;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_trekstor);
+					af9015_config.ir_table =
+					  af9015_ir_table_trekstor;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_trekstor);
+				}
+				break;
+			case USB_VID_AVERMEDIA:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_avermedia;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_avermedia);
+				af9015_config.ir_table =
+				  af9015_ir_table_avermedia;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_avermedia);
+				break;
+			case USB_VID_MSI_2:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_msi_digivox_iii;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii);
+				af9015_config.ir_table =
+				  af9015_ir_table_msi_digivox_iii;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_msi_digivox_iii);
+				break;
+			}
+		}
 	}
 
 	/* TS mode - one or two receivers */
@@ -1026,6 +998,7 @@ 
 		case AF9013_TUNER_MT2060_2:
 		case AF9013_TUNER_TDA18271:
 		case AF9013_TUNER_QT1010A:
+		case AF9013_TUNER_TDA18218:
 			af9015_af9013_config[i].rf_spec_inv = 1;
 			break;
 		case AF9013_TUNER_MXL5003D:
@@ -1037,9 +1010,6 @@ 
 			af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO;
 			af9015_af9013_config[i].rf_spec_inv = 1;
 			break;
-		case AF9013_TUNER_TDA18218:
-			warn("tuner NXP TDA18218 not supported yet");
-			return -ENODEV;
 		default:
 			warn("tuner id:%d not supported, please report!", val);
 			return -ENODEV;
@@ -1164,11 +1134,11 @@ 
 
 		deb_info("%s: init I2C\n", __func__);
 		ret = af9015_i2c_init(adap->dev);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+
+		/* dump eeprom (debug) */
 		ret = af9015_eeprom_dump(adap->dev);
 		if (ret)
 			return ret;
-#endif
 	} else {
 		/* select I2C adapter */
 		i2c_adap = &state->i2c_adap;
@@ -1247,6 +1217,10 @@ 
 	.dig_out = 1,
 };
 
+static struct tda18218_config af9015_tda18218_config = {
+	.i2c_address = 0xc0,
+};
+
 static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	struct af9015_state *state = adap->dev->priv;
@@ -1294,6 +1268,10 @@ 
 		ret = dvb_attach(mc44s803_attach, adap->fe, i2c_adap,
 			&af9015_mc44s803_config) == NULL ? -ENODEV : 0;
 		break;
+	case AF9013_TUNER_TDA18218:
+		ret = dvb_attach(tda18218_attach, adap->fe, i2c_adap,
+			&af9015_tda18218_config) == NULL ? -ENODEV : 0;
+		break;
 	case AF9013_TUNER_UNKNOWN:
 	default:
 		ret = -ENODEV;
@@ -1334,8 +1312,7 @@ 
 /* 25 */{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_399U_2)},
 	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_PC160_T)},
 	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_SVEON_STV20)},
-	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_TINYTWIN_2)},
-	{USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV2000DS)},
+	{USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_RC)},
 	{0},
 };
 MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1422,8 +1399,7 @@ 
 			},
 			{
 				.name = "DigitalNow TinyTwin DVB-T Receiver",
-				.cold_ids = {&af9015_usb_table[5],
-					     &af9015_usb_table[28], NULL},
+				.cold_ids = {&af9015_usb_table[5], NULL},
 				.warm_ids = {NULL},
 			},
 			{
@@ -1643,8 +1619,8 @@ 
 				.warm_ids = {NULL},
 			},
 			{
-				.name = "Leadtek WinFast DTV2000DS",
-				.cold_ids = {&af9015_usb_table[29], NULL},
+				.name = "TerraTec Cinergy T Stick RC",
+				.cold_ids = {&af9015_usb_table[28], NULL},
 				.warm_ids = {NULL},
 			},
 		}
diff -r 0f41fd7df85d linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h	Thu Feb 11 02:33:12 2010 +0200
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h	Thu Feb 25 17:21:11 2010 +0100
@@ -129,6 +129,7 @@ 
 #define USB_PID_KWORLD_VSTREAM_WARM			0x17df
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE		0x0055
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2		0x0069
+#define USB_PID_TERRATEC_CINERGY_T_STICK_RC        0x0097
 #define USB_PID_TWINHAN_VP7041_COLD			0x3201
 #define USB_PID_TWINHAN_VP7041_WARM			0x3202
 #define USB_PID_TWINHAN_VP7020_COLD			0x3203
diff -r 0f41fd7df85d linux/drivers/media/dvb/frontends/af9013.c
--- a/linux/drivers/media/dvb/frontends/af9013.c	Thu Feb 11 02:33:12 2010 +0200
+++ b/linux/drivers/media/dvb/frontends/af9013.c	Thu Feb 25 17:21:11 2010 +0100
@@ -488,6 +488,20 @@ 
 				break;
 			}
 		}
+		else if(state->config.tuner == AF9013_TUNER_TDA18218) {
+			switch (bw) {
+			case BANDWIDTH_6_MHZ:
+				if_sample_freq = 3000000; /* 3 MHz */
+				break;
+			case BANDWIDTH_7_MHZ:
+				if_sample_freq = 3500000; /* 3.5 MHz */
+				break;
+			case BANDWIDTH_8_MHZ:
+			default:
+				if_sample_freq = 4000000; /* 4 MHz */
+				break;
+			}
+		}
 
 		while (if_sample_freq > (adc_freq / 2))
 			if_sample_freq = if_sample_freq - adc_freq;
@@ -1390,6 +1404,7 @@ 
 		init = tuner_init_mt2060_2;
 		break;
 	case AF9013_TUNER_TDA18271:
+	case AF9013_TUNER_TDA18218:
 		len = ARRAY_SIZE(tuner_init_tda18271);
 		init = tuner_init_tda18271;
 		break;
diff -r 0f41fd7df85d linux/drivers/media/dvb/frontends/af9013.h
--- a/linux/drivers/media/dvb/frontends/af9013.h	Thu Feb 11 02:33:12 2010 +0200
+++ b/linux/drivers/media/dvb/frontends/af9013.h	Thu Feb 25 17:21:11 2010 +0100
@@ -44,7 +44,7 @@ 
 	AF9013_TUNER_MT2060_2   = 147, /* Microtune */
 	AF9013_TUNER_TDA18271   = 156, /* NXP */
 	AF9013_TUNER_QT1010A    = 162, /* Quantek */
-	AF9013_TUNER_TDA18218   = 179, /* NXP */
+	AF9013_TUNER_TDA18218    = 179, /* NXP */
 };
 
 /* AF9013/5 GPIOs (mostly guessed)
diff -r 0f41fd7df85d linux/drivers/media/dvb/frontends/af9013_priv.h
--- a/linux/drivers/media/dvb/frontends/af9013_priv.h	Thu Feb 11 02:33:12 2010 +0200
+++ b/linux/drivers/media/dvb/frontends/af9013_priv.h	Thu Feb 25 17:21:11 2010 +0100
@@ -789,8 +789,9 @@ 
 	{ 0x9bd9, 0, 8, 0x08 },
 };
 
-/* NXP TDA18271 tuner init
-   AF9013_TUNER_TDA18271   = 156 */
+/* NXP TDA18271 & TDA18218 tuner init
+   AF9013_TUNER_TDA18271   = 156
+   AF9013_TUNER_TDA18218   = 179 */
 static struct regdesc tuner_init_tda18271[] = {
 	{ 0x9bd5, 0, 8, 0x01 },
 	{ 0x9bd6, 0, 8, 0x04 },