diff options
author | Holger Waechtler <devnull@localhost> | 2002-10-16 16:52:27 +0000 |
---|---|---|
committer | Holger Waechtler <devnull@localhost> | 2002-10-16 16:52:27 +0000 |
commit | 4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7 (patch) | |
tree | 23af98b0e1da37f97558e63953011e23d1e360a5 /linux/drivers/media/dvb/frontends | |
download | mediapointer-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.help | 32 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/Config.in | 9 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/Makefile | 14 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/alps_bsru6.c | 700 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/alps_bsrv2.c | 470 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/grundig_29504-401.c | 484 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/grundig_29504-491.c | 496 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/ves1820.c | 537 |
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: ®1, 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 = ¶m->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: ®1, 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"); + |