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/Kconfig13
-rw-r--r--drivers/media/dvb/frontends/Makefile2
-rw-r--r--drivers/media/dvb/frontends/cx22702.c29
-rw-r--r--drivers/media/dvb/frontends/cx22702.h5
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c85
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h6
-rw-r--r--drivers/media/dvb/frontends/l64781.c9
-rw-r--r--drivers/media/dvb/frontends/lgdt3302.c609
-rw-r--r--drivers/media/dvb/frontends/lgdt3302.h49
-rw-r--r--drivers/media/dvb/frontends/lgdt3302_priv.h72
-rw-r--r--drivers/media/dvb/frontends/s5h1420.c800
-rw-r--r--drivers/media/dvb/frontends/s5h1420.h41
-rw-r--r--drivers/media/dvb/frontends/stv0297.c8
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c235
-rw-r--r--drivers/media/dvb/frontends/tda1004x.h31
-rw-r--r--drivers/media/dvb/frontends/tda80xx.c1
16 files changed, 1872 insertions, 123 deletions
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index b4fddf5..d847c62 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -40,6 +40,12 @@ config DVB_VES1X93
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
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
comment "DVB-T (terrestrial) frontends"
depends on DVB_CORE
@@ -181,4 +187,11 @@ config DVB_BCM3510
An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to
support this frontend.
+config DVB_LGDT3302
+ tristate "LGDT3302 based (DViCO FusionHDTV3 Gold)"
+ depends on DVB_CORE
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
endmenu
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 91d6d35..de5e240 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -29,3 +29,5 @@ obj-$(CONFIG_DVB_NXT2002) += nxt2002.o
obj-$(CONFIG_DVB_OR51211) += or51211.o
obj-$(CONFIG_DVB_OR51132) += or51132.o
obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
+obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
+obj-$(CONFIG_DVB_LGDT3302) += lgdt3302.o
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
index f4aa441..9f63929 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -76,7 +76,6 @@ static u8 init_tab [] = {
0x49, 0x56,
0x6b, 0x1e,
0xc8, 0x02,
- 0xf8, 0x02,
0xf9, 0x00,
0xfa, 0x00,
0xfb, 0x00,
@@ -203,7 +202,7 @@ static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_paramet
struct cx22702_state* state = fe->demodulator_priv;
/* set PLL */
- cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+ cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
if (state->config->pll_set) {
state->config->pll_set(fe, p);
} else if (state->config->pll_desc) {
@@ -217,7 +216,7 @@ static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_paramet
} else {
BUG();
}
- cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
+ cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
/* set inversion */
cx22702_set_inversion (state, p->inversion);
@@ -256,7 +255,7 @@ static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_paramet
cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc );
cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */
- printk("%s: Autodetecting\n",__FUNCTION__);
+ dprintk("%s: Autodetecting\n",__FUNCTION__);
return 0;
}
@@ -347,10 +346,11 @@ static int cx22702_init (struct dvb_frontend* fe)
for (i=0; i<sizeof(init_tab); i+=2)
cx22702_writereg (state, init_tab[i], init_tab[i+1]);
+ cx22702_writereg (state, 0xf8, (state->config->output_mode << 1) & 0x02);
/* init PLL */
if (state->config->pll_init) {
- cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+ cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) & 0xfe);
state->config->pll_init(fe);
cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
}
@@ -440,8 +440,10 @@ static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
/* RS Uncorrectable Packet Count then reset */
_ucblocks = cx22702_readreg (state, 0xE3);
- if (state->prevUCBlocks < _ucblocks) *ucblocks = (_ucblocks - state->prevUCBlocks);
- else *ucblocks = state->prevUCBlocks - _ucblocks;
+ if (state->prevUCBlocks < _ucblocks)
+ *ucblocks = (_ucblocks - state->prevUCBlocks);
+ else
+ *ucblocks = state->prevUCBlocks - _ucblocks;
state->prevUCBlocks = _ucblocks;
return 0;
@@ -457,6 +459,12 @@ static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
return cx22702_get_tps (state, &p->u.ofdm);
}
+static int cx22702_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
static void cx22702_release(struct dvb_frontend* fe)
{
struct cx22702_state* state = fe->demodulator_priv;
@@ -472,7 +480,8 @@ struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
- if (state == NULL) goto error;
+ if (state == NULL)
+ goto error;
/* setup the state */
state->config = config;
@@ -481,7 +490,8 @@ struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
state->prevUCBlocks = 0;
/* check if the demod is there */
- if (cx22702_readreg(state, 0x1f) != 0x3) goto error;
+ if (cx22702_readreg(state, 0x1f) != 0x3)
+ goto error;
/* create dvb_frontend */
state->frontend.ops = &state->ops;
@@ -514,6 +524,7 @@ static struct dvb_frontend_ops cx22702_ops = {
.set_frontend = cx22702_set_tps,
.get_frontend = cx22702_get_frontend,
+ .get_tune_settings = cx22702_get_tune_settings,
.read_status = cx22702_read_status,
.read_ber = cx22702_read_ber,
diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h
index 559fdb9..11f8680 100644
--- a/drivers/media/dvb/frontends/cx22702.h
+++ b/drivers/media/dvb/frontends/cx22702.h
@@ -35,6 +35,11 @@ struct cx22702_config
/* the demodulator's i2c address */
u8 demod_address;
+ /* serial/parallel output */
+#define CX22702_PARALLEL_OUTPUT 0
+#define CX22702_SERIAL_OUTPUT 1
+ u8 output_mode;
+
/* PLL maintenance */
u8 pll_address;
struct dvb_pll_desc *pll_desc;
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index f73b5f4..5afeaa9 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -55,7 +55,7 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7610 = {
};
EXPORT_SYMBOL(dvb_pll_thomson_dtt7610);
-static void thomson_dtt759x_bw(u8 *buf, int bandwidth)
+static void thomson_dtt759x_bw(u8 *buf, u32 freq, int bandwidth)
{
if (BANDWIDTH_7_MHZ == bandwidth)
buf[3] |= 0x10;
@@ -93,6 +93,32 @@ struct dvb_pll_desc dvb_pll_lg_z201 = {
};
EXPORT_SYMBOL(dvb_pll_lg_z201);
+struct dvb_pll_desc dvb_pll_microtune_4042 = {
+ .name = "Microtune 4042 FI5",
+ .min = 57000000,
+ .max = 858000000,
+ .count = 3,
+ .entries = {
+ { 162000000, 44000000, 62500, 0x8e, 0xa1 },
+ { 457000000, 44000000, 62500, 0x8e, 0x91 },
+ { 999999999, 44000000, 62500, 0x8e, 0x31 },
+ },
+};
+EXPORT_SYMBOL(dvb_pll_microtune_4042);
+
+struct dvb_pll_desc dvb_pll_thomson_dtt7611 = {
+ .name = "Thomson dtt7611",
+ .min = 44000000,
+ .max = 958000000,
+ .count = 3,
+ .entries = {
+ { 157250000, 44000000, 62500, 0x8e, 0x39 },
+ { 454000000, 44000000, 62500, 0x8e, 0x3a },
+ { 999999999, 44000000, 62500, 0x8e, 0x3c },
+ },
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt7611);
+
struct dvb_pll_desc dvb_pll_unknown_1 = {
.name = "unknown 1", /* used by dntv live dvb-t */
.min = 174000000,
@@ -146,7 +172,7 @@ EXPORT_SYMBOL(dvb_pll_env57h1xd5);
/* Philips TDA6650/TDA6651
* used in Panasonic ENV77H11D5
*/
-static void tda665x_bw(u8 *buf, int bandwidth)
+static void tda665x_bw(u8 *buf, u32 freq, int bandwidth)
{
if (bandwidth == BANDWIDTH_8_MHZ)
buf[3] |= 0x08;
@@ -178,7 +204,7 @@ EXPORT_SYMBOL(dvb_pll_tda665x);
/* Infineon TUA6034
* used in LG TDTP E102P
*/
-static void tua6034_bw(u8 *buf, int bandwidth)
+static void tua6034_bw(u8 *buf, u32 freq, int bandwidth)
{
if (BANDWIDTH_7_MHZ != bandwidth)
buf[3] |= 0x08;
@@ -198,6 +224,57 @@ struct dvb_pll_desc dvb_pll_tua6034 = {
};
EXPORT_SYMBOL(dvb_pll_tua6034);
+/* Philips FMD1216ME
+ * used in Medion Hybrid PCMCIA card and USB Box
+ */
+static void fmd1216me_bw(u8 *buf, u32 freq, int bandwidth)
+{
+ if (bandwidth == BANDWIDTH_8_MHZ && freq >= 158870000)
+ buf[3] |= 0x08;
+}
+
+struct dvb_pll_desc dvb_pll_fmd1216me = {
+ .name = "Philips FMD1216ME",
+ .min = 50870000,
+ .max = 858000000,
+ .setbw = fmd1216me_bw,
+ .count = 7,
+ .entries = {
+ { 143870000, 36213333, 166667, 0xbc, 0x41 },
+ { 158870000, 36213333, 166667, 0xf4, 0x41 },
+ { 329870000, 36213333, 166667, 0xbc, 0x42 },
+ { 441870000, 36213333, 166667, 0xf4, 0x42 },
+ { 625870000, 36213333, 166667, 0xbc, 0x44 },
+ { 803870000, 36213333, 166667, 0xf4, 0x44 },
+ { 999999999, 36213333, 166667, 0xfc, 0x44 },
+ }
+};
+EXPORT_SYMBOL(dvb_pll_fmd1216me);
+
+/* ALPS TDED4
+ * used in Nebula-Cards and USB boxes
+ */
+static void tded4_bw(u8 *buf, u32 freq, int bandwidth)
+{
+ if (bandwidth == BANDWIDTH_8_MHZ)
+ buf[3] |= 0x04;
+}
+
+struct dvb_pll_desc dvb_pll_tded4 = {
+ .name = "ALPS TDED4",
+ .min = 47000000,
+ .max = 863000000,
+ .setbw = tded4_bw,
+ .count = 4,
+ .entries = {
+ { 153000000, 36166667, 166667, 0x85, 0x01 },
+ { 470000000, 36166667, 166667, 0x85, 0x02 },
+ { 823000000, 36166667, 166667, 0x85, 0x08 },
+ { 999999999, 36166667, 166667, 0x85, 0x88 },
+ }
+};
+EXPORT_SYMBOL(dvb_pll_tded4);
+
/* ----------------------------------------------------------- */
/* code */
@@ -231,7 +308,7 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
buf[3] = desc->entries[i].cb2;
if (desc->setbw)
- desc->setbw(buf, bandwidth);
+ desc->setbw(buf, freq, bandwidth);
if (debug)
printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index b796778..cb79475 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -9,7 +9,7 @@ struct dvb_pll_desc {
char *name;
u32 min;
u32 max;
- void (*setbw)(u8 *buf, int bandwidth);
+ void (*setbw)(u8 *buf, u32 freq, int bandwidth);
int count;
struct {
u32 limit;
@@ -24,12 +24,16 @@ extern struct dvb_pll_desc dvb_pll_thomson_dtt7579;
extern struct dvb_pll_desc dvb_pll_thomson_dtt759x;
extern struct dvb_pll_desc dvb_pll_thomson_dtt7610;
extern struct dvb_pll_desc dvb_pll_lg_z201;
+extern struct dvb_pll_desc dvb_pll_microtune_4042;
+extern struct dvb_pll_desc dvb_pll_thomson_dtt7611;
extern struct dvb_pll_desc dvb_pll_unknown_1;
extern struct dvb_pll_desc dvb_pll_tua6010xs;
extern struct dvb_pll_desc dvb_pll_env57h1xd5;
extern struct dvb_pll_desc dvb_pll_tua6034;
extern struct dvb_pll_desc dvb_pll_tda665x;
+extern struct dvb_pll_desc dvb_pll_fmd1216me;
+extern struct dvb_pll_desc dvb_pll_tded4;
int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
u32 freq, int bandwidth);
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
index 031a1dd..faaad1a 100644
--- a/drivers/media/dvb/frontends/l64781.c
+++ b/drivers/media/dvb/frontends/l64781.c
@@ -474,11 +474,12 @@ static int l64781_init(struct dvb_frontend* fe)
return 0;
}
-static int l64781_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+static int l64781_get_tune_settings(struct dvb_frontend* fe,
+ struct dvb_frontend_tune_settings* fesettings)
{
- fesettings->min_delay_ms = 200;
- fesettings->step_size = 166667;
- fesettings->max_drift = 166667*2;
+ fesettings->min_delay_ms = 4000;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
return 0;
}
diff --git a/drivers/media/dvb/frontends/lgdt3302.c b/drivers/media/dvb/frontends/lgdt3302.c
new file mode 100644
index 0000000..2eea03d
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgdt3302.c
@@ -0,0 +1,609 @@
+/*
+ * Support for LGDT3302 (DViCO FustionHDTV 3 Gold) - VSB/QAM
+ *
+ * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
+ *
+ * Based on code from Kirk Lapray <kirk_lapray@bigfoot.com>
+ * Copyright (C) 2005
+ *
+ * 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.
+ *
+ */
+
+/*
+ * NOTES ABOUT THIS DRIVER
+ *
+ * This driver supports DViCO FusionHDTV 3 Gold under Linux.
+ *
+ * TODO:
+ * BER and signal strength always return 0.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+
+#include "dvb_frontend.h"
+#include "dvb-pll.h"
+#include "lgdt3302_priv.h"
+#include "lgdt3302.h"
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"Turn on/off lgdt3302 frontend debugging (default:off).");
+#define dprintk(args...) \
+do { \
+if (debug) printk(KERN_DEBUG "lgdt3302: " args); \
+} while (0)
+
+struct lgdt3302_state
+{
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+
+ /* Configuration settings */
+ const struct lgdt3302_config* config;
+
+ struct dvb_frontend frontend;
+
+ /* Demodulator private data */
+ fe_modulation_t current_modulation;
+
+ /* Tuner private data */
+ u32 current_frequency;
+};
+
+static int i2c_writebytes (struct lgdt3302_state* state,
+ u8 addr, /* demod_address or pll_address */
+ u8 *buf, /* data bytes to send */
+ int len /* number of bytes to send */ )
+{
+ if (addr == state->config->pll_address) {
+ struct i2c_msg msg =
+ { .addr = addr, .flags = 0, .buf = buf, .len = len };
+ int err;
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "lgdt3302: %s error (addr %02x <- %02x, err == %i)\n", __FUNCTION__, addr, buf[0], err);
+ if (err < 0)
+ return err;
+ else
+ return -EREMOTEIO;
+ }
+ } else {
+ u8 tmp[] = { buf[0], buf[1] };
+ struct i2c_msg msg =
+ { .addr = addr, .flags = 0, .buf = tmp, .len = 2 };
+ int err;
+ int i;
+
+ for (i=1; i<len; i++) {
+ tmp[1] = buf[i];
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "lgdt3302: %s error (addr %02x <- %02x, err == %i)\n", __FUNCTION__, addr, buf[0], err);
+ if (err < 0)
+ return err;
+ else
+ return -EREMOTEIO;
+ }
+ tmp[0]++;
+ }
+ }
+ return 0;
+}
+static int i2c_readbytes (struct lgdt3302_state* state,
+ u8 addr, /* demod_address or pll_address */
+ u8 *buf, /* holds data bytes read */
+ int len /* number of bytes to read */ )
+{
+ struct i2c_msg msg =
+ { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len };
+ int err;
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "lgdt3302: %s error (addr %02x, err == %i)\n", __FUNCTION__, addr, err);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/*
+ * This routine writes the register (reg) to the demod bus
+ * then reads the data returned for (len) bytes.
+ */
+
+static u8 i2c_selectreadbytes (struct lgdt3302_state* state,
+ enum I2C_REG reg, u8* buf, int len)
+{
+ u8 wr [] = { reg };
+ struct i2c_msg msg [] = {
+ { .addr = state->config->demod_address,
+ .flags = 0, .buf = wr, .len = 1 },
+ { .addr = state->config->demod_address,
+ .flags = I2C_M_RD, .buf = buf, .len = len },
+ };
+ int ret;
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2) {
+ printk(KERN_WARNING "lgdt3302: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __FUNCTION__, state->config->demod_address, reg, ret);
+ } else {
+ ret = 0;
+ }
+ return ret;
+}
+
+/* Software reset */
+int lgdt3302_SwReset(struct lgdt3302_state* state)
+{
+ u8 ret;
+ u8 reset[] = {
+ IRQ_MASK,
+ 0x00 /* bit 6 is active low software reset
+ * bits 5-0 are 1 to mask interrupts */
+ };
+
+ ret = i2c_writebytes(state,
+ state->config->demod_address,
+ reset, sizeof(reset));
+ if (ret == 0) {
+ /* spec says reset takes 100 ns why wait */
+ /* mdelay(100); */ /* keep low for 100mS */
+ reset[1] = 0x7f; /* force reset high (inactive)
+ * and unmask interrupts */
+ ret = i2c_writebytes(state,
+ state->config->demod_address,
+ reset, sizeof(reset));
+ }
+ /* Spec does not indicate a need for this either */
+ /*mdelay(5); */ /* wait 5 msec before doing more */
+ return ret;
+}
+
+static int lgdt3302_init(struct dvb_frontend* fe)
+{
+ /* Hardware reset is done using gpio[0] of cx23880x chip.
+ * I'd like to do it here, but don't know how to find chip address.
+ * cx88-cards.c arranges for the reset bit to be inactive (high).
+ * Maybe there needs to be a callable function in cx88-core or
+ * the caller of this function needs to do it. */
+
+ dprintk("%s entered\n", __FUNCTION__);
+ return lgdt3302_SwReset((struct lgdt3302_state*) fe->demodulator_priv);
+}
+
+static int lgdt3302_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ *ber = 0; /* Dummy out for now */
+ return 0;
+}
+
+static int lgdt3302_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
+ u8 buf[2];
+
+ i2c_selectreadbytes(state, PACKET_ERR_COUNTER1, buf, sizeof(buf));
+
+ *ucblocks = (buf[0] << 8) | buf[1];
+ return 0;
+}
+
+static int lgdt3302_set_parameters(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *param)
+{
+ u8 buf[4];
+ struct lgdt3302_state* state =
+ (struct lgdt3302_state*) fe->demodulator_priv;
+
+ /* Use 50MHz parameter values from spec sheet since xtal is 50 */
+ static u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 };
+ static u8 vsb_freq_cfg[] = { VSB_CARRIER_FREQ0, 0x00, 0x87, 0x8e, 0x01 };
+ static u8 demux_ctrl_cfg[] = { DEMUX_CONTROL, 0xfb };
+ static u8 agc_rf_cfg[] = { AGC_RF_BANDWIDTH0, 0x40, 0x93, 0x00 };
+ static u8 agc_ctrl_cfg[] = { AGC_FUNC_CTRL2, 0xc6, 0x40 };
+ static u8 agc_delay_cfg[] = { AGC_DELAY0, 0x00, 0x00, 0x00 };
+ static u8 agc_loop_cfg[] = { AGC_LOOP_BANDWIDTH0, 0x08, 0x9a };
+
+ /* Change only if we are actually changing the modulation */
+ if (state->current_modulation != param->u.vsb.modulation) {
+ int value;
+
+ switch(param->u.vsb.modulation) {
+ case VSB_8:
+ dprintk("%s: VSB_8 MODE\n", __FUNCTION__);
+
+ /* Select VSB mode and serial MPEG interface */
+ top_ctrl_cfg[1] = 0x07;
+ break;
+
+ case QAM_64:
+ dprintk("%s: QAM_64 MODE\n", __FUNCTION__);
+
+ /* Select QAM_64 mode and serial MPEG interface */
+ top_ctrl_cfg[1] = 0x04;
+ break;
+
+ case QAM_256:
+ dprintk("%s: QAM_256 MODE\n", __FUNCTION__);
+
+ /* Select QAM_256 mode and serial MPEG interface */
+ top_ctrl_cfg[1] = 0x05;
+ break;
+ default:
+ printk(KERN_WARNING "lgdt3302: %s: Modulation type(%d) UNSUPPORTED\n", __FUNCTION__, param->u.vsb.modulation);
+ return -1;
+ }
+ /* Initializations common to all modes */
+
+ /* Select the requested mode */
+ i2c_writebytes(state, state->config->demod_address,
+ top_ctrl_cfg, sizeof(top_ctrl_cfg));
+
+ /* Change the value of IFBW[11:0]
+ of AGC IF/RF loop filter bandwidth register */
+ i2c_writebytes(state, state->config->demod_address,
+ agc_rf_cfg, sizeof(agc_rf_cfg));
+
+ /* Change the value of bit 6, 'nINAGCBY' and
+ 'NSSEL[1:0] of ACG function control register 2 */
+ /* Change the value of bit 6 'RFFIX'
+ of AGC function control register 3 */
+ i2c_writebytes(state, state->config->demod_address,
+ agc_ctrl_cfg, sizeof(agc_ctrl_cfg));
+
+ /* Change the TPCLK pin polarity
+ data is valid on falling clock */
+ i2c_writebytes(state, state->config->demod_address,
+ demux_ctrl_cfg, sizeof(demux_ctrl_cfg));
+
+ /* Change the value of NCOCTFV[25:0] of carrier
+ recovery center frequency register */
+ i2c_writebytes(state, state->config->demod_address,
+ vsb_freq_cfg, sizeof(vsb_freq_cfg));
+ /* Set the value of 'INLVTHD' register 0x2a/0x2c
+ to value from 'IFACC' register 0x39/0x3b -1 */
+ i2c_selectreadbytes(state, AGC_RFIF_ACC0,
+ &agc_delay_cfg[1], 3);
+ value = ((agc_delay_cfg[1] & 0x0f) << 8) | agc_delay_cfg[3];
+ value = value -1;
+ dprintk("%s IFACC -1 = 0x%03x\n", __FUNCTION__, value);
+ agc_delay_cfg[1] = (value >> 8) & 0x0f;
+ agc_delay_cfg[2] = 0x00;
+ agc_delay_cfg[3] = value & 0xff;
+ i2c_writebytes(state, state->config->demod_address,
+ agc_delay_cfg, sizeof(agc_delay_cfg));
+
+ /* Change the value of IAGCBW[15:8]
+ of inner AGC loop filter bandwith */
+ i2c_writebytes(state, state->config->demod_address,
+ agc_loop_cfg, sizeof(agc_loop_cfg));
+
+ state->config->set_ts_params(fe, 0);
+ state->current_modulation = param->u.vsb.modulation;
+ }
+
+ /* Change only if we are actually changing the channel */
+ if (state->current_frequency != param->frequency) {
+ dvb_pll_configure(state->config->pll_desc, buf,
+ param->frequency, 0);
+ dprintk("%s: tuner bytes: 0x%02x 0x%02x "
+ "0x%02x 0x%02x\n", __FUNCTION__, buf[0],buf[1],buf[2],buf[3]);
+ i2c_writebytes(state, state->config->pll_address ,buf, 4);
+
+ /* Check the status of the tuner pll */
+ i2c_readbytes(state, state->config->pll_address, buf, 1);
+ dprintk("%s: tuner status byte = 0x%02x\n", __FUNCTION__, buf[0]);
+
+ /* Update current frequency */
+ state->current_frequency = param->frequency;
+ }
+ lgdt3302_SwReset(state);
+ return 0;
+}
+
+static int lgdt3302_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters* param)
+{
+ struct lgdt3302_state *state = fe->demodulator_priv;
+ param->frequency = state->current_frequency;
+ return 0;
+}
+
+static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
+ u8 buf[3];
+
+ *status = 0; /* Reset status result */
+
+ /* Check the status of the tuner pll */
+ i2c_readbytes(state, state->config->pll_address, buf, 1);
+ dprintk("%s: tuner status byte = 0x%02x\n", __FUNCTION__, buf[0]);
+ if ((buf[0] & 0xc0) != 0x40)
+ return 0; /* Tuner PLL not locked or not powered on */
+
+ /*
+ * You must set the Mask bits to 1 in the IRQ_MASK in order
+ * to see that status bit in the IRQ_STATUS register.
+ * This is done in SwReset();
+ */
+
+ /* AGC status register */
+ i2c_selectreadbytes(state, AGC_STATUS, buf, 1);
+ dprintk("%s: AGC_STATUS = 0x%02x\n", __FUNCTION__, buf[0]);
+ if ((buf[0] & 0x0c) == 0x8){
+ /* 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;
+ }
+
+ /* signal status */
+ i2c_selectreadbytes(state, TOP_CONTROL, buf, sizeof(buf));
+ dprintk("%s: TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", __FUNCTION__, buf[0], buf[1], buf[2]);
+
+#if 0
+ /* Alternative method to check for a signal */
+ /* using the SNR good/bad interrupts. */
+ if ((buf[2] & 0x30) == 0x10)
+ *status |= FE_HAS_SIGNAL;
+#endif
+
+ /* sync status */
+ if ((buf[2] & 0x03) == 0x01) {
+ *status |= FE_HAS_SYNC;
+ }
+
+ /* FEC error status */
+ if ((buf[2] & 0x0c) == 0x08) {
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_VITERBI;
+ }
+
+ /* Carrier Recovery Lock Status Register */
+ i2c_selectreadbytes(state, CARRIER_LOCK, buf, 1);
+ dprintk("%s: CARRIER_LOCK = 0x%02x\n", __FUNCTION__, buf[0]);
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
+ /* Need to undestand why there are 3 lock levels here */
+ if ((buf[0] & 0x07) == 0x07)
+ *status |= FE_HAS_CARRIER;
+ break;
+ case VSB_8:
+ if ((buf[0] & 0x80) == 0x80)
+ *status |= FE_HAS_CARRIER;
+ break;
+ default:
+ printk("KERN_WARNING lgdt3302: %s: Modulation set to unsupported value\n", __FUNCTION__);
+ }
+
+ return 0;
+}
+
+static int lgdt3302_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ /* not directly available. */
+ return 0;
+}
+
+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, 0x040000
+ };
+
+ static u8 buf[5];/* read data buffer */
+ static u32 noise; /* noise value */
+ static u32 snr_db; /* index into SNR_EQ[] */
+ struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
+
+ /* read both equalizer and pase tracker noise data */
+ i2c_selectreadbytes(state, EQPH_ERR0, buf, sizeof(buf));
+
+ if (state->current_modulation == VSB_8) {
+ /* Equalizer Mean-Square Error Register for VSB */
+ 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;
+ }
+ }
+ }
+#else
+ /* Return the raw noise value */
+ static u8 buf[5];/* read data buffer */
+ static u32 noise; /* noise value */
+ struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
+
+ /* read both equalizer and pase tracker noise data */
+ i2c_selectreadbytes(state, EQPH_ERR0, buf, sizeof(buf));
+
+ if (state->current_modulation == VSB_8) {
+ /* Equalizer Mean-Square Error Register for VSB */
+ noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2];
+ } else {
+ /* Phase Tracker Mean-Square Error Register for QAM */
+ noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4];
+ }
+
+ /* Small values for noise mean signal is better so invert noise */
+ /* Noise is 19 bit value so discard 3 LSB*/
+ *snr = ~noise>>3;
+#endif
+
+ dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr);
+
+ return 0;
+}
+
+static int lgdt3302_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+ /* I have no idea about this - it may not be needed */
+ fe_tune_settings->min_delay_ms = 500;
+ fe_tune_settings->step_size = 0;
+ fe_tune_settings->max_drift = 0;
+ return 0;
+}
+
+static void lgdt3302_release(struct dvb_frontend* fe)
+{
+ struct lgdt3302_state* state = (struct lgdt3302_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops lgdt3302_ops;
+
+struct dvb_frontend* lgdt3302_attach(const struct lgdt3302_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct lgdt3302_state* state = NULL;
+ u8 buf[1];
+
+ /* Allocate memory for the internal state */
+ state = (struct lgdt3302_state*) kmalloc(sizeof(struct lgdt3302_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+ memset(state,0,sizeof(*state));
+
+ /* Setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &lgdt3302_ops, sizeof(struct dvb_frontend_ops));
+ /* Verify communication with demod chip */
+ if (i2c_selectreadbytes(state, 2, buf, 1))
+ goto error;
+
+ state->current_frequency = -1;
+ state->current_modulation = -1;
+
+ /* Create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ if (state)
+ kfree(state);
+ dprintk("%s: ERROR\n",__FUNCTION__);
+ return NULL;
+}
+
+static struct dvb_frontend_ops lgdt3302_ops = {
+ .info = {
+ .name= "LG Electronics LGDT3302 VSB/QAM Frontend",
+ .type = FE_ATSC,
+ .frequency_min= 54000000,
+ .frequency_max= 858000000,
+ .frequency_stepsize= 62500,
+ /* Symbol rate is for all VSB modes need to check QAM */
+ .symbol_rate_min = 10762000,
+ .symbol_rate_max = 10762000,
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+ .init = lgdt3302_init,
+ .set_frontend = lgdt3302_set_parameters,
+ .get_frontend = lgdt3302_get_frontend,
+ .get_tune_settings = lgdt3302_get_tune_settings,
+ .read_status = lgdt3302_read_status,
+ .read_ber = lgdt3302_read_ber,
+ .read_signal_strength = lgdt3302_read_signal_strength,
+ .read_snr = lgdt3302_read_snr,
+ .read_ucblocks = lgdt3302_read_ucblocks,
+ .release = lgdt3302_release,
+};
+
+MODULE_DESCRIPTION("LGDT3302 [DViCO FusionHDTV 3 Gold] (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver");
+MODULE_AUTHOR("Wilson Michaels");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(lgdt3302_attach);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/lgdt3302.h b/drivers/media/dvb/frontends/lgdt3302.h
new file mode 100644
index 0000000..81587a4
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgdt3302.h
@@ -0,0 +1,49 @@
+/*
+ * $Id: lgdt3302.h,v 1.2 2005/06/28 23:50:48 mkrufky Exp $
+ *
+ * Support for LGDT3302 (DViCO FustionHDTV 3 Gold) - VSB/QAM
+ *
+ * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
+ *
+ * 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 LGDT3302_H
+#define LGDT3302_H
+
+#include <linux/dvb/frontend.h>
+
+struct lgdt3302_config
+{
+ /* The demodulator's i2c address */
+ u8 demod_address;
+ u8 pll_address;
+ struct dvb_pll_desc *pll_desc;
+
+ /* Need to set device param for start_dma */
+ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+};
+
+extern struct dvb_frontend* lgdt3302_attach(const struct lgdt3302_config* config,
+ struct i2c_adapter* i2c);
+
+#endif /* LGDT3302_H */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/lgdt3302_priv.h b/drivers/media/dvb/frontends/lgdt3302_priv.h
new file mode 100644
index 0000000..6193fa7
--- /dev/null
+++ b/drivers/media/dvb/frontends/lgdt3302_priv.h
@@ -0,0 +1,72 @@
+/*
+ * $Id: lgdt3302_priv.h,v 1.2 2005/06/28 23:50:48 mkrufky Exp $
+ *
+ * Support for LGDT3302 (DViCO FustionHDTV 3 Gold) - VSB/QAM
+ *
+ * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net>
+ *
+ * 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 _LGDT3302_PRIV_
+#define _LGDT3302_PRIV_
+
+/* i2c control register addresses */
+enum I2C_REG {
+ TOP_CONTROL= 0x00,
+ IRQ_MASK= 0x01,
+ IRQ_STATUS= 0x02,
+ VSB_CARRIER_FREQ0= 0x16,
+ VSB_CARRIER_FREQ1= 0x17,
+ VSB_CARRIER_FREQ2= 0x18,
+ VSB_CARRIER_FREQ3= 0x19,
+ CARRIER_MSEQAM1= 0x1a,
+ CARRIER_MSEQAM2= 0x1b,
+ CARRIER_LOCK= 0x1c,
+ TIMING_RECOVERY= 0x1d,
+ AGC_DELAY0= 0x2a,
+ AGC_DELAY1= 0x2b,
+ AGC_DELAY2= 0x2c,
+ AGC_RF_BANDWIDTH0= 0x2d,
+ AGC_RF_BANDWIDTH1= 0x2e,
+ AGC_RF_BANDWIDTH2= 0x2f,
+ AGC_LOOP_BANDWIDTH0= 0x30,
+ AGC_LOOP_BANDWIDTH1= 0x31,
+ AGC_FUNC_CTRL1= 0x32,
+ AGC_FUNC_CTRL2= 0x33,
+ AGC_FUNC_CTRL3= 0x34,
+ AGC_RFIF_ACC0= 0x39,
+ AGC_RFIF_ACC1= 0x3a,
+ 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,
+ PACKET_ERR_COUNTER1= 0x6a,
+ PACKET_ERR_COUNTER2= 0x6b,
+};
+
+#endif /* _LGDT3302_PRIV_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c
new file mode 100644
index 0000000..4f396ac
--- /dev/null
+++ b/drivers/media/dvb/frontends/s5h1420.c
@@ -0,0 +1,800 @@
+/*
+Driver for Samsung S5H1420 QPSK Demodulator
+
+Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>
+
+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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "s5h1420.h"
+
+
+
+#define TONE_FREQ 22000
+
+struct s5h1420_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ const struct s5h1420_config* config;
+ struct dvb_frontend frontend;
+
+ u8 postlocked:1;
+ u32 fclk;
+ u32 tunedfreq;
+ fe_code_rate_t fec_inner;
+ u32 symbol_rate;
+};
+
+static u32 s5h1420_getsymbolrate(struct s5h1420_state* state);
+static int s5h1420_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings);
+
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+static int s5h1420_writereg (struct s5h1420_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 };
+ int err;
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static u8 s5h1420_readreg (struct s5h1420_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg1 = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 };
+ struct i2c_msg msg2 = { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 };
+
+ if ((ret = i2c_transfer (state->i2c, &msg1, 1)) != 1)
+ return ret;
+
+ if ((ret = i2c_transfer (state->i2c, &msg2, 1)) != 1)
+ return ret;
+
+ return b1[0];
+}
+
+static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ switch(voltage) {
+ case SEC_VOLTAGE_13:
+ s5h1420_writereg(state, 0x3c, (s5h1420_readreg(state, 0x3c) & 0xfe) | 0x02);
+ break;
+
+ case SEC_VOLTAGE_18:
+ s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) | 0x03);
+ break;
+
+ case SEC_VOLTAGE_OFF:
+ s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) & 0xfd);
+ break;
+ }
+
+ return 0;
+}
+
+static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ switch(tone) {
+ case SEC_TONE_ON:
+ s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x74) | 0x08);
+ break;
+
+ case SEC_TONE_OFF:
+ s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x74) | 0x01);
+ break;
+ }
+
+ return 0;
+}
+
+static int s5h1420_send_master_cmd (struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+ u8 val;
+ int i;
+ unsigned long timeout;
+ int result = 0;
+
+ /* setup for DISEQC */
+ val = s5h1420_readreg(state, 0x3b);
+ s5h1420_writereg(state, 0x3b, 0x02);
+ msleep(15);
+
+ /* write the DISEQC command bytes */
+ for(i=0; i< cmd->msg_len; i++) {
+ s5h1420_writereg(state, 0x3c + i, cmd->msg[i]);
+ }
+
+ /* kick off transmission */
+ s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | ((cmd->msg_len-1) << 4) | 0x08);
+
+ /* wait for transmission to complete */
+ timeout = jiffies + ((100*HZ) / 1000);
+ while(time_before(jiffies, timeout)) {
+ if (s5h1420_readreg(state, 0x3b) & 0x08)
+ break;
+
+ msleep(5);
+ }
+ if (time_after(jiffies, timeout))
+ result = -ETIMEDOUT;
+
+ /* restore original settings */
+ s5h1420_writereg(state, 0x3b, val);
+ msleep(15);
+ return result;
+}
+
+static int s5h1420_recv_slave_reply (struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+ u8 val;
+ int i;
+ int length;
+ unsigned long timeout;
+ int result = 0;
+
+ /* setup for DISEQC recieve */
+ val = s5h1420_readreg(state, 0x3b);
+ s5h1420_writereg(state, 0x3b, 0x82); /* FIXME: guess - do we need to set DIS_RDY(0x08) in receive mode? */
+ msleep(15);
+
+ /* wait for reception to complete */
+ timeout = jiffies + ((reply->timeout*HZ) / 1000);
+ while(time_before(jiffies, timeout)) {
+ if (!(s5h1420_readreg(state, 0x3b) & 0x80)) /* FIXME: do we test DIS_RDY(0x08) or RCV_EN(0x80)? */
+ break;
+
+ msleep(5);
+ }
+ if (time_after(jiffies, timeout)) {
+ result = -ETIMEDOUT;
+ goto exit;
+ }
+
+ /* check error flag - FIXME: not sure what this does - docs do not describe
+ * beyond "error flag for diseqc receive data :( */
+ if (s5h1420_readreg(state, 0x49)) {
+ result = -EIO;
+ goto exit;
+ }
+
+ /* check length */
+ length = (s5h1420_readreg(state, 0x3b) & 0x70) >> 4;
+ if (length > sizeof(reply->msg)) {
+ result = -EOVERFLOW;
+ goto exit;
+ }
+ reply->msg_len = length;
+
+ /* extract data */
+ for(i=0; i< length; i++) {
+ reply->msg[i] = s5h1420_readreg(state, 0x3c + i);
+ }
+
+exit:
+ /* restore original settings */
+ s5h1420_writereg(state, 0x3b, val);
+ msleep(15);
+ return result;
+}
+
+static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+ u8 val;
+ int result = 0;
+ unsigned long timeout;
+
+ /* setup for tone burst */
+ val = s5h1420_readreg(state, 0x3b);
+ s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x70) | 0x01);
+
+ /* set value for B position if requested */
+ if (minicmd == SEC_MINI_B) {
+ s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x04);
+ }
+ msleep(15);
+
+ /* start transmission */
+ s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x08);
+
+ /* wait for transmission to complete */
+ timeout = jiffies + ((20*HZ) / 1000);
+ while(time_before(jiffies, timeout)) {
+ if (!(s5h1420_readreg(state, 0x3b) & 0x08))
+ break;
+
+ msleep(5);
+ }
+ if (time_after(jiffies, timeout))
+ result = -ETIMEDOUT;
+
+ /* restore original settings */
+ s5h1420_writereg(state, 0x3b, val);
+ msleep(15);
+ return result;
+}
+
+static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state)
+{
+ u8 val;
+ fe_status_t status = 0;
+
+ val = s5h1420_readreg(state, 0x14);
+ if (val & 0x02)
+ status |= FE_HAS_SIGNAL; // FIXME: not sure if this is right
+ if (val & 0x01)
+ status |= FE_HAS_CARRIER; // FIXME: not sure if this is right
+ val = s5h1420_readreg(state, 0x36);
+ if (val & 0x01)
+ status |= FE_HAS_VITERBI;
+ if (val & 0x20)
+ status |= FE_HAS_SYNC;
+ if (status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC))
+ status |= FE_HAS_LOCK;
+
+ return status;
+}
+
+static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+ u8 val;
+
+ if (status == NULL)
+ return -EINVAL;
+
+ /* determine lock state */
+ *status = s5h1420_get_status_bits(state);
+
+ /* fix for FEC 5/6 inversion issue - if it doesn't quite lock, invert the inversion,
+ wait a bit and check again */
+ if (*status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI)) {
+ val = s5h1420_readreg(state, 0x32);
+ if ((val & 0x07) == 0x03) {
+ if (val & 0x08)
+ s5h1420_writereg(state, 0x31, 0x13);
+ else
+ s5h1420_writereg(state, 0x31, 0x1b);
+
+ /* wait a bit then update lock status */
+ mdelay(200);
+ *status = s5h1420_get_status_bits(state);
+ }
+ }
+
+ /* perform post lock setup */
+ if ((*status & FE_HAS_LOCK) && (!state->postlocked)) {
+
+ /* calculate the data rate */
+ u32 tmp = s5h1420_getsymbolrate(state);
+ switch(s5h1420_readreg(state, 0x32) & 0x07) {
+ case 0:
+ tmp = (tmp * 2 * 1) / 2;
+ break;
+
+ case 1:
+ tmp = (tmp * 2 * 2) / 3;
+ break;
+
+ case 2:
+ tmp = (tmp * 2 * 3) / 4;
+ break;
+
+ case 3:
+ tmp = (tmp * 2 * 5) / 6;
+ break;
+
+ case 4:
+ tmp = (tmp * 2 * 6) / 7;
+ break;
+
+ case 5:
+ tmp = (tmp * 2 * 7) / 8;
+ break;
+ }
+ tmp = state->fclk / tmp;
+
+ /* set the MPEG_CLK_INTL for the calculated data rate */
+ if (tmp < 4)
+ val = 0x00;
+ else if (tmp < 8)
+ val = 0x01;
+ else if (tmp < 12)
+ val = 0x02;
+ else if (tmp < 16)
+ val = 0x03;
+ else if (tmp < 24)
+ val = 0x04;
+ else if (tmp < 32)
+ val = 0x05;
+ else
+ val = 0x06;
+ s5h1420_writereg(state, 0x22, val);
+
+ /* DC freeze */
+ s5h1420_writereg(state, 0x1f, s5h1420_readreg(state, 0x1f) | 0x01);
+
+ /* kicker disable + remove DC offset */
+ s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) & 0x6f);
+
+ /* post-lock processing has been done! */
+ state->postlocked = 1;
+ }
+
+ return 0;
+}
+
+static int s5h1420_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ s5h1420_writereg(state, 0x46, 0x1d);
+ mdelay(25);
+ return (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47);
+}
+
+static int s5h1420_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ u8 val = 0xff - s5h1420_readreg(state, 0x15);
+
+ return (int) ((val << 8) | val);
+}
+
+static int s5h1420_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ s5h1420_writereg(state, 0x46, 0x1f);
+ mdelay(25);
+ return (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47);
+}
+
+static void s5h1420_reset(struct s5h1420_state* state)
+{
+ s5h1420_writereg (state, 0x01, 0x08);
+ s5h1420_writereg (state, 0x01, 0x00);
+ udelay(10);
+}
+
+static void s5h1420_setsymbolrate(struct s5h1420_state* state, struct dvb_frontend_parameters *p)
+{
+ u64 val;
+
+ val = (p->u.qpsk.symbol_rate / 1000) * (1<<24);
+ if (p->u.qpsk.symbol_rate <= 21000000) {
+ val *= 2;
+ }
+ do_div(val, (state->fclk / 1000));
+
+ s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) & 0x7f);
+ s5h1420_writereg(state, 0x11, val >> 16);
+ s5h1420_writereg(state, 0x12, val >> 8);
+ s5h1420_writereg(state, 0x13, val & 0xff);
+ s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) | 0x80);
+}
+
+static u32 s5h1420_getsymbolrate(struct s5h1420_state* state)
+{
+ u64 val;
+ int sampling = 2;
+
+ if (s5h1420_readreg(state, 0x05) & 0x2)
+ sampling = 1;
+
+ s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08);
+ val = s5h1420_readreg(state, 0x11) << 16;
+ val |= s5h1420_readreg(state, 0x12) << 8;
+ val |= s5h1420_readreg(state, 0x13);
+ s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7);
+
+ val *= (state->fclk / 1000);
+ do_div(val, ((1<<24) * sampling));
+
+ return (u32) (val * 1000);
+}
+
+static void s5h1420_setfreqoffset(struct s5h1420_state* state, int freqoffset)
+{
+ int val;
+
+ /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so
+ * divide fclk by 1000000 to get the correct value. */
+ val = -(int) ((freqoffset * (1<<24)) / (state->fclk / 1000000));
+
+ s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) & 0xbf);
+ s5h1420_writereg(state, 0x0e, val >> 16);
+ s5h1420_writereg(state, 0x0f, val >> 8);
+ s5h1420_writereg(state, 0x10, val & 0xff);
+ s5h1420_writereg(state, 0x09, s5h1420_readreg(state, 0x09) | 0x40);
+}
+
+static int s5h1420_getfreqoffset(struct s5h1420_state* state)
+{
+ int val;
+
+ s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08);
+ val = s5h1420_readreg(state, 0x0e) << 16;
+ val |= s5h1420_readreg(state, 0x0f) << 8;
+ val |= s5h1420_readreg(state, 0x10);
+ s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7);
+
+ if (val & 0x800000)
+ val |= 0xff000000;
+
+ /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so
+ * divide fclk by 1000000 to get the correct value. */
+ val = - ((val * (state->fclk/1000000)) / (1<<24));
+
+ return val;
+}
+
+static void s5h1420_setfec(struct s5h1420_state* state, struct dvb_frontend_parameters *p)
+{
+ if ((p->u.qpsk.fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) {
+ s5h1420_writereg(state, 0x31, 0x00);
+ s5h1420_writereg(state, 0x30, 0x3f);
+ } else {
+ switch(p->u.qpsk.fec_inner) {
+ case FEC_1_2:
+ s5h1420_writereg(state, 0x31, 0x10);
+ s5h1420_writereg(state, 0x30, 0x01);
+ break;
+
+ case FEC_2_3:
+ s5h1420_writereg(state, 0x31, 0x11);
+ s5h1420_writereg(state, 0x30, 0x02);
+ break;
+
+ case FEC_3_4:
+ s5h1420_writereg(state, 0x31, 0x12);
+ s5h1420_writereg(state, 0x30, 0x04);
+ break;
+
+ case FEC_5_6:
+ s5h1420_writereg(state, 0x31, 0x13);
+ s5h1420_writereg(state, 0x30, 0x08);
+ break;
+
+ case FEC_6_7:
+ s5h1420_writereg(state, 0x31, 0x14);
+ s5h1420_writereg(state, 0x30, 0x10);
+ break;
+
+ case FEC_7_8:
+ s5h1420_writereg(state, 0x31, 0x15);
+ s5h1420_writereg(state, 0x30, 0x20);
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state)
+{
+ switch(s5h1420_readreg(state, 0x32) & 0x07) {
+ case 0:
+ return FEC_1_2;
+
+ case 1:
+ return FEC_2_3;
+
+ case 2:
+ return FEC_3_4;
+
+ case 3:
+ return FEC_5_6;
+
+ case 4:
+ return FEC_6_7;
+
+ case 5:
+ return FEC_7_8;
+ }
+
+ return FEC_NONE;
+}
+
+static void s5h1420_setinversion(struct s5h1420_state* state, struct dvb_frontend_parameters *p)
+{
+ if ((p->u.qpsk.fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) {
+ s5h1420_writereg(state, 0x31, 0x00);
+ s5h1420_writereg(state, 0x30, 0x3f);
+ } else {
+ u8 tmp = s5h1420_readreg(state, 0x31) & 0xf7;
+ tmp |= 0x10;
+
+ if (p->inversion == INVERSION_ON)
+ tmp |= 0x80;
+
+ s5h1420_writereg(state, 0x31, tmp);
+ }
+}
+
+static fe_spectral_inversion_t s5h1420_getinversion(struct s5h1420_state* state)
+{
+ if (s5h1420_readreg(state, 0x32) & 0x08)
+ return INVERSION_ON;
+
+ return INVERSION_OFF;
+}
+
+static int s5h1420_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+ u32 frequency_delta;
+ struct dvb_frontend_tune_settings fesettings;
+
+ /* check if we should do a fast-tune */
+ memcpy(&fesettings.parameters, p, sizeof(struct dvb_frontend_parameters));
+ s5h1420_get_tune_settings(fe, &fesettings);
+ frequency_delta = p->frequency - state->tunedfreq;
+ if ((frequency_delta > -fesettings.max_drift) && (frequency_delta < fesettings.max_drift) &&
+ (frequency_delta != 0) &&
+ (state->fec_inner == p->u.qpsk.fec_inner) &&
+ (state->symbol_rate == p->u.qpsk.symbol_rate)) {
+
+ s5h1420_setfreqoffset(state, frequency_delta);
+ return 0;
+ }
+
+ /* first of all, software reset */
+ s5h1420_reset(state);
+
+ /* set tuner PLL */
+ if (state->config->pll_set) {
+ s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1);
+ state->config->pll_set(fe, p, &state->tunedfreq);
+ s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe);
+ }
+
+ /* set s5h1420 fclk PLL according to desired symbol rate */
+ if (p->u.qpsk.symbol_rate > 28000000) {
+ state->fclk = 88000000;
+ s5h1420_writereg(state, 0x03, 0x50);
+ s5h1420_writereg(state, 0x04, 0x40);
+ s5h1420_writereg(state, 0x05, 0xae);
+ } else if (p->u.qpsk.symbol_rate > 21000000) {
+ state->fclk = 59000000;
+ s5h1420_writereg(state, 0x03, 0x33);
+ s5h1420_writereg(state, 0x04, 0x40);
+ s5h1420_writereg(state, 0x05, 0xae);
+ } else {
+ state->fclk = 88000000;
+ s5h1420_writereg(state, 0x03, 0x50);
+ s5h1420_writereg(state, 0x04, 0x40);
+ s5h1420_writereg(state, 0x05, 0xac);
+ }
+
+ /* set misc registers */
+ s5h1420_writereg(state, 0x02, 0x00);
+ s5h1420_writereg(state, 0x07, 0xb0);
+ s5h1420_writereg(state, 0x0a, 0x67);
+ s5h1420_writereg(state, 0x0b, 0x78);
+ s5h1420_writereg(state, 0x0c, 0x48);
+ s5h1420_writereg(state, 0x0d, 0x6b);
+ s5h1420_writereg(state, 0x2e, 0x8e);
+ s5h1420_writereg(state, 0x35, 0x33);
+ s5h1420_writereg(state, 0x38, 0x01);
+ s5h1420_writereg(state, 0x39, 0x7d);
+ s5h1420_writereg(state, 0x3a, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32));
+ s5h1420_writereg(state, 0x3c, 0x00);
+ s5h1420_writereg(state, 0x45, 0x61);
+ s5h1420_writereg(state, 0x46, 0x1d);
+
+ /* start QPSK */
+ s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) | 1);
+
+ /* set the frequency offset to adjust for PLL inaccuracy */
+ s5h1420_setfreqoffset(state, p->frequency - state->tunedfreq);
+
+ /* set the reset of the parameters */
+ s5h1420_setsymbolrate(state, p);
+ s5h1420_setinversion(state, p);
+ s5h1420_setfec(state, p);
+
+ state->fec_inner = p->u.qpsk.fec_inner;
+ state->symbol_rate = p->u.qpsk.symbol_rate;
+ state->postlocked = 0;
+ return 0;
+}
+
+static int s5h1420_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ p->frequency = state->tunedfreq + s5h1420_getfreqoffset(state);
+ p->inversion = s5h1420_getinversion(state);
+ p->u.qpsk.symbol_rate = s5h1420_getsymbolrate(state);
+ p->u.qpsk.fec_inner = s5h1420_getfec(state);
+
+ return 0;
+}
+
+static int s5h1420_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 int s5h1420_init (struct dvb_frontend* fe)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ /* disable power down and do reset */
+ s5h1420_writereg(state, 0x02, 0x10);
+ msleep(10);
+ s5h1420_reset(state);
+
+ /* init PLL */
+ if (state->config->pll_init) {
+ s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) | 1);
+ state->config->pll_init(fe);
+ s5h1420_writereg (state, 0x02, s5h1420_readreg(state,0x02) & 0xfe);
+ }
+
+ return 0;
+}
+
+static int s5h1420_sleep(struct dvb_frontend* fe)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+
+ return s5h1420_writereg(state, 0x02, 0x12);
+}
+
+static void s5h1420_release(struct dvb_frontend* fe)
+{
+ struct s5h1420_state* state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops s5h1420_ops;
+
+struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config, struct i2c_adapter* i2c)
+{
+ struct s5h1420_state* state = NULL;
+ u8 identity;
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct s5h1420_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &s5h1420_ops, sizeof(struct dvb_frontend_ops));
+ state->postlocked = 0;
+ state->fclk = 88000000;
+ state->tunedfreq = 0;
+ state->fec_inner = FEC_NONE;
+ state->symbol_rate = 0;
+
+ /* check if the demod is there + identify it */
+ identity = s5h1420_readreg(state, 0x00);
+ if (identity != 0x03)
+ goto error;
+
+ /* create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops s5h1420_ops = {
+
+ .info = {
+ .name = "Samsung S5H1420 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 125, /* kHz for QPSK frontends */
+ .frequency_tolerance = 29500,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ /* .symbol_rate_tolerance = ???,*/
+ .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 = s5h1420_release,
+
+ .init = s5h1420_init,
+ .sleep = s5h1420_sleep,
+
+ .set_frontend = s5h1420_set_frontend,
+ .get_frontend = s5h1420_get_frontend,
+ .get_tune_settings = s5h1420_get_tune_settings,
+
+ .read_status = s5h1420_read_status,
+ .read_ber = s5h1420_read_ber,
+ .read_signal_strength = s5h1420_read_signal_strength,
+ .read_ucblocks = s5h1420_read_ucblocks,
+
+ .diseqc_send_master_cmd = s5h1420_send_master_cmd,
+ .diseqc_recv_slave_reply = s5h1420_recv_slave_reply,
+ .diseqc_send_burst = s5h1420_send_burst,
+ .set_tone = s5h1420_set_tone,
+ .set_voltage = s5h1420_set_voltage,
+};
+
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("Samsung S5H1420 DVB-S Demodulator driver");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(s5h1420_attach);
diff --git a/drivers/media/dvb/frontends/s5h1420.h b/drivers/media/dvb/frontends/s5h1420.h
new file mode 100644
index 0000000..b687fc7
--- /dev/null
+++ b/drivers/media/dvb/frontends/s5h1420.h
@@ -0,0 +1,41 @@
+/*
+ Driver for S5H1420 QPSK Demodulators
+
+ Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>
+
+ 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 S5H1420_H
+#define S5H1420_H
+
+#include <linux/dvb/frontend.h>
+
+struct s5h1420_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u32* freqout);
+};
+
+extern struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // S5H1420_H
diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c
index e681263..928aca0 100644
--- a/drivers/media/dvb/frontends/stv0297.c
+++ b/drivers/media/dvb/frontends/stv0297.c
@@ -617,7 +617,7 @@ static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
/* wait for WGAGC lock */
starttime = jiffies;
- timeout = jiffies + (200 * HZ) / 1000;
+ timeout = jiffies + msecs_to_jiffies(2000);
while (time_before(jiffies, timeout)) {
msleep(10);
if (stv0297_readreg(state, 0x43) & 0x08)
@@ -629,7 +629,7 @@ static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
msleep(20);
/* wait for equaliser partial convergence */
- timeout = jiffies + (50 * HZ) / 1000;
+ timeout = jiffies + msecs_to_jiffies(500);
while (time_before(jiffies, timeout)) {
msleep(10);
@@ -642,7 +642,7 @@ static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
}
/* wait for equaliser full convergence */
- timeout = jiffies + (delay * HZ) / 1000;
+ timeout = jiffies + msecs_to_jiffies(delay);
while (time_before(jiffies, timeout)) {
msleep(10);
@@ -659,7 +659,7 @@ static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par
stv0297_writereg_mask(state, 0x88, 8, 0);
/* wait for main lock */
- timeout = jiffies + (20 * HZ) / 1000;
+ timeout = jiffies + msecs_to_jiffies(20);
while (time_before(jiffies, timeout)) {
msleep(10);
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index 0beb370..ab0c032 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -49,10 +49,8 @@ struct tda1004x_state {
/* private demod data */
u8 initialised;
enum tda1004x_demod demod_type;
- u8 fw_version;
};
-
static int debug;
#define dprintk(args...) \
do { \
@@ -122,6 +120,8 @@ static int debug;
#define TDA10046H_GPIO_OUT_SEL 0x41
#define TDA10046H_GPIO_SELECT 0x42
#define TDA10046H_AGC_CONF 0x43
+#define TDA10046H_AGC_THR 0x44
+#define TDA10046H_AGC_RENORM 0x45
#define TDA10046H_AGC_GAINS 0x46
#define TDA10046H_AGC_TUN_MIN 0x47
#define TDA10046H_AGC_TUN_MAX 0x48
@@ -274,14 +274,26 @@ static int tda10046h_set_bandwidth(struct tda1004x_state *state,
switch (bandwidth) {
case BANDWIDTH_6_MHZ:
tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+ if (state->config->if_freq == TDA10046_FREQ_045) {
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x09);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x4f);
+ }
break;
case BANDWIDTH_7_MHZ:
tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+ if (state->config->if_freq == TDA10046_FREQ_045) {
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0a);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x79);
+ }
break;
case BANDWIDTH_8_MHZ:
tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+ if (state->config->if_freq == TDA10046_FREQ_045) {
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0b);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xa3);
+ }
break;
default:
@@ -315,20 +327,35 @@ static int tda1004x_do_upload(struct tda1004x_state *state,
memcpy(buf + 1, mem + pos, tx_size);
fw_msg.len = tx_size + 1;
if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
- printk("tda1004x: Error during firmware upload\n");
+ printk(KERN_ERR "tda1004x: Error during firmware upload\n");
return -EIO;
}
pos += tx_size;
dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos);
}
+ // give the DSP a chance to settle 03/10/05 Hac
+ msleep(100);
return 0;
}
-static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion)
+static int tda1004x_check_upload_ok(struct tda1004x_state *state)
{
u8 data1, data2;
+ unsigned long timeout;
+
+ if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+ timeout = jiffies + 2 * HZ;
+ while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n");
+ break;
+ }
+ msleep(1);
+ }
+ } else
+ msleep(100);
// check upload was OK
tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
@@ -336,9 +363,11 @@ static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion)
data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);
data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);
- if ((data1 != 0x67) || (data2 != dspVersion))
+ if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2e) {
+ printk(KERN_INFO "tda1004x: found firmware revision %x -- invalid\n", data2);
return -EIO;
-
+ }
+ printk(KERN_INFO "tda1004x: found firmware revision %x -- ok\n", data2);
return 0;
}
@@ -349,14 +378,14 @@ static int tda10045_fwupload(struct dvb_frontend* fe)
const struct firmware *fw;
/* don't re-upload unless necessary */
- if (tda1004x_check_upload_ok(state, 0x2c) == 0)
+ if (tda1004x_check_upload_ok(state) == 0)
return 0;
/* request the firmware, this will block until someone uploads it */
- printk("tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE);
+ printk(KERN_INFO "tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE);
ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
if (ret) {
- printk("tda1004x: no firmware upload (timeout or file not found?)\n");
+ printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
return ret;
}
@@ -370,95 +399,93 @@ static int tda10045_fwupload(struct dvb_frontend* fe)
tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ);
ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);
+ release_firmware(fw);
if (ret)
return ret;
- printk("tda1004x: firmware upload complete\n");
+ printk(KERN_INFO "tda1004x: firmware upload complete\n");
/* wait for DSP to initialise */
/* DSPREADY doesn't seem to work on the TDA10045H */
msleep(100);
- return tda1004x_check_upload_ok(state, 0x2c);
+ return tda1004x_check_upload_ok(state);
}
-static int tda10046_get_fw_version(struct tda1004x_state *state,
- const struct firmware *fw)
+static void tda10046_init_plls(struct dvb_frontend* fe)
{
- const unsigned char pattern[] = { 0x67, 0x00, 0x50, 0x62, 0x5e, 0x18, 0x67 };
- unsigned int i;
-
- /* area guessed from firmware v20, v21 and v25 */
- for (i = 0x660; i < 0x700; i++) {
- if (!memcmp(&fw->data[i], pattern, sizeof(pattern))) {
- state->fw_version = fw->data[i + sizeof(pattern)];
- printk(KERN_INFO "tda1004x: using firmware v%02x\n",
- state->fw_version);
- return 0;
- }
- }
+ struct tda1004x_state* state = fe->demodulator_priv;
- return -EINVAL;
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0);
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); // PLL M = 10
+ if (state->config->xtal_freq == TDA10046_XTAL_4M ) {
+ dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __FUNCTION__);
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
+ } else {
+ dprintk("%s: setting up PLLs for a 16 MHz Xtal\n", __FUNCTION__);
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3
+ }
+ tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99);
+ switch (state->config->if_freq) {
+ case TDA10046_FREQ_3617:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
+ break;
+ case TDA10046_FREQ_3613:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x13);
+ break;
+ case TDA10046_FREQ_045:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0b);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xa3);
+ break;
+ case TDA10046_FREQ_052:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x06);
+ break;
+ }
+ tda10046h_set_bandwidth(state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
}
static int tda10046_fwupload(struct dvb_frontend* fe)
{
struct tda1004x_state* state = fe->demodulator_priv;
- unsigned long timeout;
int ret;
const struct firmware *fw;
/* reset + wake up chip */
- tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0);
+ tda1004x_write_byteI(state, TDA1004X_CONFC4, 0);
tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
- msleep(100);
+ /* let the clocks recover from sleep */
+ msleep(5);
/* don't re-upload unless necessary */
- if (tda1004x_check_upload_ok(state, state->fw_version) == 0)
+ if (tda1004x_check_upload_ok(state) == 0)
return 0;
- /* request the firmware, this will block until someone uploads it */
- printk("tda1004x: waiting for firmware upload (%s)...\n", TDA10046_DEFAULT_FIRMWARE);
- ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
- if (ret) {
- printk("tda1004x: no firmware upload (timeout or file not found?)\n");
- return ret;
- }
-
- if (fw->size < 24478) { /* size of firmware v20, which is the smallest of v20, v21 and v25 */
- printk("tda1004x: firmware file seems to be too small (%d bytes)\n", fw->size);
- return -EINVAL;
- }
-
- ret = tda10046_get_fw_version(state, fw);
- if (ret < 0) {
- printk("tda1004x: unable to find firmware version\n");
- return ret;
- }
-
/* set parameters */
- tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10);
- tda1004x_write_byteI(state, TDA10046H_CONFPLL3, state->config->n_i2c);
- tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c);
- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
-
- ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
- if (ret)
- return ret;
- printk("tda1004x: firmware upload complete\n");
-
- /* wait for DSP to initialise */
- timeout = jiffies + HZ;
- while (!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
- if (time_after(jiffies, timeout)) {
- printk("tda1004x: DSP failed to initialised.\n");
- return -EIO;
+ tda10046_init_plls(fe);
+
+ if (state->config->request_firmware != NULL) {
+ /* request the firmware, this will block until someone uploads it */
+ printk(KERN_INFO "tda1004x: waiting for firmware upload...\n");
+ ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
+ if (ret) {
+ printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
+ return ret;
}
- msleep(1);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+ ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
+ release_firmware(fw);
+ if (ret)
+ return ret;
+ } else {
+ /* boot from firmware eeprom */
+ /* Hac Note: we might need to do some GPIO Magic here */
+ printk(KERN_INFO "tda1004x: booting from eeprom\n");
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4);
+ msleep(300);
}
-
- return tda1004x_check_upload_ok(state, state->fw_version);
+ return tda1004x_check_upload_ok(state);
}
static int tda1004x_encode_fec(int fec)
@@ -560,12 +587,10 @@ static int tda10046_init(struct dvb_frontend* fe)
if (tda10046_fwupload(fe)) {
printk("tda1004x: firmware upload failed\n");
- return -EIO;
+ return -EIO;
}
- tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); // wake up the chip
-
- // Init the PLL
+ // Init the tuner PLL
if (state->config->pll_init) {
tda1004x_enable_tuner_i2c(state);
state->config->pll_init(fe);
@@ -574,32 +599,44 @@ static int tda10046_init(struct dvb_frontend* fe)
// tda setup
tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0x40);
- tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer
- tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); // PLL M = 10
- tda1004x_write_byteI(state, TDA10046H_CONFPLL3, state->config->n_i2c); // PLL P = N = 0
- tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // }
- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0); // AGC setup
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities
+ tda1004x_write_byteI(state, TDA1004X_AUTO, 7); // select HP stream
+ tda1004x_write_byteI(state, TDA1004X_CONFC1, 8); // disable pulse killer
+
+ tda10046_init_plls(fe);
+ switch (state->config->agc_config) {
+ case TDA10046_AGC_DEFAULT:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup
+ tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+ break;
+ case TDA10046_AGC_IFO_AUTO_NEG:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
+ tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+ break;
+ case TDA10046_AGC_IFO_AUTO_POS:
+ 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:
+ 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, 0x0E); // Gain Renormalize
+ tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
+ break;
+ }
+ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // }
tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // }
tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // }
- tda1004x_write_mask(state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits
tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1
- tda1004x_write_mask(state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm
+ tda1004x_write_byteI(state, TDA10046H_CVBER_CTRL, 0x1a); // 10^6 VBER measurement bits
tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
- tda1004x_write_mask(state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config
- tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN
+ tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config
+ tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
+
tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup
tda1004x_write_byteI(state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config
- tda1004x_write_mask(state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select
- tda10046h_set_bandwidth(state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz
-
- tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
+ tda1004x_write_byteI(state, TDA10046H_GPIO_SELECT, 8); // GPIO select
state->initialised = 1;
return 0;
@@ -629,9 +666,6 @@ static int tda1004x_set_fe(struct dvb_frontend* fe,
state->config->pll_set(fe, fe_params);
tda1004x_disable_tuner_i2c(state);
- if (state->demod_type == TDA1004X_DEMOD_TDA10046)
- tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 4);
-
// Hardcoded to use auto as much as possible on the TDA10045 as it
// is very unreliable if AUTO mode is _not_ used.
if (state->demod_type == TDA1004X_DEMOD_TDA10045) {
@@ -1089,6 +1123,11 @@ static int tda1004x_sleep(struct dvb_frontend* fe)
break;
case TDA1004X_DEMOD_TDA10046:
+ if (state->config->pll_sleep != NULL) {
+ tda1004x_enable_tuner_i2c(state);
+ state->config->pll_sleep(fe);
+ tda1004x_disable_tuner_i2c(state);
+ }
tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
break;
}
@@ -1100,8 +1139,9 @@ static int tda1004x_sleep(struct dvb_frontend* fe)
static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
{
fesettings->min_delay_ms = 800;
- fesettings->step_size = 166667;
- fesettings->max_drift = 166667*2;
+ /* Drift compensation makes no sense for DVB-T */
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
return 0;
}
@@ -1216,7 +1256,6 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops));
state->initialised = 0;
state->demod_type = TDA1004X_DEMOD_TDA10046;
- state->fw_version = 0x20; /* dummy default value */
/* check if the demod is there */
if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) {
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
index c8e1d54..8659c52 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -26,6 +26,25 @@
#include <linux/dvb/frontend.h>
#include <linux/firmware.h>
+enum tda10046_xtal {
+ TDA10046_XTAL_4M,
+ TDA10046_XTAL_16M,
+};
+
+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 */
+};
+
+enum tda10046_if {
+ TDA10046_FREQ_3617, /* original config, 36,166 MHZ */
+ TDA10046_FREQ_3613, /* 36,13 MHZ */
+ TDA10046_FREQ_045, /* low IF, 4.0, 4.5, or 5.0 MHZ */
+ TDA10046_FREQ_052, /* low IF, 5.1667 MHZ for tda9889 */
+};
+
struct tda1004x_config
{
/* the demodulator's i2c address */
@@ -37,14 +56,22 @@ struct tda1004x_config
/* Does the OCLK signal need inverted? */
u8 invert_oclk;
- /* value of N_I2C of the CONF_PLL3 register */
- u8 n_i2c;
+ /* Xtal frequency, 4 or 16MHz*/
+ enum tda10046_xtal xtal_freq;
+
+ /* IF frequency */
+ enum tda10046_if if_freq;
+
+ /* AGC configuration */
+ enum tda10046_agc agc_config;
/* PLL maintenance */
int (*pll_init)(struct dvb_frontend* fe);
+ void (*pll_sleep)(struct dvb_frontend* fe);
int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
/* request firmware for device */
+ /* set this to NULL if the card has a firmware EEPROM */
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
diff --git a/drivers/media/dvb/frontends/tda80xx.c b/drivers/media/dvb/frontends/tda80xx.c
index 032d348..88e1250 100644
--- a/drivers/media/dvb/frontends/tda80xx.c
+++ b/drivers/media/dvb/frontends/tda80xx.c
@@ -27,7 +27,6 @@
#include <linux/spinlock.h>
#include <linux/threads.h>
#include <linux/interrupt.h>
-#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
OpenPOWER on IntegriCloud