aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/common
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 21:38:45 -0300
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 18:41:38 -0300
commitb094516f9589245617eb5d0452769826063f72ac (patch)
tree45593c7f1ae4c180d97ed7b9cbc27a85c03f55d1 /drivers/media/common
parentdf7aaaf3a74016cbc72382b6388c7c62f3df49b2 (diff)
V4L/DVB (7769): Move other terrestrial tuners to common/tuners
Those tuners are currently used only under media/dvb. However, they can support also analog TV. Better to move them to the same place as the other hybrid tuners. This would make easier to use those tuners also by analog drivers. Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/common')
-rw-r--r--drivers/media/common/tuners/Kconfig28
-rw-r--r--drivers/media/common/tuners/Makefile4
-rw-r--r--drivers/media/common/tuners/mt2060.c369
-rw-r--r--drivers/media/common/tuners/mt2060.h43
-rw-r--r--drivers/media/common/tuners/mt2060_priv.h105
-rw-r--r--drivers/media/common/tuners/mt2131.c314
-rw-r--r--drivers/media/common/tuners/mt2131.h54
-rw-r--r--drivers/media/common/tuners/mt2131_priv.h49
-rw-r--r--drivers/media/common/tuners/mt2266.c351
-rw-r--r--drivers/media/common/tuners/mt2266.h37
-rw-r--r--drivers/media/common/tuners/qt1010.c485
-rw-r--r--drivers/media/common/tuners/qt1010.h53
-rw-r--r--drivers/media/common/tuners/qt1010_priv.h105
13 files changed, 1997 insertions, 0 deletions
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index 9a6a9022e97..e6926e9fa33 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -99,6 +99,34 @@ config TUNER_MT20XX
help
Say Y here to include support for the MT2032 / MT2050 tuner.
+config DVB_TUNER_MT2060
+ tristate "Microtune MT2060 silicon IF tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the silicon IF tuner MT2060 from Microtune.
+
+config DVB_TUNER_MT2266
+ tristate "Microtune MT2266 silicon tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the silicon baseband tuner MT2266 from Microtune.
+
+config DVB_TUNER_MT2131
+ tristate "Microtune MT2131 silicon tuner"
+ depends on I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the silicon baseband tuner MT2131 from Microtune.
+
+config DVB_TUNER_QT1010
+ tristate "Quantek QT1010 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the silicon tuner QT1010 from Quantek.
+
config TUNER_XC2028
tristate "XCeive xc2028/xc3028 tuners"
depends on I2C && FW_LOADER
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index 685ae64fa3b..81286431262 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -16,6 +16,10 @@ obj-$(CONFIG_TUNER_TDA9887) += tda9887.o
obj-$(CONFIG_DVB_TDA827X) += tda827x.o
obj-$(CONFIG_DVB_TDA18271) += tda18271.o
obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
+obj-$(CONFIG_DVB_TUNER_MT2266) += mt2266.o
+obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
+obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/mt2060.c b/drivers/media/common/tuners/mt2060.c
new file mode 100644
index 00000000000..1305b0e63ce
--- /dev/null
+++ b/drivers/media/common/tuners/mt2060.c
@@ -0,0 +1,369 @@
+/*
+ * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
+ *
+ * Copyright (c) 2006 Olivier DANET <odanet@caramail.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.=
+ */
+
+/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "mt2060.h"
+#include "mt2060_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0)
+
+// Reads a single register
+static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "mt2060 I2C read failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Writes a single register
+static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
+ };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mt2060 I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Writes a set of consecutive registers
+static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len)
+{
+ struct i2c_msg msg = {
+ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
+ };
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Initialisation sequences
+// LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49
+static u8 mt2060_config1[] = {
+ REG_LO1C1,
+ 0x3F, 0x74, 0x00, 0x08, 0x93
+};
+
+// FMCG=2, GP2=0, GP1=0
+static u8 mt2060_config2[] = {
+ REG_MISC_CTRL,
+ 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42
+};
+
+// VGAG=3, V1CSE=1
+
+#ifdef MT2060_SPURCHECK
+/* The function below calculates the frequency offset between the output frequency if2
+ and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */
+static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2)
+{
+ int I,J;
+ int dia,diamin,diff;
+ diamin=1000000;
+ for (I = 1; I < 10; I++) {
+ J = ((2*I*lo1)/lo2+1)/2;
+ diff = I*(int)lo1-J*(int)lo2;
+ if (diff < 0) diff=-diff;
+ dia = (diff-(int)if2);
+ if (dia < 0) dia=-dia;
+ if (diamin > dia) diamin=dia;
+ }
+ return diamin;
+}
+
+#define BANDWIDTH 4000 // kHz
+
+/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */
+static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2)
+{
+ u32 Spur,Sp1,Sp2;
+ int I,J;
+ I=0;
+ J=1000;
+
+ Spur=mt2060_spurcalc(lo1,lo2,if2);
+ if (Spur < BANDWIDTH) {
+ /* Potential spurs detected */
+ dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)",
+ (int)lo1,(int)lo2);
+ I=1000;
+ Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2);
+ Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2);
+
+ if (Sp1 < Sp2) {
+ J=-J; I=-I; Spur=Sp2;
+ } else
+ Spur=Sp1;
+
+ while (Spur < BANDWIDTH) {
+ I += J;
+ Spur = mt2060_spurcalc(lo1+I,lo2+I,if2);
+ }
+ dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)",
+ (int)(lo1+I),(int)(lo2+I));
+ }
+ return I;
+}
+#endif
+
+#define IF2 36150 // IF2 frequency = 36.150 MHz
+#define FREF 16000 // Quartz oscillator 16 MHz
+
+static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ struct mt2060_priv *priv;
+ int ret=0;
+ int i=0;
+ u32 freq;
+ u8 lnaband;
+ u32 f_lo1,f_lo2;
+ u32 div1,num1,div2,num2;
+ u8 b[8];
+ u32 if1;
+
+ priv = fe->tuner_priv;
+
+ if1 = priv->if1_freq;
+ b[0] = REG_LO1B1;
+ b[1] = 0xFF;
+
+ mt2060_writeregs(priv,b,2);
+
+ freq = params->frequency / 1000; // Hz -> kHz
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
+ f_lo1 = freq + if1 * 1000;
+ f_lo1 = (f_lo1 / 250) * 250;
+ f_lo2 = f_lo1 - freq - IF2;
+ // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise
+ f_lo2 = ((f_lo2 + 25) / 50) * 50;
+ priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000,
+
+#ifdef MT2060_SPURCHECK
+ // LO-related spurs detection and correction
+ num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2);
+ f_lo1 += num1;
+ f_lo2 += num1;
+#endif
+ //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 )
+ num1 = f_lo1 / (FREF / 64);
+ div1 = num1 / 64;
+ num1 &= 0x3f;
+
+ // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 )
+ num2 = f_lo2 * 64 / (FREF / 128);
+ div2 = num2 / 8192;
+ num2 &= 0x1fff;
+
+ if (freq <= 95000) lnaband = 0xB0; else
+ if (freq <= 180000) lnaband = 0xA0; else
+ if (freq <= 260000) lnaband = 0x90; else
+ if (freq <= 335000) lnaband = 0x80; else
+ if (freq <= 425000) lnaband = 0x70; else
+ if (freq <= 480000) lnaband = 0x60; else
+ if (freq <= 570000) lnaband = 0x50; else
+ if (freq <= 645000) lnaband = 0x40; else
+ if (freq <= 730000) lnaband = 0x30; else
+ if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10;
+
+ b[0] = REG_LO1C1;
+ b[1] = lnaband | ((num1 >>2) & 0x0F);
+ b[2] = div1;
+ b[3] = (num2 & 0x0F) | ((num1 & 3) << 4);
+ b[4] = num2 >> 4;
+ b[5] = ((num2 >>12) & 1) | (div2 << 1);
+
+ dprintk("IF1: %dMHz",(int)if1);
+ dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2);
+ dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2);
+ dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]);
+
+ mt2060_writeregs(priv,b,6);
+
+ //Waits for pll lock or timeout
+ i = 0;
+ do {
+ mt2060_readreg(priv,REG_LO_STATUS,b);
+ if ((b[0] & 0x88)==0x88)
+ break;
+ msleep(4);
+ i++;
+ } while (i<10);
+
+ return ret;
+}
+
+static void mt2060_calibrate(struct mt2060_priv *priv)
+{
+ u8 b = 0;
+ int i = 0;
+
+ if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1)))
+ return;
+ if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2)))
+ return;
+
+ /* initialize the clock output */
+ mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
+
+ do {
+ b |= (1 << 6); // FM1SS;
+ mt2060_writereg(priv, REG_LO2C1,b);
+ msleep(20);
+
+ if (i == 0) {
+ b |= (1 << 7); // FM1CA;
+ mt2060_writereg(priv, REG_LO2C1,b);
+ b &= ~(1 << 7); // FM1CA;
+ msleep(20);
+ }
+
+ b &= ~(1 << 6); // FM1SS
+ mt2060_writereg(priv, REG_LO2C1,b);
+
+ msleep(20);
+ i++;
+ } while (i < 9);
+
+ i = 0;
+ while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0)
+ msleep(20);
+
+ if (i < 10) {
+ mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :)
+ dprintk("calibration was successful: %d", (int)priv->fmfreq);
+ } else
+ dprintk("FMCAL timed out");
+}
+
+static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int mt2060_init(struct dvb_frontend *fe)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x33);
+}
+
+static int mt2060_sleep(struct dvb_frontend *fe)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
+}
+
+static int mt2060_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops mt2060_tuner_ops = {
+ .info = {
+ .name = "Microtune MT2060",
+ .frequency_min = 48000000,
+ .frequency_max = 860000000,
+ .frequency_step = 50000,
+ },
+
+ .release = mt2060_release,
+
+ .init = mt2060_init,
+ .sleep = mt2060_sleep,
+
+ .set_params = mt2060_set_params,
+ .get_frequency = mt2060_get_frequency,
+ .get_bandwidth = mt2060_get_bandwidth
+};
+
+/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */
+struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
+{
+ struct mt2060_priv *priv = NULL;
+ u8 id = 0;
+
+ priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+ priv->if1_freq = if1;
+
+ if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
+ kfree(priv);
+ return NULL;
+ }
+
+ if (id != PART_REV) {
+ kfree(priv);
+ return NULL;
+ }
+ printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1);
+ memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ mt2060_calibrate(priv);
+
+ return fe;
+}
+EXPORT_SYMBOL(mt2060_attach);
+
+MODULE_AUTHOR("Olivier DANET");
+MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/mt2060.h b/drivers/media/common/tuners/mt2060.h
new file mode 100644
index 00000000000..acba0058f51
--- /dev/null
+++ b/drivers/media/common/tuners/mt2060.h
@@ -0,0 +1,43 @@
+/*
+ * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
+ *
+ * Copyright (c) 2006 Olivier DANET <odanet@caramail.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 MT2060_H
+#define MT2060_H
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct mt2060_config {
+ u8 i2c_address;
+ u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
+};
+
+#if defined(CONFIG_DVB_TUNER_MT2060) || (defined(CONFIG_DVB_TUNER_MT2060_MODULE) && defined(MODULE))
+extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1);
+#else
+static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUNER_MT2060
+
+#endif
diff --git a/drivers/media/common/tuners/mt2060_priv.h b/drivers/media/common/tuners/mt2060_priv.h
new file mode 100644
index 00000000000..5eaccdefd0b
--- /dev/null
+++ b/drivers/media/common/tuners/mt2060_priv.h
@@ -0,0 +1,105 @@
+/*
+ * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
+ *
+ * Copyright (c) 2006 Olivier DANET <odanet@caramail.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 MT2060_PRIV_H
+#define MT2060_PRIV_H
+
+// Uncomment the #define below to enable spurs checking. The results where quite unconvincing.
+// #define MT2060_SPURCHECK
+
+/* This driver is based on the information available in the datasheet of the
+ "Comtech SDVBT-3K6M" tuner ( K1000737843.pdf ) which features the MT2060 register map :
+
+ I2C Address : 0x60
+
+ Reg.No | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | ( defaults )
+ --------------------------------------------------------------------------------
+ 00 | [ PART ] | [ REV ] | R = 0x63
+ 01 | [ LNABAND ] | [ NUM1(5:2) ] | RW = 0x3F
+ 02 | [ DIV1 ] | RW = 0x74
+ 03 | FM1CA | FM1SS | [ NUM1(1:0) ] | [ NUM2(3:0) ] | RW = 0x00
+ 04 | NUM2(11:4) ] | RW = 0x08
+ 05 | [ DIV2 ] |NUM2(12)| RW = 0x93
+ 06 | L1LK | [ TAD1 ] | L2LK | [ TAD2 ] | R
+ 07 | [ FMF ] | R
+ 08 | ? | FMCAL | ? | ? | ? | ? | ? | TEMP | R
+ 09 | 0 | 0 | [ FMGC ] | 0 | GP02 | GP01 | 0 | RW = 0x20
+ 0A | ??
+ 0B | 0 | 0 | 1 | 1 | 0 | 0 | [ VGAG ] | RW = 0x30
+ 0C | V1CSE | 1 | 1 | 1 | 1 | 1 | 1 | 1 | RW = 0xFF
+ 0D | 1 | 0 | [ V1CS ] | RW = 0xB0
+ 0E | ??
+ 0F | ??
+ 10 | ??
+ 11 | [ LOTO ] | 0 | 0 | 1 | 0 | RW = 0x42
+
+ PART : Part code : 6 for MT2060
+ REV : Revision code : 3 for current revision
+ LNABAND : Input frequency range : ( See code for details )
+ NUM1 / DIV1 / NUM2 / DIV2 : Frequencies programming ( See code for details )
+ FM1CA : Calibration Start Bit
+ FM1SS : Calibration Single Step bit
+ L1LK : LO1 Lock Detect
+ TAD1 : Tune Line ADC ( ? )
+ L2LK : LO2 Lock Detect
+ TAD2 : Tune Line ADC ( ? )
+ FMF : Estimated first IF Center frequency Offset ( ? )
+ FM1CAL : Calibration done bit
+ TEMP : On chip temperature sensor
+ FMCG : Mixer 1 Cap Gain ( ? )
+ GP01 / GP02 : Programmable digital outputs. Unconnected pins ?
+ V1CSE : LO1 VCO Automatic Capacitor Select Enable ( ? )
+ V1CS : LO1 Capacitor Selection Value ( ? )
+ LOTO : LO Timeout ( ? )
+ VGAG : Tuner Output gain
+*/
+
+#define I2C_ADDRESS 0x60
+
+#define REG_PART_REV 0
+#define REG_LO1C1 1
+#define REG_LO1C2 2
+#define REG_LO2C1 3
+#define REG_LO2C2 4
+#define REG_LO2C3 5
+#define REG_LO_STATUS 6
+#define REG_FM_FREQ 7
+#define REG_MISC_STAT 8
+#define REG_MISC_CTRL 9
+#define REG_RESERVED_A 0x0A
+#define REG_VGAG 0x0B
+#define REG_LO1B1 0x0C
+#define REG_LO1B2 0x0D
+#define REG_LOTO 0x11
+
+#define PART_REV 0x63 // The current driver works only with PART=6 and REV=3 chips
+
+struct mt2060_priv {
+ struct mt2060_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 frequency;
+ u32 bandwidth;
+ u16 if1_freq;
+ u8 fmfreq;
+};
+
+#endif
diff --git a/drivers/media/common/tuners/mt2131.c b/drivers/media/common/tuners/mt2131.c
new file mode 100644
index 00000000000..e254bcfc2ef
--- /dev/null
+++ b/drivers/media/common/tuners/mt2131.c
@@ -0,0 +1,314 @@
+/*
+ * Driver for Microtune MT2131 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "mt2131.h"
+#include "mt2131_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s: " fmt, "mt2131", ## arg)
+
+static u8 mt2131_config1[] = {
+ 0x01,
+ 0x50, 0x00, 0x50, 0x80, 0x00, 0x49, 0xfa, 0x88,
+ 0x08, 0x77, 0x41, 0x04, 0x00, 0x00, 0x00, 0x32,
+ 0x7f, 0xda, 0x4c, 0x00, 0x10, 0xaa, 0x78, 0x80,
+ 0xff, 0x68, 0xa0, 0xff, 0xdd, 0x00, 0x00
+};
+
+static u8 mt2131_config2[] = {
+ 0x10,
+ 0x7f, 0xc8, 0x0a, 0x5f, 0x00, 0x04
+};
+
+static int mt2131_readreg(struct mt2131_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address, .flags = 0,
+ .buf = &reg, .len = 1 },
+ { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
+ .buf = val, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "mt2131 I2C read failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int mt2131_writereg(struct mt2131_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address, .flags = 0,
+ .buf = buf, .len = 2 };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mt2131 I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int mt2131_writeregs(struct mt2131_priv *priv,u8 *buf, u8 len)
+{
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = buf, .len = len };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mt2131 I2C write failed (len=%i)\n",
+ (int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int mt2131_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct mt2131_priv *priv;
+ int ret=0, i;
+ u32 freq;
+ u8 if_band_center;
+ u32 f_lo1, f_lo2;
+ u32 div1, num1, div2, num2;
+ u8 b[8];
+ u8 lockval = 0;
+
+ priv = fe->tuner_priv;
+ if (fe->ops.info.type == FE_OFDM)
+ priv->bandwidth = params->u.ofdm.bandwidth;
+ else
+ priv->bandwidth = 0;
+
+ freq = params->frequency / 1000; // Hz -> kHz
+ dprintk(1, "%s() freq=%d\n", __func__, freq);
+
+ f_lo1 = freq + MT2131_IF1 * 1000;
+ f_lo1 = (f_lo1 / 250) * 250;
+ f_lo2 = f_lo1 - freq - MT2131_IF2;
+
+ priv->frequency = (f_lo1 - f_lo2 - MT2131_IF2) * 1000;
+
+ /* Frequency LO1 = 16MHz * (DIV1 + NUM1/8192 ) */
+ num1 = f_lo1 * 64 / (MT2131_FREF / 128);
+ div1 = num1 / 8192;
+ num1 &= 0x1fff;
+
+ /* Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) */
+ num2 = f_lo2 * 64 / (MT2131_FREF / 128);
+ div2 = num2 / 8192;
+ num2 &= 0x1fff;
+
+ if (freq <= 82500) if_band_center = 0x00; else
+ if (freq <= 137500) if_band_center = 0x01; else
+ if (freq <= 192500) if_band_center = 0x02; else
+ if (freq <= 247500) if_band_center = 0x03; else
+ if (freq <= 302500) if_band_center = 0x04; else
+ if (freq <= 357500) if_band_center = 0x05; else
+ if (freq <= 412500) if_band_center = 0x06; else
+ if (freq <= 467500) if_band_center = 0x07; else
+ if (freq <= 522500) if_band_center = 0x08; else
+ if (freq <= 577500) if_band_center = 0x09; else
+ if (freq <= 632500) if_band_center = 0x0A; else
+ if (freq <= 687500) if_band_center = 0x0B; else
+ if (freq <= 742500) if_band_center = 0x0C; else
+ if (freq <= 797500) if_band_center = 0x0D; else
+ if (freq <= 852500) if_band_center = 0x0E; else
+ if (freq <= 907500) if_band_center = 0x0F; else
+ if (freq <= 962500) if_band_center = 0x10; else
+ if (freq <= 1017500) if_band_center = 0x11; else
+ if (freq <= 1072500) if_band_center = 0x12; else if_band_center = 0x13;
+
+ b[0] = 1;
+ b[1] = (num1 >> 5) & 0xFF;
+ b[2] = (num1 & 0x1F);
+ b[3] = div1;
+ b[4] = (num2 >> 5) & 0xFF;
+ b[5] = num2 & 0x1F;
+ b[6] = div2;
+
+ dprintk(1, "IF1: %dMHz IF2: %dMHz\n", MT2131_IF1, MT2131_IF2);
+ dprintk(1, "PLL freq=%dkHz band=%d\n", (int)freq, (int)if_band_center);
+ dprintk(1, "PLL f_lo1=%dkHz f_lo2=%dkHz\n", (int)f_lo1, (int)f_lo2);
+ dprintk(1, "PLL div1=%d num1=%d div2=%d num2=%d\n",
+ (int)div1, (int)num1, (int)div2, (int)num2);
+ dprintk(1, "PLL [1..6]: %2x %2x %2x %2x %2x %2x\n",
+ (int)b[1], (int)b[2], (int)b[3], (int)b[4], (int)b[5],
+ (int)b[6]);
+
+ ret = mt2131_writeregs(priv,b,7);
+ if (ret < 0)
+ return ret;
+
+ mt2131_writereg(priv, 0x0b, if_band_center);
+
+ /* Wait for lock */
+ i = 0;
+ do {
+ mt2131_readreg(priv, 0x08, &lockval);
+ if ((lockval & 0x88) == 0x88)
+ break;
+ msleep(4);
+ i++;
+ } while (i < 10);
+
+ return ret;
+}
+
+static int mt2131_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mt2131_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int mt2131_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mt2131_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int mt2131_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct mt2131_priv *priv = fe->tuner_priv;
+ u8 lock_status = 0;
+ u8 afc_status = 0;
+
+ *status = 0;
+
+ mt2131_readreg(priv, 0x08, &lock_status);
+ if ((lock_status & 0x88) == 0x88)
+ *status = TUNER_STATUS_LOCKED;
+
+ mt2131_readreg(priv, 0x09, &afc_status);
+ dprintk(1, "%s() - LO Status = 0x%x, AFC Status = 0x%x\n",
+ __func__, lock_status, afc_status);
+
+ return 0;
+}
+
+static int mt2131_init(struct dvb_frontend *fe)
+{
+ struct mt2131_priv *priv = fe->tuner_priv;
+ int ret;
+ dprintk(1, "%s()\n", __func__);
+
+ if ((ret = mt2131_writeregs(priv, mt2131_config1,
+ sizeof(mt2131_config1))) < 0)
+ return ret;
+
+ mt2131_writereg(priv, 0x0b, 0x09);
+ mt2131_writereg(priv, 0x15, 0x47);
+ mt2131_writereg(priv, 0x07, 0xf2);
+ mt2131_writereg(priv, 0x0b, 0x01);
+
+ if ((ret = mt2131_writeregs(priv, mt2131_config2,
+ sizeof(mt2131_config2))) < 0)
+ return ret;
+
+ return ret;
+}
+
+static int mt2131_release(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s()\n", __func__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops mt2131_tuner_ops = {
+ .info = {
+ .name = "Microtune MT2131",
+ .frequency_min = 48000000,
+ .frequency_max = 860000000,
+ .frequency_step = 50000,
+ },
+
+ .release = mt2131_release,
+ .init = mt2131_init,
+
+ .set_params = mt2131_set_params,
+ .get_frequency = mt2131_get_frequency,
+ .get_bandwidth = mt2131_get_bandwidth,
+ .get_status = mt2131_get_status
+};
+
+struct dvb_frontend * mt2131_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct mt2131_config *cfg, u16 if1)
+{
+ struct mt2131_priv *priv = NULL;
+ u8 id = 0;
+
+ dprintk(1, "%s()\n", __func__);
+
+ priv = kzalloc(sizeof(struct mt2131_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->bandwidth = 6000000; /* 6MHz */
+ priv->i2c = i2c;
+
+ if (mt2131_readreg(priv, 0, &id) != 0) {
+ kfree(priv);
+ return NULL;
+ }
+ if ( (id != 0x3E) && (id != 0x3F) ) {
+ printk(KERN_ERR "MT2131: Device not found at addr 0x%02x\n",
+ cfg->i2c_address);
+ kfree(priv);
+ return NULL;
+ }
+
+ printk(KERN_INFO "MT2131: successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ memcpy(&fe->ops.tuner_ops, &mt2131_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+ return fe;
+}
+EXPORT_SYMBOL(mt2131_attach);
+
+MODULE_AUTHOR("Steven Toth");
+MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ */
diff --git a/drivers/media/common/tuners/mt2131.h b/drivers/media/common/tuners/mt2131.h
new file mode 100644
index 00000000000..606d8576bc9
--- /dev/null
+++ b/drivers/media/common/tuners/mt2131.h
@@ -0,0 +1,54 @@
+/*
+ * Driver for Microtune MT2131 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MT2131_H__
+#define __MT2131_H__
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct mt2131_config {
+ u8 i2c_address;
+ u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
+};
+
+#if defined(CONFIG_DVB_TUNER_MT2131) || (defined(CONFIG_DVB_TUNER_MT2131_MODULE) && defined(MODULE))
+extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct mt2131_config *cfg,
+ u16 if1);
+#else
+static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct mt2131_config *cfg,
+ u16 if1)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_TUNER_MT2131 */
+
+#endif /* __MT2131_H__ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ */
diff --git a/drivers/media/common/tuners/mt2131_priv.h b/drivers/media/common/tuners/mt2131_priv.h
new file mode 100644
index 00000000000..e930759c2c0
--- /dev/null
+++ b/drivers/media/common/tuners/mt2131_priv.h
@@ -0,0 +1,49 @@
+/*
+ * Driver for Microtune MT2131 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MT2131_PRIV_H__
+#define __MT2131_PRIV_H__
+
+/* Regs */
+#define MT2131_PWR 0x07
+#define MT2131_UPC_1 0x0b
+#define MT2131_AGC_RL 0x10
+#define MT2131_MISC_2 0x15
+
+/* frequency values in KHz */
+#define MT2131_IF1 1220
+#define MT2131_IF2 44000
+#define MT2131_FREF 16000
+
+struct mt2131_priv {
+ struct mt2131_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+#endif /* __MT2131_PRIV_H__ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ */
diff --git a/drivers/media/common/tuners/mt2266.c b/drivers/media/common/tuners/mt2266.c
new file mode 100644
index 00000000000..54b18f94b14
--- /dev/null
+++ b/drivers/media/common/tuners/mt2266.c
@@ -0,0 +1,351 @@
+/*
+ * Driver for Microtune MT2266 "Direct conversion low power broadband tuner"
+ *
+ * Copyright (c) 2007 Olivier DANET <odanet@caramail.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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+#include "mt2266.h"
+
+#define I2C_ADDRESS 0x60
+
+#define REG_PART_REV 0
+#define REG_TUNE 1
+#define REG_BAND 6
+#define REG_BANDWIDTH 8
+#define REG_LOCK 0x12
+
+#define PART_REV 0x85
+
+struct mt2266_priv {
+ struct mt2266_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 frequency;
+ u32 bandwidth;
+ u8 band;
+};
+
+#define MT2266_VHF 1
+#define MT2266_UHF 0
+
+/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2266: " args); printk("\n"); }} while (0)
+
+// Reads a single register
+static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "MT2266 I2C read failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Writes a single register
+static int mt2266_writereg(struct mt2266_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
+ };
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "MT2266 I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Writes a set of consecutive registers
+static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len)
+{
+ struct i2c_msg msg = {
+ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
+ };
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "MT2266 I2C write failed (len=%i)\n",(int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Initialisation sequences
+static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28,
+ 0x00, 0x52, 0x99, 0x3f };
+
+static u8 mt2266_init2[] = {
+ 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4,
+ 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff,
+ 0xff, 0x00, 0x77, 0x0f, 0x2d
+};
+
+static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22 };
+
+static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x32, 0x32, 0x32 };
+
+static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa7, 0xa7, 0xa7, 0xa7 };
+
+static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64,
+ 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 };
+
+static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f };
+
+#define FREF 30000 // Quartz oscillator 30 MHz
+
+static int mt2266_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ struct mt2266_priv *priv;
+ int ret=0;
+ u32 freq;
+ u32 tune;
+ u8 lnaband;
+ u8 b[10];
+ int i;
+ u8 band;
+
+ priv = fe->tuner_priv;
+
+ freq = params->frequency / 1000; // Hz -> kHz
+ if (freq < 470000 && freq > 230000)
+ return -EINVAL; /* Gap between VHF and UHF bands */
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+ priv->frequency = freq * 1000;
+
+ tune = 2 * freq * (8192/16) / (FREF/16);
+ band = (freq < 300000) ? MT2266_VHF : MT2266_UHF;
+ if (band == MT2266_VHF)
+ tune *= 2;
+
+ switch (params->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ mt2266_writeregs(priv, mt2266_init_6mhz,
+ sizeof(mt2266_init_6mhz));
+ break;
+ case BANDWIDTH_7_MHZ:
+ mt2266_writeregs(priv, mt2266_init_7mhz,
+ sizeof(mt2266_init_7mhz));
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ mt2266_writeregs(priv, mt2266_init_8mhz,
+ sizeof(mt2266_init_8mhz));
+ break;
+ }
+
+ if (band == MT2266_VHF && priv->band == MT2266_UHF) {
+ dprintk("Switch from UHF to VHF");
+ mt2266_writereg(priv, 0x05, 0x04);
+ mt2266_writereg(priv, 0x19, 0x61);
+ mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf));
+ } else if (band == MT2266_UHF && priv->band == MT2266_VHF) {
+ dprintk("Switch from VHF to UHF");
+ mt2266_writereg(priv, 0x05, 0x52);
+ mt2266_writereg(priv, 0x19, 0x61);
+ mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf));
+ }
+ msleep(10);
+
+ if (freq <= 495000)
+ lnaband = 0xEE;
+ else if (freq <= 525000)
+ lnaband = 0xDD;
+ else if (freq <= 550000)
+ lnaband = 0xCC;
+ else if (freq <= 580000)
+ lnaband = 0xBB;
+ else if (freq <= 605000)
+ lnaband = 0xAA;
+ else if (freq <= 630000)
+ lnaband = 0x99;
+ else if (freq <= 655000)
+ lnaband = 0x88;
+ else if (freq <= 685000)
+ lnaband = 0x77;
+ else if (freq <= 710000)
+ lnaband = 0x66;
+ else if (freq <= 735000)
+ lnaband = 0x55;
+ else if (freq <= 765000)
+ lnaband = 0x44;
+ else if (freq <= 802000)
+ lnaband = 0x33;
+ else if (freq <= 840000)
+ lnaband = 0x22;
+ else
+ lnaband = 0x11;
+
+ b[0] = REG_TUNE;
+ b[1] = (tune >> 8) & 0x1F;
+ b[2] = tune & 0xFF;
+ b[3] = tune >> 13;
+ mt2266_writeregs(priv,b,4);
+
+ dprintk("set_parms: tune=%d band=%d %s",
+ (int) tune, (int) lnaband,
+ (band == MT2266_UHF) ? "UHF" : "VHF");
+ dprintk("set_parms: [1..3]: %2x %2x %2x",
+ (int) b[1], (int) b[2], (int)b[3]);
+
+ if (band == MT2266_UHF) {
+ b[0] = 0x05;
+ b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62;
+ b[2] = lnaband;
+ mt2266_writeregs(priv, b, 3);
+ }
+
+ /* Wait for pll lock or timeout */
+ i = 0;
+ do {
+ mt2266_readreg(priv,REG_LOCK,b);
+ if (b[0] & 0x40)
+ break;
+ msleep(10);
+ i++;
+ } while (i<10);
+ dprintk("Lock when i=%i",(int)i);
+
+ if (band == MT2266_UHF && priv->band == MT2266_VHF)
+ mt2266_writereg(priv, 0x05, 0x62);
+
+ priv->band = band;
+
+ return ret;
+}
+
+static void mt2266_calibrate(struct mt2266_priv *priv)
+{
+ mt2266_writereg(priv, 0x11, 0x03);
+ mt2266_writereg(priv, 0x11, 0x01);
+ mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1));
+ mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2));
+ mt2266_writereg(priv, 0x33, 0x5e);
+ mt2266_writereg(priv, 0x10, 0x10);
+ mt2266_writereg(priv, 0x10, 0x00);
+ mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz));
+ msleep(25);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0x00);
+ msleep(75);
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0xff);
+}
+
+static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mt2266_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mt2266_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int mt2266_init(struct dvb_frontend *fe)
+{
+ int ret;
+ struct mt2266_priv *priv = fe->tuner_priv;
+ ret = mt2266_writereg(priv, 0x17, 0x6d);
+ if (ret < 0)
+ return ret;
+ ret = mt2266_writereg(priv, 0x1c, 0xff);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int mt2266_sleep(struct dvb_frontend *fe)
+{
+ struct mt2266_priv *priv = fe->tuner_priv;
+ mt2266_writereg(priv, 0x17, 0x6d);
+ mt2266_writereg(priv, 0x1c, 0x00);
+ return 0;
+}
+
+static int mt2266_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops mt2266_tuner_ops = {
+ .info = {
+ .name = "Microtune MT2266",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_step = 50000,
+ },
+ .release = mt2266_release,
+ .init = mt2266_init,
+ .sleep = mt2266_sleep,
+ .set_params = mt2266_set_params,
+ .get_frequency = mt2266_get_frequency,
+ .get_bandwidth = mt2266_get_bandwidth
+};
+
+struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
+{
+ struct mt2266_priv *priv = NULL;
+ u8 id = 0;
+
+ priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+ priv->band = MT2266_UHF;
+
+ if (mt2266_readreg(priv, 0, &id)) {
+ kfree(priv);
+ return NULL;
+ }
+ if (id != PART_REV) {
+ kfree(priv);
+ return NULL;
+ }
+ printk(KERN_INFO "MT2266: successfully identified\n");
+ memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+ mt2266_calibrate(priv);
+ return fe;
+}
+EXPORT_SYMBOL(mt2266_attach);
+
+MODULE_AUTHOR("Olivier DANET");
+MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/mt2266.h b/drivers/media/common/tuners/mt2266.h
new file mode 100644
index 00000000000..c5113efe333
--- /dev/null
+++ b/drivers/media/common/tuners/mt2266.h
@@ -0,0 +1,37 @@
+/*
+ * Driver for Microtune MT2266 "Direct conversion low power broadband tuner"
+ *
+ * Copyright (c) 2007 Olivier DANET <odanet@caramail.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.
+ */
+
+#ifndef MT2266_H
+#define MT2266_H
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct mt2266_config {
+ u8 i2c_address;
+};
+
+#if defined(CONFIG_DVB_TUNER_MT2266) || (defined(CONFIG_DVB_TUNER_MT2266_MODULE) && defined(MODULE))
+extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg);
+#else
+static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUNER_MT2266
+
+#endif
diff --git a/drivers/media/common/tuners/qt1010.c b/drivers/media/common/tuners/qt1010.c
new file mode 100644
index 00000000000..825aa1412e6
--- /dev/null
+++ b/drivers/media/common/tuners/qt1010.c
@@ -0,0 +1,485 @@
+/*
+ * Driver for Quantek QT1010 silicon tuner
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ * Aapo Tahkola <aet@rasterburn.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 "qt1010.h"
+#include "qt1010_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "QT1010: " args); \
+ } while (0)
+
+/* read single register */
+static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "qt1010 I2C read failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/* write single register */
+static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = buf, .len = 2 };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "qt1010 I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/* dump all registers */
+static void qt1010_dump_regs(struct qt1010_priv *priv)
+{
+ char buf[52], buf2[4];
+ u8 reg, val;
+
+ for (reg = 0; ; reg++) {
+ if (reg % 16 == 0) {
+ if (reg)
+ printk("%s\n", buf);
+ sprintf(buf, "%02x: ", reg);
+ }
+ if (qt1010_readreg(priv, reg, &val) == 0)
+ sprintf(buf2, "%02x ", val);
+ else
+ strcpy(buf2, "-- ");
+ strcat(buf, buf2);
+ if (reg == 0x2f)
+ break;
+ }
+ printk("%s\n", buf);
+}
+
+static int qt1010_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct qt1010_priv *priv;
+ int err;
+ u32 freq, div, mod1, mod2;
+ u8 i, tmpval, reg05;
+ qt1010_i2c_oper_t rd[48] = {
+ { QT1010_WR, 0x01, 0x80 },
+ { QT1010_WR, 0x02, 0x3f },
+ { QT1010_WR, 0x05, 0xff }, /* 02 c write */
+ { QT1010_WR, 0x06, 0x44 },
+ { QT1010_WR, 0x07, 0xff }, /* 04 c write */
+ { QT1010_WR, 0x08, 0x08 },
+ { QT1010_WR, 0x09, 0xff }, /* 06 c write */
+ { QT1010_WR, 0x0a, 0xff }, /* 07 c write */
+ { QT1010_WR, 0x0b, 0xff }, /* 08 c write */
+ { QT1010_WR, 0x0c, 0xe1 },
+ { QT1010_WR, 0x1a, 0xff }, /* 10 c write */
+ { QT1010_WR, 0x1b, 0x00 },
+ { QT1010_WR, 0x1c, 0x89 },
+ { QT1010_WR, 0x11, 0xff }, /* 13 c write */
+ { QT1010_WR, 0x12, 0xff }, /* 14 c write */
+ { QT1010_WR, 0x22, 0xff }, /* 15 c write */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xd0 },
+ { QT1010_RD, 0x22, 0xff }, /* 16 c read */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_RD, 0x05, 0xff }, /* 20 c read */
+ { QT1010_RD, 0x22, 0xff }, /* 21 c read */
+ { QT1010_WR, 0x23, 0xd0 },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xe0 },
+ { QT1010_RD, 0x23, 0xff }, /* 25 c read */
+ { QT1010_RD, 0x23, 0xff }, /* 26 c read */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x24, 0xd0 },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xf0 },
+ { QT1010_RD, 0x24, 0xff }, /* 31 c read */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x14, 0x7f },
+ { QT1010_WR, 0x15, 0x7f },
+ { QT1010_WR, 0x05, 0xff }, /* 35 c write */
+ { QT1010_WR, 0x06, 0x00 },
+ { QT1010_WR, 0x15, 0x1f },
+ { QT1010_WR, 0x16, 0xff },
+ { QT1010_WR, 0x18, 0xff },
+ { QT1010_WR, 0x1f, 0xff }, /* 40 c write */
+ { QT1010_WR, 0x20, 0xff }, /* 41 c write */
+ { QT1010_WR, 0x21, 0x53 },
+ { QT1010_WR, 0x25, 0xff }, /* 43 c write */
+ { QT1010_WR, 0x26, 0x15 },
+ { QT1010_WR, 0x00, 0xff }, /* 45 c write */
+ { QT1010_WR, 0x02, 0x00 },
+ { QT1010_WR, 0x01, 0x00 }
+ };
+
+#define FREQ1 32000000 /* 32 MHz */
+#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */
+
+ priv = fe->tuner_priv;
+ freq = params->frequency;
+ div = (freq + QT1010_OFFSET) / QT1010_STEP;
+ freq = (div * QT1010_STEP) - QT1010_OFFSET;
+ mod1 = (freq + QT1010_OFFSET) % FREQ1;
+ mod2 = (freq + QT1010_OFFSET) % FREQ2;
+ priv->bandwidth =
+ (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+ priv->frequency = freq;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ /* reg 05 base value */
+ if (freq < 290000000) reg05 = 0x14; /* 290 MHz */
+ else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */
+ else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */
+ else reg05 = 0x74;
+
+ /* 0x5 */
+ rd[2].val = reg05;
+
+ /* 07 - set frequency: 32 MHz scale */
+ rd[4].val = (freq + QT1010_OFFSET) / FREQ1;
+
+ /* 09 - changes every 8/24 MHz */
+ if (mod1 < 8000000) rd[6].val = 0x1d;
+ else rd[6].val = 0x1c;
+
+ /* 0a - set frequency: 4 MHz scale (max 28 MHz) */
+ if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */
+ else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */
+ else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */
+ else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */
+ else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */
+ else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */
+ else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */
+ else rd[7].val = 0x0a; /* +28 MHz */
+
+ /* 0b - changes every 2/2 MHz */
+ if (mod2 < 2000000) rd[8].val = 0x45;
+ else rd[8].val = 0x44;
+
+ /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/
+ tmpval = 0x78; /* byte, overflows intentionally */
+ rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08);
+
+ /* 11 */
+ rd[13].val = 0xfd; /* TODO: correct value calculation */
+
+ /* 12 */
+ rd[14].val = 0x91; /* TODO: correct value calculation */
+
+ /* 22 */
+ if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */
+ else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */
+ else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */
+ else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */
+ else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */
+ else rd[15].val = 0xd0;
+
+ /* 05 */
+ rd[35].val = (reg05 & 0xf0);
+
+ /* 1f */
+ if (mod1 < 8000000) tmpval = 0x00;
+ else if (mod1 < 12000000) tmpval = 0x01;
+ else if (mod1 < 16000000) tmpval = 0x02;
+ else if (mod1 < 24000000) tmpval = 0x03;
+ else if (mod1 < 28000000) tmpval = 0x04;
+ else tmpval = 0x05;
+ rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval);
+
+ /* 20 */
+ if (mod1 < 8000000) tmpval = 0x00;
+ else if (mod1 < 12000000) tmpval = 0x01;
+ else if (mod1 < 20000000) tmpval = 0x02;
+ else if (mod1 < 24000000) tmpval = 0x03;
+ else if (mod1 < 28000000) tmpval = 0x04;
+ else tmpval = 0x05;
+ rd[41].val = (priv->reg20_init_val + 0x0d + tmpval);
+
+ /* 25 */
+ rd[43].val = priv->reg25_init_val;
+
+ /* 00 */
+ rd[45].val = 0x92; /* TODO: correct value calculation */
+
+ dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \
+ "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \
+ "20:%02x 25:%02x 00:%02x", \
+ freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \
+ rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \
+ rd[40].val, rd[41].val, rd[43].val, rd[45].val);
+
+ for (i = 0; i < ARRAY_SIZE(rd); i++) {
+ if (rd[i].oper == QT1010_WR) {
+ err = qt1010_writereg(priv, rd[i].reg, rd[i].val);
+ } else { /* read is required to proper locking */
+ err = qt1010_readreg(priv, rd[i].reg, &tmpval);
+ }
+ if (err) return err;
+ }
+
+ if (debug)
+ qt1010_dump_regs(priv);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return 0;
+}
+
+static int qt1010_init_meas1(struct qt1010_priv *priv,
+ u8 oper, u8 reg, u8 reg_init_val, u8 *retval)
+{
+ u8 i, val1, val2;
+ int err;
+
+ qt1010_i2c_oper_t i2c_data[] = {
+ { QT1010_WR, reg, reg_init_val },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, oper },
+ { QT1010_RD, reg, 0xff }
+ };
+
+ for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
+ if (i2c_data[i].oper == QT1010_WR) {
+ err = qt1010_writereg(priv, i2c_data[i].reg,
+ i2c_data[i].val);
+ } else {
+ err = qt1010_readreg(priv, i2c_data[i].reg, &val2);
+ }
+ if (err) return err;
+ }
+
+ do {
+ val1 = val2;
+ err = qt1010_readreg(priv, reg, &val2);
+ if (err) return err;
+ dprintk("compare reg:%02x %02x %02x", reg, val1, val2);
+ } while (val1 != val2);
+ *retval = val1;
+
+ return qt1010_writereg(priv, 0x1e, 0x00);
+}
+
+static u8 qt1010_init_meas2(struct qt1010_priv *priv,
+ u8 reg_init_val, u8 *retval)
+{
+ u8 i, val;
+ int err;
+ qt1010_i2c_oper_t i2c_data[] = {
+ { QT1010_WR, 0x07, reg_init_val },
+ { QT1010_WR, 0x22, 0xd0 },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xd0 },
+ { QT1010_RD, 0x22, 0xff },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x22, 0xff }
+ };
+ for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
+ if (i2c_data[i].oper == QT1010_WR) {
+ err = qt1010_writereg(priv, i2c_data[i].reg,
+ i2c_data[i].val);
+ } else {
+ err = qt1010_readreg(priv, i2c_data[i].reg, &val);
+ }
+ if (err) return err;
+ }
+ *retval = val;
+ return 0;
+}
+
+static int qt1010_init(struct dvb_frontend *fe)
+{
+ struct qt1010_priv *priv = fe->tuner_priv;
+ struct dvb_frontend_parameters params;
+ int err = 0;
+ u8 i, tmpval, *valptr = NULL;
+
+ qt1010_i2c_oper_t i2c_data[] = {
+ { QT1010_WR, 0x01, 0x80 },
+ { QT1010_WR, 0x0d, 0x84 },
+ { QT1010_WR, 0x0e, 0xb7 },
+ { QT1010_WR, 0x2a, 0x23 },
+ { QT1010_WR, 0x2c, 0xdc },
+ { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */
+ { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */
+ { QT1010_WR, 0x2b, 0x70 },
+ { QT1010_WR, 0x2a, 0x23 },
+ { QT1010_M1, 0x26, 0x08 },
+ { QT1010_M1, 0x82, 0xff },
+ { QT1010_WR, 0x05, 0x14 },
+ { QT1010_WR, 0x06, 0x44 },
+ { QT1010_WR, 0x07, 0x28 },
+ { QT1010_WR, 0x08, 0x0b },
+ { QT1010_WR, 0x11, 0xfd },
+ { QT1010_M1, 0x22, 0x0d },
+ { QT1010_M1, 0xd0, 0xff },
+ { QT1010_WR, 0x06, 0x40 },
+ { QT1010_WR, 0x16, 0xf0 },
+ { QT1010_WR, 0x02, 0x38 },
+ { QT1010_WR, 0x03, 0x18 },
+ { QT1010_WR, 0x20, 0xe0 },
+ { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */
+ { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */
+ { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */
+ { QT1010_WR, 0x03, 0x19 },
+ { QT1010_WR, 0x02, 0x3f },
+ { QT1010_WR, 0x21, 0x53 },
+ { QT1010_RD, 0x21, 0xff },
+ { QT1010_WR, 0x11, 0xfd },
+ { QT1010_WR, 0x05, 0x34 },
+ { QT1010_WR, 0x06, 0x44 },
+ { QT1010_WR, 0x08, 0x08 }
+ };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
+ switch (i2c_data[i].oper) {
+ case QT1010_WR:
+ err = qt1010_writereg(priv, i2c_data[i].reg,
+ i2c_data[i].val);
+ break;
+ case QT1010_RD:
+ if (i2c_data[i].val == 0x20)
+ valptr = &priv->reg20_init_val;
+ else
+ valptr = &tmpval;
+ err = qt1010_readreg(priv, i2c_data[i].reg, valptr);
+ break;
+ case QT1010_M1:
+ if (i2c_data[i].val == 0x25)
+ valptr = &priv->reg25_init_val;
+ else if (i2c_data[i].val == 0x1f)
+ valptr = &priv->reg1f_init_val;
+ else
+ valptr = &tmpval;
+ err = qt1010_init_meas1(priv, i2c_data[i+1].reg,
+ i2c_data[i].reg,
+ i2c_data[i].val, valptr);
+ i++;
+ break;
+ }
+ if (err) return err;
+ }
+
+ for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */
+ if ((err = qt1010_init_meas2(priv, i, &tmpval)))
+ return err;
+
+ params.frequency = 545000000; /* Sigmatek DVB-110 545000000 */
+ /* MSI Megasky 580 GL861 533000000 */
+ return qt1010_set_params(fe, &params);
+}
+
+static int qt1010_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct qt1010_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int qt1010_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct qt1010_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static const struct dvb_tuner_ops qt1010_tuner_ops = {
+ .info = {
+ .name = "Quantek QT1010",
+ .frequency_min = QT1010_MIN_FREQ,
+ .frequency_max = QT1010_MAX_FREQ,
+ .frequency_step = QT1010_STEP,
+ },
+
+ .release = qt1010_release,
+ .init = qt1010_init,
+ /* TODO: implement sleep */
+
+ .set_params = qt1010_set_params,
+ .get_frequency = qt1010_get_frequency,
+ .get_bandwidth = qt1010_get_bandwidth
+};
+
+struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct qt1010_config *cfg)
+{
+ struct qt1010_priv *priv = NULL;
+ u8 id;
+
+ priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+
+ /* Try to detect tuner chip. Probably this is not correct register. */
+ if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) {
+ kfree(priv);
+ return NULL;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ printk(KERN_INFO "Quantek QT1010 successfully identified.\n");
+ memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+ return fe;
+}
+EXPORT_SYMBOL(qt1010_attach);
+
+MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/qt1010.h b/drivers/media/common/tuners/qt1010.h
new file mode 100644
index 00000000000..cff6a7ca538
--- /dev/null
+++ b/drivers/media/common/tuners/qt1010.h
@@ -0,0 +1,53 @@
+/*
+ * Driver for Quantek QT1010 silicon tuner
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ * Aapo Tahkola <aet@rasterburn.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 QT1010_H
+#define QT1010_H
+
+#include "dvb_frontend.h"
+
+struct qt1010_config {
+ u8 i2c_address;
+};
+
+/**
+ * Attach a qt1010 tuner to the supplied frontend structure.
+ *
+ * @param fe frontend to attach to
+ * @param i2c i2c adapter to use
+ * @param cfg tuner hw based configuration
+ * @return fe pointer on success, NULL on failure
+ */
+#if defined(CONFIG_DVB_TUNER_QT1010) || (defined(CONFIG_DVB_TUNER_QT1010_MODULE) && defined(MODULE))
+extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct qt1010_config *cfg);
+#else
+static inline struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct qt1010_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUNER_QT1010
+
+#endif
diff --git a/drivers/media/common/tuners/qt1010_priv.h b/drivers/media/common/tuners/qt1010_priv.h
new file mode 100644
index 00000000000..090cf475f09
--- /dev/null
+++ b/drivers/media/common/tuners/qt1010_priv.h
@@ -0,0 +1,105 @@
+/*
+ * Driver for Quantek QT1010 silicon tuner
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ * Aapo Tahkola <aet@rasterburn.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 QT1010_PRIV_H
+#define QT1010_PRIV_H
+
+/*
+reg def meaning
+=== === =======
+00 00 ?
+01 a0 ? operation start/stop; start=80, stop=00
+02 00 ?
+03 19 ?
+04 00 ?
+05 00 ? maybe band selection
+06 00 ?
+07 2b set frequency: 32 MHz scale, n*32 MHz
+08 0b ?
+09 10 ? changes every 8/24 MHz; values 1d/1c
+0a 08 set frequency: 4 MHz scale, n*4 MHz
+0b 41 ? changes every 2/2 MHz; values 45/45
+0c e1 ?
+0d 94 ?
+0e b6 ?
+0f 2c ?
+10 10 ?
+11 f1 ? maybe device specified adjustment
+12 11 ? maybe device specified adjustment
+13 3f ?
+14 1f ?
+15 3f ?
+16 ff ?
+17 ff ?
+18 f7 ?
+19 80 ?
+1a d0 set frequency: 125 kHz scale, n*125 kHz
+1b 00 ?
+1c 89 ?
+1d 00 ?
+1e 00 ? looks like operation register; write cmd here, read result from 1f-26
+1f 20 ? chip initialization
+20 e0 ? chip initialization
+21 20 ?
+22 d0 ?
+23 d0 ?
+24 d0 ?
+25 40 ? chip initialization
+26 08 ?
+27 29 ?
+28 55 ?
+29 39 ?
+2a 13 ?
+2b 01 ?
+2c ea ?
+2d 00 ?
+2e 00 ? not used?
+2f 00 ? not used?
+*/
+
+#define QT1010_STEP 125000 /* 125 kHz used by Windows drivers,
+ hw could be more precise but we don't
+ know how to use */
+#define QT1010_MIN_FREQ 48000000 /* 48 MHz */
+#define QT1010_MAX_FREQ 860000000 /* 860 MHz */
+#define QT1010_OFFSET 1246000000 /* 1246 MHz */
+
+#define QT1010_WR 0
+#define QT1010_RD 1
+#define QT1010_M1 3
+
+typedef struct {
+ u8 oper, reg, val;
+} qt1010_i2c_oper_t;
+
+struct qt1010_priv {
+ struct qt1010_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u8 reg1f_init_val;
+ u8 reg20_init_val;
+ u8 reg25_init_val;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+#endif