summaryrefslogtreecommitdiff
path: root/linux/drivers/media
diff options
context:
space:
mode:
authorDaniel Mack <devnull@localhost>2004-03-05 16:43:49 +0000
committerDaniel Mack <devnull@localhost>2004-03-05 16:43:49 +0000
commitfe21cde9d8804004378d9bcb2e6945aebdb39a29 (patch)
treee1c2271e512c1b15b65022ed4f6dfca053af3d2f /linux/drivers/media
parent82b2d6930fd7bb41b71617ca5eba804605af5513 (diff)
downloadmediapointer-dvb-s2-fe21cde9d8804004378d9bcb2e6945aebdb39a29.tar.gz
mediapointer-dvb-s2-fe21cde9d8804004378d9bcb2e6945aebdb39a29.tar.bz2
Added a driver for Zarlink's MT352 DVB-T demodulator.
Not all ioctls are fully supported yet.
Diffstat (limited to 'linux/drivers/media')
-rw-r--r--linux/drivers/media/dvb/frontends/Makefile2
-rw-r--r--linux/drivers/media/dvb/frontends/mt352.c400
2 files changed, 402 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile
index e536b0de7..2c537b606 100644
--- a/linux/drivers/media/dvb/frontends/Makefile
+++ b/linux/drivers/media/dvb/frontends/Makefile
@@ -18,3 +18,5 @@ obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o
obj-$(CONFIG_DVB_SP887X) += sp887x.o
obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
+obj-$(CONFIG_DVB_MT352) += mt352.o
+
diff --git a/linux/drivers/media/dvb/frontends/mt352.c b/linux/drivers/media/dvb/frontends/mt352.c
new file mode 100644
index 000000000..ab1e3a1b7
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/mt352.c
@@ -0,0 +1,400 @@
+/*
+ * Driver for Zarlink DVB-T MT352 demodulator
+ *
+ * Written by Holger Waechtler <holger@qanu.de>
+ * and Daniel Mack <daniel@qanu.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include "dvb_frontend.h"
+
+#define I2C_MT352_ADDR 0x0f
+#define I2C_TUNER_ADDR 0xc2
+
+#define mt352_write(ibuf, ilen) \
+do { \
+ struct i2c_msg msg = { .addr = I2C_MT352_ADDR, .flags = 0, \
+ .buf = ibuf, .len = ilen }; \
+ int err = i2c->xfer(i2c, &msg, 1); \
+ if (err != 1) { \
+ printk(KERN_WARNING "mt352_write() failed (err = %d)!\n", err); \
+ return err; \
+ } \
+} while (0)
+
+
+#define msb(x) (((x) >> 8) & 0xff)
+#define lsb(x) ((x) & 0xff)
+
+static struct dvb_frontend_info mt352_info = {
+ .name = "DVB-T Zarlink MT352 demodulator driver",
+ .type = FE_OFDM,
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 166667,
+/* .frequency_tolerance = ,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .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_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_CLEAN_SETUP |
+ FE_CAN_MUTE_TS
+};
+
+int mt352_init (struct dvb_i2c_bus *i2c)
+{
+ /**
+ * all register write sequence have the register address of the
+ * first register in the first byte, thenafter the value to write
+ * into this and the following registers.
+ */
+ static unsigned char mt352_reset [] = { 0x50, 0x80 };
+ static unsigned char mt352_clock_config [] = { 0x89, 0x38, 0x2d };
+ static unsigned char mt352_adc_ctl_1_cfg [] = { 0x8e, 0x40 };
+ static unsigned char mt352_agc_cfg [] = { 0x67, 0x19, 0xa0 };
+ static unsigned char mt352_acq_ctl [] = { 0x53, 0x50 };
+
+ /**
+ * We only write non-default settings, all default settings are
+ * restored by the full mt352_reset sequence.
+ */
+ mt352_write(mt352_clock_config, sizeof(mt352_clock_config));
+ udelay(2000);
+ mt352_write(mt352_reset, sizeof(mt352_reset));
+ mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
+ mt352_write(mt352_acq_ctl, sizeof(mt352_acq_ctl));
+
+ /**
+ * The optimal AGC target value and slope might vary from tuner
+ * type to tuner type, so check whether you need to adjust this one...
+ */
+ mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg));
+
+ return 0;
+}
+
+
+int mt352_set_parameters (struct dvb_i2c_bus *i2c,
+ struct dvb_frontend_parameters *param)
+{
+ unsigned char buf[14];
+ unsigned int tps = 0;
+ struct dvb_ofdm_parameters *op = &param->u.ofdm;
+ u32 freq = param->frequency / 1000000;
+
+ switch (op->code_rate_HP) {
+ case FEC_2_3:
+ tps |= (1 << 9);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 9);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 9);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 9);
+ break;
+ case FEC_AUTO:
+ break;
+ default:
+ ;
+ }
+
+ switch (op->code_rate_LP) {
+ case FEC_2_3:
+ tps |= (1 << 6);
+ break;
+ case FEC_3_4:
+ tps |= (2 << 6);
+ break;
+ case FEC_5_6:
+ tps |= (3 << 6);
+ break;
+ case FEC_7_8:
+ tps |= (4 << 6);
+ break;
+ case FEC_AUTO:
+ break;
+ default:
+ ;
+ }
+
+ switch (op->constellation) {
+ case QAM_16:
+ tps |= (1 << 14);
+ break;
+ case QAM_64:
+ tps |= (2 << 14);
+ break;
+ default:
+ ;
+ }
+
+ switch (op->transmission_mode) {
+ case TRANSMISSION_MODE_8K:
+ tps |= (1 << 1);
+ break;
+ default:
+ ;
+ }
+
+ switch (op->guard_interval) {
+ case GUARD_INTERVAL_1_16:
+ tps |= (1 << 3);
+ break;
+ case GUARD_INTERVAL_1_8:
+ tps |= (2 << 3);
+ break;
+ case GUARD_INTERVAL_1_4:
+ tps |= (3 << 3);
+ break;
+ default:
+ ;
+ }
+
+ switch (op->hierarchy_information) {
+ case HIERARCHY_1:
+ tps |= (1 << 12);
+ break;
+ case HIERARCHY_2:
+ tps |= (2 << 12);
+ break;
+ case HIERARCHY_4:
+ tps |= (3 << 12);
+ break;
+ default:
+ ;
+ }
+
+
+ buf[0] = 0x51; /* TPS_GIVEN_1 and following registers */
+
+ buf[1] = msb(tps); /* TPS_GIVEN_(1|0) */
+ buf[2] = lsb(tps);
+
+// buf[3] = 0xf3; /* ACQ_CTL, force parameters, automatic spectral inv */
+ buf[3] = 0x50; /* ACQ_CTL, fully automatic parameter search */
+
+ /**
+ * these settings assume 20.48MHz f_ADC, for other tuners you might
+ * need other values. See p. 33 in the MT352 Design Manual.
+ */
+ if (op->bandwidth == BANDWIDTH_8_MHZ) {
+ buf[4] = 0x72; /* TRL_NOMINAL_RATE_(1|0) */
+ buf[5] = 0x49;
+ } else if (op->bandwidth == BANDWIDTH_7_MHZ) {
+ buf[4] = 0x64;
+ buf[5] = 0x00;
+ } else { /* 6MHz */
+ buf[4] = 0x55;
+ buf[5] = 0xb7;
+ }
+
+ buf[6] = 0x31; /* INPUT_FREQ_(1|0), 20.48MHz clock, 36.166667MHz IF */
+ buf[7] = 0x05; /* see MT352 Design Manual page 32 for details */
+
+ buf[8] = I2C_TUNER_ADDR;
+
+ /**
+ * All the following settings are tuner module dependent,
+ * check the datasheet...
+ */
+ {
+ /* here we assume 1/6MHz == 166.66kHz stepsize */
+ #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */
+ uint16_t tmp = 6 * freq + IF_FREQUENCYx6;
+ buf[9] = msb(tmp); /* CHAN_START_(1|0) */
+ buf[10] = lsb(tmp);
+ }
+
+ printk (KERN_WARNING "buf9,10: %02x %02x\n", buf[9], buf[10]);
+
+ if (freq < 542)
+ buf[11] = 0xbe; /* CONT_1, charge pump byte */
+ else if (freq < 830)
+ buf[11] = 0xf6;
+ else
+ buf[11] = 0xfe;
+
+ if (freq < 250) /* VHF, freq < 250MHz */
+ buf[12] = 0x01; /* CONT_0, bandswitch byte */
+ else
+ buf[12] = 0x08;
+
+ buf[13] = 0x01; /* TUNER_GO!! */
+
+ mt352_write(buf, sizeof(buf));
+
+ return 0;
+}
+
+static u8 mt352_read_register (struct dvb_i2c_bus *i2c, u8 reg)
+{
+ int ret;
+ u8 b0 [] = { reg };
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg [] = { { .addr = I2C_MT352_ADDR, .flags = I2C_M_NOSTART, .buf = b0, .len = 1 },
+ { .addr = I2C_MT352_ADDR, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
+
+ ret = i2c->xfer (i2c, msg, 2);
+
+ if (ret != 2)
+ printk(KERN_WARNING "%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
+
+ return b1[0];
+}
+
+static int mt352_dump (struct dvb_i2c_bus *i2c) {
+ int i;
+
+ for (i=0x50; i<=0x7f; i++)
+ printk ("mt352[%03x]: %02x\n", i, mt352_read_register (i2c, i));
+
+ return 0;
+}
+
+static int mt352_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
+{
+ struct dvb_i2c_bus *i2c = fe->i2c;
+ int i;
+
+ switch (cmd) {
+ case FE_GET_INFO:
+ memcpy (arg, &mt352_info, sizeof(struct dvb_frontend_info));
+ break;
+
+ case FE_READ_STATUS:
+ {
+ u8 r;
+ fe_status_t *status = arg;
+ *status = 0;
+
+ r = mt352_read_register (i2c, 0x0);
+ if (r & (1 << 5))
+ *status |= FE_HAS_LOCK;
+ if (r & (1 << 4))
+ *status |= FE_HAS_CARRIER;
+ if (r & (1 << 1))
+ *status |= FE_HAS_VITERBI;
+
+ r = mt352_read_register (i2c, 0x1);
+ if (r & (1 << 1))
+ *status |= FE_HAS_SYNC;
+
+ r = mt352_read_register (i2c, 0x3);
+ if (r & (1 << 6))
+ *status |= FE_HAS_SIGNAL;
+
+ break;
+ }
+
+ case FE_READ_BER:
+ {
+ u32 *ber = (u32 *) arg;
+ *ber = (mt352_read_register (i2c, 0xA) << 16) |
+ (mt352_read_register (i2c, 0xB) << 8) |
+ (mt352_read_register (i2c, 0xC));
+ break;
+ }
+
+ case FE_READ_SIGNAL_STRENGTH:
+ {
+ u16 signal = (mt352_read_register (i2c, 0x12) << 8) |
+ (mt352_read_register (i2c, 0x13));
+ *((u16*) arg) = ~signal;
+ break;
+ }
+
+ case FE_READ_SNR:
+ {
+ u8 snr = mt352_read_register (i2c, 0x9);
+ *(u16*) arg = (snr << 8) | snr;
+ break;
+ }
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ *(u32*) arg = (mt352_read_register (i2c, 0x10) << 8) |
+ (mt352_read_register (i2c, 0x11));
+ break;
+
+ case FE_SET_FRONTEND:
+ i = mt352_set_parameters (i2c, (struct dvb_frontend_parameters *) arg);
+ mt352_dump(i2c);
+ return i;
+
+ case FE_GET_FRONTEND:
+ break;
+
+ case FE_SLEEP:
+ return 0;
+
+ case FE_INIT:
+ return mt352_init(i2c);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
+
+static int mt352_attach (struct dvb_i2c_bus *i2c, void **data)
+{
+ if (mt352_read_register (i2c, 0x7f) == 0x13)
+ return dvb_register_frontend (mt352_ioctl, i2c, NULL, &mt352_info);
+
+ return -ENODEV;
+}
+
+
+static void mt352_detach (struct dvb_i2c_bus *i2c, void *data)
+{
+ dvb_unregister_frontend (mt352_ioctl, i2c);
+}
+
+
+static int __init init_mt352 (void)
+{
+ return dvb_register_i2c_device (NULL,
+ mt352_attach,
+ mt352_detach);
+}
+
+
+static void __exit exit_mt352 (void)
+{
+ dvb_unregister_i2c_device (mt352_attach);
+}
+
+
+module_init(init_mt352);
+module_exit(exit_mt352);
+
+
+MODULE_DESCRIPTION("DVB-T MT352 Zarlink");
+MODULE_AUTHOR("Holniel");
+MODULE_LICENSE("GPL");