From 96dc63cd3ca635f5e626e16a04b9f15887d026f6 Mon Sep 17 00:00:00 2001 From: Holger Waechtler Date: Mon, 23 Dec 2002 21:25:34 +0000 Subject: generic stv0299 driver by Peter Schildmann replaces the old BSRU6 driver.. --- linux/drivers/media/dvb/frontends/Kconfig | 10 +- linux/drivers/media/dvb/frontends/Makefile | 2 +- linux/drivers/media/dvb/frontends/alps_bsru6.c | 721 --------------------- linux/drivers/media/dvb/frontends/stv0299.c | 827 +++++++++++++++++++++++++ 4 files changed, 835 insertions(+), 725 deletions(-) delete mode 100644 linux/drivers/media/dvb/frontends/alps_bsru6.c create mode 100644 linux/drivers/media/dvb/frontends/stv0299.c diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 77be2e7a3..e9c68eb93 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -1,13 +1,17 @@ comment "Supported Frontend Modules" depends on DVB -config DVB_ALPS_BSRU6 - tristate "Alps BSRU6 (QPSK)" +config DVB_STV0299 + tristate "STV0299 based DVB-S frontend (QPSK)" depends on DVB_CORE help A DVB-S tuner module. - Say Y when you want to support this frontend. + Say Y when you want to support frontend based on this + demodulator. + + Some examples are the Alps BSRU6, the Philips SU1278 and + the LG TDQB-S00x. If you don't know what tuner module is soldered on your DVB adapter simply enable all supported frontends, the diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 9e9a35a00..d14464a20 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -4,7 +4,7 @@ EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -obj-$(CONFIG_DVB_ALPS_BSRU6) += alps_bsru6.o +obj-$(CONFIG_DVB_STV0299) += stv0299.o obj-$(CONFIG_DVB_ALPS_BSRV2) += alps_bsrv2.o obj-$(CONFIG_DVB_ALPS_TDLB7) += alps_tdlb7.o obj-$(CONFIG_DVB_ALPS_TDMB7) += alps_tdmb7.o diff --git a/linux/drivers/media/dvb/frontends/alps_bsru6.c b/linux/drivers/media/dvb/frontends/alps_bsru6.c deleted file mode 100644 index b5efa09ff..000000000 --- a/linux/drivers/media/dvb/frontends/alps_bsru6.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - Alps BSRU6 and LG TDQB-S00x DVB QPSK frontend driver - - Copyright (C) 2001-2002 Convergence Integrated Media GmbH - , , - - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include - -#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: "stv0299 based (e.g. Alps BSRU6 or LG TDQB-S00x)", - 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_QPSK | - FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO | - FE_CAN_CLEAN_SETUP -}; - - -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, 0x40, // DiSEqC off - 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, 0x00, - 0x18, 0x14, - 0x19, 0xf2, - - 0x1a, 0x11, - - 0x1b, 0x9c, - 0x1c, 0x00, - 0x1d, 0x00, - 0x1e, 0x0b, - 0x1f, 0x50, - - 0x20, 0x00, - 0x21, 0x00, - 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, - freq > 1530000 ? 0xc0 : 0xc4 }; - - 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 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__); - - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) - return -ETIMEDOUT; - - val = stv0299_readreg (i2c, 0x08); - - if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ - return -EREMOTEIO; - - for (i=0; imsg_len; i++) { - if (stv0299_wait_diseqc_fifo (i2c, 100) < 0) - return -ETIMEDOUT; - - if (stv0299_writereg (i2c, 0x09, m->msg[i])) - return -EREMOTEIO; - } - - if (stv0299_wait_diseqc_idle (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__); - - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) - return -ETIMEDOUT; - - val = stv0299_readreg (i2c, 0x08); - - 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_idle (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__); - - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) - return -ETIMEDOUT; - - 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; - - 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_FEC (i2c, p->u.qpsk.fec_inner); - stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate); - stv0299_check_inversion (i2c); - 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) -{ - u8 id = stv0299_readreg (i2c, 0x00); - - dprintk ("%s\n", __FUNCTION__); - - if (id != 0xa1 && id != 0x80) - 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("Alps BSRU6/LG TDQB-S00x DVB Frontend driver"); -MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); -MODULE_LICENSE("GPL"); - diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c new file mode 100644 index 000000000..ef1ceeebf --- /dev/null +++ b/linux/drivers/media/dvb/frontends/stv0299.c @@ -0,0 +1,827 @@ +/* + Universal driver for STV0299/TDA5059 based + DVB QPSK frontends + + Alps BSRU6, LG TDQB-S00x + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + , + , + + + Philips SU1278/SH + + Copyright (C) 2002 by Peter Schildmann + + + 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 +#include + +#include "compat.h" +#include "dvb_frontend.h" + +static int debug = 0; +#define dprintk if (debug) printk + +/* frontend types */ +#define UNKNOWN_FRONTEND -1 +#define PHILIPS_SU1278SH 0 +#define ALPS_BSRU6 1 + +/* Master Clock = 88 MHz */ +#define M_CLK (88000000UL) + +static +struct dvb_frontend_info uni0299_info = { + name: "STV0299/TSA5059 based", + 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_QPSK | + FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO | + FE_CAN_CLEAN_SETUP +}; + + +static +u8 init_tab [] = { + /* clock registers */ + 0x01, 0x15, /* K = 0, DIRCLK = 0, M = 0x15 */ + 0x02, 0x30, /* STDBY = 0, VCO = 0 (ON), SERCLK = 0, P = 0 */ + /* f_VCO = 4MHz * 4 * (M+1) / (K+1) = 352 MHz */ + 0x03, 0x00, /* auxiliary clock not used */ + 0x04, 0x7d, /* F22FR = 0x7d */ + /* F22 = f_VCO / 128 / 0x7d = 22 kHz */ + + /* I2C bus repeater */ + 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ + + /* general purpose DAC registers */ + 0x06, 0x40, /* DAC not used, set to high impendance mode */ + 0x07, 0x00, /* DAC LSB */ + + /* DiSEqC registers */ + 0x08, 0x40, /* DiSEqC off */ + 0x09, 0x00, /* FIFO */ + + /* Input/Output configuration register */ + 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ + /* OP0 ctl = Normal, OP0 val = 1 (18 V) */ + /* Nyquist filter = 00, QPSK reverse = 0 */ + + /* AGC1 control register */ + 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ + + /* Timing loop register */ + 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ + + 0x10, 0x3f, // AGC2 0x3d + 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, /* AGC1 integrator value */ + 0x17, 0x00, + 0x18, 0x14, + 0x19, 0xf2, + + 0x1a, 0x11, + + 0x1b, 0x9c, + 0x1c, 0x00, + 0x1d, 0x00, + 0x1e, 0x0b, + 0x1f, 0x50, + + 0x20, 0x00, + 0x21, 0x00, + 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 ftype) +{ + int ret; + u8 rpt1 [] = { 0x05, 0xb5 }; /* enable i2c repeater on stv0299 */ + /* TSA5059 i2c-bus address */ + u8 addr = (ftype == PHILIPS_SU1278SH) ? 0x60 : 0x61; + struct i2c_msg msg [] = {{ addr: 0x68, flags: 0, buf: rpt1, len: 2 }, + { addr: addr, 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, int ftype) +{ + u32 div = freq / 125; + + u8 buf[4] = { (div >> 8) & 0x7f, div & 0xff, 0x84 }; + + if (ftype == PHILIPS_SU1278SH) + /* activate f_xtal/f_comp signal output */ + /* charge pump current C0/C1 = 00 */ + buf[3] = 0x20; + else + buf[3] = freq > 1530000 ? 0xc0 : 0xc4; + + dprintk ("%s\n", __FUNCTION__); + + return tsa5059_write (i2c, buf, ftype); +} + + +#if 0 +static +int tsa5059_read_status (struct dvb_i2c_bus *i2c) +{ + int ret; + u8 rpt1 [] = { 0x05, 0xb5 }; + u8 stat [] = { 0 }; + + struct i2c_msg msg [] = {{ addr: 0x68, flags: 0, buf: rpt1, len: 2 }, + { addr: 0x60, flags: I2C_M_RD, buf: stat, 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 stat[0]; +} +#endif + + +static +int stv0299_init (struct dvb_i2c_bus *i2c, int ftype) +{ + int i; + + dprintk("stv0299: init chip\n"); + + for (i=0; i 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__); + + if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + return -ETIMEDOUT; + + val = stv0299_readreg (i2c, 0x08); + + if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ + return -EREMOTEIO; + + for (i=0; imsg_len; i++) { + if (stv0299_wait_diseqc_fifo (i2c, 100) < 0) + return -ETIMEDOUT; + + if (stv0299_writereg (i2c, 0x09, m->msg[i])) + return -EREMOTEIO; + } + + if (stv0299_wait_diseqc_idle (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__); + + if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + return -ETIMEDOUT; + + val = stv0299_readreg (i2c, 0x08); + + 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_idle (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__); + + if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + return -ETIMEDOUT; + + 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; /* LNB power on */ + + 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; + + 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 uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +{ + int tuner_type = (int)fe->data; + struct dvb_i2c_bus *i2c = fe->i2c; + + dprintk ("%s\n", __FUNCTION__); + + switch (cmd) { + case FE_GET_INFO: + memcpy (arg, &uni0299_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); + + dprintk ("VSTATUS: 0x%02x\n", sync); + + *status = 0; + + if (signal > 10) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x80) + *status |= FE_HAS_CARRIER; + + if (sync & 0x10) + *status |= FE_HAS_VITERBI; + + if (sync & 0x08) + *status |= FE_HAS_SYNC; + + if ((sync & 0x98) == 0x98) + *status |= FE_HAS_LOCK; + + 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)); + + dprintk ("AGC2I: 0x%02x%02x, signal=0x%04x\n", + stv0299_readreg (i2c, 0x18), + stv0299_readreg (i2c, 0x19), signal); + + 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, tuner_type); + stv0299_set_FEC (i2c, p->u.qpsk.fec_inner); + stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate); + stv0299_check_inversion (i2c); + /* tsa5059_set_tv_freq (i2c, p->frequency); */ + stv0299_writereg (i2c, 0x22, 0x00); + stv0299_writereg (i2c, 0x23, 0x00); + stv0299_readreg (i2c, 0x23); + stv0299_writereg (i2c, 0x12, 0xb9); + + /* printk ("%s: tsa5059 status: %x\n", __FUNCTION__, tsa5059_read_status(i2c)); */ + 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, tuner_type); + + 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 probe_tuner (struct dvb_i2c_bus *i2c) +{ + int type; + + /* read the status register of TSA5059 */ + u8 rpt[] = { 0x05, 0xb5 }; + u8 stat [] = { 0 }; + struct i2c_msg msg1 [] = {{ addr: 0x68, flags: 0, buf: rpt, len: 2 }, + { addr: 0x60, flags: I2C_M_RD, buf: stat, len: 1 }}; + struct i2c_msg msg2 [] = {{ addr: 0x68, flags: 0, buf: rpt, len: 2 }, + { addr: 0x61, flags: I2C_M_RD, buf: stat, len: 1 }}; + + if (i2c->xfer(i2c, msg1, 2) == 2) { + type = PHILIPS_SU1278SH; + printk ("%s: setup for tuner SU1278/SH\n", __FILE__); + } else if (i2c->xfer(i2c, msg2, 2) == 2) { + type = ALPS_BSRU6; + printk ("%s: setup for tuner BSRU6, TDQB-S00x\n", __FILE__); + } else { + type = UNKNOWN_FRONTEND; + printk ("%s: unknown PLL synthesizer, " + "please report to !!\n", + __FILE__); + } + return type; +} + +static +int uni0299_attach (struct dvb_i2c_bus *i2c) +{ + int tuner_type; + u8 id = stv0299_readreg (i2c, 0x00); + + dprintk ("%s\n", __FUNCTION__); + + /* register 0x00 contains 0xa1 for STV0299 and STV0299B */ + /* register 0x00 might contain 0x80 when returning from standby */ + if (id != 0xa1) + return -ENODEV; + + if ((tuner_type = probe_tuner(i2c)) < 0) + return -ENODEV; + + dvb_register_frontend (uni0299_ioctl, i2c, (void*) tuner_type, + &uni0299_info); + + return 0; +} + + +static +void uni0299_detach (struct dvb_i2c_bus *i2c) +{ + dprintk ("%s\n", __FUNCTION__); + + dvb_unregister_frontend (uni0299_ioctl, i2c); +} + + +static +int __init init_uni0299 (void) +{ + dprintk ("%s\n", __FUNCTION__); + + return dvb_register_i2c_device (THIS_MODULE, uni0299_attach, uni0299_detach); +} + + +static +void __exit exit_uni0299 (void) +{ + dprintk ("%s\n", __FUNCTION__); + + dvb_unregister_i2c_device (uni0299_attach); +} + +module_init (init_uni0299); +module_exit (exit_uni0299); + +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug, "enable verbose debug messages"); + +MODULE_DESCRIPTION("Universal STV0299/TSA5059 DVB Frontend driver"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann"); +MODULE_LICENSE("GPL"); + -- cgit v1.2.3