diff options
Diffstat (limited to 'drivers/media/common/tuners')
-rw-r--r-- | drivers/media/common/tuners/Kconfig | 28 | ||||
-rw-r--r-- | drivers/media/common/tuners/Makefile | 4 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2060.c | 369 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2060.h | 43 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2060_priv.h | 105 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2131.c | 314 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2131.h | 54 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2131_priv.h | 49 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2266.c | 351 | ||||
-rw-r--r-- | drivers/media/common/tuners/mt2266.h | 37 | ||||
-rw-r--r-- | drivers/media/common/tuners/qt1010.c | 485 | ||||
-rw-r--r-- | drivers/media/common/tuners/qt1010.h | 53 | ||||
-rw-r--r-- | drivers/media/common/tuners/qt1010_priv.h | 105 |
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 = ®, .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 = ®, .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 = ®, .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 = ®, .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, ¶ms); +} + +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 |