summaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/frontends
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb/frontends')
-rw-r--r--drivers/media/dvb/frontends/Kconfig97
-rw-r--r--drivers/media/dvb/frontends/Makefile11
-rw-r--r--drivers/media/dvb/frontends/bcm3510.h9
-rw-r--r--drivers/media/dvb/frontends/cx22700.h9
-rw-r--r--drivers/media/dvb/frontends/cx22702.c4
-rw-r--r--drivers/media/dvb/frontends/cx22702.h9
-rw-r--r--drivers/media/dvb/frontends/cx24110.c17
-rw-r--r--drivers/media/dvb/frontends/cx24110.h19
-rw-r--r--drivers/media/dvb/frontends/cx24123.c102
-rw-r--r--drivers/media/dvb/frontends/cx24123.h12
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.c83
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.h135
-rw-r--r--drivers/media/dvb/frontends/dib3000.h11
-rw-r--r--drivers/media/dvb/frontends/dib3000mb.c76
-rw-r--r--drivers/media/dvb/frontends/dib3000mb_priv.h93
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.c1403
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.h61
-rw-r--r--drivers/media/dvb/frontends/dib3000mc_priv.h428
-rw-r--r--drivers/media/dvb/frontends/dib7000m.c1191
-rw-r--r--drivers/media/dvb/frontends/dib7000m.h51
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c1019
-rw-r--r--drivers/media/dvb/frontends/dib7000p.h46
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.c152
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h177
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c76
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h9
-rw-r--r--drivers/media/dvb/frontends/isl6421.c30
-rw-r--r--drivers/media/dvb/frontends/isl6421.h11
-rw-r--r--drivers/media/dvb/frontends/l64781.c2
-rw-r--r--drivers/media/dvb/frontends/l64781.h10
-rw-r--r--drivers/media/dvb/frontends/lg_h06xf.h64
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.c257
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.h9
-rw-r--r--drivers/media/dvb/frontends/lgdt330x_priv.h15
-rw-r--r--drivers/media/dvb/frontends/lgh06xf.c134
-rw-r--r--drivers/media/dvb/frontends/lgh06xf.h35
-rw-r--r--drivers/media/dvb/frontends/lnbp21.c30
-rw-r--r--drivers/media/dvb/frontends/lnbp21.h12
-rw-r--r--drivers/media/dvb/frontends/mt2060.c370
-rw-r--r--drivers/media/dvb/frontends/mt2060.h43
-rw-r--r--drivers/media/dvb/frontends/mt2060_priv.h105
-rw-r--r--drivers/media/dvb/frontends/mt312.h10
-rw-r--r--drivers/media/dvb/frontends/mt352.c16
-rw-r--r--drivers/media/dvb/frontends/mt352.h16
-rw-r--r--drivers/media/dvb/frontends/nxt200x.h9
-rw-r--r--drivers/media/dvb/frontends/nxt6000.h9
-rw-r--r--drivers/media/dvb/frontends/or51132.c176
-rw-r--r--drivers/media/dvb/frontends/or51132.h9
-rw-r--r--drivers/media/dvb/frontends/or51211.c124
-rw-r--r--drivers/media/dvb/frontends/or51211.h9
-rw-r--r--drivers/media/dvb/frontends/s5h1420.h9
-rw-r--r--drivers/media/dvb/frontends/sp8870.h9
-rw-r--r--drivers/media/dvb/frontends/sp887x.h9
-rw-r--r--drivers/media/dvb/frontends/stv0297.h9
-rw-r--r--drivers/media/dvb/frontends/stv0299.c9
-rw-r--r--drivers/media/dvb/frontends/stv0299.h19
-rw-r--r--drivers/media/dvb/frontends/tda10021.c63
-rw-r--r--drivers/media/dvb/frontends/tda10021.h19
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c20
-rw-r--r--drivers/media/dvb/frontends/tda1004x.h30
-rw-r--r--drivers/media/dvb/frontends/tda10086.c744
-rw-r--r--drivers/media/dvb/frontends/tda10086.h50
-rw-r--r--drivers/media/dvb/frontends/tda8083.c30
-rw-r--r--drivers/media/dvb/frontends/tda8083.h9
-rw-r--r--drivers/media/dvb/frontends/tda826x.c175
-rw-r--r--drivers/media/dvb/frontends/tda826x.h53
-rw-r--r--drivers/media/dvb/frontends/tua6100.c204
-rw-r--r--drivers/media/dvb/frontends/tua6100.h47
-rw-r--r--drivers/media/dvb/frontends/ves1820.h9
-rw-r--r--drivers/media/dvb/frontends/ves1x93.h9
-rw-r--r--drivers/media/dvb/frontends/zl10353.c11
-rw-r--r--drivers/media/dvb/frontends/zl10353.h14
72 files changed, 6421 insertions, 1935 deletions
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index db978555b1e..af314bb1dca 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -1,48 +1,73 @@
menu "Customise DVB Frontends"
depends on DVB_CORE
+config DVB_FE_CUSTOMISE
+ bool "Customise the frontend modules to build"
+ default N
+ help
+ This allows the user to deselect frontend drivers unnecessary
+ for their hardware from the build. Use this option with care
+ as deselecting frontends which are in fact necessary will result
+ in DVB devices which cannot be tuned due to lack of driver support.
+
+ If unsure say N.
+
comment "DVB-S (satellite) frontends"
depends on DVB_CORE
config DVB_STV0299
tristate "ST STV0299 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_CX24110
tristate "Conexant CX24110 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_CX24123
tristate "Conexant CX24123 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_TDA8083
tristate "Philips TDA8083 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_MT312
tristate "Zarlink VP310/MT312 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_VES1X93
tristate "VLSI VES1893 or VES1993 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_S5H1420
tristate "Samsung S5H1420 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10086
+ tristate "Philips TDA10086 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
@@ -52,6 +77,7 @@ comment "DVB-T (terrestrial) frontends"
config DVB_SP8870
tristate "Spase sp8870 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
A DVB-T tuner module. Say Y when you want to support this frontend.
@@ -64,6 +90,7 @@ config DVB_SP8870
config DVB_SP887X
tristate "Spase sp887x based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
A DVB-T tuner module. Say Y when you want to support this frontend.
@@ -76,24 +103,28 @@ config DVB_SP887X
config DVB_CX22700
tristate "Conexant CX22700 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_CX22702
tristate "Conexant cx22702 demodulator (OFDM)"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_L64781
tristate "LSI L64781"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_TDA1004X
tristate "Philips TDA10045H/TDA10046H based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
A DVB-T tuner module. Say Y when you want to support this frontend.
@@ -107,24 +138,28 @@ config DVB_TDA1004X
config DVB_NXT6000
tristate "NxtWave Communications NXT6000 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_MT352
tristate "Zarlink MT352 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_ZL10353
tristate "Zarlink ZL10353 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_DIB3000MB
tristate "DiBcom 3000M-B"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Designed for mobile usage. Say Y when you want
to support this frontend.
@@ -132,6 +167,23 @@ config DVB_DIB3000MB
config DVB_DIB3000MC
tristate "DiBcom 3000P/M-C"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB7000M
+ tristate "DiBcom 7000MA/MB/PA/PB/MC"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB7000P
+ tristate "DiBcom 7000PC"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Designed for mobile usage. Say Y when you want
to support this frontend.
@@ -142,18 +194,21 @@ comment "DVB-C (cable) frontends"
config DVB_VES1820
tristate "VLSI VES1820 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-C tuner module. Say Y when you want to support this frontend.
config DVB_TDA10021
tristate "Philips TDA10021 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-C tuner module. Say Y when you want to support this frontend.
config DVB_STV0297
tristate "ST STV0297 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-C tuner module. Say Y when you want to support this frontend.
@@ -163,6 +218,7 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends"
config DVB_NXT200X
tristate "NxtWave Communications NXT2002/NXT2004 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
@@ -177,6 +233,7 @@ config DVB_NXT200X
config DVB_OR51211
tristate "Oren OR51211 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
@@ -189,6 +246,7 @@ config DVB_OR51211
config DVB_OR51132
tristate "Oren OR51132 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
@@ -204,6 +262,7 @@ config DVB_OR51132
config DVB_BCM3510
tristate "Broadcom BCM3510"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to
@@ -212,28 +271,62 @@ config DVB_BCM3510
config DVB_LGDT330X
tristate "LG Electronics LGDT3302/LGDT3303 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
-
-comment "Miscellaneous devices"
+comment "Tuners/PLL support"
depends on DVB_CORE
config DVB_PLL
tristate
depends on DVB_CORE && I2C
+config DVB_TDA826X
+ tristate "Philips TDA826X silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this 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_LGH06XF
+ tristate "LG TDVS-H06xF ATSC tuner"
+ depends on DVB_CORE && I2C
+ select DVB_PLL
+ default m if DVB_FE_CUSTOMISE
+ help
+ A driver for the LG TDVS-H06xF ATSC tuner family.
+
+comment "Miscellaneous devices"
+ depends on DVB_CORE
+
config DVB_LNBP21
tristate "LNBP21 SEC controller"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
An SEC control chip.
config DVB_ISL6421
tristate "ISL6421 SEC controller"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
An SEC control chip.
+config DVB_TUA6100
+ tristate "TUA6100 PLL"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVBS PLL chip.
+
endmenu
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 0e4880b6db1..3fa6e5d32a9 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -11,8 +11,10 @@ obj-$(CONFIG_DVB_CX22700) += cx22700.o
obj-$(CONFIG_DVB_CX24110) += cx24110.o
obj-$(CONFIG_DVB_TDA8083) += tda8083.o
obj-$(CONFIG_DVB_L64781) += l64781.o
-obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o dib3000-common.o
-obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dib3000-common.o
+obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
+obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o
+obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o
obj-$(CONFIG_DVB_MT312) += mt312.o
obj-$(CONFIG_DVB_VES1820) += ves1820.o
obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
@@ -33,3 +35,8 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
obj-$(CONFIG_DVB_CX24123) += cx24123.o
obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
obj-$(CONFIG_DVB_ISL6421) += isl6421.o
+obj-$(CONFIG_DVB_TDA10086) += tda10086.o
+obj-$(CONFIG_DVB_TDA826X) += tda826x.o
+obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
+obj-$(CONFIG_DVB_TUA6100) += tua6100.o
+obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o
diff --git a/drivers/media/dvb/frontends/bcm3510.h b/drivers/media/dvb/frontends/bcm3510.h
index 80f5d0953d0..7e4f95e1734 100644
--- a/drivers/media/dvb/frontends/bcm3510.h
+++ b/drivers/media/dvb/frontends/bcm3510.h
@@ -34,7 +34,16 @@ struct bcm3510_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_BCM3510) || (defined(CONFIG_DVB_BCM3510_MODULE) && defined(MODULE))
extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_BCM3510
#endif
diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h
index dcd8979c1a1..7ac33690cdc 100644
--- a/drivers/media/dvb/frontends/cx22700.h
+++ b/drivers/media/dvb/frontends/cx22700.h
@@ -31,7 +31,16 @@ struct cx22700_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_CX22700) || (defined(CONFIG_DVB_CX22700_MODULE) && defined(MODULE))
extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX22700
#endif // CX22700_H
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
index 4106d46c957..335219ebce2 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -399,7 +399,9 @@ static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_str
{
struct cx22702_state* state = fe->demodulator_priv;
- *signal_strength = cx22702_readreg (state, 0x23);
+ u16 rs_ber = 0;
+ rs_ber = cx22702_readreg (state, 0x23);
+ *signal_strength = (rs_ber << 8) | rs_ber;
return 0;
}
diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h
index 7f2f241e5d4..9cd64da6ee4 100644
--- a/drivers/media/dvb/frontends/cx22702.h
+++ b/drivers/media/dvb/frontends/cx22702.h
@@ -41,7 +41,16 @@ struct cx22702_config
u8 output_mode;
};
+#if defined(CONFIG_DVB_CX22702) || (defined(CONFIG_DVB_CX22702_MODULE) && defined(MODULE))
extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX22702
#endif // CX22702_H
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
index ce3c7398bac..ae96395217a 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -1,4 +1,4 @@
-/*
+ /*
cx24110 - Single Chip Satellite Channel Receiver driver module
Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on
@@ -311,16 +311,17 @@ static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
}
-int cx24110_pll_write (struct dvb_frontend* fe, u32 data)
+static int _cx24110_pll_write (struct dvb_frontend* fe, u8 *buf, int len)
{
struct cx24110_state *state = fe->demodulator_priv;
+ if (len != 3)
+ return -EINVAL;
+
/* tuner data is 21 bits long, must be left-aligned in data */
/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */
/* FIXME (low): add error handling, avoid infinite loops if HW fails... */
- dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data);
-
cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */
cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */
@@ -329,19 +330,19 @@ int cx24110_pll_write (struct dvb_frontend* fe, u32 data)
cx24110_writereg(state,0x72,0);
/* write the topmost 8 bits */
- cx24110_writereg(state,0x72,(data>>24)&0xff);
+ cx24110_writereg(state,0x72,buf[0]);
/* wait for the send to be completed */
while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
;
/* send another 8 bytes */
- cx24110_writereg(state,0x72,(data>>16)&0xff);
+ cx24110_writereg(state,0x72,buf[1]);
while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
;
/* and the topmost 5 bits of this byte */
- cx24110_writereg(state,0x72,(data>>8)&0xff);
+ cx24110_writereg(state,0x72,buf[2]);
while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
;
@@ -642,6 +643,7 @@ static struct dvb_frontend_ops cx24110_ops = {
.release = cx24110_release,
.init = cx24110_initfe,
+ .write = _cx24110_pll_write,
.set_frontend = cx24110_set_frontend,
.get_frontend = cx24110_get_frontend,
.read_status = cx24110_read_status,
@@ -664,4 +666,3 @@ MODULE_AUTHOR("Peter Hettkamp");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(cx24110_attach);
-EXPORT_SYMBOL(cx24110_pll_write);
diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h
index b354a64e0e7..0ca3af4db51 100644
--- a/drivers/media/dvb/frontends/cx24110.h
+++ b/drivers/media/dvb/frontends/cx24110.h
@@ -33,9 +33,24 @@ struct cx24110_config
u8 demod_address;
};
+static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) {
+ int r = 0;
+ u8 buf[] = {(u8) (val>>24), (u8) (val>>16), (u8) (val>>8)};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 3);
+ return r;
+}
+
+#if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE))
extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
struct i2c_adapter* i2c);
-
-extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data);
+#else
+static inline struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX24110
#endif // CX24110_H
diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c
index 274a87b7a5d..a356d28fc3b 100644
--- a/drivers/media/dvb/frontends/cx24123.c
+++ b/drivers/media/dvb/frontends/cx24123.c
@@ -45,9 +45,6 @@ struct cx24123_state
struct dvb_frontend frontend;
- u32 lastber;
- u16 snr;
-
/* Some PLL specifics for tuning */
u32 VCAarg;
u32 VGAarg;
@@ -194,7 +191,7 @@ static struct {
{0x06, 0x31}, /* MPEG (default) */
{0x0b, 0x00}, /* Freq search start point (default) */
{0x0c, 0x00}, /* Demodulator sample gain (default) */
- {0x0d, 0x02}, /* Frequency search range = Fsymbol / 4 (default) */
+ {0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */
{0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */
{0x0f, 0xfe}, /* FEC search mask (all supported codes) */
{0x10, 0x01}, /* Default search inversion, no repeat (default) */
@@ -223,8 +220,9 @@ static struct {
{0x44, 0x00}, /* Constellation (default) */
{0x45, 0x00}, /* Symbol count (default) */
{0x46, 0x0d}, /* Symbol rate estimator on (default) */
- {0x56, 0x41}, /* Various (default) */
+ {0x56, 0xc1}, /* Error Counter = Viterbi BER */
{0x57, 0xff}, /* Error Counter Window (default) */
+ {0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */
{0x67, 0x83}, /* Non-DCII symbol clock */
};
@@ -321,6 +319,12 @@ static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec)
if ( (fec < FEC_NONE) || (fec > FEC_AUTO) )
fec = FEC_AUTO;
+ /* Set the soft decision threshold */
+ if(fec == FEC_1_2)
+ cx24123_writereg(state, 0x43, cx24123_readreg(state, 0x43) | 0x01);
+ else
+ cx24123_writereg(state, 0x43, cx24123_readreg(state, 0x43) & ~0x01);
+
switch (fec) {
case FEC_1_2:
dprintk("%s: set FEC to 1/2\n",__FUNCTION__);
@@ -549,8 +553,8 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa
ndiv = ( ((p->frequency * vco_div * 10) / (2 * XTAL / 1000)) / 32) & 0x1ff;
adiv = ( ((p->frequency * vco_div * 10) / (2 * XTAL / 1000)) % 32) & 0x1f;
- if (adiv == 0)
- ndiv++;
+ if (adiv == 0 && ndiv > 0)
+ ndiv--;
/* control bits 11, refdiv 11, charge pump polarity 1, charge pump current, ndiv, adiv */
state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | (pump << 14) | (ndiv << 5) | adiv;
@@ -657,6 +661,10 @@ static int cx24123_initfe(struct dvb_frontend* fe)
for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++)
cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data);
+ /* Set the LNB polarity */
+ if(state->config->lnb_polarity)
+ cx24123_writereg(state, 0x32, cx24123_readreg(state, 0x32) | 0x02);
+
return 0;
}
@@ -674,6 +682,9 @@ static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage
case SEC_VOLTAGE_18:
dprintk("%s: setting voltage 18V\n", __FUNCTION__);
return cx24123_writereg(state, 0x29, val | 0x80);
+ case SEC_VOLTAGE_OFF:
+ /* already handled in cx88-dvb */
+ return 0;
default:
return -EINVAL;
};
@@ -776,13 +787,15 @@ static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* status)
if (lock & 0x01)
*status |= FE_HAS_SIGNAL;
if (sync & 0x02)
- *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_CARRIER; /* Phase locked */
if (sync & 0x04)
*status |= FE_HAS_VITERBI;
+
+ /* Reed-Solomon Status */
if (sync & 0x08)
*status |= FE_HAS_SYNC;
if (sync & 0x80)
- *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_LOCK; /*Full Sync */
return 0;
}
@@ -795,29 +808,13 @@ static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
{
struct cx24123_state *state = fe->demodulator_priv;
- state->lastber =
- ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
+ /* The true bit error rate is this value divided by
+ the window size (set as 256 * 255) */
+ *ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
(cx24123_readreg(state, 0x1d) << 8 |
- cx24123_readreg(state, 0x1e));
-
- /* Do the signal quality processing here, it's derived from the BER. */
- /* Scale the BER from a 24bit to a SNR 16 bit where higher = better */
- if (state->lastber < 5000)
- state->snr = 655*100;
- else if ( (state->lastber >= 5000) && (state->lastber < 55000) )
- state->snr = 655*90;
- else if ( (state->lastber >= 55000) && (state->lastber < 150000) )
- state->snr = 655*80;
- else if ( (state->lastber >= 150000) && (state->lastber < 250000) )
- state->snr = 655*70;
- else if ( (state->lastber >= 250000) && (state->lastber < 450000) )
- state->snr = 655*65;
- else
- state->snr = 0;
-
- dprintk("%s: BER = %d, S/N index = %d\n",__FUNCTION__,state->lastber, state->snr);
+ cx24123_readreg(state, 0x1e));
- *ber = state->lastber;
+ dprintk("%s: BER = %d\n",__FUNCTION__,*ber);
return 0;
}
@@ -825,6 +822,7 @@ static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
{
struct cx24123_state *state = fe->demodulator_priv;
+
*signal_strength = cx24123_readreg(state, 0x3b) << 8; /* larger = better */
dprintk("%s: Signal strength = %d\n",__FUNCTION__,*signal_strength);
@@ -835,19 +833,13 @@ static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_str
static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct cx24123_state *state = fe->demodulator_priv;
- *snr = state->snr;
-
- dprintk("%s: read S/N index = %d\n",__FUNCTION__,*snr);
- return 0;
-}
+ /* Inverted raw Es/N0 count, totally bogus but better than the
+ BER threshold. */
+ *snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) |
+ (u16)cx24123_readreg(state, 0x19));
-static int cx24123_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
-{
- struct cx24123_state *state = fe->demodulator_priv;
- *ucblocks = state->lastber;
-
- dprintk("%s: ucblocks (ber) = %d\n",__FUNCTION__,*ucblocks);
+ dprintk("%s: read S/N index = %d\n",__FUNCTION__,*snr);
return 0;
}
@@ -922,6 +914,29 @@ static int cx24123_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
return 0;
}
+static int cx24123_tune(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters* params,
+ unsigned int mode_flags,
+ int *delay,
+ fe_status_t *status)
+{
+ int retval = 0;
+
+ if (params != NULL)
+ retval = cx24123_set_frontend(fe, params);
+
+ if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
+ cx24123_read_status(fe, status);
+ *delay = HZ/10;
+
+ return retval;
+}
+
+static int cx24123_get_algo(struct dvb_frontend *fe)
+{
+ return 1; //FE_ALGO_HW
+}
+
static void cx24123_release(struct dvb_frontend* fe)
{
struct cx24123_state* state = fe->demodulator_priv;
@@ -949,8 +964,6 @@ struct dvb_frontend* cx24123_attach(const struct cx24123_config* config,
/* setup the state */
state->config = config;
state->i2c = i2c;
- state->lastber = 0;
- state->snr = 0;
state->VCAarg = 0;
state->VGAarg = 0;
state->bandselectarg = 0;
@@ -1003,11 +1016,12 @@ static struct dvb_frontend_ops cx24123_ops = {
.read_ber = cx24123_read_ber,
.read_signal_strength = cx24123_read_signal_strength,
.read_snr = cx24123_read_snr,
- .read_ucblocks = cx24123_read_ucblocks,
.diseqc_send_master_cmd = cx24123_send_diseqc_msg,
.diseqc_send_burst = cx24123_diseqc_send_burst,
.set_tone = cx24123_set_tone,
.set_voltage = cx24123_set_voltage,
+ .tune = cx24123_tune,
+ .get_frontend_algo = cx24123_get_algo,
};
module_param(debug, int, 0644);
diff --git a/drivers/media/dvb/frontends/cx24123.h b/drivers/media/dvb/frontends/cx24123.h
index 9606f825935..84f9e4f5c15 100644
--- a/drivers/media/dvb/frontends/cx24123.h
+++ b/drivers/media/dvb/frontends/cx24123.h
@@ -30,9 +30,21 @@ struct cx24123_config
/* Need to set device param for start_dma */
int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+
+ /* 0 = LNB voltage normal, 1 = LNB voltage inverted */
+ int lnb_polarity;
};
+#if defined(CONFIG_DVB_CX24123) || (defined(CONFIG_DVB_CX24123_MODULE) && defined(MODULE))
extern struct dvb_frontend* cx24123_attach(const struct cx24123_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx24123_attach(const struct cx24123_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX24123
#endif /* CX24123_H */
diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c
deleted file mode 100644
index 1a4f1f7c228..00000000000
--- a/drivers/media/dvb/frontends/dib3000-common.c
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "dib3000-common.h"
-
-#ifdef CONFIG_DVB_DIBCOM_DEBUG
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able)).");
-#endif
-#define deb_info(args...) dprintk(0x01,args)
-#define deb_i2c(args...) dprintk(0x02,args)
-#define deb_srch(args...) dprintk(0x04,args)
-
-
-int dib3000_read_reg(struct dib3000_state *state, u16 reg)
-{
- u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
- u8 rb[2];
- struct i2c_msg msg[] = {
- { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 },
- { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
- };
-
- if (i2c_transfer(state->i2c, msg, 2) != 2)
- deb_i2c("i2c read error\n");
-
- deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,
- (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]);
-
- return (rb[0] << 8) | rb[1];
-}
-
-int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val)
-{
- u8 b[] = {
- (reg >> 8) & 0xff, reg & 0xff,
- (val >> 8) & 0xff, val & 0xff,
- };
- struct i2c_msg msg[] = {
- { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 }
- };
- deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val);
-
- return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0;
-}
-
-int dib3000_search_status(u16 irq,u16 lock)
-{
- if (irq & 0x02) {
- if (lock & 0x01) {
- deb_srch("auto search succeeded\n");
- return 1; // auto search succeeded
- } else {
- deb_srch("auto search not successful\n");
- return 0; // auto search failed
- }
- } else if (irq & 0x01) {
- deb_srch("auto search failed\n");
- return 0; // auto search failed
- }
- return -1; // try again
-}
-
-/* for auto search */
-u16 dib3000_seq[2][2][2] = /* fft,gua, inv */
- { /* fft */
- { /* gua */
- { 0, 1 }, /* 0 0 { 0,1 } */
- { 3, 9 }, /* 0 1 { 0,1 } */
- },
- {
- { 2, 5 }, /* 1 0 { 0,1 } */
- { 6, 11 }, /* 1 1 { 0,1 } */
- }
- };
-
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de");
-MODULE_DESCRIPTION("Common functions for the dib3000mb/dib3000mc dvb frontend drivers");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(dib3000_seq);
-
-EXPORT_SYMBOL(dib3000_read_reg);
-EXPORT_SYMBOL(dib3000_write_reg);
-EXPORT_SYMBOL(dib3000_search_status);
diff --git a/drivers/media/dvb/frontends/dib3000-common.h b/drivers/media/dvb/frontends/dib3000-common.h
deleted file mode 100644
index be1c0d3e138..00000000000
--- a/drivers/media/dvb/frontends/dib3000-common.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * .h-files for the common use of the frontend drivers made by DiBcom
- * DiBcom 3000M-B/C, 3000P
- *
- * DiBcom (http://www.dibcom.fr/)
- *
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
- *
- * based on GPL code from DibCom, which has
- *
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
- * Acknowledgements
- *
- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
- * sources, on which this driver (and the dvb-dibusb) are based.
- *
- * see Documentation/dvb/README.dibusb for more information
- *
- */
-
-#ifndef DIB3000_COMMON_H
-#define DIB3000_COMMON_H
-
-#include "dvb_frontend.h"
-#include "dib3000.h"
-
-/* info and err, taken from usb.h, if there is anything available like by default. */
-#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg)
-#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg)
-#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg)
-
-/* frontend state */
-struct dib3000_state {
- struct i2c_adapter* i2c;
-
-/* configuration settings */
- struct dib3000_config config;
-
- struct dvb_frontend frontend;
- int timing_offset;
- int timing_offset_comp_done;
-
- fe_bandwidth_t last_tuned_bw;
- u32 last_tuned_freq;
-};
-
-/* commonly used methods by the dib3000mb/mc/p frontend */
-extern int dib3000_read_reg(struct dib3000_state *state, u16 reg);
-extern int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val);
-
-extern int dib3000_search_status(u16 irq,u16 lock);
-
-/* handy shortcuts */
-#define rd(reg) dib3000_read_reg(state,reg)
-
-#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \
- { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
-
-#define wr_foreach(a,v) { int i; \
- if (sizeof(a) != sizeof(v)) \
- err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\
- for (i=0; i < sizeof(a)/sizeof(u16); i++) \
- wr(a[i],v[i]); \
- }
-
-#define set_or(reg,val) wr(reg,rd(reg) | val)
-
-#define set_and(reg,val) wr(reg,rd(reg) & val)
-
-
-/* debug */
-
-#ifdef CONFIG_DVB_DIBCOM_DEBUG
-#define dprintk(level,args...) \
- do { if ((debug & level)) { printk(args); } } while (0)
-#else
-#define dprintk(args...) do { } while (0)
-#endif
-
-/* mask for enabling a specific pid for the pid_filter */
-#define DIB3000_ACTIVATE_PID_FILTERING (0x2000)
-
-/* common values for tuning */
-#define DIB3000_ALPHA_0 ( 0)
-#define DIB3000_ALPHA_1 ( 1)
-#define DIB3000_ALPHA_2 ( 2)
-#define DIB3000_ALPHA_4 ( 4)
-
-#define DIB3000_CONSTELLATION_QPSK ( 0)
-#define DIB3000_CONSTELLATION_16QAM ( 1)
-#define DIB3000_CONSTELLATION_64QAM ( 2)
-
-#define DIB3000_GUARD_TIME_1_32 ( 0)
-#define DIB3000_GUARD_TIME_1_16 ( 1)
-#define DIB3000_GUARD_TIME_1_8 ( 2)
-#define DIB3000_GUARD_TIME_1_4 ( 3)
-
-#define DIB3000_TRANSMISSION_MODE_2K ( 0)
-#define DIB3000_TRANSMISSION_MODE_8K ( 1)
-
-#define DIB3000_SELECT_LP ( 0)
-#define DIB3000_SELECT_HP ( 1)
-
-#define DIB3000_FEC_1_2 ( 1)
-#define DIB3000_FEC_2_3 ( 2)
-#define DIB3000_FEC_3_4 ( 3)
-#define DIB3000_FEC_5_6 ( 5)
-#define DIB3000_FEC_7_8 ( 7)
-
-#define DIB3000_HRCH_OFF ( 0)
-#define DIB3000_HRCH_ON ( 1)
-
-#define DIB3000_DDS_INVERSION_OFF ( 0)
-#define DIB3000_DDS_INVERSION_ON ( 1)
-
-#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8))
-#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7)))
-
-/* for auto search */
-extern u16 dib3000_seq[2][2][2];
-
-#define DIB3000_REG_MANUFACTOR_ID ( 1025)
-#define DIB3000_I2C_ID_DIBCOM (0x01b3)
-
-#define DIB3000_REG_DEVICE_ID ( 1026)
-#define DIB3000MB_DEVICE_ID (0x3000)
-#define DIB3000MC_DEVICE_ID (0x3001)
-#define DIB3000P_DEVICE_ID (0x3002)
-
-#endif // DIB3000_COMMON_H
diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h
index ec927628d27..a6d3854a67b 100644
--- a/drivers/media/dvb/frontends/dib3000.h
+++ b/drivers/media/dvb/frontends/dib3000.h
@@ -41,9 +41,16 @@ struct dib_fe_xfer_ops
int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl);
};
+#if defined(CONFIG_DVB_DIB3000MB) || (defined(CONFIG_DVB_DIB3000MB_MODULE) && defined(MODULE))
extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
+#else
+static inline struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_DIB3000MB
-extern struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
- struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
#endif // DIB3000_H
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c
index 5302e11883a..adbabfdb04a 100644
--- a/drivers/media/dvb/frontends/dib3000mb.c
+++ b/drivers/media/dvb/frontends/dib3000mb.c
@@ -29,9 +29,10 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dib3000-common.h"
-#include "dib3000mb_priv.h"
+#include "dvb_frontend.h"
+
#include "dib3000.h"
+#include "dib3000mb_priv.h"
/* Version information */
#define DRIVER_VERSION "0.1"
@@ -44,10 +45,81 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able)).");
#endif
#define deb_info(args...) dprintk(0x01,args)
+#define deb_i2c(args...) dprintk(0x02,args)
+#define deb_srch(args...) dprintk(0x04,args)
+#define deb_info(args...) dprintk(0x01,args)
#define deb_xfer(args...) dprintk(0x02,args)
#define deb_setf(args...) dprintk(0x04,args)
#define deb_getf(args...) dprintk(0x08,args)
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able)).");
+#endif
+
+static int dib3000_read_reg(struct dib3000_state *state, u16 reg)
+{
+ u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[] = {
+ { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2)
+ deb_i2c("i2c read error\n");
+
+ deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,
+ (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val)
+{
+ u8 b[] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg[] = {
+ { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 }
+ };
+ deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val);
+
+ return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+static int dib3000_search_status(u16 irq,u16 lock)
+{
+ if (irq & 0x02) {
+ if (lock & 0x01) {
+ deb_srch("auto search succeeded\n");
+ return 1; // auto search succeeded
+ } else {
+ deb_srch("auto search not successful\n");
+ return 0; // auto search failed
+ }
+ } else if (irq & 0x01) {
+ deb_srch("auto search failed\n");
+ return 0; // auto search failed
+ }
+ return -1; // try again
+}
+
+/* for auto search */
+static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */
+ { /* fft */
+ { /* gua */
+ { 0, 1 }, /* 0 0 { 0,1 } */
+ { 3, 9 }, /* 0 1 { 0,1 } */
+ },
+ {
+ { 2, 5 }, /* 1 0 { 0,1 } */
+ { 6, 11 }, /* 1 1 { 0,1 } */
+ }
+ };
+
static int dib3000mb_get_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep);
diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h
index 999b1904781..1a12747fdc9 100644
--- a/drivers/media/dvb/frontends/dib3000mb_priv.h
+++ b/drivers/media/dvb/frontends/dib3000mb_priv.h
@@ -13,6 +13,99 @@
#ifndef __DIB3000MB_PRIV_H_INCLUDED__
#define __DIB3000MB_PRIV_H_INCLUDED__
+/* info and err, taken from usb.h, if there is anything available like by default. */
+#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg)
+
+/* handy shortcuts */
+#define rd(reg) dib3000_read_reg(state,reg)
+
+#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \
+ { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
+
+#define wr_foreach(a,v) { int i; \
+ if (sizeof(a) != sizeof(v)) \
+ err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\
+ for (i=0; i < sizeof(a)/sizeof(u16); i++) \
+ wr(a[i],v[i]); \
+ }
+
+#define set_or(reg,val) wr(reg,rd(reg) | val)
+
+#define set_and(reg,val) wr(reg,rd(reg) & val)
+
+/* debug */
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define dprintk(level,args...) \
+ do { if ((debug & level)) { printk(args); } } while (0)
+#else
+#define dprintk(args...) do { } while (0)
+#endif
+
+/* mask for enabling a specific pid for the pid_filter */
+#define DIB3000_ACTIVATE_PID_FILTERING (0x2000)
+
+/* common values for tuning */
+#define DIB3000_ALPHA_0 ( 0)
+#define DIB3000_ALPHA_1 ( 1)
+#define DIB3000_ALPHA_2 ( 2)
+#define DIB3000_ALPHA_4 ( 4)
+
+#define DIB3000_CONSTELLATION_QPSK ( 0)
+#define DIB3000_CONSTELLATION_16QAM ( 1)
+#define DIB3000_CONSTELLATION_64QAM ( 2)
+
+#define DIB3000_GUARD_TIME_1_32 ( 0)
+#define DIB3000_GUARD_TIME_1_16 ( 1)
+#define DIB3000_GUARD_TIME_1_8 ( 2)
+#define DIB3000_GUARD_TIME_1_4 ( 3)
+
+#define DIB3000_TRANSMISSION_MODE_2K ( 0)
+#define DIB3000_TRANSMISSION_MODE_8K ( 1)
+
+#define DIB3000_SELECT_LP ( 0)
+#define DIB3000_SELECT_HP ( 1)
+
+#define DIB3000_FEC_1_2 ( 1)
+#define DIB3000_FEC_2_3 ( 2)
+#define DIB3000_FEC_3_4 ( 3)
+#define DIB3000_FEC_5_6 ( 5)
+#define DIB3000_FEC_7_8 ( 7)
+
+#define DIB3000_HRCH_OFF ( 0)
+#define DIB3000_HRCH_ON ( 1)
+
+#define DIB3000_DDS_INVERSION_OFF ( 0)
+#define DIB3000_DDS_INVERSION_ON ( 1)
+
+#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8))
+#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7)))
+
+#define DIB3000_REG_MANUFACTOR_ID ( 1025)
+#define DIB3000_I2C_ID_DIBCOM (0x01b3)
+
+#define DIB3000_REG_DEVICE_ID ( 1026)
+#define DIB3000MB_DEVICE_ID (0x3000)
+#define DIB3000MC_DEVICE_ID (0x3001)
+#define DIB3000P_DEVICE_ID (0x3002)
+
+/* frontend state */
+struct dib3000_state {
+ struct i2c_adapter* i2c;
+
+/* configuration settings */
+ struct dib3000_config config;
+
+ struct dvb_frontend frontend;
+ int timing_offset;
+ int timing_offset_comp_done;
+
+ fe_bandwidth_t last_tuned_bw;
+ u32 last_tuned_freq;
+};
+
/* register addresses and some of their default values */
/* restart subsystems */
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
index 98673474a14..5da66178006 100644
--- a/drivers/media/dvb/frontends/dib3000mc.c
+++ b/drivers/media/dvb/frontends/dib3000mc.c
@@ -1,725 +1,722 @@
/*
- * Frontend driver for mobile DVB-T demodulator DiBcom 3000P/M-C
- * DiBcom (http://www.dibcom.fr/)
+ * Driver for DiBcom DiB3000MC/P-demodulator.
*
+ * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/)
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
- * based on GPL code from DiBCom, which has
+ * This code is partially based on the previous dib3000mc.c .
*
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
- *
- * This program is free software; you can redistribute it and/or
+ * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
- *
- * Acknowledgements
- *
- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
- * sources, on which this driver (and the dvb-dibusb) are based.
- *
- * see Documentation/dvb/README.dibusb for more information
- *
*/
+
#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-
-#include "dib3000-common.h"
-#include "dib3000mc_priv.h"
-#include "dib3000.h"
-
-/* Version information */
-#define DRIVER_VERSION "0.1"
-#define DRIVER_DESC "DiBcom 3000M-C DVB-T demodulator"
-#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
-
-#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#include <linux/i2c.h>
+//#include <linux/init.h>
+//#include <linux/delay.h>
+//#include <linux/string.h>
+//#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+
+#include "dib3000mc.h"
+
static int debug;
module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe,16=stat (|-able)).");
-#endif
-#define deb_info(args...) dprintk(0x01,args)
-#define deb_xfer(args...) dprintk(0x02,args)
-#define deb_setf(args...) dprintk(0x04,args)
-#define deb_getf(args...) dprintk(0x08,args)
-#define deb_stat(args...) dprintk(0x10,args)
-
-static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode,
- fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth)
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); } } while (0)
+
+struct dib3000mc_state {
+ struct dvb_frontend demod;
+ struct dib3000mc_config *cfg;
+
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+
+ struct dibx000_i2c_master i2c_master;
+
+ u32 timf;
+
+ fe_bandwidth_t current_bandwidth;
+
+ u16 dev_id;
+};
+
+static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
{
- switch (transmission_mode) {
- case TRANSMISSION_MODE_2K:
- wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[0]);
- break;
- case TRANSMISSION_MODE_8K:
- wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[1]);
- break;
- default:
- break;
- }
+ u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
- switch (bandwidth) {
-/* case BANDWIDTH_5_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[0]);
- break; */
- case BANDWIDTH_6_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[1]);
- break;
- case BANDWIDTH_7_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[2]);
- break;
- case BANDWIDTH_8_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[3]);
- break;
- default:
- break;
- }
+ if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+ dprintk("i2c read error on %d\n",reg);
- switch (mode) {
- case 0: /* no impulse */ /* fall through */
- wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[0]);
- break;
- case 1: /* new algo */
- wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[1]);
- set_or(DIB3000MC_REG_IMP_NOISE_55,DIB3000MC_IMP_NEW_ALGO(0)); /* gives 1<<10 */
- break;
- default: /* old algo */
- wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[3]);
- break;
- }
- return 0;
+ return (rb[0] << 8) | rb[1];
}
-static int dib3000mc_set_timing(struct dib3000_state *state, int upd_offset,
- fe_transmit_mode_t fft, fe_bandwidth_t bw)
+static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
{
- u16 timf_msb,timf_lsb;
- s32 tim_offset,tim_sgn;
- u64 comp1,comp2,comp=0;
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
- switch (bw) {
- case BANDWIDTH_8_MHZ: comp = DIB3000MC_CLOCK_REF*8; break;
- case BANDWIDTH_7_MHZ: comp = DIB3000MC_CLOCK_REF*7; break;
- case BANDWIDTH_6_MHZ: comp = DIB3000MC_CLOCK_REF*6; break;
- default: err("unknown bandwidth (%d)",bw); break;
+
+static int dib3000mc_identify(struct dib3000mc_state *state)
+{
+ u16 value;
+ if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) {
+ dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value);
+ return -EREMOTEIO;
}
- timf_msb = (comp >> 16) & 0xff;
- timf_lsb = (comp & 0xffff);
-
- // Update the timing offset ;
- if (upd_offset > 0) {
- if (!state->timing_offset_comp_done) {
- msleep(200);
- state->timing_offset_comp_done = 1;
- }
- tim_offset = rd(DIB3000MC_REG_TIMING_OFFS_MSB);
- if ((tim_offset & 0x2000) == 0x2000)
- tim_offset |= 0xC000;
- if (fft == TRANSMISSION_MODE_2K)
- tim_offset <<= 2;
- state->timing_offset += tim_offset;
+
+ value = dib3000mc_read_word(state, 1026);
+ if (value != 0x3001 && value != 0x3002) {
+ dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value);
+ return -EREMOTEIO;
}
+ state->dev_id = value;
+
+ dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id);
+
+ return 0;
+}
- tim_offset = state->timing_offset;
- if (tim_offset < 0) {
- tim_sgn = 1;
- tim_offset = -tim_offset;
+static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u8 bw, u8 update_offset)
+{
+ u32 timf;
+
+ if (state->timf == 0) {
+ timf = 1384402; // default value for 8MHz
+ if (update_offset)
+ msleep(200); // first time we do an update
} else
- tim_sgn = 0;
+ timf = state->timf;
- comp1 = (u32)tim_offset * (u32)timf_lsb ;
- comp2 = (u32)tim_offset * (u32)timf_msb ;
- comp = ((comp1 >> 16) + comp2) >> 7;
+ timf *= (BW_INDEX_TO_KHZ(bw) / 1000);
- if (tim_sgn == 0)
- comp = (u32)(timf_msb << 16) + (u32) timf_lsb + comp;
- else
- comp = (u32)(timf_msb << 16) + (u32) timf_lsb - comp ;
+ if (update_offset) {
+ s16 tim_offs = dib3000mc_read_word(state, 416);
+
+ if (tim_offs & 0x2000)
+ tim_offs -= 0x4000;
+
+ if (nfft == 0)
+ tim_offs *= 4;
+
+ timf += tim_offs;
+ state->timf = timf / (BW_INDEX_TO_KHZ(bw) / 1000);
+ }
+
+ dprintk("timf: %d\n", timf);
- timf_msb = (comp >> 16) & 0xff;
- timf_lsb = comp & 0xffff;
+ dib3000mc_write_word(state, 23, timf >> 16);
+ dib3000mc_write_word(state, 24, timf & 0xffff);
- wr(DIB3000MC_REG_TIMING_FREQ_MSB,timf_msb);
- wr(DIB3000MC_REG_TIMING_FREQ_LSB,timf_lsb);
return 0;
}
-static int dib3000mc_init_auto_scan(struct dib3000_state *state, fe_bandwidth_t bw, int boost)
+static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state)
{
- if (boost) {
- wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_ON);
+ u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb;
+ if (state->cfg->pwm3_inversion) {
+ reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
+ reg_52 |= (1 << 2);
} else {
- wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_OFF);
- }
- switch (bw) {
- case BANDWIDTH_8_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
- break;
- case BANDWIDTH_7_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_7mhz);
- break;
- case BANDWIDTH_6_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_6mhz);
- break;
-/* case BANDWIDTH_5_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_5mhz);
- break;*/
- case BANDWIDTH_AUTO:
- return -EOPNOTSUPP;
- default:
- err("unknown bandwidth value (%d).",bw);
- return -EINVAL;
- }
- if (boost) {
- u32 timeout = (rd(DIB3000MC_REG_BW_TIMOUT_MSB) << 16) +
- rd(DIB3000MC_REG_BW_TIMOUT_LSB);
- timeout *= 85; timeout >>= 7;
- wr(DIB3000MC_REG_BW_TIMOUT_MSB,(timeout >> 16) & 0xffff);
- wr(DIB3000MC_REG_BW_TIMOUT_LSB,timeout & 0xffff);
+ reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0);
+ reg_52 |= (1 << 8);
}
+ dib3000mc_write_word(state, 51, reg_51);
+ dib3000mc_write_word(state, 52, reg_52);
+
+ if (state->cfg->use_pwm3)
+ dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
+ else
+ dib3000mc_write_word(state, 245, 0);
+
+ dib3000mc_write_word(state, 1040, 0x3);
return 0;
}
-static int dib3000mc_set_adp_cfg(struct dib3000_state *state, fe_modulation_t con)
+static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode)
{
- switch (con) {
- case QAM_64:
- wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[2]);
- break;
- case QAM_16:
- wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[1]);
- break;
- case QPSK:
- wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[0]);
- break;
- case QAM_AUTO:
+ int ret = 0;
+ u16 fifo_threshold = 1792;
+ u16 outreg = 0;
+ u16 outmode = 0;
+ u16 elecout = 1;
+ u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */
+
+ dprintk("-I- Setting output mode for demod %p to %d\n",
+ &state->demod, mode);
+
+ switch (mode) {
+ case OUTMODE_HIGH_Z: // disable
+ elecout = 0;
+ break;
+ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
+ outmode = 0;
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
+ outmode = 1;
+ break;
+ case OUTMODE_MPEG2_SERIAL: // STBs with serial input
+ outmode = 2;
+ break;
+ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
+ elecout = 3;
+ /*ADDR @ 206 :
+ P_smo_error_discard [1;6:6] = 0
+ P_smo_rs_discard [1;5:5] = 0
+ P_smo_pid_parse [1;4:4] = 0
+ P_smo_fifo_flush [1;3:3] = 0
+ P_smo_mode [2;2:1] = 11
+ P_smo_ovf_prot [1;0:0] = 0
+ */
+ smo_reg |= 3 << 1;
+ fifo_threshold = 512;
+ outmode = 5;
+ break;
+ case OUTMODE_DIVERSITY:
+ outmode = 4;
+ elecout = 1;
break;
default:
- warn("unkown constellation.");
+ dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+ outmode = 0;
break;
}
- return 0;
+
+ if ((state->cfg->output_mpeg2_in_188_bytes))
+ smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1
+
+ outreg = dib3000mc_read_word(state, 244) & 0x07FF;
+ outreg |= (outmode << 11);
+ ret |= dib3000mc_write_word(state, 244, outreg);
+ ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/
+ ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */
+ ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */
+ return ret;
}
-static int dib3000mc_set_general_cfg(struct dib3000_state *state, struct dvb_frontend_parameters *fep, int *auto_val)
+static int dib3000mc_set_bandwidth(struct dvb_frontend *demod, u8 bw)
{
- struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
- fe_code_rate_t fe_cr = FEC_NONE;
- u8 fft=0, guard=0, qam=0, alpha=0, sel_hp=0, cr=0, hrch=0;
- int seq;
-
- switch (ofdm->transmission_mode) {
- case TRANSMISSION_MODE_2K: fft = DIB3000_TRANSMISSION_MODE_2K; break;
- case TRANSMISSION_MODE_8K: fft = DIB3000_TRANSMISSION_MODE_8K; break;
- case TRANSMISSION_MODE_AUTO: break;
- default: return -EINVAL;
- }
- switch (ofdm->guard_interval) {
- case GUARD_INTERVAL_1_32: guard = DIB3000_GUARD_TIME_1_32; break;
- case GUARD_INTERVAL_1_16: guard = DIB3000_GUARD_TIME_1_16; break;
- case GUARD_INTERVAL_1_8: guard = DIB3000_GUARD_TIME_1_8; break;
- case GUARD_INTERVAL_1_4: guard = DIB3000_GUARD_TIME_1_4; break;
- case GUARD_INTERVAL_AUTO: break;
- default: return -EINVAL;
- }
- switch (ofdm->constellation) {
- case QPSK: qam = DIB3000_CONSTELLATION_QPSK; break;
- case QAM_16: qam = DIB3000_CONSTELLATION_16QAM; break;
- case QAM_64: qam = DIB3000_CONSTELLATION_64QAM; break;
- case QAM_AUTO: break;
- default: return -EINVAL;
- }
- switch (ofdm->hierarchy_information) {
- case HIERARCHY_NONE: /* fall through */
- case HIERARCHY_1: alpha = DIB3000_ALPHA_1; break;
- case HIERARCHY_2: alpha = DIB3000_ALPHA_2; break;
- case HIERARCHY_4: alpha = DIB3000_ALPHA_4; break;
- case HIERARCHY_AUTO: break;
- default: return -EINVAL;
- }
- if (ofdm->hierarchy_information == HIERARCHY_NONE) {
- hrch = DIB3000_HRCH_OFF;
- sel_hp = DIB3000_SELECT_HP;
- fe_cr = ofdm->code_rate_HP;
- } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
- hrch = DIB3000_HRCH_ON;
- sel_hp = DIB3000_SELECT_LP;
- fe_cr = ofdm->code_rate_LP;
- }
- switch (fe_cr) {
- case FEC_1_2: cr = DIB3000_FEC_1_2; break;
- case FEC_2_3: cr = DIB3000_FEC_2_3; break;
- case FEC_3_4: cr = DIB3000_FEC_3_4; break;
- case FEC_5_6: cr = DIB3000_FEC_5_6; break;
- case FEC_7_8: cr = DIB3000_FEC_7_8; break;
- case FEC_NONE: break;
- case FEC_AUTO: break;
- default: return -EINVAL;
- }
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ u16 bw_cfg[6] = { 0 };
+ u16 imp_bw_cfg[3] = { 0 };
+ u16 reg;
- wr(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_PARM(alpha,qam,guard,fft));
- wr(DIB3000MC_REG_HRCH_PARM,DIB3000MC_HRCH_PARM(sel_hp,cr,hrch));
+/* settings here are for 27.7MHz */
+ switch (bw) {
+ case BANDWIDTH_8_MHZ:
+ bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20;
+ imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7;
+ break;
+
+ case BANDWIDTH_7_MHZ:
+ bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7;
+ imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0;
+ break;
- switch (fep->inversion) {
- case INVERSION_OFF:
- wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+ case BANDWIDTH_6_MHZ:
+ bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5;
+ imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089;
break;
- case INVERSION_AUTO: /* fall through */
- case INVERSION_ON:
- wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_ON);
+
+ case 255 /* BANDWIDTH_5_MHZ */:
+ bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500;
+ imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072;
break;
- default:
- return -EINVAL;
+
+ default: return -EINVAL;
}
- seq = dib3000_seq
- [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO]
- [ofdm->guard_interval == GUARD_INTERVAL_AUTO]
- [fep->inversion == INVERSION_AUTO];
-
- deb_setf("seq? %d\n", seq);
- wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS(seq,1));
- *auto_val = ofdm->constellation == QAM_AUTO ||
- ofdm->hierarchy_information == HIERARCHY_AUTO ||
- ofdm->guard_interval == GUARD_INTERVAL_AUTO ||
- ofdm->transmission_mode == TRANSMISSION_MODE_AUTO ||
- fe_cr == FEC_AUTO ||
- fep->inversion == INVERSION_AUTO;
+ for (reg = 6; reg < 12; reg++)
+ dib3000mc_write_word(state, reg, bw_cfg[reg - 6]);
+ dib3000mc_write_word(state, 12, 0x0000);
+ dib3000mc_write_word(state, 13, 0x03e8);
+ dib3000mc_write_word(state, 14, 0x0000);
+ dib3000mc_write_word(state, 15, 0x03f2);
+ dib3000mc_write_word(state, 16, 0x0001);
+ dib3000mc_write_word(state, 17, 0xb0d0);
+ // P_sec_len
+ dib3000mc_write_word(state, 18, 0x0393);
+ dib3000mc_write_word(state, 19, 0x8700);
+
+ for (reg = 55; reg < 58; reg++)
+ dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]);
+
+ // Timing configuration
+ dib3000mc_set_timing(state, 0, bw, 0);
+
return 0;
}
-static int dib3000mc_get_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep)
+static u16 impulse_noise_val[29] =
+
{
- struct dib3000_state* state = fe->demodulator_priv;
- struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
- fe_code_rate_t *cr;
- u16 tps_val,cr_val;
- int inv_test1,inv_test2;
- u32 dds_val, threshold = 0x1000000;
-
- if (!(rd(DIB3000MC_REG_LOCK_507) & DIB3000MC_LOCK_507))
- return 0;
-
- dds_val = (rd(DIB3000MC_REG_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_DDS_FREQ_LSB);
- deb_getf("DDS_FREQ: %6x\n",dds_val);
- if (dds_val < threshold)
- inv_test1 = 0;
- else if (dds_val == threshold)
- inv_test1 = 1;
- else
- inv_test1 = 2;
-
- dds_val = (rd(DIB3000MC_REG_SET_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_SET_DDS_FREQ_LSB);
- deb_getf("DDS_SET_FREQ: %6x\n",dds_val);
- if (dds_val < threshold)
- inv_test2 = 0;
- else if (dds_val == threshold)
- inv_test2 = 1;
+ 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3,
+ 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2,
+ 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd
+};
+
+static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft)
+{
+ u16 i;
+ for (i = 58; i < 87; i++)
+ dib3000mc_write_word(state, i, impulse_noise_val[i-58]);
+
+ if (nfft == 1) {
+ dib3000mc_write_word(state, 58, 0x3b);
+ dib3000mc_write_word(state, 84, 0x00);
+ dib3000mc_write_word(state, 85, 0x8200);
+ }
+
+ dib3000mc_write_word(state, 34, 0x1294);
+ dib3000mc_write_word(state, 35, 0x1ff8);
+ if (mode == 1)
+ dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10));
+}
+
+static int dib3000mc_init(struct dvb_frontend *demod)
+{
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ struct dibx000_agc_config *agc = state->cfg->agc;
+
+ // Restart Configuration
+ dib3000mc_write_word(state, 1027, 0x8000);
+ dib3000mc_write_word(state, 1027, 0x0000);
+
+ // power up the demod + mobility configuration
+ dib3000mc_write_word(state, 140, 0x0000);
+ dib3000mc_write_word(state, 1031, 0);
+
+ if (state->cfg->mobile_mode) {
+ dib3000mc_write_word(state, 139, 0x0000);
+ dib3000mc_write_word(state, 141, 0x0000);
+ dib3000mc_write_word(state, 175, 0x0002);
+ dib3000mc_write_word(state, 1032, 0x0000);
+ } else {
+ dib3000mc_write_word(state, 139, 0x0001);
+ dib3000mc_write_word(state, 141, 0x0000);
+ dib3000mc_write_word(state, 175, 0x0000);
+ dib3000mc_write_word(state, 1032, 0x012C);
+ }
+ dib3000mc_write_word(state, 1033, 0x0000);
+
+ // P_clk_cfg
+ dib3000mc_write_word(state, 1037, 0x3130);
+
+ // other configurations
+
+ // P_ctrl_sfreq
+ dib3000mc_write_word(state, 33, (5 << 0));
+ dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0));
+
+ // Phase noise control
+ // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange
+ dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0));
+
+ if (state->cfg->phase_noise_mode == 0)
+ dib3000mc_write_word(state, 111, 0x00);
else
- inv_test2 = 2;
+ dib3000mc_write_word(state, 111, 0x02);
+
+ // P_agc_global
+ dib3000mc_write_word(state, 50, 0x8000);
+
+ // agc setup misc
+ dib3000mc_setup_pwm_state(state);
+
+ // P_agc_counter_lock
+ dib3000mc_write_word(state, 53, 0x87);
+ // P_agc_counter_unlock
+ dib3000mc_write_word(state, 54, 0x87);
+
+ /* agc */
+ dib3000mc_write_word(state, 36, state->cfg->max_time);
+ dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0));
+ dib3000mc_write_word(state, 38, state->cfg->pwm3_value);
+ dib3000mc_write_word(state, 39, state->cfg->ln_adc_level);
+
+ // set_agc_loop_Bw
+ dib3000mc_write_word(state, 40, 0x0179);
+ dib3000mc_write_word(state, 41, 0x03f0);
+
+ dib3000mc_write_word(state, 42, agc->agc1_max);
+ dib3000mc_write_word(state, 43, agc->agc1_min);
+ dib3000mc_write_word(state, 44, agc->agc2_max);
+ dib3000mc_write_word(state, 45, agc->agc2_min);
+ dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
+ dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+ dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+// Begin: TimeOut registers
+ // P_pha3_thres
+ dib3000mc_write_word(state, 110, 3277);
+ // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80
+ dib3000mc_write_word(state, 26, 0x6680);
+ // lock_mask0
+ dib3000mc_write_word(state, 1, 4);
+ // lock_mask1
+ dib3000mc_write_word(state, 2, 4);
+ // lock_mask2
+ dib3000mc_write_word(state, 3, 0x1000);
+ // P_search_maxtrial=1
+ dib3000mc_write_word(state, 5, 1);
+
+ dib3000mc_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+
+ // div_lock_mask
+ dib3000mc_write_word(state, 4, 0x814);
+
+ dib3000mc_write_word(state, 21, (1 << 9) | 0x164);
+ dib3000mc_write_word(state, 22, 0x463d);
+
+ // Spurious rm cfg
+ // P_cspu_regul, P_cspu_win_cut
+ dib3000mc_write_word(state, 120, 0x200f);
+ // P_adp_selec_monit
+ dib3000mc_write_word(state, 134, 0);
+
+ // Fec cfg
+ dib3000mc_write_word(state, 195, 0x10);
+
+ // diversity register: P_dvsy_sync_wait..
+ dib3000mc_write_word(state, 180, 0x2FF0);
+
+ // Impulse noise configuration
+ dib3000mc_set_impulse_noise(state, 0, 1);
+
+ // output mode set-up
+ dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
+
+ /* close the i2c-gate */
+ dib3000mc_write_word(state, 769, (1 << 7) );
- fep->inversion =
- ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
- ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
- INVERSION_ON : INVERSION_OFF;
+ return 0;
+}
- deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+static int dib3000mc_sleep(struct dvb_frontend *demod)
+{
+ struct dib3000mc_state *state = demod->demodulator_priv;
- fep->frequency = state->last_tuned_freq;
- fep->u.ofdm.bandwidth= state->last_tuned_bw;
+ dib3000mc_write_word(state, 1031, 0xFFFF);
+ dib3000mc_write_word(state, 1032, 0xFFFF);
+ dib3000mc_write_word(state, 1033, 0xFFF0);
- tps_val = rd(DIB3000MC_REG_TUNING_PARM);
+ return 0;
+}
- switch (DIB3000MC_TP_QAM(tps_val)) {
- case DIB3000_CONSTELLATION_QPSK:
- deb_getf("QPSK ");
- ofdm->constellation = QPSK;
- break;
- case DIB3000_CONSTELLATION_16QAM:
- deb_getf("QAM16 ");
- ofdm->constellation = QAM_16;
+static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam)
+{
+ u16 cfg[4] = { 0 },reg;
+ switch (qam) {
+ case 0:
+ cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0;
break;
- case DIB3000_CONSTELLATION_64QAM:
- deb_getf("QAM64 ");
- ofdm->constellation = QAM_64;
+ case 1:
+ cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0;
break;
- default:
- err("Unexpected constellation returned by TPS (%d)", tps_val);
+ case 2:
+ cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8;
break;
}
+ for (reg = 129; reg < 133; reg++)
+ dib3000mc_write_word(state, reg, cfg[reg - 129]);
+}
- if (DIB3000MC_TP_HRCH(tps_val)) {
- deb_getf("HRCH ON ");
- cr = &ofdm->code_rate_LP;
- ofdm->code_rate_HP = FEC_NONE;
- switch (DIB3000MC_TP_ALPHA(tps_val)) {
- case DIB3000_ALPHA_0:
- deb_getf("HIERARCHY_NONE ");
- ofdm->hierarchy_information = HIERARCHY_NONE;
- break;
- case DIB3000_ALPHA_1:
- deb_getf("HIERARCHY_1 ");
- ofdm->hierarchy_information = HIERARCHY_1;
- break;
- case DIB3000_ALPHA_2:
- deb_getf("HIERARCHY_2 ");
- ofdm->hierarchy_information = HIERARCHY_2;
- break;
- case DIB3000_ALPHA_4:
- deb_getf("HIERARCHY_4 ");
- ofdm->hierarchy_information = HIERARCHY_4;
- break;
- default:
- err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
- break;
- }
- cr_val = DIB3000MC_TP_FEC_CR_LP(tps_val);
- } else {
- deb_getf("HRCH OFF ");
- cr = &ofdm->code_rate_HP;
- ofdm->code_rate_LP = FEC_NONE;
- ofdm->hierarchy_information = HIERARCHY_NONE;
- cr_val = DIB3000MC_TP_FEC_CR_HP(tps_val);
- }
+static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, struct dibx000_ofdm_channel *chan, u16 seq)
+{
+ u16 tmp;
- switch (cr_val) {
- case DIB3000_FEC_1_2:
- deb_getf("FEC_1_2 ");
- *cr = FEC_1_2;
- break;
- case DIB3000_FEC_2_3:
- deb_getf("FEC_2_3 ");
- *cr = FEC_2_3;
- break;
- case DIB3000_FEC_3_4:
- deb_getf("FEC_3_4 ");
- *cr = FEC_3_4;
- break;
- case DIB3000_FEC_5_6:
- deb_getf("FEC_5_6 ");
- *cr = FEC_4_5;
- break;
- case DIB3000_FEC_7_8:
- deb_getf("FEC_7_8 ");
- *cr = FEC_7_8;
- break;
- default:
- err("Unexpected FEC returned by TPS (%d)", tps_val);
- break;
- }
+ dib3000mc_set_timing(state, chan->nfft, chan->Bw, 0);
- switch (DIB3000MC_TP_GUARD(tps_val)) {
- case DIB3000_GUARD_TIME_1_32:
- deb_getf("GUARD_INTERVAL_1_32 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case DIB3000_GUARD_TIME_1_16:
- deb_getf("GUARD_INTERVAL_1_16 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case DIB3000_GUARD_TIME_1_8:
- deb_getf("GUARD_INTERVAL_1_8 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case DIB3000_GUARD_TIME_1_4:
- deb_getf("GUARD_INTERVAL_1_4 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_4;
- break;
- default:
- err("Unexpected Guard Time returned by TPS (%d)", tps_val);
- break;
- }
+// if (boost)
+// dib3000mc_write_word(state, 100, (11 << 6) + 6);
+// else
+ dib3000mc_write_word(state, 100, (16 << 6) + 9);
- switch (DIB3000MC_TP_FFT(tps_val)) {
- case DIB3000_TRANSMISSION_MODE_2K:
- deb_getf("TRANSMISSION_MODE_2K ");
- ofdm->transmission_mode = TRANSMISSION_MODE_2K;
- break;
- case DIB3000_TRANSMISSION_MODE_8K:
- deb_getf("TRANSMISSION_MODE_8K ");
- ofdm->transmission_mode = TRANSMISSION_MODE_8K;
- break;
- default:
- err("unexpected transmission mode return by TPS (%d)", tps_val);
- break;
- }
- deb_getf("\n");
+ dib3000mc_write_word(state, 1027, 0x0800);
+ dib3000mc_write_word(state, 1027, 0x0000);
- return 0;
+ //Default cfg isi offset adp
+ dib3000mc_write_word(state, 26, 0x6680);
+ dib3000mc_write_word(state, 29, 0x1273);
+ dib3000mc_write_word(state, 33, 5);
+ dib3000mc_set_adp_cfg(state, 1);
+ dib3000mc_write_word(state, 133, 15564);
+
+ dib3000mc_write_word(state, 12 , 0x0);
+ dib3000mc_write_word(state, 13 , 0x3e8);
+ dib3000mc_write_word(state, 14 , 0x0);
+ dib3000mc_write_word(state, 15 , 0x3f2);
+
+ dib3000mc_write_word(state, 93,0);
+ dib3000mc_write_word(state, 94,0);
+ dib3000mc_write_word(state, 95,0);
+ dib3000mc_write_word(state, 96,0);
+ dib3000mc_write_word(state, 97,0);
+ dib3000mc_write_word(state, 98,0);
+
+ dib3000mc_set_impulse_noise(state, 0, chan->nfft);
+
+ tmp = ((chan->nfft & 0x1) << 7) | (chan->guard << 5) | (chan->nqam << 3) | chan->vit_alpha;
+ dib3000mc_write_word(state, 0, tmp);
+
+ dib3000mc_write_word(state, 5, seq);
+
+ tmp = (chan->vit_hrch << 4) | (chan->vit_select_hp);
+ if (!chan->vit_hrch || (chan->vit_hrch && chan->vit_select_hp))
+ tmp |= chan->vit_code_rate_hp << 1;
+ else
+ tmp |= chan->vit_code_rate_lp << 1;
+ dib3000mc_write_word(state, 181, tmp);
+
+ // diversity synchro delay
+ tmp = dib3000mc_read_word(state, 180) & 0x000f;
+ tmp |= ((chan->nfft == 0) ? 64 : 256) * ((1 << (chan->guard)) * 3 / 2) << 4; // add 50% SFN margin
+ dib3000mc_write_word(state, 180, tmp);
+
+ // restart demod
+ tmp = dib3000mc_read_word(state, 0);
+ dib3000mc_write_word(state, 0, tmp | (1 << 9));
+ dib3000mc_write_word(state, 0, tmp);
+
+ msleep(30);
+
+ dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, chan->nfft);
}
-static int dib3000mc_set_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep, int tuner)
+static int dib3000mc_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *chan)
{
- struct dib3000_state* state = fe->demodulator_priv;
- struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
- int search_state,auto_val;
- u16 val;
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ u16 reg;
+// u32 val;
+ struct dibx000_ofdm_channel fchan;
- if (tuner && fe->ops.tuner_ops.set_params) { /* initial call from dvb */
- fe->ops.tuner_ops.set_params(fe, fep);
- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
-
- state->last_tuned_freq = fep->frequency;
- // if (!scanboost) {
- dib3000mc_set_timing(state,0,ofdm->transmission_mode,ofdm->bandwidth);
- dib3000mc_init_auto_scan(state, ofdm->bandwidth, 0);
- state->last_tuned_bw = ofdm->bandwidth;
-
- wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth);
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_AGC);
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
-
- /* Default cfg isi offset adp */
- wr_foreach(dib3000mc_reg_offset,dib3000mc_offset[0]);
-
- wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT | DIB3000MC_ISI_INHIBIT);
- dib3000mc_set_adp_cfg(state,ofdm->constellation);
- wr(DIB3000MC_REG_UNK_133,DIB3000MC_UNK_133);
-
- wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
- /* power smoothing */
- if (ofdm->bandwidth != BANDWIDTH_8_MHZ) {
- wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[0]);
- } else {
- wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[3]);
- }
- auto_val = 0;
- dib3000mc_set_general_cfg(state,fep,&auto_val);
- dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth);
-
- val = rd(DIB3000MC_REG_DEMOD_PARM);
- wr(DIB3000MC_REG_DEMOD_PARM,val|DIB3000MC_DEMOD_RST_DEMOD_ON);
- wr(DIB3000MC_REG_DEMOD_PARM,val);
- // }
- msleep(70);
-
- /* something has to be auto searched */
- if (auto_val) {
- int as_count=0;
-
- deb_setf("autosearch enabled.\n");
-
- val = rd(DIB3000MC_REG_DEMOD_PARM);
- wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
- wr(DIB3000MC_REG_DEMOD_PARM,val);
-
- while ((search_state = dib3000_search_status(
- rd(DIB3000MC_REG_AS_IRQ),1)) < 0 && as_count++ < 100)
- msleep(10);
-
- deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count);
-
- if (search_state == 1) {
- struct dvb_frontend_parameters feps;
- if (dib3000mc_get_frontend(fe, &feps) == 0) {
- deb_setf("reading tuning data from frontend succeeded.\n");
- return dib3000mc_set_frontend(fe, &feps, 0);
- }
- }
- } else {
- dib3000mc_set_impulse_noise(state,0,ofdm->transmission_mode,ofdm->bandwidth);
- wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE);
- dib3000mc_set_adp_cfg(state,ofdm->constellation);
-
- /* set_offset_cfg */
- wr_foreach(dib3000mc_reg_offset,
- dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]);
- }
- } else { /* second call, after autosearch (fka: set_WithKnownParams) */
-// dib3000mc_set_timing(state,1,ofdm->transmission_mode,ofdm->bandwidth);
+ INIT_OFDM_CHANNEL(&fchan);
+ fchan = *chan;
- auto_val = 0;
- dib3000mc_set_general_cfg(state,fep,&auto_val);
- if (auto_val)
- deb_info("auto_val is true, even though an auto search was already performed.\n");
- dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth);
+ /* a channel for autosearch */
+ fchan.nfft = 1; fchan.guard = 0; fchan.nqam = 2;
+ fchan.vit_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2;
+ fchan.vit_hrch = 0; fchan.vit_select_hp = 1;
- val = rd(DIB3000MC_REG_DEMOD_PARM);
- wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
- wr(DIB3000MC_REG_DEMOD_PARM,val);
+ dib3000mc_set_channel_cfg(state, &fchan, 7);
- msleep(30);
+ reg = dib3000mc_read_word(state, 0);
+ dib3000mc_write_word(state, 0, reg | (1 << 8));
+ dib3000mc_read_word(state, 511);
+ dib3000mc_write_word(state, 0, reg);
- wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE);
- dib3000mc_set_adp_cfg(state,ofdm->constellation);
- wr_foreach(dib3000mc_reg_offset,
- dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]);
- }
return 0;
}
-static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode)
+static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod)
{
- struct dib3000_state *state = fe->demodulator_priv;
- deb_info("init start\n");
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ u16 irq_pending = dib3000mc_read_word(state, 511);
- state->timing_offset = 0;
- state->timing_offset_comp_done = 0;
+ if (irq_pending & 0x1) // failed
+ return 1;
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_CONFIG);
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
- wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_UP);
- wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_PUP_MOBILE);
- wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_UP);
- wr(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_INIT);
+ if (irq_pending & 0x2) // succeeded
+ return 2;
- wr(DIB3000MC_REG_RST_UNC,DIB3000MC_RST_UNC_OFF);
- wr(DIB3000MC_REG_UNK_19,DIB3000MC_UNK_19);
+ return 0; // still pending
+}
- wr(33,5);
- wr(36,81);
- wr(DIB3000MC_REG_UNK_88,DIB3000MC_UNK_88);
+static int dib3000mc_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib3000mc_state *state = demod->demodulator_priv;
- wr(DIB3000MC_REG_UNK_99,DIB3000MC_UNK_99);
- wr(DIB3000MC_REG_UNK_111,DIB3000MC_UNK_111_PH_N_MODE_0); /* phase noise algo off */
+ // ** configure demod **
+ dib3000mc_set_channel_cfg(state, ch, 0);
- /* mobile mode - portable reception */
- wr_foreach(dib3000mc_reg_mobile_mode,dib3000mc_mobile_mode[1]);
+ // activates isi
+ dib3000mc_write_word(state, 29, 0x1073);
-/* TUNER_PANASONIC_ENV57H12D5: */
- wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth);
- wr_foreach(dib3000mc_reg_agc_bandwidth_general,dib3000mc_agc_bandwidth_general);
- wr_foreach(dib3000mc_reg_agc,dib3000mc_agc_tuner[1]);
+ dib3000mc_set_adp_cfg(state, (u8)ch->nqam);
- wr(DIB3000MC_REG_UNK_110,DIB3000MC_UNK_110);
- wr(26,0x6680);
- wr(DIB3000MC_REG_UNK_1,DIB3000MC_UNK_1);
- wr(DIB3000MC_REG_UNK_2,DIB3000MC_UNK_2);
- wr(DIB3000MC_REG_UNK_3,DIB3000MC_UNK_3);
- wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS_DEFAULT);
+ if (ch->nfft == 1) {
+ dib3000mc_write_word(state, 26, 38528);
+ dib3000mc_write_word(state, 33, 8);
+ } else {
+ dib3000mc_write_word(state, 26, 30336);
+ dib3000mc_write_word(state, 33, 6);
+ }
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
- wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
+ if (dib3000mc_read_word(state, 509) & 0x80)
+ dib3000mc_set_timing(state, ch->nfft, ch->Bw, 1);
- wr(DIB3000MC_REG_UNK_4,DIB3000MC_UNK_4);
+ return 0;
+}
- wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
- wr(DIB3000MC_REG_SET_DDS_FREQ_LSB,DIB3000MC_DDS_FREQ_LSB);
+struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating)
+{
+ struct dib3000mc_state *st = demod->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating);
+}
- dib3000mc_set_timing(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
-// wr_foreach(dib3000mc_reg_timing_freq,dib3000mc_timing_freq[3]);
+EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
- wr(DIB3000MC_REG_UNK_120,DIB3000MC_UNK_120);
- wr(DIB3000MC_REG_UNK_134,DIB3000MC_UNK_134);
- wr(DIB3000MC_REG_FEC_CFG,DIB3000MC_FEC_CFG);
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 tps = dib3000mc_read_word(state,458);
- wr(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+ fep->inversion = INVERSION_AUTO;
- dib3000mc_set_impulse_noise(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
+ fep->u.ofdm.bandwidth = state->current_bandwidth;
-/* output mode control, just the MPEG2_SLAVE */
-// set_or(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE);
- wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE);
- wr(DIB3000MC_REG_SMO_MODE,DIB3000MC_SMO_MODE_SLAVE);
- wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_SLAVE);
- wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_SLAVE);
+ switch ((tps >> 8) & 0x1) {
+ case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+ case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+ }
-/* MPEG2_PARALLEL_CONTINUOUS_CLOCK
- wr(DIB3000MC_REG_OUTMODE,
- DIB3000MC_SET_OUTMODE(DIB3000MC_OM_PAR_CONT_CLK,
- rd(DIB3000MC_REG_OUTMODE)));
+ switch (tps & 0x3) {
+ case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+ case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+ case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+ case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+ }
- wr(DIB3000MC_REG_SMO_MODE,
- DIB3000MC_SMO_MODE_DEFAULT |
- DIB3000MC_SMO_MODE_188);
+ switch ((tps >> 13) & 0x3) {
+ case 0: fep->u.ofdm.constellation = QPSK; break;
+ case 1: fep->u.ofdm.constellation = QAM_16; break;
+ case 2:
+ default: fep->u.ofdm.constellation = QAM_64; break;
+ }
- wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_DEFAULT);
- wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
-*/
+ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+ /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */
-/* diversity */
- wr(DIB3000MC_REG_DIVERSITY1,DIB3000MC_DIVERSITY1_DEFAULT);
- wr(DIB3000MC_REG_DIVERSITY2,DIB3000MC_DIVERSITY2_DEFAULT);
+ fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ switch ((tps >> 5) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
- set_and(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+ }
- set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF);
+ switch ((tps >> 2) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+ }
- deb_info("init end\n");
return 0;
}
-static int dib3000mc_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+
+static int dib3000mc_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
{
- struct dib3000_state* state = fe->demodulator_priv;
- u16 lock = rd(DIB3000MC_REG_LOCKING);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ struct dibx000_ofdm_channel ch;
+
+ INIT_OFDM_CHANNEL(&ch);
+ FEP2DIB(fep,&ch);
+
+ state->current_bandwidth = fep->u.ofdm.bandwidth;
+ dib3000mc_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, fep);
+ msleep(100);
+ }
+
+ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
+ fep->u.ofdm.constellation == QAM_AUTO ||
+ fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ int i = 100, found;
+
+ dib3000mc_autosearch_start(fe, &ch);
+ do {
+ msleep(1);
+ found = dib3000mc_autosearch_is_irq(fe);
+ } while (found == 0 && i--);
+
+ dprintk("autosearch returns: %d\n",found);
+ if (found == 0 || found == 1)
+ return 0; // no channel found
+
+ dib3000mc_get_frontend(fe, fep);
+ FEP2DIB(fep,&ch);
+ }
+
+ /* make this a config parameter */
+ dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+ return dib3000mc_tune(fe, &ch);
+}
+
+static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 lock = dib3000mc_read_word(state, 509);
*stat = 0;
- if (DIB3000MC_AGC_LOCK(lock))
+
+ if (lock & 0x8000)
*stat |= FE_HAS_SIGNAL;
- if (DIB3000MC_CARRIER_LOCK(lock))
+ if (lock & 0x3000)
*stat |= FE_HAS_CARRIER;
- if (DIB3000MC_TPS_LOCK(lock))
+ if (lock & 0x0100)
*stat |= FE_HAS_VITERBI;
- if (DIB3000MC_MPEG_SYNC_LOCK(lock))
- *stat |= (FE_HAS_SYNC | FE_HAS_LOCK);
-
- deb_stat("actual status is %2x fifo_level: %x,244: %x, 206: %x, 207: %x, 1040: %x\n",*stat,rd(510),rd(244),rd(206),rd(207),rd(1040));
+ if (lock & 0x0010)
+ *stat |= FE_HAS_SYNC;
+ if (lock & 0x0008)
+ *stat |= FE_HAS_LOCK;
return 0;
}
-static int dib3000mc_read_ber(struct dvb_frontend* fe, u32 *ber)
+static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber)
{
- struct dib3000_state* state = fe->demodulator_priv;
- *ber = ((rd(DIB3000MC_REG_BER_MSB) << 16) | rd(DIB3000MC_REG_BER_LSB));
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501);
return 0;
}
-static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
{
- struct dib3000_state* state = fe->demodulator_priv;
-
- *unc = rd(DIB3000MC_REG_PACKET_ERRORS);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ *unc = dib3000mc_read_word(state, 508);
return 0;
}
-/* see dib3000mb.c for calculation comments */
-static int dib3000mc_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
- struct dib3000_state* state = fe->demodulator_priv;
- u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB);
- *strength = (((val >> 6) & 0xff) << 8) + (val & 0x3f);
-
- deb_stat("signal: mantisse = %d, exponent = %d\n",(*strength >> 8) & 0xff, *strength & 0xff);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 val = dib3000mc_read_word(state, 392);
+ *strength = 65535 - val;
return 0;
}
-/* see dib3000mb.c for calculation comments */
static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
{
- struct dib3000_state* state = fe->demodulator_priv;
- u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB),
- val2 = rd(DIB3000MC_REG_SIGNAL_NOISE_MSB);
- u16 sig,noise;
-
- sig = (((val >> 6) & 0xff) << 8) + (val & 0x3f);
- noise = (((val >> 4) & 0xff) << 8) + ((val & 0xf) << 2) + ((val2 >> 14) & 0x3);
- if (noise == 0)
- *snr = 0xffff;
- else
- *snr = (u16) sig/noise;
-
- deb_stat("signal: mantisse = %d, exponent = %d\n",(sig >> 8) & 0xff, sig & 0xff);
- deb_stat("noise: mantisse = %d, exponent = %d\n",(noise >> 8) & 0xff, noise & 0xff);
- deb_stat("snr: %d\n",*snr);
- return 0;
-}
-
-static int dib3000mc_sleep(struct dvb_frontend* fe)
-{
- struct dib3000_state* state = fe->demodulator_priv;
-
- set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_PWR_DOWN);
- wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_DOWN);
- wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_POWER_DOWN);
- wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_DOWN);
+ *snr = 0x0000;
return 0;
}
@@ -729,185 +726,145 @@ static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_fr
return 0;
}
-static int dib3000mc_fe_init_nonmobile(struct dvb_frontend* fe)
+static void dib3000mc_release(struct dvb_frontend *fe)
{
- return dib3000mc_fe_init(fe, 0);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ dibx000_exit_i2c_master(&state->i2c_master);
+ kfree(state);
}
-static int dib3000mc_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff)
{
- return dib3000mc_set_frontend(fe, fep, 1);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0);
+ return 0;
}
+EXPORT_SYMBOL(dib3000mc_pid_control);
-static void dib3000mc_release(struct dvb_frontend* fe)
+int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
{
- struct dib3000_state *state = fe->demodulator_priv;
- kfree(state);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4);
+ tmp |= (onoff << 4);
+ return dib3000mc_write_word(state, 206, tmp);
}
+EXPORT_SYMBOL(dib3000mc_pid_parse);
-/* pid filter and transfer stuff */
-static int dib3000mc_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg)
{
- struct dib3000_state *state = fe->demodulator_priv;
- pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
- wr(index+DIB3000MC_REG_FIRST_PID,pid);
- return 0;
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ state->cfg = cfg;
}
+EXPORT_SYMBOL(dib3000mc_set_config);
-static int dib3000mc_fifo_control(struct dvb_frontend *fe, int onoff)
+int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[])
{
- struct dib3000_state *state = fe->demodulator_priv;
- u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
+ struct dib3000mc_state st = { .i2c_adap = i2c };
+ int k;
+ u8 new_addr;
+
+ static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26};
+
+ for (k = no_of_demods-1; k >= 0; k--) {
+ st.cfg = &cfg[k];
+
+ /* designated i2c address */
+ new_addr = DIB3000MC_I2C_ADDRESS[k];
+ st.i2c_addr = new_addr;
+ if (dib3000mc_identify(&st) != 0) {
+ st.i2c_addr = default_addr;
+ if (dib3000mc_identify(&st) != 0) {
+ dprintk("-E- DiB3000P/MC #%d: not identified\n", k);
+ return -ENODEV;
+ }
+ }
- deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling");
+ dib3000mc_set_output_mode(&st, OUTMODE_MPEG2_PAR_CONT_CLK);
- if (onoff) {
- deb_xfer("%d %x\n",tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH);
- wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH);
- } else {
- deb_xfer("%d %x\n",tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH);
- wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH);
+ // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0)
+ dib3000mc_write_word(&st, 1024, (new_addr << 3) | 0x1);
+ st.i2c_addr = new_addr;
}
- return 0;
-}
-static int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
-{
- struct dib3000_state *state = fe->demodulator_priv;
- u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
-
- deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling");
+ for (k = 0; k < no_of_demods; k++) {
+ st.cfg = &cfg[k];
+ st.i2c_addr = DIB3000MC_I2C_ADDRESS[k];
- if (onoff) {
- wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_PID_PARSE);
- } else {
- wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_NO_PID_PARSE);
- }
- return 0;
-}
+ dib3000mc_write_word(&st, 1024, st.i2c_addr << 3);
-static int dib3000mc_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr)
-{
- struct dib3000_state *state = fe->demodulator_priv;
- if (onoff) {
- wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr));
- } else {
- wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr));
+ /* turn off data output */
+ dib3000mc_set_output_mode(&st, OUTMODE_HIGH_Z);
}
return 0;
}
-
-static int dib3000mc_demod_init(struct dib3000_state *state)
-{
- u16 default_addr = 0x0a;
- /* first init */
- if (state->config.demod_address != default_addr) {
- deb_info("initializing the demod the first time. Setting demod addr to 0x%x\n",default_addr);
- wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
- wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_PAR_CONT_CLK);
-
- wr(DIB3000MC_REG_RST_I2C_ADDR,
- DIB3000MC_DEMOD_ADDR(default_addr) |
- DIB3000MC_DEMOD_ADDR_ON);
-
- state->config.demod_address = default_addr;
-
- wr(DIB3000MC_REG_RST_I2C_ADDR,
- DIB3000MC_DEMOD_ADDR(default_addr));
- } else
- deb_info("demod is already initialized. Demod addr: 0x%x\n",state->config.demod_address);
- return 0;
-}
-
+EXPORT_SYMBOL(dib3000mc_i2c_enumeration);
static struct dvb_frontend_ops dib3000mc_ops;
-struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
- struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
{
- struct dib3000_state* state = NULL;
- u16 devid;
+ struct dvb_frontend *demod;
+ struct dib3000mc_state *st;
+ st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct dib3000_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
+ st->cfg = cfg;
+ st->i2c_adap = i2c_adap;
+ st->i2c_addr = i2c_addr;
- /* setup the state */
- state->i2c = i2c;
- memcpy(&state->config,config,sizeof(struct dib3000_config));
+ demod = &st->demod;
+ demod->demodulator_priv = st;
+ memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
- /* check for the correct demod */
- if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+ if (dib3000mc_identify(st) != 0)
goto error;
- devid = rd(DIB3000_REG_DEVICE_ID);
- if (devid != DIB3000MC_DEVICE_ID && devid != DIB3000P_DEVICE_ID)
- goto error;
+ dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr);
- switch (devid) {
- case DIB3000MC_DEVICE_ID:
- info("Found a DiBcom 3000M-C, interesting...");
- break;
- case DIB3000P_DEVICE_ID:
- info("Found a DiBcom 3000P.");
- break;
- }
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
-
- /* set the xfer operations */
- xfer_ops->pid_parse = dib3000mc_pid_parse;
- xfer_ops->fifo_ctrl = dib3000mc_fifo_control;
- xfer_ops->pid_ctrl = dib3000mc_pid_control;
- xfer_ops->tuner_pass_ctrl = dib3000mc_tuner_pass_ctrl;
+ dib3000mc_write_word(st, 1037, 0x3130);
- dib3000mc_demod_init(state);
-
- return &state->frontend;
+ return demod;
error:
- kfree(state);
+ kfree(st);
return NULL;
}
EXPORT_SYMBOL(dib3000mc_attach);
static struct dvb_frontend_ops dib3000mc_ops = {
-
.info = {
- .name = "DiBcom 3000P/M-C DVB-T",
- .type = FE_OFDM,
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
+ .name = "DiBcom 3000MC/P",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
.caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_RECOVER |
- FE_CAN_HIERARCHY_AUTO,
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
},
- .release = dib3000mc_release,
+ .release = dib3000mc_release,
- .init = dib3000mc_fe_init_nonmobile,
- .sleep = dib3000mc_sleep,
+ .init = dib3000mc_init,
+ .sleep = dib3000mc_sleep,
- .set_frontend = dib3000mc_set_frontend_and_tuner,
- .get_frontend = dib3000mc_get_frontend,
- .get_tune_settings = dib3000mc_fe_get_tune_settings,
+ .set_frontend = dib3000mc_set_frontend,
+ .get_tune_settings = dib3000mc_fe_get_tune_settings,
+ .get_frontend = dib3000mc_get_frontend,
- .read_status = dib3000mc_read_status,
- .read_ber = dib3000mc_read_ber,
+ .read_status = dib3000mc_read_status,
+ .read_ber = dib3000mc_read_ber,
.read_signal_strength = dib3000mc_read_signal_strength,
- .read_snr = dib3000mc_read_snr,
- .read_ucblocks = dib3000mc_read_unc_blocks,
+ .read_snr = dib3000mc_read_snr,
+ .read_ucblocks = dib3000mc_read_unc_blocks,
};
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib3000mc.h b/drivers/media/dvb/frontends/dib3000mc.h
new file mode 100644
index 00000000000..72d4757601d
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mc.h
@@ -0,0 +1,61 @@
+/*
+ * Driver for DiBcom DiB3000MC/P-demodulator.
+ *
+ * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher\@desy.de)
+ *
+ * This code is partially based on the previous dib3000mc.c .
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+#ifndef DIB3000MC_H
+#define DIB3000MC_H
+
+#include "dibx000_common.h"
+
+struct dib3000mc_config {
+ struct dibx000_agc_config *agc;
+
+ u8 phase_noise_mode;
+ u8 impulse_noise_mode;
+
+ u8 pwm3_inversion;
+ u8 use_pwm3;
+ u16 pwm3_value;
+
+ u16 max_time;
+ u16 ln_adc_level;
+
+ u8 agc_command1 :1;
+ u8 agc_command2 :1;
+
+ u8 mobile_mode;
+
+ u8 output_mpeg2_in_188_bytes;
+};
+
+#define DEFAULT_DIB3000MC_I2C_ADDRESS 16
+#define DEFAULT_DIB3000P_I2C_ADDRESS 24
+
+#if defined(CONFIG_DVB_DIB3000MC) || (defined(CONFIG_DVB_DIB3000MC_MODULE) && defined(MODULE))
+extern struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg);
+#else
+static inline struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_DIB3000MC
+
+extern int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[]);
+
+extern struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating);
+
+extern int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff);
+extern int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff);
+
+extern void dib3000mc_set_config(struct dvb_frontend *, struct dib3000mc_config *);
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib3000mc_priv.h b/drivers/media/dvb/frontends/dib3000mc_priv.h
deleted file mode 100644
index 2930aac7591..00000000000
--- a/drivers/media/dvb/frontends/dib3000mc_priv.h
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * dib3000mc_priv.h
- *
- * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
- * for more information see dib3000mc.c .
- */
-
-#ifndef __DIB3000MC_PRIV_H__
-#define __DIB3000MC_PRIV_H__
-
-/*
- * Demodulator parameters
- * reg: 0 1 1 1 11 11 111
- * | | | | | |
- * | | | | | +-- alpha (000=0, 001=1, 010=2, 100=4)
- * | | | | +----- constellation (00=QPSK, 01=16QAM, 10=64QAM)
- * | | | +-------- guard (00=1/32, 01=1/16, 10=1/8, 11=1/4)
- * | | +----------- transmission mode (0=2k, 1=8k)
- * | |
- * | +-------------- restart autosearch for parameters
- * +---------------- restart the demodulator
- * reg: 181 1 111 1
- * | | |
- * | | +- FEC applies for HP or LP (0=LP, 1=HP)
- * | +---- FEC rate (001=1/2, 010=2/3, 011=3/4, 101=5/6, 111=7/8)
- * +------- hierarchy on (0=no, 1=yes)
- */
-
-/* demodulator tuning parameter and restart options */
-#define DIB3000MC_REG_DEMOD_PARM ( 0)
-#define DIB3000MC_DEMOD_PARM(a,c,g,t) ( \
- (0x7 & a) | \
- ((0x3 & c) << 3) | \
- ((0x3 & g) << 5) | \
- ((0x1 & t) << 7) )
-#define DIB3000MC_DEMOD_RST_AUTO_SRCH_ON (1 << 8)
-#define DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF (0 << 8)
-#define DIB3000MC_DEMOD_RST_DEMOD_ON (1 << 9)
-#define DIB3000MC_DEMOD_RST_DEMOD_OFF (0 << 9)
-
-/* register for hierarchy parameters */
-#define DIB3000MC_REG_HRCH_PARM ( 181)
-#define DIB3000MC_HRCH_PARM(s,f,h) ( \
- (0x1 & s) | \
- ((0x7 & f) << 1) | \
- ((0x1 & h) << 4) )
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_1 ( 1)
-#define DIB3000MC_UNK_1 ( 0x04)
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_2 ( 2)
-#define DIB3000MC_UNK_2 ( 0x04)
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_3 ( 3)
-#define DIB3000MC_UNK_3 (0x1000)
-
-#define DIB3000MC_REG_UNK_4 ( 4)
-#define DIB3000MC_UNK_4 (0x0814)
-
-/* timeout ??? */
-#define DIB3000MC_REG_SEQ_TPS ( 5)
-#define DIB3000MC_SEQ_TPS_DEFAULT ( 1)
-#define DIB3000MC_SEQ_TPS(s,t) ( \
- ((s & 0x0f) << 4) | \
- ((t & 0x01) << 8) )
-#define DIB3000MC_IS_TPS(v) ((v << 8) & 0x1)
-#define DIB3000MC_IS_AS(v) ((v >> 4) & 0xf)
-
-/* parameters for the bandwidth */
-#define DIB3000MC_REG_BW_TIMOUT_MSB ( 6)
-#define DIB3000MC_REG_BW_TIMOUT_LSB ( 7)
-
-static u16 dib3000mc_reg_bandwidth[] = { 6,7,8,9,10,11,16,17 };
-
-/*static u16 dib3000mc_bandwidth_5mhz[] =
- { 0x28, 0x9380, 0x87, 0x4100, 0x2a4, 0x4500, 0x1, 0xb0d0 };*/
-
-static u16 dib3000mc_bandwidth_6mhz[] =
- { 0x21, 0xd040, 0x70, 0xb62b, 0x233, 0x8ed5, 0x1, 0xb0d0 };
-
-static u16 dib3000mc_bandwidth_7mhz[] =
- { 0x1c, 0xfba5, 0x60, 0x9c25, 0x1e3, 0x0cb7, 0x1, 0xb0d0 };
-
-static u16 dib3000mc_bandwidth_8mhz[] =
- { 0x19, 0x5c30, 0x54, 0x88a0, 0x1a6, 0xab20, 0x1, 0xb0d0 };
-
-static u16 dib3000mc_reg_bandwidth_general[] = { 12,13,14,15 };
-static u16 dib3000mc_bandwidth_general[] = { 0x0000, 0x03e8, 0x0000, 0x03f2 };
-
-/* lock mask */
-#define DIB3000MC_REG_LOCK_MASK ( 15)
-#define DIB3000MC_ACTIVATE_LOCK_MASK (0x0800)
-
-/* reset the uncorrected packet count (??? do it 5 times) */
-#define DIB3000MC_REG_RST_UNC ( 18)
-#define DIB3000MC_RST_UNC_ON ( 1)
-#define DIB3000MC_RST_UNC_OFF ( 0)
-
-#define DIB3000MC_REG_UNK_19 ( 19)
-#define DIB3000MC_UNK_19 ( 0)
-
-/* DDS frequency value (IF position) and inversion bit */
-#define DIB3000MC_REG_INVERSION ( 21)
-#define DIB3000MC_REG_SET_DDS_FREQ_MSB ( 21)
-#define DIB3000MC_DDS_FREQ_MSB_INV_OFF (0x0164)
-#define DIB3000MC_DDS_FREQ_MSB_INV_ON (0x0364)
-
-#define DIB3000MC_REG_SET_DDS_FREQ_LSB ( 22)
-#define DIB3000MC_DDS_FREQ_LSB (0x463d)
-
-/* timing frequencies setting */
-#define DIB3000MC_REG_TIMING_FREQ_MSB ( 23)
-#define DIB3000MC_REG_TIMING_FREQ_LSB ( 24)
-#define DIB3000MC_CLOCK_REF (0x151fd1)
-
-//static u16 dib3000mc_reg_timing_freq[] = { 23,24 };
-
-//static u16 dib3000mc_timing_freq[][2] = {
-// { 0x69, 0x9f18 }, /* 5 MHz */
-// { 0x7e ,0xbee9 }, /* 6 MHz */
-// { 0x93 ,0xdebb }, /* 7 MHz */
-// { 0xa8 ,0xfe8c }, /* 8 MHz */
-//};
-
-/* timeout ??? */
-static u16 dib3000mc_reg_offset[] = { 26,33 };
-
-static u16 dib3000mc_offset[][2] = {
- { 26240, 5 }, /* default */
- { 30336, 6 }, /* 8K */
- { 38528, 8 }, /* 2K */
-};
-
-#define DIB3000MC_REG_ISI ( 29)
-#define DIB3000MC_ISI_DEFAULT (0x1073)
-#define DIB3000MC_ISI_ACTIVATE (0x0000)
-#define DIB3000MC_ISI_INHIBIT (0x0200)
-
-/* impulse noise control */
-static u16 dib3000mc_reg_imp_noise_ctl[] = { 34,35 };
-
-static u16 dib3000mc_imp_noise_ctl[][2] = {
- { 0x1294, 0x1ff8 }, /* mode 0 */
- { 0x1294, 0x1ff8 }, /* mode 1 */
- { 0x1294, 0x1ff8 }, /* mode 2 */
- { 0x1294, 0x1ff8 }, /* mode 3 */
- { 0x1294, 0x1ff8 }, /* mode 4 */
-};
-
-/* AGC registers */
-static u16 dib3000mc_reg_agc[] = {
- 36,37,38,39,42,43,44,45,46,47,48,49
-};
-
-static u16 dib3000mc_agc_tuner[][12] = {
- { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xcf5c, 0x6666,
- 0xbae1, 0xa148, 0x3b5e, 0x3c1c, 0x001a, 0x2019
- }, /* TUNER_PANASONIC_ENV77H04D5, */
-
- { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xdc29, 0x570a,
- 0xbae1, 0x8ccd, 0x3b6d, 0x551d, 0x000a, 0x951e
- }, /* TUNER_PANASONIC_ENV57H13D5, TUNER_PANASONIC_ENV57H12D5 */
-
- { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xffff, 0xffff,
- 0xffff, 0x0000, 0xfdfd, 0x4040, 0x00fd, 0x4040
- }, /* TUNER_SAMSUNG_DTOS333IH102, TUNER_RFAGCIN_UNKNOWN */
-
- { 0x0196, 0x301d, 0x0000, 0x1cc7, 0xbd71, 0x5c29,
- 0xb5c3, 0x6148, 0x6569, 0x5127, 0x0033, 0x3537
- }, /* TUNER_PROVIDER_X */
- /* TODO TUNER_PANASONIC_ENV57H10D8, TUNER_PANASONIC_ENV57H11D8 */
-};
-
-/* AGC loop bandwidth */
-static u16 dib3000mc_reg_agc_bandwidth[] = { 40,41 };
-static u16 dib3000mc_agc_bandwidth[] = { 0x119,0x330 };
-
-static u16 dib3000mc_reg_agc_bandwidth_general[] = { 50,51,52,53,54 };
-static u16 dib3000mc_agc_bandwidth_general[] =
- { 0x8000, 0x91ca, 0x01ba, 0x0087, 0x0087 };
-
-#define DIB3000MC_REG_IMP_NOISE_55 ( 55)
-#define DIB3000MC_IMP_NEW_ALGO(w) (w | (1<<10))
-
-/* Impulse noise params */
-static u16 dib3000mc_reg_impulse_noise[] = { 55,56,57 };
-static u16 dib3000mc_impluse_noise[][3] = {
- { 0x489, 0x89, 0x72 }, /* 5 MHz */
- { 0x4a5, 0xa5, 0x89 }, /* 6 MHz */
- { 0x4c0, 0xc0, 0xa0 }, /* 7 MHz */
- { 0x4db, 0xdb, 0xb7 }, /* 8 Mhz */
-};
-
-static u16 dib3000mc_reg_fft[] = {
- 58,59,60,61,62,63,64,65,66,67,68,69,
- 70,71,72,73,74,75,76,77,78,79,80,81,
- 82,83,84,85,86
-};
-
-static u16 dib3000mc_fft_modes[][29] = {
- { 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
- 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
- 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
- 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
- 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0, 0xd
- }, /* fft mode 0 */
- { 0x3b, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
- 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
- 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
- 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
- 0x3ffe, 0x5b3, 0x3feb, 0x0, 0x8200, 0xd
- }, /* fft mode 1 */
-};
-
-#define DIB3000MC_REG_UNK_88 ( 88)
-#define DIB3000MC_UNK_88 (0x0410)
-
-static u16 dib3000mc_reg_bw[] = { 93,94,95,96,97,98 };
-static u16 dib3000mc_bw[][6] = {
- { 0,0,0,0,0,0 }, /* 5 MHz */
- { 0,0,0,0,0,0 }, /* 6 MHz */
- { 0,0,0,0,0,0 }, /* 7 MHz */
- { 0x20, 0x21, 0x20, 0x23, 0x20, 0x27 }, /* 8 MHz */
-};
-
-
-/* phase noise control */
-#define DIB3000MC_REG_UNK_99 ( 99)
-#define DIB3000MC_UNK_99 (0x0220)
-
-#define DIB3000MC_REG_SCAN_BOOST ( 100)
-#define DIB3000MC_SCAN_BOOST_ON ((11 << 6) + 6)
-#define DIB3000MC_SCAN_BOOST_OFF ((16 << 6) + 9)
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_110 ( 110)
-#define DIB3000MC_UNK_110 ( 3277)
-
-#define DIB3000MC_REG_UNK_111 ( 111)
-#define DIB3000MC_UNK_111_PH_N_MODE_0 ( 0)
-#define DIB3000MC_UNK_111_PH_N_MODE_1 (1 << 1)
-
-/* superious rm config */
-#define DIB3000MC_REG_UNK_120 ( 120)
-#define DIB3000MC_UNK_120 ( 8207)
-
-#define DIB3000MC_REG_UNK_133 ( 133)
-#define DIB3000MC_UNK_133 ( 15564)
-
-#define DIB3000MC_REG_UNK_134 ( 134)
-#define DIB3000MC_UNK_134 ( 0)
-
-/* adapter config for constellation */
-static u16 dib3000mc_reg_adp_cfg[] = { 129, 130, 131, 132 };
-
-static u16 dib3000mc_adp_cfg[][4] = {
- { 0x99a, 0x7fae, 0x333, 0x7ff0 }, /* QPSK */
- { 0x23d, 0x7fdf, 0x0a4, 0x7ff0 }, /* 16-QAM */
- { 0x148, 0x7ff0, 0x0a4, 0x7ff8 }, /* 64-QAM */
-};
-
-static u16 dib3000mc_reg_mobile_mode[] = { 139, 140, 141, 175, 1032 };
-
-static u16 dib3000mc_mobile_mode[][5] = {
- { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* fixed */
- { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* portable */
- { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* mobile */
- { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* auto */
-};
-
-#define DIB3000MC_REG_DIVERSITY1 ( 177)
-#define DIB3000MC_DIVERSITY1_DEFAULT ( 1)
-
-#define DIB3000MC_REG_DIVERSITY2 ( 178)
-#define DIB3000MC_DIVERSITY2_DEFAULT ( 1)
-
-#define DIB3000MC_REG_DIVERSITY3 ( 180)
-#define DIB3000MC_DIVERSITY3_IN_OFF (0xfff0)
-#define DIB3000MC_DIVERSITY3_IN_ON (0xfff6)
-
-#define DIB3000MC_REG_FEC_CFG ( 195)
-#define DIB3000MC_FEC_CFG ( 0x10)
-
-/*
- * reg 206, output mode
- * 1111 1111
- * |||| ||||
- * |||| |||+- unk
- * |||| ||+-- unk
- * |||| |+--- unk (on by default)
- * |||| +---- fifo_ctrl (1 = inhibit (flushed), 0 = active (unflushed))
- * |||+------ pid_parse (1 = enabled, 0 = disabled)
- * ||+------- outp_188 (1 = TS packet size 188, 0 = packet size 204)
- * |+-------- unk
- * +--------- unk
- */
-
-#define DIB3000MC_REG_SMO_MODE ( 206)
-#define DIB3000MC_SMO_MODE_DEFAULT (1 << 2)
-#define DIB3000MC_SMO_MODE_FIFO_FLUSH (1 << 3)
-#define DIB3000MC_SMO_MODE_FIFO_UNFLUSH (0xfff7)
-#define DIB3000MC_SMO_MODE_PID_PARSE (1 << 4)
-#define DIB3000MC_SMO_MODE_NO_PID_PARSE (0xffef)
-#define DIB3000MC_SMO_MODE_188 (1 << 5)
-#define DIB3000MC_SMO_MODE_SLAVE (DIB3000MC_SMO_MODE_DEFAULT | \
- DIB3000MC_SMO_MODE_188 | DIB3000MC_SMO_MODE_PID_PARSE | (1<<1))
-
-#define DIB3000MC_REG_FIFO_THRESHOLD ( 207)
-#define DIB3000MC_FIFO_THRESHOLD_DEFAULT ( 1792)
-#define DIB3000MC_FIFO_THRESHOLD_SLAVE ( 512)
-/*
- * pidfilter
- * it is not a hardware pidfilter but a filter which drops all pids
- * except the ones set. When connected to USB1.1 bandwidth this is important.
- * DiB3000P/M-C can filter up to 32 PIDs
- */
-#define DIB3000MC_REG_FIRST_PID ( 212)
-#define DIB3000MC_NUM_PIDS ( 32)
-
-#define DIB3000MC_REG_OUTMODE ( 244)
-#define DIB3000MC_OM_PARALLEL_GATED_CLK ( 0)
-#define DIB3000MC_OM_PAR_CONT_CLK (1 << 11)
-#define DIB3000MC_OM_SERIAL (2 << 11)
-#define DIB3000MC_OM_DIVOUT_ON (4 << 11)
-#define DIB3000MC_OM_SLAVE (DIB3000MC_OM_DIVOUT_ON | DIB3000MC_OM_PAR_CONT_CLK)
-
-#define DIB3000MC_REG_RF_POWER ( 392)
-
-#define DIB3000MC_REG_FFT_POSITION ( 407)
-
-#define DIB3000MC_REG_DDS_FREQ_MSB ( 414)
-#define DIB3000MC_REG_DDS_FREQ_LSB ( 415)
-
-#define DIB3000MC_REG_TIMING_OFFS_MSB ( 416)
-#define DIB3000MC_REG_TIMING_OFFS_LSB ( 417)
-
-#define DIB3000MC_REG_TUNING_PARM ( 458)
-#define DIB3000MC_TP_QAM(v) ((v >> 13) & 0x03)
-#define DIB3000MC_TP_HRCH(v) ((v >> 12) & 0x01)
-#define DIB3000MC_TP_ALPHA(v) ((v >> 9) & 0x07)
-#define DIB3000MC_TP_FFT(v) ((v >> 8) & 0x01)
-#define DIB3000MC_TP_FEC_CR_HP(v) ((v >> 5) & 0x07)
-#define DIB3000MC_TP_FEC_CR_LP(v) ((v >> 2) & 0x07)
-#define DIB3000MC_TP_GUARD(v) (v & 0x03)
-
-#define DIB3000MC_REG_SIGNAL_NOISE_MSB ( 483)
-#define DIB3000MC_REG_SIGNAL_NOISE_LSB ( 484)
-
-#define DIB3000MC_REG_MER ( 485)
-
-#define DIB3000MC_REG_BER_MSB ( 500)
-#define DIB3000MC_REG_BER_LSB ( 501)
-
-#define DIB3000MC_REG_PACKET_ERRORS ( 503)
-
-#define DIB3000MC_REG_PACKET_ERROR_COUNT ( 506)
-
-#define DIB3000MC_REG_LOCK_507 ( 507)
-#define DIB3000MC_LOCK_507 (0x0002) // ? name correct ?
-
-#define DIB3000MC_REG_LOCKING ( 509)
-#define DIB3000MC_AGC_LOCK(v) (v & 0x8000)
-#define DIB3000MC_CARRIER_LOCK(v) (v & 0x2000)
-#define DIB3000MC_MPEG_SYNC_LOCK(v) (v & 0x0080)
-#define DIB3000MC_MPEG_DATA_LOCK(v) (v & 0x0040)
-#define DIB3000MC_TPS_LOCK(v) (v & 0x0004)
-
-#define DIB3000MC_REG_AS_IRQ ( 511)
-#define DIB3000MC_AS_IRQ_SUCCESS (1 << 1)
-#define DIB3000MC_AS_IRQ_FAIL ( 1)
-
-#define DIB3000MC_REG_TUNER ( 769)
-
-#define DIB3000MC_REG_RST_I2C_ADDR ( 1024)
-#define DIB3000MC_DEMOD_ADDR_ON ( 1)
-#define DIB3000MC_DEMOD_ADDR(a) ((a << 4) & 0x03F0)
-
-#define DIB3000MC_REG_RESTART ( 1027)
-#define DIB3000MC_RESTART_OFF (0x0000)
-#define DIB3000MC_RESTART_AGC (0x0800)
-#define DIB3000MC_RESTART_CONFIG (0x8000)
-
-#define DIB3000MC_REG_RESTART_VIT ( 1028)
-#define DIB3000MC_RESTART_VIT_OFF ( 0)
-#define DIB3000MC_RESTART_VIT_ON ( 1)
-
-#define DIB3000MC_REG_CLK_CFG_1 ( 1031)
-#define DIB3000MC_CLK_CFG_1_POWER_UP ( 0)
-#define DIB3000MC_CLK_CFG_1_POWER_DOWN (0xffff)
-
-#define DIB3000MC_REG_CLK_CFG_2 ( 1032)
-#define DIB3000MC_CLK_CFG_2_PUP_FIXED (0x012c)
-#define DIB3000MC_CLK_CFG_2_PUP_PORT (0x0104)
-#define DIB3000MC_CLK_CFG_2_PUP_MOBILE (0x0000)
-#define DIB3000MC_CLK_CFG_2_POWER_DOWN (0xffff)
-
-#define DIB3000MC_REG_CLK_CFG_3 ( 1033)
-#define DIB3000MC_CLK_CFG_3_POWER_UP ( 0)
-#define DIB3000MC_CLK_CFG_3_POWER_DOWN (0xfff5)
-
-#define DIB3000MC_REG_CLK_CFG_7 ( 1037)
-#define DIB3000MC_CLK_CFG_7_INIT ( 12592)
-#define DIB3000MC_CLK_CFG_7_POWER_UP (~0x0003)
-#define DIB3000MC_CLK_CFG_7_PWR_DOWN (0x0003)
-#define DIB3000MC_CLK_CFG_7_DIV_IN_OFF (1 << 8)
-
-/* was commented out ??? */
-#define DIB3000MC_REG_CLK_CFG_8 ( 1038)
-#define DIB3000MC_CLK_CFG_8_POWER_UP (0x160c)
-
-#define DIB3000MC_REG_CLK_CFG_9 ( 1039)
-#define DIB3000MC_CLK_CFG_9_POWER_UP ( 0)
-
-/* also clock ??? */
-#define DIB3000MC_REG_ELEC_OUT ( 1040)
-#define DIB3000MC_ELEC_OUT_HIGH_Z ( 0)
-#define DIB3000MC_ELEC_OUT_DIV_OUT_ON ( 1)
-#define DIB3000MC_ELEC_OUT_SLAVE ( 3)
-
-#endif
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c
new file mode 100644
index 00000000000..f5d40aa3d27
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000m.c
@@ -0,0 +1,1191 @@
+/*
+ * Linux-DVB Driver for DiBcom's DiB7000M and
+ * first generation DiB7000P-demodulator-family.
+ *
+ * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "dib7000m.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M:"); printk(args); } } while (0)
+
+struct dib7000m_state {
+ struct dvb_frontend demod;
+ struct dib7000m_config cfg;
+
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+
+ struct dibx000_i2c_master i2c_master;
+
+/* offset is 1 in case of the 7000MC */
+ u8 reg_offs;
+
+ u16 wbd_ref;
+
+ u8 current_band;
+ fe_bandwidth_t current_bandwidth;
+ struct dibx000_agc_config *current_agc;
+ u32 timf;
+
+ u16 revision;
+};
+
+enum dib7000m_power_mode {
+ DIB7000M_POWER_ALL = 0,
+
+ DIB7000M_POWER_NO,
+ DIB7000M_POWER_INTERF_ANALOG_AGC,
+ DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD,
+ DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD,
+ DIB7000M_POWER_INTERFACE_ONLY,
+};
+
+static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg)
+{
+ u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
+
+ if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+ dprintk("i2c read error on %d\n",reg);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val)
+{
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode)
+{
+ int ret = 0;
+ u16 outreg, fifo_threshold, smo_mode,
+ sram = 0x0005; /* by default SRAM output is disabled */
+
+ outreg = 0;
+ fifo_threshold = 1792;
+ smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1);
+
+ dprintk("-I- Setting output mode for demod %p to %d\n",
+ &state->demod, mode);
+
+ switch (mode) {
+ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
+ outreg = (1 << 10); /* 0x0400 */
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
+ outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+ break;
+ case OUTMODE_MPEG2_SERIAL: // STBs with serial input
+ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */
+ break;
+ case OUTMODE_DIVERSITY:
+ if (state->cfg.hostbus_diversity)
+ outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+ else
+ sram |= 0x0c00;
+ break;
+ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ outreg = (1 << 10) | (5 << 6);
+ break;
+ case OUTMODE_HIGH_Z: // disable
+ outreg = 0;
+ break;
+ default:
+ dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+ break;
+ }
+
+ if (state->cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5) ;
+
+ ret |= dib7000m_write_word(state, 294 + state->reg_offs, smo_mode);
+ ret |= dib7000m_write_word(state, 295 + state->reg_offs, fifo_threshold); /* synchronous fread */
+ ret |= dib7000m_write_word(state, 1795, outreg);
+ ret |= dib7000m_write_word(state, 1805, sram);
+
+ return ret;
+}
+
+static int dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode)
+{
+ /* by default everything is going to be powered off */
+ u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906 = 0x3fff;
+
+ /* now, depending on the requested mode, we power on */
+ switch (mode) {
+ /* power up everything in the demod */
+ case DIB7000M_POWER_ALL:
+ reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000;
+ break;
+
+ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */
+ case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2));
+ break;
+
+ case DIB7000M_POWER_INTERF_ANALOG_AGC:
+ reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10));
+ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2));
+ reg_906 &= ~((1 << 0));
+ break;
+
+ case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD:
+ reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000;
+ break;
+
+ case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD:
+ reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000;
+ break;
+ case DIB7000M_POWER_NO:
+ break;
+ }
+
+ /* always power down unused parts */
+ if (!state->cfg.mobile_mode)
+ reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1);
+
+ /* P_sdio_select_clk = 0 on MC */
+ if (state->revision != 0x4000)
+ reg_906 <<= 1;
+
+ dib7000m_write_word(state, 903, reg_903);
+ dib7000m_write_word(state, 904, reg_904);
+ dib7000m_write_word(state, 905, reg_905);
+ dib7000m_write_word(state, 906, reg_906);
+
+ return 0;
+}
+
+static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no)
+{
+ int ret = 0;
+ u16 reg_913 = dib7000m_read_word(state, 913),
+ reg_914 = dib7000m_read_word(state, 914);
+
+ switch (no) {
+ case DIBX000_SLOW_ADC_ON:
+ reg_914 |= (1 << 1) | (1 << 0);
+ ret |= dib7000m_write_word(state, 914, reg_914);
+ reg_914 &= ~(1 << 1);
+ break;
+
+ case DIBX000_SLOW_ADC_OFF:
+ reg_914 |= (1 << 1) | (1 << 0);
+ break;
+
+ case DIBX000_ADC_ON:
+ if (state->revision == 0x4000) { // workaround for PA/MA
+ // power-up ADC
+ dib7000m_write_word(state, 913, 0);
+ dib7000m_write_word(state, 914, reg_914 & 0x3);
+ // power-down bandgag
+ dib7000m_write_word(state, 913, (1 << 15));
+ dib7000m_write_word(state, 914, reg_914 & 0x3);
+ }
+
+ reg_913 &= 0x0fff;
+ reg_914 &= 0x0003;
+ break;
+
+ case DIBX000_ADC_OFF: // leave the VBG voltage on
+ reg_913 |= (1 << 14) | (1 << 13) | (1 << 12);
+ reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
+ break;
+
+ case DIBX000_VBG_ENABLE:
+ reg_913 &= ~(1 << 15);
+ break;
+
+ case DIBX000_VBG_DISABLE:
+ reg_913 |= (1 << 15);
+ break;
+
+ default:
+ break;
+ }
+
+// dprintk("-D- 913: %x, 914: %x\n", reg_913, reg_914);
+
+ ret |= dib7000m_write_word(state, 913, reg_913);
+ ret |= dib7000m_write_word(state, 914, reg_914);
+
+ return ret;
+}
+
+static int dib7000m_set_bandwidth(struct dvb_frontend *demod, u8 bw_idx)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ u32 timf;
+
+ // store the current bandwidth for later use
+ state->current_bandwidth = bw_idx;
+
+ if (state->timf == 0) {
+ dprintk("-D- Using default timf\n");
+ timf = state->cfg.bw->timf;
+ } else {
+ dprintk("-D- Using updated timf\n");
+ timf = state->timf;
+ }
+
+ timf = timf * (BW_INDEX_TO_KHZ(bw_idx) / 100) / 80;
+
+ dib7000m_write_word(state, 23, (timf >> 16) & 0xffff);
+ dib7000m_write_word(state, 24, (timf ) & 0xffff);
+
+ return 0;
+}
+
+static int dib7000m_sad_calib(struct dib7000m_state *state)
+{
+
+/* internal */
+// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+ dib7000m_write_word(state, 929, (0 << 1) | (0 << 0));
+ dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096
+
+ /* do the calibration */
+ dib7000m_write_word(state, 929, (1 << 0));
+ dib7000m_write_word(state, 929, (0 << 0));
+
+ msleep(1);
+
+ return 0;
+}
+
+static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw)
+{
+ dib7000m_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff);
+ dib7000m_write_word(state, 19, (bw->internal*1000) & 0xffff);
+ dib7000m_write_word(state, 21, (bw->ifreq >> 16) & 0xffff);
+ dib7000m_write_word(state, 22, bw->ifreq & 0xffff);
+
+ dib7000m_write_word(state, 928, bw->sad_cfg);
+}
+
+static void dib7000m_reset_pll(struct dib7000m_state *state)
+{
+ const struct dibx000_bandwidth_config *bw = state->cfg.bw;
+ u16 reg_907,reg_910;
+
+ /* default */
+ reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) |
+ (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) |
+ (bw->enable_refdiv << 1) | (0 << 0);
+ reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset;
+
+ // for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value)
+ // this is only working only for 30 MHz crystals
+ if (!state->cfg.quartz_direct) {
+ reg_910 |= (1 << 5); // forcing the predivider to 1
+
+ // if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2)
+ if(state->cfg.input_clk_is_div_2)
+ reg_907 |= (16 << 9);
+ else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary
+ reg_907 |= (8 << 9);
+ } else {
+ reg_907 |= (bw->pll_ratio & 0x3f) << 9;
+ reg_910 |= (bw->pll_prediv << 5);
+ }
+
+ dib7000m_write_word(state, 910, reg_910); // pll cfg
+ dib7000m_write_word(state, 907, reg_907); // clk cfg0
+ dib7000m_write_word(state, 908, 0x0006); // clk_cfg1
+
+ dib7000m_reset_pll_common(state, bw);
+}
+
+static void dib7000mc_reset_pll(struct dib7000m_state *state)
+{
+ const struct dibx000_bandwidth_config *bw = state->cfg.bw;
+
+ // clk_cfg0
+ dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0));
+
+ // clk_cfg1
+ //dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) |
+ dib7000m_write_word(state, 908, (0 << 14) | (3 << 12) |(0 << 11) |
+ (bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) |
+ (bw->pll_bypass << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0));
+
+ // smpl_cfg
+ dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7));
+
+ dib7000m_reset_pll_common(state, bw);
+}
+
+static int dib7000m_reset_gpio(struct dib7000m_state *st)
+{
+ /* reset the GPIOs */
+ dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",
+ st->cfg.gpio_dir, st->cfg.gpio_val,st->cfg.gpio_pwm_pos);
+
+ dib7000m_write_word(st, 773, st->cfg.gpio_dir);
+ dib7000m_write_word(st, 774, st->cfg.gpio_val);
+
+ /* TODO 782 is P_gpio_od */
+
+ dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos);
+
+ dib7000m_write_word(st, 780, st->cfg.pwm_freq_div);
+ return 0;
+}
+
+static int dib7000m_demod_reset(struct dib7000m_state *state)
+{
+ dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);
+
+ /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */
+ dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE);
+
+ /* restart all parts */
+ dib7000m_write_word(state, 898, 0xffff);
+ dib7000m_write_word(state, 899, 0xffff);
+ dib7000m_write_word(state, 900, 0xff0f);
+ dib7000m_write_word(state, 901, 0xfffc);
+
+ dib7000m_write_word(state, 898, 0);
+ dib7000m_write_word(state, 899, 0);
+ dib7000m_write_word(state, 900, 0);
+ dib7000m_write_word(state, 901, 0);
+
+ if (state->revision == 0x4000)
+ dib7000m_reset_pll(state);
+ else
+ dib7000mc_reset_pll(state);
+
+ if (dib7000m_reset_gpio(state) != 0)
+ dprintk("-E- GPIO reset was not successful.\n");
+
+ if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+ dprintk("-E- OUTPUT_MODE could not be resetted.\n");
+
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) );
+
+ dib7000m_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+
+ dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+ dib7000m_sad_calib(state);
+ dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF);
+
+ dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY);
+
+ return 0;
+}
+
+static void dib7000m_restart_agc(struct dib7000m_state *state)
+{
+ // P_restart_iqc & P_restart_agc
+ dib7000m_write_word(state, 898, 0x0c00);
+ dib7000m_write_word(state, 898, 0x0000);
+}
+
+static int dib7000m_agc_soft_split(struct dib7000m_state *state)
+{
+ u16 agc,split_offset;
+
+ if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0)
+ return 0;
+
+ // n_agc_global
+ agc = dib7000m_read_word(state, 390);
+
+ if (agc > state->current_agc->split.min_thres)
+ split_offset = state->current_agc->split.min;
+ else if (agc < state->current_agc->split.max_thres)
+ split_offset = state->current_agc->split.max;
+ else
+ split_offset = state->current_agc->split.max *
+ (agc - state->current_agc->split.min_thres) /
+ (state->current_agc->split.max_thres - state->current_agc->split.min_thres);
+
+ dprintk("AGC split_offset: %d\n",split_offset);
+
+ // P_agc_force_split and P_agc_split_offset
+ return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset);
+}
+
+static int dib7000m_update_lna(struct dib7000m_state *state)
+{
+ int i;
+ u16 dyn_gain;
+
+ // when there is no LNA to program return immediatly
+ if (state->cfg.update_lna == NULL)
+ return 0;
+
+ msleep(60);
+ for (i = 0; i < 20; i++) {
+ // read dyn_gain here (because it is demod-dependent and not tuner)
+ dyn_gain = dib7000m_read_word(state, 390);
+
+ dprintk("agc global: %d\n", dyn_gain);
+
+ if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed
+ dib7000m_restart_agc(state);
+ msleep(60);
+ } else
+ break;
+ }
+ return 0;
+}
+
+static void dib7000m_set_agc_config(struct dib7000m_state *state, u8 band)
+{
+ struct dibx000_agc_config *agc = NULL;
+ int i;
+ if (state->current_band == band)
+ return;
+ state->current_band = band;
+
+ for (i = 0; i < state->cfg.agc_config_count; i++)
+ if (state->cfg.agc[i].band_caps & band) {
+ agc = &state->cfg.agc[i];
+ break;
+ }
+
+ if (agc == NULL) {
+ dprintk("-E- No valid AGC configuration found for band 0x%02x\n",band);
+ return;
+ }
+
+ state->current_agc = agc;
+
+ /* AGC */
+ dib7000m_write_word(state, 72 , agc->setup);
+ dib7000m_write_word(state, 73 , agc->inv_gain);
+ dib7000m_write_word(state, 74 , agc->time_stabiliz);
+ dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock);
+
+ // Demod AGC loop configuration
+ dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp);
+ dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp);
+
+ dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
+ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
+
+ /* AGC continued */
+ if (state->wbd_ref != 0)
+ dib7000m_write_word(state, 102, state->wbd_ref);
+ else // use default
+ dib7000m_write_word(state, 102, agc->wbd_ref);
+
+ dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) );
+ dib7000m_write_word(state, 104, agc->agc1_max);
+ dib7000m_write_word(state, 105, agc->agc1_min);
+ dib7000m_write_word(state, 106, agc->agc2_max);
+ dib7000m_write_word(state, 107, agc->agc2_min);
+ dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 );
+ dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+ dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+ if (state->revision > 0x4000) { // settings for the MC
+ dib7000m_write_word(state, 71, agc->agc1_pt3);
+// dprintk("-D- 929: %x %d %d\n",
+// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel);
+ dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2));
+ } else {
+ // wrong default values
+ u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 };
+ for (i = 0; i < 9; i++)
+ dib7000m_write_word(state, 88 + i, b[i]);
+ }
+}
+
+static void dib7000m_update_timf_freq(struct dib7000m_state *state)
+{
+ u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437);
+ state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100);
+ dib7000m_write_word(state, 23, (u16) (timf >> 16));
+ dib7000m_write_word(state, 24, (u16) (timf & 0xffff));
+ dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf);
+}
+
+static void dib7000m_set_channel(struct dib7000m_state *state, struct dibx000_ofdm_channel *ch, u8 seq)
+{
+ u16 value, est[4];
+
+ dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->RF_kHz));
+
+ /* nfft, guard, qam, alpha */
+ dib7000m_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha));
+ dib7000m_write_word(state, 5, (seq << 4));
+
+ /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */
+ value = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1);
+ if (ch->vit_hrch == 0 || ch->vit_select_hp == 1)
+ value |= (ch->vit_code_rate_hp << 1);
+ else
+ value |= (ch->vit_code_rate_lp << 1);
+ dib7000m_write_word(state, 267 + state->reg_offs, value);
+
+ /* offset loop parameters */
+
+ /* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */
+ dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80);
+
+ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+ dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */
+ dib7000m_write_word(state, 32, (0 << 4) | 0x3);
+
+ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */
+ dib7000m_write_word(state, 33, (0 << 4) | 0x5);
+
+ /* P_dvsy_sync_wait */
+ switch (ch->nfft) {
+ case 1: value = 256; break;
+ case 2: value = 128; break;
+ case 0:
+ default: value = 64; break;
+ }
+ value *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin
+ value <<= 4;
+
+ /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */
+ /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
+ if (ch->intlv_native || state->revision > 0x4000)
+ value |= (1 << 2) | (2 << 0);
+ else
+ value |= 0;
+ dib7000m_write_word(state, 266 + state->reg_offs, value);
+
+ /* channel estimation fine configuration */
+ switch (ch->nqam) {
+ case 2:
+ est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */
+ est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */
+ break;
+ case 1:
+ est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */
+ est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */
+ break;
+ default:
+ est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */
+ est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */
+ est[2] = 0x0333; /* P_adp_regul_ext 0.1 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */
+ break;
+ }
+ for (value = 0; value < 4; value++)
+ dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]);
+
+ // set power-up level: interf+analog+AGC
+ dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC);
+ dib7000m_set_adc_state(state, DIBX000_ADC_ON);
+
+ msleep(7);
+
+ //AGC initialization
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 1);
+
+ dib7000m_restart_agc(state);
+
+ // wait AGC rough lock time
+ msleep(5);
+
+ dib7000m_update_lna(state);
+ dib7000m_agc_soft_split(state);
+
+ // wait AGC accurate lock time
+ msleep(7);
+
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 0);
+
+ // set power-up level: autosearch
+ dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD);
+}
+
+static int dib7000m_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ struct dibx000_ofdm_channel auto_ch;
+ int ret = 0;
+ u32 value;
+
+ INIT_OFDM_CHANNEL(&auto_ch);
+ auto_ch.RF_kHz = ch->RF_kHz;
+ auto_ch.Bw = ch->Bw;
+ auto_ch.nqam = 2;
+ auto_ch.guard = 0;
+ auto_ch.nfft = 1;
+ auto_ch.vit_alpha = 1;
+ auto_ch.vit_select_hp = 1;
+ auto_ch.vit_code_rate_hp = 2;
+ auto_ch.vit_code_rate_lp = 3;
+ auto_ch.vit_hrch = 0;
+ auto_ch.intlv_native = 1;
+
+ dib7000m_set_channel(state, &auto_ch, 7);
+
+ // always use the setting for 8MHz here lock_time for 7,6 MHz are longer
+ value = 30 * state->cfg.bw->internal;
+ ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time
+ ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time
+ value = 100 * state->cfg.bw->internal;
+ ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time
+ ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time
+ value = 500 * state->cfg.bw->internal;
+ ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time
+ ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time
+
+ // start search
+ value = dib7000m_read_word(state, 0);
+ ret |= dib7000m_write_word(state, 0, value | (1 << 9));
+
+ /* clear n_irq_pending */
+ if (state->revision == 0x4000)
+ dib7000m_write_word(state, 1793, 0);
+ else
+ dib7000m_read_word(state, 537);
+
+ ret |= dib7000m_write_word(state, 0, (u16) value);
+
+ return ret;
+}
+
+static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg)
+{
+ u16 irq_pending = dib7000m_read_word(state, reg);
+
+ if (irq_pending & 0x1) { // failed
+ dprintk("#\n");
+ return 1;
+ }
+
+ if (irq_pending & 0x2) { // succeeded
+ dprintk("!\n");
+ return 2;
+ }
+ return 0; // still pending
+}
+
+static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ if (state->revision == 0x4000)
+ return dib7000m_autosearch_irq(state, 1793);
+ else
+ return dib7000m_autosearch_irq(state, 537);
+}
+
+static int dib7000m_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ int ret = 0;
+ u16 value;
+
+ // we are already tuned - just resuming from suspend
+ if (ch != NULL)
+ dib7000m_set_channel(state, ch, 0);
+ else
+ return -EINVAL;
+
+ // restart demod
+ ret |= dib7000m_write_word(state, 898, 0x4000);
+ ret |= dib7000m_write_word(state, 898, 0x0000);
+ msleep(45);
+
+ ret |= dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD);
+ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+ ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+ // never achieved a lock with that bandwidth so far - wait for timfreq to update
+ if (state->timf == 0)
+ msleep(200);
+
+ //dump_reg(state);
+ /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */
+ value = (6 << 8) | 0x80;
+ switch (ch->nfft) {
+ case 0: value |= (7 << 12); break;
+ case 1: value |= (9 << 12); break;
+ case 2: value |= (8 << 12); break;
+ }
+ ret |= dib7000m_write_word(state, 26, value);
+
+ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */
+ value = (0 << 4);
+ switch (ch->nfft) {
+ case 0: value |= 0x6; break;
+ case 1: value |= 0x8; break;
+ case 2: value |= 0x7; break;
+ }
+ ret |= dib7000m_write_word(state, 32, value);
+
+ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */
+ value = (0 << 4);
+ switch (ch->nfft) {
+ case 0: value |= 0x6; break;
+ case 1: value |= 0x8; break;
+ case 2: value |= 0x7; break;
+ }
+ ret |= dib7000m_write_word(state, 33, value);
+
+ // we achieved a lock - it's time to update the osc freq
+ if ((dib7000m_read_word(state, 535) >> 6) & 0x1)
+ dib7000m_update_timf_freq(state);
+
+ return ret;
+}
+
+static int dib7000m_init(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *state = demod->demodulator_priv;
+ int ret = 0;
+ u8 o = state->reg_offs;
+
+ dib7000m_set_power_mode(state, DIB7000M_POWER_ALL);
+
+ if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0)
+ dprintk("-E- could not start Slow ADC\n");
+
+ if (state->cfg.dvbt_mode)
+ dib7000m_write_word(state, 1796, 0x0); // select DVB-T output
+
+ if (state->cfg.mobile_mode)
+ ret |= dib7000m_write_word(state, 261 + o, 2);
+ else
+ ret |= dib7000m_write_word(state, 224 + o, 1);
+
+ ret |= dib7000m_write_word(state, 173 + o, 0);
+ ret |= dib7000m_write_word(state, 174 + o, 0);
+ ret |= dib7000m_write_word(state, 175 + o, 0);
+ ret |= dib7000m_write_word(state, 176 + o, 0);
+ ret |= dib7000m_write_word(state, 177 + o, 0);
+ ret |= dib7000m_write_word(state, 178 + o, 0);
+ ret |= dib7000m_write_word(state, 179 + o, 0);
+ ret |= dib7000m_write_word(state, 180 + o, 0);
+
+ // P_corm_thres Lock algorithms configuration
+ ret |= dib7000m_write_word(state, 26, 0x6680);
+
+ // P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on
+ ret |= dib7000m_write_word(state, 170 + o, 0x0410);
+ // P_fft_nb_to_cut
+ ret |= dib7000m_write_word(state, 182 + o, 8192);
+ // P_pha3_thres
+ ret |= dib7000m_write_word(state, 195 + o, 0x0ccd);
+ // P_cti_use_cpe, P_cti_use_prog
+ ret |= dib7000m_write_word(state, 196 + o, 0);
+ // P_cspu_regul, P_cspu_win_cut
+ ret |= dib7000m_write_word(state, 205 + o, 0x200f);
+ // P_adp_regul_cnt
+ ret |= dib7000m_write_word(state, 214 + o, 0x023d);
+ // P_adp_noise_cnt
+ ret |= dib7000m_write_word(state, 215 + o, 0x00a4);
+ // P_adp_regul_ext
+ ret |= dib7000m_write_word(state, 216 + o, 0x00a4);
+ // P_adp_noise_ext
+ ret |= dib7000m_write_word(state, 217 + o, 0x7ff0);
+ // P_adp_fil
+ ret |= dib7000m_write_word(state, 218 + o, 0x3ccc);
+
+ // P_2d_byp_ti_num
+ ret |= dib7000m_write_word(state, 226 + o, 0);
+
+ // P_fec_*
+ ret |= dib7000m_write_word(state, 281 + o, 0x0010);
+ // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+ ret |= dib7000m_write_word(state, 294 + o,0x0062);
+
+ // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ...
+ if(state->cfg.tuner_is_baseband)
+ ret |= dib7000m_write_word(state, 36, 0x0755);
+ else
+ ret |= dib7000m_write_word(state, 36, 0x1f55);
+
+ // auto search configuration
+ ret |= dib7000m_write_word(state, 2, 0x0004);
+ ret |= dib7000m_write_word(state, 3, 0x1000);
+ ret |= dib7000m_write_word(state, 4, 0x0814);
+ ret |= dib7000m_write_word(state, 6, 0x001b);
+ ret |= dib7000m_write_word(state, 7, 0x7740);
+ ret |= dib7000m_write_word(state, 8, 0x005b);
+ ret |= dib7000m_write_word(state, 9, 0x8d80);
+ ret |= dib7000m_write_word(state, 10, 0x01c9);
+ ret |= dib7000m_write_word(state, 11, 0xc380);
+ ret |= dib7000m_write_word(state, 12, 0x0000);
+ ret |= dib7000m_write_word(state, 13, 0x0080);
+ ret |= dib7000m_write_word(state, 14, 0x0000);
+ ret |= dib7000m_write_word(state, 15, 0x0090);
+ ret |= dib7000m_write_word(state, 16, 0x0001);
+ ret |= dib7000m_write_word(state, 17, 0xd4c0);
+ ret |= dib7000m_write_word(state, 263 + o,0x0001);
+
+ // P_divclksel=3 P_divbitsel=1
+ if (state->revision == 0x4000)
+ dib7000m_write_word(state, 909, (3 << 10) | (1 << 6));
+ else
+ dib7000m_write_word(state, 909, (3 << 4) | 1);
+
+ // Tuner IO bank: max drive (14mA)
+ ret |= dib7000m_write_word(state, 912 ,0x2c8a);
+
+ ret |= dib7000m_write_word(state, 1817, 1);
+
+ return ret;
+}
+
+static int dib7000m_sleep(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *st = demod->demodulator_priv;
+ dib7000m_set_output_mode(st, OUTMODE_HIGH_Z);
+ return dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY) |
+ dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) |
+ dib7000m_set_adc_state(st, DIBX000_ADC_OFF);
+}
+
+static int dib7000m_identify(struct dib7000m_state *state)
+{
+ u16 value;
+ if ((value = dib7000m_read_word(state, 896)) != 0x01b3) {
+ dprintk("-E- DiB7000M: wrong Vendor ID (read=0x%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ state->revision = dib7000m_read_word(state, 897);
+ if (state->revision != 0x4000 &&
+ state->revision != 0x4001 &&
+ state->revision != 0x4002) {
+ dprintk("-E- DiB7000M: wrong Device ID (%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ /* protect this driver to be used with 7000PC */
+ if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) {
+ dprintk("-E- DiB7000M: this driver does not work with DiB7000PC\n");
+ return -EREMOTEIO;
+ }
+
+ switch (state->revision) {
+ case 0x4000: dprintk("-I- found DiB7000MA/PA/MB/PB\n"); break;
+ case 0x4001: state->reg_offs = 1; dprintk("-I- found DiB7000HC\n"); break;
+ case 0x4002: state->reg_offs = 1; dprintk("-I- found DiB7000MC\n"); break;
+ }
+
+ return 0;
+}
+
+
+static int dib7000m_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ u16 tps = dib7000m_read_word(state,480);
+
+ fep->inversion = INVERSION_AUTO;
+
+ fep->u.ofdm.bandwidth = state->current_bandwidth;
+
+ switch ((tps >> 8) & 0x3) {
+ case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+ case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+ /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
+ }
+
+ switch (tps & 0x3) {
+ case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+ case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+ case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+ case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+ }
+
+ switch ((tps >> 14) & 0x3) {
+ case 0: fep->u.ofdm.constellation = QPSK; break;
+ case 1: fep->u.ofdm.constellation = QAM_16; break;
+ case 2:
+ default: fep->u.ofdm.constellation = QAM_64; break;
+ }
+
+ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+ /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */
+
+ fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ switch ((tps >> 5) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+
+ }
+
+ switch ((tps >> 2) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+ }
+
+ /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */
+
+ return 0;
+}
+
+static int dib7000m_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ struct dibx000_ofdm_channel ch;
+
+ INIT_OFDM_CHANNEL(&ch);
+ FEP2DIB(fep,&ch);
+
+ state->current_bandwidth = fep->u.ofdm.bandwidth;
+ dib7000m_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, fep);
+
+ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
+ fep->u.ofdm.constellation == QAM_AUTO ||
+ fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ int i = 800, found;
+
+ dib7000m_autosearch_start(fe, &ch);
+ do {
+ msleep(1);
+ found = dib7000m_autosearch_is_irq(fe);
+ } while (found == 0 && i--);
+
+ dprintk("autosearch returns: %d\n",found);
+ if (found == 0 || found == 1)
+ return 0; // no channel found
+
+ dib7000m_get_frontend(fe, fep);
+ FEP2DIB(fep, &ch);
+ }
+
+ /* make this a config parameter */
+ dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+ return dib7000m_tune(fe, &ch);
+}
+
+static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ u16 lock = dib7000m_read_word(state, 535);
+
+ *stat = 0;
+
+ if (lock & 0x8000)
+ *stat |= FE_HAS_SIGNAL;
+ if (lock & 0x3000)
+ *stat |= FE_HAS_CARRIER;
+ if (lock & 0x0100)
+ *stat |= FE_HAS_VITERBI;
+ if (lock & 0x0010)
+ *stat |= FE_HAS_SYNC;
+ if (lock & 0x0008)
+ *stat |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527);
+ return 0;
+}
+
+static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ *unc = dib7000m_read_word(state, 534);
+ return 0;
+}
+
+static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dib7000m_state *state = fe->demodulator_priv;
+ u16 val = dib7000m_read_word(state, 390);
+ *strength = 65535 - val;
+ return 0;
+}
+
+static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ *snr = 0x0000;
+ return 0;
+}
+
+static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void dib7000m_release(struct dvb_frontend *demod)
+{
+ struct dib7000m_state *st = demod->demodulator_priv;
+ dibx000_exit_i2c_master(&st->i2c_master);
+ kfree(st);
+}
+
+struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+{
+ struct dib7000m_state *st = demod->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+EXPORT_SYMBOL(dib7000m_get_i2c_master);
+
+int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[])
+{
+ struct dib7000m_state st = { .i2c_adap = i2c };
+ int k = 0;
+ u8 new_addr = 0;
+
+ for (k = no_of_demods-1; k >= 0; k--) {
+ st.cfg = cfg[k];
+
+ /* designated i2c address */
+ new_addr = (0x40 + k) << 1;
+ st.i2c_addr = new_addr;
+ if (dib7000m_identify(&st) != 0) {
+ st.i2c_addr = default_addr;
+ if (dib7000m_identify(&st) != 0) {
+ dprintk("DiB7000M #%d: not identified\n", k);
+ return -EIO;
+ }
+ }
+
+ /* start diversity to pull_down div_str - just for i2c-enumeration */
+ dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY);
+
+ dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output
+
+ /* set new i2c address and force divstart */
+ dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2);
+
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
+ }
+
+ for (k = 0; k < no_of_demods; k++) {
+ st.cfg = cfg[k];
+ st.i2c_addr = (0x40 + k) << 1;
+
+ // unforce divstr
+ dib7000m_write_word(&st,1794, st.i2c_addr << 2);
+
+ /* deactivate div - it was just for i2c-enumeration */
+ dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dib7000m_i2c_enumeration);
+
+static struct dvb_frontend_ops dib7000m_ops;
+struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg)
+{
+ struct dvb_frontend *demod;
+ struct dib7000m_state *st;
+ st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+
+ memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config));
+ st->i2c_adap = i2c_adap;
+ st->i2c_addr = i2c_addr;
+
+ demod = &st->demod;
+ demod->demodulator_priv = st;
+ memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops));
+
+ if (dib7000m_identify(st) != 0)
+ goto error;
+
+ if (st->revision == 0x4000)
+ dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr);
+ else
+ dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr);
+
+ dib7000m_demod_reset(st);
+
+ return demod;
+
+error:
+ kfree(st);
+ return NULL;
+}
+EXPORT_SYMBOL(dib7000m_attach);
+
+static struct dvb_frontend_ops dib7000m_ops = {
+ .info = {
+ .name = "DiBcom 7000MA/MB/PA/PB/MC",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dib7000m_release,
+
+ .init = dib7000m_init,
+ .sleep = dib7000m_sleep,
+
+ .set_frontend = dib7000m_set_frontend,
+ .get_tune_settings = dib7000m_fe_get_tune_settings,
+ .get_frontend = dib7000m_get_frontend,
+
+ .read_status = dib7000m_read_status,
+ .read_ber = dib7000m_read_ber,
+ .read_signal_strength = dib7000m_read_signal_strength,
+ .read_snr = dib7000m_read_snr,
+ .read_ucblocks = dib7000m_read_unc_blocks,
+};
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h
new file mode 100644
index 00000000000..597e9cc2da6
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000m.h
@@ -0,0 +1,51 @@
+#ifndef DIB7000M_H
+#define DIB7000M_H
+
+#include "dibx000_common.h"
+
+struct dib7000m_config {
+ u8 dvbt_mode;
+ u8 output_mpeg2_in_188_bytes;
+ u8 hostbus_diversity;
+ u8 tuner_is_baseband;
+ u8 mobile_mode;
+ int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+ u8 agc_config_count;
+ struct dibx000_agc_config *agc;
+
+ struct dibx000_bandwidth_config *bw;
+
+#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB7000M_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB7000M_GPIO_PWM_POS0(v) ((v & 0xf) << 12)
+#define DIB7000M_GPIO_PWM_POS1(v) ((v & 0xf) << 8 )
+#define DIB7000M_GPIO_PWM_POS2(v) ((v & 0xf) << 4 )
+#define DIB7000M_GPIO_PWM_POS3(v) (v & 0xf)
+#define DIB7000M_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+
+ u16 pwm_freq_div;
+
+ u8 quartz_direct;
+
+ u8 input_clk_is_div_2;
+
+ int (*agc_control) (struct dvb_frontend *, u8 before);
+};
+
+#define DEFAULT_DIB7000M_I2C_ADDRESS 18
+
+extern struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg);
+extern struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
+
+/* TODO
+extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val);
+extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod);
+extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff);
+extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod);
+*/
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
new file mode 100644
index 00000000000..0349a4b5da3
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -0,0 +1,1019 @@
+/*
+ * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC).
+ *
+ * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "dib7000p.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P:"); printk(args); } } while (0)
+
+struct dib7000p_state {
+ struct dvb_frontend demod;
+ struct dib7000p_config cfg;
+
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+
+ struct dibx000_i2c_master i2c_master;
+
+ u16 wbd_ref;
+
+ u8 current_band;
+ fe_bandwidth_t current_bandwidth;
+ struct dibx000_agc_config *current_agc;
+ u32 timf;
+
+ u16 gpio_dir;
+ u16 gpio_val;
+};
+
+enum dib7000p_power_mode {
+ DIB7000P_POWER_ALL = 0,
+ DIB7000P_POWER_INTERFACE_ONLY,
+};
+
+static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg)
+{
+ u8 wb[2] = { reg >> 8, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
+
+ if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+ dprintk("i2c read error on %d\n",reg);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val)
+{
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
+{
+ int ret = 0;
+ u16 outreg, fifo_threshold, smo_mode;
+
+ outreg = 0;
+ fifo_threshold = 1792;
+ smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1);
+
+ dprintk("-I- Setting output mode for demod %p to %d\n",
+ &state->demod, mode);
+
+ switch (mode) {
+ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
+ outreg = (1 << 10); /* 0x0400 */
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
+ outreg = (1 << 10) | (1 << 6); /* 0x0440 */
+ break;
+ case OUTMODE_MPEG2_SERIAL: // STBs with serial input
+ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */
+ break;
+ case OUTMODE_DIVERSITY:
+ if (state->cfg.hostbus_diversity)
+ outreg = (1 << 10) | (4 << 6); /* 0x0500 */
+ else
+ outreg = (1 << 11);
+ break;
+ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
+ smo_mode |= (3 << 1);
+ fifo_threshold = 512;
+ outreg = (1 << 10) | (5 << 6);
+ break;
+ case OUTMODE_HIGH_Z: // disable
+ outreg = 0;
+ break;
+ default:
+ dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+ break;
+ }
+
+ if (state->cfg.output_mpeg2_in_188_bytes)
+ smo_mode |= (1 << 5) ;
+
+ ret |= dib7000p_write_word(state, 235, smo_mode);
+ ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */
+ ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */
+
+ return ret;
+}
+
+static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode)
+{
+ /* by default everything is powered off */
+ u16 reg_774 = 0xffff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003,
+ reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff);
+
+ /* now, depending on the requested mode, we power on */
+ switch (mode) {
+ /* power up everything in the demod */
+ case DIB7000P_POWER_ALL:
+ reg_774 = 0x0000; reg_775 = 0x0000; reg_776 = 0x0; reg_899 = 0x0; reg_1280 &= 0x01ff;
+ break;
+ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */
+ case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */
+ reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10));
+ break;
+/* TODO following stuff is just converted from the dib7000-driver - check when is used what */
+ }
+
+ dib7000p_write_word(state, 774, reg_774);
+ dib7000p_write_word(state, 775, reg_775);
+ dib7000p_write_word(state, 776, reg_776);
+ dib7000p_write_word(state, 899, reg_899);
+ dib7000p_write_word(state, 1280, reg_1280);
+
+ return 0;
+}
+
+static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no)
+{
+ u16 reg_908 = dib7000p_read_word(state, 908),
+ reg_909 = dib7000p_read_word(state, 909);
+
+ switch (no) {
+ case DIBX000_SLOW_ADC_ON:
+ reg_909 |= (1 << 1) | (1 << 0);
+ dib7000p_write_word(state, 909, reg_909);
+ reg_909 &= ~(1 << 1);
+ break;
+
+ case DIBX000_SLOW_ADC_OFF:
+ reg_909 |= (1 << 1) | (1 << 0);
+ break;
+
+ case DIBX000_ADC_ON:
+ reg_908 &= 0x0fff;
+ reg_909 &= 0x0003;
+ break;
+
+ case DIBX000_ADC_OFF: // leave the VBG voltage on
+ reg_908 |= (1 << 14) | (1 << 13) | (1 << 12);
+ reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2);
+ break;
+
+ case DIBX000_VBG_ENABLE:
+ reg_908 &= ~(1 << 15);
+ break;
+
+ case DIBX000_VBG_DISABLE:
+ reg_908 |= (1 << 15);
+ break;
+
+ default:
+ break;
+ }
+
+// dprintk("908: %x, 909: %x\n", reg_908, reg_909);
+
+ dib7000p_write_word(state, 908, reg_908);
+ dib7000p_write_word(state, 909, reg_909);
+}
+
+static int dib7000p_set_bandwidth(struct dvb_frontend *demod, u8 BW_Idx)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ u32 timf;
+
+ // store the current bandwidth for later use
+ state->current_bandwidth = BW_Idx;
+
+ if (state->timf == 0) {
+ dprintk("-D- Using default timf\n");
+ timf = state->cfg.bw->timf;
+ } else {
+ dprintk("-D- Using updated timf\n");
+ timf = state->timf;
+ }
+
+ timf = timf * (BW_INDEX_TO_KHZ(BW_Idx) / 100) / 80;
+
+ dprintk("timf: %d\n",timf);
+
+ dib7000p_write_word(state, 23, (timf >> 16) & 0xffff);
+ dib7000p_write_word(state, 24, (timf ) & 0xffff);
+
+ return 0;
+}
+
+static int dib7000p_sad_calib(struct dib7000p_state *state)
+{
+/* internal */
+// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth
+ dib7000p_write_word(state, 73, (0 << 1) | (0 << 0));
+ dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096
+
+ /* do the calibration */
+ dib7000p_write_word(state, 73, (1 << 0));
+ dib7000p_write_word(state, 73, (0 << 0));
+
+ msleep(1);
+
+ return 0;
+}
+
+static void dib7000p_reset_pll(struct dib7000p_state *state)
+{
+ struct dibx000_bandwidth_config *bw = &state->cfg.bw[0];
+
+ dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset);
+ dib7000p_write_word(state, 900, ((bw->pll_ratio & 0x3f) << 9) | (bw->pll_bypass << 15) | (bw->modulo << 7) | (bw->ADClkSrc << 6) |
+ (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0));
+
+ dib7000p_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff);
+ dib7000p_write_word(state, 19, (bw->internal*1000 ) & 0xffff);
+ dib7000p_write_word(state, 21, (bw->ifreq >> 16) & 0xffff);
+ dib7000p_write_word(state, 22, (bw->ifreq ) & 0xffff);
+
+ dib7000p_write_word(state, 72, bw->sad_cfg);
+}
+
+static int dib7000p_reset_gpio(struct dib7000p_state *st)
+{
+ /* reset the GPIOs */
+ dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",st->gpio_dir, st->gpio_val,st->cfg.gpio_pwm_pos);
+
+ dib7000p_write_word(st, 1029, st->gpio_dir);
+ dib7000p_write_word(st, 1030, st->gpio_val);
+
+ /* TODO 1031 is P_gpio_od */
+
+ dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos);
+
+ dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div);
+ return 0;
+}
+
+static int dib7000p_demod_reset(struct dib7000p_state *state)
+{
+ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+
+ dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE);
+
+ /* restart all parts */
+ dib7000p_write_word(state, 770, 0xffff);
+ dib7000p_write_word(state, 771, 0xffff);
+ dib7000p_write_word(state, 772, 0x001f);
+ dib7000p_write_word(state, 898, 0x0003);
+ /* except i2c, sdio, gpio - control interfaces */
+ dib7000p_write_word(state, 1280, 0x01fc - ((1 << 7) | (1 << 6) | (1 << 5)) );
+
+ dib7000p_write_word(state, 770, 0);
+ dib7000p_write_word(state, 771, 0);
+ dib7000p_write_word(state, 772, 0);
+ dib7000p_write_word(state, 898, 0);
+ dib7000p_write_word(state, 1280, 0);
+
+ /* default */
+ dib7000p_reset_pll(state);
+
+ if (dib7000p_reset_gpio(state) != 0)
+ dprintk("-E- GPIO reset was not successful.\n");
+
+ if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0)
+ dprintk("-E- OUTPUT_MODE could not be resetted.\n");
+
+ /* unforce divstr regardless whether i2c enumeration was done or not */
+ dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1) );
+
+ dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
+
+ return 0;
+}
+
+static void dib7000p_restart_agc(struct dib7000p_state *state)
+{
+ // P_restart_iqc & P_restart_agc
+ dib7000p_write_word(state, 770, 0x0c00);
+ dib7000p_write_word(state, 770, 0x0000);
+}
+
+static void dib7000p_update_lna(struct dib7000p_state *state)
+{
+ int i;
+ u16 dyn_gain;
+
+ // when there is no LNA to program return immediatly
+ if (state->cfg.update_lna == NULL)
+ return;
+
+ for (i = 0; i < 5; i++) {
+ // read dyn_gain here (because it is demod-dependent and not tuner)
+ dyn_gain = dib7000p_read_word(state, 394);
+
+ if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed
+ dib7000p_restart_agc(state);
+ msleep(5);
+ } else
+ break;
+ }
+}
+
+static void dib7000p_pll_clk_cfg(struct dib7000p_state *state)
+{
+ u16 tmp = 0;
+ tmp = dib7000p_read_word(state, 903);
+ dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll
+ tmp = dib7000p_read_word(state, 900);
+ dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock
+}
+
+static void dib7000p_update_timf_freq(struct dib7000p_state *state)
+{
+ u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428);
+ state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100);
+ dib7000p_write_word(state, 23, (u16) (timf >> 16));
+ dib7000p_write_word(state, 24, (u16) (timf & 0xffff));
+ dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf);
+}
+
+static void dib7000p_set_channel(struct dib7000p_state *state, struct dibx000_ofdm_channel *ch, u8 seq)
+{
+ u16 tmp, est[4]; // reg_26, reg_32, reg_33, reg_187, reg_188, reg_189, reg_190, reg_207, reg_208;
+
+ /* nfft, guard, qam, alpha */
+ dib7000p_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha));
+ dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */
+
+ /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */
+ tmp = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1);
+ if (ch->vit_hrch == 0 || ch->vit_select_hp == 1)
+ tmp |= (ch->vit_code_rate_hp << 1);
+ else
+ tmp |= (ch->vit_code_rate_lp << 1);
+ dib7000p_write_word(state, 208, tmp);
+
+ /* P_dvsy_sync_wait */
+ switch (ch->nfft) {
+ case 1: tmp = 256; break;
+ case 2: tmp = 128; break;
+ case 0:
+ default: tmp = 64; break;
+ }
+ tmp *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin
+ tmp <<= 4;
+
+ /* deactive the possibility of diversity reception if extended interleave */
+ /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
+ if (ch->intlv_native || ch->nfft == 1)
+ tmp |= (1 << 2) | (2 << 0);
+ dib7000p_write_word(state, 207, tmp);
+
+ dib7000p_write_word(state, 26, 0x6680); // timf(6xxx)
+ dib7000p_write_word(state, 29, 0x1273); // isi inh1273 on1073
+ dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3)
+ dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5)
+
+ /* channel estimation fine configuration */
+ switch (ch->nqam) {
+ case 2:
+ est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */
+ est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */
+ break;
+ case 1:
+ est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */
+ est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */
+ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */
+ est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */
+ break;
+ default:
+ est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */
+ est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */
+ est[2] = 0x0333; /* P_adp_regul_ext 0.1 */
+ est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */
+ break;
+ }
+ for (tmp = 0; tmp < 4; tmp++)
+ dib7000p_write_word(state, 187 + tmp, est[tmp]);
+
+ // set power-up level: interf+analog+AGC
+ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+ dib7000p_set_adc_state(state, DIBX000_ADC_ON);
+ dib7000p_pll_clk_cfg(state);
+ msleep(7);
+
+ // AGC initialization
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 1);
+
+ dib7000p_restart_agc(state);
+
+ // wait AGC rough lock time
+ msleep(5);
+
+ dib7000p_update_lna(state);
+
+ // wait AGC accurate lock time
+ msleep(7);
+ if (state->cfg.agc_control)
+ state->cfg.agc_control(&state->demod, 0);
+}
+
+static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ struct dibx000_ofdm_channel auto_ch;
+ u32 value;
+
+ INIT_OFDM_CHANNEL(&auto_ch);
+ auto_ch.RF_kHz = ch->RF_kHz;
+ auto_ch.Bw = ch->Bw;
+ auto_ch.nqam = 2;
+ auto_ch.guard = 0;
+ auto_ch.nfft = 1;
+ auto_ch.vit_alpha = 1;
+ auto_ch.vit_select_hp = 1;
+ auto_ch.vit_code_rate_hp = 2;
+ auto_ch.vit_code_rate_lp = 3;
+ auto_ch.vit_hrch = 0;
+ auto_ch.intlv_native = 1;
+
+ dib7000p_set_channel(state, &auto_ch, 7);
+
+ // always use the setting for 8MHz here lock_time for 7,6 MHz are longer
+ value = 30 * state->cfg.bw->internal;
+ dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time
+ dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time
+ value = 100 * state->cfg.bw->internal;
+ dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time
+ dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time
+ value = 500 * state->cfg.bw->internal;
+ dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time
+ dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time
+
+ value = dib7000p_read_word(state, 0);
+ dib7000p_write_word(state, 0, (1 << 9) | value);
+ dib7000p_read_word(state, 1284);
+ dib7000p_write_word(state, 0, (u16) value);
+
+ return 0;
+}
+
+static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ u16 irq_pending = dib7000p_read_word(state, 1284);
+
+ if (irq_pending & 0x1) // failed
+ return 1;
+
+ if (irq_pending & 0x2) // succeeded
+ return 2;
+
+ return 0; // still pending
+}
+
+static int dib7000p_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ u16 tmp = 0;
+
+ if (ch != NULL)
+ dib7000p_set_channel(state, ch, 0);
+ else
+ return -EINVAL;
+
+ // restart demod
+ dib7000p_write_word(state, 770, 0x4000);
+ dib7000p_write_word(state, 770, 0x0000);
+ msleep(45);
+
+ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */
+ dib7000p_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3));
+
+ // never achieved a lock with that bandwidth so far - wait for osc-freq to update
+ if (state->timf == 0)
+ msleep(200);
+
+ /* offset loop parameters */
+
+ /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */
+ tmp = (6 << 8) | 0x80;
+ switch (ch->nfft) {
+ case 0: tmp |= (7 << 12); break;
+ case 1: tmp |= (9 << 12); break;
+ case 2: tmp |= (8 << 12); break;
+ }
+ dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */
+
+ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */
+ tmp = (0 << 4);
+ switch (ch->nfft) {
+ case 0: tmp |= 0x6; break;
+ case 1: tmp |= 0x8; break;
+ case 2: tmp |= 0x7; break;
+ }
+ dib7000p_write_word(state, 32, tmp);
+
+ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */
+ tmp = (0 << 4);
+ switch (ch->nfft) {
+ case 0: tmp |= 0x6; break;
+ case 1: tmp |= 0x8; break;
+ case 2: tmp |= 0x7; break;
+ }
+ dib7000p_write_word(state, 33, tmp);
+
+ tmp = dib7000p_read_word(state,509);
+ if (!((tmp >> 6) & 0x1)) {
+ /* restart the fec */
+ tmp = dib7000p_read_word(state,771);
+ dib7000p_write_word(state, 771, tmp | (1 << 1));
+ dib7000p_write_word(state, 771, tmp);
+ msleep(10);
+ tmp = dib7000p_read_word(state,509);
+ }
+
+ // we achieved a lock - it's time to update the osc freq
+ if ((tmp >> 6) & 0x1)
+ dib7000p_update_timf_freq(state);
+
+ return 0;
+}
+
+static int dib7000p_init(struct dvb_frontend *demod)
+{
+ struct dibx000_agc_config *agc;
+ struct dib7000p_state *state = demod->demodulator_priv;
+ int ret = 0;
+
+ // Demodulator default configuration
+ agc = state->cfg.agc;
+
+ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL);
+ dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON);
+
+ /* AGC */
+ ret |= dib7000p_write_word(state, 75 , agc->setup );
+ ret |= dib7000p_write_word(state, 76 , agc->inv_gain );
+ ret |= dib7000p_write_word(state, 77 , agc->time_stabiliz );
+ ret |= dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock);
+
+ // Demod AGC loop configuration
+ ret |= dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp);
+ ret |= dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp);
+
+ /* AGC continued */
+ dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n",
+ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel);
+
+ if (state->wbd_ref != 0)
+ ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref);
+ else
+ ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref);
+
+ ret |= dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) );
+
+ ret |= dib7000p_write_word(state, 107, agc->agc1_max);
+ ret |= dib7000p_write_word(state, 108, agc->agc1_min);
+ ret |= dib7000p_write_word(state, 109, agc->agc2_max);
+ ret |= dib7000p_write_word(state, 110, agc->agc2_min);
+ ret |= dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2 );
+ ret |= dib7000p_write_word(state, 112, agc->agc1_pt3);
+ ret |= dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+ ret |= dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ ret |= dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+ /* disable power smoothing */
+ ret |= dib7000p_write_word(state, 145, 0);
+ ret |= dib7000p_write_word(state, 146, 0);
+ ret |= dib7000p_write_word(state, 147, 0);
+ ret |= dib7000p_write_word(state, 148, 0);
+ ret |= dib7000p_write_word(state, 149, 0);
+ ret |= dib7000p_write_word(state, 150, 0);
+ ret |= dib7000p_write_word(state, 151, 0);
+ ret |= dib7000p_write_word(state, 152, 0);
+
+ // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26
+ ret |= dib7000p_write_word(state, 26 ,0x6680);
+
+ // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16
+ ret |= dib7000p_write_word(state, 142,0x0410);
+ // P_fft_freq_dir=1, P_fft_nb_to_cut=0
+ ret |= dib7000p_write_word(state, 154,1 << 13);
+ // P_pha3_thres, default 0x3000
+ ret |= dib7000p_write_word(state, 168,0x0ccd);
+ // P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010
+ //ret |= dib7000p_write_word(state, 169,0x0010);
+ // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005
+ ret |= dib7000p_write_word(state, 183,0x200f);
+ // P_adp_regul_cnt=573, default: 410
+ ret |= dib7000p_write_word(state, 187,0x023d);
+ // P_adp_noise_cnt=
+ ret |= dib7000p_write_word(state, 188,0x00a4);
+ // P_adp_regul_ext
+ ret |= dib7000p_write_word(state, 189,0x00a4);
+ // P_adp_noise_ext
+ ret |= dib7000p_write_word(state, 190,0x7ff0);
+ // P_adp_fil
+ ret |= dib7000p_write_word(state, 191,0x3ccc);
+
+ ret |= dib7000p_write_word(state, 222,0x0010);
+ // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard
+ ret |= dib7000p_write_word(state, 235,0x0062);
+
+ // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ...
+ if(state->cfg.tuner_is_baseband)
+ ret |= dib7000p_write_word(state, 36,0x0755);
+ else
+ ret |= dib7000p_write_word(state, 36,0x1f55);
+
+ // auto search configuration
+ ret |= dib7000p_write_word(state, 2 ,0x0004);
+ ret |= dib7000p_write_word(state, 3 ,0x1000);
+
+ /* Equal Lock */
+ ret |= dib7000p_write_word(state, 4 ,0x0814);
+
+ ret |= dib7000p_write_word(state, 6 ,0x001b);
+ ret |= dib7000p_write_word(state, 7 ,0x7740);
+ ret |= dib7000p_write_word(state, 8 ,0x005b);
+ ret |= dib7000p_write_word(state, 9 ,0x8d80);
+ ret |= dib7000p_write_word(state, 10 ,0x01c9);
+ ret |= dib7000p_write_word(state, 11 ,0xc380);
+ ret |= dib7000p_write_word(state, 12 ,0x0000);
+ ret |= dib7000p_write_word(state, 13 ,0x0080);
+ ret |= dib7000p_write_word(state, 14 ,0x0000);
+ ret |= dib7000p_write_word(state, 15 ,0x0090);
+ ret |= dib7000p_write_word(state, 16 ,0x0001);
+ ret |= dib7000p_write_word(state, 17 ,0xd4c0);
+
+ // P_clk_cfg1
+ ret |= dib7000p_write_word(state, 901, 0x0006);
+
+ // P_divclksel=3 P_divbitsel=1
+ ret |= dib7000p_write_word(state, 902, (3 << 10) | (1 << 6));
+
+ // Tuner IO bank: max drive (14mA) + divout pads max drive
+ ret |= dib7000p_write_word(state, 905, 0x2c8e);
+
+ ret |= dib7000p_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+ dib7000p_sad_calib(state);
+
+ return ret;
+}
+
+static int dib7000p_sleep(struct dvb_frontend *demod)
+{
+ struct dib7000p_state *state = demod->demodulator_priv;
+ return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY);
+}
+
+static int dib7000p_identify(struct dib7000p_state *st)
+{
+ u16 value;
+ dprintk("-I- DiB7000PC: checking demod on I2C address: %d (%x)\n",
+ st->i2c_addr, st->i2c_addr);
+
+ if ((value = dib7000p_read_word(st, 768)) != 0x01b3) {
+ dprintk("-E- DiB7000PC: wrong Vendor ID (read=0x%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ if ((value = dib7000p_read_word(st, 769)) != 0x4000) {
+ dprintk("-E- DiB7000PC: wrong Device ID (%x)\n",value);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+
+static int dib7000p_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 tps = dib7000p_read_word(state,463);
+
+ fep->inversion = INVERSION_AUTO;
+
+ fep->u.ofdm.bandwidth = state->current_bandwidth;
+
+ switch ((tps >> 8) & 0x3) {
+ case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+ case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+ /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */
+ }
+
+ switch (tps & 0x3) {
+ case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+ case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+ case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+ case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+ }
+
+ switch ((tps >> 14) & 0x3) {
+ case 0: fep->u.ofdm.constellation = QPSK; break;
+ case 1: fep->u.ofdm.constellation = QAM_16; break;
+ case 2:
+ default: fep->u.ofdm.constellation = QAM_64; break;
+ }
+
+ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+ /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */
+
+ fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ switch ((tps >> 5) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+
+ }
+
+ switch ((tps >> 2) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+ }
+
+ /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */
+
+ return 0;
+}
+
+static int dib7000p_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ struct dibx000_ofdm_channel ch;
+
+ INIT_OFDM_CHANNEL(&ch);
+ FEP2DIB(fep,&ch);
+
+ state->current_bandwidth = fep->u.ofdm.bandwidth;
+ dib7000p_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe, fep);
+
+ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
+ fep->u.ofdm.constellation == QAM_AUTO ||
+ fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ int i = 800, found;
+
+ dib7000p_autosearch_start(fe, &ch);
+ do {
+ msleep(1);
+ found = dib7000p_autosearch_is_irq(fe);
+ } while (found == 0 && i--);
+
+ dprintk("autosearch returns: %d\n",found);
+ if (found == 0 || found == 1)
+ return 0; // no channel found
+
+ dib7000p_get_frontend(fe, fep);
+ FEP2DIB(fep, &ch);
+ }
+
+ /* make this a config parameter */
+ dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+ return dib7000p_tune(fe, &ch);
+}
+
+static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 lock = dib7000p_read_word(state, 509);
+
+ *stat = 0;
+
+ if (lock & 0x8000)
+ *stat |= FE_HAS_SIGNAL;
+ if (lock & 0x3000)
+ *stat |= FE_HAS_CARRIER;
+ if (lock & 0x0100)
+ *stat |= FE_HAS_VITERBI;
+ if (lock & 0x0010)
+ *stat |= FE_HAS_SYNC;
+ if (lock & 0x0008)
+ *stat |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int dib7000p_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501);
+ return 0;
+}
+
+static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ *unc = dib7000p_read_word(state, 506);
+ return 0;
+}
+
+static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct dib7000p_state *state = fe->demodulator_priv;
+ u16 val = dib7000p_read_word(state, 394);
+ *strength = 65535 - val;
+ return 0;
+}
+
+static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ *snr = 0x0000;
+ return 0;
+}
+
+static int dib7000p_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static void dib7000p_release(struct dvb_frontend *demod)
+{
+ struct dib7000p_state *st = demod->demodulator_priv;
+ dibx000_exit_i2c_master(&st->i2c_master);
+ kfree(st);
+}
+
+int dib7000pc_detection(struct i2c_adapter *i2c_adap)
+{
+ u8 tx[2], rx[2];
+ struct i2c_msg msg[2] = {
+ { .addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2 },
+ { .addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2 },
+ };
+
+ tx[0] = 0x03;
+ tx[1] = 0x00;
+
+ if (i2c_transfer(i2c_adap, msg, 2) == 2)
+ if (rx[0] == 0x01 && rx[1] == 0xb3) {
+ dprintk("-D- DiB7000PC detected\n");
+ return 1;
+ }
+
+ msg[0].addr = msg[1].addr = 0x40;
+
+ if (i2c_transfer(i2c_adap, msg, 2) == 2)
+ if (rx[0] == 0x01 && rx[1] == 0xb3) {
+ dprintk("-D- DiB7000PC detected\n");
+ return 1;
+ }
+
+ dprintk("-D- DiB7000PC not detected\n");
+ return 0;
+}
+EXPORT_SYMBOL(dib7000pc_detection);
+
+struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating)
+{
+ struct dib7000p_state *st = demod->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating);
+}
+EXPORT_SYMBOL(dib7000p_get_i2c_master);
+
+int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
+{
+ struct dib7000p_state st = { .i2c_adap = i2c };
+ int k = 0;
+ u8 new_addr = 0;
+
+ for (k = no_of_demods-1; k >= 0; k--) {
+ st.cfg = cfg[k];
+
+ /* designated i2c address */
+ new_addr = (0x40 + k) << 1;
+ st.i2c_addr = new_addr;
+ if (dib7000p_identify(&st) != 0) {
+ st.i2c_addr = default_addr;
+ if (dib7000p_identify(&st) != 0) {
+ dprintk("DiB7000P #%d: not identified\n", k);
+ return -EIO;
+ }
+ }
+
+ /* start diversity to pull_down div_str - just for i2c-enumeration */
+ dib7000p_set_output_mode(&st, OUTMODE_DIVERSITY);
+
+ /* set new i2c address and force divstart */
+ dib7000p_write_word(&st, 1285, (new_addr << 2) | 0x2);
+
+ dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr);
+ }
+
+ for (k = 0; k < no_of_demods; k++) {
+ st.cfg = cfg[k];
+ st.i2c_addr = (0x40 + k) << 1;
+
+ // unforce divstr
+ dib7000p_write_word(&st, 1285, st.i2c_addr << 2);
+
+ /* deactivate div - it was just for i2c-enumeration */
+ dib7000p_set_output_mode(&st, OUTMODE_HIGH_Z);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dib7000p_i2c_enumeration);
+
+static struct dvb_frontend_ops dib7000p_ops;
+struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg)
+{
+ struct dvb_frontend *demod;
+ struct dib7000p_state *st;
+ st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL);
+ if (st == NULL)
+ return NULL;
+
+ memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config));
+ st->i2c_adap = i2c_adap;
+ st->i2c_addr = i2c_addr;
+ st->gpio_val = cfg->gpio_val;
+ st->gpio_dir = cfg->gpio_dir;
+
+ demod = &st->demod;
+ demod->demodulator_priv = st;
+ memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
+
+ if (dib7000p_identify(st) != 0)
+ goto error;
+
+ dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
+
+ dib7000p_demod_reset(st);
+
+ return demod;
+
+error:
+ kfree(st);
+ return NULL;
+}
+EXPORT_SYMBOL(dib7000p_attach);
+
+static struct dvb_frontend_ops dib7000p_ops = {
+ .info = {
+ .name = "DiBcom 7000PC",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dib7000p_release,
+
+ .init = dib7000p_init,
+ .sleep = dib7000p_sleep,
+
+ .set_frontend = dib7000p_set_frontend,
+ .get_tune_settings = dib7000p_fe_get_tune_settings,
+ .get_frontend = dib7000p_get_frontend,
+
+ .read_status = dib7000p_read_status,
+ .read_ber = dib7000p_read_ber,
+ .read_signal_strength = dib7000p_read_signal_strength,
+ .read_snr = dib7000p_read_snr,
+ .read_ucblocks = dib7000p_read_unc_blocks,
+};
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
new file mode 100644
index 00000000000..79465cf1ace
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib7000p.h
@@ -0,0 +1,46 @@
+#ifndef DIB7000P_H
+#define DIB7000P_H
+
+#include "dibx000_common.h"
+
+struct dib7000p_config {
+ u8 output_mpeg2_in_188_bytes;
+ u8 hostbus_diversity;
+ u8 tuner_is_baseband;
+ int (*update_lna) (struct dvb_frontend *, u16 agc_global);
+
+ struct dibx000_agc_config *agc;
+ struct dibx000_bandwidth_config *bw;
+
+#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff
+ u16 gpio_dir;
+#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000
+ u16 gpio_val;
+#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12)
+#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 )
+#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 )
+#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf)
+#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff
+ u16 gpio_pwm_pos;
+
+ u16 pwm_freq_div;
+
+ u8 quartz_direct;
+
+ int (*agc_control) (struct dvb_frontend *, u8 before);
+};
+
+#define DEFAULT_DIB7000P_I2C_ADDRESS 18
+
+extern struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg);
+extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
+extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
+
+/* TODO
+extern INT dib7000p_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val);
+extern INT dib7000p_enable_vbg_voltage(struct dibDemod *demod);
+extern void dib7000p_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff);
+extern USHORT dib7000p_get_current_agc_global(struct dibDemod *demod);
+*/
+
+#endif
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
new file mode 100644
index 00000000000..a18c8f45a2e
--- /dev/null
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -0,0 +1,152 @@
+#include <linux/i2c.h>
+
+#include "dibx000_common.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); } } while (0)
+
+static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
+{
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = mst->i2c_addr, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+
+static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf)
+{
+ if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) {
+ dprintk("selecting interface: %d\n",intf);
+ mst->selected_interface = intf;
+ return dibx000_write_word(mst, mst->base_reg + 4, intf);
+ }
+ return 0;
+}
+
+static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 addr, int onoff)
+{
+ u16 val;
+
+
+ if (onoff)
+ val = addr << 8; // bit 7 = use master or not, if 0, the gate is open
+ else
+ val = 1 << 7;
+
+ if (mst->device_rev > DIB7000)
+ val <<= 1;
+
+ tx[0] = (((mst->base_reg + 1) >> 8) & 0xff);
+ tx[1] = ( (mst->base_reg + 1) & 0xff);
+ tx[2] = val >> 8;
+ tx[3] = val & 0xff;
+
+ return 0;
+}
+
+static u32 dibx000_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ struct i2c_msg m[2 + num];
+ u8 tx_open[4], tx_close[4];
+
+ memset(m,0, sizeof(struct i2c_msg) * (2 + num));
+
+ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER);
+
+ dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1);
+ m[0].addr = mst->i2c_addr;
+ m[0].buf = tx_open;
+ m[0].len = 4;
+
+ memcpy(&m[1], msg, sizeof(struct i2c_msg) * num);
+
+ dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0);
+ m[num+1].addr = mst->i2c_addr;
+ m[num+1].buf = tx_close;
+ m[num+1].len = 4;
+
+ return i2c_transfer(mst->i2c_adap, m, 2+num) == 2 + num ? num : -EIO;
+}
+
+static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
+ .master_xfer = dibx000_i2c_gated_tuner_xfer,
+ .functionality = dibx000_i2c_func,
+};
+
+struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating)
+{
+ struct i2c_adapter *i2c = NULL;
+
+ switch (intf) {
+ case DIBX000_I2C_INTERFACE_TUNER:
+ if (gating)
+ i2c = &mst->gated_tuner_i2c_adap;
+ break;
+ default:
+ printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n");
+ break;
+ }
+
+ return i2c;
+}
+EXPORT_SYMBOL(dibx000_get_i2c_adapter);
+
+static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *algo, const char name[I2C_NAME_SIZE], struct dibx000_i2c_master *mst)
+{
+ strncpy(i2c_adap->name, name, I2C_NAME_SIZE);
+ i2c_adap->class = I2C_CLASS_TV_DIGITAL,
+ i2c_adap->algo = algo;
+ i2c_adap->algo_data = NULL;
+ i2c_set_adapdata(i2c_adap, mst);
+ if (i2c_add_adapter(i2c_adap) < 0)
+ return -ENODEV;
+ return 0;
+}
+
+int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr)
+{
+ u8 tx[4];
+ struct i2c_msg m = { .addr = i2c_addr >> 1, .buf = tx, .len = 4 };
+
+ mst->device_rev = device_rev;
+ mst->i2c_adap = i2c_adap;
+ mst->i2c_addr = i2c_addr >> 1;
+
+ if (device_rev == DIB7000P)
+ mst->base_reg = 1024;
+ else
+ mst->base_reg = 768;
+
+ if (i2c_adapter_init(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, "DiBX000 tuner I2C bus", mst) != 0)
+ printk(KERN_ERR "DiBX000: could not initialize the tuner i2c_adapter\n");
+
+ /* initialize the i2c-master by closing the gate */
+ dibx000_i2c_gate_ctrl(mst, tx, 0, 0);
+
+ return i2c_transfer(i2c_adap, &m, 1) == 1;
+}
+EXPORT_SYMBOL(dibx000_init_i2c_master);
+
+void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst)
+{
+ i2c_del_adapter(&mst->gated_tuner_i2c_adap);
+}
+EXPORT_SYMBOL(dibx000_exit_i2c_master);
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Common function the DiBcom demodulator family");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
new file mode 100644
index 00000000000..a1df604366c
--- /dev/null
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -0,0 +1,177 @@
+#ifndef DIBX000_COMMON_H
+#define DIBX000_COMMON_H
+
+enum dibx000_i2c_interface {
+ DIBX000_I2C_INTERFACE_TUNER = 0,
+ DIBX000_I2C_INTERFACE_GPIO_1_2 = 1,
+ DIBX000_I2C_INTERFACE_GPIO_3_4 = 2
+};
+
+struct dibx000_i2c_master {
+#define DIB3000MC 1
+#define DIB7000 2
+#define DIB7000P 11
+#define DIB7000MC 12
+ u16 device_rev;
+
+ enum dibx000_i2c_interface selected_interface;
+
+// struct i2c_adapter tuner_i2c_adap;
+ struct i2c_adapter gated_tuner_i2c_adap;
+
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+
+ u16 base_reg;
+};
+
+extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr);
+extern struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating);
+extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
+
+#define BAND_LBAND 0x01
+#define BAND_UHF 0x02
+#define BAND_VHF 0x04
+#define BAND_SBAND 0x08
+#define BAND_FM 0x10
+
+#define BAND_OF_FREQUENCY(freq_kHz) ( (freq_kHz) <= 115000 ? BAND_FM : \
+ (freq_kHz) <= 250000 ? BAND_VHF : \
+ (freq_kHz) <= 863000 ? BAND_UHF : \
+ (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND )
+
+struct dibx000_agc_config {
+ /* defines the capabilities of this AGC-setting - using the BAND_-defines*/
+ u8 band_caps;
+
+ u16 setup;
+
+ u16 inv_gain;
+ u16 time_stabiliz;
+
+ u8 alpha_level;
+ u16 thlock;
+
+ u8 wbd_inv;
+ u16 wbd_ref;
+ u8 wbd_sel;
+ u8 wbd_alpha;
+
+ u16 agc1_max;
+ u16 agc1_min;
+ u16 agc2_max;
+ u16 agc2_min;
+
+ u8 agc1_pt1;
+ u8 agc1_pt2;
+ u8 agc1_pt3;
+
+ u8 agc1_slope1;
+ u8 agc1_slope2;
+
+ u8 agc2_pt1;
+ u8 agc2_pt2;
+
+ u8 agc2_slope1;
+ u8 agc2_slope2;
+
+ u8 alpha_mant;
+ u8 alpha_exp;
+
+ u8 beta_mant;
+ u8 beta_exp;
+
+ u8 perform_agc_softsplit;
+
+ struct {
+ u16 min;
+ u16 max;
+ u16 min_thres;
+ u16 max_thres;
+ } split;
+};
+
+struct dibx000_bandwidth_config {
+ u32 internal;
+ u32 sampling;
+
+ u8 pll_prediv;
+ u8 pll_ratio;
+ u8 pll_range;
+ u8 pll_reset;
+ u8 pll_bypass;
+
+ u8 enable_refdiv;
+ u8 bypclk_div;
+ u8 IO_CLK_en_core;
+ u8 ADClkSrc;
+ u8 modulo;
+
+ u16 sad_cfg;
+
+ u32 ifreq;
+ u32 timf;
+};
+
+enum dibx000_adc_states {
+ DIBX000_SLOW_ADC_ON = 0,
+ DIBX000_SLOW_ADC_OFF,
+ DIBX000_ADC_ON,
+ DIBX000_ADC_OFF,
+ DIBX000_VBG_ENABLE,
+ DIBX000_VBG_DISABLE,
+};
+
+#define BW_INDEX_TO_KHZ(v) ( (v) == BANDWIDTH_8_MHZ ? 8000 : \
+ (v) == BANDWIDTH_7_MHZ ? 7000 : \
+ (v) == BANDWIDTH_6_MHZ ? 6000 : 8000 )
+
+/* Chip output mode. */
+#define OUTMODE_HIGH_Z 0
+#define OUTMODE_MPEG2_PAR_GATED_CLK 1
+#define OUTMODE_MPEG2_PAR_CONT_CLK 2
+#define OUTMODE_MPEG2_SERIAL 7
+#define OUTMODE_DIVERSITY 4
+#define OUTMODE_MPEG2_FIFO 5
+
+/* I hope I can get rid of the following kludge in the near future */
+struct dibx000_ofdm_channel {
+ u32 RF_kHz;
+ u8 Bw;
+ s16 nfft;
+ s16 guard;
+ s16 nqam;
+ s16 vit_hrch;
+ s16 vit_select_hp;
+ s16 vit_alpha;
+ s16 vit_code_rate_hp;
+ s16 vit_code_rate_lp;
+ u8 intlv_native;
+};
+
+#define FEP2DIB(fep,ch) \
+ (ch)->RF_kHz = (fep)->frequency / 1000; \
+ (ch)->Bw = (fep)->u.ofdm.bandwidth; \
+ (ch)->nfft = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \
+ (ch)->guard = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \
+ (ch)->nqam = (fep)->u.ofdm.constellation == QAM_AUTO ? -1 : (fep)->u.ofdm.constellation == QAM_64 ? 2 : (fep)->u.ofdm.constellation; \
+ (ch)->vit_hrch = 0; /* linux-dvb is not prepared for HIERARCHICAL TRANSMISSION */ \
+ (ch)->vit_select_hp = 1; \
+ (ch)->vit_alpha = 1; \
+ (ch)->vit_code_rate_hp = (fep)->u.ofdm.code_rate_HP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_HP; \
+ (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; \
+ (ch)->intlv_native = 1;
+
+#define INIT_OFDM_CHANNEL(ch) do {\
+ (ch)->Bw = 0; \
+ (ch)->nfft = -1; \
+ (ch)->guard = -1; \
+ (ch)->nqam = -1; \
+ (ch)->vit_hrch = -1; \
+ (ch)->vit_select_hp = -1; \
+ (ch)->vit_alpha = -1; \
+ (ch)->vit_code_rate_hp = -1; \
+ (ch)->vit_code_rate_lp = -1; \
+} while (0)
+
+#endif
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index 2be33f27c69..62de760c844 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -472,14 +472,14 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
desc->name, div, buf[0], buf[1], buf[2], buf[3]);
- return 0;
+ // calculate the frequency we set it to
+ return (div * desc->entries[i].stepsize) - desc->entries[i].offset;
}
EXPORT_SYMBOL(dvb_pll_configure);
static int dvb_pll_release(struct dvb_frontend *fe)
{
- if (fe->tuner_priv)
- kfree(fe->tuner_priv);
+ kfree(fe->tuner_priv);
fe->tuner_priv = NULL;
return 0;
}
@@ -489,10 +489,14 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
struct dvb_pll_priv *priv = fe->tuner_priv;
u8 buf[4];
struct i2c_msg msg =
- { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ { .addr = priv->pll_i2c_address, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
int i;
int result;
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
for (i = 0; i < priv->pll_desc->count; i++) {
if (priv->pll_desc->entries[i].limit == 0)
break;
@@ -514,16 +518,16 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
return 0;
}
-static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+static int dvb_pll_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
{
struct dvb_pll_priv *priv = fe->tuner_priv;
u8 buf[4];
struct i2c_msg msg =
- { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ { .addr = priv->pll_i2c_address, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
int result;
- u32 div;
- int i;
- u32 bandwidth = 0;
+ u32 bandwidth = 0, frequency = 0;
if (priv->i2c == NULL)
return -EINVAL;
@@ -533,8 +537,11 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param
bandwidth = params->u.ofdm.bandwidth;
}
- if ((result = dvb_pll_configure(priv->pll_desc, buf, params->frequency, bandwidth)) != 0)
+ if ((result = dvb_pll_configure(priv->pll_desc, buf,
+ params->frequency, bandwidth)) < 0)
return result;
+ else
+ frequency = result;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -542,26 +549,19 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param
return result;
}
- // calculate the frequency we set it to
- for (i = 0; i < priv->pll_desc->count; i++) {
- if (params->frequency > priv->pll_desc->entries[i].limit)
- continue;
- break;
- }
- div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize;
- priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset;
+ priv->frequency = frequency;
priv->bandwidth = bandwidth;
return 0;
}
-static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len)
+static int dvb_pll_calc_regs(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params,
+ u8 *buf, int buf_len)
{
struct dvb_pll_priv *priv = fe->tuner_priv;
int result;
- u32 div;
- int i;
- u32 bandwidth = 0;
+ u32 bandwidth = 0, frequency = 0;
if (buf_len < 5)
return -EINVAL;
@@ -571,18 +571,15 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parame
bandwidth = params->u.ofdm.bandwidth;
}
- if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params->frequency, bandwidth)) != 0)
+ if ((result = dvb_pll_configure(priv->pll_desc, buf+1,
+ params->frequency, bandwidth)) < 0)
return result;
+ else
+ frequency = result;
+
buf[0] = priv->pll_i2c_address;
- // calculate the frequency we set it to
- for (i = 0; i < priv->pll_desc->count; i++) {
- if (params->frequency > priv->pll_desc->entries[i].limit)
- continue;
- break;
- }
- div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize;
- priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset;
+ priv->frequency = frequency;
priv->bandwidth = bandwidth;
return 5;
@@ -611,10 +608,13 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = {
.get_bandwidth = dvb_pll_get_bandwidth,
};
-int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc)
+struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
+ struct i2c_adapter *i2c,
+ struct dvb_pll_desc *desc)
{
u8 b1 [] = { 0 };
- struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 };
+ struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD,
+ .buf = b1, .len = 1 };
struct dvb_pll_priv *priv = NULL;
int ret;
@@ -624,26 +624,28 @@ int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2
ret = i2c_transfer (i2c, &msg, 1);
if (ret != 1)
- return -1;
+ return NULL;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
}
priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL);
if (priv == NULL)
- return -ENOMEM;
+ return NULL;
priv->pll_i2c_address = pll_addr;
priv->i2c = i2c;
priv->pll_desc = desc;
- memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops));
+ memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
strncpy(fe->ops.tuner_ops.info.name, desc->name, 128);
fe->ops.tuner_ops.info.frequency_min = desc->min;
fe->ops.tuner_ops.info.frequency_min = desc->max;
fe->tuner_priv = priv;
- return 0;
+ return fe;
}
EXPORT_SYMBOL(dvb_pll_attach);
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index 66361cd1880..681186a5e5e 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -48,7 +48,7 @@ extern struct dvb_pll_desc dvb_pll_philips_td1316;
extern struct dvb_pll_desc dvb_pll_thomson_fe6600;
extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
- u32 freq, int bandwidth);
+ u32 freq, int bandwidth);
/**
* Attach a dvb-pll to the supplied frontend structure.
@@ -57,8 +57,11 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
* @param pll_addr i2c address of the PLL (if used).
* @param i2c i2c adapter to use (set to NULL if not used).
* @param desc dvb_pll_desc to use.
- * @return 0 on success, nonzero on failure.
+ * @return Frontend pointer on success, NULL on failure
*/
-extern int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc);
+extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+ int pll_addr,
+ struct i2c_adapter *i2c,
+ struct dvb_pll_desc *desc);
#endif
diff --git a/drivers/media/dvb/frontends/isl6421.c b/drivers/media/dvb/frontends/isl6421.c
index 58c34db3107..ef319369ec2 100644
--- a/drivers/media/dvb/frontends/isl6421.c
+++ b/drivers/media/dvb/frontends/isl6421.c
@@ -42,12 +42,11 @@ struct isl6421 {
u8 override_and;
struct i2c_adapter *i2c;
u8 i2c_addr;
- void (*release_chain)(struct dvb_frontend* fe);
};
static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
- struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv;
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
.buf = &isl6421->config,
.len = sizeof(isl6421->config) };
@@ -75,7 +74,7 @@ static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage
static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
{
- struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv;
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
.buf = &isl6421->config,
.len = sizeof(isl6421->config) };
@@ -93,31 +92,26 @@ static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
static void isl6421_release(struct dvb_frontend *fe)
{
- struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv;
-
/* power off */
isl6421_set_voltage(fe, SEC_VOLTAGE_OFF);
- /* free data & call next release routine */
- fe->ops.release = isl6421->release_chain;
- kfree(fe->misc_priv);
- fe->misc_priv = NULL;
- if (fe->ops.release)
- fe->ops.release(fe);
+ /* free */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
}
-int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
u8 override_set, u8 override_clear)
{
struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL);
if (!isl6421)
- return -ENOMEM;
+ return NULL;
/* default configuration */
isl6421->config = ISL6421_ISEL1;
isl6421->i2c = i2c;
isl6421->i2c_addr = i2c_addr;
- fe->misc_priv = isl6421;
+ fe->sec_priv = isl6421;
/* bits which should be forced to '1' */
isl6421->override_or = override_set;
@@ -128,19 +122,17 @@ int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr
/* detect if it is present or not */
if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) {
kfree(isl6421);
- fe->misc_priv = NULL;
- return -EIO;
+ return NULL;
}
/* install release callback */
- isl6421->release_chain = fe->ops.release;
- fe->ops.release = isl6421_release;
+ fe->ops.release_sec = isl6421_release;
/* override frontend ops */
fe->ops.set_voltage = isl6421_set_voltage;
fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage;
- return 0;
+ return fe;
}
EXPORT_SYMBOL(isl6421_attach);
diff --git a/drivers/media/dvb/frontends/isl6421.h b/drivers/media/dvb/frontends/isl6421.h
index 675f80a19b9..ea7f78a7d3c 100644
--- a/drivers/media/dvb/frontends/isl6421.h
+++ b/drivers/media/dvb/frontends/isl6421.h
@@ -39,8 +39,17 @@
#define ISL6421_ISEL1 0x20
#define ISL6421_DCL 0x40
+#if defined(CONFIG_DVB_ISL6421) || (defined(CONFIG_DVB_ISL6421_MODULE) && defined(MODULE))
/* override_set and override_clear control which system register bits (above) to always set & clear */
-extern int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
u8 override_set, u8 override_clear);
+#else
+static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+ u8 override_set, u8 override_clear)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_ISL6421
#endif
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
index f3bc82e44a2..1aeacb1c4af 100644
--- a/drivers/media/dvb/frontends/l64781.c
+++ b/drivers/media/dvb/frontends/l64781.c
@@ -36,7 +36,7 @@ struct l64781_state {
struct dvb_frontend frontend;
/* private demodulator data */
- int first:1;
+ unsigned int first:1;
};
#define dprintk(args...) \
diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h
index 83b8bc21027..cd15f76ff28 100644
--- a/drivers/media/dvb/frontends/l64781.h
+++ b/drivers/media/dvb/frontends/l64781.h
@@ -31,8 +31,16 @@ struct l64781_config
u8 demod_address;
};
-
+#if defined(CONFIG_DVB_L64781) || (defined(CONFIG_DVB_L64781_MODULE) && defined(MODULE))
extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_L64781
#endif // L64781_H
diff --git a/drivers/media/dvb/frontends/lg_h06xf.h b/drivers/media/dvb/frontends/lg_h06xf.h
deleted file mode 100644
index 754d51d1112..00000000000
--- a/drivers/media/dvb/frontends/lg_h06xf.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * lg_h06xf.h - ATSC Tuner support for LG TDVS-H06xF
- *
- * 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 _LG_H06XF_H_
-#define _LG_H06XF_H_
-#include "dvb-pll.h"
-
-static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap,
- struct dvb_frontend_parameters* params)
-{
- u8 buf[4];
- struct i2c_msg msg = { .addr = 0x61, .flags = 0,
- .buf = buf, .len = sizeof(buf) };
- int err;
-
- dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) {
- printk(KERN_WARNING "lg_h06xf: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, buf[0], buf[1], err);
- if (err < 0)
- return err;
- else
- return -EREMOTEIO;
- }
-
- /* Set the Auxiliary Byte. */
- buf[0] = buf[2];
- buf[0] &= ~0x20;
- buf[0] |= 0x18;
- buf[1] = 0x50;
- msg.len = 2;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) {
- printk(KERN_WARNING "lg_h06xf: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, buf[0], buf[1], err);
- if (err < 0)
- return err;
- else
- return -EREMOTEIO;
- }
-
- return 0;
-}
-#endif
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index 9a354708bd2..68aad0f6519 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -31,9 +31,6 @@
* Air2PC/AirStar 2 ATSC 3rd generation (HD5000)
* pcHDTV HD5500
*
- * TODO:
- * signal strength always returns 0.
- *
*/
#include <linux/kernel.h>
@@ -46,9 +43,13 @@
#include <asm/byteorder.h>
#include "dvb_frontend.h"
+#include "dvb_math.h"
#include "lgdt330x_priv.h"
#include "lgdt330x.h"
+/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */
+/* #define USE_EQMSE */
+
static int debug = 0;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
@@ -68,6 +69,7 @@ struct lgdt330x_state
/* Demodulator private data */
fe_modulation_t current_modulation;
+ u32 snr; /* Result of last SNR calculation */
/* Tuner private data */
u32 current_frequency;
@@ -302,10 +304,10 @@ static int lgdt330x_set_parameters(struct dvb_frontend* fe,
static u8 lgdt3303_8vsb_44_data[] = {
0x04, 0x00,
0x0d, 0x40,
- 0x0e, 0x87,
- 0x0f, 0x8e,
- 0x10, 0x01,
- 0x47, 0x8b };
+ 0x0e, 0x87,
+ 0x0f, 0x8e,
+ 0x10, 0x01,
+ 0x47, 0x8b };
/*
* Array of byte pairs <address, value>
@@ -435,9 +437,6 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
/* Test signal does not exist flag */
/* as well as the AGC lock flag. */
*status |= FE_HAS_SIGNAL;
- } else {
- /* Without a signal all other status bits are meaningless */
- return 0;
}
/*
@@ -500,9 +499,6 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
/* Test input signal does not exist flag */
/* as well as the AGC lock flag. */
*status |= FE_HAS_SIGNAL;
- } else {
- /* Without a signal all other status bits are meaningless */
- return 0;
}
/* Carrier Recovery Lock Status Register */
@@ -543,151 +539,150 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
return 0;
}
-static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+/* Calculate SNR estimation (scaled by 2^24)
+
+ 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM
+ equations from LGDT3303 datasheet. VSB is the same between the '02
+ and '03, so maybe QAM is too? Perhaps someone with a newer datasheet
+ that has QAM information could verify?
+
+ For 8-VSB: (two ways, take your pick)
+ LGDT3302:
+ SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE)
+ LGDT3303:
+ SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE)
+ LGDT3302 & LGDT3303:
+ SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one)
+ For 64-QAM:
+ SNR = 10 * log10( 688128 / MSEQAM)
+ For 256-QAM:
+ SNR = 10 * log10( 696320 / MSEQAM)
+
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - intlog10(MSE))
+ Where for 256-QAM, c = log10(696320) * 2^24, and so on. */
+
+static u32 calculate_snr(u32 mse, u32 c)
{
- /* not directly available. */
- *strength = 0;
- return 0;
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
}
static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr)
{
-#ifdef SNR_IN_DB
- /*
- * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise)
- * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker
- * respectively. The following tables are built on these formulas.
- * The usual definition is SNR = 20 log10(signal/noise)
- * If the specification is wrong the value retuned is 1/2 the actual SNR in db.
- *
- * This table is a an ordered list of noise values computed by the
- * formula from the spec sheet such that the index into the table
- * starting at 43 or 45 is the SNR value in db. There are duplicate noise
- * value entries at the beginning because the SNR varies more than
- * 1 db for a change of 1 digit in noise at very small values of noise.
- *
- * Examples from SNR_EQ table:
- * noise SNR
- * 0 43
- * 1 42
- * 2 39
- * 3 37
- * 4 36
- * 5 35
- * 6 34
- * 7 33
- * 8 33
- * 9 32
- * 10 32
- * 11 31
- * 12 31
- * 13 30
- */
-
- static const u32 SNR_EQ[] =
- { 1, 2, 2, 2, 3, 3, 4, 4, 5, 7,
- 9, 11, 13, 17, 21, 26, 33, 41, 52, 65,
- 81, 102, 129, 162, 204, 257, 323, 406, 511, 644,
- 810, 1020, 1284, 1616, 2035, 2561, 3224, 4059, 5110, 6433,
- 8098, 10195, 12835, 16158, 20341, 25608, 32238, 40585, 51094, 64323,
- 80978, 101945, 128341, 161571, 203406, 256073, 0x40000
- };
-
- static const u32 SNR_PH[] =
- { 1, 2, 2, 2, 3, 3, 4, 5, 6, 8,
- 10, 12, 15, 19, 23, 29, 37, 46, 58, 73,
- 91, 115, 144, 182, 229, 288, 362, 456, 574, 722,
- 909, 1144, 1440, 1813, 2282, 2873, 3617, 4553, 5732, 7216,
- 9084, 11436, 14396, 18124, 22817, 28724, 36161, 45524, 57312, 72151,
- 90833, 114351, 143960, 181235, 228161, 0x080000
- };
-
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
- static u32 snr_db; /* index into SNR_EQ[] */
struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
- /* read both equalizer and phase tracker noise data */
- i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
- if (state->current_modulation == VSB_8) {
- /* Equalizer Mean-Square Error Register for VSB */
+ switch(state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -15.61 to +41.58 */
noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
-
- /*
- * Look up noise value in table.
- * A better search algorithm could be used...
- * watch out there are duplicate entries.
- */
- for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) {
- if (noise < SNR_EQ[snr_db]) {
- *snr = 43 - snr_db;
- break;
- }
- }
- } else {
- /* Phase Tracker Mean-Square Error Register for QAM */
- noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
-
- /* Look up noise value in table. */
- for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) {
- if (noise < SNR_PH[snr_db]) {
- *snr = 45 - snr_db;
- break;
- }
- }
- }
+ c = 69765745; /* log10(25*24^2)*2^24 */
#else
- /* Return the raw noise value */
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
-
- /* read both equalizer and pase tracker noise data */
- i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf));
-
- if (state->current_modulation == VSB_8) {
- /* Phase Tracker Mean-Square Error Register for VSB */
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
- } else {
-
- /* Carrier Recovery Mean-Square Error for QAM */
- i2c_read_demod_bytes(state, 0x1a, buf, 2);
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
noise = ((buf[0] & 3) << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+ __FUNCTION__);
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
}
- /* Small values for noise mean signal is better so invert noise */
- *snr = ~noise;
-#endif
+ state->snr = calculate_snr(noise, c);
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
- dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
return 0;
}
static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr)
{
- /* Return the raw noise value */
- static u8 buf[5];/* read data buffer */
- static u32 noise; /* noise value */
struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u8 buf[5]; /* read data buffer */
+ u32 noise; /* noise value */
+ u32 c; /* per-modulation SNR calculation constant */
- if (state->current_modulation == VSB_8) {
-
- i2c_read_demod_bytes(state, 0x6e, buf, 5);
- /* Phase Tracker Mean-Square Error Register for VSB */
+ switch(state->current_modulation) {
+ case VSB_8:
+ i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5);
+#ifdef USE_EQMSE
+ /* Use Equalizer Mean-Square Error Register */
+ /* SNR for ranges from -16.12 to +44.08 */
+ noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2];
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#else
+ /* Use Phase Tracker Mean-Square Error Register */
+ /* SNR for ranges from -13.11 to +44.08 */
noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4];
- } else {
-
- /* Carrier Recovery Mean-Square Error for QAM */
- i2c_read_demod_bytes(state, 0x1a, buf, 2);
+ c = 73957994; /* log10(25*32^2)*2^24 */
+#endif
+ break;
+ case QAM_64:
+ case QAM_256:
+ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2);
noise = (buf[0] << 8) | buf[1];
+ c = state->current_modulation == QAM_64 ? 97939837 : 98026066;
+ /* log10(688128)*2^24 and log10(696320)*2^24 */
+ break;
+ default:
+ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n",
+ __FUNCTION__);
+ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */
}
- /* Small values for noise mean signal is better so invert noise */
- *snr = ~noise;
+ state->snr = calculate_snr(noise, c);
+ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */
+
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
+
+ return 0;
+}
+
+static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv;
+ u16 snr;
+ int ret;
- dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
return 0;
}
diff --git a/drivers/media/dvb/frontends/lgdt330x.h b/drivers/media/dvb/frontends/lgdt330x.h
index bad903c6f0f..995059004b1 100644
--- a/drivers/media/dvb/frontends/lgdt330x.h
+++ b/drivers/media/dvb/frontends/lgdt330x.h
@@ -52,8 +52,17 @@ struct lgdt330x_config
int clock_polarity_flip;
};
+#if defined(CONFIG_DVB_LGDT330X) || (defined(CONFIG_DVB_LGDT330X_MODULE) && defined(MODULE))
extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_LGDT330X
#endif /* LGDT330X_H */
diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb/frontends/lgdt330x_priv.h
index 59b7c5b9012..38c76695abf 100644
--- a/drivers/media/dvb/frontends/lgdt330x_priv.h
+++ b/drivers/media/dvb/frontends/lgdt330x_priv.h
@@ -51,14 +51,19 @@ enum I2C_REG {
AGC_RFIF_ACC2= 0x3b,
AGC_STATUS= 0x3f,
SYNC_STATUS_VSB= 0x43,
- EQPH_ERR0= 0x47,
- EQ_ERR1= 0x48,
- EQ_ERR2= 0x49,
- PH_ERR1= 0x4a,
- PH_ERR2= 0x4b,
DEMUX_CONTROL= 0x66,
+ LGDT3302_EQPH_ERR0= 0x47,
+ LGDT3302_EQ_ERR1= 0x48,
+ LGDT3302_EQ_ERR2= 0x49,
+ LGDT3302_PH_ERR1= 0x4a,
+ LGDT3302_PH_ERR2= 0x4b,
LGDT3302_PACKET_ERR_COUNTER1= 0x6a,
LGDT3302_PACKET_ERR_COUNTER2= 0x6b,
+ LGDT3303_EQPH_ERR0= 0x6e,
+ LGDT3303_EQ_ERR1= 0x6f,
+ LGDT3303_EQ_ERR2= 0x70,
+ LGDT3303_PH_ERR1= 0x71,
+ LGDT3303_PH_ERR2= 0x72,
LGDT3303_PACKET_ERR_COUNTER1= 0x8b,
LGDT3303_PACKET_ERR_COUNTER2= 0x8c,
};
diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c
new file mode 100644
index 00000000000..2202d0cc878
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgh06xf.c
@@ -0,0 +1,134 @@
+/*
+ * lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF
+ *
+ * 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 "dvb-pll.h"
+#include "lgh06xf.h"
+
+#define LG_H06XF_PLL_I2C_ADDR 0x61
+
+struct lgh06xf_priv {
+ struct i2c_adapter *i2c;
+ u32 frequency;
+};
+
+static int lgh06xf_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int lgh06xf_set_params(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters* params)
+{
+ struct lgh06xf_priv *priv = fe->tuner_priv;
+ u8 buf[4];
+ struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+ u32 frequency;
+ int result;
+
+ if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf,
+ params->frequency, 0)) < 0)
+ return result;
+ else
+ frequency = result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "lgh06xf: %s error "
+ "(addr %02x <- %02x, result = %i)\n",
+ __FUNCTION__, buf[0], buf[1], result);
+ if (result < 0)
+ return result;
+ else
+ return -EREMOTEIO;
+ }
+
+ /* Set the Auxiliary Byte. */
+ buf[0] = buf[2];
+ buf[0] &= ~0x20;
+ buf[0] |= 0x18;
+ buf[1] = 0x50;
+ msg.len = 2;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "lgh06xf: %s error "
+ "(addr %02x <- %02x, result = %i)\n",
+ __FUNCTION__, buf[0], buf[1], result);
+ if (result < 0)
+ return result;
+ else
+ return -EREMOTEIO;
+ }
+
+ priv->frequency = frequency;
+
+ return 0;
+}
+
+static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct lgh06xf_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops lgh06xf_tuner_ops = {
+ .release = lgh06xf_release,
+ .set_params = lgh06xf_set_params,
+ .get_frequency = lgh06xf_get_frequency,
+};
+
+struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c)
+{
+ struct lgh06xf_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c = i2c;
+
+ memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name,
+ sizeof(fe->ops.tuner_ops.info.name));
+
+ fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min;
+ fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max;
+
+ fe->tuner_priv = priv;
+ return fe;
+}
+
+EXPORT_SYMBOL(lgh06xf_attach);
+
+MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support");
+MODULE_AUTHOR("Michael Krufky");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h
new file mode 100644
index 00000000000..510b4bedfb2
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgh06xf.h
@@ -0,0 +1,35 @@
+/*
+ * lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF
+ *
+ * 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 _LGH06XF_H_
+#define _LGH06XF_H_
+#include "dvb_frontend.h"
+
+#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE))
+extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_TUNER_LGH06XF */
+
+#endif /* _LGH06XF_H_ */
diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c
index e933edc8dd2..2d2f58c2622 100644
--- a/drivers/media/dvb/frontends/lnbp21.c
+++ b/drivers/media/dvb/frontends/lnbp21.c
@@ -40,12 +40,11 @@ struct lnbp21 {
u8 override_or;
u8 override_and;
struct i2c_adapter *i2c;
- void (*release_chain)(struct dvb_frontend* fe);
};
static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv;
+ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
struct i2c_msg msg = { .addr = 0x08, .flags = 0,
.buf = &lnbp21->config,
.len = sizeof(lnbp21->config) };
@@ -73,7 +72,7 @@ static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
{
- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv;
+ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
struct i2c_msg msg = { .addr = 0x08, .flags = 0,
.buf = &lnbp21->config,
.len = sizeof(lnbp21->config) };
@@ -91,29 +90,24 @@ static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
static void lnbp21_release(struct dvb_frontend *fe)
{
- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv;
-
/* LNBP power off */
lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF);
- /* free data & call next release routine */
- fe->ops.release = lnbp21->release_chain;
- kfree(fe->misc_priv);
- fe->misc_priv = NULL;
- if (fe->ops.release)
- fe->ops.release(fe);
+ /* free data */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
}
-int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
+struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
{
struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL);
if (!lnbp21)
- return -ENOMEM;
+ return NULL;
/* default configuration */
lnbp21->config = LNBP21_ISEL;
lnbp21->i2c = i2c;
- fe->misc_priv = lnbp21;
+ fe->sec_priv = lnbp21;
/* bits which should be forced to '1' */
lnbp21->override_or = override_set;
@@ -124,19 +118,17 @@ int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_
/* detect if it is present or not */
if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) {
kfree(lnbp21);
- fe->misc_priv = NULL;
- return -EIO;
+ return NULL;
}
/* install release callback */
- lnbp21->release_chain = fe->ops.release;
- fe->ops.release = lnbp21_release;
+ fe->ops.release_sec = lnbp21_release;
/* override frontend ops */
fe->ops.set_voltage = lnbp21_set_voltage;
fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage;
- return 0;
+ return fe;
}
EXPORT_SYMBOL(lnbp21_attach);
diff --git a/drivers/media/dvb/frontends/lnbp21.h b/drivers/media/dvb/frontends/lnbp21.h
index 047a4ab68c0..68906acf7d6 100644
--- a/drivers/media/dvb/frontends/lnbp21.h
+++ b/drivers/media/dvb/frontends/lnbp21.h
@@ -39,7 +39,15 @@
#include <linux/dvb/frontend.h>
+#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) && defined(MODULE))
/* override_set and override_clear control which system register bits (above) to always set & clear */
-extern int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear);
+extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear);
+#else
+static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_LNBP21
-#endif
+#endif // _LNBP21_H
diff --git a/drivers/media/dvb/frontends/mt2060.c b/drivers/media/dvb/frontends/mt2060.c
new file mode 100644
index 00000000000..450fad8d9b6
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt2060.c
@@ -0,0 +1,370 @@
+/*
+ * 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/moduleparam.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/dvb/frontends/mt2060.h b/drivers/media/dvb/frontends/mt2060.h
new file mode 100644
index 00000000000..0a86eab3a95
--- /dev/null
+++ b/drivers/media/dvb/frontends/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", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUNER_MT2060
+
+#endif
diff --git a/drivers/media/dvb/frontends/mt2060_priv.h b/drivers/media/dvb/frontends/mt2060_priv.h
new file mode 100644
index 00000000000..5eaccdefd0b
--- /dev/null
+++ b/drivers/media/dvb/frontends/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/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h
index 666a1bd1c24..cf9a1505ad4 100644
--- a/drivers/media/dvb/frontends/mt312.h
+++ b/drivers/media/dvb/frontends/mt312.h
@@ -34,8 +34,16 @@ struct mt312_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE))
struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
struct i2c_adapter* i2c);
-
+#else
+static inline struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_MT312
#endif // MT312_H
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c
index 5de7376c94c..87e31ca7e10 100644
--- a/drivers/media/dvb/frontends/mt352.c
+++ b/drivers/media/dvb/frontends/mt352.c
@@ -70,7 +70,7 @@ static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val)
return 0;
}
-int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
+static int _mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
{
int err,i;
for (i=0; i < ilen-1; i++)
@@ -107,7 +107,7 @@ static int mt352_sleep(struct dvb_frontend* fe)
{
static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 };
- mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
+ _mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
return 0;
}
@@ -293,14 +293,14 @@ static int mt352_set_parameters(struct dvb_frontend* fe,
fe->ops.i2c_gate_ctrl(fe, 0);
}
- mt352_write(fe, buf, 8);
- mt352_write(fe, fsm_go, 2);
+ _mt352_write(fe, buf, 8);
+ _mt352_write(fe, fsm_go, 2);
} else {
if (fe->ops.tuner_ops.calc_regs) {
fe->ops.tuner_ops.calc_regs(fe, param, buf+8, 5);
buf[8] <<= 1;
- mt352_write(fe, buf, sizeof(buf));
- mt352_write(fe, tuner_go, 2);
+ _mt352_write(fe, buf, sizeof(buf));
+ _mt352_write(fe, tuner_go, 2);
}
}
@@ -522,7 +522,7 @@ static int mt352_init(struct dvb_frontend* fe)
(mt352_read_register(state, CONFIG) & 0x20) == 0) {
/* Do a "hard" reset */
- mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
+ _mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
return state->config.demod_init(fe);
}
@@ -585,6 +585,7 @@ static struct dvb_frontend_ops mt352_ops = {
.init = mt352_init,
.sleep = mt352_sleep,
+ .write = _mt352_write,
.set_frontend = mt352_set_parameters,
.get_frontend = mt352_get_parameters,
@@ -605,4 +606,3 @@ MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(mt352_attach);
-EXPORT_SYMBOL(mt352_write);
diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h
index 9e7ff4b8fe5..e9964081fd8 100644
--- a/drivers/media/dvb/frontends/mt352.h
+++ b/drivers/media/dvb/frontends/mt352.h
@@ -51,9 +51,23 @@ struct mt352_config
int (*demod_init)(struct dvb_frontend* fe);
};
+#if defined(CONFIG_DVB_MT352) || (defined(CONFIG_DVB_MT352_MODULE) && defined(MODULE))
extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_MT352
-extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen);
+static inline int mt352_write(struct dvb_frontend *fe, u8 *buf, int len) {
+ int r = 0;
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, len);
+ return r;
+}
#endif // MT352_H
diff --git a/drivers/media/dvb/frontends/nxt200x.h b/drivers/media/dvb/frontends/nxt200x.h
index 34d61735845..28bc5591b31 100644
--- a/drivers/media/dvb/frontends/nxt200x.h
+++ b/drivers/media/dvb/frontends/nxt200x.h
@@ -45,8 +45,17 @@ struct nxt200x_config
int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
};
+#if defined(CONFIG_DVB_NXT200X) || (defined(CONFIG_DVB_NXT200X_MODULE) && defined(MODULE))
extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_NXT200X
#endif /* NXT200X_H */
diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb/frontends/nxt6000.h
index 117031d1170..13d22518356 100644
--- a/drivers/media/dvb/frontends/nxt6000.h
+++ b/drivers/media/dvb/frontends/nxt6000.h
@@ -33,7 +33,16 @@ struct nxt6000_config
u8 clock_inversion:1;
};
+#if defined(CONFIG_DVB_NXT6000) || (defined(CONFIG_DVB_NXT6000_MODULE) && defined(MODULE))
extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_NXT6000
#endif // NXT6000_H
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
index d20ab30c1e8..5a3a6e53cda 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -40,6 +40,7 @@
#include <linux/slab.h>
#include <asm/byteorder.h>
+#include "dvb_math.h"
#include "dvb_frontend.h"
#include "dvb-pll.h"
#include "or51132.h"
@@ -62,6 +63,7 @@ struct or51132_state
/* Demodulator private data */
fe_modulation_t current_modulation;
+ u32 snr; /* Result of last SNR calculation */
/* Tuner private data */
u32 current_frequency;
@@ -465,124 +467,128 @@ static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
return 0;
}
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
- 0, 352, 602, 795, 954, 1088, 1204, 1306, 1397, 1480,
- 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
- 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
- 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
- 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
- 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
- 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
- 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
- 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
- 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
- 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
- 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
- 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
- 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
- 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
- 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
- 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
- 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
- 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
- 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
+/* Calculate SNR estimation (scaled by 2^24)
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+ 8-VSB SNR and QAM equations from Oren datasheets
-static unsigned int i20Log10(unsigned short val)
-{
- unsigned int rntval = 100;
- unsigned int tmp = val;
- unsigned int exp = 1;
+ For 8-VSB:
+ SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K
+
+ Where K = 0 if NTSC rejection filter is OFF; and
+ K = 3 if NTSC rejection filter is ON
+
+ For QAM64:
+ SNR[dB] = 10 * log10(897152044.8282 / MSE^2 )
- while(tmp > 100) {tmp /= 100; exp++;}
+ For QAM256:
+ SNR[dB] = 10 * log10(907832426.314266 / MSE^2 )
- val = (2 * val)/denom[exp];
- if (exp > 1) rntval = 2000*exp;
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+ Where for QAM256, c = log10(907832426.314266) * 2^24
+ and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */
- rntval += i100x20log10[val];
- return rntval;
+static u32 calculate_snr(u32 mse, u32 c)
+{
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = 2*intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
}
-static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct or51132_state* state = fe->demodulator_priv;
- unsigned char rec_buf[2];
- unsigned char snd_buf[2];
- u8 rcvr_stat;
- u16 snr_equ;
- u32 signal_strength;
- int usK;
+ u8 rec_buf[2];
+ u8 snd_buf[2];
+ u32 noise;
+ u32 c;
+ u32 usK;
+ /* Register is same for VSB or QAM firmware */
snd_buf[0]=0x04;
snd_buf[1]=0x02; /* SNR after Equalizer */
msleep(30); /* 30ms */
if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: read_status write error\n");
- return -1;
+ printk(KERN_WARNING "or51132: snr write error\n");
+ return -EREMOTEIO;
}
msleep(30); /* 30ms */
if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: read_status read error\n");
- return -1;
+ printk(KERN_WARNING "or51132: snr read error\n");
+ return -EREMOTEIO;
}
- snr_equ = rec_buf[0] | (rec_buf[1] << 8);
- dprintk("read_signal_strength snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+ noise = rec_buf[0] | (rec_buf[1] << 8);
+ dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise);
- /* Receiver Status */
+ /* Read status, contains modulation type for QAM_AUTO and
+ NTSC filter for VSB */
snd_buf[0]=0x04;
- snd_buf[1]=0x00;
+ snd_buf[1]=0x00; /* Status register */
msleep(30); /* 30ms */
if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: read_signal_strength read_status write error\n");
- return -1;
+ printk(KERN_WARNING "or51132: status write error\n");
+ return -EREMOTEIO;
}
msleep(30); /* 30ms */
if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: read_signal_strength read_status read error\n");
- return -1;
+ printk(KERN_WARNING "or51132: status read error\n");
+ return -EREMOTEIO;
}
- dprintk("read_signal_strength read_status %x %x\n",rec_buf[0],rec_buf[1]);
- rcvr_stat = rec_buf[1];
- usK = (rcvr_stat & 0x10) ? 3 : 0;
- /* The value reported back from the frontend will be FFFF=100% 0000=0% */
- signal_strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000;
- if (signal_strength > 0xffff)
- *strength = 0xffff;
- else
- *strength = signal_strength;
- dprintk("read_signal_strength %i\n",*strength);
+ usK = 0;
+ switch (rec_buf[0]) {
+ case 0x06:
+ usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0;
+ /* Fall through to QAM64 case */
+ case 0x43:
+ c = 150204167;
+ break;
+ case 0x45:
+ c = 150290396;
+ break;
+ default:
+ printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]);
+ return -EREMOTEIO;
+ }
+ dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__,
+ rec_buf[0], rec_buf[1]&0x10?"n":"ff");
+
+ /* Calculate SNR using noise, c, and NTSC rejection correction */
+ state->snr = calculate_snr(noise, c) - usK;
+ *snr = (state->snr) >> 16;
+
+ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
return 0;
}
-static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
+static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
{
- struct or51132_state* state = fe->demodulator_priv;
- unsigned char rec_buf[2];
- unsigned char snd_buf[2];
- u16 snr_equ;
-
- snd_buf[0]=0x04;
- snd_buf[1]=0x02; /* SNR after Equalizer */
- msleep(30); /* 30ms */
- if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
- printk(KERN_WARNING "or51132: read_snr write error\n");
- return -1;
- }
- msleep(30); /* 30ms */
- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51132: read_snr dvr read error\n");
- return -1;
- }
- snr_equ = rec_buf[0] | (rec_buf[1] << 8);
- dprintk("read_snr snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ u16 snr;
+ int ret;
- *snr = 0xFFFF - snr_equ;
- dprintk("read_snr %i\n",*snr);
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
return 0;
}
diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb/frontends/or51132.h
index 89658883abf..add24f0a743 100644
--- a/drivers/media/dvb/frontends/or51132.h
+++ b/drivers/media/dvb/frontends/or51132.h
@@ -34,8 +34,17 @@ struct or51132_config
int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
};
+#if defined(CONFIG_DVB_OR51132) || (defined(CONFIG_DVB_OR51132_MODULE) && defined(MODULE))
extern struct dvb_frontend* or51132_attach(const struct or51132_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_OR51132
#endif // OR51132_H
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
index 2bf124b5368..048d7cfe12d 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -39,6 +39,7 @@
#include <linux/slab.h>
#include <asm/byteorder.h>
+#include "dvb_math.h"
#include "dvb_frontend.h"
#include "or51211.h"
@@ -63,6 +64,7 @@ struct or51211_state {
/* Demodulator private data */
u8 initialized:1;
+ u32 snr; /* Result of last SNR claculation */
/* Tuner private data */
u32 current_frequency;
@@ -292,107 +294,81 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status)
return 0;
}
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
- 0, 352, 602, 795, 954, 1088, 1204, 1306, 1397, 1480,
- 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
- 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
- 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
- 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
- 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
- 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
- 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
- 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
- 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
- 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
- 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
- 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
- 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
- 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
- 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
- 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
- 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
- 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
- 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
-
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+/* Calculate SNR estimation (scaled by 2^24)
-static unsigned int i20Log10(unsigned short val)
-{
- unsigned int rntval = 100;
- unsigned int tmp = val;
- unsigned int exp = 1;
+ 8-VSB SNR equation from Oren datasheets
- while(tmp > 100) {tmp /= 100; exp++;}
+ For 8-VSB:
+ SNR[dB] = 10 * log10(219037.9454 / MSE^2 )
- val = (2 * val)/denom[exp];
- if (exp > 1) rntval = 2000*exp;
+ We re-write the snr equation as:
+ SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+ Where for 8-VSB, c = log10(219037.9454) * 2^24 */
- rntval += i100x20log10[val];
- return rntval;
+static u32 calculate_snr(u32 mse, u32 c)
+{
+ if (mse == 0) /* No signal */
+ return 0;
+
+ mse = 2*intlog10(mse);
+ if (mse > c) {
+ /* Negative SNR, which is possible, but realisticly the
+ demod will lose lock before the signal gets this bad. The
+ API only allows for unsigned values, so just return 0 */
+ return 0;
+ }
+ return 10*(c - mse);
}
-static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct or51211_state* state = fe->demodulator_priv;
u8 rec_buf[2];
- u8 snd_buf[4];
- u8 snr_equ;
- u32 signal_strength;
+ u8 snd_buf[3];
/* SNR after Equalizer */
snd_buf[0] = 0x04;
snd_buf[1] = 0x00;
snd_buf[2] = 0x04;
- snd_buf[3] = 0x00;
if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
- printk(KERN_WARNING "or51211: read_status write error\n");
+ printk(KERN_WARNING "%s: error writing snr reg\n",
+ __FUNCTION__);
return -1;
}
- msleep(3);
if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51211: read_status read error\n");
+ printk(KERN_WARNING "%s: read_status read error\n",
+ __FUNCTION__);
return -1;
}
- snr_equ = rec_buf[0] & 0xff;
- /* The value reported back from the frontend will be FFFF=100% 0000=0% */
- signal_strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000;
- if (signal_strength > 0xffff)
- *strength = 0xffff;
- else
- *strength = signal_strength;
- dprintk("read_signal_strength %i\n",*strength);
+ state->snr = calculate_snr(rec_buf[0], 89599047);
+ *snr = (state->snr) >> 16;
+
+ dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __FUNCTION__, rec_buf[0],
+ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
return 0;
}
-static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
+static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
{
- struct or51211_state* state = fe->demodulator_priv;
- u8 rec_buf[2];
- u8 snd_buf[4];
-
- /* SNR after Equalizer */
- snd_buf[0] = 0x04;
- snd_buf[1] = 0x00;
- snd_buf[2] = 0x04;
- snd_buf[3] = 0x00;
-
- if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
- printk(KERN_WARNING "or51211: read_status write error\n");
- return -1;
- }
- msleep(3);
- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
- printk(KERN_WARNING "or51211: read_status read error\n");
- return -1;
- }
- *snr = rec_buf[0] & 0xff;
-
- dprintk("read_snr %i\n",*snr);
+ /* Calculate Strength from SNR up to 35dB */
+ /* Even though the SNR can go higher than 35dB, there is some comfort */
+ /* factor in having a range of strong signals that can show at 100% */
+ struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv;
+ u16 snr;
+ int ret;
+
+ ret = fe->ops.read_snr(fe, &snr);
+ if (ret != 0)
+ return ret;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
return 0;
}
diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb/frontends/or51211.h
index 13a5a3afbf8..8aad8402d61 100644
--- a/drivers/media/dvb/frontends/or51211.h
+++ b/drivers/media/dvb/frontends/or51211.h
@@ -37,8 +37,17 @@ struct or51211_config
void (*sleep)(struct dvb_frontend * fe);
};
+#if defined(CONFIG_DVB_OR51211) || (defined(CONFIG_DVB_OR51211_MODULE) && defined(MODULE))
extern struct dvb_frontend* or51211_attach(const struct or51211_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_OR51211
#endif // OR51211_H
diff --git a/drivers/media/dvb/frontends/s5h1420.h b/drivers/media/dvb/frontends/s5h1420.h
index 4e39015fa67..1555870f722 100644
--- a/drivers/media/dvb/frontends/s5h1420.h
+++ b/drivers/media/dvb/frontends/s5h1420.h
@@ -34,7 +34,16 @@ struct s5h1420_config
u8 invert:1;
};
+#if defined(CONFIG_DVB_S5H1420) || (defined(CONFIG_DVB_S5H1420_MODULE) && defined(MODULE))
extern struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_S5H1420
#endif // S5H1420_H
diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h
index 93afbb969d6..909cefe7139 100644
--- a/drivers/media/dvb/frontends/sp8870.h
+++ b/drivers/media/dvb/frontends/sp8870.h
@@ -35,7 +35,16 @@ struct sp8870_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_SP8870) || (defined(CONFIG_DVB_SP8870_MODULE) && defined(MODULE))
extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_SP8870
#endif // SP8870_H
diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h
index c44b0ebdf1e..7ee78d7d916 100644
--- a/drivers/media/dvb/frontends/sp887x.h
+++ b/drivers/media/dvb/frontends/sp887x.h
@@ -17,7 +17,16 @@ struct sp887x_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_SP887X) || (defined(CONFIG_DVB_SP887X_MODULE) && defined(MODULE))
extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_SP887X
#endif // SP887X_H
diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h
index 1da5384fb98..69f4515df2b 100644
--- a/drivers/media/dvb/frontends/stv0297.h
+++ b/drivers/media/dvb/frontends/stv0297.h
@@ -42,7 +42,16 @@ struct stv0297_config
u8 stop_during_read:1;
};
+#if defined(CONFIG_DVB_STV0297) || (defined(CONFIG_DVB_STV0297_MODULE) && defined(MODULE))
extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_STV0297
#endif // STV0297_H
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
index 96648a75440..93483769eca 100644
--- a/drivers/media/dvb/frontends/stv0299.c
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -92,11 +92,14 @@ static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data)
return (ret != 1) ? -EREMOTEIO : 0;
}
-int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data)
+int stv0299_write(struct dvb_frontend* fe, u8 *buf, int len)
{
struct stv0299_state* state = fe->demodulator_priv;
- return stv0299_writeregI(state, reg, data);
+ if (len != 2)
+ return -EINVAL;
+
+ return stv0299_writeregI(state, buf[0], buf[1]);
}
static u8 stv0299_readreg (struct stv0299_state* state, u8 reg)
@@ -694,6 +697,7 @@ static struct dvb_frontend_ops stv0299_ops = {
.init = stv0299_init,
.sleep = stv0299_sleep,
+ .write = stv0299_write,
.i2c_gate_ctrl = stv0299_i2c_gate_ctrl,
.set_frontend = stv0299_set_frontend,
@@ -724,5 +728,4 @@ MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, "
"Andreas Oberritter, Andrew de Quincey, Kenneth Aafly");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(stv0299_writereg);
EXPORT_SYMBOL(stv0299_attach);
diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h
index 1504828e423..33df9495908 100644
--- a/drivers/media/dvb/frontends/stv0299.h
+++ b/drivers/media/dvb/frontends/stv0299.h
@@ -89,9 +89,24 @@ struct stv0299_config
int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
};
-extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data);
-
+#if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE))
extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_STV0299
+
+static inline int stv0299_writereg(struct dvb_frontend *fe, u8 reg, u8 val) {
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
#endif // STV0299_H
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
index 9cbd164aa28..dca89171be1 100644
--- a/drivers/media/dvb/frontends/tda10021.c
+++ b/drivers/media/dvb/frontends/tda10021.c
@@ -72,7 +72,7 @@ static u8 tda10021_inittab[0x40]=
0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
};
-static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
+static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
{
u8 buf[] = { reg, data };
struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
@@ -88,14 +88,6 @@ static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
return (ret != 1) ? -EREMOTEIO : 0;
}
-int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data)
-{
- struct tda10021_state* state = fe->demodulator_priv;
-
- return tda10021_writereg(state, reg, data);
-}
-EXPORT_SYMBOL(tda10021_write_byte);
-
static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
{
u8 b0 [] = { reg };
@@ -149,8 +141,8 @@ static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0,
else if (INVERSION_OFF == inversion)
DISABLE_INVERSION(reg0);
- tda10021_writereg (state, 0x00, reg0 & 0xfe);
- tda10021_writereg (state, 0x00, reg0 | 0x01);
+ _tda10021_writereg (state, 0x00, reg0 & 0xfe);
+ _tda10021_writereg (state, 0x00, reg0 | 0x01);
state->reg0 = reg0;
return 0;
@@ -198,17 +190,27 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate
NDEC = (NDEC << 6) | tda10021_inittab[0x03];
- tda10021_writereg (state, 0x03, NDEC);
- tda10021_writereg (state, 0x0a, BDR&0xff);
- tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
- tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
+ _tda10021_writereg (state, 0x03, NDEC);
+ _tda10021_writereg (state, 0x0a, BDR&0xff);
+ _tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
+ _tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
- tda10021_writereg (state, 0x0d, BDRI);
- tda10021_writereg (state, 0x0e, SFIL);
+ _tda10021_writereg (state, 0x0d, BDRI);
+ _tda10021_writereg (state, 0x0e, SFIL);
return 0;
}
+int tda10021_write(struct dvb_frontend* fe, u8 *buf, int len)
+{
+ struct tda10021_state* state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return _tda10021_writereg(state, buf[0], buf[1]);
+}
+
static int tda10021_init (struct dvb_frontend *fe)
{
struct tda10021_state* state = fe->demodulator_priv;
@@ -216,12 +218,12 @@ static int tda10021_init (struct dvb_frontend *fe)
dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
- //tda10021_writereg (fe, 0, 0);
+ //_tda10021_writereg (fe, 0, 0);
for (i=0; i<tda10021_inittab_size; i++)
- tda10021_writereg (state, i, tda10021_inittab[i]);
+ _tda10021_writereg (state, i, tda10021_inittab[i]);
- tda10021_writereg (state, 0x34, state->pwm);
+ _tda10021_writereg (state, 0x34, state->pwm);
//Comment by markus
//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
@@ -230,7 +232,7 @@ static int tda10021_init (struct dvb_frontend *fe)
//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
//Activate PLL
- tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
+ _tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
return 0;
}
@@ -264,12 +266,12 @@ static int tda10021_set_parameters (struct dvb_frontend *fe,
}
tda10021_set_symbolrate (state, p->u.qam.symbol_rate);
- tda10021_writereg (state, 0x34, state->pwm);
+ _tda10021_writereg (state, 0x34, state->pwm);
- tda10021_writereg (state, 0x01, reg0x01[qam]);
- tda10021_writereg (state, 0x05, reg0x05[qam]);
- tda10021_writereg (state, 0x08, reg0x08[qam]);
- tda10021_writereg (state, 0x09, reg0x09[qam]);
+ _tda10021_writereg (state, 0x01, reg0x01[qam]);
+ _tda10021_writereg (state, 0x05, reg0x05[qam]);
+ _tda10021_writereg (state, 0x08, reg0x08[qam]);
+ _tda10021_writereg (state, 0x09, reg0x09[qam]);
tda10021_setup_reg0 (state, reg0x00[qam], p->inversion);
@@ -342,8 +344,8 @@ static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
*ucblocks = 0xffffffff;
/* reset uncorrected block counter */
- tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
- tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
+ _tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
+ _tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
return 0;
}
@@ -392,8 +394,8 @@ static int tda10021_sleep(struct dvb_frontend* fe)
{
struct tda10021_state* state = fe->demodulator_priv;
- tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */
- tda10021_writereg (state, 0x00, 0x80); /* standby */
+ _tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */
+ _tda10021_writereg (state, 0x00, 0x80); /* standby */
return 0;
}
@@ -459,6 +461,7 @@ static struct dvb_frontend_ops tda10021_ops = {
.init = tda10021_init,
.sleep = tda10021_sleep,
+ .write = tda10021_write,
.i2c_gate_ctrl = tda10021_i2c_gate_ctrl,
.set_frontend = tda10021_set_parameters,
diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h
index b1df4259bee..e3da780108f 100644
--- a/drivers/media/dvb/frontends/tda10021.h
+++ b/drivers/media/dvb/frontends/tda10021.h
@@ -32,9 +32,24 @@ struct tda10021_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE))
extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
struct i2c_adapter* i2c, u8 pwm);
-
-extern int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data);
+#else
+static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+ struct i2c_adapter* i2c, u8 pwm)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA10021
+
+static inline int tda10021_writereg(struct dvb_frontend *fe, u8 reg, u8 val) {
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
#endif // TDA10021_H
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index 59a2ed614fc..00e4bcd9f1a 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -579,11 +579,14 @@ static int tda1004x_decode_fec(int tdafec)
return -1;
}
-int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data)
+int tda1004x_write(struct dvb_frontend* fe, u8 *buf, int len)
{
struct tda1004x_state* state = fe->demodulator_priv;
- return tda1004x_write_byteI(state, reg, data);
+ if (len != 2)
+ return -EINVAL;
+
+ return tda1004x_write_byteI(state, buf[0], buf[1]);
}
static int tda10045_init(struct dvb_frontend* fe)
@@ -645,18 +648,24 @@ static int tda10046_init(struct dvb_frontend* fe)
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities
break;
- case TDA10046_AGC_TDA827X:
+ case TDA10046_AGC_TDA827X_GP11:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities
break;
- case TDA10046_AGC_TDA827X_GPL:
+ case TDA10046_AGC_TDA827X_GP00:
tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
break;
+ case TDA10046_AGC_TDA827X_GP01:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
+ tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
+ tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
+ tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities
+ break;
}
tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
@@ -1216,6 +1225,7 @@ static struct dvb_frontend_ops tda10045_ops = {
.init = tda10045_init,
.sleep = tda1004x_sleep,
+ .write = tda1004x_write,
.i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
.set_frontend = tda1004x_set_fe,
@@ -1274,6 +1284,7 @@ static struct dvb_frontend_ops tda10046_ops = {
.init = tda10046_init,
.sleep = tda1004x_sleep,
+ .write = tda1004x_write,
.i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
.set_frontend = tda1004x_set_fe,
@@ -1323,4 +1334,3 @@ MODULE_LICENSE("GPL");
EXPORT_SYMBOL(tda10045_attach);
EXPORT_SYMBOL(tda10046_attach);
-EXPORT_SYMBOL(tda1004x_write_byte);
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
index b877b23ed73..ec502d71b83 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -35,8 +35,9 @@ enum tda10046_agc {
TDA10046_AGC_DEFAULT, /* original configuration */
TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */
TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */
- TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */
- TDA10046_AGC_TDA827X_GPL, /* same as above, but GPIOs 0 */
+ TDA10046_AGC_TDA827X_GP11, /* IF AGC only, special setup for tda827x */
+ TDA10046_AGC_TDA827X_GP00, /* same as above, but GPIOs 0 */
+ TDA10046_AGC_TDA827X_GP01, /* same as above, but GPIO3=0 GPIO1=1*/
};
enum tda10046_if {
@@ -71,12 +72,33 @@ struct tda1004x_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE))
extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
struct i2c_adapter* i2c);
extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
struct i2c_adapter* i2c);
-
-extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data);
+#else
+static inline struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+static inline struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA1004X
+
+static inline int tda1004x_writereg(struct dvb_frontend *fe, u8 reg, u8 val) {
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
#endif // TDA1004X_H
diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c
new file mode 100644
index 00000000000..4c27a2d90a3
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10086.c
@@ -0,0 +1,744 @@
+ /*
+ Driver for Philips tda10086 DVBS Demodulator
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda10086.h"
+
+#define SACLK 96000000
+
+struct tda10086_state {
+ struct i2c_adapter* i2c;
+ const struct tda10086_config* config;
+ struct dvb_frontend frontend;
+
+ /* private demod data */
+ u32 frequency;
+ u32 symbol_rate;
+};
+
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda10086: " args); \
+ } while (0)
+
+static int tda10086_write_byte(struct tda10086_state *state, int reg, int data)
+{
+ int ret;
+ u8 b0[] = { reg, data };
+ struct i2c_msg msg = { .flags = 0, .buf = b0, .len = 2 };
+
+ msg.addr = state->config->demod_address;
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
+ __FUNCTION__, reg, data, ret);
+
+ return (ret != 1) ? ret : 0;
+}
+
+static int tda10086_read_byte(struct tda10086_state *state, int reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 },
+ { .flags = I2C_M_RD, .buf = b1, .len = 1 }};
+
+ msg[0].addr = state->config->demod_address;
+ msg[1].addr = state->config->demod_address;
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg,
+ ret);
+ return ret;
+ }
+
+ return b1[0];
+}
+
+static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, int data)
+{
+ int val;
+
+ // read a byte and check
+ val = tda10086_read_byte(state, reg);
+ if (val < 0)
+ return val;
+
+ // mask if off
+ val = val & ~mask;
+ val |= data & 0xff;
+
+ // write it out again
+ return tda10086_write_byte(state, reg, val);
+}
+
+static int tda10086_init(struct dvb_frontend* fe)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // reset
+ tda10086_write_byte(state, 0x00, 0x00);
+ msleep(10);
+
+ // misc setup
+ tda10086_write_byte(state, 0x01, 0x94);
+ tda10086_write_byte(state, 0x02, 0x35); // NOTE: TT drivers appear to disable CSWP
+ tda10086_write_byte(state, 0x03, 0x64);
+ tda10086_write_byte(state, 0x04, 0x43);
+ tda10086_write_byte(state, 0x0c, 0x0c);
+ tda10086_write_byte(state, 0x1b, 0xb0); // noise threshold
+ tda10086_write_byte(state, 0x20, 0x89); // misc
+ tda10086_write_byte(state, 0x30, 0x04); // acquisition period length
+ tda10086_write_byte(state, 0x32, 0x00); // irq off
+ tda10086_write_byte(state, 0x31, 0x56); // setup AFC
+
+ // setup PLL (assumes 16Mhz XIN)
+ tda10086_write_byte(state, 0x55, 0x2c); // misc PLL setup
+ tda10086_write_byte(state, 0x3a, 0x0b); // M=12
+ tda10086_write_byte(state, 0x3b, 0x01); // P=2
+ tda10086_write_mask(state, 0x55, 0x20, 0x00); // powerup PLL
+
+ // setup TS interface
+ tda10086_write_byte(state, 0x11, 0x81);
+ tda10086_write_byte(state, 0x12, 0x81);
+ tda10086_write_byte(state, 0x19, 0x40); // parallel mode A + MSBFIRST
+ tda10086_write_byte(state, 0x56, 0x80); // powerdown WPLL - unused in the mode we use
+ tda10086_write_byte(state, 0x57, 0x08); // bypass WPLL - unused in the mode we use
+ tda10086_write_byte(state, 0x10, 0x2a);
+
+ // setup ADC
+ tda10086_write_byte(state, 0x58, 0x61); // ADC setup
+ tda10086_write_mask(state, 0x58, 0x01, 0x00); // powerup ADC
+
+ // setup AGC
+ tda10086_write_byte(state, 0x05, 0x0B);
+ tda10086_write_byte(state, 0x37, 0x63);
+ tda10086_write_byte(state, 0x3f, 0x03); // NOTE: flydvb uses 0x0a and varies it
+ tda10086_write_byte(state, 0x40, 0x64);
+ tda10086_write_byte(state, 0x41, 0x4f);
+ tda10086_write_byte(state, 0x42, 0x43);
+
+ // setup viterbi
+ tda10086_write_byte(state, 0x1a, 0x11); // VBER 10^6, DVB, QPSK
+
+ // setup carrier recovery
+ tda10086_write_byte(state, 0x3d, 0x80);
+
+ // setup SEC
+ tda10086_write_byte(state, 0x36, 0x00); // all SEC off
+ tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); // } tone frequency
+ tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); // }
+
+ return 0;
+}
+
+static void tda10086_diseqc_wait(struct tda10086_state *state)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(200);
+ while (!(tda10086_read_byte(state, 0x50) & 0x01)) {
+ if(time_after(jiffies, timeout)) {
+ printk("%s: diseqc queue not ready, command may be lost.\n", __FUNCTION__);
+ break;
+ }
+ msleep(10);
+ }
+}
+
+static int tda10086_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch(tone) {
+ case SEC_TONE_OFF:
+ tda10086_write_byte(state, 0x36, 0x00);
+ break;
+
+ case SEC_TONE_ON:
+ tda10086_write_byte(state, 0x36, 0x01);
+ break;
+ }
+
+ return 0;
+}
+
+static int tda10086_send_master_cmd (struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd* cmd)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ int i;
+ u8 oldval;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (cmd->msg_len > 6)
+ return -EINVAL;
+ oldval = tda10086_read_byte(state, 0x36);
+
+ for(i=0; i< cmd->msg_len; i++) {
+ tda10086_write_byte(state, 0x48+i, cmd->msg[i]);
+ }
+ tda10086_write_byte(state, 0x36, 0x08 | ((cmd->msg_len + 1) << 4));
+
+ tda10086_diseqc_wait(state);
+
+ tda10086_write_byte(state, 0x36, oldval);
+
+ return 0;
+}
+
+static int tda10086_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 oldval = tda10086_read_byte(state, 0x36);
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch(minicmd) {
+ case SEC_MINI_A:
+ tda10086_write_byte(state, 0x36, 0x04);
+ break;
+
+ case SEC_MINI_B:
+ tda10086_write_byte(state, 0x36, 0x06);
+ break;
+ }
+
+ tda10086_diseqc_wait(state);
+
+ tda10086_write_byte(state, 0x36, oldval);
+
+ return 0;
+}
+
+static int tda10086_set_inversion(struct tda10086_state *state,
+ struct dvb_frontend_parameters *fe_params)
+{
+ u8 invval = 0x80;
+
+ dprintk ("%s %i %i\n", __FUNCTION__, fe_params->inversion, state->config->invert);
+
+ switch(fe_params->inversion) {
+ case INVERSION_OFF:
+ if (state->config->invert)
+ invval = 0x40;
+ break;
+ case INVERSION_ON:
+ if (!state->config->invert)
+ invval = 0x40;
+ break;
+ case INVERSION_AUTO:
+ invval = 0x00;
+ break;
+ }
+ tda10086_write_mask(state, 0x0c, 0xc0, invval);
+
+ return 0;
+}
+
+static int tda10086_set_symbol_rate(struct tda10086_state *state,
+ struct dvb_frontend_parameters *fe_params)
+{
+ u8 dfn = 0;
+ u8 afs = 0;
+ u8 byp = 0;
+ u8 reg37 = 0x43;
+ u8 reg42 = 0x43;
+ u64 big;
+ u32 tmp;
+ u32 bdr;
+ u32 bdri;
+ u32 symbol_rate = fe_params->u.qpsk.symbol_rate;
+
+ dprintk ("%s %i\n", __FUNCTION__, symbol_rate);
+
+ // setup the decimation and anti-aliasing filters..
+ if (symbol_rate < (u32) (SACLK * 0.0137)) {
+ dfn=4;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.0208)) {
+ dfn=4;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.0270)) {
+ dfn=3;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.0416)) {
+ dfn=3;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.0550)) {
+ dfn=2;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.0833)) {
+ dfn=2;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.1100)) {
+ dfn=1;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.1666)) {
+ dfn=1;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.2200)) {
+ dfn=0;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.3333)) {
+ dfn=0;
+ afs=0;
+ } else {
+ reg37 = 0x63;
+ reg42 = 0x4f;
+ byp=1;
+ }
+
+ // calculate BDR
+ big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<<dfn);
+ big += ((SACLK/1000ULL)-1ULL);
+ do_div(big, (SACLK/1000ULL));
+ bdr = big & 0xfffff;
+
+ // calculate BDRI
+ tmp = (1<<dfn)*(symbol_rate/1000);
+ bdri = ((32 * (SACLK/1000)) + (tmp-1)) / tmp;
+
+ tda10086_write_byte(state, 0x21, (afs << 7) | dfn);
+ tda10086_write_mask(state, 0x20, 0x08, byp << 3);
+ tda10086_write_byte(state, 0x06, bdr);
+ tda10086_write_byte(state, 0x07, bdr >> 8);
+ tda10086_write_byte(state, 0x08, bdr >> 16);
+ tda10086_write_byte(state, 0x09, bdri);
+ tda10086_write_byte(state, 0x37, reg37);
+ tda10086_write_byte(state, 0x42, reg42);
+
+ return 0;
+}
+
+static int tda10086_set_fec(struct tda10086_state *state,
+ struct dvb_frontend_parameters *fe_params)
+{
+ u8 fecval;
+
+ dprintk ("%s %i\n", __FUNCTION__, fe_params->u.qpsk.fec_inner);
+
+ switch(fe_params->u.qpsk.fec_inner) {
+ case FEC_1_2:
+ fecval = 0x00;
+ break;
+ case FEC_2_3:
+ fecval = 0x01;
+ break;
+ case FEC_3_4:
+ fecval = 0x02;
+ break;
+ case FEC_4_5:
+ fecval = 0x03;
+ break;
+ case FEC_5_6:
+ fecval = 0x04;
+ break;
+ case FEC_6_7:
+ fecval = 0x05;
+ break;
+ case FEC_7_8:
+ fecval = 0x06;
+ break;
+ case FEC_8_9:
+ fecval = 0x07;
+ break;
+ case FEC_AUTO:
+ fecval = 0x08;
+ break;
+ default:
+ return -1;
+ }
+ tda10086_write_byte(state, 0x0d, fecval);
+
+ return 0;
+}
+
+static int tda10086_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fe_params)
+{
+ struct tda10086_state *state = fe->demodulator_priv;
+ int ret;
+ u32 freq = 0;
+ int freqoff;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // set params
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, fe_params);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (fe->ops.tuner_ops.get_frequency)
+ fe->ops.tuner_ops.get_frequency(fe, &freq);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ // calcluate the frequency offset (in *Hz* not kHz)
+ freqoff = fe_params->frequency - freq;
+ freqoff = ((1<<16) * freqoff) / (SACLK/1000);
+ tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f));
+ tda10086_write_byte(state, 0x3e, freqoff);
+
+ if ((ret = tda10086_set_inversion(state, fe_params)) < 0)
+ return ret;
+ if ((ret = tda10086_set_symbol_rate(state, fe_params)) < 0)
+ return ret;
+ if ((ret = tda10086_set_fec(state, fe_params)) < 0)
+ return ret;
+
+ // soft reset + disable TS output until lock
+ tda10086_write_mask(state, 0x10, 0x40, 0x40);
+ tda10086_write_mask(state, 0x00, 0x01, 0x00);
+
+ state->symbol_rate = fe_params->u.qpsk.symbol_rate;
+ state->frequency = fe_params->frequency;
+ return 0;
+}
+
+static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 val;
+ int tmp;
+ u64 tmp64;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // check for invalid symbol rate
+ if (fe_params->u.qpsk.symbol_rate < 500000)
+ return -EINVAL;
+
+ // calculate the updated frequency (note: we convert from Hz->kHz)
+ tmp64 = tda10086_read_byte(state, 0x52);
+ tmp64 |= (tda10086_read_byte(state, 0x51) << 8);
+ if (tmp64 & 0x8000)
+ tmp64 |= 0xffffffffffff0000ULL;
+ tmp64 = (tmp64 * (SACLK/1000ULL));
+ do_div(tmp64, (1ULL<<15) * (1ULL<<1));
+ fe_params->frequency = (int) state->frequency + (int) tmp64;
+
+ // the inversion
+ val = tda10086_read_byte(state, 0x0c);
+ if (val & 0x80) {
+ switch(val & 0x40) {
+ case 0x00:
+ fe_params->inversion = INVERSION_OFF;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_ON;
+ break;
+ default:
+ fe_params->inversion = INVERSION_ON;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_OFF;
+ break;
+ }
+ } else {
+ tda10086_read_byte(state, 0x0f);
+ switch(val & 0x02) {
+ case 0x00:
+ fe_params->inversion = INVERSION_OFF;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_ON;
+ break;
+ default:
+ fe_params->inversion = INVERSION_ON;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_OFF;
+ break;
+ }
+ }
+
+ // calculate the updated symbol rate
+ tmp = tda10086_read_byte(state, 0x1d);
+ if (tmp & 0x80)
+ tmp |= 0xffffff00;
+ tmp = (tmp * 480 * (1<<1)) / 128;
+ tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000);
+ fe_params->u.qpsk.symbol_rate = state->symbol_rate + tmp;
+
+ // the FEC
+ val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4;
+ switch(val) {
+ case 0x00:
+ fe_params->u.qpsk.fec_inner = FEC_1_2;
+ break;
+ case 0x01:
+ fe_params->u.qpsk.fec_inner = FEC_2_3;
+ break;
+ case 0x02:
+ fe_params->u.qpsk.fec_inner = FEC_3_4;
+ break;
+ case 0x03:
+ fe_params->u.qpsk.fec_inner = FEC_4_5;
+ break;
+ case 0x04:
+ fe_params->u.qpsk.fec_inner = FEC_5_6;
+ break;
+ case 0x05:
+ fe_params->u.qpsk.fec_inner = FEC_6_7;
+ break;
+ case 0x06:
+ fe_params->u.qpsk.fec_inner = FEC_7_8;
+ break;
+ case 0x07:
+ fe_params->u.qpsk.fec_inner = FEC_8_9;
+ break;
+ }
+
+ return 0;
+}
+
+static int tda10086_read_status(struct dvb_frontend* fe, fe_status_t *fe_status)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ val = tda10086_read_byte(state, 0x0e);
+ *fe_status = 0;
+ if (val & 0x01)
+ *fe_status |= FE_HAS_SIGNAL;
+ if (val & 0x02)
+ *fe_status |= FE_HAS_CARRIER;
+ if (val & 0x04)
+ *fe_status |= FE_HAS_VITERBI;
+ if (val & 0x08)
+ *fe_status |= FE_HAS_SYNC;
+ if (val & 0x10)
+ *fe_status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int tda10086_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 _str;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ _str = tda10086_read_byte(state, 0x43);
+ *signal = (_str << 8) | _str;
+
+ return 0;
+}
+
+static int tda10086_read_snr(struct dvb_frontend* fe, u16 * snr)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 _snr;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ _snr = tda10086_read_byte(state, 0x1c);
+ *snr = (_snr << 8) | _snr;
+
+ return 0;
+}
+
+static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // read it
+ *ucblocks = tda10086_read_byte(state, 0x18) & 0x7f;
+
+ // reset counter
+ tda10086_write_byte(state, 0x18, 0x00);
+ tda10086_write_byte(state, 0x18, 0x80);
+
+ return 0;
+}
+
+static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // read it
+ *ber = 0;
+ *ber |= tda10086_read_byte(state, 0x15);
+ *ber |= tda10086_read_byte(state, 0x16) << 8;
+ *ber |= (tda10086_read_byte(state, 0x17) & 0xf) << 16;
+
+ return 0;
+}
+
+static int tda10086_sleep(struct dvb_frontend* fe)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ tda10086_write_mask(state, 0x00, 0x08, 0x08);
+
+ return 0;
+}
+
+static int tda10086_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (enable) {
+ tda10086_write_mask(state, 0x00, 0x10, 0x10);
+ } else {
+ tda10086_write_mask(state, 0x00, 0x10, 0x00);
+ }
+
+ return 0;
+}
+
+static int tda10086_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ if (fesettings->parameters.u.qpsk.symbol_rate > 20000000) {
+ fesettings->min_delay_ms = 50;
+ fesettings->step_size = 2000;
+ fesettings->max_drift = 8000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 12000000) {
+ fesettings->min_delay_ms = 100;
+ fesettings->step_size = 1500;
+ fesettings->max_drift = 9000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 8000000) {
+ fesettings->min_delay_ms = 100;
+ fesettings->step_size = 1000;
+ fesettings->max_drift = 8000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 4000000) {
+ fesettings->min_delay_ms = 100;
+ fesettings->step_size = 500;
+ fesettings->max_drift = 7000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 2000000) {
+ fesettings->min_delay_ms = 200;
+ fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000);
+ fesettings->max_drift = 14 * fesettings->step_size;
+ } else {
+ fesettings->min_delay_ms = 200;
+ fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000);
+ fesettings->max_drift = 18 * fesettings->step_size;
+ }
+
+ return 0;
+}
+
+static void tda10086_release(struct dvb_frontend* fe)
+{
+ struct tda10086_state *state = fe->demodulator_priv;
+ tda10086_sleep(fe);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda10086_ops = {
+
+ .info = {
+ .name = "Philips TDA10086 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 125, /* kHz for QPSK frontends */
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK
+ },
+
+ .release = tda10086_release,
+
+ .init = tda10086_init,
+ .sleep = tda10086_sleep,
+ .i2c_gate_ctrl = tda10086_i2c_gate_ctrl,
+
+ .set_frontend = tda10086_set_frontend,
+ .get_frontend = tda10086_get_frontend,
+ .get_tune_settings = tda10086_get_tune_settings,
+
+ .read_status = tda10086_read_status,
+ .read_ber = tda10086_read_ber,
+ .read_signal_strength = tda10086_read_signal_strength,
+ .read_snr = tda10086_read_snr,
+ .read_ucblocks = tda10086_read_ucblocks,
+
+ .diseqc_send_master_cmd = tda10086_send_master_cmd,
+ .diseqc_send_burst = tda10086_send_burst,
+ .set_tone = tda10086_set_tone,
+};
+
+struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda10086_state *state;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ /* check if the demod is there */
+ if (tda10086_read_byte(state, 0x1e) != 0xe1) {
+ kfree(state);
+ return NULL;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &tda10086_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10086_attach);
diff --git a/drivers/media/dvb/frontends/tda10086.h b/drivers/media/dvb/frontends/tda10086.h
new file mode 100644
index 00000000000..ed584a8f4a8
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10086.h
@@ -0,0 +1,50 @@
+ /*
+ Driver for Philips tda10086 DVBS Frontend
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef TDA10086_H
+#define TDA10086_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct tda10086_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* does the "inversion" need inverted? */
+ u8 invert;
+};
+
+#if defined(CONFIG_DVB_TDA10086) || (defined(CONFIG_DVB_TDA10086_MODULE) && defined(MODULE))
+extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
+ struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA10086
+
+#endif // TDA10086_H
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c
index 3aa45ebbac3..67415c9db6f 100644
--- a/drivers/media/dvb/frontends/tda8083.c
+++ b/drivers/media/dvb/frontends/tda8083.c
@@ -262,12 +262,29 @@ static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status)
if (sync & 0x10)
*status |= FE_HAS_SYNC;
+ if (sync & 0x20) /* frontend can not lock */
+ *status |= FE_TIMEDOUT;
+
if ((sync & 0x1f) == 0x1f)
*status |= FE_HAS_LOCK;
return 0;
}
+static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda8083_state* state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3];
+
+ if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf))))
+ return ret;
+
+ *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2];
+
+ return 0;
+}
+
static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
{
struct tda8083_state* state = fe->demodulator_priv;
@@ -288,6 +305,17 @@ static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr)
return 0;
}
+static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda8083_state* state = fe->demodulator_priv;
+
+ *ucblocks = tda8083_readreg(state, 0x0f);
+ if (*ucblocks == 0xff)
+ *ucblocks = 0xffffffff;
+
+ return 0;
+}
+
static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
{
struct tda8083_state* state = fe->demodulator_priv;
@@ -440,6 +468,8 @@ static struct dvb_frontend_ops tda8083_ops = {
.read_status = tda8083_read_status,
.read_signal_strength = tda8083_read_signal_strength,
.read_snr = tda8083_read_snr,
+ .read_ber = tda8083_read_ber,
+ .read_ucblocks = tda8083_read_ucblocks,
.diseqc_send_master_cmd = tda8083_send_diseqc_msg,
.diseqc_send_burst = tda8083_diseqc_send_burst,
diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h
index e7a48f61ea2..2d3307999f2 100644
--- a/drivers/media/dvb/frontends/tda8083.h
+++ b/drivers/media/dvb/frontends/tda8083.h
@@ -35,7 +35,16 @@ struct tda8083_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_TDA8083) || (defined(CONFIG_DVB_TDA8083_MODULE) && defined(MODULE))
extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA8083
#endif // TDA8083_H
diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c
new file mode 100644
index 00000000000..79f971dc52b
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda826x.c
@@ -0,0 +1,175 @@
+ /*
+ Driver for Philips tda8262/tda8263 DVBS Silicon tuners
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "tda826x.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda826x: " args); \
+ } while (0)
+
+struct tda826x_priv {
+ /* i2c details */
+ int i2c_address;
+ struct i2c_adapter *i2c;
+ u8 has_loopthrough:1;
+ u32 frequency;
+};
+
+static int tda826x_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int tda826x_sleep(struct dvb_frontend *fe)
+{
+ struct tda826x_priv *priv = fe->tuner_priv;
+ int ret;
+ u8 buf [] = { 0x00, 0x8d };
+ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 };
+
+ dprintk("%s:\n", __FUNCTION__);
+
+ if (!priv->has_loopthrough)
+ buf[1] = 0xad;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
+ dprintk("%s: i2c error\n", __FUNCTION__);
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int tda826x_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ struct tda826x_priv *priv = fe->tuner_priv;
+ int ret;
+ u32 div;
+ u8 buf [11];
+ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 };
+
+ dprintk("%s:\n", __FUNCTION__);
+
+ div = (params->frequency + (1000-1)) / 1000;
+
+ buf[0] = 0x00; // subaddress
+ buf[1] = 0x09; // powerdown RSSI + the magic value 1
+ if (!priv->has_loopthrough)
+ buf[1] |= 0x20; // power down loopthrough if not needed
+ buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO
+ buf[3] = div >> 7;
+ buf[4] = div << 1;
+ buf[5] = 0xff; // basedband filter to max
+ buf[6] = 0xfe; // gains at max + no RF attenuation
+ buf[7] = 0x83; // charge pumps at high, tests off
+ buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports.
+ buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL
+ buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
+ dprintk("%s: i2c error\n", __FUNCTION__);
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ priv->frequency = div * 1000;
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda826x_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops tda826x_tuner_ops = {
+ .info = {
+ .name = "Philips TDA826X",
+ .frequency_min = 950000,
+ .frequency_max = 2175000
+ },
+ .release = tda826x_release,
+ .sleep = tda826x_sleep,
+ .set_params = tda826x_set_params,
+ .get_frequency = tda826x_get_frequency,
+};
+
+struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough)
+{
+ struct tda826x_priv *priv = NULL;
+ u8 b1 [] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ { .addr = addr, .flags = 0, .buf = NULL, .len = 0 },
+ { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 }
+ };
+ int ret;
+
+ dprintk("%s:\n", __FUNCTION__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = i2c_transfer (i2c, msg, 2);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ return NULL;
+ if (!(b1[1] & 0x80))
+ return NULL;
+
+ priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c_address = addr;
+ priv->i2c = i2c;
+ priv->has_loopthrough = has_loopthrough;
+
+ memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ return fe;
+}
+EXPORT_SYMBOL(tda826x_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("DVB TDA826x driver");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda826x.h b/drivers/media/dvb/frontends/tda826x.h
new file mode 100644
index 00000000000..ad998119596
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda826x.h
@@ -0,0 +1,53 @@
+ /*
+ Driver for Philips tda8262/tda8263 DVBS Silicon tuners
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef __DVB_TDA826X_H__
+#define __DVB_TDA826X_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/**
+ * Attach a tda826x tuner to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param addr i2c address of the tuner.
+ * @param i2c i2c adapter to use.
+ * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector.
+ * @return FE pointer on success, NULL on failure.
+ */
+#if defined(CONFIG_DVB_TDA826X) || (defined(CONFIG_DVB_TDA826X_MODULE) && defined(MODULE))
+extern struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, int addr,
+ struct i2c_adapter *i2c,
+ int has_loopthrough);
+#else
+static inline struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe,
+ int addr,
+ struct i2c_adapter *i2c,
+ int has_loopthrough)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA826X
+
+#endif // __DVB_TDA826X_H__
diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c
new file mode 100644
index 00000000000..6ba0029dcf2
--- /dev/null
+++ b/drivers/media/dvb/frontends/tua6100.c
@@ -0,0 +1,204 @@
+/**
+ * Driver for Infineon tua6100 pll.
+ *
+ * (c) 2006 Andrew de Quincey
+ *
+ * Based on code found in budget-av.c, which has the following:
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
+ * Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ * 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/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "tua6100.h"
+
+struct tua6100_priv {
+ /* i2c details */
+ int i2c_address;
+ struct i2c_adapter *i2c;
+ u32 frequency;
+};
+
+static int tua6100_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int tua6100_sleep(struct dvb_frontend *fe)
+{
+ struct tua6100_priv *priv = fe->tuner_priv;
+ int ret;
+ u8 reg0[] = { 0x00, 0x00 };
+ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
+ printk("%s: i2c error\n", __FUNCTION__);
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int tua6100_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tua6100_priv *priv = fe->tuner_priv;
+ u32 div;
+ u32 prediv;
+ u8 reg0[] = { 0x00, 0x00 };
+ u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 };
+ u8 reg2[] = { 0x02, 0x00, 0x00 };
+ struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
+ struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 };
+ struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 };
+
+#define _R 4
+#define _P 32
+#define _ri 4000000
+
+ // setup register 0
+ if (params->frequency < 2000000) {
+ reg0[1] = 0x03;
+ } else {
+ reg0[1] = 0x07;
+ }
+
+ // setup register 1
+ if (params->frequency < 1630000) {
+ reg1[1] = 0x2c;
+ } else {
+ reg1[1] = 0x0c;
+ }
+ if (_P == 64)
+ reg1[1] |= 0x40;
+ if (params->frequency >= 1525000)
+ reg1[1] |= 0x80;
+
+ // register 2
+ reg2[1] = (_R >> 8) & 0x03;
+ reg2[2] = _R;
+ if (params->frequency < 1455000) {
+ reg2[1] |= 0x1c;
+ } else if (params->frequency < 1630000) {
+ reg2[1] |= 0x0c;
+ } else {
+ reg2[1] |= 0x1c;
+ }
+
+ // The N divisor ratio (note: params->frequency is in kHz, but we need it in Hz)
+ prediv = (params->frequency * _R) / (_ri / 1000);
+ div = prediv / _P;
+ reg1[1] |= (div >> 9) & 0x03;
+ reg1[2] = div >> 1;
+ reg1[3] = (div << 7);
+ priv->frequency = ((div * _P) * (_ri / 1000)) / _R;
+
+ // Finally, calculate and store the value for A
+ reg1[3] |= (prediv - (div*_P)) & 0x7f;
+
+#undef _R
+#undef _P
+#undef _ri
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c, &msg0, 1) != 1)
+ return -EIO;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c, &msg2, 1) != 1)
+ return -EIO;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c, &msg1, 1) != 1)
+ return -EIO;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tua6100_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops tua6100_tuner_ops = {
+ .info = {
+ .name = "Infineon TUA6100",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 1000,
+ },
+ .release = tua6100_release,
+ .sleep = tua6100_sleep,
+ .set_params = tua6100_set_params,
+ .get_frequency = tua6100_get_frequency,
+};
+
+struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c)
+{
+ struct tua6100_priv *priv = NULL;
+ u8 b1 [] = { 0x80 };
+ u8 b2 [] = { 0x00 };
+ struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 },
+ { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } };
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = i2c_transfer (i2c, msg, 2);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ return NULL;
+
+ priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c_address = addr;
+ priv->i2c = i2c;
+
+ memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ return fe;
+}
+EXPORT_SYMBOL(tua6100_attach);
+
+MODULE_DESCRIPTION("DVB tua6100 driver");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tua6100.h b/drivers/media/dvb/frontends/tua6100.h
new file mode 100644
index 00000000000..03a665e7df6
--- /dev/null
+++ b/drivers/media/dvb/frontends/tua6100.h
@@ -0,0 +1,47 @@
+/**
+ * Driver for Infineon tua6100 PLL.
+ *
+ * (c) 2006 Andrew de Quincey
+ *
+ * Based on code found in budget-av.c, which has the following:
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
+ * Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __DVB_TUA6100_H__
+#define __DVB_TUA6100_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+#if defined(CONFIG_DVB_TUA6100) || (defined(CONFIG_DVB_TUA6100_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUA6100
+
+#endif
diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h
index 520f09522fb..e4a2a324046 100644
--- a/drivers/media/dvb/frontends/ves1820.h
+++ b/drivers/media/dvb/frontends/ves1820.h
@@ -41,7 +41,16 @@ struct ves1820_config
u8 selagc:1;
};
+#if defined(CONFIG_DVB_VES1820) || (defined(CONFIG_DVB_VES1820_MODULE) && defined(MODULE))
extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
struct i2c_adapter* i2c, u8 pwm);
+#else
+static inline struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+ struct i2c_adapter* i2c, u8 pwm)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_VES1820
#endif // VES1820_H
diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h
index ba88ae0855c..d507f8966f8 100644
--- a/drivers/media/dvb/frontends/ves1x93.h
+++ b/drivers/media/dvb/frontends/ves1x93.h
@@ -40,7 +40,16 @@ struct ves1x93_config
u8 invert_pwm:1;
};
+#if defined(CONFIG_DVB_VES1X93) || (defined(CONFIG_DVB_VES1X93_MODULE) && defined(MODULE))
extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_VES1X93
#endif // VES1X93_H
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index 2b95e8b6cd3..0e9b59af271 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -140,6 +140,8 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
zl10353_single_write(fe, 0x5E, 0x00);
zl10353_single_write(fe, 0x65, 0x5A);
zl10353_single_write(fe, 0x66, 0xE9);
+ zl10353_single_write(fe, 0x6C, 0xCD);
+ zl10353_single_write(fe, 0x6D, 0x7E);
zl10353_single_write(fe, 0x62, 0x0A);
// if there is no attached secondary tuner, we call set_params to program
@@ -168,6 +170,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
// even if there isn't a PLL attached to the secondary bus
zl10353_write(fe, pllbuf, sizeof(pllbuf));
+ zl10353_single_write(fe, 0x5F, 0x13);
zl10353_single_write(fe, 0x70, 0x01);
udelay(250);
zl10353_single_write(fe, 0xE4, 0x00);
@@ -243,9 +246,12 @@ static int zl10353_init(struct dvb_frontend *fe)
if (debug_regs)
zl10353_dump_regs(fe);
+ if (state->config.parallel_ts)
+ zl10353_reset_attach[2] &= ~0x20;
/* Do a "hard" reset if not already done */
- if (zl10353_read_register(state, 0x50) != 0x03) {
+ if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] ||
+ zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) {
rc = zl10353_write(fe, zl10353_reset_attach,
sizeof(zl10353_reset_attach));
if (debug_regs)
@@ -258,7 +264,6 @@ static int zl10353_init(struct dvb_frontend *fe)
static void zl10353_release(struct dvb_frontend *fe)
{
struct zl10353_state *state = fe->demodulator_priv;
-
kfree(state);
}
@@ -314,6 +319,7 @@ static struct dvb_frontend_ops zl10353_ops = {
.init = zl10353_init,
.sleep = zl10353_sleep,
+ .write = zl10353_write,
.set_frontend = zl10353_set_parameters,
.get_tune_settings = zl10353_get_tune_settings,
@@ -330,4 +336,3 @@ MODULE_AUTHOR("Chris Pascoe");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(zl10353_attach);
-EXPORT_SYMBOL(zl10353_write);
diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb/frontends/zl10353.h
index 9770cb840cf..0bc0109737f 100644
--- a/drivers/media/dvb/frontends/zl10353.h
+++ b/drivers/media/dvb/frontends/zl10353.h
@@ -31,11 +31,21 @@ struct zl10353_config
/* set if no pll is connected to the secondary i2c bus */
int no_tuner;
+
+ /* set if parallel ts output is required */
+ int parallel_ts;
};
+#if defined(CONFIG_DVB_ZL10353) || (defined(CONFIG_DVB_ZL10353_MODULE) && defined(MODULE))
extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config,
struct i2c_adapter *i2c);
-
-extern int zl10353_write(struct dvb_frontend *fe, u8 *ibuf, int ilen);
+#else
+static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_ZL10353
#endif /* ZL10353_H */