summaryrefslogtreecommitdiff
path: root/linux/drivers/media/dvb/frontends/ves1820.c
diff options
context:
space:
mode:
authorHolger Waechtler <devnull@localhost>2002-10-16 16:52:27 +0000
committerHolger Waechtler <devnull@localhost>2002-10-16 16:52:27 +0000
commit4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7 (patch)
tree23af98b0e1da37f97558e63953011e23d1e360a5 /linux/drivers/media/dvb/frontends/ves1820.c
downloadmediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.gz
mediapointer-dvb-s2-4cdfc7177bcafe782e27dfa468b0aa5d33cc81b7.tar.bz2
the 2.5 tree
Diffstat (limited to 'linux/drivers/media/dvb/frontends/ves1820.c')
-rw-r--r--linux/drivers/media/dvb/frontends/ves1820.c537
1 files changed, 537 insertions, 0 deletions
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");
+