summaryrefslogtreecommitdiff
path: root/linux/drivers/media/dvb/frontends
diff options
context:
space:
mode:
authorHolger Waechtler <devnull@localhost>2002-10-16 16:52:27 +0000
committerHolger Waechtler <devnull@localhost>2002-10-16 16:52:27 +0000
commit4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7 (patch)
tree23af98b0e1da37f97558e63953011e23d1e360a5 /linux/drivers/media/dvb/frontends
downloadmediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.gz
mediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.bz2
the 2.5 tree
Diffstat (limited to 'linux/drivers/media/dvb/frontends')
-rw-r--r--linux/drivers/media/dvb/frontends/Config.help32
-rw-r--r--linux/drivers/media/dvb/frontends/Config.in9
-rw-r--r--linux/drivers/media/dvb/frontends/Makefile14
-rw-r--r--linux/drivers/media/dvb/frontends/alps_bsru6.c700
-rw-r--r--linux/drivers/media/dvb/frontends/alps_bsrv2.c470
-rw-r--r--linux/drivers/media/dvb/frontends/grundig_29504-401.c484
-rw-r--r--linux/drivers/media/dvb/frontends/grundig_29504-491.c496
-rw-r--r--linux/drivers/media/dvb/frontends/ves1820.c537
8 files changed, 2742 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/frontends/Config.help b/linux/drivers/media/dvb/frontends/Config.help
new file mode 100644
index 000000000..443a0c7cb
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/Config.help
@@ -0,0 +1,32 @@
+CONFIG_DVB_ALPS_BSRU6
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+ If you don't know what tuner module is soldered on your DVB adapter simply
+ enable all supported frontends, the right one will get autodetected.
+
+CONFIG_DVB_ALPS_BSRV2
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+ If you don't know what tuner module is soldered on your DVB adapter simply
+ enable all supported frontends, the right one will get autodetected.
+
+CONFIG_DVB_GRUNDIG_29504_491
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+ If you don't know what tuner module is soldered on your DVB adapter simply
+ enable all supported frontends, the right one will get autodetected.
+
+CONFIG_DVB_GRUNDIG_29504_401
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ If you don't know what tuner module is soldered on your DVB adapter simply
+ enable all supported frontends, the right one will get autodetected.
+
+CONFIG_DVB_VES1820
+ The VES1820 Demodulator is used on many DVB-C PCI cards and in some
+ DVB-C SetTopBoxes. Say Y when you see this demodulator chip near your
+ tuner module.
+
+ If you don't know what tuner module is soldered on your DVB adapter simply
+ enable all supported frontends, the right one will get autodetected.
+
diff --git a/linux/drivers/media/dvb/frontends/Config.in b/linux/drivers/media/dvb/frontends/Config.in
new file mode 100644
index 000000000..811708ba7
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/Config.in
@@ -0,0 +1,9 @@
+
+comment 'Supported Frontend Modules'
+
+dep_tristate ' Alps BSRU6 (QPSK)' CONFIG_DVB_ALPS_BSRU6
+dep_tristate ' Alps BSRV2 (QPSK)' CONFIG_DVB_ALPS_BSRV2
+dep_tristate ' Grundig 29504-491 (QPSK)' CONFIG_DVB_GRUNDIG_29504_491
+dep_tristate ' Grundig 29504-401 (OFDM)' CONFIG_DVB_GRUNDIG_29504_401
+dep_tristate ' Frontends with external VES1820 demodulator (QAM)' CONFIG_DVB_VES1820
+
diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile
new file mode 100644
index 000000000..3ee94a40a
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the kernel DVB frontend device drivers.
+#
+
+EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/
+
+obj-$(CONFIG_DVB_ALPS_BSRU6) += alps_bsru6.o
+obj-$(CONFIG_DVB_ALPS_BSRV2) += alps_bsrv2.o
+obj-$(CONFIG_DVB_GRUNDIG_29504_491) += grundig_29504-491.o
+obj-$(CONFIG_DVB_GRUNDIG_29504_401) += grundig_29504-401.o
+obj-$(CONFIG_DVB_VES1820) += ves1820.o
+
+include $(TOPDIR)/Rules.make
+
diff --git a/linux/drivers/media/dvb/frontends/alps_bsru6.c b/linux/drivers/media/dvb/frontends/alps_bsru6.c
new file mode 100644
index 000000000..1d2c9983c
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/alps_bsru6.c
@@ -0,0 +1,700 @@
+/*
+ Alps BSRU6 DVB QPSK frontend driver
+
+ Copyright (C) 2001-2002 Convergence Integrated Media GmbH
+ <ralph@convergence.de>, <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/module.h>
+
+#include "compat.h"
+#include "dvb_frontend.h"
+
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+
+#define M_CLK (88000000UL)
+
+/* M=21, K=0, P=0, f_VCO = 4MHz*4*(M+1)/(K+1) = 352 MHz */
+
+static
+struct dvb_frontend_info bsru6_info = {
+ name: "Alps BSRU6",
+ type: FE_QPSK,
+ frequency_min: 950000,
+ frequency_max: 2150000,
+ frequency_stepsize: 125, /* kHz for QPSK frontends */
+ frequency_tolerance: M_CLK/2000,
+ symbol_rate_min: 1000000,
+ symbol_rate_max: 45000000,
+ symbol_rate_tolerance: 500, /* ppm */
+ notifier_delay: 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
+};
+
+
+static
+u8 init_tab [] = {
+ 0x01, 0x15, // M: 0x15 DIRCLK: 0 K:0
+ 0x02, 0x30, // P: 0 SERCLK: 0 VCO:ON STDBY:0
+
+ 0x03, 0x00,
+ 0x04, 0x7d, // F22FR, F22=22kHz
+ 0x05, 0x35, // SDAT:0 SCLT:0 I2CT:1
+ 0x06, 0x00, // DAC mode and MSB
+ 0x07, 0x00, // DAC LSB
+ 0x08, 0x43, // DiSEqC
+ 0x09, 0x00,
+ 0x0a, 0x42,
+ 0x0c, 0x51, // QPSK reverse:1 Nyquist:0 OP0 val:1 OP0 con:1 OP1 val:1 OP1 con:1
+ 0x0d, 0x82,
+ 0x0e, 0x23,
+ 0x0f, 0x52,
+
+ 0x10, 0x3d, // AGC2
+ 0x11, 0x84,
+ 0x12, 0xb5, // Lock detect: -64 Carrier freq detect:on
+ 0x13, 0xb6, // alpha_car b:4 a:0 noise est:256ks derot:on
+ 0x14, 0x93, // beat carc:0 d:0 e:0xf phase detect algo: 1
+ 0x15, 0xc9, // lock detector threshold
+
+ 0x16, 0x1d,
+ 0x17, 0x0,
+ 0x18, 0x14,
+ 0x19, 0xf2,
+
+ 0x1a, 0x11,
+
+ 0x1b, 0x9c,
+ 0x1c, 0x0,
+ 0x1d, 0x0,
+ 0x1e, 0xb,
+
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x24, 0xff,
+ 0x25, 0xff,
+ 0x26, 0xff,
+
+ 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0
+ 0x29, 0x1e, // 1/2 threshold
+ 0x2a, 0x14, // 2/3 threshold
+ 0x2b, 0x0f, // 3/4 threshold
+ 0x2c, 0x09, // 5/6 threshold
+ 0x2d, 0x05, // 7/8 threshold
+ 0x2e, 0x01,
+
+ 0x31, 0x1f, // test all FECs
+
+ 0x32, 0x19, // viterbi and synchro search
+ 0x33, 0xfc, // rs control
+ 0x34, 0x93, // error control
+
+
+ 0x0b, 0x00,
+ 0x27, 0x00, 0x2f, 0x00, 0x30, 0x00,
+ 0x35, 0x00, 0x36, 0x00, 0x37, 0x00,
+ 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00,
+ 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00,
+ 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00,
+ 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, 0x47, 0x00,
+ 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00,
+ 0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00
+};
+
+
+static
+int stv0299_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { addr: 0x68, flags: 0, buf: buf, len: 2 };
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ret = i2c->xfer (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
+u8 stv0299_readreg (struct dvb_i2c_bus *i2c, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: b0, len: 1 },
+ { addr: 0x68, flags: I2C_M_RD, buf: b1, len: 1 } };
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ret = i2c->xfer (i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return b1[0];
+}
+
+
+static
+int stv0299_readregs (struct dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len)
+{
+ int ret;
+ struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: &reg1, len: 1 },
+ { addr: 0x68, flags: I2C_M_RD, buf: b, len: len } };
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ret = i2c->xfer (i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return ret == 2 ? 0 : -1;
+}
+
+
+
+static
+int tsa5059_write (struct dvb_i2c_bus *i2c, u8 data [4])
+{
+ int ret;
+ u8 rpt1 [] = { 0x05, 0xb5 }; /* enable i2c repeater on stv0299 */
+ struct i2c_msg msg [] = {{ addr: 0x68, flags: 0, buf: rpt1, len: 2 },
+ { addr: 0x61, flags: 0, buf: data, len: 4 }};
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ret = i2c->xfer (i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
+
+ return (ret != 2) ? -1 : 0;
+}
+
+
+/**
+ * set up the downconverter frequency divisor for a
+ * reference clock comparision frequency of 125 kHz.
+ */
+static
+int tsa5059_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
+{
+ u32 div = freq / 125;
+ u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x84, (pwr << 5) | 0x20 };
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ return tsa5059_write (i2c, buf);
+}
+
+
+static
+int stv0299_init (struct dvb_i2c_bus *i2c)
+{
+ int i;
+
+ dprintk("stv0299: init chip\n");
+
+ for (i=0; i<sizeof(init_tab); i+=2)
+ stv0299_writereg (i2c, init_tab[i], init_tab[i+1]);
+
+ return 0;
+}
+
+
+static
+int stv0299_set_inversion (struct dvb_i2c_bus *i2c, int inversion)
+{
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch (inversion) {
+ case INVERSION_AUTO:
+ return -EOPNOTSUPP;
+ case INVERSION_OFF:
+ val = stv0299_readreg (i2c, 0x0c);
+ return stv0299_writereg (i2c, 0x0c, val | 0x01);
+ case INVERSION_ON:
+ val = stv0299_readreg (i2c, 0x0c);
+ return stv0299_writereg (i2c, 0x0c, val & 0xfe);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static
+int stv0299_set_FEC (struct dvb_i2c_bus *i2c, fe_code_rate_t fec)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch (fec) {
+ case FEC_AUTO:
+ return stv0299_writereg (i2c, 0x31, 0x1f);
+ case FEC_1_2:
+ return stv0299_writereg (i2c, 0x31, 0x01);
+ case FEC_2_3:
+ return stv0299_writereg (i2c, 0x31, 0x02);
+ case FEC_3_4:
+ return stv0299_writereg (i2c, 0x31, 0x04);
+ case FEC_5_6:
+ return stv0299_writereg (i2c, 0x31, 0x08);
+ case FEC_7_8:
+ return stv0299_writereg (i2c, 0x31, 0x10);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static
+fe_code_rate_t stv0299_get_FEC (struct dvb_i2c_bus *i2c)
+{
+ 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 (i2c, 0x1b);
+ index &= 0x7;
+
+ if (index > 4)
+ return FEC_AUTO;
+
+ return fec_tab [index];
+}
+
+
+static
+int stv0299_wait_diseqc_fifo (struct dvb_i2c_bus *i2c, int timeout)
+{
+ unsigned long start = jiffies;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ while (stv0299_readreg(i2c, 0x0a) & 1) {
+ if (jiffies - start > timeout) {
+ dprintk ("%s: timeout!!\n", __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout (1);
+ };
+
+ return 0;
+}
+
+
+static
+int stv0299_wait_diseqc_idle (struct dvb_i2c_bus *i2c, int timeout)
+{
+ unsigned long start = jiffies;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ while ((stv0299_readreg(i2c, 0x0a) & 3) != 2 ) {
+ if (jiffies - start > timeout) {
+ dprintk ("%s: timeout!!\n", __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout (1);
+ };
+
+ return 0;
+}
+
+
+static
+int stv0299_send_diseqc_msg (struct dvb_i2c_bus *i2c,
+ struct dvb_diseqc_master_cmd *m)
+{
+ u8 val;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ val = stv0299_readreg (i2c, 0x08);
+
+ if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */
+ return -EREMOTEIO;
+
+ for (i=0; i<m->msg_len; i++) {
+ if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (stv0299_writereg (i2c, 0x09, m->msg[i]))
+ return -EREMOTEIO;
+ }
+
+ /* Shouldn't we wait for idle state (FE=1, FF=0) here to
+ make certain all bytes have been sent ?
+ Hmm, actually we should do that before all mode changes too ...
+ if (stv0299_wait_diseqc_idle (i2c, 100) < 0) */
+
+ if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+
+static
+int stv0299_send_diseqc_burst (struct dvb_i2c_bus *i2c, fe_sec_mini_cmd_t burst)
+{
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ val = stv0299_readreg (i2c, 0x08);
+
+ if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x2)) /* burst mode */
+ return -EREMOTEIO;
+
+ if (stv0299_writereg (i2c, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff))
+ return -EREMOTEIO;
+
+ if (stv0299_wait_diseqc_fifo (i2c, 100) < 0)
+ return -ETIMEDOUT;
+
+ if (stv0299_writereg (i2c, 0x08, val))
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+
+static
+int stv0299_set_tone (struct dvb_i2c_bus *i2c, fe_sec_tone_mode_t tone)
+{
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ val = stv0299_readreg (i2c, 0x08);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ return stv0299_writereg (i2c, 0x08, val | 0x3);
+ case SEC_TONE_OFF:
+ return stv0299_writereg (i2c, 0x08, (val & ~0x3) | 0x02);
+ default:
+ return -EINVAL;
+ };
+}
+
+
+static
+int stv0299_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
+{
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ val = stv0299_readreg (i2c, 0x0c);
+ val &= 0x0f;
+ val |= 0x40;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ return stv0299_writereg (i2c, 0x0c, val);
+ case SEC_VOLTAGE_18:
+ return stv0299_writereg (i2c, 0x0c, val | 0x10);
+ default:
+ return -EINVAL;
+ };
+}
+
+
+static
+int stv0299_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate)
+{
+ u32 ratio;
+ u32 tmp;
+ u8 aclk = 0xb4, bclk = 0x51;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (srate > M_CLK)
+ srate = M_CLK;
+ if (srate < 500000)
+ srate = 500000;
+
+ if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; }
+ if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; }
+ if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; }
+ if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; }
+ if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; }
+
+#define FIN (M_CLK >> 4)
+
+ tmp = srate << 4;
+ ratio = tmp / FIN;
+
+ tmp = (tmp % FIN) << 8;
+ ratio = (ratio << 8) + tmp / FIN;
+
+ tmp = (tmp % FIN) << 8;
+ ratio = (ratio << 8) + tmp / FIN;
+
+ stv0299_writereg (i2c, 0x13, aclk);
+ stv0299_writereg (i2c, 0x14, bclk);
+ stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0);
+
+ return 0;
+}
+
+
+
+static
+int stv0299_get_symbolrate (struct dvb_i2c_bus *i2c)
+{
+ u32 Mclk = M_CLK / 4096L;
+ u32 srate;
+ s32 offset;
+ u8 sfr[3];
+ s8 rtf;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ stv0299_readregs (i2c, 0x1f, sfr, 3);
+ stv0299_readregs (i2c, 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;
+
+ srate += offset;
+
+ srate += 1000;
+ srate /= 2000;
+ srate *= 2000;
+
+ return srate;
+}
+
+
+static
+int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+{
+ struct dvb_i2c_bus *i2c = fe->i2c;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch (cmd) {
+ case FE_GET_INFO:
+ memcpy (arg, &bsru6_info, sizeof(struct dvb_frontend_info));
+ break;
+
+ case FE_READ_STATUS:
+ {
+ fe_status_t *status = (fe_status_t *) arg;
+ u8 signal = 0xff - stv0299_readreg (i2c, 0x18);
+ u8 sync = stv0299_readreg (i2c, 0x1b);
+
+ *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;
+
+ break;
+ }
+
+ case FE_READ_BER:
+ *((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8)
+ | stv0299_readreg (i2c, 0x1e);
+ break;
+
+ case FE_READ_SIGNAL_STRENGTH:
+ {
+ s32 signal = 0xffff - ((stv0299_readreg (i2c, 0x18) << 8)
+ | stv0299_readreg (i2c, 0x19));
+ signal = signal * 5 / 4;
+ *((u16*) arg) = (signal > 0xffff) ? 0xffff :
+ (signal < 0) ? 0 : signal;
+ break;
+ }
+ case FE_READ_SNR:
+ {
+ s32 snr = 0xffff - ((stv0299_readreg (i2c, 0x24) << 8)
+ | stv0299_readreg (i2c, 0x25));
+ snr = 3 * (snr - 0xa100);
+ *((u16*) arg) = (snr > 0xffff) ? 0xffff :
+ (snr < 0) ? 0 : snr;
+ break;
+ }
+ case FE_READ_UNCORRECTED_BLOCKS:
+ *((u32*) arg) = 0; /* the stv0299 can't measure BER and */
+ return -EOPNOTSUPP; /* errors at the same time.... */
+
+ case FE_SET_FRONTEND:
+ {
+ struct dvb_frontend_parameters *p = arg;
+
+ tsa5059_set_tv_freq (i2c, p->frequency, 3);
+ stv0299_set_inversion (i2c, p->inversion);
+ stv0299_set_FEC (i2c, p->u.qpsk.fec_inner);
+ stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
+ tsa5059_set_tv_freq (i2c, p->frequency, 0);
+ stv0299_writereg (i2c, 0x22, 0x00);
+ stv0299_writereg (i2c, 0x23, 0x00);
+ stv0299_readreg (i2c, 0x23);
+ stv0299_writereg (i2c, 0x12, 0xb9);
+ break;
+ }
+
+ case FE_GET_FRONTEND:
+ {
+ struct dvb_frontend_parameters *p = arg;
+ s32 derot_freq;
+
+ derot_freq = (s32)(s16) ((stv0299_readreg (i2c, 0x22) << 8)
+ | stv0299_readreg (i2c, 0x23));
+
+ derot_freq *= (M_CLK >> 16);
+ derot_freq += 500;
+ derot_freq /= 1000;
+
+ p->frequency += derot_freq;
+ p->inversion = (stv0299_readreg (i2c, 0x0c) & 1) ?
+ INVERSION_OFF : INVERSION_ON;
+ p->u.qpsk.fec_inner = stv0299_get_FEC (i2c);
+ p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c);
+ break;
+ }
+
+ case FE_SLEEP:
+ stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */
+ stv0299_writereg (i2c, 0x02, 0x80);
+ break;
+
+ case FE_INIT:
+ return stv0299_init (i2c);
+
+ case FE_RESET:
+ stv0299_writereg (i2c, 0x22, 0x00);
+ stv0299_writereg (i2c, 0x23, 0x00);
+ stv0299_readreg (i2c, 0x23);
+ stv0299_writereg (i2c, 0x12, 0xb9);
+ break;
+
+ case FE_DISEQC_SEND_MASTER_CMD:
+ return stv0299_send_diseqc_msg (i2c, arg);
+
+ case FE_DISEQC_SEND_BURST:
+ return stv0299_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg);
+
+ case FE_SET_TONE:
+ return stv0299_set_tone (i2c, (fe_sec_tone_mode_t) arg);
+
+ case FE_SET_VOLTAGE:
+ return stv0299_set_voltage (i2c, (fe_sec_voltage_t) arg);
+
+ default:
+ return -EOPNOTSUPP;
+ };
+
+ return 0;
+}
+
+
+
+static
+int bsru6_attach (struct dvb_i2c_bus *i2c)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ if ((stv0299_readreg (i2c, 0x00)) != 0xa1)
+ return -ENODEV;
+
+ dvb_register_frontend (bsru6_ioctl, i2c, NULL, &bsru6_info);
+
+ return 0;
+}
+
+
+static
+void bsru6_detach (struct dvb_i2c_bus *i2c)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ dvb_unregister_frontend (bsru6_ioctl, i2c);
+}
+
+
+static
+int __init init_bsru6 (void)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ return dvb_register_i2c_device (THIS_MODULE, bsru6_attach, bsru6_detach);
+}
+
+
+static
+void __exit exit_bsru6 (void)
+{
+ dprintk ("%s\n", __FUNCTION__);
+
+ dvb_unregister_i2c_device (bsru6_attach);
+}
+
+module_init (init_bsru6);
+module_exit (exit_bsru6);
+
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+MODULE_DESCRIPTION("BSRU6 DVB Frontend driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
diff --git a/linux/drivers/media/dvb/frontends/alps_bsrv2.c b/linux/drivers/media/dvb/frontends/alps_bsrv2.c
new file mode 100644
index 000000000..8da1f5bb5
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/alps_bsrv2.c
@@ -0,0 +1,470 @@
+/*
+ Driver for Alps BSRV2 QPSK Frontend
+
+ 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/module.h>
+#include <linux/init.h>
+
+#include "compat.h"
+#include "dvb_frontend.h"
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+
+static
+struct dvb_frontend_info bsrv2_info = {
+ name: "Alps BSRV2",
+ 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,
+/* symbol_rate_tolerance: ???,*/
+ notifier_delay: 50, /* 1/20 s */
+ 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
+};
+
+
+
+static
+u8 init_1893_tab [] = {
+ 0x01, 0xA4, 0x35, 0x81, 0x2A, 0x0d, 0x55, 0xC4,
+ 0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7F, 0x00,
+ 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x31, 0xb0, 0x14, 0x00, 0xDC, 0x20,
+ 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_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
+int ves1893_writereg (struct dvb_i2c_bus *i2c, int reg, int data)
+{
+ u8 buf [] = { 0x00, reg, data };
+ struct i2c_msg msg = { addr: 0x08, flags: 0, buf: buf, len: 3 };
+ int err;
+
+ if ((err = i2c->xfer (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 ves1893_readreg (struct dvb_i2c_bus *i2c, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { 0x00, reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { addr: 0x08, flags: 0, buf: b0, len: 2 },
+ { addr: 0x08, flags: I2C_M_RD, buf: b1, len: 1 } };
+
+ ret = i2c->xfer (i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return b1[0];
+}
+
+
+static
+int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
+{
+ int ret;
+ struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
+
+ ret = i2c->xfer (i2c, &msg, 1);
+
+ if (ret != 1)
+ printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+
+
+/**
+ * set up the downconverter frequency divisor for a
+ * reference clock comparision frequency of 125 kHz.
+ */
+static
+int sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
+{
+ u32 div = (freq + 479500) / 125;
+ u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x95, (pwr << 5) | 0x30 };
+
+ return sp5659_write (i2c, buf);
+}
+
+
+static
+int ves1893_init (struct dvb_i2c_bus *i2c)
+{
+ int i;
+
+ dprintk("%s: init chip\n", __FUNCTION__);
+
+ for (i=0; i<54; i++)
+ if (init_1893_wtab[i])
+ ves1893_writereg (i2c, i, init_1893_tab[i]);
+
+ return 0;
+}
+
+
+static
+int ves1893_clr_bit (struct dvb_i2c_bus *i2c)
+{
+ ves1893_writereg (i2c, 0, init_1893_tab[0] & 0xfe);
+ ves1893_writereg (i2c, 0, init_1893_tab[0]);
+ ves1893_writereg (i2c, 3, 0x00);
+ return ves1893_writereg (i2c, 3, init_1893_tab[3]);
+}
+
+
+static
+int ves1893_set_inversion (struct dvb_i2c_bus *i2c, fe_spectral_inversion_t inversion)
+{
+ u8 val;
+
+ switch (inversion) {
+ case INVERSION_OFF:
+ val = 0xc0;
+ break;
+ case INVERSION_ON:
+ val = 0x80;
+ break;
+ case INVERSION_AUTO:
+ val = 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ves1893_writereg (i2c, 0x0c, (init_1893_tab[0x0c] & 0x3f) | val);
+}
+
+
+static
+int ves1893_set_fec (struct dvb_i2c_bus *i2c, fe_code_rate_t fec)
+{
+ if (fec == FEC_AUTO)
+ return ves1893_writereg (i2c, 0x0d, 0x08);
+ else if (fec < FEC_1_2 || fec > FEC_8_9)
+ return -EINVAL;
+ else
+ return ves1893_writereg (i2c, 0x0d, fec - FEC_1_2);
+}
+
+
+static
+fe_code_rate_t ves1893_get_fec (struct dvb_i2c_bus *i2c)
+{
+ return FEC_1_2 + ((ves1893_readreg (i2c, 0x0d) >> 4) & 0x7);
+}
+
+
+static
+int ves1893_set_symbolrate (struct dvb_i2c_bus *i2c, u32 srate)
+{
+ u32 BDR;
+ u32 ratio;
+ u8 ADCONF, FCONF, FNR;
+ u32 BDRI;
+ u32 tmp;
+
+ dprintk("%s: srate == %d\n", __FUNCTION__, srate);
+
+ if (srate > 90100000UL/2)
+ srate = 90100000UL/2;
+
+ if (srate < 500000)
+ srate = 500000;
+
+#define MUL (1UL<<26)
+#define FIN (90106000UL>>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);
+ }
+
+ 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", ratio);
+ dprintk("BDR= %08x\n", BDR);
+ dprintk("BDRI= %02x\n", BDRI);
+
+ if (BDRI > 0xff)
+ BDRI = 0xff;
+
+ ves1893_writereg (i2c, 0x06, 0xff & BDR);
+ ves1893_writereg (i2c, 0x07, 0xff & (BDR >> 8));
+ ves1893_writereg (i2c, 0x08, 0x0f & (BDR >> 16));
+
+ ves1893_writereg (i2c, 0x09, BDRI);
+ ves1893_writereg (i2c, 0x20, ADCONF);
+ ves1893_writereg (i2c, 0x21, FCONF);
+
+ if (srate < 6000000)
+ ves1893_writereg (i2c, 0x05, init_1893_tab[0x05] | 0x80);
+ else
+ ves1893_writereg (i2c, 0x05, init_1893_tab[0x05] & 0x7f);
+
+ ves1893_writereg (i2c, 0x00, 0x00);
+ ves1893_writereg (i2c, 0x00, 0x01);
+
+ ves1893_clr_bit (i2c);
+
+ return 0;
+}
+
+
+static
+int ves1893_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
+{
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ return ves1893_writereg (i2c, 0x1f, 0x20);
+ case SEC_VOLTAGE_18:
+ return ves1893_writereg (i2c, 0x1f, 0x30);
+ default:
+ return -EINVAL;
+ };
+}
+
+
+static
+int bsrv2_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+{
+ struct dvb_i2c_bus *i2c = fe->i2c;
+
+ switch (cmd) {
+ case FE_GET_INFO:
+ memcpy (arg, &bsrv2_info, sizeof(struct dvb_frontend_info));
+ break;
+
+ case FE_READ_STATUS:
+ {
+ fe_status_t *status = arg;
+ int sync = ves1893_readreg (i2c, 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;
+
+ break;
+ }
+
+ case FE_READ_BER:
+ {
+ u32 *ber = (u32 *) arg;
+
+ *ber = ves1893_readreg (i2c, 0x15);
+ *ber |= (ves1893_readreg (i2c, 0x16) << 8);
+ *ber |= (ves1893_readreg (i2c, 0x17) << 16);
+ *ber *= 10;
+ break;
+ }
+
+ case FE_READ_SIGNAL_STRENGTH:
+ {
+ u8 signal = ~ves1893_readreg (i2c, 0x0b);
+ *((u16*) arg) = (signal << 8) | signal;
+ break;
+ }
+
+ case FE_READ_SNR:
+ {
+ u8 snr = ~ves1893_readreg (i2c, 0x1c);
+ *(u16*) arg = (snr << 8) | snr;
+ break;
+ }
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ {
+ *(u32*) arg = ves1893_readreg (i2c, 0x18) & 0x7f;
+
+ if (*(u32*) arg == 0x7f)
+ *(u32*) arg = 0xffffffff; /* counter overflow... */
+
+ ves1893_writereg (i2c, 0x18, 0x00); /* reset the counter */
+ ves1893_writereg (i2c, 0x18, 0x80); /* dto. */
+ break;
+ }
+
+ case FE_SET_FRONTEND:
+ {
+ struct dvb_frontend_parameters *p = arg;
+
+ sp5659_set_tv_freq (i2c, p->frequency, 0);
+ ves1893_set_inversion (i2c, p->inversion);
+ ves1893_set_fec (i2c, p->u.qpsk.fec_inner);
+// sp5659_set_tv_freq (i2c, p->frequency, 0);
+ ves1893_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
+ break;
+ }
+
+ case FE_GET_FRONTEND:
+ {
+ struct dvb_frontend_parameters *p = arg;
+ s32 afc;
+
+ afc = ((int)((char)(ves1893_readreg (i2c, 0x0a) << 1)))/2;
+ afc = (afc * (int)(p->u.qpsk.symbol_rate/8))/16;
+
+ p->frequency += afc;
+ p->inversion = (ves1893_readreg (i2c, 0x0f) & 2) ?
+ INVERSION_ON : INVERSION_OFF;
+ p->u.qpsk.fec_inner = ves1893_get_fec (i2c);
+ /* XXX FIXME: timing offset !! */
+ break;
+ }
+
+ case FE_SLEEP:
+ ves1893_writereg (i2c, 0x1f, 0x00); /* LNB power off */
+ return ves1893_writereg (i2c, 0x00, 0x08);
+
+ case FE_INIT:
+ return ves1893_init (i2c);
+
+ case FE_RESET:
+ return ves1893_clr_bit (i2c);
+
+ case FE_SET_TONE:
+ return -EOPNOTSUPP; /* the ves1893 can generate the 22k */
+ /* let's implement this when we have */
+ /* a box that uses the 22K_0 pin... */
+ case FE_SET_VOLTAGE:
+ return ves1893_set_voltage (i2c, (fe_sec_voltage_t) arg);
+
+ default:
+ return -EOPNOTSUPP;
+ };
+
+ return 0;
+}
+
+
+static
+int bsrv2_attach (struct dvb_i2c_bus *i2c)
+{
+ if ((ves1893_readreg (i2c, 0x1e) & 0xf0) != 0xd0)
+ return -ENODEV;
+
+ dvb_register_frontend (bsrv2_ioctl, i2c, NULL, &bsrv2_info);
+
+ return 0;
+}
+
+
+static
+void bsrv2_detach (struct dvb_i2c_bus *i2c)
+{
+ dvb_unregister_frontend (bsrv2_ioctl, i2c);
+}
+
+
+static
+int __init init_bsrv2 (void)
+{
+ return dvb_register_i2c_device (THIS_MODULE, bsrv2_attach, bsrv2_detach);
+}
+
+
+static
+void __exit exit_bsrv2 (void)
+{
+ dvb_unregister_i2c_device (bsrv2_attach);
+}
+
+
+module_init(init_bsrv2);
+module_exit(exit_bsrv2);
+
+
+MODULE_DESCRIPTION("BSRV2 DVB-S Frontend");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug,"i");
+
diff --git a/linux/drivers/media/dvb/frontends/grundig_29504-401.c b/linux/drivers/media/dvb/frontends/grundig_29504-401.c
new file mode 100644
index 000000000..a65e9843f
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/grundig_29504-401.c
@@ -0,0 +1,484 @@
+/*
+ driver for Grundig 29504-401 DVB-T Frontends based on
+ LSI L64781 COFDM demodulator as used in Technotrend DVB-T cards
+
+ Copyright (C) 2001 Holger Waechtler <holger@convergence.de>
+ for Convergence Integrated Media GmbH
+ Marko Kohtala <marko.kohtala@nokia.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "compat.h"
+#include "dvb_frontend.h"
+
+static int debug = 0;
+
+#define dprintk if (debug) printk
+
+
+struct dvb_frontend_info grundig_29504_401_info = {
+ name: "Grundig 29504-401",
+ type: FE_OFDM,
+/* frequency_min: ???,*/
+/* frequency_max: ???,*/
+ frequency_stepsize: 166666,
+/* frequency_tolerance: ???,*/
+/* symbol_rate_tolerance: ???,*/
+ notifier_delay: 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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
+ FE_CAN_MUTE_TS
+};
+
+
+static
+int l64781_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { addr: 0x55, flags: 0, buf: buf, len: 2 };
+
+ if ((ret = i2c->xfer (i2c, &msg, 1)) != 1)
+ dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
+ __FUNCTION__, reg, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+
+static
+u8 l64781_readreg (struct dvb_i2c_bus *i2c, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { addr: 0x55, flags: 0, buf: b0, len: 1 },
+ { addr: 0x55, flags: I2C_M_RD, buf: b1, len: 1 } };
+
+ ret = i2c->xfer (i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return b1[0];
+}
+
+
+static
+int tsa5060_write (struct dvb_i2c_bus *i2c, u8 data [4])
+{
+ int ret;
+ struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
+
+ if ((ret = i2c->xfer (i2c, &msg, 1)) != 1)
+ dprintk ("%s: write_reg error == %02x!\n", __FUNCTION__, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+
+/**
+ * set up the downconverter frequency divisor for a
+ * reference clock comparision frequency of 166666 Hz.
+ * frequency offset is 36000000 Hz.
+ */
+static
+int tsa5060_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
+{
+ u32 div;
+ u8 buf [4];
+ u8 cfg;
+
+ if (freq < 700000000) {
+ div = (36000000 + freq) / 31250;
+ cfg = 0x86;
+ } else {
+ div = (36000000 + freq) / 166666;
+ cfg = 0x88;
+ }
+
+ buf [0] = (div >> 8) & 0x7f;
+ buf [1] = div & 0xff;
+ buf [2] = ((div >> 10) & 0x60) | cfg;
+ buf [3] = pwr << 6;
+
+ return tsa5060_write (i2c, buf);
+}
+
+
+static
+void apply_tps (struct dvb_i2c_bus *i2c)
+{
+ l64781_writereg (i2c, 0x2a, 0x00);
+ l64781_writereg (i2c, 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 (i2c, 0x2a, 0x02);
+}
+
+
+static
+void reset_afc (struct dvb_i2c_bus *i2c)
+{
+ /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for
+ timing offset */
+ l64781_writereg (i2c, 0x07, 0x9e); /* stall AFC */
+ l64781_writereg (i2c, 0x08, 0); /* AFC INIT FREQ */
+ l64781_writereg (i2c, 0x09, 0);
+ l64781_writereg (i2c, 0x0a, 0);
+ l64781_writereg (i2c, 0x07, 0x8e);
+ l64781_writereg (i2c, 0x0e, 0); /* AGC gain to zero in beginning */
+ l64781_writereg (i2c, 0x11, 0x80); /* stall TIM */
+ l64781_writereg (i2c, 0x10, 0); /* TIM_OFFSET_LSB */
+ l64781_writereg (i2c, 0x12, 0);
+ l64781_writereg (i2c, 0x13, 0);
+ l64781_writereg (i2c, 0x11, 0x00);
+}
+
+
+static
+int apply_frontend_param (struct dvb_i2c_bus *i2c,
+ struct dvb_frontend_parameters *param)
+{
+ /* 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;
+
+ if (param->inversion != INVERSION_ON &&
+ param->inversion != INVERSION_OFF)
+ return -EINVAL;
+
+ if (p->bandwidth < BANDWIDTH_8_MHZ || p->bandwidth > BANDWIDTH_6_MHZ)
+ 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 (i2c, 0x04, val0x04);
+ l64781_writereg (i2c, 0x05, val0x05);
+ l64781_writereg (i2c, 0x06, val0x06);
+
+ reset_afc (i2c);
+
+ /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */
+ l64781_writereg (i2c, 0x15,
+ p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3);
+ l64781_writereg (i2c, 0x16, init_freq & 0xff);
+ l64781_writereg (i2c, 0x17, (init_freq >> 8) & 0xff);
+ l64781_writereg (i2c, 0x18, (init_freq >> 16) & 0xff);
+
+ l64781_writereg (i2c, 0x1b, spi_bias & 0xff);
+ l64781_writereg (i2c, 0x1c, (spi_bias >> 8) & 0xff);
+ l64781_writereg (i2c, 0x1d, ((spi_bias >> 16) & 0x7f) |
+ (param->inversion == INVERSION_ON ? 0x80 : 0x00));
+
+ l64781_writereg (i2c, 0x22, ddfs_offset_fixed & 0xff);
+ l64781_writereg (i2c, 0x23, (ddfs_offset_fixed >> 8) & 0x3f);
+
+ l64781_readreg (i2c, 0x00); /* clear interrupt registers... */
+ l64781_readreg (i2c, 0x01); /* dto. */
+
+ apply_tps (i2c);
+
+ return 0;
+}
+
+
+static
+void reset_and_configure (struct dvb_i2c_bus *i2c)
+{
+ u8 buf [] = { 0x06 };
+ struct i2c_msg msg = { addr: 0x00, flags: 0, buf: buf, len: 1 };
+
+ i2c->xfer (i2c, &msg, 1);
+}
+
+
+
+static
+int init (struct dvb_i2c_bus *i2c)
+{
+ reset_and_configure (i2c);
+
+ /* Power up */
+ l64781_writereg (i2c, 0x3e, 0xa5);
+
+ /* Reset hard */
+ l64781_writereg (i2c, 0x2a, 0x04);
+ l64781_writereg (i2c, 0x2a, 0x00);
+
+ /* Set tuner specific things */
+ /* AFC_POL, set also in reset_afc */
+ l64781_writereg (i2c, 0x07, 0x8e);
+
+ /* Use internal ADC */
+ l64781_writereg (i2c, 0x0b, 0x81);
+
+ /* AGC loop gain, and polarity is positive */
+ l64781_writereg (i2c, 0x0c, 0x84);
+
+ /* Internal ADC outputs two's complement */
+ l64781_writereg (i2c, 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 (i2c, 0x19, 0x92);*/
+
+ /* Everything is two's complement, soft bit and CSI_OUT too */
+ l64781_writereg (i2c, 0x1e, 0x49);
+
+ return 0;
+}
+
+
+static
+int grundig_29504_401_ioctl (struct dvb_frontend *fe,
+ unsigned int cmd, void *arg)
+{
+ struct dvb_i2c_bus *i2c = fe->i2c;
+ switch (cmd) {
+ case FE_GET_INFO:
+ memcpy (arg, &grundig_29504_401_info,
+ sizeof(struct dvb_frontend_info));
+ break;
+
+ case FE_READ_STATUS:
+ {
+ fe_status_t *status = (fe_status_t *) arg;
+ int sync = l64781_readreg (i2c, 0x32);
+ int gain = l64781_readreg (i2c, 0x0e);
+
+ l64781_readreg (i2c, 0x00); /* clear interrupt registers... */
+ l64781_readreg (i2c, 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;
+
+ break;
+ }
+
+ case FE_READ_BER:
+ {
+ /* XXX FIXME: set up counting period (reg 0x26...0x28)
+ */
+ u32 *ber = (u32 *) arg;
+ *ber = l64781_readreg (i2c, 0x39)
+ | (l64781_readreg (i2c, 0x3a) << 8);
+ break;
+ }
+
+ case FE_READ_SIGNAL_STRENGTH:
+ {
+ u8 gain = l64781_readreg (i2c, 0x0e);
+ *(u16 *) arg = (gain << 8) | gain;
+ break;
+ }
+
+ case FE_READ_SNR:
+ {
+ u16 *snr = (u16 *) arg;
+ u8 avg_quality = 0xff - l64781_readreg (i2c, 0x33);
+ *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/
+ break;
+ }
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ {
+ u32 *ub = (u32 *) arg;
+ *ub = l64781_readreg (i2c, 0x37)
+ | (l64781_readreg (i2c, 0x38) << 8);
+ break;
+ }
+
+ case FE_SET_FRONTEND:
+ {
+ struct dvb_frontend_parameters *p = arg;
+
+ tsa5060_set_tv_freq (i2c, p->frequency, 3);
+ apply_frontend_param (i2c, p);
+// tsa5060_set_tv_freq (i2c, p->frequency, 0);
+ }
+ case FE_GET_FRONTEND:
+ /* we could correct the frequency here, but...
+ * (...do you want to implement this?;)
+ */
+ return 0;
+
+ case FE_SLEEP:
+ /* Power down */
+ return l64781_writereg (i2c, 0x3e, 0x5a);
+
+ case FE_INIT:
+ return init (i2c);
+
+ case FE_RESET:
+ //reset_afc (i2c);
+ apply_tps (i2c);
+ l64781_readreg (i2c, 0x00); /* clear interrupt registers... */
+ l64781_readreg (i2c, 0x01); /* dto. */
+ break;
+
+ default:
+ dprintk ("%s: unknown command !!!\n", __FUNCTION__);
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+
+static
+int l64781_attach (struct dvb_i2c_bus *i2c)
+{
+ u8 b0 [] = { 0x1a };
+ u8 b1 [] = { 0x00 };
+ struct i2c_msg msg [] = { { addr: 0x55, flags: 0, buf: b0, len: 1 },
+ { addr: 0x55, flags: I2C_M_RD, buf: b1, len: 1 } };
+
+ if (i2c->xfer (i2c, msg, 2) == 2) /* probably an EEPROM... */
+ return -ENODEV;
+
+ reset_and_configure (i2c);
+
+ if (i2c->xfer (i2c, msg, 2) != 2) /* nothing... */
+ return -ENODEV;
+
+ if (b1[0] != 0xa1)
+ return -ENODEV;
+
+ dvb_register_frontend (grundig_29504_401_ioctl, i2c, NULL,
+ &grundig_29504_401_info);
+ return 0;
+}
+
+
+static
+void l64781_detach (struct dvb_i2c_bus *i2c)
+{
+ dvb_unregister_frontend (grundig_29504_401_ioctl, i2c);
+}
+
+
+static
+int __init init_grundig_29504_401 (void)
+{
+ return dvb_register_i2c_device (THIS_MODULE,
+ l64781_attach, l64781_detach);
+}
+
+
+static
+void __exit exit_grundig_29504_401 (void)
+{
+ dvb_unregister_i2c_device (l64781_attach);
+}
+
+module_init(init_grundig_29504_401);
+module_exit(exit_grundig_29504_401);
+
+MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+MODULE_DESCRIPTION("Grundig 29504-401 DVB-T Frontend");
+MODULE_AUTHOR("Holger Waechtler, Marko Kohtala");
+MODULE_LICENSE("GPL");
+
diff --git a/linux/drivers/media/dvb/frontends/grundig_29504-491.c b/linux/drivers/media/dvb/frontends/grundig_29504-491.c
new file mode 100644
index 000000000..563377137
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/grundig_29504-491.c
@@ -0,0 +1,496 @@
+/*
+ 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.
+
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include "compat.h"
+#include "dvb_frontend.h"
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+
+static
+struct dvb_frontend_info grundig_29504_491_info = {
+ name: "Grundig 29504-491, (TDA8083 based)",
+ 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: ???,*/
+ notifier_delay: 0,
+ 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
+};
+
+
+
+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 dvb_i2c_bus *i2c, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf [] = { reg, data };
+ struct i2c_msg msg = { addr: 0x68, flags: 0, buf: buf, len: 2 };
+
+ ret = i2c->xfer (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 dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len)
+{
+ int ret;
+ struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: &reg1, len: 1 },
+ { addr: 0x68, flags: I2C_M_RD, buf: b, len: len } };
+
+ ret = i2c->xfer (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 dvb_i2c_bus *i2c, u8 reg)
+{
+ u8 val;
+
+ tda8083_readregs (i2c, reg, &val, 1);
+
+ return val;
+}
+
+
+static
+int tsa5522_write (struct dvb_i2c_bus *i2c, u8 data [4])
+{
+ int ret;
+ struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
+
+ ret = i2c->xfer (i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+
+/**
+ * set up the downconverter frequency divisor for a
+ * reference clock comparision frequency of 125 kHz.
+ */
+static
+int tsa5522_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
+{
+ u32 div = freq / 125;
+ u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x8e, 0x00 };
+
+ return tsa5522_write (i2c, buf);
+}
+
+
+static int
+tda8083_init (struct dvb_i2c_bus *i2c)
+{
+ int i;
+
+ dprintk("%s: init TDA8083\n", __FILE__);
+
+ for (i=0; i<44; i++)
+ tda8083_writereg (i2c, i, tda8083_init_tab[i]);
+
+ return 0;
+}
+
+
+static int
+tda8083_set_inversion (struct dvb_i2c_bus *i2c, 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 dvb_i2c_bus *i2c, fe_code_rate_t fec)
+{
+ if (fec == FEC_AUTO)
+ return tda8083_writereg (i2c, 0x07, 0xff);
+
+ if (fec >= FEC_1_2 && fec <= FEC_8_9)
+ return tda8083_writereg (i2c, 0x07, 1 << (FEC_8_9 - fec));
+
+ return -EINVAL;
+}
+
+
+static
+fe_code_rate_t tda8083_get_fec (struct dvb_i2c_bus *i2c)
+{
+ 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 (i2c, 0x0e) & 0x3;
+
+ if (index > 7)
+ return FEC_NONE;
+
+ return fec_tab [index];
+}
+
+
+static
+int tda8083_set_symbolrate (struct dvb_i2c_bus *i2c, 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", ratio);
+
+ tda8083_writereg (i2c, 0x05, filter);
+ tda8083_writereg (i2c, 0x02, (ratio >> 16) & 0xff);
+ tda8083_writereg (i2c, 0x03, (ratio >> 8) & 0xff);
+ tda8083_writereg (i2c, 0x04, (ratio ) & 0xff);
+
+ tda8083_writereg (i2c, 0x00, 0x3c);
+ tda8083_writereg (i2c, 0x00, 0x04);
+
+ return 1;
+}
+
+
+static
+void tda8083_wait_diseqc_fifo (struct dvb_i2c_bus *i2c, int timeout)
+{
+ unsigned long start = jiffies;
+
+ while (jiffies - start < timeout &&
+ !(tda8083_readreg(i2c, 0x02) & 0x80))
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout (5);
+ };
+}
+
+
+static
+int tda8083_send_diseqc_msg (struct dvb_i2c_bus *i2c,
+ struct dvb_diseqc_master_cmd *m)
+{
+ int i;
+
+ tda8083_writereg (i2c, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */
+
+ for (i=0; i<m->msg_len; i++)
+ tda8083_writereg (i2c, 0x23 + i, m->msg[i]);
+
+ tda8083_writereg (i2c, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */
+
+ tda8083_wait_diseqc_fifo (i2c, 100);
+
+ return 0;
+}
+
+
+static
+int tda8083_send_diseqc_burst (struct dvb_i2c_bus *i2c, fe_sec_mini_cmd_t burst)
+{
+ switch (burst) {
+ case SEC_MINI_A:
+ tda8083_writereg (i2c, 0x29, (5 << 2)); /* send burst A */
+ break;
+ case SEC_MINI_B:
+ tda8083_writereg (i2c, 0x29, (7 << 2)); /* send B */
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ tda8083_wait_diseqc_fifo (i2c, 100);
+
+ return 0;
+}
+
+
+static
+int tda8083_set_tone (struct dvb_i2c_bus *i2c, fe_sec_tone_mode_t tone)
+{
+ tda8083_writereg (i2c, 0x26, 0xf1);
+
+ switch (tone) {
+ case SEC_TONE_OFF:
+ return tda8083_writereg (i2c, 0x29, 0x00);
+ case SEC_TONE_ON:
+ return tda8083_writereg (i2c, 0x29, 0x80);
+ default:
+ return -EINVAL;
+ };
+}
+
+
+static
+int tda8083_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
+{
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ return tda8083_writereg (i2c, 0x20, 0x00);
+ case SEC_VOLTAGE_18:
+ return tda8083_writereg (i2c, 0x20, 0x11);
+ default:
+ return -EINVAL;
+ };
+}
+
+
+static
+int grundig_29504_491_ioctl (struct dvb_frontend *fe, unsigned int cmd,
+ void *arg)
+{
+ struct dvb_i2c_bus *i2c = fe->i2c;
+
+ switch (cmd) {
+ case FE_GET_INFO:
+ memcpy (arg, &grundig_29504_491_info,
+ sizeof(struct dvb_frontend_info));
+ break;
+
+ case FE_READ_STATUS:
+ {
+ fe_status_t *status=(fe_status_t *) arg;
+ u8 signal = ~tda8083_readreg (i2c, 0x01);
+ u8 sync = tda8083_readreg (i2c, 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;
+
+ break;
+ }
+
+ case FE_READ_BER:
+ *((u32*) arg) = 0; /* XXX FIXME: implement me!!! */
+ return -EOPNOTSUPP;
+
+ case FE_READ_SIGNAL_STRENGTH:
+ {
+ u8 signal = ~tda8083_readreg (i2c, 0x01);
+ *((u16*) arg) = (signal << 8) | signal;
+ break;
+ }
+ case FE_READ_SNR:
+ {
+ u8 snr = tda8083_readreg (i2c, 0x08);
+ *((u16*) arg) = (snr << 8) | snr;
+ break;
+ }
+ case FE_READ_UNCORRECTED_BLOCKS:
+ *((u32*) arg) = 0; /* XXX FIXME: implement me!!! */
+ return -EOPNOTSUPP;
+
+
+ case FE_SET_FRONTEND:
+ {
+ struct dvb_frontend_parameters *p = arg;
+
+ tsa5522_set_tv_freq (i2c, p->frequency);
+ tda8083_set_inversion (i2c, p->inversion);
+ tda8083_set_fec (i2c, p->u.qpsk.fec_inner);
+ tda8083_set_symbolrate (i2c, p->u.qpsk.symbol_rate);
+
+ tda8083_writereg (i2c, 0x00, 0x3c);
+ tda8083_writereg (i2c, 0x00, 0x04);
+
+ break;
+ }
+
+ case FE_GET_FRONTEND:
+ {
+ struct dvb_frontend_parameters *p = arg;
+
+ /* FIXME: get symbolrate & frequency offset...*/
+ /*p->frequency = ???;*/
+ p->inversion = (tda8083_readreg (i2c, 0x0e) & 0x80) ?
+ INVERSION_ON : INVERSION_OFF;
+ p->u.qpsk.fec_inner = tda8083_get_fec (i2c);
+ /*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (i2c);*/
+ break;
+ }
+
+ case FE_SLEEP:
+ tda8083_writereg (i2c, 0x00, 0x02);
+ break;
+
+ case FE_INIT:
+ tda8083_init (i2c);
+ tda8083_writereg (i2c, 0x00, 0x3c);
+ tda8083_writereg (i2c, 0x00, 0x04);
+ break;
+
+ case FE_RESET:
+ tda8083_writereg (i2c, 0x00, 0x3c);
+ tda8083_writereg (i2c, 0x00, 0x04);
+ break;
+
+ case FE_DISEQC_SEND_MASTER_CMD:
+ return tda8083_send_diseqc_msg (i2c, arg);
+
+ case FE_DISEQC_SEND_BURST:
+ tda8083_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg);
+ tda8083_writereg (i2c, 0x00, 0x3c);
+ tda8083_writereg (i2c, 0x00, 0x04);
+
+ break;
+
+ case FE_SET_TONE:
+ tda8083_set_tone (i2c, (fe_sec_tone_mode_t) arg);
+ tda8083_writereg (i2c, 0x00, 0x3c);
+ tda8083_writereg (i2c, 0x00, 0x04);
+ break;
+
+ case FE_SET_VOLTAGE:
+ tda8083_set_voltage (i2c, (fe_sec_voltage_t) arg);
+ tda8083_writereg (i2c, 0x00, 0x3c);
+ tda8083_writereg (i2c, 0x00, 0x04);
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ };
+
+ return 0;
+}
+
+
+static
+int tda8083_attach (struct dvb_i2c_bus *i2c)
+{
+ if ((tda8083_readreg (i2c, 0x00)) != 0x05)
+ return -ENODEV;
+
+ dvb_register_frontend (grundig_29504_491_ioctl, i2c, NULL,
+ &grundig_29504_491_info);
+
+ return 0;
+}
+
+
+static
+void tda8083_detach (struct dvb_i2c_bus *i2c)
+{
+ dvb_unregister_frontend (grundig_29504_491_ioctl, i2c);
+}
+
+
+static
+int __init init_tda8083 (void)
+{
+ return dvb_register_i2c_device (THIS_MODULE,
+ tda8083_attach, tda8083_detach);
+}
+
+
+static
+void __exit exit_tda8083 (void)
+{
+ dvb_unregister_i2c_device (tda8083_attach);
+}
+
+module_init(init_tda8083);
+module_exit(exit_tda8083);
+
+MODULE_PARM(debug,"i");
+MODULE_DESCRIPTION("Grundig 29504-491 DVB frontend driver");
+MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
+
diff --git a/linux/drivers/media/dvb/frontends/ves1820.c b/linux/drivers/media/dvb/frontends/ves1820.c
new file mode 100644
index 000000000..fe2c4e20d
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/ves1820.c
@@ -0,0 +1,537 @@
+/*
+ VES1820 - 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>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "compat.h"
+#include "dvb_frontend.h"
+
+
+static int debug = 0;
+#define dprintk if (debug) printk
+
+
+/**
+ * since we need only a few bits to store internal state we don't allocate
+ * extra memory but use frontend->data as bitfield
+ */
+
+#define SET_PWM(frontend,pwm) do { \
+ (int) frontend->data &= ~0xff; \
+ (int) frontend->data |= pwm; \
+} while (0)
+
+#define SET_REG0(frontend,reg0) do { \
+ (int) frontend->data &= ~(0xff << 8); \
+ (int) frontend->data |= reg0 << 8; \
+} while (0)
+
+#define SET_TUNER(frontend,type) do { \
+ (int) frontend->data &= ~(0xff << 16); \
+ (int) frontend->data |= type << 16; \
+} while (0)
+
+#define GET_PWM(frontend) ((u8) ((int) frontend->data & 0xff))
+#define GET_REG0(frontend) ((u8) (((int) frontend->data >> 8) & 0xff))
+#define GET_TUNER(frontend) ((u8) (((int) frontend->data >> 16) & 0xff))
+
+
+
+static
+struct dvb_frontend_info ves1820_info = {
+ name: "VES1820/Grundig tuner as used on the Siemens DVB-C card",
+ type: FE_QAM,
+ frequency_stepsize: 62500,
+ frequency_min: 51000000,
+ frequency_max: 858000000,
+#if 0
+ frequency_tolerance: ???,
+ symbol_rate_min: ???,
+ symbol_rate_max: ???,
+ symbol_rate_tolerance: ???, /* ppm */ /* == 8% (spec p. 5) */
+ notifier_delay: ?,
+#endif
+ caps: FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256
+};
+
+
+
+static
+u8 ves1820_inittab [] =
+{
+ 0x69, 0x6A, 0x9B, 0x0A, 0x52, 0x46, 0x26, 0x1A,
+ 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x28,
+ 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 dvb_i2c_bus *i2c, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { 0x00, reg, data };
+ struct i2c_msg msg = { addr: 0x09, flags: 0, buf: buf, len: 3 };
+
+ ret = i2c->xfer (i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: writereg error "
+ "(reg == 0x%02x, val == 0x%02x, ret == %i)\n",
+ __FUNCTION__, reg, data, ret);
+
+ mdelay(10);
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+
+static
+u8 ves1820_readreg (struct dvb_i2c_bus *i2c, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { 0x00, reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { addr: 0x09, flags: 0, buf: b0, len: 2 },
+ { addr: 0x09, flags: I2C_M_RD, buf: b1, len: 1 } };
+
+
+ ret = i2c->xfer (i2c, msg, 2);
+
+ if (ret != 2)
+ dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return b1[0];
+}
+
+
+static
+int tuner_write (struct dvb_i2c_bus *i2c, u8 addr, u8 data [4])
+{
+ int ret;
+ struct i2c_msg msg = { addr: addr, flags: 0, buf: data, len: 4 };
+
+ ret = i2c->xfer (i2c, &msg, 1);
+
+ if (ret != 1)
+ printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+
+/**
+ * set up the downconverter frequency divisor for a
+ * reference clock comparision frequency of 62.5 kHz.
+ */
+static
+int tuner_set_tv_freq (struct dvb_frontend *frontend, u32 freq)
+{
+ u32 div;
+ static u8 addr [] = { 0x61, 0x62 };
+ static u8 byte3 [] = { 0x8e, 0x85 };
+ int tuner_type = GET_TUNER(frontend);
+ u8 buf [4];
+
+ div = (freq + 36250000 + 31250) / 62500;
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = byte3[tuner_type];
+
+ if (tuner_type == 1) {
+ buf[2] |= (div >> 10) & 0x60;
+ buf[3] = (freq < 174000000 ? 0x88 :
+ freq < 470000000 ? 0x84 : 0x81);
+ } else {
+ buf[3] = (freq < 174000000 ? 0xa1 :
+ freq < 454000000 ? 0x92 : 0x34);
+ }
+
+ return tuner_write (frontend->i2c, addr[tuner_type], buf);
+}
+
+
+static
+int probe_tuner (struct dvb_frontend *frontend)
+{
+ struct dvb_i2c_bus *i2c = frontend->i2c;
+ struct i2c_msg msg = { addr: 0x61, flags: 0, buf: NULL, len: 0 };
+
+ if (i2c->xfer(i2c, &msg, 1) == 1) {
+ SET_TUNER(frontend,0);
+ printk ("%s: setup for tuner spXXXX\n", __FILE__);
+ } else {
+ SET_TUNER(frontend,1);
+ printk ("%s: setup for tuner sp5659c\n", __FILE__);
+ }
+
+ return 0;
+}
+
+
+static
+int ves1820_init (struct dvb_frontend *frontend)
+{
+ struct dvb_i2c_bus *i2c = frontend->i2c;
+ u8 b0 [] = { 0xff };
+ u8 pwm;
+ int i;
+ struct i2c_msg msg [] = { { addr: 0x28, flags: 0, buf: b0, len: 1 },
+ { addr: 0x28, flags: I2C_M_RD, buf: &pwm, len: 1 } };
+
+ dprintk("VES1820: init chip\n");
+
+ i2c->xfer (i2c, msg, 2);
+
+ dprintk("VES1820: pwm=%02x\n", pwm);
+
+ if (pwm == 0xff)
+ pwm=0x48;
+
+ ves1820_writereg (i2c, 0, 0);
+
+ for (i=0; i<53; i++)
+ ves1820_writereg (i2c, i, ves1820_inittab[i]);
+
+ ves1820_writereg (i2c, 0x34, pwm);
+
+ (int) frontend->data = 0;
+ SET_PWM(frontend,pwm);
+
+ probe_tuner (frontend);
+
+ return 0;
+}
+
+
+static
+int ves1820_setup_reg0 (struct dvb_frontend *frontend,
+ u8 real_qam, fe_spectral_inversion_t inversion)
+{
+ struct dvb_i2c_bus *i2c = frontend->i2c;
+ u8 reg0 = (ves1820_inittab[0] & 0xe3) | (real_qam << 2);
+
+ switch (inversion) {
+ case INVERSION_OFF: /* XXX FIXME: reversed?? p. 25 */
+ reg0 |= 0x20;
+ break;
+
+ case INVERSION_ON:
+ reg0 &= 0xdf;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ SET_REG0(frontend, reg0);
+
+ ves1820_writereg (i2c, 0x00, reg0 & 0xfe);
+ ves1820_writereg (i2c, 0x00, reg0);
+
+ return 0;
+}
+
+
+static
+int ves1820_reset (struct dvb_frontend *frontend)
+{
+ struct dvb_i2c_bus *i2c = frontend->i2c;
+ u8 reg0 = GET_REG0(frontend);
+
+ ves1820_writereg (i2c, 0x00, reg0 & 0xfe);
+ ves1820_writereg (i2c, 0x00, reg0);
+
+ return 0;
+}
+
+
+static
+void ves1820_reset_uncorrected_block_counter (struct dvb_i2c_bus *i2c)
+{
+ ves1820_writereg (i2c, 0x10, ves1820_inittab[0x10] & 0xdf);
+ ves1820_writereg (i2c, 0x10, ves1820_inittab[0x10]);
+}
+
+
+static
+int ves1820_set_symbolrate (struct dvb_i2c_bus *i2c, u32 symbolrate)
+{
+ s32 BDR;
+ s32 BDRI;
+ s16 SFIL=0;
+ u16 NDEC = 0;
+ u32 tmp, ratio;
+
+#define XIN 57840000UL
+#define FIN (57840000UL>>4)
+
+ 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) | ves1820_inittab[0x0E];
+
+ NDEC = (NDEC << 6) | ves1820_inittab[0x03];
+
+ ves1820_writereg (i2c, 0x03, NDEC);
+ ves1820_writereg (i2c, 0x0a, BDR&0xff);
+ ves1820_writereg (i2c, 0x0b, (BDR>> 8)&0xff);
+ ves1820_writereg (i2c, 0x0c, (BDR>>16)&0x3f);
+
+ ves1820_writereg (i2c, 0x0d, BDRI);
+ ves1820_writereg (i2c, 0x0e, SFIL);
+
+ return 0;
+}
+
+
+static
+void ves1820_reset_pwm (struct dvb_frontend *frontend)
+{
+ u8 pwm = GET_PWM(frontend);
+
+ ves1820_writereg (frontend->i2c, 0x34, pwm);
+}
+
+
+typedef struct {
+ fe_modulation_t QAM_Mode;
+ int NoOfSym;
+ u8 Reg1;
+ u8 Reg5;
+ u8 Reg8;
+ u8 Reg9;
+} QAM_SETTING;
+
+
+QAM_SETTING QAM_Values[] = {
+ { QAM_16, 16, 140, 164, 162, 145 },
+ { QAM_32, 32, 140, 120, 116, 150 },
+ { QAM_64, 64, 106, 70, 67, 106 },
+ { QAM_128, 128, 120, 54, 52, 126 },
+ { QAM_256, 256, 92, 38, 35, 107 }
+};
+
+
+static
+int ves1820_set_parameters (struct dvb_frontend *frontend,
+ struct dvb_frontend_parameters *p)
+{
+ struct dvb_i2c_bus* i2c = frontend->i2c;
+ int real_qam;
+
+ switch (p->u.qam.modulation) {
+ case QAM_16 : real_qam = 0; break;
+ case QAM_32 : real_qam = 1; break;
+ case QAM_64 : real_qam = 2; break;
+ case QAM_128: real_qam = 3; break;
+ case QAM_256: real_qam = 4; break;
+ default:
+ return -EINVAL;
+ }
+
+ tuner_set_tv_freq (frontend, p->frequency);
+ ves1820_set_symbolrate (i2c, p->u.qam.symbol_rate);
+ ves1820_reset_pwm (frontend);
+
+ ves1820_writereg (i2c, 0x01, QAM_Values[real_qam].Reg1);
+ ves1820_writereg (i2c, 0x05, QAM_Values[real_qam].Reg5);
+ ves1820_writereg (i2c, 0x08, QAM_Values[real_qam].Reg8);
+ ves1820_writereg (i2c, 0x09, QAM_Values[real_qam].Reg9);
+
+ ves1820_setup_reg0 (frontend, real_qam, p->inversion);
+
+ return 0;
+}
+
+
+
+static
+int ves1820_ioctl (struct dvb_frontend *frontend, unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+ case FE_GET_INFO:
+ memcpy (arg, &ves1820_info, sizeof(struct dvb_frontend_info));
+ break;
+
+ case FE_READ_STATUS:
+ {
+ fe_status_t *status = (fe_status_t *) arg;
+ int sync;
+
+ *status = 0;
+
+ sync = ves1820_readreg (frontend->i2c, 0x11);
+
+ if (sync & 2)
+ *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;
+
+ break;
+ }
+
+ case FE_READ_BER:
+ *((u32*) arg) = ves1820_readreg(frontend->i2c, 0x14) |
+ (ves1820_readreg(frontend->i2c, 0x15) << 8) |
+ (ves1820_readreg(frontend->i2c, 0x16) << 16);
+ /* XXX FIXME: scale!!*/
+ break;
+
+ case FE_READ_SIGNAL_STRENGTH:
+ {
+ u8 gain = ves1820_readreg(frontend->i2c, 0x17);
+ *((u16*) arg) = (gain << 8) | gain;
+ break;
+ }
+
+ case FE_READ_SNR:
+ {
+ u8 quality = ~ves1820_readreg(frontend->i2c, 0x18);
+ *((u16*) arg) = (quality << 8) | quality;
+ break;
+ }
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ *((u32*) arg) = ves1820_readreg (frontend->i2c, 0x13) & 0x7f;
+ if (*((u32*) arg) == 0x7f)
+ *((u32*) arg) = 0xffffffff;
+ ves1820_reset_uncorrected_block_counter (frontend->i2c);
+ break;
+
+ case FE_SET_FRONTEND:
+ return ves1820_set_parameters (frontend, arg);
+
+ case FE_GET_FRONTEND:
+ /* XXX FIXME: implement! */
+/*
+ struct frontend *front = (struct frontend *)arg;
+
+ front->afc=(int)((char)(readreg(client,0x19)));
+ front->afc=(front->afc*(int)(front->param.u.qam.SymbolRate/8))/128;
+*/
+ break;
+
+ case FE_SLEEP:
+ ves1820_writereg (frontend->i2c, 0x1b, 0x02); /* pdown ADC */
+ ves1820_writereg (frontend->i2c, 0x00, 0x80); /* standby */
+ break;
+
+ case FE_INIT:
+ return ves1820_init (frontend);
+
+ case FE_RESET:
+ ves1820_reset (frontend);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static
+int ves1820_attach (struct dvb_i2c_bus *i2c)
+{
+ if ((ves1820_readreg (i2c, 0x1a) & 0xf0) != 0x70)
+ return -ENODEV;
+
+ dvb_register_frontend (ves1820_ioctl, i2c, NULL, &ves1820_info);
+
+ return 0;
+}
+
+
+static
+void ves1820_detach (struct dvb_i2c_bus *i2c)
+{
+ dvb_unregister_frontend (ves1820_ioctl, i2c);
+}
+
+
+static
+int __init init_ves1820 (void)
+{
+ return dvb_register_i2c_device (THIS_MODULE,
+ ves1820_attach, ves1820_detach);
+}
+
+
+static
+void __exit exit_ves1820 (void)
+{
+ dvb_unregister_i2c_device (ves1820_attach);
+}
+
+
+module_init(init_ves1820);
+module_exit(exit_ves1820);
+
+MODULE_DESCRIPTION("");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug,"i");
+