aboutsummaryrefslogtreecommitdiffstats
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/Kconfig172
-rw-r--r--drivers/media/dvb/frontends/Makefile30
-rw-r--r--drivers/media/dvb/frontends/at76c651.c450
-rw-r--r--drivers/media/dvb/frontends/at76c651.h47
-rw-r--r--drivers/media/dvb/frontends/cx22700.c435
-rw-r--r--drivers/media/dvb/frontends/cx22700.h41
-rw-r--r--drivers/media/dvb/frontends/cx22702.c519
-rw-r--r--drivers/media/dvb/frontends/cx22702.h46
-rw-r--r--drivers/media/dvb/frontends/cx24110.c657
-rw-r--r--drivers/media/dvb/frontends/cx24110.h45
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.c83
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.h137
-rw-r--r--drivers/media/dvb/frontends/dib3000.h54
-rw-r--r--drivers/media/dvb/frontends/dib3000mb.c784
-rw-r--r--drivers/media/dvb/frontends/dib3000mb_priv.h467
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.c931
-rw-r--r--drivers/media/dvb/frontends/dib3000mc_priv.h428
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c168
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h34
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.c279
-rw-r--r--drivers/media/dvb/frontends/dvb_dummy_fe.h32
-rw-r--r--drivers/media/dvb/frontends/l64781.c602
-rw-r--r--drivers/media/dvb/frontends/l64781.h42
-rw-r--r--drivers/media/dvb/frontends/mt312.c729
-rw-r--r--drivers/media/dvb/frontends/mt312.h47
-rw-r--r--drivers/media/dvb/frontends/mt312_priv.h162
-rw-r--r--drivers/media/dvb/frontends/mt352.c610
-rw-r--r--drivers/media/dvb/frontends/mt352.h72
-rw-r--r--drivers/media/dvb/frontends/mt352_priv.h127
-rw-r--r--drivers/media/dvb/frontends/nxt2002.c705
-rw-r--r--drivers/media/dvb/frontends/nxt2002.h23
-rw-r--r--drivers/media/dvb/frontends/nxt6000.c554
-rw-r--r--drivers/media/dvb/frontends/nxt6000.h43
-rw-r--r--drivers/media/dvb/frontends/nxt6000_priv.h265
-rw-r--r--drivers/media/dvb/frontends/or51132.c628
-rw-r--r--drivers/media/dvb/frontends/or51132.h48
-rw-r--r--drivers/media/dvb/frontends/or51211.c631
-rw-r--r--drivers/media/dvb/frontends/or51211.h44
-rw-r--r--drivers/media/dvb/frontends/sp8870.c614
-rw-r--r--drivers/media/dvb/frontends/sp8870.h45
-rw-r--r--drivers/media/dvb/frontends/sp887x.c606
-rw-r--r--drivers/media/dvb/frontends/sp887x.h29
-rw-r--r--drivers/media/dvb/frontends/stv0297.c798
-rw-r--r--drivers/media/dvb/frontends/stv0297.h44
-rw-r--r--drivers/media/dvb/frontends/stv0299.c731
-rw-r--r--drivers/media/dvb/frontends/stv0299.h104
-rw-r--r--drivers/media/dvb/frontends/tda10021.c469
-rw-r--r--drivers/media/dvb/frontends/tda10021.h42
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c1206
-rw-r--r--drivers/media/dvb/frontends/tda1004x.h56
-rw-r--r--drivers/media/dvb/frontends/tda8083.c456
-rw-r--r--drivers/media/dvb/frontends/tda8083.h45
-rw-r--r--drivers/media/dvb/frontends/tda80xx.c734
-rw-r--r--drivers/media/dvb/frontends/tda80xx.h51
-rw-r--r--drivers/media/dvb/frontends/ves1820.c450
-rw-r--r--drivers/media/dvb/frontends/ves1820.h51
-rw-r--r--drivers/media/dvb/frontends/ves1x93.c545
-rw-r--r--drivers/media/dvb/frontends/ves1x93.h50
58 files changed, 18297 insertions, 0 deletions
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
new file mode 100644
index 000000000000..0bfd4df17d08
--- /dev/null
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -0,0 +1,172 @@
+menu "Customise DVB Frontends"
+ depends on DVB_CORE
+
+comment "DVB-S (satellite) frontends"
+ depends on DVB_CORE
+
+config DVB_STV0299
+ tristate "ST STV0299 based"
+ depends on DVB_CORE
+ 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
+ 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
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA80XX
+ tristate "Philips TDA8044 or TDA8083 based"
+ depends on DVB_CORE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_MT312
+ tristate "Zarlink MT312 based"
+ depends on DVB_CORE
+ 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
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+comment "DVB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_SP8870
+ tristate "Spase sp8870 based"
+ depends on DVB_CORE
+ select FW_LOADER
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware sp8870" to
+ download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+
+config DVB_SP887X
+ tristate "Spase sp887x based"
+ depends on DVB_CORE
+ select FW_LOADER
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+
+config DVB_CX22700
+ tristate "Conexant CX22700 based"
+ depends on DVB_CORE
+ 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
+ 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
+ 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
+ select FW_LOADER
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the commands
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ download/extract them, and then copy them to /usr/lib/hotplug/firmware.
+
+config DVB_NXT6000
+ tristate "NxtWave Communications NXT6000 based"
+ depends on DVB_CORE
+ 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
+ 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
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB3000MC
+ tristate "DiBcom 3000P/M-C"
+ depends on DVB_CORE
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+comment "DVB-C (cable) frontends"
+ depends on DVB_CORE
+
+config DVB_ATMEL_AT76C651
+ tristate "Atmel AT76C651 based"
+ depends on DVB_CORE
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_VES1820
+ tristate "VLSI VES1820 based"
+ depends on DVB_CORE
+ 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
+ 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
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+comment "ATSC (North American/Korean Terresterial DTV) frontends"
+ depends on DVB_CORE
+
+config DVB_NXT2002
+ tristate "Nxt2002 based"
+ depends on DVB_CORE
+ select FW_LOADER
+ help
+ An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
+
+config DVB_OR51132
+ tristate "OR51132 based (pcHDTV)"
+ depends on DVB_CORE
+
+config DVB_OR51211
+ tristate "or51211 based (pcHDTV HD2000 card)"
+ depends on DVB_CORE
+ select FW_LOADER
+ help
+ An ATSC 8VSB 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
new file mode 100644
index 000000000000..7f8784870eab
--- /dev/null
+++ b/drivers/media/dvb/frontends/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the kernel DVB frontend device drivers.
+#
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
+
+obj-$(CONFIG_DVB_CORE) += dvb-pll.o
+obj-$(CONFIG_DVB_STV0299) += stv0299.o
+obj-$(CONFIG_DVB_SP8870) += sp8870.o
+obj-$(CONFIG_DVB_CX22700) += cx22700.o
+obj-$(CONFIG_DVB_ATMEL_AT76C651) += at76c651.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_MT312) += mt312.o
+obj-$(CONFIG_DVB_VES1820) += ves1820.o
+obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
+obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o
+obj-$(CONFIG_DVB_SP887X) += sp887x.o
+obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
+obj-$(CONFIG_DVB_MT352) += mt352.o
+obj-$(CONFIG_DVB_CX22702) += cx22702.o
+obj-$(CONFIG_DVB_TDA80XX) += tda80xx.o
+obj-$(CONFIG_DVB_TDA10021) += tda10021.o
+obj-$(CONFIG_DVB_STV0297) += stv0297.o
+obj-$(CONFIG_DVB_NXT2002) += nxt2002.o
+obj-$(CONFIG_DVB_OR51211) += or51211.o
+obj-$(CONFIG_DVB_OR51132) += or51132.o
diff --git a/drivers/media/dvb/frontends/at76c651.c b/drivers/media/dvb/frontends/at76c651.c
new file mode 100644
index 000000000000..ce2eaa1640e8
--- /dev/null
+++ b/drivers/media/dvb/frontends/at76c651.c
@@ -0,0 +1,450 @@
+/*
+ * at76c651.c
+ *
+ * Atmel DVB-C Frontend Driver (at76c651/tua6010xs)
+ *
+ * Copyright (C) 2001 fnbrd <fnbrd@gmx.de>
+ * & 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ * & 2003 Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * AT76C651
+ * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf
+ * http://www.atmel.com/atmel/acrobat/doc1320.pdf
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include "dvb_frontend.h"
+#include "at76c651.h"
+
+
+struct at76c651_state {
+
+ struct i2c_adapter* i2c;
+
+ struct dvb_frontend_ops ops;
+
+ const struct at76c651_config* config;
+
+ struct dvb_frontend frontend;
+
+ /* revision of the chip */
+ u8 revision;
+
+ /* last QAM value set */
+ u8 qam;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "at76c651: " args); \
+ } while (0)
+
+
+#if ! defined(__powerpc__)
+static __inline__ int __ilog2(unsigned long x)
+{
+ int i;
+
+ if (x == 0)
+ return -1;
+
+ for (i = 0; x != 0; i++)
+ x >>= 1;
+
+ return i - 1;
+}
+#endif
+
+static int at76c651_writereg(struct at76c651_state* state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg =
+ { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error "
+ "(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+ __FUNCTION__, reg, data, ret);
+
+ msleep(10);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static u8 at76c651_readreg(struct at76c651_state* state, u8 reg)
+{
+ int ret;
+ u8 val;
+ struct i2c_msg msg[] = {
+ { .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = &val, .len = 1 }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return val;
+}
+
+static int at76c651_reset(struct at76c651_state* state)
+{
+ return at76c651_writereg(state, 0x07, 0x01);
+}
+
+static void at76c651_disable_interrupts(struct at76c651_state* state)
+{
+ at76c651_writereg(state, 0x0b, 0x00);
+}
+
+static int at76c651_set_auto_config(struct at76c651_state *state)
+{
+ /*
+ * Autoconfig
+ */
+
+ at76c651_writereg(state, 0x06, 0x01);
+
+ /*
+ * Performance optimizations, should be done after autoconfig
+ */
+
+ at76c651_writereg(state, 0x10, 0x06);
+ at76c651_writereg(state, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10);
+ at76c651_writereg(state, 0x15, 0x28);
+ at76c651_writereg(state, 0x20, 0x09);
+ at76c651_writereg(state, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90);
+ at76c651_writereg(state, 0x30, 0x90);
+ if (state->qam == 5)
+ at76c651_writereg(state, 0x35, 0x2A);
+
+ /*
+ * Initialize A/D-converter
+ */
+
+ if (state->revision == 0x11) {
+ at76c651_writereg(state, 0x2E, 0x38);
+ at76c651_writereg(state, 0x2F, 0x13);
+ }
+
+ at76c651_disable_interrupts(state);
+
+ /*
+ * Restart operation
+ */
+
+ at76c651_reset(state);
+
+ return 0;
+}
+
+static void at76c651_set_bbfreq(struct at76c651_state* state)
+{
+ at76c651_writereg(state, 0x04, 0x3f);
+ at76c651_writereg(state, 0x05, 0xee);
+}
+
+static int at76c651_set_symbol_rate(struct at76c651_state* state, u32 symbol_rate)
+{
+ u8 exponent;
+ u32 mantissa;
+
+ if (symbol_rate > 9360000)
+ return -EINVAL;
+
+ /*
+ * FREF = 57800 kHz
+ * exponent = 10 + floor (log2(symbol_rate / FREF))
+ * mantissa = (symbol_rate / FREF) * (1 << (30 - exponent))
+ */
+
+ exponent = __ilog2((symbol_rate << 4) / 903125);
+ mantissa = ((symbol_rate / 3125) * (1 << (24 - exponent))) / 289;
+
+ at76c651_writereg(state, 0x00, mantissa >> 13);
+ at76c651_writereg(state, 0x01, mantissa >> 5);
+ at76c651_writereg(state, 0x02, (mantissa << 3) | exponent);
+
+ return 0;
+}
+
+static int at76c651_set_qam(struct at76c651_state *state, fe_modulation_t qam)
+{
+ switch (qam) {
+ case QPSK:
+ state->qam = 0x02;
+ break;
+ case QAM_16:
+ state->qam = 0x04;
+ break;
+ case QAM_32:
+ state->qam = 0x05;
+ break;
+ case QAM_64:
+ state->qam = 0x06;
+ break;
+ case QAM_128:
+ state->qam = 0x07;
+ break;
+ case QAM_256:
+ state->qam = 0x08;
+ break;
+#if 0
+ case QAM_512:
+ state->qam = 0x09;
+ break;
+ case QAM_1024:
+ state->qam = 0x0A;
+ break;
+#endif
+ default:
+ return -EINVAL;
+
+ }
+
+ return at76c651_writereg(state, 0x03, state->qam);
+}
+
+static int at76c651_set_inversion(struct at76c651_state* state, fe_spectral_inversion_t inversion)
+{
+ u8 feciqinv = at76c651_readreg(state, 0x60);
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ feciqinv |= 0x02;
+ feciqinv &= 0xFE;
+ break;
+
+ case INVERSION_ON:
+ feciqinv |= 0x03;
+ break;
+
+ case INVERSION_AUTO:
+ feciqinv &= 0xFC;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return at76c651_writereg(state, 0x60, feciqinv);
+}
+
+static int at76c651_set_parameters(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *p)
+{
+ int ret;
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+ at76c651_writereg(state, 0x0c, 0xc3);
+ state->config->pll_set(fe, p);
+ at76c651_writereg(state, 0x0c, 0xc2);
+
+ if ((ret = at76c651_set_symbol_rate(state, p->u.qam.symbol_rate)))
+ return ret;
+
+ if ((ret = at76c651_set_inversion(state, p->inversion)))
+ return ret;
+
+ return at76c651_set_auto_config(state);
+}
+
+static int at76c651_set_defaults(struct dvb_frontend* fe)
+{
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+ at76c651_set_symbol_rate(state, 6900000);
+ at76c651_set_qam(state, QAM_64);
+ at76c651_set_bbfreq(state);
+ at76c651_set_auto_config(state);
+
+ if (state->config->pll_init) {
+ at76c651_writereg(state, 0x0c, 0xc3);
+ state->config->pll_init(fe);
+ at76c651_writereg(state, 0x0c, 0xc2);
+ }
+
+ return 0;
+}
+
+static int at76c651_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+ u8 sync;
+
+ /*
+ * Bits: FEC, CAR, EQU, TIM, AGC2, AGC1, ADC, PLL (PLL=0)
+ */
+ sync = at76c651_readreg(state, 0x80);
+ *status = 0;
+
+ if (sync & (0x04 | 0x10)) /* AGC1 || TIM */
+ *status |= FE_HAS_SIGNAL;
+ if (sync & 0x10) /* TIM */
+ *status |= FE_HAS_CARRIER;
+ if (sync & 0x80) /* FEC */
+ *status |= FE_HAS_VITERBI;
+ if (sync & 0x40) /* CAR */
+ *status |= FE_HAS_SYNC;
+ if ((sync & 0xF0) == 0xF0) /* TIM && EQU && CAR && FEC */
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int at76c651_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+ *ber = (at76c651_readreg(state, 0x81) & 0x0F) << 16;
+ *ber |= at76c651_readreg(state, 0x82) << 8;
+ *ber |= at76c651_readreg(state, 0x83);
+ *ber *= 10;
+
+ return 0;
+}
+
+static int at76c651_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+ u8 gain = ~at76c651_readreg(state, 0x91);
+ *strength = (gain << 8) | gain;
+
+ return 0;
+}
+
+static int at76c651_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+ *snr = 0xFFFF -
+ ((at76c651_readreg(state, 0x8F) << 8) |
+ at76c651_readreg(state, 0x90));
+
+ return 0;
+}
+
+static int at76c651_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+
+ *ucblocks = at76c651_readreg(state, 0x82);
+
+ return 0;
+}
+
+static int at76c651_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 50;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void at76c651_release(struct dvb_frontend* fe)
+{
+ struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops at76c651_ops;
+
+struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct at76c651_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct at76c651_state*) kmalloc(sizeof(struct at76c651_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->qam = 0;
+
+ /* check if the demod is there */
+ if (at76c651_readreg(state, 0x0e) != 0x65) goto error;
+
+ /* finalise state setup */
+ state->i2c = i2c;
+ state->revision = at76c651_readreg(state, 0x0f) & 0xfe;
+ memcpy(&state->ops, &at76c651_ops, sizeof(struct dvb_frontend_ops));
+
+ /* 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 at76c651_ops = {
+
+ .info = {
+ .name = "Atmel AT76C651B DVB-C",
+ .type = FE_QAM,
+ .frequency_min = 48250000,
+ .frequency_max = 863250000,
+ .frequency_stepsize = 62500,
+ /*.frequency_tolerance = */ /* FIXME: 12% of SR */
+ .symbol_rate_min = 0, /* FIXME */
+ .symbol_rate_max = 9360000, /* FIXME */
+ .symbol_rate_tolerance = 4000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 |
+ FE_CAN_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER
+ },
+
+ .release = at76c651_release,
+
+ .init = at76c651_set_defaults,
+
+ .set_frontend = at76c651_set_parameters,
+ .get_tune_settings = at76c651_get_tune_settings,
+
+ .read_status = at76c651_read_status,
+ .read_ber = at76c651_read_ber,
+ .read_signal_strength = at76c651_read_signal_strength,
+ .read_snr = at76c651_read_snr,
+ .read_ucblocks = at76c651_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Atmel AT76C651 DVB-C Demodulator Driver");
+MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(at76c651_attach);
diff --git a/drivers/media/dvb/frontends/at76c651.h b/drivers/media/dvb/frontends/at76c651.h
new file mode 100644
index 000000000000..34054df93608
--- /dev/null
+++ b/drivers/media/dvb/frontends/at76c651.h
@@ -0,0 +1,47 @@
+/*
+ * at76c651.c
+ *
+ * Atmel DVB-C Frontend Driver (at76c651)
+ *
+ * Copyright (C) 2001 fnbrd <fnbrd@gmx.de>
+ * & 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ * & 2003 Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * AT76C651
+ * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf
+ * http://www.atmel.com/atmel/acrobat/doc1320.pdf
+ */
+
+#ifndef AT76C651_H
+#define AT76C651_H
+
+#include <linux/dvb/frontend.h>
+
+struct at76c651_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);
+};
+
+extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // AT76C651_H
diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c
new file mode 100644
index 000000000000..a212279042b8
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22700.c
@@ -0,0 +1,435 @@
+/*
+ Conexant cx22700 DVB OFDM demodulator driver
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ Holger Waechtler <holger@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "cx22700.h"
+
+
+struct cx22700_state {
+
+ struct i2c_adapter* i2c;
+
+ struct dvb_frontend_ops ops;
+
+ const struct cx22700_config* config;
+
+ struct dvb_frontend frontend;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "cx22700: " args); \
+ } while (0)
+
+static u8 init_tab [] = {
+ 0x04, 0x10,
+ 0x05, 0x09,
+ 0x06, 0x00,
+ 0x08, 0x04,
+ 0x09, 0x00,
+ 0x0a, 0x01,
+ 0x15, 0x40,
+ 0x16, 0x10,
+ 0x17, 0x87,
+ 0x18, 0x17,
+ 0x1a, 0x10,
+ 0x25, 0x04,
+ 0x2e, 0x00,
+ 0x39, 0x00,
+ 0x3a, 0x04,
+ 0x45, 0x08,
+ 0x46, 0x02,
+ 0x47, 0x05,
+};
+
+
+static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ret = i2c_transfer (state->i2c, &msg, 1);
+
+ if (ret != 1)
+ printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+ __FUNCTION__, reg, data, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+static int cx22700_readreg (struct cx22700_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ret = i2c_transfer (state->i2c, msg, 2);
+
+ if (ret != 2) return -EIO;
+
+ return b1[0];
+}
+
+static int cx22700_set_inversion (struct cx22700_state* state, int inversion)
+{
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch (inversion) {
+ case INVERSION_AUTO:
+ return -EOPNOTSUPP;
+ case INVERSION_ON:
+ val = cx22700_readreg (state, 0x09);
+ return cx22700_writereg (state, 0x09, val | 0x01);
+ case INVERSION_OFF:
+ val = cx22700_readreg (state, 0x09);
+ return cx22700_writereg (state, 0x09, val & 0xfe);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_parameters *p)
+{
+ static const u8 qam_tab [4] = { 0, 1, 0, 2 };
+ static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 };
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8)
+ return -EINVAL;
+
+ if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8)
+
+ if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5)
+ return -EINVAL;
+
+ if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+ p->guard_interval > GUARD_INTERVAL_1_4)
+ return -EINVAL;
+
+ if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+ p->transmission_mode != TRANSMISSION_MODE_8K)
+ return -EINVAL;
+
+ if (p->constellation != QPSK &&
+ p->constellation != QAM_16 &&
+ p->constellation != QAM_64)
+ return -EINVAL;
+
+ if (p->hierarchy_information < HIERARCHY_NONE ||
+ p->hierarchy_information > HIERARCHY_4)
+ return -EINVAL;
+
+ if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ)
+ return -EINVAL;
+
+ if (p->bandwidth == BANDWIDTH_7_MHZ)
+ cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10));
+ else
+ cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10));
+
+ val = qam_tab[p->constellation - QPSK];
+ val |= p->hierarchy_information - HIERARCHY_NONE;
+
+ cx22700_writereg (state, 0x04, val);
+
+ val = fec_tab[p->code_rate_HP - FEC_1_2] << 3;
+ val |= fec_tab[p->code_rate_LP - FEC_1_2];
+
+ cx22700_writereg (state, 0x05, val);
+
+ val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2;
+ val |= p->transmission_mode - TRANSMISSION_MODE_2K;
+
+ cx22700_writereg (state, 0x06, val);
+
+ cx22700_writereg (state, 0x08, 0x04 | 0x02); /* use user tps parameters */
+ cx22700_writereg (state, 0x08, 0x04); /* restart aquisition */
+
+ return 0;
+}
+
+static int cx22700_get_tps (struct cx22700_state* state, struct dvb_ofdm_parameters *p)
+{
+ static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 };
+ static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4,
+ FEC_5_6, FEC_7_8 };
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (!(cx22700_readreg(state, 0x07) & 0x20)) /* tps valid? */
+ return -EAGAIN;
+
+ val = cx22700_readreg (state, 0x01);
+
+ if ((val & 0x7) > 4)
+ p->hierarchy_information = HIERARCHY_AUTO;
+ else
+ p->hierarchy_information = HIERARCHY_NONE + (val & 0x7);
+
+ if (((val >> 3) & 0x3) > 2)
+ p->constellation = QAM_AUTO;
+ else
+ p->constellation = qam_tab[(val >> 3) & 0x3];
+
+ val = cx22700_readreg (state, 0x02);
+
+ if (((val >> 3) & 0x07) > 4)
+ p->code_rate_HP = FEC_AUTO;
+ else
+ p->code_rate_HP = fec_tab[(val >> 3) & 0x07];
+
+ if ((val & 0x07) > 4)
+ p->code_rate_LP = FEC_AUTO;
+ else
+ p->code_rate_LP = fec_tab[val & 0x07];
+
+ val = cx22700_readreg (state, 0x03);
+
+ p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3);
+ p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1);
+
+ return 0;
+}
+
+static int cx22700_init (struct dvb_frontend* fe)
+
+{ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+ int i;
+
+ dprintk("cx22700_init: init chip\n");
+
+ cx22700_writereg (state, 0x00, 0x02); /* soft reset */
+ cx22700_writereg (state, 0x00, 0x00);
+
+ msleep(10);
+
+ for (i=0; i<sizeof(init_tab); i+=2)
+ cx22700_writereg (state, init_tab[i], init_tab[i+1]);
+
+ cx22700_writereg (state, 0x00, 0x01);
+
+ if (state->config->pll_init) {
+ cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */
+ state->config->pll_init(fe);
+ cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */
+ }
+
+ return 0;
+}
+
+static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+ u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+ | (cx22700_readreg (state, 0x0e) << 1);
+ u8 sync = cx22700_readreg (state, 0x07);
+
+ *status = 0;
+
+ if (rs_ber < 0xff00)
+ *status |= FE_HAS_SIGNAL;
+
+ if (sync & 0x20)
+ *status |= FE_HAS_CARRIER;
+
+ if (sync & 0x10)
+ *status |= FE_HAS_VITERBI;
+
+ if (sync & 0x10)
+ *status |= FE_HAS_SYNC;
+
+ if (*status == 0x0f)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+ *ber = cx22700_readreg (state, 0x0c) & 0x7f;
+ cx22700_writereg (state, 0x0c, 0x00);
+
+ return 0;
+}
+
+static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+ u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+ | (cx22700_readreg (state, 0x0e) << 1);
+ *signal_strength = ~rs_ber;
+
+ return 0;
+}
+
+static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+ u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9)
+ | (cx22700_readreg (state, 0x0e) << 1);
+ *snr = ~rs_ber;
+
+ return 0;
+}
+
+static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+ *ucblocks = cx22700_readreg (state, 0x0f);
+ cx22700_writereg (state, 0x0f, 0x00);
+
+ return 0;
+}
+
+static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+
+ cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/
+ cx22700_writereg (state, 0x00, 0x00);
+
+ cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */
+ state->config->pll_set(fe, p);
+ cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */
+ cx22700_set_inversion (state, p->inversion);
+ cx22700_set_tps (state, &p->u.ofdm);
+ cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */
+ cx22700_writereg (state, 0x00, 0x01); /* restart acquire */
+
+ return 0;
+}
+
+static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+ u8 reg09 = cx22700_readreg (state, 0x09);
+
+ p->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF;
+ return cx22700_get_tps (state, &p->u.ofdm);
+}
+
+static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 150;
+ fesettings->step_size = 166667;
+ fesettings->max_drift = 166667*2;
+ return 0;
+}
+
+static void cx22700_release(struct dvb_frontend* fe)
+{
+ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops cx22700_ops;
+
+struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct cx22700_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct cx22700_state*) kmalloc(sizeof(struct cx22700_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &cx22700_ops, sizeof(struct dvb_frontend_ops));
+
+ /* check if the demod is there */
+ if (cx22700_readreg(state, 0x07) < 0) 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 cx22700_ops = {
+
+ .info = {
+ .name = "Conexant CX22700 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 470000000,
+ .frequency_max = 860000000,
+ .frequency_stepsize = 166667,
+ .caps = 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_RECOVER
+ },
+
+ .release = cx22700_release,
+
+ .init = cx22700_init,
+
+ .set_frontend = cx22700_set_frontend,
+ .get_frontend = cx22700_get_frontend,
+ .get_tune_settings = cx22700_get_tune_settings,
+
+ .read_status = cx22700_read_status,
+ .read_ber = cx22700_read_ber,
+ .read_signal_strength = cx22700_read_signal_strength,
+ .read_snr = cx22700_read_snr,
+ .read_ucblocks = cx22700_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx22700_attach);
diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h
new file mode 100644
index 000000000000..c9145b45874b
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22700.h
@@ -0,0 +1,41 @@
+/*
+ Conexant CX22700 DVB OFDM demodulator driver
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ Holger Waechtler <holger@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CX22700_H
+#define CX22700_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22700_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);
+};
+
+extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // CX22700_H
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
new file mode 100644
index 000000000000..1930b513eefa
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -0,0 +1,519 @@
+/*
+ Conexant 22702 DVB OFDM demodulator driver
+
+ based on:
+ Alps TDMB7 DVB OFDM demodulator driver
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ Holger Waechtler <holger@convergence.de>
+
+ Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk>
+
+ 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/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "cx22702.h"
+
+
+struct cx22702_state {
+
+ struct i2c_adapter* i2c;
+
+ struct dvb_frontend_ops ops;
+
+ /* configuration settings */
+ const struct cx22702_config* config;
+
+ struct dvb_frontend frontend;
+
+ /* previous uncorrected block counter */
+ u8 prevUCBlocks;
+};
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+/* Register values to initialise the demod */
+static u8 init_tab [] = {
+ 0x00, 0x00, /* Stop aquisition */
+ 0x0B, 0x06,
+ 0x09, 0x01,
+ 0x0D, 0x41,
+ 0x16, 0x32,
+ 0x20, 0x0A,
+ 0x21, 0x17,
+ 0x24, 0x3e,
+ 0x26, 0xff,
+ 0x27, 0x10,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x10,
+ 0x2b, 0x00,
+ 0x2c, 0x10,
+ 0x2d, 0x00,
+ 0x48, 0xd4,
+ 0x49, 0x56,
+ 0x6b, 0x1e,
+ 0xc8, 0x02,
+ 0xf8, 0x02,
+ 0xf9, 0x00,
+ 0xfa, 0x00,
+ 0xfb, 0x00,
+ 0xfc, 0x00,
+ 0xfd, 0x00,
+};
+
+static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+ __FUNCTION__, reg, data, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+static u8 cx22702_readreg (struct cx22702_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+
+ struct i2c_msg msg [] = {
+ { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return b1[0];
+}
+
+static int cx22702_set_inversion (struct cx22702_state *state, int inversion)
+{
+ u8 val;
+
+ switch (inversion) {
+
+ case INVERSION_AUTO:
+ return -EOPNOTSUPP;
+
+ case INVERSION_ON:
+ val = cx22702_readreg (state, 0x0C);
+ return cx22702_writereg (state, 0x0C, val | 0x01);
+
+ case INVERSION_OFF:
+ val = cx22702_readreg (state, 0x0C);
+ return cx22702_writereg (state, 0x0C, val & 0xfe);
+
+ default:
+ return -EINVAL;
+
+ }
+
+}
+
+/* Retrieve the demod settings */
+static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p)
+{
+ u8 val;
+
+ /* Make sure the TPS regs are valid */
+ if (!(cx22702_readreg(state, 0x0A) & 0x20))
+ return -EAGAIN;
+
+ val = cx22702_readreg (state, 0x01);
+ switch( (val&0x18)>>3) {
+ case 0: p->constellation = QPSK; break;
+ case 1: p->constellation = QAM_16; break;
+ case 2: p->constellation = QAM_64; break;
+ }
+ switch( val&0x07 ) {
+ case 0: p->hierarchy_information = HIERARCHY_NONE; break;
+ case 1: p->hierarchy_information = HIERARCHY_1; break;
+ case 2: p->hierarchy_information = HIERARCHY_2; break;
+ case 3: p->hierarchy_information = HIERARCHY_4; break;
+ }
+
+
+ val = cx22702_readreg (state, 0x02);
+ switch( (val&0x38)>>3 ) {
+ case 0: p->code_rate_HP = FEC_1_2; break;
+ case 1: p->code_rate_HP = FEC_2_3; break;
+ case 2: p->code_rate_HP = FEC_3_4; break;
+ case 3: p->code_rate_HP = FEC_5_6; break;
+ case 4: p->code_rate_HP = FEC_7_8; break;
+ }
+ switch( val&0x07 ) {
+ case 0: p->code_rate_LP = FEC_1_2; break;
+ case 1: p->code_rate_LP = FEC_2_3; break;
+ case 2: p->code_rate_LP = FEC_3_4; break;
+ case 3: p->code_rate_LP = FEC_5_6; break;
+ case 4: p->code_rate_LP = FEC_7_8; break;
+ }
+
+
+ val = cx22702_readreg (state, 0x03);
+ switch( (val&0x0c)>>2 ) {
+ case 0: p->guard_interval = GUARD_INTERVAL_1_32; break;
+ case 1: p->guard_interval = GUARD_INTERVAL_1_16; break;
+ case 2: p->guard_interval = GUARD_INTERVAL_1_8; break;
+ case 3: p->guard_interval = GUARD_INTERVAL_1_4; break;
+ }
+ switch( val&0x03 ) {
+ case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break;
+ case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break;
+ }
+
+ return 0;
+}
+
+/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
+static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ u8 val;
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+ /* set PLL */
+ cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+ state->config->pll_set(fe, p);
+ cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
+
+ /* set inversion */
+ cx22702_set_inversion (state, p->inversion);
+
+ /* set bandwidth */
+ switch(p->u.ofdm.bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 );
+ break;
+ case BANDWIDTH_7_MHZ:
+ cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 );
+ break;
+ case BANDWIDTH_8_MHZ:
+ cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf );
+ break;
+ default:
+ dprintk ("%s: invalid bandwidth\n",__FUNCTION__);
+ return -EINVAL;
+ }
+
+
+ p->u.ofdm.code_rate_LP = FEC_AUTO; //temp hack as manual not working
+
+ /* use auto configuration? */
+ if((p->u.ofdm.hierarchy_information==HIERARCHY_AUTO) ||
+ (p->u.ofdm.constellation==QAM_AUTO) ||
+ (p->u.ofdm.code_rate_HP==FEC_AUTO) ||
+ (p->u.ofdm.code_rate_LP==FEC_AUTO) ||
+ (p->u.ofdm.guard_interval==GUARD_INTERVAL_AUTO) ||
+ (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) {
+
+ /* TPS Source - use hardware driven values */
+ cx22702_writereg(state, 0x06, 0x10);
+ cx22702_writereg(state, 0x07, 0x9);
+ cx22702_writereg(state, 0x08, 0xC1);
+ 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__);
+ return 0;
+ }
+
+ /* manually programmed values */
+ val=0;
+ switch(p->u.ofdm.constellation) {
+ case QPSK: val = (val&0xe7); break;
+ case QAM_16: val = (val&0xe7)|0x08; break;
+ case QAM_64: val = (val&0xe7)|0x10; break;
+ default:
+ dprintk ("%s: invalid constellation\n",__FUNCTION__);
+ return -EINVAL;
+ }
+ switch(p->u.ofdm.hierarchy_information) {
+ case HIERARCHY_NONE: val = (val&0xf8); break;
+ case HIERARCHY_1: val = (val&0xf8)|1; break;
+ case HIERARCHY_2: val = (val&0xf8)|2; break;
+ case HIERARCHY_4: val = (val&0xf8)|3; break;
+ default:
+ dprintk ("%s: invalid hierarchy\n",__FUNCTION__);
+ return -EINVAL;
+ }
+ cx22702_writereg (state, 0x06, val);
+
+ val=0;
+ switch(p->u.ofdm.code_rate_HP) {
+ case FEC_NONE:
+ case FEC_1_2: val = (val&0xc7); break;
+ case FEC_2_3: val = (val&0xc7)|0x08; break;
+ case FEC_3_4: val = (val&0xc7)|0x10; break;
+ case FEC_5_6: val = (val&0xc7)|0x18; break;
+ case FEC_7_8: val = (val&0xc7)|0x20; break;
+ default:
+ dprintk ("%s: invalid code_rate_HP\n",__FUNCTION__);
+ return -EINVAL;
+ }
+ switch(p->u.ofdm.code_rate_LP) {
+ case FEC_NONE:
+ case FEC_1_2: val = (val&0xf8); break;
+ case FEC_2_3: val = (val&0xf8)|1; break;
+ case FEC_3_4: val = (val&0xf8)|2; break;
+ case FEC_5_6: val = (val&0xf8)|3; break;
+ case FEC_7_8: val = (val&0xf8)|4; break;
+ default:
+ dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__);
+ return -EINVAL;
+ }
+ cx22702_writereg (state, 0x07, val);
+
+ val=0;
+ switch(p->u.ofdm.guard_interval) {
+ case GUARD_INTERVAL_1_32: val = (val&0xf3); break;
+ case GUARD_INTERVAL_1_16: val = (val&0xf3)|0x04; break;
+ case GUARD_INTERVAL_1_8: val = (val&0xf3)|0x08; break;
+ case GUARD_INTERVAL_1_4: val = (val&0xf3)|0x0c; break;
+ default:
+ dprintk ("%s: invalid guard_interval\n",__FUNCTION__);
+ return -EINVAL;
+ }
+ switch(p->u.ofdm.transmission_mode) {
+ case TRANSMISSION_MODE_2K: val = (val&0xfc); break;
+ case TRANSMISSION_MODE_8K: val = (val&0xfc)|1; break;
+ default:
+ dprintk ("%s: invalid transmission_mode\n",__FUNCTION__);
+ return -EINVAL;
+ }
+ cx22702_writereg(state, 0x08, val);
+ cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 );
+ cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 );
+
+ /* Begin channel aquisition */
+ cx22702_writereg(state, 0x00, 0x01);
+
+ return 0;
+}
+
+/* Reset the demod hardware and reset all of the configuration registers
+ to a default state. */
+static int cx22702_init (struct dvb_frontend* fe)
+{
+ int i;
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+ cx22702_writereg (state, 0x00, 0x02);
+
+ msleep(10);
+
+ for (i=0; i<sizeof(init_tab); i+=2)
+ cx22702_writereg (state, init_tab[i], init_tab[i+1]);
+
+
+ /* init PLL */
+ if (state->config->pll_init) {
+ cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe);
+ state->config->pll_init(fe);
+ cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1);
+ }
+
+ return 0;
+}
+
+static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+ u8 reg0A;
+ u8 reg23;
+
+ *status = 0;
+
+ reg0A = cx22702_readreg (state, 0x0A);
+ reg23 = cx22702_readreg (state, 0x23);
+
+ dprintk ("%s: status demod=0x%02x agc=0x%02x\n"
+ ,__FUNCTION__,reg0A,reg23);
+
+ if(reg0A & 0x10) {
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ }
+
+ if(reg0A & 0x20)
+ *status |= FE_HAS_CARRIER;
+
+ if(reg23 < 0xf0)
+ *status |= FE_HAS_SIGNAL;
+
+ return 0;
+}
+
+static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+ if(cx22702_readreg (state, 0xE4) & 0x02) {
+ /* Realtime statistics */
+ *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+ | (cx22702_readreg (state, 0xDF)&0x7F);
+ } else {
+ /* Averagtine statistics */
+ *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+ | cx22702_readreg (state, 0xDF);
+ }
+
+ return 0;
+}
+
+static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+ *signal_strength = cx22702_readreg (state, 0x23);
+
+ return 0;
+}
+
+static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+ u16 rs_ber=0;
+ if(cx22702_readreg (state, 0xE4) & 0x02) {
+ /* Realtime statistics */
+ rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7
+ | (cx22702_readreg (state, 0xDF)& 0x7F);
+ } else {
+ /* Averagine statistics */
+ rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8
+ | cx22702_readreg (state, 0xDF);
+ }
+ *snr = ~rs_ber;
+
+ return 0;
+}
+
+static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+ u8 _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;
+ state->prevUCBlocks = _ucblocks;
+
+ return 0;
+}
+
+static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+
+ u8 reg0C = cx22702_readreg (state, 0x0C);
+
+ p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF;
+ return cx22702_get_tps (state, &p->u.ofdm);
+}
+
+static void cx22702_release(struct dvb_frontend* fe)
+{
+ struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops cx22702_ops;
+
+struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct cx22702_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct cx22702_state*) kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &cx22702_ops, sizeof(struct dvb_frontend_ops));
+ state->prevUCBlocks = 0;
+
+ /* check if the demod is there */
+ if (cx22702_readreg(state, 0x1f) != 0x3) 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 cx22702_ops = {
+
+ .info = {
+ .name = "Conexant CX22702 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 177000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166666,
+ .caps = 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_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER
+ },
+
+ .release = cx22702_release,
+
+ .init = cx22702_init,
+
+ .set_frontend = cx22702_set_tps,
+ .get_frontend = cx22702_get_frontend,
+
+ .read_status = cx22702_read_status,
+ .read_ber = cx22702_read_ber,
+ .read_signal_strength = cx22702_read_signal_strength,
+ .read_snr = cx22702_read_snr,
+ .read_ucblocks = cx22702_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx22702_attach);
diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h
new file mode 100644
index 000000000000..6e34f997aba2
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx22702.h
@@ -0,0 +1,46 @@
+/*
+ Conexant 22702 DVB OFDM demodulator driver
+
+ based on:
+ Alps TDMB7 DVB OFDM demodulator driver
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ Holger Waechtler <holger@convergence.de>
+
+ Copyright (C) 2004 Steven Toth <steve@toth.demon.co.uk>
+
+ 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 CX22702_H
+#define CX22702_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx22702_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);
+};
+
+extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // CX22702_H
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
new file mode 100644
index 000000000000..ae16112a0653
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -0,0 +1,657 @@
+/*
+ cx24110 - Single Chip Satellite Channel Receiver driver module
+
+ Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
+ work
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include "dvb_frontend.h"
+#include "cx24110.h"
+
+
+struct cx24110_state {
+
+ struct i2c_adapter* i2c;
+
+ struct dvb_frontend_ops ops;
+
+ const struct cx24110_config* config;
+
+ struct dvb_frontend frontend;
+
+ u32 lastber;
+ u32 lastbler;
+ u32 lastesn0;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "cx24110: " args); \
+ } while (0)
+
+static struct {u8 reg; u8 data;} cx24110_regdata[]=
+ /* Comments beginning with @ denote this value should
+ be the default */
+ {{0x09,0x01}, /* SoftResetAll */
+ {0x09,0x00}, /* release reset */
+ {0x01,0xe8}, /* MSB of code rate 27.5MS/s */
+ {0x02,0x17}, /* middle byte " */
+ {0x03,0x29}, /* LSB " */
+ {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */
+ {0x06,0xa5}, /* @ PLL 60MHz */
+ {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */
+ {0x0a,0x00}, /* @ partial chip disables, do not set */
+ {0x0b,0x01}, /* set output clock in gapped mode, start signal low
+ active for first byte */
+ {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */
+ {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */
+ {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1
+ to avoid starting the BER counter. Reset the
+ CRC test bit. Finite counting selected */
+ {0x15,0xff}, /* @ size of the limited time window for RS BER
+ estimation. It is <value>*256 RS blocks, this
+ gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */
+ {0x16,0x00}, /* @ enable all RS output ports */
+ {0x17,0x04}, /* @ time window allowed for the RS to sync */
+ {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned
+ for automatically */
+ /* leave the current code rate and normalization
+ registers as they are after reset... */
+ {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting
+ only once */
+ {0x23,0x18}, /* @ size of the limited time window for Viterbi BER
+ estimation. It is <value>*65536 channel bits, i.e.
+ approx. 38ms at 27.5MS/s, rate 3/4 */
+ {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */
+ /* leave front-end AGC parameters at default values */
+ /* leave decimation AGC parameters at default values */
+ {0x35,0x40}, /* disable all interrupts. They are not connected anyway */
+ {0x36,0xff}, /* clear all interrupt pending flags */
+ {0x37,0x00}, /* @ fully enable AutoAcqq state machine */
+ {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */
+ /* leave the equalizer parameters on their default values */
+ /* leave the final AGC parameters on their default values */
+ {0x41,0x00}, /* @ MSB of front-end derotator frequency */
+ {0x42,0x00}, /* @ middle bytes " */
+ {0x43,0x00}, /* @ LSB " */
+ /* leave the carrier tracking loop parameters on default */
+ /* leave the bit timing loop parameters at gefault */
+ {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */
+ /* the cx24108 data sheet for symbol rates above 15MS/s */
+ {0x57,0x00}, /* @ Filter sigma delta enabled, positive */
+ {0x61,0x95}, /* GPIO pins 1-4 have special function */
+ {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */
+ {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */
+ {0x64,0x20}, /* GPIO 6 is input, all others are outputs */
+ {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */
+ {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */
+ {0x73,0x00}, /* @ disable several demod bypasses */
+ {0x74,0x00}, /* @ " */
+ {0x75,0x00} /* @ " */
+ /* the remaining registers are for SEC */
+ };
+
+
+static int cx24110_writereg (struct cx24110_state* state, int reg, int 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 int cx24110_readreg (struct cx24110_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) return ret;
+
+ return b1[0];
+}
+
+static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion)
+{
+/* fixme (low): error handling */
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
+ /* AcqSpectrInvDis on. No idea why someone should want this */
+ cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7);
+ /* Initial value 0 at start of acq */
+ cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef);
+ /* current value 0 */
+ /* The cx24110 manual tells us this reg is read-only.
+ But what the heck... set it ayways */
+ break;
+ case INVERSION_ON:
+ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1);
+ /* AcqSpectrInvDis on. No idea why someone should want this */
+ cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08);
+ /* Initial value 1 at start of acq */
+ cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10);
+ /* current value 1 */
+ break;
+ case INVERSION_AUTO:
+ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe);
+ /* AcqSpectrInvDis off. Leave initial & current states as is */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec)
+{
+/* fixme (low): error handling */
+
+ static const int rate[]={-1,1,2,3,5,7,-1};
+ static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1};
+ static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1};
+
+ /* Well, the AutoAcq engine of the cx24106 and 24110 automatically
+ searches all enabled viterbi rates, and can handle non-standard
+ rates as well. */
+
+ if (fec>FEC_AUTO)
+ fec=FEC_AUTO;
+
+ if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */
+ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf);
+ /* clear AcqVitDis bit */
+ cx24110_writereg(state,0x18,0xae);
+ /* allow all DVB standard code rates */
+ cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3);
+ /* set nominal Viterbi rate 3/4 */
+ cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3);
+ /* set current Viterbi rate 3/4 */
+ cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06);
+ /* set the puncture registers for code rate 3/4 */
+ return 0;
+ } else {
+ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20);
+ /* set AcqVitDis bit */
+ if(rate[fec]>0) {
+ cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]);
+ /* set nominal Viterbi rate */
+ cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]);
+ /* set current Viterbi rate */
+ cx24110_writereg(state,0x1a,g1[fec]);
+ cx24110_writereg(state,0x1b,g2[fec]);
+ /* not sure if this is the right way: I always used AutoAcq mode */
+ } else
+ return -EOPNOTSUPP;
+/* fixme (low): which is the correct return code? */
+ };
+ return 0;
+}
+
+static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state)
+{
+ int i;
+
+ i=cx24110_readreg(state,0x22)&0x0f;
+ if(!(i&0x08)) {
+ return FEC_1_2 + i - 1;
+ } else {
+/* fixme (low): a special code rate has been selected. In theory, we need to
+ return a denominator value, a numerator value, and a pair of puncture
+ maps to correctly describe this mode. But this should never happen in
+ practice, because it cannot be set by cx24110_get_fec. */
+ return FEC_NONE;
+ }
+}
+
+static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
+{
+/* fixme (low): add error handling */
+ u32 ratio;
+ u32 tmp, fclk, BDRI;
+
+ static const u32 bands[]={5000000UL,15000000UL,90999000UL/2};
+ int i;
+
+dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate);
+ if (srate>90999000UL/2)
+ srate=90999000UL/2;
+ if (srate<500000)
+ srate=500000;
+
+ for(i=0;(i<sizeof(bands)/sizeof(bands[0]))&&(srate>bands[i]);i++)
+ ;
+ /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz,
+ and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult,
+ R06[3:0] PLLphaseDetGain */
+ tmp=cx24110_readreg(state,0x07)&0xfc;
+ if(srate<90999000UL/4) { /* sample rate 45MHz*/
+ cx24110_writereg(state,0x07,tmp);
+ cx24110_writereg(state,0x06,0x78);
+ fclk=90999000UL/2;
+ } else if(srate<60666000UL/2) { /* sample rate 60MHz */
+ cx24110_writereg(state,0x07,tmp|0x1);
+ cx24110_writereg(state,0x06,0xa5);
+ fclk=60666000UL;
+ } else if(srate<80888000UL/2) { /* sample rate 80MHz */
+ cx24110_writereg(state,0x07,tmp|0x2);
+ cx24110_writereg(state,0x06,0x87);
+ fclk=80888000UL;
+ } else { /* sample rate 90MHz */
+ cx24110_writereg(state,0x07,tmp|0x3);
+ cx24110_writereg(state,0x06,0x78);
+ fclk=90999000UL;
+ };
+ dprintk("cx24110 debug: fclk %d Hz\n",fclk);
+ /* we need to divide two integers with approx. 27 bits in 32 bit
+ arithmetic giving a 25 bit result */
+ /* the maximum dividend is 90999000/2, 0x02b6446c, this number is
+ also the most complex divisor. Hence, the dividend has,
+ assuming 32bit unsigned arithmetic, 6 clear bits on top, the
+ divisor 2 unused bits at the bottom. Also, the quotient is
+ always less than 1/2. Borrowed from VES1893.c, of course */
+
+ tmp=srate<<6;
+ BDRI=fclk>>2;
+ ratio=(tmp/BDRI);
+
+ tmp=(tmp%BDRI)<<8;
+ ratio=(ratio<<8)+(tmp/BDRI);
+
+ tmp=(tmp%BDRI)<<8;
+ ratio=(ratio<<8)+(tmp/BDRI);
+
+ tmp=(tmp%BDRI)<<1;
+ ratio=(ratio<<1)+(tmp/BDRI);
+
+ dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]);
+ dprintk("fclk = %d\n", fclk);
+ dprintk("ratio= %08x\n", ratio);
+
+ cx24110_writereg(state, 0x1, (ratio>>16)&0xff);
+ cx24110_writereg(state, 0x2, (ratio>>8)&0xff);
+ cx24110_writereg(state, 0x3, (ratio)&0xff);
+
+ return 0;
+
+}
+
+int cx24110_pll_write (struct dvb_frontend* fe, u32 data)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+/* 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 */
+
+ /* if the auto tuner writer is still busy, clear it out */
+ while (cx24110_readreg(state,0x6d)&0x80)
+ cx24110_writereg(state,0x72,0);
+
+ /* write the topmost 8 bits */
+ cx24110_writereg(state,0x72,(data>>24)&0xff);
+
+ /* 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);
+ while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+ ;
+
+ /* and the topmost 5 bits of this byte */
+ cx24110_writereg(state,0x72,(data>>8)&0xff);
+ while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
+ ;
+
+ /* now strobe the enable line once */
+ cx24110_writereg(state,0x6d,0x32);
+ cx24110_writereg(state,0x6d,0x30);
+
+ return 0;
+}
+
+static int cx24110_initfe(struct dvb_frontend* fe)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+/* fixme (low): error handling */
+ int i;
+
+ dprintk("%s: init chip\n", __FUNCTION__);
+
+ for(i=0;i<sizeof(cx24110_regdata)/sizeof(cx24110_regdata[0]);i++) {
+ cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data);
+ };
+
+ if (state->config->pll_init) state->config->pll_init(fe);
+
+ return 0;
+}
+
+static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0);
+ case SEC_VOLTAGE_18:
+ return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40);
+ default:
+ return -EINVAL;
+ };
+}
+
+static int cx24110_diseqc_send_burst(struct dvb_frontend* fe,
+ fe_sec_mini_cmd_t burst)
+{
+ int rv, bit, i;
+ struct cx24110_state *state = fe->demodulator_priv;
+
+ if (burst == SEC_MINI_A)
+ bit = 0x00;
+ else if (burst == SEC_MINI_B)
+ bit = 0x08;
+ else
+ return -EINVAL;
+
+ rv = cx24110_readreg(state, 0x77);
+ cx24110_writereg(state, 0x77, rv|0x04);
+
+ rv = cx24110_readreg(state, 0x76);
+ cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit));
+ for (i = 500; i-- > 0 && !(cx24110_readreg(state,0x76)&0x40) ; )
+ ; /* wait for LNB ready */
+
+ return 0;
+}
+
+static int cx24110_send_diseqc_msg(struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd *cmd)
+{
+ int i, rv;
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+ for (i = 0; i < cmd->msg_len; i++)
+ cx24110_writereg(state, 0x79 + i, cmd->msg[i]);
+
+ rv = cx24110_readreg(state, 0x77);
+ cx24110_writereg(state, 0x77, rv|0x04);
+
+ rv = cx24110_readreg(state, 0x76);
+
+ cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3));
+ for (i=500; i-- > 0 && !(cx24110_readreg(state,0x76)&0x40);)
+ ; /* wait for LNB ready */
+
+ return 0;
+}
+
+static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+ int sync = cx24110_readreg (state, 0x55);
+
+ *status = 0;
+
+ if (sync & 0x10)
+ *status |= FE_HAS_SIGNAL;
+
+ if (sync & 0x08)
+ *status |= FE_HAS_CARRIER;
+
+ sync = cx24110_readreg (state, 0x08);
+
+ if (sync & 0x40)
+ *status |= FE_HAS_VITERBI;
+
+ if (sync & 0x20)
+ *status |= FE_HAS_SYNC;
+
+ if ((sync & 0x60) == 0x60)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+ /* fixme (maybe): value range is 16 bit. Scale? */
+ if(cx24110_readreg(state,0x24)&0x10) {
+ /* the Viterbi error counter has finished one counting window */
+ cx24110_writereg(state,0x24,0x04); /* select the ber reg */
+ state->lastber=cx24110_readreg(state,0x25)|
+ (cx24110_readreg(state,0x26)<<8);
+ cx24110_writereg(state,0x24,0x04); /* start new count window */
+ cx24110_writereg(state,0x24,0x14);
+ }
+ *ber = state->lastber;
+
+ return 0;
+}
+
+static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+/* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */
+ u8 signal = cx24110_readreg (state, 0x27)+128;
+ *signal_strength = (signal << 8) | signal;
+
+ return 0;
+}
+
+static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+ /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */
+ if(cx24110_readreg(state,0x6a)&0x80) {
+ /* the Es/N0 error counter has finished one counting window */
+ state->lastesn0=cx24110_readreg(state,0x69)|
+ (cx24110_readreg(state,0x68)<<8);
+ cx24110_writereg(state,0x6a,0x84); /* start new count window */
+ }
+ *snr = state->lastesn0;
+
+ return 0;
+}
+
+static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+ u32 lastbyer;
+
+ if(cx24110_readreg(state,0x10)&0x40) {
+ /* the RS error counter has finished one counting window */
+ cx24110_writereg(state,0x10,0x60); /* select the byer reg */
+ lastbyer=cx24110_readreg(state,0x12)|
+ (cx24110_readreg(state,0x13)<<8)|
+ (cx24110_readreg(state,0x14)<<16);
+ cx24110_writereg(state,0x10,0x70); /* select the bler reg */
+ state->lastbler=cx24110_readreg(state,0x12)|
+ (cx24110_readreg(state,0x13)<<8)|
+ (cx24110_readreg(state,0x14)<<16);
+ cx24110_writereg(state,0x10,0x20); /* start new count window */
+ }
+ *ucblocks = state->lastbler;
+
+ return 0;
+}
+
+static int cx24110_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+ state->config->pll_set(fe, p);
+ cx24110_set_inversion (state, p->inversion);
+ cx24110_set_fec (state, p->u.qpsk.fec_inner);
+ cx24110_set_symbolrate (state, p->u.qpsk.symbol_rate);
+ cx24110_writereg(state,0x04,0x05); /* start aquisition */
+
+ return 0;
+}
+
+static int cx24110_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+ s32 afc; unsigned sclk;
+
+/* cannot read back tuner settings (freq). Need to have some private storage */
+
+ sclk = cx24110_readreg (state, 0x07) & 0x03;
+/* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz.
+ * Need 64 bit arithmetic. Is thiss possible in the kernel? */
+ if (sclk==0) sclk=90999000L/2L;
+ else if (sclk==1) sclk=60666000L;
+ else if (sclk==2) sclk=80888000L;
+ else sclk=90999000L;
+ sclk>>=8;
+ afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+
+ ((sclk*cx24110_readreg (state, 0x45))>>8)+
+ ((sclk*cx24110_readreg (state, 0x46))>>16);
+
+ p->frequency += afc;
+ p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ?
+ INVERSION_ON : INVERSION_OFF;
+ p->u.qpsk.fec_inner = cx24110_get_fec (state);
+
+ return 0;
+}
+
+static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv;
+
+ return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0));
+}
+
+static void cx24110_release(struct dvb_frontend* fe)
+{
+ struct cx24110_state* state = (struct cx24110_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops cx24110_ops;
+
+struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct cx24110_state* state = NULL;
+ int ret;
+
+ /* allocate memory for the internal state */
+ state = (struct cx24110_state*) kmalloc(sizeof(struct cx24110_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &cx24110_ops, sizeof(struct dvb_frontend_ops));
+ state->lastber = 0;
+ state->lastbler = 0;
+ state->lastesn0 = 0;
+
+ /* check if the demod is there */
+ ret = cx24110_readreg(state, 0x00);
+ if ((ret != 0x5a) && (ret != 0x69)) 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 cx24110_ops = {
+
+ .info = {
+ .name = "Conexant CX24110 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1011, /* kHz for QPSK frontends */
+ .frequency_tolerance = 29500,
+ .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_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_RECOVER
+ },
+
+ .release = cx24110_release,
+
+ .init = cx24110_initfe,
+ .set_frontend = cx24110_set_frontend,
+ .get_frontend = cx24110_get_frontend,
+ .read_status = cx24110_read_status,
+ .read_ber = cx24110_read_ber,
+ .read_signal_strength = cx24110_read_signal_strength,
+ .read_snr = cx24110_read_snr,
+ .read_ucblocks = cx24110_read_ucblocks,
+
+ .diseqc_send_master_cmd = cx24110_send_diseqc_msg,
+ .set_tone = cx24110_set_tone,
+ .set_voltage = cx24110_set_voltage,
+ .diseqc_send_burst = cx24110_diseqc_send_burst,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver");
+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
new file mode 100644
index 000000000000..6b663f4744e0
--- /dev/null
+++ b/drivers/media/dvb/frontends/cx24110.h
@@ -0,0 +1,45 @@
+/*
+ cx24110 - Single Chip Satellite Channel Receiver driver module
+
+ Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@t-online.de> based on
+ work
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef CX24110_H
+#define CX24110_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24110_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);
+};
+
+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);
+
+#endif // CX24110_H
diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c
new file mode 100644
index 000000000000..47ab02e133d1
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000-common.c
@@ -0,0 +1,83 @@
+#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
new file mode 100644
index 000000000000..c31d6df15472
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000-common.h
@@ -0,0 +1,137 @@
+/*
+ * .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;
+
+ struct dvb_frontend_ops ops;
+
+/* 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
new file mode 100644
index 000000000000..80687c130836
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000.h
@@ -0,0 +1,54 @@
+/*
+ * public header file of the frontend drivers for mobile DVB-T demodulators
+ * DiBcom 3000M-B and DiBcom 3000P/M-C (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_H
+#define DIB3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct dib3000_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* PLL maintenance and the i2c address of the PLL */
+ u8 (*pll_addr)(struct dvb_frontend *fe);
+ int (*pll_init)(struct dvb_frontend *fe, u8 pll_buf[5]);
+ int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params, u8 pll_buf[5]);
+};
+
+struct dib_fe_xfer_ops
+{
+ /* pid and transfer handling is done in the demodulator */
+ int (*pid_parse)(struct dvb_frontend *fe, int onoff);
+ int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff);
+ int (*pid_ctrl)(struct dvb_frontend *fe, int index, int pid, int onoff);
+ int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl);
+};
+
+extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
+
+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
new file mode 100644
index 000000000000..a853d12a26f1
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mb.c
@@ -0,0 +1,784 @@
+/*
+ * Frontend driver for mobile DVB-T demodulator DiBcom 3000M-B
+ * 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
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dib3000-common.h"
+#include "dib3000mb_priv.h"
+#include "dib3000.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.1"
+#define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator"
+#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+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_xfer(args...) dprintk(0x02,args)
+#define deb_setf(args...) dprintk(0x04,args)
+#define deb_getf(args...) dprintk(0x08,args)
+
+static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr);
+
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep);
+
+static int dib3000mb_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep, int tuner)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+ struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+ fe_code_rate_t fe_cr = FEC_NONE;
+ int search_state, seq;
+
+ if (tuner) {
+ dib3000mb_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+ state->config.pll_set(fe, fep, NULL);
+ dib3000mb_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+
+ deb_setf("bandwidth: ");
+ switch (ofdm->bandwidth) {
+ case BANDWIDTH_8_MHZ:
+ deb_setf("8 MHz\n");
+ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]);
+ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz);
+ break;
+ case BANDWIDTH_7_MHZ:
+ deb_setf("7 MHz\n");
+ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[1]);
+ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_7mhz);
+ break;
+ case BANDWIDTH_6_MHZ:
+ deb_setf("6 MHz\n");
+ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[0]);
+ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_6mhz);
+ break;
+ case BANDWIDTH_AUTO:
+ return -EOPNOTSUPP;
+ default:
+ err("unkown bandwidth value.");
+ return -EINVAL;
+ }
+ }
+ wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4);
+
+ deb_setf("transmission mode: ");
+ switch (ofdm->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ deb_setf("2k\n");
+ wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K);
+ break;
+ case TRANSMISSION_MODE_8K:
+ deb_setf("8k\n");
+ wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K);
+ break;
+ case TRANSMISSION_MODE_AUTO:
+ deb_setf("auto\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ deb_setf("guard: ");
+ switch (ofdm->guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ deb_setf("1_32\n");
+ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32);
+ break;
+ case GUARD_INTERVAL_1_16:
+ deb_setf("1_16\n");
+ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16);
+ break;
+ case GUARD_INTERVAL_1_8:
+ deb_setf("1_8\n");
+ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8);
+ break;
+ case GUARD_INTERVAL_1_4:
+ deb_setf("1_4\n");
+ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4);
+ break;
+ case GUARD_INTERVAL_AUTO:
+ deb_setf("auto\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ deb_setf("inversion: ");
+ switch (fep->inversion) {
+ case INVERSION_OFF:
+ deb_setf("off\n");
+ wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF);
+ break;
+ case INVERSION_AUTO:
+ deb_setf("auto ");
+ break;
+ case INVERSION_ON:
+ deb_setf("on\n");
+ wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ deb_setf("constellation: ");
+ switch (ofdm->constellation) {
+ case QPSK:
+ deb_setf("qpsk\n");
+ wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK);
+ break;
+ case QAM_16:
+ deb_setf("qam16\n");
+ wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM);
+ break;
+ case QAM_64:
+ deb_setf("qam64\n");
+ wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM);
+ break;
+ case QAM_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+ deb_setf("hierachy: ");
+ switch (ofdm->hierarchy_information) {
+ case HIERARCHY_NONE:
+ deb_setf("none ");
+ /* fall through */
+ case HIERARCHY_1:
+ deb_setf("alpha=1\n");
+ wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1);
+ break;
+ case HIERARCHY_2:
+ deb_setf("alpha=2\n");
+ wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2);
+ break;
+ case HIERARCHY_4:
+ deb_setf("alpha=4\n");
+ wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4);
+ break;
+ case HIERARCHY_AUTO:
+ deb_setf("alpha=auto\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ deb_setf("hierarchy: ");
+ if (ofdm->hierarchy_information == HIERARCHY_NONE) {
+ deb_setf("none\n");
+ wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF);
+ wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP);
+ fe_cr = ofdm->code_rate_HP;
+ } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
+ deb_setf("on\n");
+ wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON);
+ wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP);
+ fe_cr = ofdm->code_rate_LP;
+ }
+ deb_setf("fec: ");
+ switch (fe_cr) {
+ case FEC_1_2:
+ deb_setf("1_2\n");
+ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2);
+ break;
+ case FEC_2_3:
+ deb_setf("2_3\n");
+ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3);
+ break;
+ case FEC_3_4:
+ deb_setf("3_4\n");
+ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4);
+ break;
+ case FEC_5_6:
+ deb_setf("5_6\n");
+ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6);
+ break;
+ case FEC_7_8:
+ deb_setf("7_8\n");
+ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8);
+ break;
+ case FEC_NONE:
+ deb_setf("none ");
+ break;
+ case FEC_AUTO:
+ deb_setf("auto\n");
+ break;
+ 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(DIB3000MB_REG_SEQ, seq);
+
+ wr(DIB3000MB_REG_ISI, seq ? DIB3000MB_ISI_INHIBIT : DIB3000MB_ISI_ACTIVATE);
+
+ if (ofdm->transmission_mode == TRANSMISSION_MODE_2K) {
+ if (ofdm->guard_interval == GUARD_INTERVAL_1_8) {
+ wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_2K_1_8);
+ } else {
+ wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_DEFAULT);
+ }
+
+ wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_2K);
+ } else {
+ wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_DEFAULT);
+ }
+
+ wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_OFF);
+ wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF);
+ wr(DIB3000MB_REG_MOBILE_MODE, DIB3000MB_MOBILE_MODE_OFF);
+
+ wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_high);
+
+ wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_ACTIVATE);
+
+ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC + DIB3000MB_RESTART_CTRL);
+ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF);
+
+ /* wait for AGC lock */
+ msleep(70);
+
+ wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low);
+
+ /* something has to be auto searched */
+ if (ofdm->constellation == QAM_AUTO ||
+ ofdm->hierarchy_information == HIERARCHY_AUTO ||
+ fe_cr == FEC_AUTO ||
+ fep->inversion == INVERSION_AUTO) {
+ int as_count=0;
+
+ deb_setf("autosearch enabled.\n");
+
+ wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT);
+
+ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AUTO_SEARCH);
+ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF);
+
+ while ((search_state =
+ dib3000_search_status(
+ rd(DIB3000MB_REG_AS_IRQ_PENDING),
+ rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100)
+ msleep(1);
+
+ deb_setf("search_state after autosearch %d after %d checks\n",search_state,as_count);
+
+ if (search_state == 1) {
+ struct dvb_frontend_parameters feps;
+ if (dib3000mb_get_frontend(fe, &feps) == 0) {
+ deb_setf("reading tuning data from frontend succeeded.\n");
+ return dib3000mb_set_frontend(fe, &feps, 0);
+ }
+ }
+
+ } else {
+ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_CTRL);
+ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF);
+ }
+
+ return 0;
+}
+
+static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+ deb_info("dib3000mb is getting up.\n");
+ wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_UP);
+
+ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC);
+
+ wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE);
+ wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE_RST);
+
+ wr(DIB3000MB_REG_CLOCK, DIB3000MB_CLOCK_DEFAULT);
+
+ wr(DIB3000MB_REG_ELECT_OUT_MODE, DIB3000MB_ELECT_OUT_MODE_ON);
+
+ wr(DIB3000MB_REG_DDS_FREQ_MSB, DIB3000MB_DDS_FREQ_MSB);
+ wr(DIB3000MB_REG_DDS_FREQ_LSB, DIB3000MB_DDS_FREQ_LSB);
+
+ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]);
+
+ wr_foreach(dib3000mb_reg_impulse_noise,
+ dib3000mb_impulse_noise_values[DIB3000MB_IMPNOISE_OFF]);
+
+ wr_foreach(dib3000mb_reg_agc_gain, dib3000mb_default_agc_gain);
+
+ wr(DIB3000MB_REG_PHASE_NOISE, DIB3000MB_PHASE_NOISE_DEFAULT);
+
+ wr_foreach(dib3000mb_reg_phase_noise, dib3000mb_default_noise_phase);
+
+ wr_foreach(dib3000mb_reg_lock_duration, dib3000mb_default_lock_duration);
+
+ wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low);
+
+ wr(DIB3000MB_REG_LOCK0_MASK, DIB3000MB_LOCK0_DEFAULT);
+ wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4);
+ wr(DIB3000MB_REG_LOCK2_MASK, DIB3000MB_LOCK2_DEFAULT);
+ wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]);
+
+ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz);
+
+ wr(DIB3000MB_REG_UNK_68, DIB3000MB_UNK_68);
+ wr(DIB3000MB_REG_UNK_69, DIB3000MB_UNK_69);
+ wr(DIB3000MB_REG_UNK_71, DIB3000MB_UNK_71);
+ wr(DIB3000MB_REG_UNK_77, DIB3000MB_UNK_77);
+ wr(DIB3000MB_REG_UNK_78, DIB3000MB_UNK_78);
+ wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT);
+ wr(DIB3000MB_REG_UNK_92, DIB3000MB_UNK_92);
+ wr(DIB3000MB_REG_UNK_96, DIB3000MB_UNK_96);
+ wr(DIB3000MB_REG_UNK_97, DIB3000MB_UNK_97);
+ wr(DIB3000MB_REG_UNK_106, DIB3000MB_UNK_106);
+ wr(DIB3000MB_REG_UNK_107, DIB3000MB_UNK_107);
+ wr(DIB3000MB_REG_UNK_108, DIB3000MB_UNK_108);
+ wr(DIB3000MB_REG_UNK_122, DIB3000MB_UNK_122);
+ wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF);
+ wr(DIB3000MB_REG_BERLEN, DIB3000MB_BERLEN_DEFAULT);
+
+ wr_foreach(dib3000mb_reg_filter_coeffs, dib3000mb_filter_coeffs);
+
+ wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_ON);
+ wr(DIB3000MB_REG_MULTI_DEMOD_MSB, DIB3000MB_MULTI_DEMOD_MSB);
+ wr(DIB3000MB_REG_MULTI_DEMOD_LSB, DIB3000MB_MULTI_DEMOD_LSB);
+
+ wr(DIB3000MB_REG_OUTPUT_MODE, DIB3000MB_OUTPUT_MODE_SLAVE);
+
+ wr(DIB3000MB_REG_FIFO_142, DIB3000MB_FIFO_142);
+ wr(DIB3000MB_REG_MPEG2_OUT_MODE, DIB3000MB_MPEG2_OUT_MODE_188);
+ wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE);
+ wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT);
+ wr(DIB3000MB_REG_FIFO_146, DIB3000MB_FIFO_146);
+ wr(DIB3000MB_REG_FIFO_147, DIB3000MB_FIFO_147);
+
+ wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF);
+
+ if (state->config.pll_init) {
+ dib3000mb_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+ state->config.pll_init(fe,NULL);
+ dib3000mb_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+ }
+
+ return 0;
+}
+
+static int dib3000mb_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+ struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+ fe_code_rate_t *cr;
+ u16 tps_val;
+ int inv_test1,inv_test2;
+ u32 dds_val, threshold = 0x800000;
+
+ if (!rd(DIB3000MB_REG_TPS_LOCK))
+ return 0;
+
+ dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB);
+ deb_getf("DDS_VAL: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_VALUE_MSB), rd(DIB3000MB_REG_DDS_VALUE_LSB));
+ if (dds_val < threshold)
+ inv_test1 = 0;
+ else if (dds_val == threshold)
+ inv_test1 = 1;
+ else
+ inv_test1 = 2;
+
+ dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB);
+ deb_getf("DDS_FREQ: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_FREQ_MSB), rd(DIB3000MB_REG_DDS_FREQ_LSB));
+ if (dds_val < threshold)
+ inv_test2 = 0;
+ else if (dds_val == threshold)
+ inv_test2 = 1;
+ else
+ inv_test2 = 2;
+
+ fep->inversion =
+ ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
+ ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
+ INVERSION_ON : INVERSION_OFF;
+
+ deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+
+ switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) {
+ case DIB3000_CONSTELLATION_QPSK:
+ deb_getf("QPSK ");
+ ofdm->constellation = QPSK;
+ break;
+ case DIB3000_CONSTELLATION_16QAM:
+ deb_getf("QAM16 ");
+ ofdm->constellation = QAM_16;
+ break;
+ case DIB3000_CONSTELLATION_64QAM:
+ deb_getf("QAM64 ");
+ ofdm->constellation = QAM_64;
+ break;
+ default:
+ err("Unexpected constellation returned by TPS (%d)", tps_val);
+ break;
+ }
+ deb_getf("TPS: %d\n", tps_val);
+
+ if (rd(DIB3000MB_REG_TPS_HRCH)) {
+ deb_getf("HRCH ON\n");
+ cr = &ofdm->code_rate_LP;
+ ofdm->code_rate_HP = FEC_NONE;
+ switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) {
+ 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;
+ }
+ deb_getf("TPS: %d\n", tps_val);
+
+ tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP);
+ } else {
+ deb_getf("HRCH OFF\n");
+ cr = &ofdm->code_rate_HP;
+ ofdm->code_rate_LP = FEC_NONE;
+ ofdm->hierarchy_information = HIERARCHY_NONE;
+
+ tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP);
+ }
+
+ switch (tps_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;
+ }
+ deb_getf("TPS: %d\n",tps_val);
+
+ switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) {
+ 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;
+ }
+ deb_getf("TPS: %d\n", tps_val);
+
+ switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) {
+ 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("TPS: %d\n", tps_val);
+
+ return 0;
+}
+
+static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+ *stat = 0;
+
+ if (rd(DIB3000MB_REG_AGC_LOCK))
+ *stat |= FE_HAS_SIGNAL;
+ if (rd(DIB3000MB_REG_CARRIER_LOCK))
+ *stat |= FE_HAS_CARRIER;
+ if (rd(DIB3000MB_REG_VIT_LCK))
+ *stat |= FE_HAS_VITERBI;
+ if (rd(DIB3000MB_REG_TS_SYNC_LOCK))
+ *stat |= (FE_HAS_SYNC | FE_HAS_LOCK);
+
+ deb_getf("actual status is %2x\n",*stat);
+
+ deb_getf("autoval: tps: %d, qam: %d, hrch: %d, alpha: %d, hp: %d, lp: %d, guard: %d, fft: %d cell: %d\n",
+ rd(DIB3000MB_REG_TPS_LOCK),
+ rd(DIB3000MB_REG_TPS_QAM),
+ rd(DIB3000MB_REG_TPS_HRCH),
+ rd(DIB3000MB_REG_TPS_VIT_ALPHA),
+ rd(DIB3000MB_REG_TPS_CODE_RATE_HP),
+ rd(DIB3000MB_REG_TPS_CODE_RATE_LP),
+ rd(DIB3000MB_REG_TPS_GUARD_TIME),
+ rd(DIB3000MB_REG_TPS_FFT),
+ rd(DIB3000MB_REG_TPS_CELL_ID));
+
+ //*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ return 0;
+}
+
+static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+ *ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB));
+ return 0;
+}
+
+/* see dib3000-watch dvb-apps for exact calcuations of signal_strength and snr */
+static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+ *strength = rd(DIB3000MB_REG_SIGNAL_POWER) * 0xffff / 0x170;
+ return 0;
+}
+
+static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+ short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER);
+ int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) |
+ rd(DIB3000MB_REG_NOISE_POWER_LSB);
+ *snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1);
+ return 0;
+}
+
+static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+ *unc = rd(DIB3000MB_REG_UNC);
+ return 0;
+}
+
+static int dib3000mb_sleep(struct dvb_frontend* fe)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+ deb_info("dib3000mb is going to bed.\n");
+ wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_DOWN);
+ return 0;
+}
+
+static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 800;
+ tune->step_size = 166667;
+ tune->max_drift = 166667 * 2;
+
+ return 0;
+}
+
+static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe)
+{
+ return dib3000mb_fe_init(fe, 0);
+}
+
+static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+{
+ return dib3000mb_set_frontend(fe, fep, 1);
+}
+
+static void dib3000mb_release(struct dvb_frontend* fe)
+{
+ struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+/* pid filter and transfer stuff */
+static int dib3000mb_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+{
+ struct dib3000_state *state = fe->demodulator_priv;
+ pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
+ wr(index+DIB3000MB_REG_FIRST_PID,pid);
+ return 0;
+}
+
+static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+ struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+
+ deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling");
+ if (onoff) {
+ wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE);
+ } else {
+ wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT);
+ }
+ return 0;
+}
+
+static int dib3000mb_pid_parse(struct dvb_frontend *fe, int onoff)
+{
+ struct dib3000_state *state = fe->demodulator_priv;
+ deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling");
+ wr(DIB3000MB_REG_PID_PARSE,onoff);
+ return 0;
+}
+
+static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr)
+{
+ struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+ if (onoff) {
+ wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr));
+ } else {
+ wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr));
+ }
+ return 0;
+}
+
+static struct dvb_frontend_ops dib3000mb_ops;
+
+struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+{
+ struct dib3000_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+ memset(state,0,sizeof(struct dib3000_state));
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config,config,sizeof(struct dib3000_config));
+ memcpy(&state->ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops));
+
+ /* check for the correct demod */
+ if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+ goto error;
+
+ if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID)
+ goto error;
+
+ /* create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+
+ /* set the xfer operations */
+ xfer_ops->pid_parse = dib3000mb_pid_parse;
+ xfer_ops->fifo_ctrl = dib3000mb_fifo_control;
+ xfer_ops->pid_ctrl = dib3000mb_pid_control;
+ xfer_ops->tuner_pass_ctrl = dib3000mb_tuner_pass_ctrl;
+
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops dib3000mb_ops = {
+
+ .info = {
+ .name = "DiBcom 3000M-B DVB-T",
+ .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 = dib3000mb_release,
+
+ .init = dib3000mb_fe_init_nonmobile,
+ .sleep = dib3000mb_sleep,
+
+ .set_frontend = dib3000mb_set_frontend_and_tuner,
+ .get_frontend = dib3000mb_get_frontend,
+ .get_tune_settings = dib3000mb_fe_get_tune_settings,
+
+ .read_status = dib3000mb_read_status,
+ .read_ber = dib3000mb_read_ber,
+ .read_signal_strength = dib3000mb_read_signal_strength,
+ .read_snr = dib3000mb_read_snr,
+ .read_ucblocks = dib3000mb_read_unc_blocks,
+};
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000mb_attach);
diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h
new file mode 100644
index 000000000000..57e61aa5b07b
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mb_priv.h
@@ -0,0 +1,467 @@
+/*
+ * dib3000mb_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 dib3000mb.c .
+ */
+
+#ifndef __DIB3000MB_PRIV_H_INCLUDED__
+#define __DIB3000MB_PRIV_H_INCLUDED__
+
+/* register addresses and some of their default values */
+
+/* restart subsystems */
+#define DIB3000MB_REG_RESTART ( 0)
+
+#define DIB3000MB_RESTART_OFF ( 0)
+#define DIB3000MB_RESTART_AUTO_SEARCH (1 << 1)
+#define DIB3000MB_RESTART_CTRL (1 << 2)
+#define DIB3000MB_RESTART_AGC (1 << 3)
+
+/* FFT size */
+#define DIB3000MB_REG_FFT ( 1)
+
+/* Guard time */
+#define DIB3000MB_REG_GUARD_TIME ( 2)
+
+/* QAM */
+#define DIB3000MB_REG_QAM ( 3)
+
+/* Alpha coefficient high priority Viterbi algorithm */
+#define DIB3000MB_REG_VIT_ALPHA ( 4)
+
+/* spectrum inversion */
+#define DIB3000MB_REG_DDS_INV ( 5)
+
+/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */
+#define DIB3000MB_REG_DDS_FREQ_MSB ( 6)
+#define DIB3000MB_REG_DDS_FREQ_LSB ( 7)
+#define DIB3000MB_DDS_FREQ_MSB ( 178)
+#define DIB3000MB_DDS_FREQ_LSB ( 8990)
+
+/* timing frequency (carrier spacing) */
+static u16 dib3000mb_reg_timing_freq[] = { 8,9 };
+static u16 dib3000mb_timing_freq[][2] = {
+ { 126 , 48873 }, /* 6 MHz */
+ { 147 , 57019 }, /* 7 MHz */
+ { 168 , 65164 }, /* 8 MHz */
+};
+
+/* impulse noise parameter */
+/* 36 ??? */
+
+static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 };
+
+enum dib3000mb_impulse_noise_type {
+ DIB3000MB_IMPNOISE_OFF,
+ DIB3000MB_IMPNOISE_MOBILE,
+ DIB3000MB_IMPNOISE_FIXED,
+ DIB3000MB_IMPNOISE_DEFAULT
+};
+
+static u16 dib3000mb_impulse_noise_values[][5] = {
+ { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */
+ { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */
+ { 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */
+ { 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */
+};
+
+/*
+ * Dual Automatic-Gain-Control
+ * - gains RF in tuner (AGC1)
+ * - gains IF after filtering (AGC2)
+ */
+
+/* also from 16 to 18 */
+static u16 dib3000mb_reg_agc_gain[] = {
+ 19,20,21,22,23,24,25,26,27,28,29,30,31,32
+};
+
+static u16 dib3000mb_default_agc_gain[] =
+ { 0x0001, 52429, 623, 128, 166, 195, 61, /* RF ??? */
+ 0x0001, 53766, 38011, 0, 90, 33, 23 }; /* IF ??? */
+
+/* phase noise */
+/* 36 is set when setting the impulse noise */
+static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 };
+
+static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 };
+
+/* lock duration */
+static u16 dib3000mb_reg_lock_duration[] = { 39,40 };
+static u16 dib3000mb_default_lock_duration[] = { 135, 135 };
+
+/* AGC loop bandwidth */
+static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 };
+
+static u16 dib3000mb_agc_bandwidth_low[] =
+ { 2088, 10, 2088, 10, 3448, 5, 3448, 5 };
+static u16 dib3000mb_agc_bandwidth_high[] =
+ { 2349, 5, 2349, 5, 2586, 2, 2586, 2 };
+
+/*
+ * lock0 definition (coff_lock)
+ */
+#define DIB3000MB_REG_LOCK0_MASK ( 51)
+#define DIB3000MB_LOCK0_DEFAULT ( 4)
+
+/*
+ * lock1 definition (cpil_lock)
+ * for auto search
+ * which values hide behind the lock masks
+ */
+#define DIB3000MB_REG_LOCK1_MASK ( 52)
+#define DIB3000MB_LOCK1_SEARCH_4 (0x0004)
+#define DIB3000MB_LOCK1_SEARCH_2048 (0x0800)
+#define DIB3000MB_LOCK1_DEFAULT (0x0001)
+
+/*
+ * lock2 definition (fec_lock) */
+#define DIB3000MB_REG_LOCK2_MASK ( 53)
+#define DIB3000MB_LOCK2_DEFAULT (0x0080)
+
+/*
+ * SEQ ? what was that again ... :)
+ * changes when, inversion, guard time and fft is
+ * either automatically detected or not
+ */
+#define DIB3000MB_REG_SEQ ( 54)
+
+/* bandwidth */
+static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 };
+static u16 dib3000mb_bandwidth_6mhz[] =
+ { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_7mhz[] =
+ { 0, 28, 64421, 96, 39973, 483, 3255, 0, 1000, 0, 1010, 1, 45264 };
+
+static u16 dib3000mb_bandwidth_8mhz[] =
+ { 0, 25, 23600, 84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 };
+
+#define DIB3000MB_REG_UNK_68 ( 68)
+#define DIB3000MB_UNK_68 ( 0)
+
+#define DIB3000MB_REG_UNK_69 ( 69)
+#define DIB3000MB_UNK_69 ( 0)
+
+#define DIB3000MB_REG_UNK_71 ( 71)
+#define DIB3000MB_UNK_71 ( 0)
+
+#define DIB3000MB_REG_UNK_77 ( 77)
+#define DIB3000MB_UNK_77 ( 6)
+
+#define DIB3000MB_REG_UNK_78 ( 78)
+#define DIB3000MB_UNK_78 (0x0080)
+
+/* isi */
+#define DIB3000MB_REG_ISI ( 79)
+#define DIB3000MB_ISI_ACTIVATE ( 0)
+#define DIB3000MB_ISI_INHIBIT ( 1)
+
+/* sync impovement */
+#define DIB3000MB_REG_SYNC_IMPROVEMENT ( 84)
+#define DIB3000MB_SYNC_IMPROVE_2K_1_8 ( 3)
+#define DIB3000MB_SYNC_IMPROVE_DEFAULT ( 0)
+
+/* phase noise compensation inhibition */
+#define DIB3000MB_REG_PHASE_NOISE ( 87)
+#define DIB3000MB_PHASE_NOISE_DEFAULT ( 0)
+
+#define DIB3000MB_REG_UNK_92 ( 92)
+#define DIB3000MB_UNK_92 (0x0080)
+
+#define DIB3000MB_REG_UNK_96 ( 96)
+#define DIB3000MB_UNK_96 (0x0010)
+
+#define DIB3000MB_REG_UNK_97 ( 97)
+#define DIB3000MB_UNK_97 (0x0009)
+
+/* mobile mode ??? */
+#define DIB3000MB_REG_MOBILE_MODE ( 101)
+#define DIB3000MB_MOBILE_MODE_ON ( 1)
+#define DIB3000MB_MOBILE_MODE_OFF ( 0)
+
+#define DIB3000MB_REG_UNK_106 ( 106)
+#define DIB3000MB_UNK_106 (0x0080)
+
+#define DIB3000MB_REG_UNK_107 ( 107)
+#define DIB3000MB_UNK_107 (0x0080)
+
+#define DIB3000MB_REG_UNK_108 ( 108)
+#define DIB3000MB_UNK_108 (0x0080)
+
+/* fft */
+#define DIB3000MB_REG_UNK_121 ( 121)
+#define DIB3000MB_UNK_121_2K ( 7)
+#define DIB3000MB_UNK_121_DEFAULT ( 5)
+
+#define DIB3000MB_REG_UNK_122 ( 122)
+#define DIB3000MB_UNK_122 ( 2867)
+
+/* QAM for mobile mode */
+#define DIB3000MB_REG_MOBILE_MODE_QAM ( 126)
+#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3)
+#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16 ( 1)
+#define DIB3000MB_MOBILE_MODE_QAM_OFF ( 0)
+
+/*
+ * data diversity when having more than one chip on-board
+ * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY
+ */
+#define DIB3000MB_REG_DATA_IN_DIVERSITY ( 127)
+#define DIB3000MB_DATA_DIVERSITY_IN_OFF ( 0)
+#define DIB3000MB_DATA_DIVERSITY_IN_ON ( 2)
+
+/* vit hrch */
+#define DIB3000MB_REG_VIT_HRCH ( 128)
+
+/* vit code rate */
+#define DIB3000MB_REG_VIT_CODE_RATE ( 129)
+
+/* vit select hp */
+#define DIB3000MB_REG_VIT_HP ( 130)
+
+/* time frame for Bit-Error-Rate calculation */
+#define DIB3000MB_REG_BERLEN ( 135)
+#define DIB3000MB_BERLEN_LONG ( 0)
+#define DIB3000MB_BERLEN_DEFAULT ( 1)
+#define DIB3000MB_BERLEN_MEDIUM ( 2)
+#define DIB3000MB_BERLEN_SHORT ( 3)
+
+/* 142 - 152 FIFO parameters
+ * which is what ?
+ */
+
+#define DIB3000MB_REG_FIFO_142 ( 142)
+#define DIB3000MB_FIFO_142 ( 0)
+
+/* MPEG2 TS output mode */
+#define DIB3000MB_REG_MPEG2_OUT_MODE ( 143)
+#define DIB3000MB_MPEG2_OUT_MODE_204 ( 0)
+#define DIB3000MB_MPEG2_OUT_MODE_188 ( 1)
+
+#define DIB3000MB_REG_PID_PARSE ( 144)
+#define DIB3000MB_PID_PARSE_INHIBIT ( 0)
+#define DIB3000MB_PID_PARSE_ACTIVATE ( 1)
+
+#define DIB3000MB_REG_FIFO ( 145)
+#define DIB3000MB_FIFO_INHIBIT ( 1)
+#define DIB3000MB_FIFO_ACTIVATE ( 0)
+
+#define DIB3000MB_REG_FIFO_146 ( 146)
+#define DIB3000MB_FIFO_146 ( 3)
+
+#define DIB3000MB_REG_FIFO_147 ( 147)
+#define DIB3000MB_FIFO_147 (0x0100)
+
+/*
+ * pidfilter
+ * it is not a hardware pidfilter but a filter which drops all pids
+ * except the ones set. Necessary because of the limited USB1.1 bandwidth.
+ * regs 153-168
+ */
+
+#define DIB3000MB_REG_FIRST_PID ( 153)
+#define DIB3000MB_NUM_PIDS ( 16)
+
+/*
+ * output mode
+ * USB devices have to use 'slave'-mode
+ * see also DIB3000MB_REG_ELECT_OUT_MODE
+ */
+#define DIB3000MB_REG_OUTPUT_MODE ( 169)
+#define DIB3000MB_OUTPUT_MODE_GATED_CLK ( 0)
+#define DIB3000MB_OUTPUT_MODE_CONT_CLK ( 1)
+#define DIB3000MB_OUTPUT_MODE_SERIAL ( 2)
+#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY ( 5)
+#define DIB3000MB_OUTPUT_MODE_SLAVE ( 6)
+
+/* irq event mask */
+#define DIB3000MB_REG_IRQ_EVENT_MASK ( 170)
+#define DIB3000MB_IRQ_EVENT_MASK ( 0)
+
+/* filter coefficients */
+static u16 dib3000mb_reg_filter_coeffs[] = {
+ 171, 172, 173, 174, 175, 176, 177, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186,
+ 188, 189, 190, 191, 192, 194
+};
+
+static u16 dib3000mb_filter_coeffs[] = {
+ 226, 160, 29,
+ 979, 998, 19,
+ 22, 1019, 1006,
+ 1022, 12, 6,
+ 1017, 1017, 3,
+ 6, 1019,
+ 1021, 2, 3,
+ 1, 0,
+};
+
+/*
+ * mobile algorithm (when you are moving with your device)
+ * but not faster than 90 km/h
+ */
+#define DIB3000MB_REG_MOBILE_ALGO ( 195)
+#define DIB3000MB_MOBILE_ALGO_ON ( 0)
+#define DIB3000MB_MOBILE_ALGO_OFF ( 1)
+
+/* multiple demodulators algorithm */
+#define DIB3000MB_REG_MULTI_DEMOD_MSB ( 206)
+#define DIB3000MB_REG_MULTI_DEMOD_LSB ( 207)
+
+/* terminator, no more demods */
+#define DIB3000MB_MULTI_DEMOD_MSB ( 32767)
+#define DIB3000MB_MULTI_DEMOD_LSB ( 4095)
+
+/* bring the device into a known */
+#define DIB3000MB_REG_RESET_DEVICE ( 1024)
+#define DIB3000MB_RESET_DEVICE (0x812c)
+#define DIB3000MB_RESET_DEVICE_RST ( 0)
+
+/* hardware clock configuration */
+#define DIB3000MB_REG_CLOCK ( 1027)
+#define DIB3000MB_CLOCK_DEFAULT (0x9000)
+#define DIB3000MB_CLOCK_DIVERSITY (0x92b0)
+
+/* power down config */
+#define DIB3000MB_REG_POWER_CONTROL ( 1028)
+#define DIB3000MB_POWER_DOWN ( 1)
+#define DIB3000MB_POWER_UP ( 0)
+
+/* electrical output mode */
+#define DIB3000MB_REG_ELECT_OUT_MODE ( 1029)
+#define DIB3000MB_ELECT_OUT_MODE_OFF ( 0)
+#define DIB3000MB_ELECT_OUT_MODE_ON ( 1)
+
+/* set the tuner i2c address */
+#define DIB3000MB_REG_TUNER ( 1089)
+
+/* monitoring registers (read only) */
+
+/* agc loop locked (size: 1) */
+#define DIB3000MB_REG_AGC_LOCK ( 324)
+
+/* agc power (size: 16) */
+#define DIB3000MB_REG_AGC_POWER ( 325)
+
+/* agc1 value (16) */
+#define DIB3000MB_REG_AGC1_VALUE ( 326)
+
+/* agc2 value (16) */
+#define DIB3000MB_REG_AGC2_VALUE ( 327)
+
+/* total RF power (16), can be used for signal strength */
+#define DIB3000MB_REG_RF_POWER ( 328)
+
+/* dds_frequency with offset (24) */
+#define DIB3000MB_REG_DDS_VALUE_MSB ( 339)
+#define DIB3000MB_REG_DDS_VALUE_LSB ( 340)
+
+/* timing offset signed (24) */
+#define DIB3000MB_REG_TIMING_OFFSET_MSB ( 341)
+#define DIB3000MB_REG_TIMING_OFFSET_LSB ( 342)
+
+/* fft start position (13) */
+#define DIB3000MB_REG_FFT_WINDOW_POS ( 353)
+
+/* carriers locked (1) */
+#define DIB3000MB_REG_CARRIER_LOCK ( 355)
+
+/* noise power (24) */
+#define DIB3000MB_REG_NOISE_POWER_MSB ( 372)
+#define DIB3000MB_REG_NOISE_POWER_LSB ( 373)
+
+#define DIB3000MB_REG_MOBILE_NOISE_MSB ( 374)
+#define DIB3000MB_REG_MOBILE_NOISE_LSB ( 375)
+
+/*
+ * signal power (16), this and the above can be
+ * used to calculate the signal/noise - ratio
+ */
+#define DIB3000MB_REG_SIGNAL_POWER ( 380)
+
+/* mer (24) */
+#define DIB3000MB_REG_MER_MSB ( 381)
+#define DIB3000MB_REG_MER_LSB ( 382)
+
+/*
+ * Transmission Parameter Signalling (TPS)
+ * the following registers can be used to get TPS-information.
+ * The values are according to the DVB-T standard.
+ */
+
+/* TPS locked (1) */
+#define DIB3000MB_REG_TPS_LOCK ( 394)
+
+/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */
+#define DIB3000MB_REG_TPS_QAM ( 398)
+
+/* hierarchy from TPS (1) */
+#define DIB3000MB_REG_TPS_HRCH ( 399)
+
+/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */
+#define DIB3000MB_REG_TPS_VIT_ALPHA ( 400)
+
+/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */
+#define DIB3000MB_REG_TPS_CODE_RATE_HP ( 401)
+
+/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */
+#define DIB3000MB_REG_TPS_CODE_RATE_LP ( 402)
+
+/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */
+#define DIB3000MB_REG_TPS_GUARD_TIME ( 403)
+
+/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */
+#define DIB3000MB_REG_TPS_FFT ( 404)
+
+/* cell id from TPS (16) */
+#define DIB3000MB_REG_TPS_CELL_ID ( 406)
+
+/* TPS (68) */
+#define DIB3000MB_REG_TPS_1 ( 408)
+#define DIB3000MB_REG_TPS_2 ( 409)
+#define DIB3000MB_REG_TPS_3 ( 410)
+#define DIB3000MB_REG_TPS_4 ( 411)
+#define DIB3000MB_REG_TPS_5 ( 412)
+
+/* bit error rate (before RS correction) (21) */
+#define DIB3000MB_REG_BER_MSB ( 414)
+#define DIB3000MB_REG_BER_LSB ( 415)
+
+/* packet error rate (uncorrected TS packets) (16) */
+#define DIB3000MB_REG_PACKET_ERROR_RATE ( 417)
+
+/* uncorrected packet count (16) */
+#define DIB3000MB_REG_UNC ( 420)
+
+/* viterbi locked (1) */
+#define DIB3000MB_REG_VIT_LCK ( 421)
+
+/* viterbi inidcator (16) */
+#define DIB3000MB_REG_VIT_INDICATOR ( 422)
+
+/* transport stream sync lock (1) */
+#define DIB3000MB_REG_TS_SYNC_LOCK ( 423)
+
+/* transport stream RS lock (1) */
+#define DIB3000MB_REG_TS_RS_LOCK ( 424)
+
+/* lock mask 0 value (1) */
+#define DIB3000MB_REG_LOCK0_VALUE ( 425)
+
+/* lock mask 1 value (1) */
+#define DIB3000MB_REG_LOCK1_VALUE ( 426)
+
+/* lock mask 2 value (1) */
+#define DIB3000MB_REG_LOCK2_VALUE ( 427)
+
+/* interrupt pending for auto search */
+#define DIB3000MB_REG_AS_IRQ_PENDING ( 434)
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
new file mode 100644
index 000000000000..4a31c05eaecd
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mc.c
@@ -0,0 +1,931 @@
+/*
+ * Frontend driver for mobile DVB-T demodulator DiBcom 3000P/M-C
+ * 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
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.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
+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_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr);
+
+static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode,
+ fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth)
+{
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+}
+
+static int dib3000mc_set_timing(struct dib3000_state *state, int upd_offset,
+ fe_transmit_mode_t fft, fe_bandwidth_t bw)
+{
+ u16 timf_msb,timf_lsb;
+ s32 tim_offset,tim_sgn;
+ u64 comp1,comp2,comp=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;
+ }
+ 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;
+ }
+
+ tim_offset = state->timing_offset;
+ if (tim_offset < 0) {
+ tim_sgn = 1;
+ tim_offset = -tim_offset;
+ } else
+ tim_sgn = 0;
+
+ comp1 = (u32)tim_offset * (u32)timf_lsb ;
+ comp2 = (u32)tim_offset * (u32)timf_msb ;
+ comp = ((comp1 >> 16) + comp2) >> 7;
+
+ if (tim_sgn == 0)
+ comp = (u32)(timf_msb << 16) + (u32) timf_lsb + comp;
+ else
+ comp = (u32)(timf_msb << 16) + (u32) timf_lsb - comp ;
+
+ timf_msb = (comp >> 16) & 0xff;
+ timf_lsb = comp & 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)
+{
+ if (boost) {
+ wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_ON);
+ } 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);
+ }
+ return 0;
+}
+
+static int dib3000mc_set_adp_cfg(struct dib3000_state *state, fe_modulation_t con)
+{
+ 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:
+ break;
+ default:
+ warn("unkown constellation.");
+ break;
+ }
+ return 0;
+}
+
+static int dib3000mc_set_general_cfg(struct dib3000_state *state, struct dvb_frontend_parameters *fep, int *auto_val)
+{
+ 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;
+ }
+
+ wr(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_PARM(alpha,qam,guard,fft));
+ wr(DIB3000MC_REG_HRCH_PARM,DIB3000MC_HRCH_PARM(sel_hp,cr,hrch));
+
+ switch (fep->inversion) {
+ case INVERSION_OFF:
+ wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+ break;
+ case INVERSION_AUTO: /* fall through */
+ case INVERSION_ON:
+ wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_ON);
+ break;
+ 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;
+ return 0;
+}
+
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib3000_state* state = (struct dib3000_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;
+ else
+ inv_test2 = 2;
+
+ fep->inversion =
+ ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
+ ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
+ INVERSION_ON : INVERSION_OFF;
+
+ deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+
+ fep->frequency = state->last_tuned_freq;
+ fep->u.ofdm.bandwidth= state->last_tuned_bw;
+
+ tps_val = rd(DIB3000MC_REG_TUNING_PARM);
+
+ 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;
+ break;
+ case DIB3000_CONSTELLATION_64QAM:
+ deb_getf("QAM64 ");
+ ofdm->constellation = QAM_64;
+ break;
+ default:
+ err("Unexpected constellation returned by TPS (%d)", tps_val);
+ break;
+ }
+
+ 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);
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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");
+
+ return 0;
+}
+
+static int dib3000mc_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep, int tuner)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+ struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
+ int search_state,auto_val;
+ u16 val;
+
+ if (tuner) { /* initial call from dvb */
+ dib3000mc_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+ state->config.pll_set(fe,fep,NULL);
+ dib3000mc_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+
+ 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);
+
+ 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);
+
+ val = rd(DIB3000MC_REG_DEMOD_PARM);
+ wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
+ wr(DIB3000MC_REG_DEMOD_PARM,val);
+
+ msleep(30);
+
+ 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)
+{
+ struct dib3000_state *state;
+
+ deb_info("init start\n");
+
+ state = fe->demodulator_priv;
+ state->timing_offset = 0;
+ state->timing_offset_comp_done = 0;
+
+ 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);
+
+ wr(DIB3000MC_REG_RST_UNC,DIB3000MC_RST_UNC_OFF);
+ wr(DIB3000MC_REG_UNK_19,DIB3000MC_UNK_19);
+
+ wr(33,5);
+ wr(36,81);
+ wr(DIB3000MC_REG_UNK_88,DIB3000MC_UNK_88);
+
+ wr(DIB3000MC_REG_UNK_99,DIB3000MC_UNK_99);
+ wr(DIB3000MC_REG_UNK_111,DIB3000MC_UNK_111_PH_N_MODE_0); /* phase noise algo off */
+
+ /* mobile mode - portable reception */
+ wr_foreach(dib3000mc_reg_mobile_mode,dib3000mc_mobile_mode[1]);
+
+/* 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]);
+
+ 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);
+
+ wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
+ wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
+
+ wr(DIB3000MC_REG_UNK_4,DIB3000MC_UNK_4);
+
+ wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+ wr(DIB3000MC_REG_SET_DDS_FREQ_LSB,DIB3000MC_DDS_FREQ_LSB);
+
+ dib3000mc_set_timing(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
+// wr_foreach(dib3000mc_reg_timing_freq,dib3000mc_timing_freq[3]);
+
+ wr(DIB3000MC_REG_UNK_120,DIB3000MC_UNK_120);
+ wr(DIB3000MC_REG_UNK_134,DIB3000MC_UNK_134);
+ wr(DIB3000MC_REG_FEC_CFG,DIB3000MC_FEC_CFG);
+
+ wr(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+
+ dib3000mc_set_impulse_noise(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
+
+/* 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);
+
+/* MPEG2_PARALLEL_CONTINUOUS_CLOCK
+ wr(DIB3000MC_REG_OUTMODE,
+ DIB3000MC_SET_OUTMODE(DIB3000MC_OM_PAR_CONT_CLK,
+ rd(DIB3000MC_REG_OUTMODE)));
+
+ wr(DIB3000MC_REG_SMO_MODE,
+ DIB3000MC_SMO_MODE_DEFAULT |
+ DIB3000MC_SMO_MODE_188);
+
+ wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_DEFAULT);
+ wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
+*/
+
+/* diversity */
+ wr(DIB3000MC_REG_DIVERSITY1,DIB3000MC_DIVERSITY1_DEFAULT);
+ wr(DIB3000MC_REG_DIVERSITY2,DIB3000MC_DIVERSITY2_DEFAULT);
+
+ set_and(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+
+ set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF);
+
+/* if (state->config->pll_init) {
+ dib3000mc_tuner_pass_ctrl(fe,1,state->config.pll_addr(fe));
+ state->config->pll_init(fe,NULL);
+ dib3000mc_tuner_pass_ctrl(fe,0,state->config.pll_addr(fe));
+ }*/
+ deb_info("init end\n");
+ return 0;
+}
+static int dib3000mc_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+ u16 lock = rd(DIB3000MC_REG_LOCKING);
+
+ *stat = 0;
+ if (DIB3000MC_AGC_LOCK(lock))
+ *stat |= FE_HAS_SIGNAL;
+ if (DIB3000MC_CARRIER_LOCK(lock))
+ *stat |= FE_HAS_CARRIER;
+ if (DIB3000MC_TPS_LOCK(lock))
+ *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));
+
+ return 0;
+}
+
+static int dib3000mc_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+ *ber = ((rd(DIB3000MC_REG_BER_MSB) << 16) | rd(DIB3000MC_REG_BER_LSB));
+ return 0;
+}
+
+static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+ struct dib3000_state* state = (struct dib3000_state*) fe->demodulator_priv;
+
+ *unc = rd(DIB3000MC_REG_PACKET_ERROR_COUNT);
+ return 0;
+}
+
+/* see dib3000mb.c for calculation comments */
+static int dib3000mc_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+ struct dib3000_state* state = (struct dib3000_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);
+ return 0;
+}
+
+/* see dib3000mb.c for calculation comments */
+static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ struct dib3000_state* state = (struct dib3000_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 = (struct dib3000_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);
+ return 0;
+}
+
+static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 2000;
+ tune->step_size = 166667;
+ tune->max_drift = 166667 * 2;
+
+ return 0;
+}
+
+static int dib3000mc_fe_init_nonmobile(struct dvb_frontend* fe)
+{
+ return dib3000mc_fe_init(fe, 0);
+}
+
+static int dib3000mc_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+{
+ return dib3000mc_set_frontend(fe, fep, 1);
+}
+
+static void dib3000mc_release(struct dvb_frontend* fe)
+{
+ struct dib3000_state *state = (struct dib3000_state *) fe->demodulator_priv;
+ kfree(state);
+}
+
+/* pid filter and transfer stuff */
+static int dib3000mc_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+{
+ struct dib3000_state *state = fe->demodulator_priv;
+ pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
+ wr(index+DIB3000MC_REG_FIRST_PID,pid);
+ return 0;
+}
+
+static int dib3000mc_fifo_control(struct dvb_frontend *fe, int onoff)
+{
+ struct dib3000_state *state = (struct dib3000_state*) fe->demodulator_priv;
+ u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
+
+ deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling");
+
+ 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);
+ }
+ 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");
+
+ 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;
+}
+
+static int dib3000mc_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr)
+{
+ struct dib3000_state *state = (struct dib3000_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));
+ }
+ 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;
+}
+
+
+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 dib3000_state* state = NULL;
+ u16 devid;
+
+ /* allocate memory for the internal state */
+ state = (struct dib3000_state*) kmalloc(sizeof(struct dib3000_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+ memset(state,0,sizeof(struct dib3000_state));
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config,config,sizeof(struct dib3000_config));
+ memcpy(&state->ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
+
+ /* check for the correct demod */
+ if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
+ goto error;
+
+ devid = rd(DIB3000_REG_DEVICE_ID);
+ if (devid != DIB3000MC_DEVICE_ID && devid != DIB3000P_DEVICE_ID)
+ goto error;
+
+ 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 */
+ state->frontend.ops = &state->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_demod_init(state);
+
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+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,
+ .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 = dib3000mc_release,
+
+ .init = dib3000mc_fe_init_nonmobile,
+ .sleep = dib3000mc_sleep,
+
+ .set_frontend = dib3000mc_set_frontend_and_tuner,
+ .get_frontend = dib3000mc_get_frontend,
+ .get_tune_settings = dib3000mc_fe_get_tune_settings,
+
+ .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,
+};
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dib3000mc_attach);
diff --git a/drivers/media/dvb/frontends/dib3000mc_priv.h b/drivers/media/dvb/frontends/dib3000mc_priv.h
new file mode 100644
index 000000000000..2930aac7591b
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mc_priv.h
@@ -0,0 +1,428 @@
+/*
+ * 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/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
new file mode 100644
index 000000000000..2a3c2ce7b2aa
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -0,0 +1,168 @@
+/*
+ * $Id: dvb-pll.c,v 1.7 2005/02/10 11:52:02 kraxel Exp $
+ *
+ * descriptions + helper functions for simple dvb plls.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * 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 "dvb-pll.h"
+
+/* ----------------------------------------------------------- */
+/* descriptions */
+
+struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
+ .name = "Thomson dtt7579",
+ .min = 177000000,
+ .max = 858000000,
+ .count = 5,
+ .entries = {
+ { 0, 36166667, 166666, 0xb4, 0x03 }, /* go sleep */
+ { 443250000, 36166667, 166666, 0xb4, 0x02 },
+ { 542000000, 36166667, 166666, 0xb4, 0x08 },
+ { 771000000, 36166667, 166666, 0xbc, 0x08 },
+ { 999999999, 36166667, 166666, 0xf4, 0x08 },
+ },
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt7579);
+
+struct dvb_pll_desc dvb_pll_thomson_dtt7610 = {
+ .name = "Thomson dtt7610",
+ .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_dtt7610);
+
+static void thomson_dtt759x_bw(u8 *buf, int bandwidth)
+{
+ if (BANDWIDTH_7_MHZ == bandwidth)
+ buf[3] |= 0x10;
+}
+
+struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
+ .name = "Thomson dtt759x",
+ .min = 177000000,
+ .max = 896000000,
+ .setbw = thomson_dtt759x_bw,
+ .count = 6,
+ .entries = {
+ { 0, 36166667, 166666, 0x84, 0x03 },
+ { 264000000, 36166667, 166666, 0xb4, 0x02 },
+ { 470000000, 36166667, 166666, 0xbc, 0x02 },
+ { 735000000, 36166667, 166666, 0xbc, 0x08 },
+ { 835000000, 36166667, 166666, 0xf4, 0x08 },
+ { 999999999, 36166667, 166666, 0xfc, 0x08 },
+ },
+};
+EXPORT_SYMBOL(dvb_pll_thomson_dtt759x);
+
+struct dvb_pll_desc dvb_pll_lg_z201 = {
+ .name = "LG z201",
+ .min = 174000000,
+ .max = 862000000,
+ .count = 5,
+ .entries = {
+ { 0, 36166667, 166666, 0xbc, 0x03 },
+ { 443250000, 36166667, 166666, 0xbc, 0x01 },
+ { 542000000, 36166667, 166666, 0xbc, 0x02 },
+ { 830000000, 36166667, 166666, 0xf4, 0x02 },
+ { 999999999, 36166667, 166666, 0xfc, 0x02 },
+ },
+};
+EXPORT_SYMBOL(dvb_pll_lg_z201);
+
+struct dvb_pll_desc dvb_pll_unknown_1 = {
+ .name = "unknown 1", /* used by dntv live dvb-t */
+ .min = 174000000,
+ .max = 862000000,
+ .count = 9,
+ .entries = {
+ { 150000000, 36166667, 166666, 0xb4, 0x01 },
+ { 173000000, 36166667, 166666, 0xbc, 0x01 },
+ { 250000000, 36166667, 166666, 0xb4, 0x02 },
+ { 400000000, 36166667, 166666, 0xbc, 0x02 },
+ { 420000000, 36166667, 166666, 0xf4, 0x02 },
+ { 470000000, 36166667, 166666, 0xfc, 0x02 },
+ { 600000000, 36166667, 166666, 0xbc, 0x08 },
+ { 730000000, 36166667, 166666, 0xf4, 0x08 },
+ { 999999999, 36166667, 166666, 0xfc, 0x08 },
+ },
+};
+EXPORT_SYMBOL(dvb_pll_unknown_1);
+
+/* ----------------------------------------------------------- */
+/* code */
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
+ u32 freq, int bandwidth)
+{
+ u32 div;
+ int i;
+
+ if (freq != 0 && (freq < desc->min || freq > desc->max))
+ return -EINVAL;
+
+ for (i = 0; i < desc->count; i++) {
+ if (freq > desc->entries[i].limit)
+ continue;
+ break;
+ }
+ if (debug)
+ printk("pll: %s: freq=%d bw=%d | i=%d/%d\n",
+ desc->name, freq, bandwidth, i, desc->count);
+ BUG_ON(i == desc->count);
+
+ div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize;
+ buf[0] = div >> 8;
+ buf[1] = div & 0xff;
+ buf[2] = desc->entries[i].cb1;
+ buf[3] = desc->entries[i].cb2;
+
+ if (desc->setbw)
+ desc->setbw(buf, bandwidth);
+
+ if (debug)
+ 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;
+}
+EXPORT_SYMBOL(dvb_pll_configure);
+
+MODULE_DESCRIPTION("dvb pll library");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
new file mode 100644
index 000000000000..016c794a5677
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -0,0 +1,34 @@
+/*
+ * $Id: dvb-pll.h,v 1.2 2005/02/10 11:43:41 kraxel Exp $
+ */
+
+struct dvb_pll_desc {
+ char *name;
+ u32 min;
+ u32 max;
+ void (*setbw)(u8 *buf, int bandwidth);
+ int count;
+ struct {
+ u32 limit;
+ u32 offset;
+ u32 stepsize;
+ u8 cb1;
+ u8 cb2;
+ } entries[9];
+};
+
+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_unknown_1;
+
+int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
+ u32 freq, int bandwidth);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c
new file mode 100644
index 000000000000..c05a9b05600c
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c
@@ -0,0 +1,279 @@
+/*
+ * Driver for Dummy Frontend
+ *
+ * Written by Emard <emard@softhome.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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include "dvb_frontend.h"
+#include "dvb_dummy_fe.h"
+
+
+struct dvb_dummy_fe_state {
+ struct dvb_frontend_ops ops;
+ struct dvb_frontend frontend;
+};
+
+
+static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ *status = FE_HAS_SIGNAL
+ | FE_HAS_CARRIER
+ | FE_HAS_VITERBI
+ | FE_HAS_SYNC
+ | FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int dvb_dummy_fe_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static int dvb_dummy_fe_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_sleep(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_init(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ return 0;
+}
+
+static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ return 0;
+}
+
+static void dvb_dummy_fe_release(struct dvb_frontend* fe)
+{
+ struct dvb_dummy_fe_state* state = (struct dvb_dummy_fe_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops;
+
+struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void)
+{
+ struct dvb_dummy_fe_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ memcpy(&state->ops, &dvb_dummy_fe_ofdm_ops, sizeof(struct dvb_frontend_ops));
+
+ /* 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 dvb_dummy_fe_qpsk_ops;
+
+struct dvb_frontend* dvb_dummy_fe_qpsk_attach()
+{
+ struct dvb_dummy_fe_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ memcpy(&state->ops, &dvb_dummy_fe_qpsk_ops, sizeof(struct dvb_frontend_ops));
+
+ /* create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ if (state) kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops dvb_dummy_fe_qam_ops;
+
+struct dvb_frontend* dvb_dummy_fe_qam_attach()
+{
+ struct dvb_dummy_fe_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ memcpy(&state->ops, &dvb_dummy_fe_qam_ops, sizeof(struct dvb_frontend_ops));
+
+ /* create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ if (state) kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = {
+
+ .info = {
+ .name = "Dummy DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 0,
+ .frequency_max = 863250000,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = dvb_dummy_fe_release,
+
+ .init = dvb_dummy_fe_init,
+ .sleep = dvb_dummy_fe_sleep,
+
+ .set_frontend = dvb_dummy_fe_set_frontend,
+ .get_frontend = dvb_dummy_fe_get_frontend,
+
+ .read_status = dvb_dummy_fe_read_status,
+ .read_ber = dvb_dummy_fe_read_ber,
+ .read_signal_strength = dvb_dummy_fe_read_signal_strength,
+ .read_snr = dvb_dummy_fe_read_snr,
+ .read_ucblocks = dvb_dummy_fe_read_ucblocks,
+};
+
+static struct dvb_frontend_ops dvb_dummy_fe_qam_ops = {
+
+ .info = {
+ .name = "Dummy DVB-C",
+ .type = FE_QAM,
+ .frequency_stepsize = 62500,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */
+ .symbol_rate_max = (57840000/2)/4, /* SACLK/4 */
+ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+ FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO
+ },
+
+ .release = dvb_dummy_fe_release,
+
+ .init = dvb_dummy_fe_init,
+ .sleep = dvb_dummy_fe_sleep,
+
+ .set_frontend = dvb_dummy_fe_set_frontend,
+ .get_frontend = dvb_dummy_fe_get_frontend,
+
+ .read_status = dvb_dummy_fe_read_status,
+ .read_ber = dvb_dummy_fe_read_ber,
+ .read_signal_strength = dvb_dummy_fe_read_signal_strength,
+ .read_snr = dvb_dummy_fe_read_snr,
+ .read_ucblocks = dvb_dummy_fe_read_ucblocks,
+};
+
+static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = {
+
+ .info = {
+ .name = "Dummy DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 250, /* kHz for QPSK frontends */
+ .frequency_tolerance = 29500,
+ .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_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK
+ },
+
+ .release = dvb_dummy_fe_release,
+
+ .init = dvb_dummy_fe_init,
+ .sleep = dvb_dummy_fe_sleep,
+
+ .set_frontend = dvb_dummy_fe_set_frontend,
+ .get_frontend = dvb_dummy_fe_get_frontend,
+
+ .read_status = dvb_dummy_fe_read_status,
+ .read_ber = dvb_dummy_fe_read_ber,
+ .read_signal_strength = dvb_dummy_fe_read_signal_strength,
+ .read_snr = dvb_dummy_fe_read_snr,
+ .read_ucblocks = dvb_dummy_fe_read_ucblocks,
+
+ .set_voltage = dvb_dummy_fe_set_voltage,
+ .set_tone = dvb_dummy_fe_set_tone,
+};
+
+MODULE_DESCRIPTION("DVB DUMMY Frontend");
+MODULE_AUTHOR("Emard");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach);
+EXPORT_SYMBOL(dvb_dummy_fe_qam_attach);
+EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach);
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.h b/drivers/media/dvb/frontends/dvb_dummy_fe.h
new file mode 100644
index 000000000000..8210f19d56ce
--- /dev/null
+++ b/drivers/media/dvb/frontends/dvb_dummy_fe.h
@@ -0,0 +1,32 @@
+/*
+ * Driver for Dummy Frontend
+ *
+ * Written by Emard <emard@softhome.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 DVB_DUMMY_FE_H
+#define DVB_DUMMY_FE_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void);
+extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void);
+
+#endif // DVB_DUMMY_FE_H
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
new file mode 100644
index 000000000000..9ac95de9834d
--- /dev/null
+++ b/drivers/media/dvb/frontends/l64781.c
@@ -0,0 +1,602 @@
+/*
+ driver for LSI L64781 COFDM demodulator
+
+ Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH
+ Marko Kohtala <marko.kohtala@luukku.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "l64781.h"
+
+
+struct l64781_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ const struct l64781_config* config;
+ struct dvb_frontend frontend;
+
+ /* private demodulator data */
+ int first:1;
+};
+
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "l64781: " args); \
+ } while (0)
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+
+static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
+ __FUNCTION__, reg, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+static int l64781_readreg (struct l64781_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) return ret;
+
+ return b1[0];
+}
+
+static void apply_tps (struct l64781_state* state)
+{
+ l64781_writereg (state, 0x2a, 0x00);
+ l64781_writereg (state, 0x2a, 0x01);
+
+ /* This here is a little bit questionable because it enables
+ the automatic update of TPS registers. I think we'd need to
+ handle the IRQ from FE to update some other registers as
+ well, or at least implement some magic to tuning to correct
+ to the TPS received from transmission. */
+ l64781_writereg (state, 0x2a, 0x02);
+}
+
+
+static void reset_afc (struct l64781_state* state)
+{
+ /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
+ timing offset */
+ l64781_writereg (state, 0x07, 0x9e); /* stall AFC */
+ l64781_writereg (state, 0x08, 0); /* AFC INIT FREQ */
+ l64781_writereg (state, 0x09, 0);
+ l64781_writereg (state, 0x0a, 0);
+ l64781_writereg (state, 0x07, 0x8e);
+ l64781_writereg (state, 0x0e, 0); /* AGC gain to zero in beginning */
+ l64781_writereg (state, 0x11, 0x80); /* stall TIM */
+ l64781_writereg (state, 0x10, 0); /* TIM_OFFSET_LSB */
+ l64781_writereg (state, 0x12, 0);
+ l64781_writereg (state, 0x13, 0);
+ l64781_writereg (state, 0x11, 0x00);
+}
+
+static int reset_and_configure (struct l64781_state* state)
+{
+ u8 buf [] = { 0x06 };
+ struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 };
+ // NOTE: this is correct in writing to address 0x00
+
+ return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV;
+}
+
+static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_parameters *param)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+ /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */
+ static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 };
+ /* QPSK, QAM_16, QAM_64 */
+ static const u8 qam_tab [] = { 2, 4, 0, 6 };
+ static const u8 bw_tab [] = { 8, 7, 6 }; /* 8Mhz, 7MHz, 6MHz */
+ static const u8 guard_tab [] = { 1, 2, 4, 8 };
+ /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */
+ static const u32 ppm = 8000;
+ struct dvb_ofdm_parameters *p = &param->u.ofdm;
+ u32 ddfs_offset_fixed;
+/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */
+/* bw_tab[p->bandWidth]<<10)/15625; */
+ u32 init_freq;
+ u32 spi_bias;
+ u8 val0x04;
+ u8 val0x05;
+ u8 val0x06;
+ int bw = p->bandwidth - BANDWIDTH_8_MHZ;
+
+ state->config->pll_set(fe, param);
+
+ if (param->inversion != INVERSION_ON &&
+ param->inversion != INVERSION_OFF)
+ return -EINVAL;
+
+ if (bw < 0 || bw > 2)
+ return -EINVAL;
+
+ if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 &&
+ p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 &&
+ p->code_rate_HP != FEC_7_8)
+ return -EINVAL;
+
+ if (p->hierarchy_information != HIERARCHY_NONE &&
+ (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 &&
+ p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 &&
+ p->code_rate_LP != FEC_7_8))
+ return -EINVAL;
+
+ if (p->constellation != QPSK && p->constellation != QAM_16 &&
+ p->constellation != QAM_64)
+ return -EINVAL;
+
+ if (p->transmission_mode != TRANSMISSION_MODE_2K &&
+ p->transmission_mode != TRANSMISSION_MODE_8K)
+ return -EINVAL;
+
+ if (p->guard_interval < GUARD_INTERVAL_1_32 ||
+ p->guard_interval > GUARD_INTERVAL_1_4)
+ return -EINVAL;
+
+ if (p->hierarchy_information < HIERARCHY_NONE ||
+ p->hierarchy_information > HIERARCHY_4)
+ return -EINVAL;
+
+ ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000;
+
+ /* This works up to 20000 ppm, it overflows if too large ppm! */
+ init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) /
+ bw_tab[p->bandwidth] & 0xFFFFFF);
+
+ /* SPI bias calculation is slightly modified to fit in 32bit */
+ /* will work for high ppm only... */
+ spi_bias = 378 * (1 << 10);
+ spi_bias *= 16;
+ spi_bias *= bw_tab[p->bandwidth];
+ spi_bias *= qam_tab[p->constellation];
+ spi_bias /= p->code_rate_HP + 1;
+ spi_bias /= (guard_tab[p->guard_interval] + 32);
+ spi_bias *= 1000ULL;
+ spi_bias /= 1000ULL + ppm/1000;
+ spi_bias *= p->code_rate_HP;
+
+ val0x04 = (p->transmission_mode << 2) | p->guard_interval;
+ val0x05 = fec_tab[p->code_rate_HP];
+
+ if (p->hierarchy_information != HIERARCHY_NONE)
+ val0x05 |= (p->code_rate_LP - FEC_1_2) << 3;
+
+ val0x06 = (p->hierarchy_information << 2) | p->constellation;
+
+ l64781_writereg (state, 0x04, val0x04);
+ l64781_writereg (state, 0x05, val0x05);
+ l64781_writereg (state, 0x06, val0x06);
+
+ reset_afc (state);
+
+ /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */
+ l64781_writereg (state, 0x15,
+ p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3);
+ l64781_writereg (state, 0x16, init_freq & 0xff);
+ l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff);
+ l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff);
+
+ l64781_writereg (state, 0x1b, spi_bias & 0xff);
+ l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff);
+ l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) |
+ (param->inversion == INVERSION_ON ? 0x80 : 0x00));
+
+ l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff);
+ l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f);
+
+ l64781_readreg (state, 0x00); /* clear interrupt registers... */
+ l64781_readreg (state, 0x01); /* dto. */
+
+ apply_tps (state);
+
+ return 0;
+}
+
+static int get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* param)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+ int tmp;
+
+
+ tmp = l64781_readreg(state, 0x04);
+ switch(tmp & 3) {
+ case 0:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+ switch((tmp >> 2) & 3) {
+ case 0:
+ param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ default:
+ printk("Unexpected value for transmission_mode\n");
+ }
+
+
+
+ tmp = l64781_readreg(state, 0x05);
+ switch(tmp & 7) {
+ case 0:
+ param->u.ofdm.code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ param->u.ofdm.code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ param->u.ofdm.code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ param->u.ofdm.code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ param->u.ofdm.code_rate_HP = FEC_7_8;
+ break;
+ default:
+ printk("Unexpected value for code_rate_HP\n");
+ }
+ switch((tmp >> 3) & 7) {
+ case 0:
+ param->u.ofdm.code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ param->u.ofdm.code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ param->u.ofdm.code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ param->u.ofdm.code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ param->u.ofdm.code_rate_LP = FEC_7_8;
+ break;
+ default:
+ printk("Unexpected value for code_rate_LP\n");
+ }
+
+
+ tmp = l64781_readreg(state, 0x06);
+ switch(tmp & 3) {
+ case 0:
+ param->u.ofdm.constellation = QPSK;
+ break;
+ case 1:
+ param->u.ofdm.constellation = QAM_16;
+ break;
+ case 2:
+ param->u.ofdm.constellation = QAM_64;
+ break;
+ default:
+ printk("Unexpected value for constellation\n");
+ }
+ switch((tmp >> 2) & 7) {
+ case 0:
+ param->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ param->u.ofdm.hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ param->u.ofdm.hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ param->u.ofdm.hierarchy_information = HIERARCHY_4;
+ break;
+ default:
+ printk("Unexpected value for hierarchy\n");
+ }
+
+
+ tmp = l64781_readreg (state, 0x1d);
+ param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF;
+
+ tmp = (int) (l64781_readreg (state, 0x08) |
+ (l64781_readreg (state, 0x09) << 8) |
+ (l64781_readreg (state, 0x0a) << 16));
+ param->frequency += tmp;
+
+ return 0;
+}
+
+static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+ int sync = l64781_readreg (state, 0x32);
+ int gain = l64781_readreg (state, 0x0e);
+
+ l64781_readreg (state, 0x00); /* clear interrupt registers... */
+ l64781_readreg (state, 0x01); /* dto. */
+
+ *status = 0;
+
+ if (gain > 5)
+ *status |= FE_HAS_SIGNAL;
+
+ if (sync & 0x02) /* VCXO locked, this criteria should be ok */
+ *status |= FE_HAS_CARRIER;
+
+ if (sync & 0x20)
+ *status |= FE_HAS_VITERBI;
+
+ if (sync & 0x40)
+ *status |= FE_HAS_SYNC;
+
+ if (sync == 0x7f)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int l64781_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+ /* XXX FIXME: set up counting period (reg 0x26...0x28)
+ */
+ *ber = l64781_readreg (state, 0x39)
+ | (l64781_readreg (state, 0x3a) << 8);
+
+ return 0;
+}
+
+static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+ u8 gain = l64781_readreg (state, 0x0e);
+ *signal_strength = (gain << 8) | gain;
+
+ return 0;
+}
+
+static int l64781_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+ u8 avg_quality = 0xff - l64781_readreg (state, 0x33);
+ *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/
+
+ return 0;
+}
+
+static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+ *ucblocks = l64781_readreg (state, 0x37)
+ | (l64781_readreg (state, 0x38) << 8);
+
+ return 0;
+}
+
+static int l64781_sleep(struct dvb_frontend* fe)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+ /* Power down */
+ return l64781_writereg (state, 0x3e, 0x5a);
+}
+
+static int l64781_init(struct dvb_frontend* fe)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+
+ reset_and_configure (state);
+
+ /* Power up */
+ l64781_writereg (state, 0x3e, 0xa5);
+
+ /* Reset hard */
+ l64781_writereg (state, 0x2a, 0x04);
+ l64781_writereg (state, 0x2a, 0x00);
+
+ /* Set tuner specific things */
+ /* AFC_POL, set also in reset_afc */
+ l64781_writereg (state, 0x07, 0x8e);
+
+ /* Use internal ADC */
+ l64781_writereg (state, 0x0b, 0x81);
+
+ /* AGC loop gain, and polarity is positive */
+ l64781_writereg (state, 0x0c, 0x84);
+
+ /* Internal ADC outputs two's complement */
+ l64781_writereg (state, 0x0d, 0x8c);
+
+ /* With ppm=8000, it seems the DTR_SENSITIVITY will result in
+ value of 2 with all possible bandwidths and guard
+ intervals, which is the initial value anyway. */
+ /*l64781_writereg (state, 0x19, 0x92);*/
+
+ /* Everything is two's complement, soft bit and CSI_OUT too */
+ l64781_writereg (state, 0x1e, 0x09);
+
+ if (state->config->pll_init) state->config->pll_init(fe);
+
+ /* delay a bit after first init attempt */
+ if (state->first) {
+ state->first = 0;
+ msleep(200);
+ }
+
+ return 0;
+}
+
+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;
+ return 0;
+}
+
+static void l64781_release(struct dvb_frontend* fe)
+{
+ struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops l64781_ops;
+
+struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct l64781_state* state = NULL;
+ int reg0x3e = -1;
+ u8 b0 [] = { 0x1a };
+ u8 b1 [] = { 0x00 };
+ struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ /* allocate memory for the internal state */
+ state = (struct l64781_state*) kmalloc(sizeof(struct l64781_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &l64781_ops, sizeof(struct dvb_frontend_ops));
+ state->first = 1;
+
+ /**
+ * the L64781 won't show up before we send the reset_and_configure()
+ * broadcast. If nothing responds there is no L64781 on the bus...
+ */
+ if (reset_and_configure(state) < 0) {
+ dprintk("No response to reset and configure broadcast...\n");
+ goto error;
+ }
+
+ /* The chip always responds to reads */
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ dprintk("No response to read on I2C bus\n");
+ goto error;
+ }
+
+ /* Save current register contents for bailout */
+ reg0x3e = l64781_readreg(state, 0x3e);
+
+ /* Reading the POWER_DOWN register always returns 0 */
+ if (reg0x3e != 0) {
+ dprintk("Device doesn't look like L64781\n");
+ goto error;
+ }
+
+ /* Turn the chip off */
+ l64781_writereg (state, 0x3e, 0x5a);
+
+ /* Responds to all reads with 0 */
+ if (l64781_readreg(state, 0x1a) != 0) {
+ dprintk("Read 1 returned unexpcted value\n");
+ goto error;
+ }
+
+ /* Turn the chip on */
+ l64781_writereg (state, 0x3e, 0xa5);
+
+ /* Responds with register default value */
+ if (l64781_readreg(state, 0x1a) != 0xa1) {
+ dprintk("Read 2 returned unexpcted value\n");
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ if (reg0x3e >= 0) l64781_writereg (state, 0x3e, reg0x3e); /* restore reg 0x3e */
+ kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops l64781_ops = {
+
+ .info = {
+ .name = "LSI L64781 DVB-T",
+ .type = FE_OFDM,
+ /* .frequency_min = ???,*/
+ /* .frequency_max = ???,*/
+ .frequency_stepsize = 166666,
+ /* .frequency_tolerance = ???,*/
+ /* .symbol_rate_tolerance = ???,*/
+ .caps = 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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = l64781_release,
+
+ .init = l64781_init,
+ .sleep = l64781_sleep,
+
+ .set_frontend = apply_frontend_param,
+ .get_frontend = get_frontend,
+ .get_tune_settings = l64781_get_tune_settings,
+
+ .read_status = l64781_read_status,
+ .read_ber = l64781_read_ber,
+ .read_signal_strength = l64781_read_signal_strength,
+ .read_snr = l64781_read_snr,
+ .read_ucblocks = l64781_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(l64781_attach);
diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h
new file mode 100644
index 000000000000..7e30fb0fdfa7
--- /dev/null
+++ b/drivers/media/dvb/frontends/l64781.h
@@ -0,0 +1,42 @@
+/*
+ driver for LSI L64781 COFDM demodulator
+
+ Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH
+ Marko Kohtala <marko.kohtala@luukku.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 L64781_H
+#define L64781_H
+
+#include <linux/dvb/frontend.h>
+
+struct l64781_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);
+};
+
+
+extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // L64781_H
diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c
new file mode 100644
index 000000000000..176a22e3441b
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt312.c
@@ -0,0 +1,729 @@
+/*
+ Driver for Zarlink VP310/MT312 Satellite Channel Decoder
+
+ Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ References:
+ http://products.zarlink.com/product_profiles/MT312.htm
+ http://products.zarlink.com/product_profiles/SL1935.htm
+*/
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include "dvb_frontend.h"
+#include "mt312_priv.h"
+#include "mt312.h"
+
+
+struct mt312_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ /* configuration settings */
+ const struct mt312_config* config;
+ struct dvb_frontend frontend;
+
+ u8 id;
+ u8 frequency;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "mt312: " args); \
+ } while (0)
+
+#define MT312_SYS_CLK 90000000UL /* 90 MHz */
+#define MT312_LPOWER_SYS_CLK 60000000UL /* 60 MHz */
+#define MT312_PLL_CLK 10000000UL /* 10 MHz */
+
+static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg,
+ void *buf, const size_t count)
+{
+ int ret;
+ struct i2c_msg msg[2];
+ u8 regbuf[1] = { reg };
+
+ msg[0].addr = state->config->demod_address;
+ msg[0].flags = 0;
+ msg[0].buf = regbuf;
+ msg[0].len = 1;
+ msg[1].addr = state->config->demod_address;
+ msg[1].flags = I2C_M_RD;
+ msg[1].buf = buf;
+ msg[1].len = count;
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk(KERN_ERR "%s: ret == %d\n", __FUNCTION__, ret);
+ return -EREMOTEIO;
+ }
+
+ if(debug) {
+ int i;
+ dprintk("R(%d):", reg & 0x7f);
+ for (i = 0; i < count; i++)
+ printk(" %02x", ((const u8 *) buf)[i]);
+ printk("\n");
+ }
+
+ return 0;
+}
+
+static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg,
+ const void *src, const size_t count)
+{
+ int ret;
+ u8 buf[count + 1];
+ struct i2c_msg msg;
+
+ if(debug) {
+ int i;
+ dprintk("W(%d):", reg & 0x7f);
+ for (i = 0; i < count; i++)
+ printk(" %02x", ((const u8 *) src)[i]);
+ printk("\n");
+ }
+
+ buf[0] = reg;
+ memcpy(&buf[1], src, count);
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.buf = buf;
+ msg.len = count + 1;
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1) {
+ dprintk("%s: ret == %d\n", __FUNCTION__, ret);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static inline int mt312_readreg(struct mt312_state* state,
+ const enum mt312_reg_addr reg, u8 *val)
+{
+ return mt312_read(state, reg, val, 1);
+}
+
+static inline int mt312_writereg(struct mt312_state* state,
+ const enum mt312_reg_addr reg, const u8 val)
+{
+ return mt312_write(state, reg, &val, 1);
+}
+
+static inline u32 mt312_div(u32 a, u32 b)
+{
+ return (a + (b / 2)) / b;
+}
+
+static int mt312_reset(struct mt312_state* state, const u8 full)
+{
+ return mt312_writereg(state, RESET, full ? 0x80 : 0x40);
+}
+
+static int mt312_get_inversion(struct mt312_state* state,
+ fe_spectral_inversion_t *i)
+{
+ int ret;
+ u8 vit_mode;
+
+ if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0)
+ return ret;
+
+ if (vit_mode & 0x80) /* auto inversion was used */
+ *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF;
+
+ return 0;
+}
+
+static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr)
+{
+ int ret;
+ u8 sym_rate_h;
+ u8 dec_ratio;
+ u16 sym_rat_op;
+ u16 monitor;
+ u8 buf[2];
+
+ if ((ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h)) < 0)
+ return ret;
+
+ if (sym_rate_h & 0x80) { /* symbol rate search was used */
+ if ((ret = mt312_writereg(state, MON_CTRL, 0x03)) < 0)
+ return ret;
+
+ if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+ return ret;
+
+ monitor = (buf[0] << 8) | buf[1];
+
+ dprintk(KERN_DEBUG "sr(auto) = %u\n",
+ mt312_div(monitor * 15625, 4));
+ } else {
+ if ((ret = mt312_writereg(state, MON_CTRL, 0x05)) < 0)
+ return ret;
+
+ if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0)
+ return ret;
+
+ dec_ratio = ((buf[0] >> 5) & 0x07) * 32;
+
+ if ((ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf))) < 0)
+ return ret;
+
+ sym_rat_op = (buf[0] << 8) | buf[1];
+
+ dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n",
+ sym_rat_op, dec_ratio);
+ dprintk(KERN_DEBUG "*sr(manual) = %lu\n",
+ (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) *
+ 2) - dec_ratio);
+ }
+
+ return 0;
+}
+
+static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr)
+{
+ const fe_code_rate_t fec_tab[8] =
+ { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8,
+ FEC_AUTO, FEC_AUTO };
+
+ int ret;
+ u8 fec_status;
+
+ if ((ret = mt312_readreg(state, FEC_STATUS, &fec_status)) < 0)
+ return ret;
+
+ *cr = fec_tab[(fec_status >> 4) & 0x07];
+
+ return 0;
+}
+
+static int mt312_initfe(struct dvb_frontend* fe)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ /* wake up */
+ if ((ret = mt312_writereg(state, CONFIG, (state->frequency == 60 ? 0x88 : 0x8c))) < 0)
+ return ret;
+
+ /* wait at least 150 usec */
+ udelay(150);
+
+ /* full reset */
+ if ((ret = mt312_reset(state, 1)) < 0)
+ return ret;
+
+// Per datasheet, write correct values. 09/28/03 ACCJr.
+// If we don't do this, we won't get FE_HAS_VITERBI in the VP310.
+ {
+ u8 buf_def[8]={0x14, 0x12, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00};
+
+ if ((ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def))) < 0)
+ return ret;
+ }
+
+ /* SYS_CLK */
+ buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000);
+
+ /* DISEQC_RATIO */
+ buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4);
+
+ if ((ret = mt312_write(state, SYS_CLK, buf, sizeof(buf))) < 0)
+ return ret;
+
+ if ((ret = mt312_writereg(state, SNR_THS_HIGH, 0x32)) < 0)
+ return ret;
+
+ if ((ret = mt312_writereg(state, OP_CTRL, 0x53)) < 0)
+ return ret;
+
+ /* TS_SW_LIM */
+ buf[0] = 0x8c;
+ buf[1] = 0x98;
+
+ if ((ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf))) < 0)
+ return ret;
+
+ if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0)
+ return ret;
+
+ if (state->config->pll_init) {
+ mt312_writereg(state, GPP_CTRL, 0x40);
+ state->config->pll_init(fe);
+ mt312_writereg(state, GPP_CTRL, 0x00);
+ }
+
+ return 0;
+}
+
+static int mt312_send_master_cmd(struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd *c)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 diseqc_mode;
+
+ if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg)))
+ return -EINVAL;
+
+ if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ return ret;
+
+ if ((ret =
+ mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0)
+ return ret;
+
+ if ((ret =
+ mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3)
+ | 0x04)) < 0)
+ return ret;
+
+ /* set DISEQC_MODE[2:0] to zero if a return message is expected */
+ if (c->msg[0] & 0x02)
+ if ((ret =
+ mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40))) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ const u8 mini_tab[2] = { 0x02, 0x03 };
+
+ int ret;
+ u8 diseqc_mode;
+
+ if (c > SEC_MINI_B)
+ return -EINVAL;
+
+ if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ return ret;
+
+ if ((ret =
+ mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | mini_tab[c])) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ const u8 tone_tab[2] = { 0x01, 0x00 };
+
+ int ret;
+ u8 diseqc_mode;
+
+ if (t > SEC_TONE_OFF)
+ return -EINVAL;
+
+ if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0)
+ return ret;
+
+ if ((ret =
+ mt312_writereg(state, DISEQC_MODE,
+ (diseqc_mode & 0x40) | tone_tab[t])) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ const u8 volt_tab[3] = { 0x00, 0x40, 0x00 };
+
+ if (v > SEC_VOLTAGE_OFF)
+ return -EINVAL;
+
+ return mt312_writereg(state, DISEQC_MODE, volt_tab[v]);
+}
+
+static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 status[3];
+
+ *s = 0;
+
+ if ((ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status))) < 0)
+ return ret;
+
+ dprintk(KERN_DEBUG "QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]);
+
+ if (status[0] & 0xc0)
+ *s |= FE_HAS_SIGNAL; /* signal noise ratio */
+ if (status[0] & 0x04)
+ *s |= FE_HAS_CARRIER; /* qpsk carrier lock */
+ if (status[2] & 0x02)
+ *s |= FE_HAS_VITERBI; /* viterbi lock */
+ if (status[2] & 0x04)
+ *s |= FE_HAS_SYNC; /* byte align lock */
+ if (status[0] & 0x01)
+ *s |= FE_HAS_LOCK; /* qpsk lock */
+
+ return 0;
+}
+
+static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 buf[3];
+
+ if ((ret = mt312_read(state, RS_BERCNT_H, buf, 3)) < 0)
+ return ret;
+
+ *ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64;
+
+ return 0;
+}
+
+static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_strength)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 buf[3];
+ u16 agc;
+ s16 err_db;
+
+ if ((ret = mt312_read(state, AGC_H, buf, sizeof(buf))) < 0)
+ return ret;
+
+ agc = (buf[0] << 6) | (buf[1] >> 2);
+ err_db = (s16) (((buf[1] & 0x03) << 14) | buf[2] << 6) >> 6;
+
+ *signal_strength = agc;
+
+ dprintk(KERN_DEBUG "agc=%08x err_db=%hd\n", agc, err_db);
+
+ return 0;
+}
+
+static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ if ((ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf))) < 0)
+ return ret;
+
+ *snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1);
+
+ return 0;
+}
+
+static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ if ((ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf))) < 0)
+ return ret;
+
+ *ubc = (buf[0] << 8) | buf[1];
+
+ return 0;
+}
+
+static int mt312_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 buf[5], config_val;
+ u16 sr;
+
+ const u8 fec_tab[10] =
+ { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f };
+ const u8 inv_tab[3] = { 0x00, 0x40, 0x80 };
+
+ dprintk("%s: Freq %d\n", __FUNCTION__, p->frequency);
+
+ if ((p->frequency < fe->ops->info.frequency_min)
+ || (p->frequency > fe->ops->info.frequency_max))
+ return -EINVAL;
+
+ if ((p->inversion < INVERSION_OFF)
+ || (p->inversion > INVERSION_ON))
+ return -EINVAL;
+
+ if ((p->u.qpsk.symbol_rate < fe->ops->info.symbol_rate_min)
+ || (p->u.qpsk.symbol_rate > fe->ops->info.symbol_rate_max))
+ return -EINVAL;
+
+ if ((p->u.qpsk.fec_inner < FEC_NONE)
+ || (p->u.qpsk.fec_inner > FEC_AUTO))
+ return -EINVAL;
+
+ if ((p->u.qpsk.fec_inner == FEC_4_5)
+ || (p->u.qpsk.fec_inner == FEC_8_9))
+ return -EINVAL;
+
+ switch (state->id) {
+ case ID_VP310:
+ // For now we will do this only for the VP310.
+ // It should be better for the mt312 as well, but tunning will be slower. ACCJr 09/29/03
+ if ((ret = mt312_readreg(state, CONFIG, &config_val) < 0))
+ return ret;
+ if (p->u.qpsk.symbol_rate >= 30000000) //Note that 30MS/s should use 90MHz
+ {
+ if ((config_val & 0x0c) == 0x08) { //We are running 60MHz
+ state->frequency = 90;
+ if ((ret = mt312_initfe(fe)) < 0)
+ return ret;
+ }
+ }
+ else
+ {
+ if ((config_val & 0x0c) == 0x0C) { //We are running 90MHz
+ state->frequency = 60;
+ if ((ret = mt312_initfe(fe)) < 0)
+ return ret;
+ }
+ }
+ break;
+
+ case ID_MT312:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ mt312_writereg(state, GPP_CTRL, 0x40);
+ state->config->pll_set(fe, p);
+ mt312_writereg(state, GPP_CTRL, 0x00);
+
+ /* sr = (u16)(sr * 256.0 / 1000000.0) */
+ sr = mt312_div(p->u.qpsk.symbol_rate * 4, 15625);
+
+ /* SYM_RATE */
+ buf[0] = (sr >> 8) & 0x3f;
+ buf[1] = (sr >> 0) & 0xff;
+
+ /* VIT_MODE */
+ buf[2] = inv_tab[p->inversion] | fec_tab[p->u.qpsk.fec_inner];
+
+ /* QPSK_CTRL */
+ buf[3] = 0x40; /* swap I and Q before QPSK demodulation */
+
+ if (p->u.qpsk.symbol_rate < 10000000)
+ buf[3] |= 0x04; /* use afc mode */
+
+ /* GO */
+ buf[4] = 0x01;
+
+ if ((ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf))) < 0)
+ return ret;
+
+ mt312_reset(state, 0);
+
+ return 0;
+}
+
+static int mt312_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+
+ if ((ret = mt312_get_inversion(state, &p->inversion)) < 0)
+ return ret;
+
+ if ((ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate)) < 0)
+ return ret;
+
+ if ((ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mt312_sleep(struct dvb_frontend* fe)
+{
+ struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv;
+ int ret;
+ u8 config;
+
+ /* reset all registers to defaults */
+ if ((ret = mt312_reset(state, 1)) < 0)
+ return ret;
+
+ if ((ret = mt312_readreg(state, CONFIG, &config)) < 0)
+ return ret;
+
+ /* enter standby */
+ if ((ret = mt312_writereg(state, CONFIG, config & 0x7f)) < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 50;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void mt312_release(struct dvb_frontend* fe)
+{
+ struct mt312_state* state = (struct mt312_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops vp310_mt312_ops;
+
+struct dvb_frontend* vp310_attach(const struct mt312_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct mt312_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops));
+ strcpy(state->ops.info.name, "Zarlink VP310 DVB-S");
+
+ /* check if the demod is there */
+ if (mt312_readreg(state, ID, &state->id) < 0)
+ goto error;
+ if (state->id != ID_VP310) {
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ state->frequency = 90;
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+
+struct dvb_frontend* mt312_attach(const struct mt312_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct mt312_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &vp310_mt312_ops, sizeof(struct dvb_frontend_ops));
+ strcpy(state->ops.info.name, "Zarlink MT312 DVB-S");
+
+ /* check if the demod is there */
+ if (mt312_readreg(state, ID, &state->id) < 0)
+ goto error;
+ if (state->id != ID_MT312) {
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ state->frequency = 60;
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ if (state)
+ kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops vp310_mt312_ops = {
+
+ .info = {
+ .name = "Zarlink ???? DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128,
+ .symbol_rate_min = MT312_SYS_CLK / 128,
+ .symbol_rate_max = MT312_SYS_CLK / 2,
+ .caps =
+ 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_MUTE_TS |
+ FE_CAN_RECOVER
+ },
+
+ .release = mt312_release,
+
+ .init = mt312_initfe,
+ .sleep = mt312_sleep,
+
+ .set_frontend = mt312_set_frontend,
+ .get_frontend = mt312_get_frontend,
+ .get_tune_settings = mt312_get_tune_settings,
+
+ .read_status = mt312_read_status,
+ .read_ber = mt312_read_ber,
+ .read_signal_strength = mt312_read_signal_strength,
+ .read_snr = mt312_read_snr,
+ .read_ucblocks = mt312_read_ucblocks,
+
+ .diseqc_send_master_cmd = mt312_send_master_cmd,
+ .diseqc_send_burst = mt312_send_burst,
+ .set_tone = mt312_set_tone,
+ .set_voltage = mt312_set_voltage,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver");
+MODULE_AUTHOR("Andreas Oberritter <obi@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(mt312_attach);
+EXPORT_SYMBOL(vp310_attach);
diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h
new file mode 100644
index 000000000000..b3a53a73a117
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt312.h
@@ -0,0 +1,47 @@
+/*
+ Driver for Zarlink MT312 Satellite Channel Decoder
+
+ Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ References:
+ http://products.zarlink.com/product_profiles/MT312.htm
+ http://products.zarlink.com/product_profiles/SL1935.htm
+*/
+
+#ifndef MT312_H
+#define MT312_H
+
+#include <linux/dvb/frontend.h>
+
+struct mt312_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);
+};
+
+extern struct dvb_frontend* mt312_attach(const struct mt312_config* config,
+ struct i2c_adapter* i2c);
+
+extern struct dvb_frontend* vp310_attach(const struct mt312_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // MT312_H
diff --git a/drivers/media/dvb/frontends/mt312_priv.h b/drivers/media/dvb/frontends/mt312_priv.h
new file mode 100644
index 000000000000..5e0b95b5337b
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt312_priv.h
@@ -0,0 +1,162 @@
+/*
+ Driver for Zarlink MT312 QPSK Frontend
+
+ Copyright (C) 2003 Andreas Oberritter <obi@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef _DVB_FRONTENDS_MT312_PRIV
+#define _DVB_FRONTENDS_MT312_PRIV
+
+enum mt312_reg_addr {
+ QPSK_INT_H = 0,
+ QPSK_INT_M = 1,
+ QPSK_INT_L = 2,
+ FEC_INT = 3,
+ QPSK_STAT_H = 4,
+ QPSK_STAT_L = 5,
+ FEC_STATUS = 6,
+ LNB_FREQ_H = 7,
+ LNB_FREQ_L = 8,
+ M_SNR_H = 9,
+ M_SNR_L = 10,
+ VIT_ERRCNT_H = 11,
+ VIT_ERRCNT_M = 12,
+ VIT_ERRCNT_L = 13,
+ RS_BERCNT_H = 14,
+ RS_BERCNT_M = 15,
+ RS_BERCNT_L = 16,
+ RS_UBC_H = 17,
+ RS_UBC_L = 18,
+ SIG_LEVEL = 19,
+ GPP_CTRL = 20,
+ RESET = 21,
+ DISEQC_MODE = 22,
+ SYM_RATE_H = 23,
+ SYM_RATE_L = 24,
+ VIT_MODE = 25,
+ QPSK_CTRL = 26,
+ GO = 27,
+ IE_QPSK_H = 28,
+ IE_QPSK_M = 29,
+ IE_QPSK_L = 30,
+ IE_FEC = 31,
+ QPSK_STAT_EN = 32,
+ FEC_STAT_EN = 33,
+ SYS_CLK = 34,
+ DISEQC_RATIO = 35,
+ DISEQC_INSTR = 36,
+ FR_LIM = 37,
+ FR_OFF = 38,
+ AGC_CTRL = 39,
+ AGC_INIT = 40,
+ AGC_REF = 41,
+ AGC_MAX = 42,
+ AGC_MIN = 43,
+ AGC_LK_TH = 44,
+ TS_AGC_LK_TH = 45,
+ AGC_PWR_SET = 46,
+ QPSK_MISC = 47,
+ SNR_THS_LOW = 48,
+ SNR_THS_HIGH = 49,
+ TS_SW_RATE = 50,
+ TS_SW_LIM_L = 51,
+ TS_SW_LIM_H = 52,
+ CS_SW_RATE_1 = 53,
+ CS_SW_RATE_2 = 54,
+ CS_SW_RATE_3 = 55,
+ CS_SW_RATE_4 = 56,
+ CS_SW_LIM = 57,
+ TS_LPK = 58,
+ TS_LPK_M = 59,
+ TS_LPK_L = 60,
+ CS_KPROP_H = 61,
+ CS_KPROP_L = 62,
+ CS_KINT_H = 63,
+ CS_KINT_L = 64,
+ QPSK_SCALE = 65,
+ TLD_OUTCLK_TH = 66,
+ TLD_INCLK_TH = 67,
+ FLD_TH = 68,
+ PLD_OUTLK3 = 69,
+ PLD_OUTLK2 = 70,
+ PLD_OUTLK1 = 71,
+ PLD_OUTLK0 = 72,
+ PLD_INLK3 = 73,
+ PLD_INLK2 = 74,
+ PLD_INLK1 = 75,
+ PLD_INLK0 = 76,
+ PLD_ACC_TIME = 77,
+ SWEEP_PAR = 78,
+ STARTUP_TIME = 79,
+ LOSSLOCK_TH = 80,
+ FEC_LOCK_TM = 81,
+ LOSSLOCK_TM = 82,
+ VIT_ERRPER_H = 83,
+ VIT_ERRPER_M = 84,
+ VIT_ERRPER_L = 85,
+ VIT_SETUP = 86,
+ VIT_REF0 = 87,
+ VIT_REF1 = 88,
+ VIT_REF2 = 89,
+ VIT_REF3 = 90,
+ VIT_REF4 = 91,
+ VIT_REF5 = 92,
+ VIT_REF6 = 93,
+ VIT_MAXERR = 94,
+ BA_SETUPT = 95,
+ OP_CTRL = 96,
+ FEC_SETUP = 97,
+ PROG_SYNC = 98,
+ AFC_SEAR_TH = 99,
+ CSACC_DIF_TH = 100,
+ QPSK_LK_CT = 101,
+ QPSK_ST_CT = 102,
+ MON_CTRL = 103,
+ QPSK_RESET = 104,
+ QPSK_TST_CT = 105,
+ QPSK_TST_ST = 106,
+ TEST_R = 107,
+ AGC_H = 108,
+ AGC_M = 109,
+ AGC_L = 110,
+ FREQ_ERR1_H = 111,
+ FREQ_ERR1_M = 112,
+ FREQ_ERR1_L = 113,
+ FREQ_ERR2_H = 114,
+ FREQ_ERR2_L = 115,
+ SYM_RAT_OP_H = 116,
+ SYM_RAT_OP_L = 117,
+ DESEQC2_INT = 118,
+ DISEQC2_STAT = 119,
+ DISEQC2_FIFO = 120,
+ DISEQC2_CTRL1 = 121,
+ DISEQC2_CTRL2 = 122,
+ MONITOR_H = 123,
+ MONITOR_L = 124,
+ TEST_MODE = 125,
+ ID = 126,
+ CONFIG = 127
+};
+
+enum mt312_model_id {
+ ID_VP310 = 1,
+ ID_MT312 = 3
+};
+
+#endif /* DVB_FRONTENDS_MT312_PRIV */
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c
new file mode 100644
index 000000000000..50326c7248fa
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt352.c
@@ -0,0 +1,610 @@
+/*
+ * Driver for Zarlink DVB-T MT352 demodulator
+ *
+ * Written by Holger Waechtler <holger@qanu.de>
+ * and Daniel Mack <daniel@qanu.de>
+ *
+ * AVerMedia AVerTV DVB-T 771 support by
+ * Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * Support for Samsung TDTC9251DH01C(M) tuner
+ * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ * Amauri Celani <acelani@essegi.net>
+ *
+ * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ * Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ *
+ * 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "mt352_priv.h"
+#include "mt352.h"
+
+struct mt352_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend frontend;
+ struct dvb_frontend_ops ops;
+
+ /* configuration settings */
+ const struct mt352_config* config;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "mt352: " args); \
+ } while (0)
+
+static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0,
+ .buf = buf, .len = 2 };
+ int err = i2c_transfer(state->i2c, &msg, 1);
+ if (err != 1) {
+ printk("mt352_write() to reg %x failed (err = %d)!\n", reg, err);
+ return err;
+ }
+ return 0;
+}
+
+int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
+{
+ int err,i;
+ for (i=0; i < ilen-1; i++)
+ if ((err = mt352_single_write(fe,ibuf[0]+i,ibuf[i+1])))
+ return err;
+
+ return 0;
+}
+
+static int mt352_read_register(struct mt352_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1, .len = 1 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ printk("%s: readreg error (reg=%d, ret==%i)\n",
+ __FUNCTION__, reg, ret);
+ return ret;
+ }
+
+ return b1[0];
+}
+
+int mt352_read(struct dvb_frontend *fe, u8 reg)
+{
+ return mt352_read_register(fe->demodulator_priv,reg);
+}
+
+static int mt352_sleep(struct dvb_frontend* fe)
+{
+ static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 };
+
+ mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
+ return 0;
+}
+
+static void mt352_calc_nominal_rate(struct mt352_state* state,
+ enum fe_bandwidth bandwidth,
+ unsigned char *buf)
+{
+ u32 adc_clock = 20480; /* 20.340 MHz */
+ u32 bw,value;
+
+ switch (bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ bw = 6;
+ break;
+ case BANDWIDTH_7_MHZ:
+ bw = 7;
+ break;
+ case BANDWIDTH_8_MHZ:
+ default:
+ bw = 8;
+ break;
+ }
+ if (state->config->adc_clock)
+ adc_clock = state->config->adc_clock;
+
+ value = 64 * bw * (1<<16) / (7 * 8);
+ value = value * 1000 / adc_clock;
+ dprintk("%s: bw %d, adc_clock %d => 0x%x\n",
+ __FUNCTION__, bw, adc_clock, value);
+ buf[0] = msb(value);
+ buf[1] = lsb(value);
+}
+
+static void mt352_calc_input_freq(struct mt352_state* state,
+ unsigned char *buf)
+{
+ int adc_clock = 20480; /* 20.480000 MHz */
+ int if2 = 36167; /* 36.166667 MHz */
+ int ife,value;
+
+ if (state->config->adc_clock)
+ adc_clock = state->config->adc_clock;
+ if (state->config->if2)
+ if2 = state->config->if2;
+
+ ife = (2*adc_clock - if2);
+ value = -16374 * ife / adc_clock;
+ dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n",
+ __FUNCTION__, if2, ife, adc_clock, value, value & 0x3fff);
+ buf[0] = msb(value);
+ buf[1] = lsb(value);
+}
+
+static int mt352_set_parameters(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+ unsigned char buf[13];
+ static unsigned char tuner_go[] = { 0x5d, 0x01 };
+ static unsigned char fsm_go[] = { 0x5e, 0x01 };
+ unsigned int tps = 0;
+ struct dvb_ofdm_parameters *op = &param->u.ofdm;
+
+ switch (op->code_rate_HP) {
+ case FEC_2_3:
+ tps |= (1 << 7);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 7);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 7);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 7);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->code_rate_LP) {
+ case FEC_2_3:
+ tps |= (1 << 4);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 4);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 4);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 4);
+ break;
+ case FEC_1_2:
+ case FEC_AUTO:
+ break;
+ case FEC_NONE:
+ if (op->hierarchy_information == HIERARCHY_AUTO ||
+ op->hierarchy_information == HIERARCHY_NONE)
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->constellation) {
+ case QPSK:
+ break;
+ case QAM_AUTO:
+ case QAM_16:
+ tps |= (1 << 13);
+ break;
+ case QAM_64:
+ tps |= (2 << 13);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ case TRANSMISSION_MODE_AUTO:
+ break;
+ case TRANSMISSION_MODE_8K:
+ tps |= (1 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ case GUARD_INTERVAL_AUTO:
+ break;
+ case GUARD_INTERVAL_1_16:
+ tps |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ tps |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ tps |= (3 << 2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (op->hierarchy_information) {
+ case HIERARCHY_AUTO:
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ tps |= (1 << 10);
+ break;
+ case HIERARCHY_2:
+ tps |= (2 << 10);
+ break;
+ case HIERARCHY_4:
+ tps |= (3 << 10);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ buf[0] = TPS_GIVEN_1; /* TPS_GIVEN_1 and following registers */
+
+ buf[1] = msb(tps); /* TPS_GIVEN_(1|0) */
+ buf[2] = lsb(tps);
+
+ buf[3] = 0x50; // old
+// buf[3] = 0xf4; // pinnacle
+
+ mt352_calc_nominal_rate(state, op->bandwidth, buf+4);
+ mt352_calc_input_freq(state, buf+6);
+ state->config->pll_set(fe, param, buf+8);
+
+ mt352_write(fe, buf, sizeof(buf));
+ if (state->config->no_tuner) {
+ /* start decoding */
+ mt352_write(fe, fsm_go, 2);
+ } else {
+ /* start tuning */
+ mt352_write(fe, tuner_go, 2);
+ }
+ return 0;
+}
+
+static int mt352_get_parameters(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+ u16 tps;
+ u16 div;
+ u8 trl;
+ struct dvb_ofdm_parameters *op = &param->u.ofdm;
+ static const u8 tps_fec_to_api[8] =
+ {
+ FEC_1_2,
+ FEC_2_3,
+ FEC_3_4,
+ FEC_5_6,
+ FEC_7_8,
+ FEC_AUTO,
+ FEC_AUTO,
+ FEC_AUTO
+ };
+
+ if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 )
+ return -EINVAL;
+
+ /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because
+ * the mt352 sometimes works with the wrong parameters
+ */
+ tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0);
+ div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0);
+ trl = mt352_read_register(state, TRL_NOMINAL_RATE_1);
+
+ op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7];
+ op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7];
+
+ switch ( (tps >> 13) & 3)
+ {
+ case 0:
+ op->constellation = QPSK;
+ break;
+ case 1:
+ op->constellation = QAM_16;
+ break;
+ case 2:
+ op->constellation = QAM_64;
+ break;
+ default:
+ op->constellation = QAM_AUTO;
+ break;
+ }
+
+ op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K;
+
+ switch ( (tps >> 2) & 3)
+ {
+ case 0:
+ op->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ op->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ op->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ op->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ default:
+ op->guard_interval = GUARD_INTERVAL_AUTO;
+ break;
+ }
+
+ switch ( (tps >> 10) & 7)
+ {
+ case 0:
+ op->hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ op->hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ op->hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ op->hierarchy_information = HIERARCHY_4;
+ break;
+ default:
+ op->hierarchy_information = HIERARCHY_AUTO;
+ break;
+ }
+
+ param->frequency = ( 500 * (div - IF_FREQUENCYx6) ) / 3 * 1000;
+
+ if (trl == 0x72)
+ op->bandwidth = BANDWIDTH_8_MHZ;
+ else if (trl == 0x64)
+ op->bandwidth = BANDWIDTH_7_MHZ;
+ else
+ op->bandwidth = BANDWIDTH_6_MHZ;
+
+
+ if (mt352_read_register(state, STATUS_2) & 0x02)
+ param->inversion = INVERSION_OFF;
+ else
+ param->inversion = INVERSION_ON;
+
+ return 0;
+}
+
+static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+ int s0, s1, s3;
+
+ /* FIXME:
+ *
+ * The MT352 design manual from Zarlink states (page 46-47):
+ *
+ * Notes about the TUNER_GO register:
+ *
+ * If the Read_Tuner_Byte (bit-1) is activated, then the tuner status
+ * byte is copied from the tuner to the STATUS_3 register and
+ * completion of the read operation is indicated by bit-5 of the
+ * INTERRUPT_3 register.
+ */
+
+ if ((s0 = mt352_read_register(state, STATUS_0)) < 0)
+ return -EREMOTEIO;
+ if ((s1 = mt352_read_register(state, STATUS_1)) < 0)
+ return -EREMOTEIO;
+ if ((s3 = mt352_read_register(state, STATUS_3)) < 0)
+ return -EREMOTEIO;
+
+ *status = 0;
+ if (s0 & (1 << 4))
+ *status |= FE_HAS_CARRIER;
+ if (s0 & (1 << 1))
+ *status |= FE_HAS_VITERBI;
+ if (s0 & (1 << 5))
+ *status |= FE_HAS_LOCK;
+ if (s1 & (1 << 1))
+ *status |= FE_HAS_SYNC;
+ if (s3 & (1 << 6))
+ *status |= FE_HAS_SIGNAL;
+
+ if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
+ (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
+ *status &= ~FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int mt352_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+
+ *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) |
+ (mt352_read_register (state, RS_ERR_CNT_1) << 8) |
+ (mt352_read_register (state, RS_ERR_CNT_0));
+
+ return 0;
+}
+
+static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+
+ u16 signal = ((mt352_read_register(state, AGC_GAIN_1) << 8) & 0x0f) |
+ (mt352_read_register(state, AGC_GAIN_0));
+
+ *strength = ~signal;
+ return 0;
+}
+
+static int mt352_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+
+ u8 _snr = mt352_read_register (state, SNR);
+ *snr = (_snr << 8) | _snr;
+
+ return 0;
+}
+
+static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+
+ *ucblocks = (mt352_read_register (state, RS_UBC_1) << 8) |
+ (mt352_read_register (state, RS_UBC_0));
+
+ return 0;
+}
+
+static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+ fe_tune_settings->min_delay_ms = 800;
+ fe_tune_settings->step_size = 0;
+ fe_tune_settings->max_drift = 0;
+
+ return 0;
+}
+
+static int mt352_init(struct dvb_frontend* fe)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+
+ static u8 mt352_reset_attach [] = { RESET, 0xC0 };
+
+ dprintk("%s: hello\n",__FUNCTION__);
+
+ if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 ||
+ (mt352_read_register(state, CONFIG) & 0x20) == 0) {
+
+ /* Do a "hard" reset */
+ mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
+ return state->config->demod_init(fe);
+ }
+
+ return 0;
+}
+
+static void mt352_release(struct dvb_frontend* fe)
+{
+ struct mt352_state* state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops mt352_ops;
+
+struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct mt352_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct mt352_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, &mt352_ops, sizeof(struct dvb_frontend_ops));
+
+ /* check if the demod is there */
+ if (mt352_read_register(state, CHIP_ID) != ID_MT352) 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 mt352_ops = {
+
+ .info = {
+ .name = "Zarlink MT352 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 166667,
+ .frequency_tolerance = 0,
+ .caps = 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_HIERARCHY_AUTO | FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = mt352_release,
+
+ .init = mt352_init,
+ .sleep = mt352_sleep,
+
+ .set_frontend = mt352_set_parameters,
+ .get_frontend = mt352_get_parameters,
+ .get_tune_settings = mt352_get_tune_settings,
+
+ .read_status = mt352_read_status,
+ .read_ber = mt352_read_ber,
+ .read_signal_strength = mt352_read_signal_strength,
+ .read_snr = mt352_read_snr,
+ .read_ucblocks = mt352_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver");
+MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(mt352_attach);
+EXPORT_SYMBOL(mt352_write);
+EXPORT_SYMBOL(mt352_read);
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * compile-command: "make DVB=1"
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h
new file mode 100644
index 000000000000..f5d8a5aed8a9
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt352.h
@@ -0,0 +1,72 @@
+/*
+ * Driver for Zarlink DVB-T MT352 demodulator
+ *
+ * Written by Holger Waechtler <holger@qanu.de>
+ * and Daniel Mack <daniel@qanu.de>
+ *
+ * AVerMedia AVerTV DVB-T 771 support by
+ * Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * Support for Samsung TDTC9251DH01C(M) tuner
+ * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ * Amauri Celani <acelani@essegi.net>
+ *
+ * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ * Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ *
+ * 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 MT352_H
+#define MT352_H
+
+#include <linux/dvb/frontend.h>
+
+struct mt352_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* frequencies in kHz */
+ int adc_clock; // default: 20480
+ int if2; // default: 36166
+
+ /* set if no pll is connected to the secondary i2c bus */
+ int no_tuner;
+
+ /* Initialise the demodulator and PLL. Cannot be NULL */
+ int (*demod_init)(struct dvb_frontend* fe);
+
+ /* PLL setup - fill out the supplied 5 byte buffer with your PLL settings.
+ * byte0: Set to pll i2c address (nonlinux; left shifted by 1)
+ * byte1-4: PLL configuration.
+ */
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf);
+};
+
+extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+ struct i2c_adapter* i2c);
+
+extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen);
+extern int mt352_read(struct dvb_frontend *fe, u8 reg);
+
+#endif // MT352_H
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/mt352_priv.h b/drivers/media/dvb/frontends/mt352_priv.h
new file mode 100644
index 000000000000..44ad0d4c8f12
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt352_priv.h
@@ -0,0 +1,127 @@
+/*
+ * Driver for Zarlink DVB-T MT352 demodulator
+ *
+ * Written by Holger Waechtler <holger@qanu.de>
+ * and Daniel Mack <daniel@qanu.de>
+ *
+ * AVerMedia AVerTV DVB-T 771 support by
+ * Wolfram Joost <dbox2@frokaschwei.de>
+ *
+ * Support for Samsung TDTC9251DH01C(M) tuner
+ * Copyright (C) 2004 Antonio Mancuso <antonio.mancuso@digitaltelevision.it>
+ * Amauri Celani <acelani@essegi.net>
+ *
+ * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by
+ * Christopher Pascoe <c.pascoe@itee.uq.edu.au>
+ *
+ * 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 _MT352_PRIV_
+#define _MT352_PRIV_
+
+#define ID_MT352 0x13
+
+#define msb(x) (((x) >> 8) & 0xff)
+#define lsb(x) ((x) & 0xff)
+
+enum mt352_reg_addr {
+ STATUS_0 = 0x00,
+ STATUS_1 = 0x01,
+ STATUS_2 = 0x02,
+ STATUS_3 = 0x03,
+ STATUS_4 = 0x04,
+ INTERRUPT_0 = 0x05,
+ INTERRUPT_1 = 0x06,
+ INTERRUPT_2 = 0x07,
+ INTERRUPT_3 = 0x08,
+ SNR = 0x09,
+ VIT_ERR_CNT_2 = 0x0A,
+ VIT_ERR_CNT_1 = 0x0B,
+ VIT_ERR_CNT_0 = 0x0C,
+ RS_ERR_CNT_2 = 0x0D,
+ RS_ERR_CNT_1 = 0x0E,
+ RS_ERR_CNT_0 = 0x0F,
+ RS_UBC_1 = 0x10,
+ RS_UBC_0 = 0x11,
+ AGC_GAIN_3 = 0x12,
+ AGC_GAIN_2 = 0x13,
+ AGC_GAIN_1 = 0x14,
+ AGC_GAIN_0 = 0x15,
+ FREQ_OFFSET_2 = 0x17,
+ FREQ_OFFSET_1 = 0x18,
+ FREQ_OFFSET_0 = 0x19,
+ TIMING_OFFSET_1 = 0x1A,
+ TIMING_OFFSET_0 = 0x1B,
+ CHAN_FREQ_1 = 0x1C,
+ CHAN_FREQ_0 = 0x1D,
+ TPS_RECEIVED_1 = 0x1E,
+ TPS_RECEIVED_0 = 0x1F,
+ TPS_CURRENT_1 = 0x20,
+ TPS_CURRENT_0 = 0x21,
+ TPS_CELL_ID_1 = 0x22,
+ TPS_CELL_ID_0 = 0x23,
+ TPS_MISC_DATA_2 = 0x24,
+ TPS_MISC_DATA_1 = 0x25,
+ TPS_MISC_DATA_0 = 0x26,
+ RESET = 0x50,
+ TPS_GIVEN_1 = 0x51,
+ TPS_GIVEN_0 = 0x52,
+ ACQ_CTL = 0x53,
+ TRL_NOMINAL_RATE_1 = 0x54,
+ TRL_NOMINAL_RATE_0 = 0x55,
+ INPUT_FREQ_1 = 0x56,
+ INPUT_FREQ_0 = 0x57,
+ TUNER_ADDR = 0x58,
+ CHAN_START_1 = 0x59,
+ CHAN_START_0 = 0x5A,
+ CONT_1 = 0x5B,
+ CONT_0 = 0x5C,
+ TUNER_GO = 0x5D,
+ STATUS_EN_0 = 0x5F,
+ STATUS_EN_1 = 0x60,
+ INTERRUPT_EN_0 = 0x61,
+ INTERRUPT_EN_1 = 0x62,
+ INTERRUPT_EN_2 = 0x63,
+ INTERRUPT_EN_3 = 0x64,
+ AGC_TARGET = 0x67,
+ AGC_CTL = 0x68,
+ CAPT_RANGE = 0x75,
+ SNR_SELECT_1 = 0x79,
+ SNR_SELECT_0 = 0x7A,
+ RS_ERR_PER_1 = 0x7C,
+ RS_ERR_PER_0 = 0x7D,
+ CHIP_ID = 0x7F,
+ CHAN_STOP_1 = 0x80,
+ CHAN_STOP_0 = 0x81,
+ CHAN_STEP_1 = 0x82,
+ CHAN_STEP_0 = 0x83,
+ FEC_LOCK_TIME = 0x85,
+ OFDM_LOCK_TIME = 0x86,
+ ACQ_DELAY = 0x87,
+ SCAN_CTL = 0x88,
+ CLOCK_CTL = 0x89,
+ CONFIG = 0x8A,
+ MCLK_RATIO = 0x8B,
+ GPP_CTL = 0x8C,
+ ADC_CTL_1 = 0x8E,
+ ADC_CTL_0 = 0x8F
+};
+
+/* here we assume 1/6MHz == 166.66kHz stepsize */
+#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
+
+#endif /* _MT352_PRIV_ */
diff --git a/drivers/media/dvb/frontends/nxt2002.c b/drivers/media/dvb/frontends/nxt2002.c
new file mode 100644
index 000000000000..4743aa17406e
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt2002.c
@@ -0,0 +1,705 @@
+/*
+ Support for B2C2/BBTI Technisat Air2PC - ATSC
+
+ Copyright (C) 2004 Taylor Jacob <rtjacob@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.
+
+*/
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
+#define CRC_CCIT_MASK 0x1021
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "nxt2002.h"
+
+struct nxt2002_state {
+
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ const struct nxt2002_config* config;
+ struct dvb_frontend frontend;
+
+ /* demodulator private data */
+ u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "nxt2002: " args); \
+ } while (0)
+
+static int i2c_writebytes (struct nxt2002_state* state, u8 reg, u8 *buf, u8 len)
+{
+ /* probbably a much better way or doing this */
+ u8 buf2 [256],x;
+ int err;
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 };
+
+ buf2[0] = reg;
+ for (x = 0 ; x < len ; x++)
+ buf2[x+1] = buf[x];
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ printk ("%s: i2c write error (addr %02x, err == %i)\n",
+ __FUNCTION__, state->config->demod_address, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static u8 i2c_readbytes (struct nxt2002_state* state, u8 reg, u8* buf, u8 len)
+{
+ u8 reg2 [] = { reg };
+
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+ int err;
+
+ if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) {
+ printk ("%s: i2c read error (addr %02x, err == %i)\n",
+ __FUNCTION__, state->config->demod_address, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static u16 nxt2002_crc(u16 crc, u8 c)
+{
+
+ u8 i;
+ u16 input = (u16) c & 0xFF;
+
+ input<<=8;
+ for(i=0 ;i<8 ;i++) {
+ if((crc ^ input) & 0x8000)
+ crc=(crc<<1)^CRC_CCIT_MASK;
+ else
+ crc<<=1;
+ input<<=1;
+ }
+ return crc;
+}
+
+static int nxt2002_writereg_multibyte (struct nxt2002_state* state, u8 reg, u8* data, u8 len)
+{
+ u8 buf;
+ dprintk("%s\n", __FUNCTION__);
+
+ /* set multi register length */
+ i2c_writebytes(state,0x34,&len,1);
+
+ /* set mutli register register */
+ i2c_writebytes(state,0x35,&reg,1);
+
+ /* send the actual data */
+ i2c_writebytes(state,0x36,data,len);
+
+ /* toggle the multireg write bit*/
+ buf = 0x02;
+ i2c_writebytes(state,0x21,&buf,1);
+
+ i2c_readbytes(state,0x21,&buf,1);
+
+ if ((buf & 0x02) == 0)
+ return 0;
+
+ dprintk("Error writing multireg register %02X\n",reg);
+
+ return 0;
+}
+
+static int nxt2002_readreg_multibyte (struct nxt2002_state* state, u8 reg, u8* data, u8 len)
+{
+ u8 len2;
+ dprintk("%s\n", __FUNCTION__);
+
+ /* set multi register length */
+ len2 = len & 0x80;
+ i2c_writebytes(state,0x34,&len2,1);
+
+ /* set mutli register register */
+ i2c_writebytes(state,0x35,&reg,1);
+
+ /* send the actual data */
+ i2c_readbytes(state,reg,data,len);
+
+ return 0;
+}
+
+static void nxt2002_microcontroller_stop (struct nxt2002_state* state)
+{
+ u8 buf[2],counter = 0;
+ dprintk("%s\n", __FUNCTION__);
+
+ buf[0] = 0x80;
+ i2c_writebytes(state,0x22,buf,1);
+
+ while (counter < 20) {
+ i2c_readbytes(state,0x31,buf,1);
+ if (buf[0] & 0x40)
+ return;
+ msleep(10);
+ counter++;
+ }
+
+ dprintk("Timeout waiting for micro to stop.. This is ok after firmware upload\n");
+ return;
+}
+
+static void nxt2002_microcontroller_start (struct nxt2002_state* state)
+{
+ u8 buf;
+ dprintk("%s\n", __FUNCTION__);
+
+ buf = 0x00;
+ i2c_writebytes(state,0x22,&buf,1);
+}
+
+static int nxt2002_writetuner (struct nxt2002_state* state, u8* data)
+{
+ u8 buf,count = 0;
+
+ dprintk("Tuner Bytes: %02X %02X %02X %02X\n",data[0],data[1],data[2],data[3]);
+
+ dprintk("%s\n", __FUNCTION__);
+ /* stop the micro first */
+ nxt2002_microcontroller_stop(state);
+
+ /* set the i2c transfer speed to the tuner */
+ buf = 0x03;
+ i2c_writebytes(state,0x20,&buf,1);
+
+ /* setup to transfer 4 bytes via i2c */
+ buf = 0x04;
+ i2c_writebytes(state,0x34,&buf,1);
+
+ /* write actual tuner bytes */
+ i2c_writebytes(state,0x36,data,4);
+
+ /* set tuner i2c address */
+ buf = 0xC2;
+ i2c_writebytes(state,0x35,&buf,1);
+
+ /* write UC Opmode to begin transfer */
+ buf = 0x80;
+ i2c_writebytes(state,0x21,&buf,1);
+
+ while (count < 20) {
+ i2c_readbytes(state,0x21,&buf,1);
+ if ((buf & 0x80)== 0x00)
+ return 0;
+ msleep(100);
+ count++;
+ }
+
+ printk("nxt2002: timeout error writing tuner\n");
+ return 0;
+}
+
+static void nxt2002_agc_reset(struct nxt2002_state* state)
+{
+ u8 buf;
+ dprintk("%s\n", __FUNCTION__);
+
+ buf = 0x08;
+ i2c_writebytes(state,0x08,&buf,1);
+
+ buf = 0x00;
+ i2c_writebytes(state,0x08,&buf,1);
+
+ return;
+}
+
+static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ u8 buf[256],written = 0,chunkpos = 0;
+ u16 rambase,position,crc = 0;
+
+ dprintk("%s\n", __FUNCTION__);
+ dprintk("Firmware is %zu bytes\n",fw->size);
+
+ /* Get the RAM base for this nxt2002 */
+ i2c_readbytes(state,0x10,buf,1);
+
+ if (buf[0] & 0x10)
+ rambase = 0x1000;
+ else
+ rambase = 0x0000;
+
+ dprintk("rambase on this nxt2002 is %04X\n",rambase);
+
+ /* Hold the micro in reset while loading firmware */
+ buf[0] = 0x80;
+ i2c_writebytes(state,0x2B,buf,1);
+
+ for (position = 0; position < fw->size ; position++) {
+ if (written == 0) {
+ crc = 0;
+ chunkpos = 0x28;
+ buf[0] = ((rambase + position) >> 8);
+ buf[1] = (rambase + position) & 0xFF;
+ buf[2] = 0x81;
+ /* write starting address */
+ i2c_writebytes(state,0x29,buf,3);
+ }
+ written++;
+ chunkpos++;
+
+ if ((written % 4) == 0)
+ i2c_writebytes(state,chunkpos,&fw->data[position-3],4);
+
+ crc = nxt2002_crc(crc,fw->data[position]);
+
+ if ((written == 255) || (position+1 == fw->size)) {
+ /* write remaining bytes of firmware */
+ i2c_writebytes(state, chunkpos+4-(written %4),
+ &fw->data[position-(written %4) + 1],
+ written %4);
+ buf[0] = crc << 8;
+ buf[1] = crc & 0xFF;
+
+ /* write crc */
+ i2c_writebytes(state,0x2C,buf,2);
+
+ /* do a read to stop things */
+ i2c_readbytes(state,0x2A,buf,1);
+
+ /* set transfer mode to complete */
+ buf[0] = 0x80;
+ i2c_writebytes(state,0x2B,buf,1);
+
+ written = 0;
+ }
+ }
+
+ printk ("done.\n");
+ return 0;
+};
+
+static int nxt2002_setup_frontend_parameters (struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ u32 freq = 0;
+ u16 tunerfreq = 0;
+ u8 buf[4];
+
+ freq = 44000 + ( p->frequency / 1000 );
+
+ dprintk("freq = %d p->frequency = %d\n",freq,p->frequency);
+
+ tunerfreq = freq * 24/4000;
+
+ buf[0] = (tunerfreq >> 8) & 0x7F;
+ buf[1] = (tunerfreq & 0xFF);
+
+ if (p->frequency <= 214000000) {
+ buf[2] = 0x84 + (0x06 << 3);
+ buf[3] = (p->frequency <= 172000000) ? 0x01 : 0x02;
+ } else if (p->frequency <= 721000000) {
+ buf[2] = 0x84 + (0x07 << 3);
+ buf[3] = (p->frequency <= 467000000) ? 0x02 : 0x08;
+ } else if (p->frequency <= 841000000) {
+ buf[2] = 0x84 + (0x0E << 3);
+ buf[3] = 0x08;
+ } else {
+ buf[2] = 0x84 + (0x0F << 3);
+ buf[3] = 0x02;
+ }
+
+ /* write frequency information */
+ nxt2002_writetuner(state,buf);
+
+ /* reset the agc now that tuning has been completed */
+ nxt2002_agc_reset(state);
+
+
+
+ /* set target power level */
+ switch (p->u.vsb.modulation) {
+ case QAM_64:
+ case QAM_256:
+ buf[0] = 0x74;
+ break;
+ case VSB_8:
+ buf[0] = 0x70;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ i2c_writebytes(state,0x42,buf,1);
+
+ /* configure sdm */
+ buf[0] = 0x87;
+ i2c_writebytes(state,0x57,buf,1);
+
+ /* write sdm1 input */
+ buf[0] = 0x10;
+ buf[1] = 0x00;
+ nxt2002_writereg_multibyte(state,0x58,buf,2);
+
+ /* write sdmx input */
+ switch (p->u.vsb.modulation) {
+ case QAM_64:
+ buf[0] = 0x68;
+ break;
+ case QAM_256:
+ buf[0] = 0x64;
+ break;
+ case VSB_8:
+ buf[0] = 0x60;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ buf[1] = 0x00;
+ nxt2002_writereg_multibyte(state,0x5C,buf,2);
+
+ /* write adc power lpf fc */
+ buf[0] = 0x05;
+ i2c_writebytes(state,0x43,buf,1);
+
+ /* write adc power lpf fc */
+ buf[0] = 0x05;
+ i2c_writebytes(state,0x43,buf,1);
+
+ /* write accumulator2 input */
+ buf[0] = 0x80;
+ buf[1] = 0x00;
+ nxt2002_writereg_multibyte(state,0x4B,buf,2);
+
+ /* write kg1 */
+ buf[0] = 0x00;
+ i2c_writebytes(state,0x4D,buf,1);
+
+ /* write sdm12 lpf fc */
+ buf[0] = 0x44;
+ i2c_writebytes(state,0x55,buf,1);
+
+ /* write agc control reg */
+ buf[0] = 0x04;
+ i2c_writebytes(state,0x41,buf,1);
+
+ /* write agc ucgp0 */
+ switch (p->u.vsb.modulation) {
+ case QAM_64:
+ buf[0] = 0x02;
+ break;
+ case QAM_256:
+ buf[0] = 0x03;
+ break;
+ case VSB_8:
+ buf[0] = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ i2c_writebytes(state,0x30,buf,1);
+
+ /* write agc control reg */
+ buf[0] = 0x00;
+ i2c_writebytes(state,0x41,buf,1);
+
+ /* write accumulator2 input */
+ buf[0] = 0x80;
+ buf[1] = 0x00;
+ nxt2002_writereg_multibyte(state,0x49,buf,2);
+ nxt2002_writereg_multibyte(state,0x4B,buf,2);
+
+ /* write agc control reg */
+ buf[0] = 0x04;
+ i2c_writebytes(state,0x41,buf,1);
+
+ nxt2002_microcontroller_start(state);
+
+ /* adjacent channel detection should be done here, but I don't
+ have any stations with this need so I cannot test it */
+
+ return 0;
+}
+
+static int nxt2002_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ u8 lock;
+ i2c_readbytes(state,0x31,&lock,1);
+
+ *status = 0;
+ if (lock & 0x20) {
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_LOCK;
+ }
+ return 0;
+}
+
+static int nxt2002_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ u8 b[3];
+
+ nxt2002_readreg_multibyte(state,0xE6,b,3);
+
+ *ber = ((b[0] << 8) + b[1]) * 8;
+
+ return 0;
+}
+
+static int nxt2002_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ u8 b[2];
+ u16 temp = 0;
+
+ /* setup to read cluster variance */
+ b[0] = 0x00;
+ i2c_writebytes(state,0xA1,b,1);
+
+ /* get multreg val */
+ nxt2002_readreg_multibyte(state,0xA6,b,2);
+
+ temp = (b[0] << 8) | b[1];
+ *strength = ((0x7FFF - temp) & 0x0FFF) * 16;
+
+ return 0;
+}
+
+static int nxt2002_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ u8 b[2];
+ u16 temp = 0, temp2;
+ u32 snrdb = 0;
+
+ /* setup to read cluster variance */
+ b[0] = 0x00;
+ i2c_writebytes(state,0xA1,b,1);
+
+ /* get multreg val from 0xA6 */
+ nxt2002_readreg_multibyte(state,0xA6,b,2);
+
+ temp = (b[0] << 8) | b[1];
+ temp2 = 0x7FFF - temp;
+
+ /* snr will be in db */
+ if (temp2 > 0x7F00)
+ snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) );
+ else if (temp2 > 0x7EC0)
+ snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) );
+ else if (temp2 > 0x7C00)
+ snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) );
+ else
+ snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) );
+
+ /* the value reported back from the frontend will be FFFF=32db 0000=0db */
+
+ *snr = snrdb * (0xFFFF/32000);
+
+ return 0;
+}
+
+static int nxt2002_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ u8 b[3];
+
+ nxt2002_readreg_multibyte(state,0xE6,b,3);
+ *ucblocks = b[2];
+
+ return 0;
+}
+
+static int nxt2002_sleep(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int nxt2002_init(struct dvb_frontend* fe)
+{
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret;
+ u8 buf[2];
+
+ if (!state->initialised) {
+ /* request the firmware, this will block until someone uploads it */
+ printk("nxt2002: Waiting for firmware upload (%s)...\n", NXT2002_DEFAULT_FIRMWARE);
+ ret = state->config->request_firmware(fe, &fw, NXT2002_DEFAULT_FIRMWARE);
+ printk("nxt2002: Waiting for firmware upload(2)...\n");
+ if (ret) {
+ printk("nxt2002: no firmware upload (timeout or file not found?)\n");
+ return ret;
+ }
+
+ ret = nxt2002_load_firmware(fe, fw);
+ if (ret) {
+ printk("nxt2002: writing firmware to device failed\n");
+ release_firmware(fw);
+ return ret;
+ }
+ printk("nxt2002: firmware upload complete\n");
+
+ /* Put the micro into reset */
+ nxt2002_microcontroller_stop(state);
+
+ /* ensure transfer is complete */
+ buf[0]=0;
+ i2c_writebytes(state,0x2B,buf,1);
+
+ /* Put the micro into reset for real this time */
+ nxt2002_microcontroller_stop(state);
+
+ /* soft reset everything (agc,frontend,eq,fec)*/
+ buf[0] = 0x0F;
+ i2c_writebytes(state,0x08,buf,1);
+ buf[0] = 0x00;
+ i2c_writebytes(state,0x08,buf,1);
+
+ /* write agc sdm configure */
+ buf[0] = 0xF1;
+ i2c_writebytes(state,0x57,buf,1);
+
+ /* write mod output format */
+ buf[0] = 0x20;
+ i2c_writebytes(state,0x09,buf,1);
+
+ /* write fec mpeg mode */
+ buf[0] = 0x7E;
+ buf[1] = 0x00;
+ i2c_writebytes(state,0xE9,buf,2);
+
+ /* write mux selection */
+ buf[0] = 0x00;
+ i2c_writebytes(state,0xCC,buf,1);
+
+ state->initialised = 1;
+ }
+
+ return 0;
+}
+
+static int nxt2002_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 500;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void nxt2002_release(struct dvb_frontend* fe)
+{
+ struct nxt2002_state* state = (struct nxt2002_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops nxt2002_ops;
+
+struct dvb_frontend* nxt2002_attach(const struct nxt2002_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct nxt2002_state* state = NULL;
+ u8 buf [] = {0,0,0,0,0};
+
+ /* allocate memory for the internal state */
+ state = (struct nxt2002_state*) kmalloc(sizeof(struct nxt2002_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &nxt2002_ops, sizeof(struct dvb_frontend_ops));
+ state->initialised = 0;
+
+ /* Check the first 5 registers to ensure this a revision we can handle */
+
+ i2c_readbytes(state, 0x00, buf, 5);
+ if (buf[0] != 0x04) goto error; /* device id */
+ if (buf[1] != 0x02) goto error; /* fab id */
+ if (buf[2] != 0x11) goto error; /* month */
+ if (buf[3] != 0x20) goto error; /* year msb */
+ if (buf[4] != 0x00) goto error; /* year lsb */
+
+ /* 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 nxt2002_ops = {
+
+ .info = {
+ .name = "Nextwave nxt2002 VSB/QAM frontend",
+ .type = FE_ATSC,
+ .frequency_min = 54000000,
+ .frequency_max = 860000000,
+ /* stepsize is just a guess */
+ .frequency_stepsize = 166666,
+ .caps = 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_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256
+ },
+
+ .release = nxt2002_release,
+
+ .init = nxt2002_init,
+ .sleep = nxt2002_sleep,
+
+ .set_frontend = nxt2002_setup_frontend_parameters,
+ .get_tune_settings = nxt2002_get_tune_settings,
+
+ .read_status = nxt2002_read_status,
+ .read_ber = nxt2002_read_ber,
+ .read_signal_strength = nxt2002_read_signal_strength,
+ .read_snr = nxt2002_read_snr,
+ .read_ucblocks = nxt2002_read_ucblocks,
+
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("NXT2002 ATSC (8VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver");
+MODULE_AUTHOR("Taylor Jacob");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nxt2002_attach);
diff --git a/drivers/media/dvb/frontends/nxt2002.h b/drivers/media/dvb/frontends/nxt2002.h
new file mode 100644
index 000000000000..462301f577ee
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt2002.h
@@ -0,0 +1,23 @@
+/*
+ Driver for the Nxt2002 demodulator
+*/
+
+#ifndef NXT2002_H
+#define NXT2002_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct nxt2002_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* request firmware for device */
+ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* nxt2002_attach(const struct nxt2002_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // NXT2002_H
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c
new file mode 100644
index 000000000000..a41f7da8b842
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt6000.c
@@ -0,0 +1,554 @@
+/*
+ NxtWave Communications - NXT6000 demodulator driver
+
+ Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
+ Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au>
+
+ 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/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "nxt6000_priv.h"
+#include "nxt6000.h"
+
+
+
+struct nxt6000_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ /* configuration settings */
+ const struct nxt6000_config* config;
+ struct dvb_frontend frontend;
+};
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+static int nxt6000_writereg(struct nxt6000_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 ret;
+
+ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret);
+
+ return (ret != 1) ? -EFAULT : 0;
+}
+
+static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msgs[] = {
+ {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1},
+ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
+ };
+
+ ret = i2c_transfer(state->i2c, msgs, 2);
+
+ if (ret != 2)
+ dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret);
+
+ return b1[0];
+}
+
+static void nxt6000_reset(struct nxt6000_state* state)
+{
+ u8 val;
+
+ val = nxt6000_readreg(state, OFDM_COR_CTL);
+
+ nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT);
+ nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT);
+}
+
+static int nxt6000_set_bandwidth(struct nxt6000_state* state, fe_bandwidth_t bandwidth)
+{
+ u16 nominal_rate;
+ int result;
+
+ switch (bandwidth) {
+
+ case BANDWIDTH_6_MHZ:
+ nominal_rate = 0x55B7;
+ break;
+
+ case BANDWIDTH_7_MHZ:
+ nominal_rate = 0x6400;
+ break;
+
+ case BANDWIDTH_8_MHZ:
+ nominal_rate = 0x7249;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0)
+ return result;
+
+ return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF);
+}
+
+static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval)
+{
+ switch (guard_interval) {
+
+ case GUARD_INTERVAL_1_32:
+ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+ case GUARD_INTERVAL_1_16:
+ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+ case GUARD_INTERVAL_AUTO:
+ case GUARD_INTERVAL_1_8:
+ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+ case GUARD_INTERVAL_1_4:
+ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03));
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion)
+{
+ switch (inversion) {
+
+ case INVERSION_OFF:
+ return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00);
+
+ case INVERSION_ON:
+ return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV);
+
+ default:
+ return -EINVAL;
+
+ }
+}
+
+static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode)
+{
+ int result;
+
+ switch (transmission_mode) {
+
+ case TRANSMISSION_MODE_2K:
+ if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0)
+ return result;
+
+ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04));
+
+ case TRANSMISSION_MODE_8K:
+ case TRANSMISSION_MODE_AUTO:
+ if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0)
+ return result;
+
+ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04));
+
+ default:
+ return -EINVAL;
+
+ }
+}
+
+static void nxt6000_setup(struct dvb_frontend* fe)
+{
+ struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+
+ nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM);
+ nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01);
+ nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC);
+ nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F));
+ nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02);
+ nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW);
+ nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06);
+ nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31);
+ nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04);
+ nxt6000_writereg(state, CAS_FREQ, 0xBB); /* CHECKME */
+ nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2);
+ nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256);
+ nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49);
+ nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72);
+ nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5);
+ nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2);
+ nxt6000_writereg(state, DIAG_CONFIG, TB_SET);
+
+ if (state->config->clock_inversion)
+ nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION);
+ else
+ nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0);
+
+ nxt6000_writereg(state, TS_FORMAT, 0);
+
+ if (state->config->pll_init) {
+ nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */
+ state->config->pll_init(fe);
+ nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */
+ }
+}
+
+static void nxt6000_dump_status(struct nxt6000_state *state)
+{
+ u8 val;
+
+/*
+ printk("RS_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, RS_COR_STAT));
+ printk("VIT_SYNC_STATUS: 0x%02X\n", nxt6000_readreg(fe, VIT_SYNC_STATUS));
+ printk("OFDM_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_COR_STAT));
+ printk("OFDM_SYR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_SYR_STAT));
+ printk("OFDM_TPS_RCVD_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_1));
+ printk("OFDM_TPS_RCVD_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_2));
+ printk("OFDM_TPS_RCVD_3: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_3));
+ printk("OFDM_TPS_RCVD_4: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_4));
+ printk("OFDM_TPS_RESERVED_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_1));
+ printk("OFDM_TPS_RESERVED_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_2));
+*/
+ printk("NXT6000 status:");
+
+ val = nxt6000_readreg(state, RS_COR_STAT);
+
+ printk(" DATA DESCR LOCK: %d,", val & 0x01);
+ printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01);
+
+ val = nxt6000_readreg(state, VIT_SYNC_STATUS);
+
+ printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01);
+
+ switch ((val >> 4) & 0x07) {
+
+ case 0x00:
+ printk(" VITERBI CODERATE: 1/2,");
+ break;
+
+ case 0x01:
+ printk(" VITERBI CODERATE: 2/3,");
+ break;
+
+ case 0x02:
+ printk(" VITERBI CODERATE: 3/4,");
+ break;
+
+ case 0x03:
+ printk(" VITERBI CODERATE: 5/6,");
+ break;
+
+ case 0x04:
+ printk(" VITERBI CODERATE: 7/8,");
+ break;
+
+ default:
+ printk(" VITERBI CODERATE: Reserved,");
+
+ }
+
+ val = nxt6000_readreg(state, OFDM_COR_STAT);
+
+ printk(" CHCTrack: %d,", (val >> 7) & 0x01);
+ printk(" TPSLock: %d,", (val >> 6) & 0x01);
+ printk(" SYRLock: %d,", (val >> 5) & 0x01);
+ printk(" AGCLock: %d,", (val >> 4) & 0x01);
+
+ switch (val & 0x0F) {
+
+ case 0x00:
+ printk(" CoreState: IDLE,");
+ break;
+
+ case 0x02:
+ printk(" CoreState: WAIT_AGC,");
+ break;
+
+ case 0x03:
+ printk(" CoreState: WAIT_SYR,");
+ break;
+
+ case 0x04:
+ printk(" CoreState: WAIT_PPM,");
+ break;
+
+ case 0x01:
+ printk(" CoreState: WAIT_TRL,");
+ break;
+
+ case 0x05:
+ printk(" CoreState: WAIT_TPS,");
+ break;
+
+ case 0x06:
+ printk(" CoreState: MONITOR_TPS,");
+ break;
+
+ default:
+ printk(" CoreState: Reserved,");
+
+ }
+
+ val = nxt6000_readreg(state, OFDM_SYR_STAT);
+
+ printk(" SYRLock: %d,", (val >> 4) & 0x01);
+ printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K");
+
+ switch ((val >> 4) & 0x03) {
+
+ case 0x00:
+ printk(" SYRGuard: 1/32,");
+ break;
+
+ case 0x01:
+ printk(" SYRGuard: 1/16,");
+ break;
+
+ case 0x02:
+ printk(" SYRGuard: 1/8,");
+ break;
+
+ case 0x03:
+ printk(" SYRGuard: 1/4,");
+ break;
+ }
+
+ val = nxt6000_readreg(state, OFDM_TPS_RCVD_3);
+
+ switch ((val >> 4) & 0x07) {
+
+ case 0x00:
+ printk(" TPSLP: 1/2,");
+ break;
+
+ case 0x01:
+ printk(" TPSLP: 2/3,");
+ break;
+
+ case 0x02:
+ printk(" TPSLP: 3/4,");
+ break;
+
+ case 0x03:
+ printk(" TPSLP: 5/6,");
+ break;
+
+ case 0x04:
+ printk(" TPSLP: 7/8,");
+ break;
+
+ default:
+ printk(" TPSLP: Reserved,");
+
+ }
+
+ switch (val & 0x07) {
+
+ case 0x00:
+ printk(" TPSHP: 1/2,");
+ break;
+
+ case 0x01:
+ printk(" TPSHP: 2/3,");
+ break;
+
+ case 0x02:
+ printk(" TPSHP: 3/4,");
+ break;
+
+ case 0x03:
+ printk(" TPSHP: 5/6,");
+ break;
+
+ case 0x04:
+ printk(" TPSHP: 7/8,");
+ break;
+
+ default:
+ printk(" TPSHP: Reserved,");
+
+ }
+
+ val = nxt6000_readreg(state, OFDM_TPS_RCVD_4);
+
+ printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K");
+
+ switch ((val >> 4) & 0x03) {
+
+ case 0x00:
+ printk(" TPSGuard: 1/32,");
+ break;
+
+ case 0x01:
+ printk(" TPSGuard: 1/16,");
+ break;
+
+ case 0x02:
+ printk(" TPSGuard: 1/8,");
+ break;
+
+ case 0x03:
+ printk(" TPSGuard: 1/4,");
+ break;
+
+ }
+
+ /* Strange magic required to gain access to RF_AGC_STATUS */
+ nxt6000_readreg(state, RF_AGC_VAL_1);
+ val = nxt6000_readreg(state, RF_AGC_STATUS);
+ val = nxt6000_readreg(state, RF_AGC_STATUS);
+
+ printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01);
+ printk("\n");
+}
+
+static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ u8 core_status;
+ struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+
+ *status = 0;
+
+ core_status = nxt6000_readreg(state, OFDM_COR_STAT);
+
+ if (core_status & AGCLOCKED)
+ *status |= FE_HAS_SIGNAL;
+
+ if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK)
+ *status |= FE_HAS_CARRIER;
+
+ if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC)
+ *status |= FE_HAS_VITERBI;
+
+ if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS)
+ *status |= FE_HAS_SYNC;
+
+ if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)))
+ *status |= FE_HAS_LOCK;
+
+ if (debug)
+ nxt6000_dump_status(state);
+
+ return 0;
+}
+
+static int nxt6000_init(struct dvb_frontend* fe)
+{
+ struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+
+ nxt6000_reset(state);
+ nxt6000_setup(fe);
+
+ return 0;
+}
+
+static int nxt6000_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *param)
+{
+ struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+ int result;
+
+ nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */
+ state->config->pll_set(fe, param);
+ nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */
+
+ if ((result = nxt6000_set_bandwidth(state, param->u.ofdm.bandwidth)) < 0)
+ return result;
+ if ((result = nxt6000_set_guard_interval(state, param->u.ofdm.guard_interval)) < 0)
+ return result;
+ if ((result = nxt6000_set_transmission_mode(state, param->u.ofdm.transmission_mode)) < 0)
+ return result;
+ if ((result = nxt6000_set_inversion(state, param->inversion)) < 0)
+ return result;
+
+ return 0;
+}
+
+static void nxt6000_release(struct dvb_frontend* fe)
+{
+ struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops nxt6000_ops;
+
+struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct nxt6000_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct nxt6000_state*) kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops));
+
+ /* check if the demod is there */
+ if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) 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 nxt6000_ops = {
+
+ .info = {
+ .name = "NxtWave NXT6000 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 0,
+ .frequency_max = 863250000,
+ .frequency_stepsize = 62500,
+ /*.frequency_tolerance = *//* FIXME: 12% of SR */
+ .symbol_rate_min = 0, /* FIXME */
+ .symbol_rate_max = 9360000, /* FIXME */
+ .symbol_rate_tolerance = 4000,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = nxt6000_release,
+
+ .init = nxt6000_init,
+
+ .set_frontend = nxt6000_set_frontend,
+
+ .read_status = nxt6000_read_status,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver");
+MODULE_AUTHOR("Florian Schirmer");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nxt6000_attach);
diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb/frontends/nxt6000.h
new file mode 100644
index 000000000000..b7d9bead3002
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt6000.h
@@ -0,0 +1,43 @@
+/*
+ NxtWave Communications - NXT6000 demodulator driver
+
+ Copyright (C) 2002-2003 Florian Schirmer <jolt@tuxbox.org>
+ Copyright (C) 2003 Paul Andreassen <paul@andreassen.com.au>
+
+ 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 NXT6000_H
+#define NXT6000_H
+
+#include <linux/dvb/frontend.h>
+
+struct nxt6000_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* should clock inversion be used? */
+ u8 clock_inversion:1;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // NXT6000_H
diff --git a/drivers/media/dvb/frontends/nxt6000_priv.h b/drivers/media/dvb/frontends/nxt6000_priv.h
new file mode 100644
index 000000000000..64b1a89b2a22
--- /dev/null
+++ b/drivers/media/dvb/frontends/nxt6000_priv.h
@@ -0,0 +1,265 @@
+/*
+ * Public Include File for DRV6000 users
+ * (ie. NxtWave Communications - NXT6000 demodulator driver)
+ *
+ * Copyright (C) 2001 NxtWave Communications, Inc.
+ *
+ */
+
+/* Nxt6000 Register Addresses and Bit Masks */
+
+/* Maximum Register Number */
+#define MAXNXT6000REG (0x9A)
+
+/* 0x1B A_VIT_BER_0 aka 0x3A */
+#define A_VIT_BER_0 (0x1B)
+
+/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */
+#define A_VIT_BER_TIMER_0 (0x1D)
+
+/* 0x21 RS_COR_STAT */
+#define RS_COR_STAT (0x21)
+#define RSCORESTATUS (0x03)
+
+/* 0x22 RS_COR_INTEN */
+#define RS_COR_INTEN (0x22)
+
+/* 0x23 RS_COR_INSTAT */
+#define RS_COR_INSTAT (0x23)
+#define INSTAT_ERROR (0x04)
+#define LOCK_LOSS_BITS (0x03)
+
+/* 0x24 RS_COR_SYNC_PARAM */
+#define RS_COR_SYNC_PARAM (0x24)
+#define SYNC_PARAM (0x03)
+
+/* 0x25 BER_CTRL */
+#define BER_CTRL (0x25)
+#define BER_ENABLE (0x02)
+#define BER_RESET (0x01)
+
+/* 0x26 BER_PAY */
+#define BER_PAY (0x26)
+
+/* 0x27 BER_PKT_L */
+#define BER_PKT_L (0x27)
+#define BER_PKTOVERFLOW (0x80)
+
+/* 0x30 VIT_COR_CTL */
+#define VIT_COR_CTL (0x30)
+#define BER_CONTROL (0x02)
+#define VIT_COR_MASK (0x82)
+#define VIT_COR_RESYNC (0x80)
+
+
+/* 0x32 VIT_SYNC_STATUS */
+#define VIT_SYNC_STATUS (0x32)
+#define VITINSYNC (0x80)
+
+/* 0x33 VIT_COR_INTEN */
+#define VIT_COR_INTEN (0x33)
+#define GLOBAL_ENABLE (0x80)
+
+/* 0x34 VIT_COR_INTSTAT */
+#define VIT_COR_INTSTAT (0x34)
+#define BER_DONE (0x08)
+#define BER_OVERFLOW (0x10)
+
+ /* 0x38 OFDM_BERTimer *//* Use the alias registers */
+#define A_VIT_BER_TIMER_0 (0x1D)
+
+ /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */
+#define A_VIT_BER_0 (0x1B)
+
+/* 0x40 OFDM_COR_CTL */
+#define OFDM_COR_CTL (0x40)
+#define COREACT (0x20)
+#define HOLDSM (0x10)
+#define WAIT_AGC (0x02)
+#define WAIT_SYR (0x03)
+
+/* 0x41 OFDM_COR_STAT */
+#define OFDM_COR_STAT (0x41)
+#define COR_STATUS (0x0F)
+#define MONITOR_TPS (0x06)
+#define TPSLOCKED (0x40)
+#define AGCLOCKED (0x10)
+
+/* 0x42 OFDM_COR_INTEN */
+#define OFDM_COR_INTEN (0x42)
+#define TPSRCVBAD (0x04)
+#define TPSRCVCHANGED (0x02)
+#define TPSRCVUPDATE (0x01)
+
+/* 0x43 OFDM_COR_INSTAT */
+#define OFDM_COR_INSTAT (0x43)
+
+/* 0x44 OFDM_COR_MODEGUARD */
+#define OFDM_COR_MODEGUARD (0x44)
+#define FORCEMODE (0x08)
+#define FORCEMODE8K (0x04)
+
+/* 0x45 OFDM_AGC_CTL */
+#define OFDM_AGC_CTL (0x45)
+#define INITIAL_AGC_BW (0x08)
+#define AGCNEG (0x02)
+#define AGCLAST (0x10)
+
+/* 0x48 OFDM_AGC_TARGET */
+#define OFDM_AGC_TARGET (0x48)
+#define OFDM_AGC_TARGET_DEFAULT (0x28)
+#define OFDM_AGC_TARGET_IMPULSE (0x38)
+
+/* 0x49 OFDM_AGC_GAIN_1 */
+#define OFDM_AGC_GAIN_1 (0x49)
+
+/* 0x4B OFDM_ITB_CTL */
+#define OFDM_ITB_CTL (0x4B)
+#define ITBINV (0x01)
+
+/* 0x4C OFDM_ITB_FREQ_1 */
+#define OFDM_ITB_FREQ_1 (0x4C)
+
+/* 0x4D OFDM_ITB_FREQ_2 */
+#define OFDM_ITB_FREQ_2 (0x4D)
+
+/* 0x4E OFDM_CAS_CTL */
+#define OFDM_CAS_CTL (0x4E)
+#define ACSDIS (0x40)
+#define CCSEN (0x80)
+
+/* 0x4F CAS_FREQ */
+#define CAS_FREQ (0x4F)
+
+/* 0x51 OFDM_SYR_CTL */
+#define OFDM_SYR_CTL (0x51)
+#define SIXTH_ENABLE (0x80)
+#define SYR_TRACKING_DISABLE (0x01)
+
+/* 0x52 OFDM_SYR_STAT */
+#define OFDM_SYR_STAT (0x52)
+#define GI14_2K_SYR_LOCK (0x13)
+#define GI14_8K_SYR_LOCK (0x17)
+#define GI14_SYR_LOCK (0x10)
+
+/* 0x55 OFDM_SYR_OFFSET_1 */
+#define OFDM_SYR_OFFSET_1 (0x55)
+
+/* 0x56 OFDM_SYR_OFFSET_2 */
+#define OFDM_SYR_OFFSET_2 (0x56)
+
+/* 0x58 OFDM_SCR_CTL */
+#define OFDM_SCR_CTL (0x58)
+#define SYR_ADJ_DECAY_MASK (0x70)
+#define SYR_ADJ_DECAY (0x30)
+
+/* 0x59 OFDM_PPM_CTL_1 */
+#define OFDM_PPM_CTL_1 (0x59)
+#define PPMMAX_MASK (0x30)
+#define PPM256 (0x30)
+
+/* 0x5B OFDM_TRL_NOMINALRATE_1 */
+#define OFDM_TRL_NOMINALRATE_1 (0x5B)
+
+/* 0x5C OFDM_TRL_NOMINALRATE_2 */
+#define OFDM_TRL_NOMINALRATE_2 (0x5C)
+
+/* 0x5D OFDM_TRL_TIME_1 */
+#define OFDM_TRL_TIME_1 (0x5D)
+
+/* 0x60 OFDM_CRL_FREQ_1 */
+#define OFDM_CRL_FREQ_1 (0x60)
+
+/* 0x63 OFDM_CHC_CTL_1 */
+#define OFDM_CHC_CTL_1 (0x63)
+#define MANMEAN1 (0xF0);
+#define CHCFIR (0x01)
+
+/* 0x64 OFDM_CHC_SNR */
+#define OFDM_CHC_SNR (0x64)
+
+/* 0x65 OFDM_BDI_CTL */
+#define OFDM_BDI_CTL (0x65)
+#define LP_SELECT (0x02)
+
+/* 0x67 OFDM_TPS_RCVD_1 */
+#define OFDM_TPS_RCVD_1 (0x67)
+#define TPSFRAME (0x03)
+
+/* 0x68 OFDM_TPS_RCVD_2 */
+#define OFDM_TPS_RCVD_2 (0x68)
+
+/* 0x69 OFDM_TPS_RCVD_3 */
+#define OFDM_TPS_RCVD_3 (0x69)
+
+/* 0x6A OFDM_TPS_RCVD_4 */
+#define OFDM_TPS_RCVD_4 (0x6A)
+
+/* 0x6B OFDM_TPS_RESERVED_1 */
+#define OFDM_TPS_RESERVED_1 (0x6B)
+
+/* 0x6C OFDM_TPS_RESERVED_2 */
+#define OFDM_TPS_RESERVED_2 (0x6C)
+
+/* 0x73 OFDM_MSC_REV */
+#define OFDM_MSC_REV (0x73)
+
+/* 0x76 OFDM_SNR_CARRIER_2 */
+#define OFDM_SNR_CARRIER_2 (0x76)
+#define MEAN_MASK (0x80)
+#define MEANBIT (0x80)
+
+/* 0x80 ANALOG_CONTROL_0 */
+#define ANALOG_CONTROL_0 (0x80)
+#define POWER_DOWN_ADC (0x40)
+
+/* 0x81 ENABLE_TUNER_IIC */
+#define ENABLE_TUNER_IIC (0x81)
+#define ENABLE_TUNER_BIT (0x01)
+
+/* 0x82 EN_DMD_RACQ */
+#define EN_DMD_RACQ (0x82)
+#define EN_DMD_RACQ_REG_VAL (0x81)
+#define EN_DMD_RACQ_REG_VAL_14 (0x01)
+
+/* 0x84 SNR_COMMAND */
+#define SNR_COMMAND (0x84)
+#define SNRStat (0x80)
+
+/* 0x85 SNRCARRIERNUMBER_LSB */
+#define SNRCARRIERNUMBER_LSB (0x85)
+
+/* 0x87 SNRMINTHRESHOLD_LSB */
+#define SNRMINTHRESHOLD_LSB (0x87)
+
+/* 0x89 SNR_PER_CARRIER_LSB */
+#define SNR_PER_CARRIER_LSB (0x89)
+
+/* 0x8B SNRBELOWTHRESHOLD_LSB */
+#define SNRBELOWTHRESHOLD_LSB (0x8B)
+
+/* 0x91 RF_AGC_VAL_1 */
+#define RF_AGC_VAL_1 (0x91)
+
+/* 0x92 RF_AGC_STATUS */
+#define RF_AGC_STATUS (0x92)
+
+/* 0x98 DIAG_CONFIG */
+#define DIAG_CONFIG (0x98)
+#define DIAG_MASK (0x70)
+#define TB_SET (0x10)
+#define TRAN_SELECT (0x07)
+#define SERIAL_SELECT (0x01)
+
+/* 0x99 SUB_DIAG_MODE_SEL */
+#define SUB_DIAG_MODE_SEL (0x99)
+#define CLKINVERSION (0x01)
+
+/* 0x9A TS_FORMAT */
+#define TS_FORMAT (0x9A)
+#define ERROR_SENSE (0x08)
+#define VALID_SENSE (0x04)
+#define SYNC_SENSE (0x02)
+#define GATED_CLOCK (0x01)
+
+#define NXT6000ASICDEVICE (0x0b)
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c
new file mode 100644
index 000000000000..df5dee7760a3
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51132.c
@@ -0,0 +1,628 @@
+/*
+ * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ * Based on code from Jack Kelliher (kelliher@xmission.com)
+ * Copyright (C) 2002 & pcHDTV, inc.
+ *
+ * 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.
+ *
+*/
+
+/*
+ * This driver needs two external firmware files. Please copy
+ * "dvb-fe-or51132-vsb.fw" and "dvb-fe-or51132-qam.fw" to
+ * /usr/lib/hotplug/firmware/ or /lib/firmware/
+ * (depending on configuration of firmware hotplug).
+ */
+#define OR51132_VSB_FIRMWARE "dvb-fe-or51132-vsb.fw"
+#define OR51132_QAM_FIRMWARE "dvb-fe-or51132-qam.fw"
+
+#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 "or51132.h"
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "or51132: " args); \
+ } while (0)
+
+
+struct or51132_state
+{
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+
+ /* Configuration settings */
+ const struct or51132_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 or51132_state* state, u8 reg, u8 *buf, int len)
+{
+ int err;
+ struct i2c_msg msg;
+ msg.addr = reg;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = buf;
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "or51132: i2c_writebytes error (addr %02x, err == %i)\n", reg, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static u8 i2c_readbytes (struct or51132_state* state, u8 reg, u8* buf, int len)
+{
+ int err;
+ struct i2c_msg msg;
+ msg.addr = reg;
+ msg.flags = I2C_M_RD;
+ msg.len = len;
+ msg.buf = buf;
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "or51132: i2c_readbytes error (addr %02x, err == %i)\n", reg, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ static u8 run_buf[] = {0x7F,0x01};
+ static u8 get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
+ u8 rec_buf[14];
+ u8 cmd_buf[14];
+ u32 firmwareAsize, firmwareBsize;
+ int i,ret;
+
+ dprintk("Firmware is %Zd bytes\n",fw->size);
+
+ /* Get size of firmware A and B */
+ firmwareAsize = le32_to_cpu(*((u32*)fw->data));
+ dprintk("FirmwareA is %i bytes\n",firmwareAsize);
+ firmwareBsize = le32_to_cpu(*((u32*)(fw->data+4)));
+ dprintk("FirmwareB is %i bytes\n",firmwareBsize);
+
+ /* Upload firmware */
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ &fw->data[8],firmwareAsize))) {
+ printk(KERN_WARNING "or51132: load_firmware error 1\n");
+ return ret;
+ }
+ msleep(1); /* 1ms */
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ &fw->data[8+firmwareAsize],firmwareBsize))) {
+ printk(KERN_WARNING "or51132: load_firmware error 2\n");
+ return ret;
+ }
+ msleep(1); /* 1ms */
+
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ run_buf,2))) {
+ printk(KERN_WARNING "or51132: load_firmware error 3\n");
+ return ret;
+ }
+
+ /* Wait at least 5 msec */
+ msleep(20); /* 10ms */
+
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ run_buf,2))) {
+ printk(KERN_WARNING "or51132: load_firmware error 4\n");
+ return ret;
+ }
+
+ /* 50ms for operation to begin */
+ msleep(50);
+
+ /* Read back ucode version to besure we loaded correctly and are really up and running */
+ /* Get uCode version */
+ cmd_buf[0] = 0x10;
+ cmd_buf[1] = 0x10;
+ cmd_buf[2] = 0x00;
+ cmd_buf[3] = 0x00;
+ msleep(20); /* 20ms */
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,3))) {
+ printk(KERN_WARNING "or51132: load_firmware error a\n");
+ return ret;
+ }
+
+ cmd_buf[0] = 0x04;
+ cmd_buf[1] = 0x17;
+ cmd_buf[2] = 0x00;
+ cmd_buf[3] = 0x00;
+ msleep(20); /* 20ms */
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,2))) {
+ printk(KERN_WARNING "or51132: load_firmware error b\n");
+ return ret;
+ }
+
+ cmd_buf[0] = 0x00;
+ cmd_buf[1] = 0x00;
+ cmd_buf[2] = 0x00;
+ cmd_buf[3] = 0x00;
+ msleep(20); /* 20ms */
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,2))) {
+ printk(KERN_WARNING "or51132: load_firmware error c\n");
+ return ret;
+ }
+
+ for(i=0;i<4;i++) {
+ msleep(20); /* 20ms */
+ get_ver_buf[4] = i+1;
+ if ((ret = i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[i*2],2))) {
+ printk(KERN_WARNING
+ "or51132: load_firmware error d - %d\n",i);
+ return ret;
+ }
+ }
+
+ printk(KERN_WARNING
+ "or51132: Version: %02X%02X%02X%02X-%02X%02X%02X%02X (%02X%01X-%01X-%02X%01X-%01X)\n",
+ rec_buf[1],rec_buf[0],rec_buf[3],rec_buf[2],
+ rec_buf[5],rec_buf[4],rec_buf[7],rec_buf[6],
+ rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f,
+ rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f);
+
+ cmd_buf[0] = 0x10;
+ cmd_buf[1] = 0x00;
+ cmd_buf[2] = 0x00;
+ cmd_buf[3] = 0x00;
+ msleep(20); /* 20ms */
+ if ((ret = i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,3))) {
+ printk(KERN_WARNING "or51132: load_firmware error e\n");
+ return ret;
+ }
+ return 0;
+};
+
+static int or51132_init(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int or51132_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int or51132_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int or51132_sleep(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int or51132_setmode(struct dvb_frontend* fe)
+{
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ unsigned char cmd_buf[4];
+
+ dprintk("setmode %d\n",(int)state->current_modulation);
+ /* set operation mode in Receiver 1 register; */
+ cmd_buf[0] = 0x04;
+ cmd_buf[1] = 0x01;
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
+ case QAM_AUTO:
+ /* Auto-deinterleave; MPEG ser, MPEG2tr, phase noise-high*/
+ cmd_buf[2] = 0x5F;
+ break;
+ case VSB_8:
+ /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high*/
+ cmd_buf[2] = 0x50;
+ break;
+ default:
+ printk("setmode:Modulation set to unsupported value\n");
+ };
+ cmd_buf[3] = 0x00;
+ if (i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,3)) {
+ printk(KERN_WARNING "or51132: set_mode error 1\n");
+ return -1;
+ }
+ dprintk("or51132: set #1 to %02x\n", cmd_buf[2]);
+
+ /* Set operation mode in Receiver 6 register */
+ cmd_buf[0] = 0x1C;
+ switch (state->current_modulation) {
+ case QAM_AUTO:
+ /* REC MODE Normal Carrier Lock */
+ cmd_buf[1] = 0x00;
+ /* Channel MODE Auto QAM64/256 */
+ cmd_buf[2] = 0x4f;
+ break;
+ case QAM_256:
+ /* REC MODE Normal Carrier Lock */
+ cmd_buf[1] = 0x00;
+ /* Channel MODE QAM256 */
+ cmd_buf[2] = 0x45;
+ break;
+ case QAM_64:
+ /* REC MODE Normal Carrier Lock */
+ cmd_buf[1] = 0x00;
+ /* Channel MODE QAM64 */
+ cmd_buf[2] = 0x43;
+ break;
+ case VSB_8:
+ /* REC MODE inv IF spectrum, Normal */
+ cmd_buf[1] = 0x03;
+ /* Channel MODE ATSC/VSB8 */
+ cmd_buf[2] = 0x06;
+ break;
+ default:
+ printk("setmode: Modulation set to unsupported value\n");
+ };
+ cmd_buf[3] = 0x00;
+ msleep(20); /* 20ms */
+ if (i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,3)) {
+ printk(KERN_WARNING "or51132: set_mode error 2\n");
+ return -1;
+ }
+ dprintk("or51132: set #6 to 0x%02x%02x\n", cmd_buf[1], cmd_buf[2]);
+
+ return 0;
+}
+
+static int or51132_set_parameters(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *param)
+{
+ int ret;
+ u8 buf[4];
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ const struct firmware *fw;
+
+ /* Change only if we are actually changing the modulation */
+ if (state->current_modulation != param->u.vsb.modulation) {
+ switch(param->u.vsb.modulation) {
+ case VSB_8:
+ dprintk("set_parameters VSB MODE\n");
+ printk("or51132: Waiting for firmware upload(%s)...\n",
+ OR51132_VSB_FIRMWARE);
+ ret = request_firmware(&fw, OR51132_VSB_FIRMWARE,
+ &state->i2c->dev);
+ if (ret){
+ printk(KERN_WARNING "or51132: No firmware up"
+ "loaded(timeout or file not found?)\n");
+ return ret;
+ }
+ /* Set non-punctured clock for VSB */
+ state->config->set_ts_params(fe, 0);
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ case QAM_256:
+ dprintk("set_parameters QAM MODE\n");
+ printk("or51132: Waiting for firmware upload(%s)...\n",
+ OR51132_QAM_FIRMWARE);
+ ret = request_firmware(&fw, OR51132_QAM_FIRMWARE,
+ &state->i2c->dev);
+ if (ret){
+ printk(KERN_WARNING "or51132: No firmware up"
+ "loaded(timeout or file not found?)\n");
+ return ret;
+ }
+ /* Set punctured clock for QAM */
+ state->config->set_ts_params(fe, 1);
+ break;
+ default:
+ printk("or51132:Modulation type(%d) UNSUPPORTED\n",
+ param->u.vsb.modulation);
+ return -1;
+ };
+ ret = or51132_load_firmware(fe, fw);
+ release_firmware(fw);
+ if (ret) {
+ printk(KERN_WARNING "or51132: Writing firmware to "
+ "device failed!\n");
+ return ret;
+ }
+ printk("or51132: Firmware upload complete.\n");
+
+ state->current_modulation = param->u.vsb.modulation;
+ or51132_setmode(fe);
+ }
+
+ /* 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("set_parameters tuner bytes: 0x%02x 0x%02x "
+ "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]);
+ if (i2c_writebytes(state, state->config->pll_address ,buf, 4))
+ printk(KERN_WARNING "or51132: set_parameters error "
+ "writing to tuner\n");
+
+ /* Set to current mode */
+ or51132_setmode(fe);
+
+ /* Update current frequency */
+ state->current_frequency = param->frequency;
+ }
+ return 0;
+}
+
+static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ unsigned char rec_buf[2];
+ unsigned char snd_buf[2];
+ *status = 0;
+
+ /* Receiver Status */
+ snd_buf[0]=0x04;
+ snd_buf[1]=0x00;
+ 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;
+ }
+ 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;
+ }
+ dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
+
+ if (rec_buf[1] & 0x01) { /* Receiver Lock */
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_LOCK;
+ }
+ 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};
+
+static unsigned int i20Log10(unsigned short val)
+{
+ unsigned int rntval = 100;
+ unsigned int tmp = val;
+ unsigned int exp = 1;
+
+ while(tmp > 100) {tmp /= 100; exp++;}
+
+ val = (2 * val)/denom[exp];
+ if (exp > 1) rntval = 2000*exp;
+
+ rntval += i100x20log10[val];
+ return rntval;
+}
+
+static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ unsigned char rec_buf[2];
+ unsigned char snd_buf[2];
+ u8 rcvr_stat;
+ u16 snr_equ;
+ int usK;
+
+ 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;
+ }
+ 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;
+ }
+ 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);
+
+ /* Receiver Status */
+ snd_buf[0]=0x04;
+ snd_buf[1]=0x00;
+ 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;
+ }
+ 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;
+ }
+ 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% */
+ *strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000;
+ dprintk("read_signal_strength %i\n",*strength);
+
+ return 0;
+}
+
+static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct or51132_state* state = (struct or51132_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);
+
+ *snr = 0xFFFF - snr_equ;
+ dprintk("read_snr %i\n",*snr);
+
+ return 0;
+}
+
+static int or51132_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings)
+{
+ fe_tune_settings->min_delay_ms = 500;
+ fe_tune_settings->step_size = 0;
+ fe_tune_settings->max_drift = 0;
+
+ return 0;
+}
+
+static void or51132_release(struct dvb_frontend* fe)
+{
+ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops or51132_ops;
+
+struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct or51132_state* state = NULL;
+
+ /* Allocate memory for the internal state */
+ state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* Setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &or51132_ops, sizeof(struct dvb_frontend_ops));
+ 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);
+ return NULL;
+}
+
+static struct dvb_frontend_ops or51132_ops = {
+
+ .info = {
+ .name = "Oren OR51132 VSB/QAM Frontend",
+ .type = FE_ATSC,
+ .frequency_min = 44000000,
+ .frequency_max = 958000000,
+ .frequency_stepsize = 166666,
+ .caps = 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_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO |
+ FE_CAN_8VSB
+ },
+
+ .release = or51132_release,
+
+ .init = or51132_init,
+ .sleep = or51132_sleep,
+
+ .set_frontend = or51132_set_parameters,
+ .get_tune_settings = or51132_get_tune_settings,
+
+ .read_status = or51132_read_status,
+ .read_ber = or51132_read_ber,
+ .read_signal_strength = or51132_read_signal_strength,
+ .read_snr = or51132_read_snr,
+ .read_ucblocks = or51132_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(or51132_attach);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb/frontends/or51132.h
new file mode 100644
index 000000000000..622cdd18381b
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51132.h
@@ -0,0 +1,48 @@
+/*
+ * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.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 OR51132_H
+#define OR51132_H
+
+#include <linux/firmware.h>
+#include <linux/dvb/frontend.h>
+
+struct or51132_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* or51132_attach(const struct or51132_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // OR51132_H
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
new file mode 100644
index 000000000000..ad56a9958404
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -0,0 +1,631 @@
+/*
+ * Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com>
+ *
+ * Based on code from Jack Kelliher (kelliher@xmission.com)
+ * Copyright (C) 2002 & pcHDTV, inc.
+ *
+ * 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.
+ *
+*/
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <asm/byteorder.h>
+
+#include "dvb_frontend.h"
+#include "or51211.h"
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "or51211: " args); \
+ } while (0)
+
+static u8 run_buf[] = {0x7f,0x01};
+static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC
+
+struct or51211_state {
+
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+
+ /* Configuration settings */
+ const struct or51211_config* config;
+
+ struct dvb_frontend frontend;
+ struct bt878* bt;
+
+ /* Demodulator private data */
+ u8 initialized:1;
+
+ /* Tuner private data */
+ u32 current_frequency;
+};
+
+static int i2c_writebytes (struct or51211_state* state, u8 reg, u8 *buf,
+ int len)
+{
+ int err;
+ struct i2c_msg msg;
+ msg.addr = reg;
+ msg.flags = 0;
+ msg.len = len;
+ msg.buf = buf;
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "or51211: i2c_writebytes error "
+ "(addr %02x, err == %i)\n", reg, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static u8 i2c_readbytes (struct or51211_state* state, u8 reg, u8* buf, int len)
+{
+ int err;
+ struct i2c_msg msg;
+ msg.addr = reg;
+ msg.flags = I2C_M_RD;
+ msg.len = len;
+ msg.buf = buf;
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ printk(KERN_WARNING "or51211: i2c_readbytes error "
+ "(addr %02x, err == %i)\n", reg, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int or51211_load_firmware (struct dvb_frontend* fe,
+ const struct firmware *fw)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ u8 tudata[585];
+ int i;
+
+ dprintk("Firmware is %d bytes\n",fw->size);
+
+ /* Get eprom data */
+ tudata[0] = 17;
+ if (i2c_writebytes(state,0x50,tudata,1)) {
+ printk(KERN_WARNING "or51211:load_firmware error eprom addr\n");
+ return -1;
+ }
+ if (i2c_readbytes(state,0x50,&tudata[145],192)) {
+ printk(KERN_WARNING "or51211: load_firmware error eprom\n");
+ return -1;
+ }
+
+ /* Create firmware buffer */
+ for (i = 0; i < 145; i++)
+ tudata[i] = fw->data[i];
+
+ for (i = 0; i < 248; i++)
+ tudata[i+337] = fw->data[145+i];
+
+ state->config->reset(fe);
+
+ if (i2c_writebytes(state,state->config->demod_address,tudata,585)) {
+ printk(KERN_WARNING "or51211: load_firmware error 1\n");
+ return -1;
+ }
+ msleep(1);
+
+ if (i2c_writebytes(state,state->config->demod_address,
+ &fw->data[393],8125)) {
+ printk(KERN_WARNING "or51211: load_firmware error 2\n");
+ return -1;
+ }
+ msleep(1);
+
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ printk(KERN_WARNING "or51211: load_firmware error 3\n");
+ return -1;
+ }
+
+ /* Wait at least 5 msec */
+ msleep(10);
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ printk(KERN_WARNING "or51211: load_firmware error 4\n");
+ return -1;
+ }
+ msleep(10);
+
+ printk("or51211: Done.\n");
+ return 0;
+};
+
+static int or51211_setmode(struct dvb_frontend* fe, int mode)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ u8 rec_buf[14];
+
+ state->config->setmode(fe, mode);
+
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ printk(KERN_WARNING "or51211: setmode error 1\n");
+ return -1;
+ }
+
+ /* Wait at least 5 msec */
+ msleep(10);
+ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) {
+ printk(KERN_WARNING "or51211: setmode error 2\n");
+ return -1;
+ }
+
+ msleep(10);
+
+ /* Set operation mode in Receiver 1 register;
+ * type 1:
+ * data 0x50h Automatic sets receiver channel conditions
+ * Automatic NTSC rejection filter
+ * Enable MPEG serial data output
+ * MPEG2tr
+ * High tuner phase noise
+ * normal +/-150kHz Carrier acquisition range
+ */
+ if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) {
+ printk(KERN_WARNING "or51211: setmode error 3\n");
+ return -1;
+ }
+
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x03;
+ rec_buf[3] = 0x00;
+ msleep(20);
+ if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) {
+ printk(KERN_WARNING "or51211: setmode error 5\n");
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) {
+ printk(KERN_WARNING "or51211: setmode error 6");
+ return -1;
+ }
+ dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]);
+
+ return 0;
+}
+
+static int or51211_set_parameters(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ u32 freq = 0;
+ u16 tunerfreq = 0;
+ u8 buf[4];
+
+ /* Change only if we are actually changing the channel */
+ if (state->current_frequency != param->frequency) {
+ freq = 44000 + (param->frequency/1000);
+ tunerfreq = freq * 16/1000;
+
+ dprintk("set_parameters frequency = %d (tunerfreq = %d)\n",
+ param->frequency,tunerfreq);
+
+ buf[0] = (tunerfreq >> 8) & 0x7F;
+ buf[1] = (tunerfreq & 0xFF);
+ buf[2] = 0x8E;
+
+ if (param->frequency < 157250000) {
+ buf[3] = 0xA0;
+ dprintk("set_parameters VHF low range\n");
+ } else if (param->frequency < 454000000) {
+ buf[3] = 0x90;
+ dprintk("set_parameters VHF high range\n");
+ } else {
+ buf[3] = 0x30;
+ dprintk("set_parameters UHF range\n");
+ }
+ dprintk("set_parameters tuner bytes: 0x%02x 0x%02x "
+ "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]);
+
+ if (i2c_writebytes(state,0xC2>>1,buf,4))
+ printk(KERN_WARNING "or51211:set_parameters error "
+ "writing to tuner\n");
+
+ /* Set to ATSC mode */
+ or51211_setmode(fe,0);
+
+ /* Update current frequency */
+ state->current_frequency = param->frequency;
+ }
+ return 0;
+}
+
+static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ unsigned char rec_buf[2];
+ unsigned char snd_buf[] = {0x04,0x00,0x03,0x00};
+ *status = 0;
+
+ /* Receiver Status */
+ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
+ printk(KERN_WARNING "or51132: read_status write error\n");
+ return -1;
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
+ printk(KERN_WARNING "or51132: read_status read error\n");
+ return -1;
+ }
+ dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]);
+
+ if (rec_buf[0] & 0x01) { /* Receiver Lock */
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_LOCK;
+ }
+ 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};
+
+static unsigned int i20Log10(unsigned short val)
+{
+ unsigned int rntval = 100;
+ unsigned int tmp = val;
+ unsigned int exp = 1;
+
+ while(tmp > 100) {tmp /= 100; exp++;}
+
+ val = (2 * val)/denom[exp];
+ if (exp > 1) rntval = 2000*exp;
+
+ rntval += i100x20log10[val];
+ return rntval;
+}
+
+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];
+ u8 snr_equ;
+
+ /* 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_equ = rec_buf[0] & 0xff;
+
+ /* The value reported back from the frontend will be FFFF=100% 0000=0% */
+ *strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000;
+
+ dprintk("read_signal_strength %i\n",*strength);
+
+ return 0;
+}
+
+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];
+
+ /* 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);
+
+ return 0;
+}
+
+static int or51211_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ *ber = -ENOSYS;
+ return 0;
+}
+
+static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ *ucblocks = -ENOSYS;
+ return 0;
+}
+
+static int or51211_sleep(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int or51211_init(struct dvb_frontend* fe)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ const struct or51211_config* config = state->config;
+ const struct firmware* fw;
+ unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00};
+ unsigned char rec_buf[14];
+ int ret,i;
+
+ if (!state->initialized) {
+ /* Request the firmware, this will block until it uploads */
+ printk(KERN_INFO "or51211: Waiting for firmware upload "
+ "(%s)...\n", OR51211_DEFAULT_FIRMWARE);
+ ret = config->request_firmware(fe, &fw,
+ OR51211_DEFAULT_FIRMWARE);
+ printk(KERN_INFO "or51211:Got Hotplug firmware\n");
+ if (ret) {
+ printk(KERN_WARNING "or51211: No firmware uploaded "
+ "(timeout or file not found?)\n");
+ return ret;
+ }
+
+ ret = or51211_load_firmware(fe, fw);
+ if (ret) {
+ printk(KERN_WARNING "or51211: Writing firmware to "
+ "device failed!\n");
+ release_firmware(fw);
+ return ret;
+ }
+ printk(KERN_INFO "or51211: Firmware upload complete.\n");
+
+ /* Set operation mode in Receiver 1 register;
+ * type 1:
+ * data 0x50h Automatic sets receiver channel conditions
+ * Automatic NTSC rejection filter
+ * Enable MPEG serial data output
+ * MPEG2tr
+ * High tuner phase noise
+ * normal +/-150kHz Carrier acquisition range
+ */
+ if (i2c_writebytes(state,state->config->demod_address,
+ cmd_buf,3)) {
+ printk(KERN_WARNING "or51211: Load DVR Error 5\n");
+ return -1;
+ }
+
+ /* Read back ucode version to besure we loaded correctly */
+ /* and are really up and running */
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x03;
+ rec_buf[3] = 0x00;
+ msleep(30);
+ if (i2c_writebytes(state,state->config->demod_address,
+ rec_buf,3)) {
+ printk(KERN_WARNING "or51211: Load DVR Error A\n");
+ return -1;
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[10],2)) {
+ printk(KERN_WARNING "or51211: Load DVR Error B\n");
+ return -1;
+ }
+
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x01;
+ rec_buf[3] = 0x00;
+ msleep(20);
+ if (i2c_writebytes(state,state->config->demod_address,
+ rec_buf,3)) {
+ printk(KERN_WARNING "or51211: Load DVR Error C\n");
+ return -1;
+ }
+ msleep(3);
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[12],2)) {
+ printk(KERN_WARNING "or51211: Load DVR Error D\n");
+ return -1;
+ }
+
+ for (i = 0; i < 8; i++)
+ rec_buf[i]=0xed;
+
+ for (i = 0; i < 5; i++) {
+ msleep(30);
+ get_ver_buf[4] = i+1;
+ if (i2c_writebytes(state,state->config->demod_address,
+ get_ver_buf,5)) {
+ printk(KERN_WARNING "or51211:Load DVR Error 6"
+ " - %d\n",i);
+ return -1;
+ }
+ msleep(3);
+
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[i*2],2)) {
+ printk(KERN_WARNING "or51211:Load DVR Error 7"
+ " - %d\n",i);
+ return -1;
+ }
+ /* If we didn't receive the right index, try again */
+ if ((int)rec_buf[i*2+1]!=i+1){
+ i--;
+ }
+ }
+ dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n",
+ rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3],
+ rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7],
+ rec_buf[8], rec_buf[9]);
+
+ printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x"
+ " Status %02x\n",
+ rec_buf[2], rec_buf[4],rec_buf[6],
+ rec_buf[12],rec_buf[10]);
+
+ rec_buf[0] = 0x04;
+ rec_buf[1] = 0x00;
+ rec_buf[2] = 0x03;
+ rec_buf[3] = 0x00;
+ msleep(20);
+ if (i2c_writebytes(state,state->config->demod_address,
+ rec_buf,3)) {
+ printk(KERN_WARNING "or51211: Load DVR Error 8\n");
+ return -1;
+ }
+ msleep(20);
+ if (i2c_readbytes(state,state->config->demod_address,
+ &rec_buf[8],2)) {
+ printk(KERN_WARNING "or51211: Load DVR Error 9\n");
+ return -1;
+ }
+ state->initialized = 1;
+ }
+
+ return 0;
+}
+
+static int or51211_get_tune_settings(struct dvb_frontend* fe,
+ struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 500;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void or51211_release(struct dvb_frontend* fe)
+{
+ struct or51211_state* state = fe->demodulator_priv;
+ state->config->sleep(fe);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops or51211_ops;
+
+struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct or51211_state* state = NULL;
+
+ /* Allocate memory for the internal state */
+ state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* Setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &or51211_ops, sizeof(struct dvb_frontend_ops));
+ state->initialized = 0;
+ state->current_frequency = 0;
+
+ /* 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 or51211_ops = {
+
+ .info = {
+ .name = "Oren OR51211 VSB Frontend",
+ .type = FE_ATSC,
+ .frequency_min = 44000000,
+ .frequency_max = 958000000,
+ .frequency_stepsize = 166666,
+ .caps = 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_8VSB
+ },
+
+ .release = or51211_release,
+
+ .init = or51211_init,
+ .sleep = or51211_sleep,
+
+ .set_frontend = or51211_set_parameters,
+ .get_tune_settings = or51211_get_tune_settings,
+
+ .read_status = or51211_read_status,
+ .read_ber = or51211_read_ber,
+ .read_signal_strength = or51211_read_signal_strength,
+ .read_snr = or51211_read_snr,
+ .read_ucblocks = or51211_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(or51211_attach);
+
diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb/frontends/or51211.h
new file mode 100644
index 000000000000..13a5a3afbf8b
--- /dev/null
+++ b/drivers/media/dvb/frontends/or51211.h
@@ -0,0 +1,44 @@
+/*
+ * Support for OR51211 (pcHDTV HD-2000) - VSB
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.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 OR51211_H
+#define OR51211_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct or51211_config
+{
+ /* The demodulator's i2c address */
+ u8 demod_address;
+
+ /* Request firmware for device */
+ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+ void (*setmode)(struct dvb_frontend * fe, int mode);
+ void (*reset)(struct dvb_frontend * fe);
+ void (*sleep)(struct dvb_frontend * fe);
+};
+
+extern struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // OR51211_H
+
diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c
new file mode 100644
index 000000000000..58ad34ef0a00
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp8870.c
@@ -0,0 +1,614 @@
+/*
+ Driver for Spase SP8870 demodulator
+
+ Copyright (C) 1999 Juergen Peitz
+
+ 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.
+
+*/
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware alps_tdlb7" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "sp8870.h"
+
+
+struct sp8870_state {
+
+ struct i2c_adapter* i2c;
+
+ struct dvb_frontend_ops ops;
+
+ const struct sp8870_config* config;
+
+ struct dvb_frontend frontend;
+
+ /* demodulator private data */
+ u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "sp8870: " args); \
+ } while (0)
+
+/* firmware size for sp8870 */
+#define SP8870_FIRMWARE_SIZE 16382
+
+/* starting point for firmware in file 'Sc_main.mc' */
+#define SP8870_FIRMWARE_OFFSET 0x0A
+
+static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data)
+{
+ u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 };
+ 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 int sp8870_readreg (struct sp8870_state* state, u16 reg)
+{
+ int ret;
+ u8 b0 [] = { reg >> 8 , reg & 0xff };
+ u8 b1 [] = { 0, 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
+
+ ret = i2c_transfer (state->i2c, msg, 2);
+
+ if (ret != 2) {
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ return (b1[0] << 8 | b1[1]);
+}
+
+static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw)
+{
+ struct i2c_msg msg;
+ char *fw_buf = fw->data;
+ int fw_pos;
+ u8 tx_buf[255];
+ int tx_len;
+ int err = 0;
+
+ dprintk ("%s: ...\n", __FUNCTION__);
+
+ if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET)
+ return -EINVAL;
+
+ // system controller stop
+ sp8870_writereg(state, 0x0F00, 0x0000);
+
+ // instruction RAM register hiword
+ sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF));
+
+ // instruction RAM MWR
+ sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16));
+
+ // do firmware upload
+ fw_pos = SP8870_FIRMWARE_OFFSET;
+ while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){
+ tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos;
+ // write register 0xCF0A
+ tx_buf[0] = 0xCF;
+ tx_buf[1] = 0x0A;
+ memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len);
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.buf = tx_buf;
+ msg.len = tx_len + 2;
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ printk("%s: firmware upload failed!\n", __FUNCTION__);
+ printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
+ return err;
+ }
+ fw_pos += tx_len;
+ }
+
+ dprintk ("%s: done!\n", __FUNCTION__);
+ return 0;
+};
+
+static void sp8870_microcontroller_stop (struct sp8870_state* state)
+{
+ sp8870_writereg(state, 0x0F08, 0x000);
+ sp8870_writereg(state, 0x0F09, 0x000);
+
+ // microcontroller STOP
+ sp8870_writereg(state, 0x0F00, 0x000);
+}
+
+static void sp8870_microcontroller_start (struct sp8870_state* state)
+{
+ sp8870_writereg(state, 0x0F08, 0x000);
+ sp8870_writereg(state, 0x0F09, 0x000);
+
+ // microcontroller START
+ sp8870_writereg(state, 0x0F00, 0x001);
+ // not documented but if we don't read 0x0D01 out here
+ // we don't get a correct data valid signal
+ sp8870_readreg(state, 0x0D01);
+}
+
+static int sp8870_read_data_valid_signal(struct sp8870_state* state)
+{
+ return (sp8870_readreg(state, 0x0D02) > 0);
+}
+
+static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
+{
+ int known_parameters = 1;
+
+ *reg0xc05 = 0x000;
+
+ switch (p->u.ofdm.constellation) {
+ case QPSK:
+ break;
+ case QAM_16:
+ *reg0xc05 |= (1 << 10);
+ break;
+ case QAM_64:
+ *reg0xc05 |= (2 << 10);
+ break;
+ case QAM_AUTO:
+ known_parameters = 0;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (p->u.ofdm.hierarchy_information) {
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ *reg0xc05 |= (1 << 7);
+ break;
+ case HIERARCHY_2:
+ *reg0xc05 |= (2 << 7);
+ break;
+ case HIERARCHY_4:
+ *reg0xc05 |= (3 << 7);
+ break;
+ case HIERARCHY_AUTO:
+ known_parameters = 0;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (p->u.ofdm.code_rate_HP) {
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ *reg0xc05 |= (1 << 3);
+ break;
+ case FEC_3_4:
+ *reg0xc05 |= (2 << 3);
+ break;
+ case FEC_5_6:
+ *reg0xc05 |= (3 << 3);
+ break;
+ case FEC_7_8:
+ *reg0xc05 |= (4 << 3);
+ break;
+ case FEC_AUTO:
+ known_parameters = 0;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ if (known_parameters)
+ *reg0xc05 |= (2 << 1); /* use specified parameters */
+ else
+ *reg0xc05 |= (1 << 1); /* enable autoprobing */
+
+ return 0;
+}
+
+static int sp8870_wake_up(struct sp8870_state* state)
+{
+ // enable TS output and interface pins
+ return sp8870_writereg(state, 0xC18, 0x00D);
+}
+
+static int sp8870_set_frontend_parameters (struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+ int err;
+ u16 reg0xc05;
+
+ if ((err = configure_reg0xc05(p, &reg0xc05)))
+ return err;
+
+ // system controller stop
+ sp8870_microcontroller_stop(state);
+
+ // set tuner parameters
+ sp8870_writereg(state, 0x206, 0x001);
+ state->config->pll_set(fe, p);
+ sp8870_writereg(state, 0x206, 0x000);
+
+ // sample rate correction bit [23..17]
+ sp8870_writereg(state, 0x0319, 0x000A);
+
+ // sample rate correction bit [16..0]
+ sp8870_writereg(state, 0x031A, 0x0AAB);
+
+ // integer carrier offset
+ sp8870_writereg(state, 0x0309, 0x0400);
+
+ // fractional carrier offset
+ sp8870_writereg(state, 0x030A, 0x0000);
+
+ // filter for 6/7/8 Mhz channel
+ if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+ sp8870_writereg(state, 0x0311, 0x0002);
+ else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+ sp8870_writereg(state, 0x0311, 0x0001);
+ else
+ sp8870_writereg(state, 0x0311, 0x0000);
+
+ // scan order: 2k first = 0x0000, 8k first = 0x0001
+ if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
+ sp8870_writereg(state, 0x0338, 0x0000);
+ else
+ sp8870_writereg(state, 0x0338, 0x0001);
+
+ sp8870_writereg(state, 0xc05, reg0xc05);
+
+ // read status reg in order to clear pending irqs
+ sp8870_readreg(state, 0x200);
+
+ // system controller start
+ sp8870_microcontroller_start(state);
+
+ return 0;
+}
+
+static int sp8870_init (struct dvb_frontend* fe)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+ const struct firmware *fw = NULL;
+
+ sp8870_wake_up(state);
+ if (state->initialised) return 0;
+ state->initialised = 1;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+
+ /* request the firmware, this will block until someone uploads it */
+ printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE);
+ if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) {
+ printk("sp8870: no firmware upload (timeout or file not found?)\n");
+ release_firmware(fw);
+ return -EIO;
+ }
+
+ if (sp8870_firmware_upload(state, fw)) {
+ printk("sp8870: writing firmware to device failed\n");
+ release_firmware(fw);
+ return -EIO;
+ }
+ printk("sp8870: firmware upload complete\n");
+
+ /* enable TS output and interface pins */
+ sp8870_writereg(state, 0xc18, 0x00d);
+
+ // system controller stop
+ sp8870_microcontroller_stop(state);
+
+ // ADC mode
+ sp8870_writereg(state, 0x0301, 0x0003);
+
+ // Reed Solomon parity bytes passed to output
+ sp8870_writereg(state, 0x0C13, 0x0001);
+
+ // MPEG clock is suppressed if no valid data
+ sp8870_writereg(state, 0x0C14, 0x0001);
+
+ /* bit 0x010: enable data valid signal */
+ sp8870_writereg(state, 0x0D00, 0x010);
+ sp8870_writereg(state, 0x0D01, 0x000);
+
+ /* setup PLL */
+ if (state->config->pll_init) {
+ sp8870_writereg(state, 0x206, 0x001);
+ state->config->pll_init(fe);
+ sp8870_writereg(state, 0x206, 0x000);
+ }
+
+ return 0;
+}
+
+static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+ int status;
+ int signal;
+
+ *fe_status = 0;
+
+ status = sp8870_readreg (state, 0x0200);
+ if (status < 0)
+ return -EIO;
+
+ signal = sp8870_readreg (state, 0x0303);
+ if (signal < 0)
+ return -EIO;
+
+ if (signal > 0x0F)
+ *fe_status |= FE_HAS_SIGNAL;
+ if (status & 0x08)
+ *fe_status |= FE_HAS_SYNC;
+ if (status & 0x04)
+ *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI;
+
+ return 0;
+}
+
+static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+ int ret;
+ u32 tmp;
+
+ *ber = 0;
+
+ ret = sp8870_readreg(state, 0xC08);
+ if (ret < 0)
+ return -EIO;
+
+ tmp = ret & 0x3F;
+
+ ret = sp8870_readreg(state, 0xC07);
+ if (ret < 0)
+ return -EIO;
+
+ tmp = ret << 6;
+
+ if (tmp >= 0x3FFF0)
+ tmp = ~0;
+
+ *ber = tmp;
+
+ return 0;
+}
+
+static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+ int ret;
+ u16 tmp;
+
+ *signal = 0;
+
+ ret = sp8870_readreg (state, 0x306);
+ if (ret < 0)
+ return -EIO;
+
+ tmp = ret << 8;
+
+ ret = sp8870_readreg (state, 0x303);
+ if (ret < 0)
+ return -EIO;
+
+ tmp |= ret;
+
+ if (tmp)
+ *signal = 0xFFFF - tmp;
+
+ return 0;
+}
+
+static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+ int ret;
+
+ *ublocks = 0;
+
+ ret = sp8870_readreg(state, 0xC0C);
+ if (ret < 0)
+ return -EIO;
+
+ if (ret == 0xFFFF)
+ ret = ~0;
+
+ *ublocks = ret;
+
+ return 0;
+}
+
+// number of trials to recover from lockup
+#define MAXTRIALS 5
+// maximum checks for data valid signal
+#define MAXCHECKS 100
+
+// only for debugging: counter for detected lockups
+static int lockups = 0;
+// only for debugging: counter for channel switches
+static int switches = 0;
+
+static int sp8870_set_frontend (struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+ /*
+ The firmware of the sp8870 sometimes locks up after setting frontend parameters.
+ We try to detect this by checking the data valid signal.
+ If it is not set after MAXCHECKS we try to recover the lockup by setting
+ the frontend parameters again.
+ */
+
+ int err = 0;
+ int valid = 0;
+ int trials = 0;
+ int check_count = 0;
+
+ dprintk("%s: frequency = %i\n", __FUNCTION__, p->frequency);
+
+ for (trials = 1; trials <= MAXTRIALS; trials++) {
+
+ if ((err = sp8870_set_frontend_parameters(fe, p)))
+ return err;
+
+ for (check_count = 0; check_count < MAXCHECKS; check_count++) {
+// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0);
+ valid = sp8870_read_data_valid_signal(state);
+ if (valid) {
+ dprintk("%s: delay = %i usec\n",
+ __FUNCTION__, check_count * 10);
+ break;
+ }
+ udelay(10);
+ }
+ if (valid)
+ break;
+ }
+
+ if (!valid) {
+ printk("%s: firmware crash!!!!!!\n", __FUNCTION__);
+ return -EIO;
+ }
+
+ if (debug) {
+ if (valid) {
+ if (trials > 1) {
+ printk("%s: firmware lockup!!!\n", __FUNCTION__);
+ printk("%s: recovered after %i trial(s))\n", __FUNCTION__, trials - 1);
+ lockups++;
+ }
+ }
+ switches++;
+ printk("%s: switches = %i lockups = %i\n", __FUNCTION__, switches, lockups);
+ }
+
+ return 0;
+}
+
+static int sp8870_sleep(struct dvb_frontend* fe)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+
+ // tristate TS output and disable interface pins
+ return sp8870_writereg(state, 0xC18, 0x000);
+}
+
+static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 350;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void sp8870_release(struct dvb_frontend* fe)
+{
+ struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops sp8870_ops;
+
+struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct sp8870_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct sp8870_state*) kmalloc(sizeof(struct sp8870_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &sp8870_ops, sizeof(struct dvb_frontend_ops));
+ state->initialised = 0;
+
+ /* check if the demod is there */
+ if (sp8870_readreg(state, 0x0200) < 0) 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 sp8870_ops = {
+
+ .info = {
+ .name = "Spase SP8870 DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 470000000,
+ .frequency_max = 860000000,
+ .frequency_stepsize = 166666,
+ .caps = 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_HIERARCHY_AUTO | FE_CAN_RECOVER
+ },
+
+ .release = sp8870_release,
+
+ .init = sp8870_init,
+ .sleep = sp8870_sleep,
+
+ .set_frontend = sp8870_set_frontend,
+ .get_tune_settings = sp8870_get_tune_settings,
+
+ .read_status = sp8870_read_status,
+ .read_ber = sp8870_read_ber,
+ .read_signal_strength = sp8870_read_signal_strength,
+ .read_ucblocks = sp8870_read_uncorrected_blocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver");
+MODULE_AUTHOR("Juergen Peitz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sp8870_attach);
diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h
new file mode 100644
index 000000000000..f3b555dbc960
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp8870.h
@@ -0,0 +1,45 @@
+/*
+ Driver for Spase SP8870 demodulator
+
+ Copyright (C) 1999 Juergen Peitz
+
+ 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 SP8870_H
+#define SP8870_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp8870_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);
+
+ /* request firmware for device */
+ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // SP8870_H
diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c
new file mode 100644
index 000000000000..7eae833ece49
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp887x.c
@@ -0,0 +1,606 @@
+/*
+ Driver for the Spase sp887x demodulator
+*/
+
+/*
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ * download/extract it, and then copy it to /usr/lib/hotplug/firmware.
+ */
+#define SP887X_DEFAULT_FIRMWARE "dvb-fe-sp887x.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "sp887x.h"
+
+
+struct sp887x_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ const struct sp887x_config* config;
+ struct dvb_frontend frontend;
+
+ /* demodulator private data */
+ u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "sp887x: " args); \
+ } while (0)
+
+static int i2c_writebytes (struct sp887x_state* state, u8 *buf, u8 len)
+{
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = len };
+ int err;
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ printk ("%s: i2c write error (addr %02x, err == %i)\n",
+ __FUNCTION__, state->config->demod_address, err);
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data)
+{
+ u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 };
+ int ret;
+
+ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+ /**
+ * in case of soft reset we ignore ACK errors...
+ */
+ if (!(reg == 0xf1a && data == 0x000 &&
+ (ret == -EREMOTEIO || ret == -EFAULT)))
+ {
+ printk("%s: writereg error "
+ "(reg %03x, data %03x, ret == %i)\n",
+ __FUNCTION__, reg & 0xffff, data & 0xffff, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sp887x_readreg (struct sp887x_state* state, u16 reg)
+{
+ u8 b0 [] = { reg >> 8 , reg & 0xff };
+ u8 b1 [2];
+ int ret;
+ struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }};
+
+ if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) {
+ printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ return (((b1[0] << 8) | b1[1]) & 0xfff);
+}
+
+static void sp887x_microcontroller_stop (struct sp887x_state* state)
+{
+ dprintk("%s\n", __FUNCTION__);
+ sp887x_writereg(state, 0xf08, 0x000);
+ sp887x_writereg(state, 0xf09, 0x000);
+
+ /* microcontroller STOP */
+ sp887x_writereg(state, 0xf00, 0x000);
+}
+
+static void sp887x_microcontroller_start (struct sp887x_state* state)
+{
+ dprintk("%s\n", __FUNCTION__);
+ sp887x_writereg(state, 0xf08, 0x000);
+ sp887x_writereg(state, 0xf09, 0x000);
+
+ /* microcontroller START */
+ sp887x_writereg(state, 0xf00, 0x001);
+}
+
+static void sp887x_setup_agc (struct sp887x_state* state)
+{
+ /* setup AGC parameters */
+ dprintk("%s\n", __FUNCTION__);
+ sp887x_writereg(state, 0x33c, 0x054);
+ sp887x_writereg(state, 0x33b, 0x04c);
+ sp887x_writereg(state, 0x328, 0x000);
+ sp887x_writereg(state, 0x327, 0x005);
+ sp887x_writereg(state, 0x326, 0x001);
+ sp887x_writereg(state, 0x325, 0x001);
+ sp887x_writereg(state, 0x324, 0x001);
+ sp887x_writereg(state, 0x318, 0x050);
+ sp887x_writereg(state, 0x317, 0x3fe);
+ sp887x_writereg(state, 0x316, 0x001);
+ sp887x_writereg(state, 0x313, 0x005);
+ sp887x_writereg(state, 0x312, 0x002);
+ sp887x_writereg(state, 0x306, 0x000);
+ sp887x_writereg(state, 0x303, 0x000);
+}
+
+#define BLOCKSIZE 30
+#define FW_SIZE 0x4000
+/**
+ * load firmware and setup MPEG interface...
+ */
+static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+ u8 buf [BLOCKSIZE+2];
+ int i;
+ int fw_size = fw->size;
+ unsigned char *mem = fw->data;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ /* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */
+ if (fw_size < FW_SIZE+10)
+ return -ENODEV;
+
+ mem = fw->data + 10;
+
+ /* soft reset */
+ sp887x_writereg(state, 0xf1a, 0x000);
+
+ sp887x_microcontroller_stop (state);
+
+ printk ("%s: firmware upload... ", __FUNCTION__);
+
+ /* setup write pointer to -1 (end of memory) */
+ /* bit 0x8000 in address is set to enable 13bit mode */
+ sp887x_writereg(state, 0x8f08, 0x1fff);
+
+ /* dummy write (wrap around to start of memory) */
+ sp887x_writereg(state, 0x8f0a, 0x0000);
+
+ for (i = 0; i < FW_SIZE; i += BLOCKSIZE) {
+ int c = BLOCKSIZE;
+ int err;
+
+ if (i+c > FW_SIZE)
+ c = FW_SIZE - i;
+
+ /* bit 0x8000 in address is set to enable 13bit mode */
+ /* bit 0x4000 enables multibyte read/write transfers */
+ /* write register is 0xf0a */
+ buf[0] = 0xcf;
+ buf[1] = 0x0a;
+
+ memcpy(&buf[2], mem + i, c);
+
+ if ((err = i2c_writebytes (state, buf, c+2)) < 0) {
+ printk ("failed.\n");
+ printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
+ return err;
+ }
+ }
+
+ /* don't write RS bytes between packets */
+ sp887x_writereg(state, 0xc13, 0x001);
+
+ /* suppress clock if (!data_valid) */
+ sp887x_writereg(state, 0xc14, 0x000);
+
+ /* setup MPEG interface... */
+ sp887x_writereg(state, 0xc1a, 0x872);
+ sp887x_writereg(state, 0xc1b, 0x001);
+ sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */
+ sp887x_writereg(state, 0xc1a, 0x871);
+
+ /* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */
+ sp887x_writereg(state, 0x301, 0x002);
+
+ sp887x_setup_agc(state);
+
+ /* bit 0x010: enable data valid signal */
+ sp887x_writereg(state, 0xd00, 0x010);
+ sp887x_writereg(state, 0x0d1, 0x000);
+
+ /* setup the PLL */
+ if (state->config->pll_init) {
+ sp887x_writereg(state, 0x206, 0x001);
+ state->config->pll_init(fe);
+ sp887x_writereg(state, 0x206, 0x000);
+ }
+
+ printk ("done.\n");
+ return 0;
+};
+
+static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
+{
+ int known_parameters = 1;
+
+ *reg0xc05 = 0x000;
+
+ switch (p->u.ofdm.constellation) {
+ case QPSK:
+ break;
+ case QAM_16:
+ *reg0xc05 |= (1 << 10);
+ break;
+ case QAM_64:
+ *reg0xc05 |= (2 << 10);
+ break;
+ case QAM_AUTO:
+ known_parameters = 0;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (p->u.ofdm.hierarchy_information) {
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ *reg0xc05 |= (1 << 7);
+ break;
+ case HIERARCHY_2:
+ *reg0xc05 |= (2 << 7);
+ break;
+ case HIERARCHY_4:
+ *reg0xc05 |= (3 << 7);
+ break;
+ case HIERARCHY_AUTO:
+ known_parameters = 0;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ switch (p->u.ofdm.code_rate_HP) {
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ *reg0xc05 |= (1 << 3);
+ break;
+ case FEC_3_4:
+ *reg0xc05 |= (2 << 3);
+ break;
+ case FEC_5_6:
+ *reg0xc05 |= (3 << 3);
+ break;
+ case FEC_7_8:
+ *reg0xc05 |= (4 << 3);
+ break;
+ case FEC_AUTO:
+ known_parameters = 0;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ if (known_parameters)
+ *reg0xc05 |= (2 << 1); /* use specified parameters */
+ else
+ *reg0xc05 |= (1 << 1); /* enable autoprobing */
+
+ return 0;
+}
+
+/**
+ * estimates division of two 24bit numbers,
+ * derived from the ves1820/stv0299 driver code
+ */
+static void divide (int n, int d, int *quotient_i, int *quotient_f)
+{
+ unsigned int q, r;
+
+ r = (n % d) << 8;
+ q = (r / d);
+
+ if (quotient_i)
+ *quotient_i = q;
+
+ if (quotient_f) {
+ r = (r % d) << 8;
+ q = (q << 8) | (r / d);
+ r = (r % d) << 8;
+ *quotient_f = (q << 8) | (r / d);
+ }
+}
+
+static void sp887x_correct_offsets (struct sp887x_state* state,
+ struct dvb_frontend_parameters *p,
+ int actual_freq)
+{
+ static const u32 srate_correction [] = { 1879617, 4544878, 8098561 };
+ int bw_index = p->u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+ int freq_offset = actual_freq - p->frequency;
+ int sysclock = 61003; //[kHz]
+ int ifreq = 36000000;
+ int freq;
+ int frequency_shift;
+
+ if (p->inversion == INVERSION_ON)
+ freq = ifreq - freq_offset;
+ else
+ freq = ifreq + freq_offset;
+
+ divide(freq / 333, sysclock, NULL, &frequency_shift);
+
+ if (p->inversion == INVERSION_ON)
+ frequency_shift = -frequency_shift;
+
+ /* sample rate correction */
+ sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12);
+ sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff);
+
+ /* carrier offset correction */
+ sp887x_writereg(state, 0x309, frequency_shift >> 12);
+ sp887x_writereg(state, 0x30a, frequency_shift & 0xfff);
+}
+
+static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+ int actual_freq, err;
+ u16 val, reg0xc05;
+
+ if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ &&
+ p->u.ofdm.bandwidth != BANDWIDTH_7_MHZ &&
+ p->u.ofdm.bandwidth != BANDWIDTH_6_MHZ)
+ return -EINVAL;
+
+ if ((err = configure_reg0xc05(p, &reg0xc05)))
+ return err;
+
+ sp887x_microcontroller_stop(state);
+
+ /* setup the PLL */
+ sp887x_writereg(state, 0x206, 0x001);
+ actual_freq = state->config->pll_set(fe, p);
+ sp887x_writereg(state, 0x206, 0x000);
+
+ /* read status reg in order to clear <pending irqs */
+ sp887x_readreg(state, 0x200);
+
+ sp887x_correct_offsets(state, p, actual_freq);
+
+ /* filter for 6/7/8 Mhz channel */
+ if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+ val = 2;
+ else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+ val = 1;
+ else
+ val = 0;
+
+ sp887x_writereg(state, 0x311, val);
+
+ /* scan order: 2k first = 0, 8k first = 1 */
+ if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
+ sp887x_writereg(state, 0x338, 0x000);
+ else
+ sp887x_writereg(state, 0x338, 0x001);
+
+ sp887x_writereg(state, 0xc05, reg0xc05);
+
+ if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+ val = 2 << 3;
+ else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+ val = 3 << 3;
+ else
+ val = 0 << 3;
+
+ /* enable OFDM and SAW bits as lock indicators in sync register 0xf17,
+ * optimize algorithm for given bandwidth...
+ */
+ sp887x_writereg(state, 0xf14, 0x160 | val);
+ sp887x_writereg(state, 0xf15, 0x000);
+
+ sp887x_microcontroller_start(state);
+ return 0;
+}
+
+static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+ u16 snr12 = sp887x_readreg(state, 0xf16);
+ u16 sync0x200 = sp887x_readreg(state, 0x200);
+ u16 sync0xf17 = sp887x_readreg(state, 0xf17);
+
+ *status = 0;
+
+ if (snr12 > 0x00f)
+ *status |= FE_HAS_SIGNAL;
+
+ //if (sync0x200 & 0x004)
+ // *status |= FE_HAS_SYNC | FE_HAS_CARRIER;
+
+ //if (sync0x200 & 0x008)
+ // *status |= FE_HAS_VITERBI;
+
+ if ((sync0xf17 & 0x00f) == 0x002) {
+ *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_CARRIER;
+ }
+
+ if (sync0x200 & 0x001) { /* tuner adjustment requested...*/
+ int steps = (sync0x200 >> 4) & 0x00f;
+ if (steps & 0x008)
+ steps = -steps;
+ dprintk("sp887x: implement tuner adjustment (%+i steps)!!\n",
+ steps);
+ }
+
+ return 0;
+}
+
+static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+ *ber = (sp887x_readreg(state, 0xc08) & 0x3f) |
+ (sp887x_readreg(state, 0xc07) << 6);
+ sp887x_writereg(state, 0xc08, 0x000);
+ sp887x_writereg(state, 0xc07, 0x000);
+ if (*ber >= 0x3fff0)
+ *ber = ~0;
+
+ return 0;
+}
+
+static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+ u16 snr12 = sp887x_readreg(state, 0xf16);
+ u32 signal = 3 * (snr12 << 4);
+ *strength = (signal < 0xffff) ? signal : 0xffff;
+
+ return 0;
+}
+
+static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+ u16 snr12 = sp887x_readreg(state, 0xf16);
+ *snr = (snr12 << 4) | (snr12 >> 8);
+
+ return 0;
+}
+
+static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+ *ucblocks = sp887x_readreg(state, 0xc0c);
+ if (*ucblocks == 0xfff)
+ *ucblocks = ~0;
+
+ return 0;
+}
+
+static int sp887x_sleep(struct dvb_frontend* fe)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+
+ /* tristate TS output and disable interface pins */
+ sp887x_writereg(state, 0xc18, 0x000);
+
+ return 0;
+}
+
+static int sp887x_init(struct dvb_frontend* fe)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+ const struct firmware *fw = NULL;
+ int ret;
+
+ if (!state->initialised) {
+ /* request the firmware, this will block until someone uploads it */
+ printk("sp887x: waiting for firmware upload (%s)...\n", SP887X_DEFAULT_FIRMWARE);
+ ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE);
+ if (ret) {
+ printk("sp887x: no firmware upload (timeout or file not found?)\n");
+ return ret;
+ }
+
+ ret = sp887x_initial_setup(fe, fw);
+ if (ret) {
+ printk("sp887x: writing firmware to device failed\n");
+ release_firmware(fw);
+ return ret;
+ }
+ printk("sp887x: firmware upload complete\n");
+ state->initialised = 1;
+ }
+
+ /* enable TS output and interface pins */
+ sp887x_writereg(state, 0xc18, 0x00d);
+
+ return 0;
+}
+
+static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 350;
+ fesettings->step_size = 166666*2;
+ fesettings->max_drift = (166666*2)+1;
+ return 0;
+}
+
+static void sp887x_release(struct dvb_frontend* fe)
+{
+ struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops sp887x_ops;
+
+struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct sp887x_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct sp887x_state*) kmalloc(sizeof(struct sp887x_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &sp887x_ops, sizeof(struct dvb_frontend_ops));
+ state->initialised = 0;
+
+ /* check if the demod is there */
+ if (sp887x_readreg(state, 0x0200) < 0) 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 sp887x_ops = {
+
+ .info = {
+ .name = "Spase SP887x DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 50500000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166666,
+ .caps = 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_RECOVER
+ },
+
+ .release = sp887x_release,
+
+ .init = sp887x_init,
+ .sleep = sp887x_sleep,
+
+ .set_frontend = sp887x_setup_frontend_parameters,
+ .get_tune_settings = sp887x_get_tune_settings,
+
+ .read_status = sp887x_read_status,
+ .read_ber = sp887x_read_ber,
+ .read_signal_strength = sp887x_read_signal_strength,
+ .read_snr = sp887x_read_snr,
+ .read_ucblocks = sp887x_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(sp887x_attach);
diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h
new file mode 100644
index 000000000000..6a05d8f8e8cc
--- /dev/null
+++ b/drivers/media/dvb/frontends/sp887x.h
@@ -0,0 +1,29 @@
+/*
+ Driver for the Spase sp887x demodulator
+*/
+
+#ifndef SP887X_H
+#define SP887X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct sp887x_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+
+ /* this should return the actual frequency tuned to */
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+ /* request firmware for device */
+ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // SP887X_H
diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c
new file mode 100644
index 000000000000..502c6403dfc6
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0297.c
@@ -0,0 +1,798 @@
+/*
+ Driver for STV0297 demodulator
+
+ Copyright (C) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
+ Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+#include "stv0297.h"
+
+struct stv0297_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend_ops ops;
+ const struct stv0297_config *config;
+ struct dvb_frontend frontend;
+
+ unsigned long base_freq;
+ u8 pwm;
+};
+
+#if 1
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+#define STV0297_CLOCK_KHZ 28900
+
+static u8 init_tab[] = {
+ 0x00, 0x09,
+ 0x01, 0x69,
+ 0x03, 0x00,
+ 0x04, 0x00,
+ 0x07, 0x00,
+ 0x08, 0x00,
+ 0x20, 0x00,
+ 0x21, 0x40,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x24, 0x40,
+ 0x25, 0x88,
+ 0x30, 0xff,
+ 0x31, 0x00,
+ 0x32, 0xff,
+ 0x33, 0x00,
+ 0x34, 0x50,
+ 0x35, 0x7f,
+ 0x36, 0x00,
+ 0x37, 0x20,
+ 0x38, 0x00,
+ 0x40, 0x1c,
+ 0x41, 0xff,
+ 0x42, 0x29,
+ 0x43, 0x00,
+ 0x44, 0xff,
+ 0x45, 0x00,
+ 0x46, 0x00,
+ 0x49, 0x04,
+ 0x4a, 0xff,
+ 0x4b, 0x7f,
+ 0x52, 0x30,
+ 0x55, 0xae,
+ 0x56, 0x47,
+ 0x57, 0xe1,
+ 0x58, 0x3a,
+ 0x5a, 0x1e,
+ 0x5b, 0x34,
+ 0x60, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x00,
+ 0x65, 0x00,
+ 0x66, 0x00,
+ 0x67, 0x00,
+ 0x68, 0x00,
+ 0x69, 0x00,
+ 0x6a, 0x02,
+ 0x6b, 0x00,
+ 0x70, 0xff,
+ 0x71, 0x00,
+ 0x72, 0x00,
+ 0x73, 0x00,
+ 0x74, 0x0c,
+ 0x80, 0x00,
+ 0x81, 0x00,
+ 0x82, 0x00,
+ 0x83, 0x00,
+ 0x84, 0x04,
+ 0x85, 0x80,
+ 0x86, 0x24,
+ 0x87, 0x78,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x90, 0x01,
+ 0x91, 0x01,
+ 0xa0, 0x00,
+ 0xa1, 0x00,
+ 0xa2, 0x00,
+ 0xb0, 0x91,
+ 0xb1, 0x0b,
+ 0xc0, 0x53,
+ 0xc1, 0x70,
+ 0xc2, 0x12,
+ 0xd0, 0x00,
+ 0xd1, 0x00,
+ 0xd2, 0x00,
+ 0xd3, 0x00,
+ 0xd4, 0x00,
+ 0xd5, 0x00,
+ 0xde, 0x00,
+ 0xdf, 0x00,
+ 0x61, 0x49,
+ 0x62, 0x0b,
+ 0x53, 0x08,
+ 0x59, 0x08,
+};
+
+
+static int stv0297_writereg(struct stv0297_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+ "ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+static int stv0297_readreg(struct stv0297_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len =
+ 1},
+ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
+ };
+
+ // this device needs a STOP between the register and data
+ if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) {
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret);
+ return -1;
+ }
+ if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) {
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg, ret);
+ return -1;
+ }
+
+ return b1[0];
+}
+
+static int stv0297_writereg_mask(struct stv0297_state *state, u8 reg, u8 mask, u8 data)
+{
+ int val;
+
+ val = stv0297_readreg(state, reg);
+ val &= ~mask;
+ val |= (data & mask);
+ stv0297_writereg(state, reg, val);
+
+ return 0;
+}
+
+static int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len)
+{
+ int ret;
+ struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf =
+ &reg1,.len = 1},
+ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b,.len = len}
+ };
+
+ // this device needs a STOP between the register and data
+ if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) {
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret);
+ return -1;
+ }
+ if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) {
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __FUNCTION__, reg1, ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static u32 stv0297_get_symbolrate(struct stv0297_state *state)
+{
+ u64 tmp;
+
+ tmp = stv0297_readreg(state, 0x55);
+ tmp |= stv0297_readreg(state, 0x56) << 8;
+ tmp |= stv0297_readreg(state, 0x57) << 16;
+ tmp |= stv0297_readreg(state, 0x58) << 24;
+
+ tmp *= STV0297_CLOCK_KHZ;
+ tmp >>= 32;
+
+ return (u32) tmp;
+}
+
+static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate)
+{
+ long tmp;
+
+ tmp = 131072L * srate; /* 131072 = 2^17 */
+ tmp = tmp / (STV0297_CLOCK_KHZ / 4); /* 1/4 = 2^-2 */
+ tmp = tmp * 8192L; /* 8192 = 2^13 */
+
+ stv0297_writereg(state, 0x55, (unsigned char) (tmp & 0xFF));
+ stv0297_writereg(state, 0x56, (unsigned char) (tmp >> 8));
+ stv0297_writereg(state, 0x57, (unsigned char) (tmp >> 16));
+ stv0297_writereg(state, 0x58, (unsigned char) (tmp >> 24));
+}
+
+static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate)
+{
+ long tmp;
+
+ tmp = (long) fshift *262144L; /* 262144 = 2*18 */
+ tmp /= symrate;
+ tmp *= 1024; /* 1024 = 2*10 */
+
+ // adjust
+ if (tmp >= 0) {
+ tmp += 500000;
+ } else {
+ tmp -= 500000;
+ }
+ tmp /= 1000000;
+
+ stv0297_writereg(state, 0x60, tmp & 0xFF);
+ stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0);
+}
+
+static void stv0297_set_carrieroffset(struct stv0297_state *state, long offset)
+{
+ long tmp;
+
+ /* symrate is hardcoded to 10000 */
+ tmp = offset * 26844L; /* (2**28)/10000 */
+ if (tmp < 0)
+ tmp += 0x10000000;
+ tmp &= 0x0FFFFFFF;
+
+ stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF));
+ stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8));
+ stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16));
+ stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f);
+}
+
+/*
+static long stv0297_get_carrieroffset(struct stv0297_state *state)
+{
+ s64 tmp;
+
+ stv0297_writereg(state, 0x6B, 0x00);
+
+ tmp = stv0297_readreg(state, 0x66);
+ tmp |= (stv0297_readreg(state, 0x67) << 8);
+ tmp |= (stv0297_readreg(state, 0x68) << 16);
+ tmp |= (stv0297_readreg(state, 0x69) & 0x0F) << 24;
+
+ tmp *= stv0297_get_symbolrate(state);
+ tmp >>= 28;
+
+ return (s32) tmp;
+}
+*/
+
+static void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq)
+{
+ s32 tmp;
+
+ if (freq > 10000)
+ freq -= STV0297_CLOCK_KHZ;
+
+ tmp = (STV0297_CLOCK_KHZ * 1000) / (1 << 16);
+ tmp = (freq * 1000) / tmp;
+ if (tmp > 0xffff)
+ tmp = 0xffff;
+
+ stv0297_writereg_mask(state, 0x25, 0x80, 0x80);
+ stv0297_writereg(state, 0x21, tmp >> 8);
+ stv0297_writereg(state, 0x20, tmp);
+}
+
+static int stv0297_set_qam(struct stv0297_state *state, fe_modulation_t modulation)
+{
+ int val = 0;
+
+ switch (modulation) {
+ case QAM_16:
+ val = 0;
+ break;
+
+ case QAM_32:
+ val = 1;
+ break;
+
+ case QAM_64:
+ val = 4;
+ break;
+
+ case QAM_128:
+ val = 2;
+ break;
+
+ case QAM_256:
+ val = 3;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ stv0297_writereg_mask(state, 0x00, 0x70, val << 4);
+
+ return 0;
+}
+
+static int stv0297_set_inversion(struct stv0297_state *state, fe_spectral_inversion_t inversion)
+{
+ int val = 0;
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ val = 0;
+ break;
+
+ case INVERSION_ON:
+ val = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ stv0297_writereg_mask(state, 0x83, 0x08, val << 3);
+
+ return 0;
+}
+
+int stv0297_enable_plli2c(struct dvb_frontend *fe)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+ stv0297_writereg(state, 0x87, 0x78);
+ stv0297_writereg(state, 0x86, 0xc8);
+
+ return 0;
+}
+
+static int stv0297_init(struct dvb_frontend *fe)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+ int i;
+
+ /* soft reset */
+ stv0297_writereg_mask(state, 0x80, 1, 1);
+ stv0297_writereg_mask(state, 0x80, 1, 0);
+
+ /* reset deinterleaver */
+ stv0297_writereg_mask(state, 0x81, 1, 1);
+ stv0297_writereg_mask(state, 0x81, 1, 0);
+
+ /* load init table */
+ for (i = 0; i < sizeof(init_tab); i += 2) {
+ stv0297_writereg(state, init_tab[i], init_tab[i + 1]);
+ }
+
+ /* set a dummy symbol rate */
+ stv0297_set_symbolrate(state, 6900);
+
+ /* invert AGC1 polarity */
+ stv0297_writereg_mask(state, 0x88, 0x10, 0x10);
+
+ /* setup bit error counting */
+ stv0297_writereg_mask(state, 0xA0, 0x80, 0x00);
+ stv0297_writereg_mask(state, 0xA0, 0x10, 0x00);
+ stv0297_writereg_mask(state, 0xA0, 0x08, 0x00);
+ stv0297_writereg_mask(state, 0xA0, 0x07, 0x04);
+
+ /* min + max PWM */
+ stv0297_writereg(state, 0x4a, 0x00);
+ stv0297_writereg(state, 0x4b, state->pwm);
+ msleep(200);
+
+ if (state->config->pll_init)
+ state->config->pll_init(fe);
+
+ return 0;
+}
+
+static int stv0297_sleep(struct dvb_frontend *fe)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+ stv0297_writereg_mask(state, 0x80, 1, 1);
+
+ return 0;
+}
+
+static int stv0297_read_status(struct dvb_frontend *fe, fe_status_t * status)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+ u8 sync = stv0297_readreg(state, 0xDF);
+
+ *status = 0;
+ if (sync & 0x80)
+ *status |=
+ FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK;
+ return 0;
+}
+
+static int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+ u8 BER[3];
+
+ stv0297_writereg(state, 0xA0, 0x80); // Start Counting bit errors for 4096 Bytes
+ mdelay(25); // Hopefully got 4096 Bytes
+ stv0297_readregs(state, 0xA0, BER, 3);
+ mdelay(25);
+ *ber = (BER[2] << 8 | BER[1]) / (8 * 4096);
+
+ return 0;
+}
+
+
+static int stv0297_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+ u8 STRENGTH[2];
+
+ stv0297_readregs(state, 0x41, STRENGTH, 2);
+ *strength = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0];
+
+ return 0;
+}
+
+static int stv0297_read_snr(struct dvb_frontend *fe, u16 * snr)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+ u8 SNR[2];
+
+ stv0297_readregs(state, 0x07, SNR, 2);
+ *snr = SNR[1] << 8 | SNR[0];
+
+ return 0;
+}
+
+static int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+
+ *ucblocks = (stv0297_readreg(state, 0xD5) << 8)
+ | stv0297_readreg(state, 0xD4);
+
+ return 0;
+}
+
+static int stv0297_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+ int u_threshold;
+ int initial_u;
+ int blind_u;
+ int delay;
+ int sweeprate;
+ int carrieroffset;
+ unsigned long starttime;
+ unsigned long timeout;
+ fe_spectral_inversion_t inversion;
+
+ switch (p->u.qam.modulation) {
+ case QAM_16:
+ case QAM_32:
+ case QAM_64:
+ delay = 100;
+ sweeprate = 1500;
+ break;
+
+ case QAM_128:
+ delay = 150;
+ sweeprate = 1000;
+ break;
+
+ case QAM_256:
+ delay = 200;
+ sweeprate = 500;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // determine inversion dependant parameters
+ inversion = p->inversion;
+ if (state->config->invert)
+ inversion = (inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON;
+ carrieroffset = -330;
+ switch (inversion) {
+ case INVERSION_OFF:
+ break;
+
+ case INVERSION_ON:
+ sweeprate = -sweeprate;
+ carrieroffset = -carrieroffset;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ stv0297_init(fe);
+ state->config->pll_set(fe, p);
+
+ /* clear software interrupts */
+ stv0297_writereg(state, 0x82, 0x0);
+
+ /* set initial demodulation frequency */
+ stv0297_set_initialdemodfreq(state, 7250);
+
+ /* setup AGC */
+ stv0297_writereg_mask(state, 0x43, 0x10, 0x00);
+ stv0297_writereg(state, 0x41, 0x00);
+ stv0297_writereg_mask(state, 0x42, 0x03, 0x01);
+ stv0297_writereg_mask(state, 0x36, 0x60, 0x00);
+ stv0297_writereg_mask(state, 0x36, 0x18, 0x00);
+ stv0297_writereg_mask(state, 0x71, 0x80, 0x80);
+ stv0297_writereg(state, 0x72, 0x00);
+ stv0297_writereg(state, 0x73, 0x00);
+ stv0297_writereg_mask(state, 0x74, 0x0F, 0x00);
+ stv0297_writereg_mask(state, 0x43, 0x08, 0x00);
+ stv0297_writereg_mask(state, 0x71, 0x80, 0x00);
+
+ /* setup STL */
+ stv0297_writereg_mask(state, 0x5a, 0x20, 0x20);
+ stv0297_writereg_mask(state, 0x5b, 0x02, 0x02);
+ stv0297_writereg_mask(state, 0x5b, 0x02, 0x00);
+ stv0297_writereg_mask(state, 0x5b, 0x01, 0x00);
+ stv0297_writereg_mask(state, 0x5a, 0x40, 0x40);
+
+ /* disable frequency sweep */
+ stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+
+ /* reset deinterleaver */
+ stv0297_writereg_mask(state, 0x81, 0x01, 0x01);
+ stv0297_writereg_mask(state, 0x81, 0x01, 0x00);
+
+ /* ??? */
+ stv0297_writereg_mask(state, 0x83, 0x20, 0x20);
+ stv0297_writereg_mask(state, 0x83, 0x20, 0x00);
+
+ /* reset equaliser */
+ u_threshold = stv0297_readreg(state, 0x00) & 0xf;
+ initial_u = stv0297_readreg(state, 0x01) >> 4;
+ blind_u = stv0297_readreg(state, 0x01) & 0xf;
+ stv0297_writereg_mask(state, 0x84, 0x01, 0x01);
+ stv0297_writereg_mask(state, 0x84, 0x01, 0x00);
+ stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold);
+ stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4);
+ stv0297_writereg_mask(state, 0x01, 0x0f, blind_u);
+
+ /* data comes from internal A/D */
+ stv0297_writereg_mask(state, 0x87, 0x80, 0x00);
+
+ /* clear phase registers */
+ stv0297_writereg(state, 0x63, 0x00);
+ stv0297_writereg(state, 0x64, 0x00);
+ stv0297_writereg(state, 0x65, 0x00);
+ stv0297_writereg(state, 0x66, 0x00);
+ stv0297_writereg(state, 0x67, 0x00);
+ stv0297_writereg(state, 0x68, 0x00);
+ stv0297_writereg_mask(state, 0x69, 0x0f, 0x00);
+
+ /* set parameters */
+ stv0297_set_qam(state, p->u.qam.modulation);
+ stv0297_set_symbolrate(state, p->u.qam.symbol_rate / 1000);
+ stv0297_set_sweeprate(state, sweeprate, p->u.qam.symbol_rate / 1000);
+ stv0297_set_carrieroffset(state, carrieroffset);
+ stv0297_set_inversion(state, inversion);
+
+ /* kick off lock */
+ stv0297_writereg_mask(state, 0x88, 0x08, 0x08);
+ stv0297_writereg_mask(state, 0x5a, 0x20, 0x00);
+ stv0297_writereg_mask(state, 0x6a, 0x01, 0x01);
+ stv0297_writereg_mask(state, 0x43, 0x40, 0x40);
+ stv0297_writereg_mask(state, 0x5b, 0x30, 0x00);
+ stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c);
+ stv0297_writereg_mask(state, 0x03, 0x03, 0x03);
+ stv0297_writereg_mask(state, 0x43, 0x10, 0x10);
+
+ /* wait for WGAGC lock */
+ starttime = jiffies;
+ timeout = jiffies + (200 * HZ) / 1000;
+ while (time_before(jiffies, timeout)) {
+ msleep(10);
+ if (stv0297_readreg(state, 0x43) & 0x08)
+ break;
+ }
+ if (time_after(jiffies, timeout)) {
+ goto timeout;
+ }
+ msleep(20);
+
+ /* wait for equaliser partial convergence */
+ timeout = jiffies + (50 * HZ) / 1000;
+ while (time_before(jiffies, timeout)) {
+ msleep(10);
+
+ if (stv0297_readreg(state, 0x82) & 0x04) {
+ break;
+ }
+ }
+ if (time_after(jiffies, timeout)) {
+ goto timeout;
+ }
+
+ /* wait for equaliser full convergence */
+ timeout = jiffies + (delay * HZ) / 1000;
+ while (time_before(jiffies, timeout)) {
+ msleep(10);
+
+ if (stv0297_readreg(state, 0x82) & 0x08) {
+ break;
+ }
+ }
+ if (time_after(jiffies, timeout)) {
+ goto timeout;
+ }
+
+ /* disable sweep */
+ stv0297_writereg_mask(state, 0x6a, 1, 0);
+ stv0297_writereg_mask(state, 0x88, 8, 0);
+
+ /* wait for main lock */
+ timeout = jiffies + (20 * HZ) / 1000;
+ while (time_before(jiffies, timeout)) {
+ msleep(10);
+
+ if (stv0297_readreg(state, 0xDF) & 0x80) {
+ break;
+ }
+ }
+ if (time_after(jiffies, timeout)) {
+ goto timeout;
+ }
+ msleep(100);
+
+ /* is it still locked after that delay? */
+ if (!(stv0297_readreg(state, 0xDF) & 0x80)) {
+ goto timeout;
+ }
+
+ /* success!! */
+ stv0297_writereg_mask(state, 0x5a, 0x40, 0x00);
+ state->base_freq = p->frequency;
+ return 0;
+
+timeout:
+ stv0297_writereg_mask(state, 0x6a, 0x01, 0x00);
+ return 0;
+}
+
+static int stv0297_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+ int reg_00, reg_83;
+
+ reg_00 = stv0297_readreg(state, 0x00);
+ reg_83 = stv0297_readreg(state, 0x83);
+
+ p->frequency = state->base_freq;
+ p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF;
+ if (state->config->invert)
+ p->inversion = (p->inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON;
+ p->u.qam.symbol_rate = stv0297_get_symbolrate(state) * 1000;
+ p->u.qam.fec_inner = FEC_NONE;
+
+ switch ((reg_00 >> 4) & 0x7) {
+ case 0:
+ p->u.qam.modulation = QAM_16;
+ break;
+ case 1:
+ p->u.qam.modulation = QAM_32;
+ break;
+ case 2:
+ p->u.qam.modulation = QAM_128;
+ break;
+ case 3:
+ p->u.qam.modulation = QAM_256;
+ break;
+ case 4:
+ p->u.qam.modulation = QAM_64;
+ break;
+ }
+
+ return 0;
+}
+
+static void stv0297_release(struct dvb_frontend *fe)
+{
+ struct stv0297_state *state = (struct stv0297_state *) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops stv0297_ops;
+
+struct dvb_frontend *stv0297_attach(const struct stv0297_config *config,
+ struct i2c_adapter *i2c, int pwm)
+{
+ struct stv0297_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct stv0297_state *) kmalloc(sizeof(struct stv0297_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &stv0297_ops, sizeof(struct dvb_frontend_ops));
+ state->base_freq = 0;
+ state->pwm = pwm;
+
+ /* check if the demod is there */
+ if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20)
+ 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 stv0297_ops = {
+
+ .info = {
+ .name = "ST STV0297 DVB-C",
+ .type = FE_QAM,
+ .frequency_min = 64000000,
+ .frequency_max = 1300000000,
+ .frequency_stepsize = 62500,
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000,
+ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO},
+
+ .release = stv0297_release,
+
+ .init = stv0297_init,
+ .sleep = stv0297_sleep,
+
+ .set_frontend = stv0297_set_frontend,
+ .get_frontend = stv0297_get_frontend,
+
+ .read_status = stv0297_read_status,
+ .read_ber = stv0297_read_ber,
+ .read_signal_strength = stv0297_read_signal_strength,
+ .read_snr = stv0297_read_snr,
+ .read_ucblocks = stv0297_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver");
+MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(stv0297_attach);
+EXPORT_SYMBOL(stv0297_enable_plli2c);
diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h
new file mode 100644
index 000000000000..3be535989302
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0297.h
@@ -0,0 +1,44 @@
+/*
+ Driver for STV0297 demodulator
+
+ Copyright (C) 2003-2004 Dennis Noermann <dennis.noermann@noernet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef STV0297_H
+#define STV0297_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0297_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* does the "inversion" need inverted? */
+ u8 invert:1;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+ struct i2c_adapter* i2c, int pwm);
+extern int stv0297_enable_plli2c(struct dvb_frontend* fe);
+
+#endif // STV0297_H
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
new file mode 100644
index 000000000000..15b40541b62d
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -0,0 +1,731 @@
+/*
+ Driver for ST STV0299 demodulator
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ <ralph@convergence.de>,
+ <holger@convergence.de>,
+ <js@convergence.de>
+
+
+ Philips SU1278/SH
+
+ Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de>
+
+
+ LG TDQF-S001F
+
+ Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net>
+ & Andreas Oberritter <obi@linuxtv.org>
+
+
+ Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B
+
+ Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>:
+
+ Support for Philips SU1278 on Technotrend hardware
+
+ Copyright (C) 2004 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "stv0299.h"
+
+struct stv0299_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ const struct stv0299_config* config;
+ struct dvb_frontend frontend;
+
+ u8 initialised:1;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ fe_code_rate_t fec_inner;
+ int errmode;
+};
+
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "stv0299: " args); \
+ } while (0)
+
+
+static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+ ret = i2c_transfer (state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+ "ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ return stv0299_writeregI(state, reg, data);
+}
+
+static u8 stv0299_readreg (struct stv0299_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ ret = i2c_transfer (state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __FUNCTION__, reg, ret);
+
+ return b1[0];
+}
+
+static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len)
+{
+ int ret;
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+ ret = i2c_transfer (state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return ret == 2 ? 0 : ret;
+}
+
+static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch (fec) {
+ case FEC_AUTO:
+ {
+ return stv0299_writeregI (state, 0x31, 0x1f);
+ }
+ case FEC_1_2:
+ {
+ return stv0299_writeregI (state, 0x31, 0x01);
+ }
+ case FEC_2_3:
+ {
+ return stv0299_writeregI (state, 0x31, 0x02);
+ }
+ case FEC_3_4:
+ {
+ return stv0299_writeregI (state, 0x31, 0x04);
+ }
+ case FEC_5_6:
+ {
+ return stv0299_writeregI (state, 0x31, 0x08);
+ }
+ case FEC_7_8:
+ {
+ return stv0299_writeregI (state, 0x31, 0x10);
+ }
+ default:
+ {
+ return -EINVAL;
+ }
+ }
+}
+
+static fe_code_rate_t stv0299_get_fec (struct stv0299_state* state)
+{
+ static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6,
+ FEC_7_8, FEC_1_2 };
+ u8 index;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ index = stv0299_readreg (state, 0x1b);
+ index &= 0x7;
+
+ if (index > 4)
+ return FEC_AUTO;
+
+ return fec_tab [index];
+}
+
+static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout)
+{
+ unsigned long start = jiffies;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ while (stv0299_readreg(state, 0x0a) & 1) {
+ if (jiffies - start > timeout) {
+ dprintk ("%s: timeout!!\n", __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ };
+
+ return 0;
+}
+
+static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout)
+{
+ unsigned long start = jiffies;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) {
+ if (jiffies - start > timeout) {
+ dprintk ("%s: timeout!!\n", __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ };
+
+ return 0;
+}
+
+static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ u64 big = srate;
+ u32 ratio;
+
+ // check rate is within limits
+ if ((srate < 1000000) || (srate > 45000000)) return -EINVAL;
+
+ // calculate value to program
+ big = big << 20;
+ big += (state->config->mclk-1); // round correctly
+ do_div(big, state->config->mclk);
+ ratio = big << 4;
+
+ return state->config->set_symbol_rate(fe, srate, ratio);
+}
+
+static int stv0299_get_symbolrate (struct stv0299_state* state)
+{
+ u32 Mclk = state->config->mclk / 4096L;
+ u32 srate;
+ s32 offset;
+ u8 sfr[3];
+ s8 rtf;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ stv0299_readregs (state, 0x1f, sfr, 3);
+ stv0299_readregs (state, 0x1a, &rtf, 1);
+
+ srate = (sfr[0] << 8) | sfr[1];
+ srate *= Mclk;
+ srate /= 16;
+ srate += (sfr[2] >> 4) * Mclk / 256;
+ offset = (s32) rtf * (srate / 4096L);
+ offset /= 128;
+
+ dprintk ("%s : srate = %i\n", __FUNCTION__, srate);
+ dprintk ("%s : ofset = %i\n", __FUNCTION__, offset);
+
+ srate += offset;
+
+ srate += 1000;
+ srate /= 2000;
+ srate *= 2000;
+
+ return srate;
+}
+
+static int stv0299_send_diseqc_msg (struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ u8 val;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (stv0299_wait_diseqc_idle (state, 100) < 0)
+ return -ETIMEDOUT;
+
+ val = stv0299_readreg (state, 0x08);
+
+ if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */
+ return -EREMOTEIO;
+
+ for (i=0; i<m->msg_len; i++) {
+ if (stv0299_wait_diseqc_fifo (state, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (stv0299_writeregI (state, 0x09, m->msg[i]))
+ return -EREMOTEIO;
+ }
+
+ if (stv0299_wait_diseqc_idle (state, 100) < 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (stv0299_wait_diseqc_idle (state, 100) < 0)
+ return -ETIMEDOUT;
+
+ val = stv0299_readreg (state, 0x08);
+
+ if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2)) /* burst mode */
+ return -EREMOTEIO;
+
+ if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff))
+ return -EREMOTEIO;
+
+ if (stv0299_wait_diseqc_idle (state, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (stv0299_writeregI (state, 0x08, val))
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ u8 val;
+
+ if (stv0299_wait_diseqc_idle (state, 100) < 0)
+ return -ETIMEDOUT;
+
+ val = stv0299_readreg (state, 0x08);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ return stv0299_writeregI (state, 0x08, val | 0x3);
+
+ case SEC_TONE_OFF:
+ return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ u8 reg0x08;
+ u8 reg0x0c;
+
+ dprintk("%s: %s\n", __FUNCTION__,
+ voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+ reg0x08 = stv0299_readreg (state, 0x08);
+ reg0x0c = stv0299_readreg (state, 0x0c);
+
+ /**
+ * H/V switching over OP0, OP1 and OP2 are LNB power enable bits
+ */
+ reg0x0c &= 0x0f;
+
+ if (voltage == SEC_VOLTAGE_OFF) {
+ stv0299_writeregI (state, 0x0c, 0x00); /* LNB power off! */
+ return stv0299_writeregI (state, 0x08, 0x00); /* LNB power off! */
+ }
+
+ stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6));
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) reg0x0c |= 0x10;
+ else reg0x0c |= 0x40;
+
+ return stv0299_writeregI(state, 0x0c, reg0x0c);
+
+ case SEC_VOLTAGE_18:
+ return stv0299_writeregI(state, 0x0c, reg0x0c | 0x50);
+ default:
+ return -EINVAL;
+ };
+}
+
+static int stv0299_send_legacy_dish_cmd(struct dvb_frontend* fe, u32 cmd)
+{
+ u8 last = 1;
+ int i;
+
+ /* reset voltage at the end
+ if((0x50 & stv0299_readreg (i2c, 0x0c)) == 0x50)
+ cmd |= 0x80;
+ else
+ cmd &= 0x7F;
+ */
+
+ cmd = cmd << 1;
+ dprintk("%s switch command: 0x%04x\n",__FUNCTION__, cmd);
+
+ stv0299_set_voltage(fe,SEC_VOLTAGE_18);
+ msleep(32);
+
+ for (i=0; i<9; i++) {
+ if((cmd & 0x01) != last) {
+ stv0299_set_voltage(fe, last ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
+ last = (last) ? 0 : 1;
+ }
+
+ cmd = cmd >> 1;
+
+ if (i != 8)
+ msleep(8);
+ }
+
+ return 0;
+}
+
+static int stv0299_init (struct dvb_frontend* fe)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ int i;
+
+ dprintk("stv0299: init chip\n");
+
+ for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2)
+ stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]);
+
+ if (state->config->pll_init) {
+ stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */
+ state->config->pll_init(fe);
+ stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */
+ }
+
+ return 0;
+}
+
+static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ u8 signal = 0xff - stv0299_readreg (state, 0x18);
+ u8 sync = stv0299_readreg (state, 0x1b);
+
+ dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __FUNCTION__, sync);
+ *status = 0;
+
+ if (signal > 10)
+ *status |= FE_HAS_SIGNAL;
+
+ if (sync & 0x80)
+ *status |= FE_HAS_CARRIER;
+
+ if (sync & 0x10)
+ *status |= FE_HAS_VITERBI;
+
+ if (sync & 0x08)
+ *status |= FE_HAS_SYNC;
+
+ if ((sync & 0x98) == 0x98)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ if (state->errmode != STATUS_BER) return 0;
+ *ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e);
+
+ return 0;
+}
+
+static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ s32 signal = 0xffff - ((stv0299_readreg (state, 0x18) << 8)
+ | stv0299_readreg (state, 0x19));
+
+ dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __FUNCTION__,
+ stv0299_readreg (state, 0x18),
+ stv0299_readreg (state, 0x19), (int) signal);
+
+ signal = signal * 5 / 4;
+ *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
+
+ return 0;
+}
+
+static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8)
+ | stv0299_readreg (state, 0x25));
+ xsnr = 3 * (xsnr - 0xa100);
+ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+
+ return 0;
+}
+
+static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0;
+ else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e);
+
+ return 0;
+}
+
+static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ int invval = 0;
+
+ dprintk ("%s : FE_SET_FRONTEND\n", __FUNCTION__);
+
+ // set the inversion
+ if (p->inversion == INVERSION_OFF) invval = 0;
+ else if (p->inversion == INVERSION_ON) invval = 1;
+ else {
+ printk("stv0299 does not support auto-inversion\n");
+ return -EINVAL;
+ }
+ if (state->config->invert) invval = (~invval) & 1;
+ stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval);
+
+ if (state->config->enhanced_tuning) {
+ /* check if we should do a finetune */
+ int frequency_delta = p->frequency - state->tuner_frequency;
+ int minmax = p->u.qpsk.symbol_rate / 2000;
+ if (minmax < 5000) minmax = 5000;
+
+ if ((frequency_delta > -minmax) && (frequency_delta < minmax) && (frequency_delta != 0) &&
+ (state->fec_inner == p->u.qpsk.fec_inner) &&
+ (state->symbol_rate == p->u.qpsk.symbol_rate)) {
+ int Drot_freq = (frequency_delta << 16) / (state->config->mclk / 1000);
+
+ // zap the derotator registers first
+ stv0299_writeregI(state, 0x22, 0x00);
+ stv0299_writeregI(state, 0x23, 0x00);
+
+ // now set them as we want
+ stv0299_writeregI(state, 0x22, Drot_freq >> 8);
+ stv0299_writeregI(state, 0x23, Drot_freq);
+ } else {
+ /* A "normal" tune is requested */
+ stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */
+ state->config->pll_set(fe, p);
+ stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */
+
+ stv0299_writeregI(state, 0x32, 0x80);
+ stv0299_writeregI(state, 0x22, 0x00);
+ stv0299_writeregI(state, 0x23, 0x00);
+ stv0299_writeregI(state, 0x32, 0x19);
+ stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate);
+ stv0299_set_FEC (state, p->u.qpsk.fec_inner);
+ }
+ } else {
+ stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */
+ state->config->pll_set(fe, p);
+ stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */
+
+ stv0299_set_FEC (state, p->u.qpsk.fec_inner);
+ stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate);
+ stv0299_writeregI(state, 0x22, 0x00);
+ stv0299_writeregI(state, 0x23, 0x00);
+ stv0299_readreg (state, 0x23);
+ stv0299_writeregI(state, 0x12, 0xb9);
+ }
+
+ state->tuner_frequency = p->frequency;
+ state->fec_inner = p->u.qpsk.fec_inner;
+ state->symbol_rate = p->u.qpsk.symbol_rate;
+
+ return 0;
+}
+
+static int stv0299_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ s32 derot_freq;
+ int invval;
+
+ derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8)
+ | stv0299_readreg (state, 0x23));
+
+ derot_freq *= (state->config->mclk >> 16);
+ derot_freq += 500;
+ derot_freq /= 1000;
+
+ p->frequency += derot_freq;
+
+ invval = stv0299_readreg (state, 0x0c) & 1;
+ if (state->config->invert) invval = (~invval) & 1;
+ p->inversion = invval ? INVERSION_ON : INVERSION_OFF;
+
+ p->u.qpsk.fec_inner = stv0299_get_fec (state);
+ p->u.qpsk.symbol_rate = stv0299_get_symbolrate (state);
+
+ return 0;
+}
+
+static int stv0299_sleep(struct dvb_frontend* fe)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ stv0299_writeregI(state, 0x02, 0x80);
+ state->initialised = 0;
+
+ return 0;
+}
+
+static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+
+ fesettings->min_delay_ms = state->config->min_delay_ms;
+ if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) {
+ fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000;
+ fesettings->max_drift = 5000;
+ } else {
+ fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000;
+ fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000;
+ }
+ return 0;
+}
+
+static void stv0299_release(struct dvb_frontend* fe)
+{
+ struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops stv0299_ops;
+
+struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct stv0299_state* state = NULL;
+ int id;
+
+ /* allocate memory for the internal state */
+ state = (struct stv0299_state*) kmalloc(sizeof(struct stv0299_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &stv0299_ops, sizeof(struct dvb_frontend_ops));
+ state->initialised = 0;
+ state->tuner_frequency = 0;
+ state->symbol_rate = 0;
+ state->fec_inner = 0;
+ state->errmode = STATUS_BER;
+
+ /* check if the demod is there */
+ stv0299_writeregI(state, 0x02, 0x34); /* standby off */
+ msleep(200);
+ id = stv0299_readreg(state, 0x00);
+
+ /* register 0x00 contains 0xa1 for STV0299 and STV0299B */
+ /* register 0x00 might contain 0x80 when returning from standby */
+ if (id != 0xa1 && id != 0x80) 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 stv0299_ops = {
+
+ .info = {
+ .name = "ST STV0299 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 125, /* kHz for QPSK frontends */
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = 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_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = stv0299_release,
+
+ .init = stv0299_init,
+ .sleep = stv0299_sleep,
+
+ .set_frontend = stv0299_set_frontend,
+ .get_frontend = stv0299_get_frontend,
+ .get_tune_settings = stv0299_get_tune_settings,
+
+ .read_status = stv0299_read_status,
+ .read_ber = stv0299_read_ber,
+ .read_signal_strength = stv0299_read_signal_strength,
+ .read_snr = stv0299_read_snr,
+ .read_ucblocks = stv0299_read_ucblocks,
+
+ .diseqc_send_master_cmd = stv0299_send_diseqc_msg,
+ .diseqc_send_burst = stv0299_send_diseqc_burst,
+ .set_tone = stv0299_set_tone,
+ .set_voltage = stv0299_set_voltage,
+ .dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, "
+ "Andreas Oberritter, Andrew de Quincey, Kenneth Aafløy");
+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
new file mode 100644
index 000000000000..79457a80a11f
--- /dev/null
+++ b/drivers/media/dvb/frontends/stv0299.h
@@ -0,0 +1,104 @@
+/*
+ Driver for ST STV0299 demodulator
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ <ralph@convergence.de>,
+ <holger@convergence.de>,
+ <js@convergence.de>
+
+
+ Philips SU1278/SH
+
+ Copyright (C) 2002 by Peter Schildmann <peter.schildmann@web.de>
+
+
+ LG TDQF-S001F
+
+ Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net>
+ & Andreas Oberritter <obi@linuxtv.org>
+
+
+ Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B
+
+ Copyright (C) 2003 Vadim Catana <skystar@moldova.cc>:
+
+ Support for Philips SU1278 on Technotrend hardware
+
+ Copyright (C) 2004 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 STV0299_H
+#define STV0299_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#define STV0229_LOCKOUTPUT_0 0
+#define STV0229_LOCKOUTPUT_1 1
+#define STV0229_LOCKOUTPUT_CF 2
+#define STV0229_LOCKOUTPUT_LK 3
+
+#define STV0299_VOLT13_OP0 0
+#define STV0299_VOLT13_OP1 1
+
+struct stv0299_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* inittab - array of pairs of values.
+ * First of each pair is the register, second is the value.
+ * List should be terminated with an 0xff, 0xff pair.
+ */
+ u8* inittab;
+
+ /* master clock to use */
+ u32 mclk;
+
+ /* does the inversion require inversion? */
+ u8 invert:1;
+
+ /* Should the enhanced tuning code be used? */
+ u8 enhanced_tuning:1;
+
+ /* Skip reinitialisation? */
+ u8 skip_reinit:1;
+
+ /* LOCK OUTPUT setting */
+ u8 lock_output:2;
+
+ /* Is 13v controlled by OP0 or OP1? */
+ u8 volt13_op0_op1:1;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+
+ /* Set the symbol rate */
+ int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data);
+
+extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // STV0299_H
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
new file mode 100644
index 000000000000..4e40d95ee95d
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10021.c
@@ -0,0 +1,469 @@
+/*
+ TDA10021 - Single Chip Cable Channel Receiver driver module
+ used on the the Siemens DVB-C cards
+
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+ Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+ Support for TDA10021
+
+ 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/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda10021.h"
+
+
+struct tda10021_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ /* configuration settings */
+ const struct tda10021_config* config;
+ struct dvb_frontend frontend;
+
+ u8 pwm;
+ u8 reg0;
+};
+
+
+#if 0
+#define dprintk(x...) printk(x)
+#else
+#define dprintk(x...)
+#endif
+
+static int verbose;
+
+#define XIN 57840000UL
+#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0)
+#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0)
+#define HAS_INVERSION(reg0) (!(reg0 & 0x20))
+
+#define FIN (XIN >> 4)
+
+static int tda10021_inittab_size = 0x40;
+static u8 tda10021_inittab[0x40]=
+{
+ 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
+ 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
+ 0xb8, 0x3f, 0xa0, 0x00, 0xcd, 0x01, 0x00, 0xff,
+ 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
+ 0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
+ 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
+};
+
+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 };
+ int ret;
+
+ ret = i2c_transfer (state->i2c, &msg, 1);
+ if (ret != 1)
+ printk("DVB: TDA10021(%d): %s, writereg error "
+ "(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+ state->frontend.dvb->num, __FUNCTION__, reg, data, ret);
+
+ msleep(10);
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
+{
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+ int ret;
+
+ ret = i2c_transfer (state->i2c, msg, 2);
+ if (ret != 2)
+ printk("DVB: TDA10021(%d): %s: readreg error (ret == %i)\n",
+ state->frontend.dvb->num, __FUNCTION__, ret);
+ return b1[0];
+}
+
+//get access to tuner
+static int lock_tuner(struct tda10021_state* state)
+{
+ u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
+ struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+ if(i2c_transfer(state->i2c, &msg, 1) != 1)
+ {
+ printk("tda10021: lock tuner fails\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+//release access from tuner
+static int unlock_tuner(struct tda10021_state* state)
+{
+ u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
+ struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2};
+
+ if(i2c_transfer(state->i2c, &msg_post, 1) != 1)
+ {
+ printk("tda10021: unlock tuner fails\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0,
+ fe_spectral_inversion_t inversion)
+{
+ reg0 |= state->reg0 & 0x63;
+
+ if (INVERSION_ON == inversion)
+ ENABLE_INVERSION(reg0);
+ else if (INVERSION_OFF == inversion)
+ DISABLE_INVERSION(reg0);
+
+ tda10021_writereg (state, 0x00, reg0 & 0xfe);
+ tda10021_writereg (state, 0x00, reg0 | 0x01);
+
+ state->reg0 = reg0;
+ return 0;
+}
+
+static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate)
+{
+ s32 BDR;
+ s32 BDRI;
+ s16 SFIL=0;
+ u16 NDEC = 0;
+ u32 tmp, ratio;
+
+ if (symbolrate > XIN/2)
+ symbolrate = XIN/2;
+ if (symbolrate < 500000)
+ symbolrate = 500000;
+
+ if (symbolrate < XIN/16) NDEC = 1;
+ if (symbolrate < XIN/32) NDEC = 2;
+ if (symbolrate < XIN/64) NDEC = 3;
+
+ if (symbolrate < (u32)(XIN/12.3)) SFIL = 1;
+ if (symbolrate < (u32)(XIN/16)) SFIL = 0;
+ if (symbolrate < (u32)(XIN/24.6)) SFIL = 1;
+ if (symbolrate < (u32)(XIN/32)) SFIL = 0;
+ if (symbolrate < (u32)(XIN/49.2)) SFIL = 1;
+ if (symbolrate < (u32)(XIN/64)) SFIL = 0;
+ if (symbolrate < (u32)(XIN/98.4)) SFIL = 1;
+
+ symbolrate <<= NDEC;
+ ratio = (symbolrate << 4) / FIN;
+ tmp = ((symbolrate << 4) % FIN) << 8;
+ ratio = (ratio << 8) + tmp / FIN;
+ tmp = (tmp % FIN) << 8;
+ ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
+
+ BDR = ratio;
+ BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
+
+ if (BDRI > 0xFF)
+ BDRI = 0xFF;
+
+ SFIL = (SFIL << 4) | tda10021_inittab[0x0E];
+
+ 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, 0x0d, BDRI);
+ tda10021_writereg (state, 0x0e, SFIL);
+
+ return 0;
+}
+
+static int tda10021_init (struct dvb_frontend *fe)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+ int i;
+
+ dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
+
+ //tda10021_writereg (fe, 0, 0);
+
+ for (i=0; i<tda10021_inittab_size; i++)
+ tda10021_writereg (state, i, tda10021_inittab[i]);
+
+ tda10021_writereg (state, 0x34, state->pwm);
+
+ //Comment by markus
+ //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
+ //0x2A[4] == BYPPLL -> Power down mode (default 1)
+ //0x2A[5] == LCK -> PLL Lock Flag
+ //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
+
+ //Activate PLL
+ tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
+
+ if (state->config->pll_init) {
+ lock_tuner(state);
+ state->config->pll_init(fe);
+ unlock_tuner(state);
+ }
+
+ return 0;
+}
+
+static int tda10021_set_parameters (struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+ //table for QAM4-QAM256 ready QAM4 QAM16 QAM32 QAM64 QAM128 QAM256
+ //CONF
+ static const u8 reg0x00 [] = { 0x14, 0x00, 0x04, 0x08, 0x0c, 0x10 };
+ //AGCREF value
+ static const u8 reg0x01 [] = { 0x78, 0x8c, 0x8c, 0x6a, 0x78, 0x5c };
+ //LTHR value
+ static const u8 reg0x05 [] = { 0x78, 0x87, 0x64, 0x46, 0x36, 0x26 };
+ //MSETH
+ static const u8 reg0x08 [] = { 0x8c, 0xa2, 0x74, 0x43, 0x34, 0x23 };
+ //AREF
+ static const u8 reg0x09 [] = { 0x96, 0x91, 0x96, 0x6a, 0x7e, 0x6b };
+
+ int qam = p->u.qam.modulation;
+
+ if (qam < 0 || qam > 5)
+ return -EINVAL;
+
+ //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate);
+
+ lock_tuner(state);
+ state->config->pll_set(fe, p);
+ unlock_tuner(state);
+
+ tda10021_set_symbolrate (state, p->u.qam.symbol_rate);
+ 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_setup_reg0 (state, reg0x00[qam], p->inversion);
+
+ return 0;
+}
+
+static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+ int sync;
+
+ *status = 0;
+ //0x11[0] == EQALGO -> Equalizer algorithms state
+ //0x11[1] == CARLOCK -> Carrier locked
+ //0x11[2] == FSYNC -> Frame synchronisation
+ //0x11[3] == FEL -> Front End locked
+ //0x11[6] == NODVB -> DVB Mode Information
+ sync = tda10021_readreg (state, 0x11);
+
+ if (sync & 2)
+ *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER;
+
+ if (sync & 4)
+ *status |= FE_HAS_SYNC|FE_HAS_VITERBI;
+
+ if (sync & 8)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+ u32 _ber = tda10021_readreg(state, 0x14) |
+ (tda10021_readreg(state, 0x15) << 8) |
+ ((tda10021_readreg(state, 0x16) & 0x0f) << 16);
+ *ber = 10 * _ber;
+
+ return 0;
+}
+
+static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+ u8 gain = tda10021_readreg(state, 0x17);
+ *strength = (gain << 8) | gain;
+
+ return 0;
+}
+
+static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+ u8 quality = ~tda10021_readreg(state, 0x18);
+ *snr = (quality << 8) | quality;
+
+ return 0;
+}
+
+static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+ *ucblocks = tda10021_readreg (state, 0x13) & 0x7f;
+ if (*ucblocks == 0x7f)
+ *ucblocks = 0xffffffff;
+
+ /* reset uncorrected block counter */
+ tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
+ tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
+
+ return 0;
+}
+
+static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+ int sync;
+ s8 afc = 0;
+
+ sync = tda10021_readreg(state, 0x11);
+ afc = tda10021_readreg(state, 0x19);
+ if (verbose) {
+ /* AFC only valid when carrier has been recovered */
+ printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" :
+ "DVB: TDA10021(%d): [AFC (%d) %dHz]\n",
+ state->frontend.dvb->num, afc,
+ -((s32)p->u.qam.symbol_rate * afc) >> 10);
+ }
+
+ p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
+ p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+ p->u.qam.fec_inner = FEC_NONE;
+ p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+
+ if (sync & 2)
+ p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10;
+
+ return 0;
+}
+
+static int tda10021_sleep(struct dvb_frontend* fe)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+
+ tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */
+ tda10021_writereg (state, 0x00, 0x80); /* standby */
+
+ return 0;
+}
+
+static void tda10021_release(struct dvb_frontend* fe)
+{
+ struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda10021_ops;
+
+struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+ struct i2c_adapter* i2c,
+ u8 pwm)
+{
+ struct tda10021_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct tda10021_state*) kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &tda10021_ops, sizeof(struct dvb_frontend_ops));
+ state->pwm = pwm;
+ state->reg0 = tda10021_inittab[0];
+
+ /* check if the demod is there */
+ if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) 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 tda10021_ops = {
+
+ .info = {
+ .name = "Philips TDA10021 DVB-C",
+ .type = FE_QAM,
+ .frequency_stepsize = 62500,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */
+ .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */
+ #if 0
+ .frequency_tolerance = ???,
+ .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */
+ #endif
+ .caps = 0x400 | //FE_CAN_QAM_4
+ FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256 |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = tda10021_release,
+
+ .init = tda10021_init,
+ .sleep = tda10021_sleep,
+
+ .set_frontend = tda10021_set_parameters,
+ .get_frontend = tda10021_get_frontend,
+
+ .read_status = tda10021_read_status,
+ .read_ber = tda10021_read_ber,
+ .read_signal_strength = tda10021_read_signal_strength,
+ .read_snr = tda10021_read_snr,
+ .read_ucblocks = tda10021_read_ucblocks,
+};
+
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
+
+MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10021_attach);
diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h
new file mode 100644
index 000000000000..7d6a51ce291e
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10021.h
@@ -0,0 +1,42 @@
+/*
+ TDA10021 - Single Chip Cable Channel Receiver driver module
+ used on the the Siemens DVB-C cards
+
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+ Copyright (C) 2004 Markus Schulz <msc@antzsystem.de>
+ Support for TDA10021
+
+ 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 TDA10021_H
+#define TDA10021_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda10021_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);
+};
+
+extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+ struct i2c_adapter* i2c, u8 pwm);
+
+#endif // TDA10021_H
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
new file mode 100644
index 000000000000..687ad9cf3384
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -0,0 +1,1206 @@
+ /*
+ Driver for Philips tda1004xh OFDM Demodulator
+
+ (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach
+
+ 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.
+
+ */
+/*
+ * This driver needs external firmware. Please use the commands
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ * download/extract them, and then copy them to /usr/lib/hotplug/firmware.
+ */
+#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw"
+#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include "dvb_frontend.h"
+#include "tda1004x.h"
+
+#define TDA1004X_DEMOD_TDA10045 0
+#define TDA1004X_DEMOD_TDA10046 1
+
+
+struct tda1004x_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ const struct tda1004x_config* config;
+ struct dvb_frontend frontend;
+
+ /* private demod data */
+ u8 initialised:1;
+ u8 demod_type;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda1004x: " args); \
+ } while (0)
+
+#define TDA1004X_CHIPID 0x00
+#define TDA1004X_AUTO 0x01
+#define TDA1004X_IN_CONF1 0x02
+#define TDA1004X_IN_CONF2 0x03
+#define TDA1004X_OUT_CONF1 0x04
+#define TDA1004X_OUT_CONF2 0x05
+#define TDA1004X_STATUS_CD 0x06
+#define TDA1004X_CONFC4 0x07
+#define TDA1004X_DSSPARE2 0x0C
+#define TDA10045H_CODE_IN 0x0D
+#define TDA10045H_FWPAGE 0x0E
+#define TDA1004X_SCAN_CPT 0x10
+#define TDA1004X_DSP_CMD 0x11
+#define TDA1004X_DSP_ARG 0x12
+#define TDA1004X_DSP_DATA1 0x13
+#define TDA1004X_DSP_DATA2 0x14
+#define TDA1004X_CONFADC1 0x15
+#define TDA1004X_CONFC1 0x16
+#define TDA10045H_S_AGC 0x1a
+#define TDA10046H_AGC_TUN_LEVEL 0x1a
+#define TDA1004X_SNR 0x1c
+#define TDA1004X_CONF_TS1 0x1e
+#define TDA1004X_CONF_TS2 0x1f
+#define TDA1004X_CBER_RESET 0x20
+#define TDA1004X_CBER_MSB 0x21
+#define TDA1004X_CBER_LSB 0x22
+#define TDA1004X_CVBER_LUT 0x23
+#define TDA1004X_VBER_MSB 0x24
+#define TDA1004X_VBER_MID 0x25
+#define TDA1004X_VBER_LSB 0x26
+#define TDA1004X_UNCOR 0x27
+
+#define TDA10045H_CONFPLL_P 0x2D
+#define TDA10045H_CONFPLL_M_MSB 0x2E
+#define TDA10045H_CONFPLL_M_LSB 0x2F
+#define TDA10045H_CONFPLL_N 0x30
+
+#define TDA10046H_CONFPLL1 0x2D
+#define TDA10046H_CONFPLL2 0x2F
+#define TDA10046H_CONFPLL3 0x30
+#define TDA10046H_TIME_WREF1 0x31
+#define TDA10046H_TIME_WREF2 0x32
+#define TDA10046H_TIME_WREF3 0x33
+#define TDA10046H_TIME_WREF4 0x34
+#define TDA10046H_TIME_WREF5 0x35
+
+#define TDA10045H_UNSURW_MSB 0x31
+#define TDA10045H_UNSURW_LSB 0x32
+#define TDA10045H_WREF_MSB 0x33
+#define TDA10045H_WREF_MID 0x34
+#define TDA10045H_WREF_LSB 0x35
+#define TDA10045H_MUXOUT 0x36
+#define TDA1004X_CONFADC2 0x37
+
+#define TDA10045H_IOFFSET 0x38
+
+#define TDA10046H_CONF_TRISTATE1 0x3B
+#define TDA10046H_CONF_TRISTATE2 0x3C
+#define TDA10046H_CONF_POLARITY 0x3D
+#define TDA10046H_FREQ_OFFSET 0x3E
+#define TDA10046H_GPIO_OUT_SEL 0x41
+#define TDA10046H_GPIO_SELECT 0x42
+#define TDA10046H_AGC_CONF 0x43
+#define TDA10046H_AGC_GAINS 0x46
+#define TDA10046H_AGC_TUN_MIN 0x47
+#define TDA10046H_AGC_TUN_MAX 0x48
+#define TDA10046H_AGC_IF_MIN 0x49
+#define TDA10046H_AGC_IF_MAX 0x4A
+
+#define TDA10046H_FREQ_PHY2_MSB 0x4D
+#define TDA10046H_FREQ_PHY2_LSB 0x4E
+
+#define TDA10046H_CVBER_CTRL 0x4F
+#define TDA10046H_AGC_IF_LEVEL 0x52
+#define TDA10046H_CODE_CPT 0x57
+#define TDA10046H_CODE_IN 0x58
+
+
+static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .addr=0, .flags=0, .buf=buf, .len=2 };
+
+ dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data);
+
+ 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);
+
+ dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,
+ reg, data, ret);
+ return (ret != 1) ? -1 : 0;
+}
+
+static int tda1004x_read_byte(struct tda1004x_state *state, int reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {{ .addr=0, .flags=0, .buf=b0, .len=1},
+ { .addr=0, .flags=I2C_M_RD, .buf=b1, .len = 1}};
+
+ dprintk("%s: reg=0x%x\n", __FUNCTION__, reg);
+
+ 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 -1;
+ }
+
+ dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__,
+ reg, b1[0], ret);
+ return b1[0];
+}
+
+static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data)
+{
+ int val;
+ dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg,
+ mask, data);
+
+ // read a byte and check
+ val = tda1004x_read_byte(state, reg);
+ if (val < 0)
+ return val;
+
+ // mask if off
+ val = val & ~mask;
+ val |= data & 0xff;
+
+ // write it out again
+ return tda1004x_write_byteI(state, reg, val);
+}
+
+static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len)
+{
+ int i;
+ int result;
+
+ dprintk("%s: reg=0x%x, len=0x%x\n", __FUNCTION__, reg, len);
+
+ result = 0;
+ for (i = 0; i < len; i++) {
+ result = tda1004x_write_byteI(state, reg + i, buf[i]);
+ if (result != 0)
+ break;
+ }
+
+ return result;
+}
+
+static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state)
+{
+ int result;
+ dprintk("%s\n", __FUNCTION__);
+
+ result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2);
+ msleep(1);
+ return result;
+}
+
+static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state)
+{
+ dprintk("%s\n", __FUNCTION__);
+
+ return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0);
+}
+
+static int tda10045h_set_bandwidth(struct tda1004x_state *state,
+ fe_bandwidth_t bandwidth)
+{
+ static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f };
+ static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb };
+ static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 };
+
+ switch (bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+ break;
+
+ case BANDWIDTH_7_MHZ:
+ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+ break;
+
+ case BANDWIDTH_8_MHZ:
+ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0);
+
+ return 0;
+}
+
+static int tda10046h_set_bandwidth(struct tda1004x_state *state,
+ fe_bandwidth_t bandwidth)
+{
+ static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e };
+ static u8 bandwidth_7mhz[] = { 0x6e, 0x02, 0x53, 0xc8, 0x25 };
+ static u8 bandwidth_8mhz[] = { 0x60, 0x12, 0xa8, 0xe4, 0xbd };
+
+ switch (bandwidth) {
+ case BANDWIDTH_6_MHZ:
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+ break;
+
+ case BANDWIDTH_7_MHZ:
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+ break;
+
+ case BANDWIDTH_8_MHZ:
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tda1004x_do_upload(struct tda1004x_state *state,
+ unsigned char *mem, unsigned int len,
+ u8 dspCodeCounterReg, u8 dspCodeInReg)
+{
+ u8 buf[65];
+ struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 };
+ int tx_size;
+ int pos = 0;
+
+ /* clear code counter */
+ tda1004x_write_byteI(state, dspCodeCounterReg, 0);
+ fw_msg.addr = state->config->demod_address;
+
+ buf[0] = dspCodeInReg;
+ while (pos != len) {
+
+ // work out how much to send this time
+ tx_size = len - pos;
+ if (tx_size > 0x10) {
+ tx_size = 0x10;
+ }
+
+ // send the chunk
+ 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");
+ return -EIO;
+ }
+ pos += tx_size;
+
+ dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos);
+ }
+ return 0;
+}
+
+static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion)
+{
+ u8 data1, data2;
+
+ // check upload was OK
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
+ tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67);
+
+ data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);
+ data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);
+ if (data1 != 0x67 || data2 != dspVersion) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int tda10045_fwupload(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int ret;
+ const struct firmware *fw;
+
+
+ /* don't re-upload unless necessary */
+ if (tda1004x_check_upload_ok(state, 0x2c) == 0) return 0;
+
+ /* request the firmware, this will block until someone uploads it */
+ printk("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");
+ return ret;
+ }
+
+ /* reset chip */
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+ msleep(10);
+
+ /* set parameters */
+ tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ);
+
+ ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);
+ if (ret)
+ return ret;
+ printk("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);
+}
+
+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_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
+ msleep(100);
+
+ /* don't re-upload unless necessary */
+ if (tda1004x_check_upload_ok(state, 0x20) == 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;
+ }
+
+ /* set parameters */
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10);
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0);
+ 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;
+ }
+ msleep(1);
+ }
+
+ return tda1004x_check_upload_ok(state, 0x20);
+}
+
+static int tda1004x_encode_fec(int fec)
+{
+ // convert known FEC values
+ switch (fec) {
+ case FEC_1_2:
+ return 0;
+ case FEC_2_3:
+ return 1;
+ case FEC_3_4:
+ return 2;
+ case FEC_5_6:
+ return 3;
+ case FEC_7_8:
+ return 4;
+ }
+
+ // unsupported
+ return -EINVAL;
+}
+
+static int tda1004x_decode_fec(int tdafec)
+{
+ // convert known FEC values
+ switch (tdafec) {
+ 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_7_8;
+ }
+
+ // unsupported
+ return -1;
+}
+
+int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+
+ return tda1004x_write_byteI(state, reg, data);
+}
+
+static int tda10045_init(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ if (state->initialised) return 0;
+
+ if (tda10045_fwupload(fe)) {
+ printk("tda1004x: firmware upload failed\n");
+ return -EIO;
+ }
+
+ tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
+
+ // Init the PLL
+ if (state->config->pll_init) {
+ tda1004x_enable_tuner_i2c(state);
+ state->config->pll_init(fe);
+ tda1004x_disable_tuner_i2c(state);
+ }
+
+ // tda setup
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+ tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
+ tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
+ tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
+ tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
+ tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
+ tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e);
+
+ tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk);
+
+ state->initialised = 1;
+ return 0;
+}
+
+static int tda10046_init(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ dprintk("%s\n", __FUNCTION__);
+
+ if (state->initialised) return 0;
+
+ if (tda10046_fwupload(fe)) {
+ printk("tda1004x: firmware upload failed\n");
+ return -EIO;
+ }
+
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); // wake up the chip
+
+ // Init the PLL
+ if (state->config->pll_init) {
+ tda1004x_enable_tuner_i2c(state);
+ state->config->pll_init(fe);
+ tda1004x_disable_tuner_i2c(state);
+ }
+
+ // 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, 0); // 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, 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, 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, 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);
+
+ state->initialised = 1;
+ return 0;
+}
+
+static int tda1004x_set_fe(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fe_params)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+ int inversion;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+ // setup auto offset
+ tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0);
+
+ // disable agc_conf[2]
+ tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0);
+ }
+
+ // set frequency
+ tda1004x_enable_tuner_i2c(state);
+ 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) {
+ fe_params->u.ofdm.code_rate_HP = FEC_AUTO;
+ fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+ fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+ }
+
+ // Set standard params.. or put them to auto
+ if ((fe_params->u.ofdm.code_rate_HP == FEC_AUTO) ||
+ (fe_params->u.ofdm.code_rate_LP == FEC_AUTO) ||
+ (fe_params->u.ofdm.constellation == QAM_AUTO) ||
+ (fe_params->u.ofdm.hierarchy_information == HIERARCHY_AUTO)) {
+ tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); // turn off constellation bits
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits
+ } else {
+ tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto
+
+ // set HP FEC
+ tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_HP);
+ if (tmp < 0) return tmp;
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp);
+
+ // set LP FEC
+ tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_LP);
+ if (tmp < 0) return tmp;
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3);
+
+ // set constellation
+ switch (fe_params->u.ofdm.constellation) {
+ case QPSK:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0);
+ break;
+
+ case QAM_16:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1);
+ break;
+
+ case QAM_64:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // set hierarchy
+ switch (fe_params->u.ofdm.hierarchy_information) {
+ case HIERARCHY_NONE:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5);
+ break;
+
+ case HIERARCHY_1:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5);
+ break;
+
+ case HIERARCHY_2:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5);
+ break;
+
+ case HIERARCHY_4:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ // set bandwidth
+ switch(state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ tda10045h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ tda10046h_set_bandwidth(state, fe_params->u.ofdm.bandwidth);
+ break;
+ }
+
+ // set inversion
+ inversion = fe_params->inversion;
+ if (state->config->invert) inversion = inversion ? INVERSION_OFF : INVERSION_ON;
+ switch (inversion) {
+ case INVERSION_OFF:
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0);
+ break;
+
+ case INVERSION_ON:
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // set guard interval
+ switch (fe_params->u.ofdm.guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+ break;
+
+ case GUARD_INTERVAL_1_16:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2);
+ break;
+
+ case GUARD_INTERVAL_1_8:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2);
+ break;
+
+ case GUARD_INTERVAL_1_4:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2);
+ break;
+
+ case GUARD_INTERVAL_AUTO:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // set transmission mode
+ switch (fe_params->u.ofdm.transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4);
+ break;
+
+ case TRANSMISSION_MODE_8K:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4);
+ break;
+
+ case TRANSMISSION_MODE_AUTO:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // start the lock
+ switch(state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+ msleep(10);
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40);
+ msleep(10);
+ break;
+ }
+
+ return 0;
+}
+
+static int tda1004x_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ dprintk("%s\n", __FUNCTION__);
+
+ // inversion status
+ fe_params->inversion = INVERSION_OFF;
+ if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) {
+ fe_params->inversion = INVERSION_ON;
+ }
+ if (state->config->invert) fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
+
+ // bandwidth
+ switch(state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) {
+ case 0x14:
+ fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ case 0xdb:
+ fe_params->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 0x4f:
+ fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ }
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) {
+ case 0x60:
+ fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ case 0x6e:
+ fe_params->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 0x80:
+ fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ }
+ break;
+ }
+
+ // FEC
+ fe_params->u.ofdm.code_rate_HP =
+ tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7);
+ fe_params->u.ofdm.code_rate_LP =
+ tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7);
+
+ // constellation
+ switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) {
+ case 0:
+ fe_params->u.ofdm.constellation = QPSK;
+ break;
+ case 1:
+ fe_params->u.ofdm.constellation = QAM_16;
+ break;
+ case 2:
+ fe_params->u.ofdm.constellation = QAM_64;
+ break;
+ }
+
+ // transmission mode
+ fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+ if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) {
+ fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ // guard interval
+ switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) {
+ case 0:
+ fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ // hierarchy
+ switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) {
+ case 0:
+ fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ break;
+ case 1:
+ fe_params->u.ofdm.hierarchy_information = HIERARCHY_1;
+ break;
+ case 2:
+ fe_params->u.ofdm.hierarchy_information = HIERARCHY_2;
+ break;
+ case 3:
+ fe_params->u.ofdm.hierarchy_information = HIERARCHY_4;
+ break;
+ }
+
+ return 0;
+}
+
+static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int status;
+ int cber;
+ int vber;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ // read status
+ status = tda1004x_read_byte(state, TDA1004X_STATUS_CD);
+ if (status == -1) {
+ return -EIO;
+ }
+
+ // decode
+ *fe_status = 0;
+ if (status & 4) *fe_status |= FE_HAS_SIGNAL;
+ if (status & 2) *fe_status |= FE_HAS_CARRIER;
+ if (status & 8) *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+
+ // if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi
+ // is getting anything valid
+ if (!(*fe_status & FE_HAS_VITERBI)) {
+ // read the CBER
+ cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
+ if (cber == -1) return -EIO;
+ status = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+ if (status == -1) return -EIO;
+ cber |= (status << 8);
+ tda1004x_read_byte(state, TDA1004X_CBER_RESET);
+
+ if (cber != 65535) {
+ *fe_status |= FE_HAS_VITERBI;
+ }
+ }
+
+ // if we DO have some valid VITERBI output, but don't already have SYNC
+ // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid.
+ if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) {
+ // read the VBER
+ vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB);
+ if (vber == -1) return -EIO;
+ status = tda1004x_read_byte(state, TDA1004X_VBER_MID);
+ if (status == -1) return -EIO;
+ vber |= (status << 8);
+ status = tda1004x_read_byte(state, TDA1004X_VBER_MSB);
+ if (status == -1) return -EIO;
+ vber |= ((status << 16) & 0x0f);
+ tda1004x_read_byte(state, TDA1004X_CVBER_LUT);
+
+ // if RS has passed some valid TS packets, then we must be
+ // getting some SYNC bytes
+ if (vber < 16632) {
+ *fe_status |= FE_HAS_SYNC;
+ }
+ }
+
+ // success
+ dprintk("%s: fe_status=0x%x\n", __FUNCTION__, *fe_status);
+ return 0;
+}
+
+static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+ int reg = 0;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ // determine the register to use
+ switch(state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ reg = TDA10045H_S_AGC;
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ reg = TDA10046H_AGC_IF_LEVEL;
+ break;
+ }
+
+ // read it
+ tmp = tda1004x_read_byte(state, reg);
+ if (tmp < 0)
+ return -EIO;
+
+ *signal = (tmp << 8) | tmp;
+ dprintk("%s: signal=0x%x\n", __FUNCTION__, *signal);
+ return 0;
+}
+
+static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ // read it
+ tmp = tda1004x_read_byte(state, TDA1004X_SNR);
+ if (tmp < 0)
+ return -EIO;
+ if (tmp) {
+ tmp = 255 - tmp;
+ }
+
+ *snr = ((tmp << 8) | tmp);
+ dprintk("%s: snr=0x%x\n", __FUNCTION__, *snr);
+ return 0;
+}
+
+static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+ int tmp2;
+ int counter;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ // read the UCBLOCKS and reset
+ counter = 0;
+ tmp = tda1004x_read_byte(state, TDA1004X_UNCOR);
+ if (tmp < 0)
+ return -EIO;
+ tmp &= 0x7f;
+ while (counter++ < 5) {
+ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+
+ tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR);
+ if (tmp2 < 0)
+ return -EIO;
+ tmp2 &= 0x7f;
+ if ((tmp2 < tmp) || (tmp2 == 0))
+ break;
+ }
+
+ if (tmp != 0x7f) {
+ *ucblocks = tmp;
+ } else {
+ *ucblocks = 0xffffffff;
+ }
+ dprintk("%s: ucblocks=0x%x\n", __FUNCTION__, *ucblocks);
+ return 0;
+}
+
+static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ // read it in
+ tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
+ if (tmp < 0) return -EIO;
+ *ber = tmp << 1;
+ tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+ if (tmp < 0) return -EIO;
+ *ber |= (tmp << 9);
+ tda1004x_read_byte(state, TDA1004X_CBER_RESET);
+
+ dprintk("%s: ber=0x%x\n", __FUNCTION__, *ber);
+ return 0;
+}
+
+static int tda1004x_sleep(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+
+ switch(state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10);
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
+ break;
+ }
+ state->initialised = 0;
+
+ return 0;
+}
+
+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;
+ return 0;
+}
+
+static void tda1004x_release(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = (struct tda1004x_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda10045_ops;
+
+struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda1004x_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops));
+ state->initialised = 0;
+ state->demod_type = TDA1004X_DEMOD_TDA10045;
+
+ /* check if the demod is there */
+ if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) 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 tda10046_ops;
+
+struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda1004x_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops));
+ state->initialised = 0;
+ state->demod_type = TDA1004X_DEMOD_TDA10046;
+
+ /* check if the demod is there */
+ if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) goto error;
+
+ /* create dvb_frontend */
+ state->frontend.ops = &state->ops;
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ if (state) kfree(state);
+ return NULL;
+}
+
+static struct dvb_frontend_ops tda10045_ops = {
+
+ .info = {
+ .name = "Philips TDA10045H DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166667,
+ .caps =
+ 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
+ },
+
+ .release = tda1004x_release,
+
+ .init = tda10045_init,
+ .sleep = tda1004x_sleep,
+
+ .set_frontend = tda1004x_set_fe,
+ .get_frontend = tda1004x_get_fe,
+ .get_tune_settings = tda1004x_get_tune_settings,
+
+ .read_status = tda1004x_read_status,
+ .read_ber = tda1004x_read_ber,
+ .read_signal_strength = tda1004x_read_signal_strength,
+ .read_snr = tda1004x_read_snr,
+ .read_ucblocks = tda1004x_read_ucblocks,
+};
+
+static struct dvb_frontend_ops tda10046_ops = {
+
+ .info = {
+ .name = "Philips TDA10046H DVB-T",
+ .type = FE_OFDM,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166667,
+ .caps =
+ 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
+ },
+
+ .release = tda1004x_release,
+
+ .init = tda10046_init,
+ .sleep = tda1004x_sleep,
+
+ .set_frontend = tda1004x_set_fe,
+ .get_frontend = tda1004x_get_fe,
+ .get_tune_settings = tda1004x_get_tune_settings,
+
+ .read_status = tda1004x_read_status,
+ .read_ber = tda1004x_read_ber,
+ .read_signal_strength = tda1004x_read_signal_strength,
+ .read_snr = tda1004x_read_snr,
+ .read_ucblocks = tda1004x_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator");
+MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach");
+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
new file mode 100644
index 000000000000..e452fc0bad11
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -0,0 +1,56 @@
+ /*
+ Driver for Philips tda1004xh OFDM Frontend
+
+ (c) 2004 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 TDA1004X_H
+#define TDA1004X_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct tda1004x_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* does the "inversion" need inverted? */
+ u8 invert:1;
+
+ /* Does the OCLK signal need inverted? */
+ u8 invert_oclk:1;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+
+ /* request firmware for device */
+ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
+};
+
+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);
+
+#endif // TDA1004X_H
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c
new file mode 100644
index 000000000000..da82e90d6d13
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8083.c
@@ -0,0 +1,456 @@
+/*
+ Driver for Philips TDA8083 based QPSK Demodulator
+
+ Copyright (C) 2001 Convergence Integrated Media GmbH
+
+ written by Ralph Metzler <ralph@convergence.de>
+
+ adoption to the new DVB frontend API and diagnostic ioctl's
+ by Holger Waechtler <holger@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "tda8083.h"
+
+
+struct tda8083_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ /* configuration settings */
+ const struct tda8083_config* config;
+ struct dvb_frontend frontend;
+};
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda8083: " args); \
+ } while (0)
+
+
+static u8 tda8083_init_tab [] = {
+ 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+ 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+ 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+ 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+ 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+
+static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk ("%s: writereg error (reg %02x, ret == %i)\n",
+ __FUNCTION__, reg, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len)
+{
+ int ret;
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg1, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk ("%s: readreg error (reg %02x, ret == %i)\n",
+ __FUNCTION__, reg1, ret);
+
+ return ret == 2 ? 0 : -1;
+}
+
+static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg)
+{
+ u8 val;
+
+ tda8083_readregs (state, reg, &val, 1);
+
+ return val;
+}
+
+static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion)
+{
+ /* XXX FIXME: implement other modes than FEC_AUTO */
+ if (inversion == INVERSION_AUTO)
+ return 0;
+
+ return -EINVAL;
+}
+
+static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec)
+{
+ if (fec == FEC_AUTO)
+ return tda8083_writereg (state, 0x07, 0xff);
+
+ if (fec >= FEC_1_2 && fec <= FEC_8_9)
+ return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec));
+
+ return -EINVAL;
+}
+
+static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state)
+{
+ u8 index;
+ static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+ FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 };
+
+ index = tda8083_readreg(state, 0x0e) & 0x07;
+
+ return fec_tab [index];
+}
+
+static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate)
+{
+ u32 ratio;
+ u32 tmp;
+ u8 filter;
+
+ if (srate > 32000000)
+ srate = 32000000;
+ if (srate < 500000)
+ srate = 500000;
+
+ filter = 0;
+ if (srate < 24000000)
+ filter = 2;
+ if (srate < 16000000)
+ filter = 3;
+
+ tmp = 31250 << 16;
+ ratio = tmp / srate;
+
+ tmp = (tmp % srate) << 8;
+ ratio = (ratio << 8) + tmp / srate;
+
+ tmp = (tmp % srate) << 8;
+ ratio = (ratio << 8) + tmp / srate;
+
+ dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio);
+
+ tda8083_writereg (state, 0x05, filter);
+ tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff);
+ tda8083_writereg (state, 0x03, (ratio >> 8) & 0xff);
+ tda8083_writereg (state, 0x04, (ratio ) & 0xff);
+
+ tda8083_writereg (state, 0x00, 0x3c);
+ tda8083_writereg (state, 0x00, 0x04);
+
+ return 1;
+}
+
+static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout)
+{
+ unsigned long start = jiffies;
+
+ while (jiffies - start < timeout &&
+ !(tda8083_readreg(state, 0x02) & 0x80))
+ {
+ msleep(50);
+ };
+}
+
+static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone)
+{
+ tda8083_writereg (state, 0x26, 0xf1);
+
+ switch (tone) {
+ case SEC_TONE_OFF:
+ return tda8083_writereg (state, 0x29, 0x00);
+ case SEC_TONE_ON:
+ return tda8083_writereg (state, 0x29, 0x80);
+ default:
+ return -EINVAL;
+ };
+}
+
+static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage)
+{
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ return tda8083_writereg (state, 0x20, 0x00);
+ case SEC_VOLTAGE_18:
+ return tda8083_writereg (state, 0x20, 0x11);
+ default:
+ return -EINVAL;
+ };
+}
+
+static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst)
+{
+ switch (burst) {
+ case SEC_MINI_A:
+ tda8083_writereg (state, 0x29, (5 << 2)); /* send burst A */
+ break;
+ case SEC_MINI_B:
+ tda8083_writereg (state, 0x29, (7 << 2)); /* send B */
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ tda8083_wait_diseqc_fifo (state, 100);
+
+ return 0;
+}
+
+static int tda8083_send_diseqc_msg (struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+ int i;
+
+ tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */
+
+ for (i=0; i<m->msg_len; i++)
+ tda8083_writereg (state, 0x23 + i, m->msg[i]);
+
+ tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */
+
+ tda8083_wait_diseqc_fifo (state, 100);
+
+ return 0;
+}
+
+static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ u8 signal = ~tda8083_readreg (state, 0x01);
+ u8 sync = tda8083_readreg (state, 0x02);
+
+ *status = 0;
+
+ if (signal > 10)
+ *status |= FE_HAS_SIGNAL;
+
+ if (sync & 0x01)
+ *status |= FE_HAS_CARRIER;
+
+ if (sync & 0x02)
+ *status |= FE_HAS_VITERBI;
+
+ if (sync & 0x10)
+ *status |= FE_HAS_SYNC;
+
+ if ((sync & 0x1f) == 0x1f)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ u8 signal = ~tda8083_readreg (state, 0x01);
+ *strength = (signal << 8) | signal;
+
+ return 0;
+}
+
+static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ u8 _snr = tda8083_readreg (state, 0x08);
+ *snr = (_snr << 8) | _snr;
+
+ return 0;
+}
+
+static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ state->config->pll_set(fe, p);
+ tda8083_set_inversion (state, p->inversion);
+ tda8083_set_fec (state, p->u.qpsk.fec_inner);
+ tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate);
+
+ tda8083_writereg (state, 0x00, 0x3c);
+ tda8083_writereg (state, 0x00, 0x04);
+
+ return 0;
+}
+
+static int tda8083_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ /* FIXME: get symbolrate & frequency offset...*/
+ /*p->frequency = ???;*/
+ p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ?
+ INVERSION_ON : INVERSION_OFF;
+ p->u.qpsk.fec_inner = tda8083_get_fec (state);
+ /*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (state);*/
+
+ return 0;
+}
+
+static int tda8083_sleep(struct dvb_frontend* fe)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ tda8083_writereg (state, 0x00, 0x02);
+ return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+ int i;
+
+ for (i=0; i<44; i++)
+ tda8083_writereg (state, i, tda8083_init_tab[i]);
+
+ if (state->config->pll_init) state->config->pll_init(fe);
+
+ tda8083_writereg (state, 0x00, 0x3c);
+ tda8083_writereg (state, 0x00, 0x04);
+
+ return 0;
+}
+
+static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ tda8083_send_diseqc_burst (state, burst);
+ tda8083_writereg (state, 0x00, 0x3c);
+ tda8083_writereg (state, 0x00, 0x04);
+
+ return 0;
+}
+
+static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ tda8083_set_tone (state, tone);
+ tda8083_writereg (state, 0x00, 0x3c);
+ tda8083_writereg (state, 0x00, 0x04);
+
+ return 0;
+}
+
+static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+
+ tda8083_set_voltage (state, voltage);
+ tda8083_writereg (state, 0x00, 0x3c);
+ tda8083_writereg (state, 0x00, 0x04);
+
+ return 0;
+}
+
+static void tda8083_release(struct dvb_frontend* fe)
+{
+ struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda8083_ops;
+
+struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda8083_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct tda8083_state*) kmalloc(sizeof(struct tda8083_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &tda8083_ops, sizeof(struct dvb_frontend_ops));
+
+ /* check if the demod is there */
+ if ((tda8083_readreg(state, 0x00)) != 0x05) 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 tda8083_ops = {
+
+ .info = {
+ .name = "Philips TDA8083 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000, /* FIXME: guessed! */
+ .frequency_max = 1400000, /* FIXME: guessed! */
+ .frequency_stepsize = 125, /* kHz for QPSK frontends */
+ /* .frequency_tolerance = ???,*/
+ .symbol_rate_min = 1000000, /* FIXME: guessed! */
+ .symbol_rate_max = 45000000, /* FIXME: guessed! */
+ /* .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_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_MUTE_TS
+ },
+
+ .release = tda8083_release,
+
+ .init = tda8083_init,
+ .sleep = tda8083_sleep,
+
+ .set_frontend = tda8083_set_frontend,
+ .get_frontend = tda8083_get_frontend,
+
+ .read_status = tda8083_read_status,
+ .read_signal_strength = tda8083_read_signal_strength,
+ .read_snr = tda8083_read_snr,
+
+ .diseqc_send_master_cmd = tda8083_send_diseqc_msg,
+ .diseqc_send_burst = tda8083_diseqc_send_burst,
+ .set_tone = tda8083_diseqc_set_tone,
+ .set_voltage = tda8083_diseqc_set_voltage,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda8083_attach);
diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h
new file mode 100644
index 000000000000..466663307bf1
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda8083.h
@@ -0,0 +1,45 @@
+/*
+ Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend
+
+ Copyright (C) 2001 Convergence Integrated Media GmbH
+
+ written by Ralph Metzler <ralph@convergence.de>
+
+ adoption to the new DVB frontend API and diagnostic ioctl's
+ by Holger Waechtler <holger@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef TDA8083_H
+#define TDA8083_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda8083_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);
+};
+
+extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // TDA8083_H
diff --git a/drivers/media/dvb/frontends/tda80xx.c b/drivers/media/dvb/frontends/tda80xx.c
new file mode 100644
index 000000000000..c99632114283
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda80xx.c
@@ -0,0 +1,734 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "tda80xx.h"
+
+enum {
+ ID_TDA8044 = 0x04,
+ ID_TDA8083 = 0x05,
+};
+
+
+struct tda80xx_state {
+
+ struct i2c_adapter* i2c;
+
+ struct dvb_frontend_ops ops;
+
+ /* configuration settings */
+ const struct tda80xx_config* config;
+
+ struct dvb_frontend frontend;
+
+ u32 clk;
+ int afc_loop;
+ struct work_struct worklet;
+ fe_code_rate_t code_rate;
+ fe_spectral_inversion_t spectral_inversion;
+ fe_status_t status;
+ u8 id;
+};
+
+static int debug = 1;
+#define dprintk if (debug) printk
+
+static u8 tda8044_inittab_pre[] = {
+ 0x02, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+ 0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x58,
+ 0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+ 0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x00,
+ 0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+static u8 tda8044_inittab_post[] = {
+ 0x04, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea,
+ 0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x50,
+ 0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68,
+ 0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x6c,
+ 0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+static u8 tda8083_inittab[] = {
+ 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea,
+ 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10,
+ 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8,
+ 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00,
+ 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static __inline__ u32 tda80xx_div(u32 a, u32 b)
+{
+ return (a + (b / 2)) / b;
+}
+
+static __inline__ u32 tda80xx_gcd(u32 a, u32 b)
+{
+ u32 r;
+
+ while ((r = a % b)) {
+ a = b;
+ b = r;
+ }
+
+ return b;
+}
+
+static int tda80xx_read(struct tda80xx_state* state, u8 reg, u8 *buf, u8 len)
+{
+ int ret;
+ struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (reg %02x, ret == %i)\n",
+ __FUNCTION__, reg, ret);
+
+ mdelay(10);
+
+ return (ret == 2) ? 0 : -EREMOTEIO;
+}
+
+static int tda80xx_write(struct tda80xx_state* state, u8 reg, const u8 *buf, u8 len)
+{
+ int ret;
+ u8 wbuf[len + 1];
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = wbuf, .len = len + 1 };
+
+ wbuf[0] = reg;
+ memcpy(&wbuf[1], buf, len);
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: i2c xfer error (ret == %i)\n", __FUNCTION__, ret);
+
+ mdelay(10);
+
+ return (ret == 1) ? 0 : -EREMOTEIO;
+}
+
+static __inline__ u8 tda80xx_readreg(struct tda80xx_state* state, u8 reg)
+{
+ u8 val;
+
+ tda80xx_read(state, reg, &val, 1);
+
+ return val;
+}
+
+static __inline__ int tda80xx_writereg(struct tda80xx_state* state, u8 reg, u8 data)
+{
+ return tda80xx_write(state, reg, &data, 1);
+}
+
+static int tda80xx_set_parameters(struct tda80xx_state* state,
+ fe_spectral_inversion_t inversion,
+ u32 symbol_rate,
+ fe_code_rate_t fec_inner)
+{
+ u8 buf[15];
+ u64 ratio;
+ u32 clk;
+ u32 k;
+ u32 sr = symbol_rate;
+ u32 gcd;
+ u8 scd;
+
+ if (symbol_rate > (state->clk * 3) / 16)
+ scd = 0;
+ else if (symbol_rate > (state->clk * 3) / 32)
+ scd = 1;
+ else if (symbol_rate > (state->clk * 3) / 64)
+ scd = 2;
+ else
+ scd = 3;
+
+ clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+ /*
+ * Viterbi decoder:
+ * Differential decoding off
+ * Spectral inversion unknown
+ * QPSK modulation
+ */
+ if (inversion == INVERSION_ON)
+ buf[0] = 0x60;
+ else if (inversion == INVERSION_OFF)
+ buf[0] = 0x20;
+ else
+ buf[0] = 0x00;
+
+ /*
+ * CLK ratio:
+ * system clock frequency is up to 64 or 96 MHz
+ *
+ * formula:
+ * r = k * clk / symbol_rate
+ *
+ * k: 2^21 for caa 0..3,
+ * 2^20 for caa 4..5,
+ * 2^19 for caa 6..7
+ */
+ if (symbol_rate <= (clk * 3) / 32)
+ k = (1 << 19);
+ else if (symbol_rate <= (clk * 3) / 16)
+ k = (1 << 20);
+ else
+ k = (1 << 21);
+
+ gcd = tda80xx_gcd(clk, sr);
+ clk /= gcd;
+ sr /= gcd;
+
+ gcd = tda80xx_gcd(k, sr);
+ k /= gcd;
+ sr /= gcd;
+
+ ratio = (u64)k * (u64)clk;
+ do_div(ratio, sr);
+
+ buf[1] = ratio >> 16;
+ buf[2] = ratio >> 8;
+ buf[3] = ratio;
+
+ /* nyquist filter roll-off factor 35% */
+ buf[4] = 0x20;
+
+ clk = scd ? (state->clk / (scd * 2)) : state->clk;
+
+ /* Anti Alias Filter */
+ if (symbol_rate < (clk * 3) / 64)
+ printk("tda80xx: unsupported symbol rate: %u\n", symbol_rate);
+ else if (symbol_rate <= clk / 16)
+ buf[4] |= 0x07;
+ else if (symbol_rate <= (clk * 3) / 32)
+ buf[4] |= 0x06;
+ else if (symbol_rate <= clk / 8)
+ buf[4] |= 0x05;
+ else if (symbol_rate <= (clk * 3) / 16)
+ buf[4] |= 0x04;
+ else if (symbol_rate <= clk / 4)
+ buf[4] |= 0x03;
+ else if (symbol_rate <= (clk * 3) / 8)
+ buf[4] |= 0x02;
+ else if (symbol_rate <= clk / 2)
+ buf[4] |= 0x01;
+ else
+ buf[4] |= 0x00;
+
+ /* Sigma Delta converter */
+ buf[5] = 0x00;
+
+ /* FEC: Possible puncturing rates */
+ if (fec_inner == FEC_NONE)
+ buf[6] = 0x00;
+ else if ((fec_inner >= FEC_1_2) && (fec_inner <= FEC_8_9))
+ buf[6] = (1 << (8 - fec_inner));
+ else if (fec_inner == FEC_AUTO)
+ buf[6] = 0xff;
+ else
+ return -EINVAL;
+
+ /* carrier lock detector threshold value */
+ buf[7] = 0x30;
+ /* AFC1: proportional part settings */
+ buf[8] = 0x42;
+ /* AFC1: integral part settings */
+ buf[9] = 0x98;
+ /* PD: Leaky integrator SCPC mode */
+ buf[10] = 0x28;
+ /* AFC2, AFC1 controls */
+ buf[11] = 0x30;
+ /* PD: proportional part settings */
+ buf[12] = 0x42;
+ /* PD: integral part settings */
+ buf[13] = 0x99;
+ /* AGC */
+ buf[14] = 0x50 | scd;
+
+ printk("symbol_rate=%u clk=%u\n", symbol_rate, clk);
+
+ return tda80xx_write(state, 0x01, buf, sizeof(buf));
+}
+
+static int tda80xx_set_clk(struct tda80xx_state* state)
+{
+ u8 buf[2];
+
+ /* CLK proportional part */
+ buf[0] = (0x06 << 5) | 0x08; /* CMP[2:0], CSP[4:0] */
+ /* CLK integral part */
+ buf[1] = (0x04 << 5) | 0x1a; /* CMI[2:0], CSI[4:0] */
+
+ return tda80xx_write(state, 0x17, buf, sizeof(buf));
+}
+
+#if 0
+static int tda80xx_set_scpc_freq_offset(struct tda80xx_state* state)
+{
+ /* a constant value is nonsense here imho */
+ return tda80xx_writereg(state, 0x22, 0xf9);
+}
+#endif
+
+static int tda80xx_close_loop(struct tda80xx_state* state)
+{
+ u8 buf[2];
+
+ /* PD: Loop closed, LD: lock detect enable, SCPC: Sweep mode - AFC1 loop closed */
+ buf[0] = 0x68;
+ /* AFC1: Loop closed, CAR Feedback: 8192 */
+ buf[1] = 0x70;
+
+ return tda80xx_write(state, 0x0b, buf, sizeof(buf));
+}
+
+static irqreturn_t tda80xx_irq(int irq, void *priv, struct pt_regs *pt)
+{
+ schedule_work(priv);
+
+ return IRQ_HANDLED;
+}
+
+static void tda80xx_read_status_int(struct tda80xx_state* state)
+{
+ u8 val;
+
+ static const fe_spectral_inversion_t inv_tab[] = {
+ INVERSION_OFF, INVERSION_ON
+ };
+
+ static const fe_code_rate_t fec_tab[] = {
+ FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4,
+ FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8,
+ };
+
+ val = tda80xx_readreg(state, 0x02);
+
+ state->status = 0;
+
+ if (val & 0x01) /* demodulator lock */
+ state->status |= FE_HAS_SIGNAL;
+ if (val & 0x02) /* clock recovery lock */
+ state->status |= FE_HAS_CARRIER;
+ if (val & 0x04) /* viterbi lock */
+ state->status |= FE_HAS_VITERBI;
+ if (val & 0x08) /* deinterleaver lock (packet sync) */
+ state->status |= FE_HAS_SYNC;
+ if (val & 0x10) /* derandomizer lock (frame sync) */
+ state->status |= FE_HAS_LOCK;
+ if (val & 0x20) /* frontend can not lock */
+ state->status |= FE_TIMEDOUT;
+
+ if ((state->status & (FE_HAS_CARRIER)) && (state->afc_loop)) {
+ printk("tda80xx: closing loop\n");
+ tda80xx_close_loop(state);
+ state->afc_loop = 0;
+ }
+
+ if (state->status & (FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK)) {
+ val = tda80xx_readreg(state, 0x0e);
+ state->code_rate = fec_tab[val & 0x07];
+ if (state->status & (FE_HAS_SYNC | FE_HAS_LOCK))
+ state->spectral_inversion = inv_tab[(val >> 7) & 0x01];
+ else
+ state->spectral_inversion = INVERSION_AUTO;
+ }
+ else {
+ state->code_rate = FEC_AUTO;
+ }
+}
+
+static void tda80xx_worklet(void *priv)
+{
+ struct tda80xx_state *state = priv;
+
+ tda80xx_writereg(state, 0x00, 0x04);
+ enable_irq(state->config->irq);
+
+ tda80xx_read_status_int(state);
+}
+
+static void tda80xx_wait_diseqc_fifo(struct tda80xx_state* state)
+{
+ size_t i;
+
+ for (i = 0; i < 100; i++) {
+ if (tda80xx_readreg(state, 0x02) & 0x80)
+ break;
+ msleep(10);
+ }
+}
+
+static int tda8044_init(struct dvb_frontend* fe)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+ int ret;
+
+ /*
+ * this function is a mess...
+ */
+
+ if ((ret = tda80xx_write(state, 0x00, tda8044_inittab_pre, sizeof(tda8044_inittab_pre))))
+ return ret;
+
+ tda80xx_writereg(state, 0x0f, 0x50);
+#if 1
+ tda80xx_writereg(state, 0x20, 0x8F); /* FIXME */
+ tda80xx_writereg(state, 0x20, state->config->volt18setting); /* FIXME */
+ //tda80xx_writereg(state, 0x00, 0x04);
+ tda80xx_writereg(state, 0x00, 0x0C);
+#endif
+ //tda80xx_writereg(state, 0x00, 0x08); /* Reset AFC1 loop filter */
+
+ tda80xx_write(state, 0x00, tda8044_inittab_post, sizeof(tda8044_inittab_post));
+
+ if (state->config->pll_init) {
+ tda80xx_writereg(state, 0x1c, 0x80);
+ state->config->pll_init(fe);
+ tda80xx_writereg(state, 0x1c, 0x00);
+ }
+
+ return 0;
+}
+
+static int tda8083_init(struct dvb_frontend* fe)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ tda80xx_write(state, 0x00, tda8083_inittab, sizeof(tda8083_inittab));
+
+ if (state->config->pll_init) {
+ tda80xx_writereg(state, 0x1c, 0x80);
+ state->config->pll_init(fe);
+ tda80xx_writereg(state, 0x1c, 0x00);
+ }
+
+ return 0;
+}
+
+static int tda80xx_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ return tda80xx_writereg(state, 0x20, state->config->volt13setting);
+ case SEC_VOLTAGE_18:
+ return tda80xx_writereg(state, 0x20, state->config->volt18setting);
+ case SEC_VOLTAGE_OFF:
+ return tda80xx_writereg(state, 0x20, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tda80xx_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ switch (tone) {
+ case SEC_TONE_OFF:
+ return tda80xx_writereg(state, 0x29, 0x00);
+ case SEC_TONE_ON:
+ return tda80xx_writereg(state, 0x29, 0x80);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int tda80xx_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ if (cmd->msg_len > 6)
+ return -EINVAL;
+
+ tda80xx_writereg(state, 0x29, 0x08 | (cmd->msg_len - 3));
+ tda80xx_write(state, 0x23, cmd->msg, cmd->msg_len);
+ tda80xx_writereg(state, 0x29, 0x0c | (cmd->msg_len - 3));
+ tda80xx_wait_diseqc_fifo(state);
+
+ return 0;
+}
+
+static int tda80xx_send_diseqc_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t cmd)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ switch (cmd) {
+ case SEC_MINI_A:
+ tda80xx_writereg(state, 0x29, 0x14);
+ break;
+ case SEC_MINI_B:
+ tda80xx_writereg(state, 0x29, 0x1c);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tda80xx_wait_diseqc_fifo(state);
+
+ return 0;
+}
+
+static int tda80xx_sleep(struct dvb_frontend* fe)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ tda80xx_writereg(state, 0x00, 0x02); /* enter standby */
+
+ return 0;
+}
+
+static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ tda80xx_writereg(state, 0x1c, 0x80);
+ state->config->pll_set(fe, p);
+ tda80xx_writereg(state, 0x1c, 0x00);
+
+ tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner);
+ tda80xx_set_clk(state);
+ //tda80xx_set_scpc_freq_offset(state);
+ state->afc_loop = 1;
+
+ return 0;
+}
+
+static int tda80xx_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ if (!state->config->irq)
+ tda80xx_read_status_int(state);
+
+ p->inversion = state->spectral_inversion;
+ p->u.qpsk.fec_inner = state->code_rate;
+
+ return 0;
+}
+
+static int tda80xx_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ if (!state->config->irq)
+ tda80xx_read_status_int(state);
+ *status = state->status;
+
+ return 0;
+}
+
+static int tda80xx_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+ int ret;
+ u8 buf[3];
+
+ if ((ret = tda80xx_read(state, 0x0b, buf, sizeof(buf))))
+ return ret;
+
+ *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2];
+
+ return 0;
+}
+
+static int tda80xx_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ u8 gain = ~tda80xx_readreg(state, 0x01);
+ *strength = (gain << 8) | gain;
+
+ return 0;
+}
+
+static int tda80xx_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ u8 quality = tda80xx_readreg(state, 0x08);
+ *snr = (quality << 8) | quality;
+
+ return 0;
+}
+
+static int tda80xx_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ *ucblocks = tda80xx_readreg(state, 0x0f);
+ if (*ucblocks == 0xff)
+ *ucblocks = 0xffffffff;
+
+ return 0;
+}
+
+static int tda80xx_init(struct dvb_frontend* fe)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ switch(state->id) {
+ case ID_TDA8044:
+ return tda8044_init(fe);
+
+ case ID_TDA8083:
+ return tda8083_init(fe);
+ }
+ return 0;
+}
+
+static void tda80xx_release(struct dvb_frontend* fe)
+{
+ struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv;
+
+ if (state->config->irq)
+ free_irq(state->config->irq, &state->worklet);
+
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda80xx_ops;
+
+struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda80xx_state* state = NULL;
+ int ret;
+
+ /* allocate memory for the internal state */
+ state = (struct tda80xx_state*) kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &tda80xx_ops, sizeof(struct dvb_frontend_ops));
+ state->spectral_inversion = INVERSION_AUTO;
+ state->code_rate = FEC_AUTO;
+ state->status = 0;
+ state->afc_loop = 0;
+
+ /* check if the demod is there */
+ if (tda80xx_writereg(state, 0x89, 0x00) < 0) goto error;
+ state->id = tda80xx_readreg(state, 0x00);
+
+ switch (state->id) {
+ case ID_TDA8044:
+ state->clk = 96000000;
+ printk("tda80xx: Detected tda8044\n");
+ break;
+
+ case ID_TDA8083:
+ state->clk = 64000000;
+ printk("tda80xx: Detected tda8083\n");
+ break;
+
+ default:
+ goto error;
+ }
+
+ /* setup IRQ */
+ if (state->config->irq) {
+ INIT_WORK(&state->worklet, tda80xx_worklet, state);
+ if ((ret = request_irq(state->config->irq, tda80xx_irq, SA_ONESHOT, "tda80xx", &state->worklet)) < 0) {
+ printk(KERN_ERR "tda80xx: request_irq failed (%d)\n", ret);
+ 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 tda80xx_ops = {
+
+ .info = {
+ .name = "Philips TDA80xx DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 500000,
+ .frequency_max = 2700000,
+ .frequency_stepsize = 125,
+ .symbol_rate_min = 4500000,
+ .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_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = tda80xx_release,
+
+ .init = tda80xx_init,
+ .sleep = tda80xx_sleep,
+
+ .set_frontend = tda80xx_set_frontend,
+ .get_frontend = tda80xx_get_frontend,
+
+ .read_status = tda80xx_read_status,
+ .read_ber = tda80xx_read_ber,
+ .read_signal_strength = tda80xx_read_signal_strength,
+ .read_snr = tda80xx_read_snr,
+ .read_ucblocks = tda80xx_read_ucblocks,
+
+ .diseqc_send_master_cmd = tda80xx_send_diseqc_msg,
+ .diseqc_send_burst = tda80xx_send_diseqc_burst,
+ .set_tone = tda80xx_set_tone,
+ .set_voltage = tda80xx_set_voltage,
+};
+
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("Philips TDA8044 / TDA8083 DVB-S Demodulator driver");
+MODULE_AUTHOR("Felix Domke, Andreas Oberritter");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda80xx_attach);
diff --git a/drivers/media/dvb/frontends/tda80xx.h b/drivers/media/dvb/frontends/tda80xx.h
new file mode 100644
index 000000000000..cd639a0aad55
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda80xx.h
@@ -0,0 +1,51 @@
+/*
+ * tda80xx.c
+ *
+ * Philips TDA8044 / TDA8083 QPSK demodulator driver
+ *
+ * Copyright (C) 2001 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2002-2004 Andreas Oberritter <obi@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef TDA80XX_H
+#define TDA80XX_H
+
+#include <linux/dvb/frontend.h>
+
+struct tda80xx_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* IRQ to use (0=>no IRQ used) */
+ u32 irq;
+
+ /* Register setting to use for 13v */
+ u8 volt13setting;
+
+ /* Register setting to use for 18v */
+ u8 volt18setting;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // TDA80XX_H
diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c
new file mode 100644
index 000000000000..9c0d23e1d9e5
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1820.c
@@ -0,0 +1,450 @@
+/*
+ VES1820 - Single Chip Cable Channel Receiver driver module
+
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "ves1820.h"
+
+
+
+struct ves1820_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ /* configuration settings */
+ const struct ves1820_config* config;
+ struct dvb_frontend frontend;
+
+ /* private demodulator data */
+ u8 reg0;
+ u8 pwm;
+};
+
+
+static int verbose;
+
+static u8 ves1820_inittab[] = {
+ 0x69, 0x6A, 0x93, 0x12, 0x12, 0x46, 0x26, 0x1A,
+ 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
+ 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40
+};
+
+static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data)
+{
+ u8 buf[] = { 0x00, reg, data };
+ struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 };
+ int ret;
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ printk("ves1820: %s(): writereg error (reg == 0x%02x,"
+ "val == 0x%02x, ret == %i)\n", __FUNCTION__, reg, data, ret);
+
+ msleep(10);
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static u8 ves1820_readreg(struct ves1820_state *state, u8 reg)
+{
+ u8 b0[] = { 0x00, reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2},
+ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1}
+ };
+ int ret;
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ printk("ves1820: %s(): readreg error (reg == 0x%02x,"
+ "ret == %i)\n", __FUNCTION__, reg, ret);
+
+ return b1[0];
+}
+
+static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion)
+{
+ reg0 |= state->reg0 & 0x62;
+
+ if (INVERSION_ON == inversion) {
+ if (!state->config->invert) reg0 |= 0x20;
+ else reg0 &= ~0x20;
+ } else if (INVERSION_OFF == inversion) {
+ if (!state->config->invert) reg0 &= ~0x20;
+ else reg0 |= 0x20;
+ }
+
+ ves1820_writereg(state, 0x00, reg0 & 0xfe);
+ ves1820_writereg(state, 0x00, reg0 | 0x01);
+
+ state->reg0 = reg0;
+
+ return 0;
+}
+
+static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
+{
+ s32 BDR;
+ s32 BDRI;
+ s16 SFIL = 0;
+ u16 NDEC = 0;
+ u32 ratio;
+ u32 fin;
+ u32 tmp;
+ u64 fptmp;
+ u64 fpxin;
+
+ if (symbolrate > state->config->xin / 2)
+ symbolrate = state->config->xin / 2;
+
+ if (symbolrate < 500000)
+ symbolrate = 500000;
+
+ if (symbolrate < state->config->xin / 16)
+ NDEC = 1;
+ if (symbolrate < state->config->xin / 32)
+ NDEC = 2;
+ if (symbolrate < state->config->xin / 64)
+ NDEC = 3;
+
+ /* yeuch! */
+ fpxin = state->config->xin * 10;
+ fptmp = fpxin; do_div(fptmp, 123);
+ if (symbolrate < fptmp);
+ SFIL = 1;
+ fptmp = fpxin; do_div(fptmp, 160);
+ if (symbolrate < fptmp);
+ SFIL = 0;
+ fptmp = fpxin; do_div(fptmp, 246);
+ if (symbolrate < fptmp);
+ SFIL = 1;
+ fptmp = fpxin; do_div(fptmp, 320);
+ if (symbolrate < fptmp);
+ SFIL = 0;
+ fptmp = fpxin; do_div(fptmp, 492);
+ if (symbolrate < fptmp);
+ SFIL = 1;
+ fptmp = fpxin; do_div(fptmp, 640);
+ if (symbolrate < fptmp);
+ SFIL = 0;
+ fptmp = fpxin; do_div(fptmp, 984);
+ if (symbolrate < fptmp);
+ SFIL = 1;
+
+ fin = state->config->xin >> 4;
+ symbolrate <<= NDEC;
+ ratio = (symbolrate << 4) / fin;
+ tmp = ((symbolrate << 4) % fin) << 8;
+ ratio = (ratio << 8) + tmp / fin;
+ tmp = (tmp % fin) << 8;
+ ratio = (ratio << 8) + (tmp + fin / 2) / fin;
+
+ BDR = ratio;
+ BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
+
+ if (BDRI > 0xFF)
+ BDRI = 0xFF;
+
+ SFIL = (SFIL << 4) | ves1820_inittab[0x0E];
+
+ NDEC = (NDEC << 6) | ves1820_inittab[0x03];
+
+ ves1820_writereg(state, 0x03, NDEC);
+ ves1820_writereg(state, 0x0a, BDR & 0xff);
+ ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff);
+ ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f);
+
+ ves1820_writereg(state, 0x0d, BDRI);
+ ves1820_writereg(state, 0x0e, SFIL);
+
+ return 0;
+}
+
+static int ves1820_init(struct dvb_frontend* fe)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+ int i;
+ int val;
+
+ ves1820_writereg(state, 0, 0);
+
+ for (i = 0; i < 53; i++) {
+ val = ves1820_inittab[i];
+ if ((i == 2) && (state->config->selagc)) val |= 0x08;
+ ves1820_writereg(state, i, val);
+ }
+
+ ves1820_writereg(state, 0x34, state->pwm);
+
+ if (state->config->pll_init) state->config->pll_init(fe);
+
+ return 0;
+}
+
+static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+ static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
+ static const u8 reg0x01[] = { 140, 140, 106, 100, 92 };
+ static const u8 reg0x05[] = { 135, 100, 70, 54, 38 };
+ static const u8 reg0x08[] = { 162, 116, 67, 52, 35 };
+ static const u8 reg0x09[] = { 145, 150, 106, 126, 107 };
+ int real_qam = p->u.qam.modulation - QAM_16;
+
+ if (real_qam < 0 || real_qam > 4)
+ return -EINVAL;
+
+ state->config->pll_set(fe, p);
+ ves1820_set_symbolrate(state, p->u.qam.symbol_rate);
+ ves1820_writereg(state, 0x34, state->pwm);
+
+ ves1820_writereg(state, 0x01, reg0x01[real_qam]);
+ ves1820_writereg(state, 0x05, reg0x05[real_qam]);
+ ves1820_writereg(state, 0x08, reg0x08[real_qam]);
+ ves1820_writereg(state, 0x09, reg0x09[real_qam]);
+
+ ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion);
+
+ return 0;
+}
+
+static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+ int sync;
+
+ *status = 0;
+ sync = ves1820_readreg(state, 0x11);
+
+ if (sync & 1)
+ *status |= FE_HAS_SIGNAL;
+
+ if (sync & 2)
+ *status |= FE_HAS_CARRIER;
+
+ if (sync & 2) /* XXX FIXME! */
+ *status |= FE_HAS_VITERBI;
+
+ if (sync & 4)
+ *status |= FE_HAS_SYNC;
+
+ if (sync & 8)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+ u32 _ber = ves1820_readreg(state, 0x14) |
+ (ves1820_readreg(state, 0x15) << 8) |
+ ((ves1820_readreg(state, 0x16) & 0x0f) << 16);
+ *ber = 10 * _ber;
+
+ return 0;
+}
+
+static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+ u8 gain = ves1820_readreg(state, 0x17);
+ *strength = (gain << 8) | gain;
+
+ return 0;
+}
+
+static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+ u8 quality = ~ves1820_readreg(state, 0x18);
+ *snr = (quality << 8) | quality;
+
+ return 0;
+}
+
+static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+ *ucblocks = ves1820_readreg(state, 0x13) & 0x7f;
+ if (*ucblocks == 0x7f)
+ *ucblocks = 0xffffffff;
+
+ /* reset uncorrected block counter */
+ ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf);
+ ves1820_writereg(state, 0x10, ves1820_inittab[0x10]);
+
+ return 0;
+}
+
+static int ves1820_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+ int sync;
+ s8 afc = 0;
+
+ sync = ves1820_readreg(state, 0x11);
+ afc = ves1820_readreg(state, 0x19);
+ if (verbose) {
+ /* AFC only valid when carrier has been recovered */
+ printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" :
+ "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10);
+ }
+
+ if (!state->config->invert) {
+ p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF;
+ } else {
+ p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF;
+ }
+
+ p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
+
+ p->u.qam.fec_inner = FEC_NONE;
+
+ p->frequency = ((p->frequency + 31250) / 62500) * 62500;
+ if (sync & 2)
+ p->frequency -= ((s32) p->u.qam.symbol_rate * afc) >> 10;
+
+ return 0;
+}
+
+static int ves1820_sleep(struct dvb_frontend* fe)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+
+ ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */
+ ves1820_writereg(state, 0x00, 0x80); /* standby */
+
+ return 0;
+}
+
+static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+
+ fesettings->min_delay_ms = 200;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void ves1820_release(struct dvb_frontend* fe)
+{
+ struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops ves1820_ops;
+
+struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+ struct i2c_adapter* i2c,
+ u8 pwm)
+{
+ struct ves1820_state* state = NULL;
+
+ /* allocate memory for the internal state */
+ state = (struct ves1820_state*) kmalloc(sizeof(struct ves1820_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ memcpy(&state->ops, &ves1820_ops, sizeof(struct dvb_frontend_ops));
+ state->reg0 = ves1820_inittab[0];
+ state->config = config;
+ state->i2c = i2c;
+ state->pwm = pwm;
+
+ /* check if the demod is there */
+ if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70)
+ goto error;
+
+ if (verbose)
+ printk("ves1820: pwm=0x%02x\n", state->pwm);
+
+ state->ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */
+ state->ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */
+
+ /* 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 ves1820_ops = {
+
+ .info = {
+ .name = "VLSI VES1820 DVB-C",
+ .type = FE_QAM,
+ .frequency_stepsize = 62500,
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .caps = FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = ves1820_release,
+
+ .init = ves1820_init,
+ .sleep = ves1820_sleep,
+
+ .set_frontend = ves1820_set_parameters,
+ .get_frontend = ves1820_get_frontend,
+ .get_tune_settings = ves1820_get_tune_settings,
+
+ .read_status = ves1820_read_status,
+ .read_ber = ves1820_read_ber,
+ .read_signal_strength = ves1820_read_signal_strength,
+ .read_snr = ves1820_read_snr,
+ .read_ucblocks = ves1820_read_ucblocks,
+};
+
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
+
+MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ves1820_attach);
diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h
new file mode 100644
index 000000000000..355f130b1be8
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1820.h
@@ -0,0 +1,51 @@
+/*
+ VES1820 - Single Chip Cable Channel Receiver driver module
+
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef VES1820_H
+#define VES1820_H
+
+#include <linux/dvb/frontend.h>
+
+#define VES1820_SELAGC_PWM 0
+#define VES1820_SELAGC_SIGNAMPERR 1
+
+struct ves1820_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* value of XIN to use */
+ u32 xin;
+
+ /* does inversion need inverted? */
+ u8 invert:1;
+
+ /* SELAGC control */
+ u8 selagc:1;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+ struct i2c_adapter* i2c, u8 pwm);
+
+#endif // VES1820_H
diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c
new file mode 100644
index 000000000000..edcad283aa86
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1x93.c
@@ -0,0 +1,545 @@
+/*
+ Driver for VES1893 and VES1993 QPSK Demodulators
+
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+ Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de>
+ Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de>
+ Copyright (C) 2002-2003 Andreas Oberritter <obi@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <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 "ves1x93.h"
+
+
+struct ves1x93_state {
+ struct i2c_adapter* i2c;
+ struct dvb_frontend_ops ops;
+ /* configuration settings */
+ const struct ves1x93_config* config;
+ struct dvb_frontend frontend;
+
+ /* previous uncorrected block counter */
+ fe_spectral_inversion_t inversion;
+ u8 *init_1x93_tab;
+ u8 *init_1x93_wtab;
+ u8 tab_size;
+ u8 demod_type;
+};
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+#define DEMOD_VES1893 0
+#define DEMOD_VES1993 1
+
+static u8 init_1893_tab [] = {
+ 0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4,
+ 0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7f, 0x00,
+ 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x21, 0xb0, 0x14, 0x00, 0xdc, 0x00,
+ 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x55, 0x00, 0x00, 0x7f, 0x00
+};
+
+static u8 init_1993_tab [] = {
+ 0x00, 0x9c, 0x35, 0x80, 0x6a, 0x09, 0x72, 0x8c,
+ 0x09, 0x6b, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00,
+ 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x40, 0x21, 0xb0, 0x00, 0x00, 0x00, 0x10,
+ 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x0e, 0x80, 0x00
+};
+
+static u8 init_1893_wtab[] =
+{
+ 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0,
+ 0,1,0,0,0,0,0,0, 1,0,1,1,0,0,0,1,
+ 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0,
+ 1,1,1,0,1,1
+};
+
+static u8 init_1993_wtab[] =
+{
+ 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0,
+ 0,1,0,0,0,0,0,0, 1,1,1,1,0,0,0,1,
+ 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0,
+ 1,1,1,0,1,1,1,1, 1,1,1,1,1
+};
+
+static int ves1x93_writereg (struct ves1x93_state* state, u8 reg, u8 data)
+{
+ u8 buf [] = { 0x00, reg, data };
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 };
+ 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 ves1x93_readreg (struct ves1x93_state* state, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { 0x00, reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ ret = i2c_transfer (state->i2c, msg, 2);
+
+ if (ret != 2) return ret;
+
+ return b1[0];
+}
+
+static int ves1x93_clr_bit (struct ves1x93_state* state)
+{
+ msleep(10);
+ ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe);
+ ves1x93_writereg (state, 0, state->init_1x93_tab[0]);
+ msleep(50);
+ return 0;
+}
+
+static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion)
+{
+ u8 val;
+
+ /*
+ * inversion on/off are interchanged because i and q seem to
+ * be swapped on the hardware
+ */
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ val = 0xc0;
+ break;
+ case INVERSION_ON:
+ val = 0x80;
+ break;
+ case INVERSION_AUTO:
+ val = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val);
+}
+
+static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec)
+{
+ if (fec == FEC_AUTO)
+ return ves1x93_writereg (state, 0x0d, 0x08);
+ else if (fec < FEC_1_2 || fec > FEC_8_9)
+ return -EINVAL;
+ else
+ return ves1x93_writereg (state, 0x0d, fec - FEC_1_2);
+}
+
+static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state)
+{
+ return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7);
+}
+
+static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate)
+{
+ u32 BDR;
+ u32 ratio;
+ u8 ADCONF, FCONF, FNR, AGCR;
+ u32 BDRI;
+ u32 tmp;
+ u32 FIN;
+
+ dprintk("%s: srate == %d\n", __FUNCTION__, (unsigned int) srate);
+
+ if (srate > state->config->xin/2)
+ srate = state->config->xin/2;
+
+ if (srate < 500000)
+ srate = 500000;
+
+#define MUL (1UL<<26)
+
+ FIN = (state->config->xin + 6000) >> 4;
+
+ tmp = srate << 6;
+ ratio = tmp / FIN;
+
+ tmp = (tmp % FIN) << 8;
+ ratio = (ratio << 8) + tmp / FIN;
+
+ tmp = (tmp % FIN) << 8;
+ ratio = (ratio << 8) + tmp / FIN;
+
+ FNR = 0xff;
+
+ if (ratio < MUL/3) FNR = 0;
+ if (ratio < (MUL*11)/50) FNR = 1;
+ if (ratio < MUL/6) FNR = 2;
+ if (ratio < MUL/9) FNR = 3;
+ if (ratio < MUL/12) FNR = 4;
+ if (ratio < (MUL*11)/200) FNR = 5;
+ if (ratio < MUL/24) FNR = 6;
+ if (ratio < (MUL*27)/1000) FNR = 7;
+ if (ratio < MUL/48) FNR = 8;
+ if (ratio < (MUL*137)/10000) FNR = 9;
+
+ if (FNR == 0xff) {
+ ADCONF = 0x89;
+ FCONF = 0x80;
+ FNR = 0;
+ } else {
+ ADCONF = 0x81;
+ FCONF = 0x88 | (FNR >> 1) | ((FNR & 0x01) << 5);
+ /*FCONF = 0x80 | ((FNR & 0x01) << 5) | (((FNR > 1) & 0x03) << 3) | ((FNR >> 1) & 0x07);*/
+ }
+
+ BDR = (( (ratio << (FNR >> 1)) >> 4) + 1) >> 1;
+ BDRI = ( ((FIN << 8) / ((srate << (FNR >> 1)) >> 2)) + 1) >> 1;
+
+ dprintk("FNR= %d\n", FNR);
+ dprintk("ratio= %08x\n", (unsigned int) ratio);
+ dprintk("BDR= %08x\n", (unsigned int) BDR);
+ dprintk("BDRI= %02x\n", (unsigned int) BDRI);
+
+ if (BDRI > 0xff)
+ BDRI = 0xff;
+
+ ves1x93_writereg (state, 0x06, 0xff & BDR);
+ ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8));
+ ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16));
+
+ ves1x93_writereg (state, 0x09, BDRI);
+ ves1x93_writereg (state, 0x20, ADCONF);
+ ves1x93_writereg (state, 0x21, FCONF);
+
+ AGCR = state->init_1x93_tab[0x05];
+ if (state->config->invert_pwm)
+ AGCR |= 0x20;
+
+ if (srate < 6000000)
+ AGCR |= 0x80;
+ else
+ AGCR &= ~0x80;
+
+ ves1x93_writereg (state, 0x05, AGCR);
+
+ /* ves1993 hates this, will lose lock */
+ if (state->demod_type != DEMOD_VES1993)
+ ves1x93_clr_bit (state);
+
+ return 0;
+}
+
+static int ves1x93_init (struct dvb_frontend* fe)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+ int i;
+ int val;
+
+ dprintk("%s: init chip\n", __FUNCTION__);
+
+ for (i = 0; i < state->tab_size; i++) {
+ if (state->init_1x93_wtab[i]) {
+ val = state->init_1x93_tab[i];
+
+ if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */
+ ves1x93_writereg (state, i, val);
+ }
+ }
+
+ if (state->config->pll_init) {
+ ves1x93_writereg(state, 0x00, 0x11);
+ state->config->pll_init(fe);
+ ves1x93_writereg(state, 0x00, 0x01);
+ }
+
+ return 0;
+}
+
+static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ return ves1x93_writereg (state, 0x1f, 0x20);
+ case SEC_VOLTAGE_18:
+ return ves1x93_writereg (state, 0x1f, 0x30);
+ case SEC_VOLTAGE_OFF:
+ return ves1x93_writereg (state, 0x1f, 0x00);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ u8 sync = ves1x93_readreg (state, 0x0e);
+
+ /*
+ * The ves1893 sometimes returns sync values that make no sense,
+ * because, e.g., the SIGNAL bit is 0, while some of the higher
+ * bits are 1 (and how can there be a CARRIER w/o a SIGNAL?).
+ * Tests showed that the the VITERBI and SYNC bits are returned
+ * reliably, while the SIGNAL and CARRIER bits ar sometimes wrong.
+ * If such a case occurs, we read the value again, until we get a
+ * valid value.
+ */
+ int maxtry = 10; /* just for safety - let's not get stuck here */
+ while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) {
+ msleep(10);
+ sync = ves1x93_readreg (state, 0x0e);
+ }
+
+ *status = 0;
+
+ if (sync & 1)
+ *status |= FE_HAS_SIGNAL;
+
+ if (sync & 2)
+ *status |= FE_HAS_CARRIER;
+
+ if (sync & 4)
+ *status |= FE_HAS_VITERBI;
+
+ if (sync & 8)
+ *status |= FE_HAS_SYNC;
+
+ if ((sync & 0x1f) == 0x1f)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ *ber = ves1x93_readreg (state, 0x15);
+ *ber |= (ves1x93_readreg (state, 0x16) << 8);
+ *ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16);
+ *ber *= 10;
+
+ return 0;
+}
+
+static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ u8 signal = ~ves1x93_readreg (state, 0x0b);
+ *strength = (signal << 8) | signal;
+
+ return 0;
+}
+
+static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ u8 _snr = ~ves1x93_readreg (state, 0x1c);
+ *snr = (_snr << 8) | _snr;
+
+ return 0;
+}
+
+static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ *ucblocks = ves1x93_readreg (state, 0x18) & 0x7f;
+
+ if (*ucblocks == 0x7f)
+ *ucblocks = 0xffffffff; /* counter overflow... */
+
+ ves1x93_writereg (state, 0x18, 0x00); /* reset the counter */
+ ves1x93_writereg (state, 0x18, 0x80); /* dto. */
+
+ return 0;
+}
+
+static int ves1x93_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ ves1x93_writereg(state, 0x00, 0x11);
+ state->config->pll_set(fe, p);
+ ves1x93_writereg(state, 0x00, 0x01);
+ ves1x93_set_inversion (state, p->inversion);
+ ves1x93_set_fec (state, p->u.qpsk.fec_inner);
+ ves1x93_set_symbolrate (state, p->u.qpsk.symbol_rate);
+ state->inversion = p->inversion;
+
+ return 0;
+}
+
+static int ves1x93_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+ int afc;
+
+ afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2;
+ afc = (afc * (int)(p->u.qpsk.symbol_rate/1000/8))/16;
+
+ p->frequency -= afc;
+
+ /*
+ * inversion indicator is only valid
+ * if auto inversion was used
+ */
+ if (state->inversion == INVERSION_AUTO)
+ p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ?
+ INVERSION_OFF : INVERSION_ON;
+ p->u.qpsk.fec_inner = ves1x93_get_fec (state);
+ /* XXX FIXME: timing offset !! */
+
+ return 0;
+}
+
+static int ves1x93_sleep(struct dvb_frontend* fe)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+
+ return ves1x93_writereg (state, 0x00, 0x08);
+}
+
+static void ves1x93_release(struct dvb_frontend* fe)
+{
+ struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops ves1x93_ops;
+
+struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct ves1x93_state* state = NULL;
+ u8 identity;
+
+ /* allocate memory for the internal state */
+ state = (struct ves1x93_state*) kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
+ if (state == NULL) goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ memcpy(&state->ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops));
+ state->inversion = INVERSION_OFF;
+
+ /* check if the demod is there + identify it */
+ identity = ves1x93_readreg(state, 0x1e);
+ switch (identity) {
+ case 0xdc: /* VES1893A rev1 */
+ printk("ves1x93: Detected ves1893a rev1\n");
+ state->demod_type = DEMOD_VES1893;
+ state->init_1x93_tab = init_1893_tab;
+ state->init_1x93_wtab = init_1893_wtab;
+ state->tab_size = sizeof(init_1893_tab);
+ break;
+
+ case 0xdd: /* VES1893A rev2 */
+ printk("ves1x93: Detected ves1893a rev2\n");
+ state->demod_type = DEMOD_VES1893;
+ state->init_1x93_tab = init_1893_tab;
+ state->init_1x93_wtab = init_1893_wtab;
+ state->tab_size = sizeof(init_1893_tab);
+ break;
+
+ case 0xde: /* VES1993 */
+ printk("ves1x93: Detected ves1993\n");
+ state->demod_type = DEMOD_VES1993;
+ state->init_1x93_tab = init_1993_tab;
+ state->init_1x93_wtab = init_1993_wtab;
+ state->tab_size = sizeof(init_1993_tab);
+ break;
+
+ default:
+ 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 ves1x93_ops = {
+
+ .info = {
+ .name = "VLSI VES1x93 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_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK
+ },
+
+ .release = ves1x93_release,
+
+ .init = ves1x93_init,
+ .sleep = ves1x93_sleep,
+
+ .set_frontend = ves1x93_set_frontend,
+ .get_frontend = ves1x93_get_frontend,
+
+ .read_status = ves1x93_read_status,
+ .read_ber = ves1x93_read_ber,
+ .read_signal_strength = ves1x93_read_signal_strength,
+ .read_snr = ves1x93_read_snr,
+ .read_ucblocks = ves1x93_read_ucblocks,
+
+ .set_voltage = ves1x93_set_voltage,
+};
+
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(ves1x93_attach);
diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h
new file mode 100644
index 000000000000..1627e37c57a4
--- /dev/null
+++ b/drivers/media/dvb/frontends/ves1x93.h
@@ -0,0 +1,50 @@
+/*
+ Driver for VES1893 and VES1993 QPSK Demodulators
+
+ Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
+ Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de>
+ Copyright (C) 2002 Dennis Noermann <dennis.noermann@noernet.de>
+ Copyright (C) 2002-2003 Andreas Oberritter <obi@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef VES1X93_H
+#define VES1X93_H
+
+#include <linux/dvb/frontend.h>
+
+struct ves1x93_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* value of XIN to use */
+ u32 xin;
+
+ /* should PWM be inverted? */
+ u8 invert_pwm:1;
+
+ /* PLL maintenance */
+ int (*pll_init)(struct dvb_frontend* fe);
+ int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params);
+};
+
+extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // VES1X93_H