diff options
author | Daniel Mack <devnull@localhost> | 2004-03-05 16:43:49 +0000 |
---|---|---|
committer | Daniel Mack <devnull@localhost> | 2004-03-05 16:43:49 +0000 |
commit | fe21cde9d8804004378d9bcb2e6945aebdb39a29 (patch) | |
tree | e1c2271e512c1b15b65022ed4f6dfca053af3d2f /linux/drivers/media | |
parent | 82b2d6930fd7bb41b71617ca5eba804605af5513 (diff) | |
download | mediapointer-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/Makefile | 2 | ||||
-rw-r--r-- | linux/drivers/media/dvb/frontends/mt352.c | 400 |
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 = ¶m->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"); |