Commit e1bd9bfd authored by Michael Hunold's avatar Michael Hunold Committed by Linus Torvalds

[PATCH] Misc. fixes for ALPS TDLB7 DVB frontend driver

 - applied latest changes by Juergen Peitz (great work!)
  o as a workaround for the lockup problem the data valid signal is
    checked after every channel switch.  If it is not set FEC parameters
    are set again.
  o disabled autoprobing if FEC settings are known (from sp887x). 
  o added support for FE_READ_UNCORRECTED_BLOCKS (from sp887x).
  o added support for FE_SLEEP (from sp887x).
  o bit error rate is now not only read from register 0xC07 but also
    from 0xC08 (from sp887x). 
  o I2C feedthrough to the tuner is now only enabled when needed (from
    sp887x).
  o Added FE_CAN_QAM_AUTO and FE_CAN_HIERARCHY_AUTO to
    dvb_frontend_info.
  o Removed obsolete setting of default frontend parameters in
    sp8870_init.
  o Removed obsolete module parameter 'loadcode' because changes in the
    saa7146 driver made firmware loading very fast.
parent 7bd39161
/* /*
Driver for Alps TDLB7 Frontend Driver for Alps TDLB7 Frontend
Copyright (C) 1999 Juergen Peitz <peitz@snafu.de> Copyright (C) 1999 Juergen Peitz
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -22,23 +22,9 @@ ...@@ -22,23 +22,9 @@
/* /*
This driver needs a copy of the firmware file 'Sc_main.mc' from the Haupauge
Wrote this code mainly to get my own card running. It's working for me, but I windows driver in the '/usr/lib/DVB/driver/frontends' directory.
hope somebody who knows more about linux programming and the DVB driver can You can also pass the complete file name with the module parameter 'firmware_file'.
improve it.
Reused a lot from the existing driver and tuner code.
Thanks to everybody who worked on it!
This driver needs a copy of the microcode file 'Sc_main.mc' from the Haupauge
windows driver in the 'usr/lib/DVB/driver/frontends' directory.
You can also pass the complete file name with the module parameter 'mcfile'.
The code only needs to be loaded once after a power on. Because loading the
microcode to the card takes some time, you can use the 'loadcode=0' module
parameter, if you only want to reload the dvb driver.
Juergen Peitz
*/ */
...@@ -50,49 +36,46 @@ ...@@ -50,49 +36,46 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/delay.h>
#include "dvb_frontend.h" #include "dvb_frontend.h"
#include "dvb_functions.h"
static int debug = 0; #ifndef CONFIG_ALPS_TDLB7_FIRMWARE_LOCATION
#define CONFIG_ALPS_TDLB7_FIRMWARE_LOCATION "/usr/lib/DVB/driver/frontends/Sc_main.mc"
static int loadcode = 1; #endif
static char * mcfile = "/usr/lib/DVB/driver/frontends/Sc_main.mc"; static char * firmware_file = CONFIG_ALPS_TDLB7_FIRMWARE_LOCATION;
static int debug = 0;
#define dprintk if (debug) printk #define dprintk if (debug) printk
/* microcode size for sp8870 */ /* firmware size for sp8870 */
#define SP8870_CODE_SIZE 16382 #define SP8870_FIRMWARE_SIZE 16382
/* starting point for microcode in file 'Sc_main.mc' */ /* starting point for firmware in file 'Sc_main.mc' */
#define SP8870_CODE_OFFSET 0x0A #define SP8870_FIRMWARE_OFFSET 0x0A
static int errno; static int errno;
static struct dvb_frontend_info tdlb7_info = { static struct dvb_frontend_info tdlb7_info = {
.name = "Alps TDLB7", name: "Alps TDLB7",
.type = FE_OFDM, type: FE_OFDM,
.frequency_min = 470000000, frequency_min: 470000000,
.frequency_max = 860000000, frequency_max: 860000000,
.frequency_stepsize = 166666, frequency_stepsize: 166666,
#if 0 caps: FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
.frequency_tolerance = ???,
.symbol_rate_min = ???,
.symbol_rate_max = ???,
.symbol_rate_tolerance = ???,
.notifier_delay = 0,
#endif
.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_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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER
}; };
static int sp8870_writereg (struct dvb_i2c_bus *i2c, u16 reg, u16 data) static int sp8870_writereg (struct dvb_i2c_bus *i2c, u16 reg, u16 data)
{ {
u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff };
struct i2c_msg msg = { .addr = 0x71, .flags = 0, .buf = buf, .len = 4 }; struct i2c_msg msg = { addr: 0x71, flags: 0, buf: buf, len: 4 };
int err; int err;
if ((err = i2c->xfer (i2c, &msg, 1)) != 1) { if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
...@@ -109,13 +92,15 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg) ...@@ -109,13 +92,15 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg)
int ret; int ret;
u8 b0 [] = { reg >> 8 , reg & 0xff }; u8 b0 [] = { reg >> 8 , reg & 0xff };
u8 b1 [] = { 0, 0 }; u8 b1 [] = { 0, 0 };
struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 }, struct i2c_msg msg [] = { { addr: 0x71, flags: 0, buf: b0, len: 2 },
{ .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; { addr: 0x71, flags: I2C_M_RD, buf: b1, len: 2 } };
ret = i2c->xfer (i2c, msg, 2); ret = i2c->xfer (i2c, msg, 2);
if (ret != 2) if (ret != 2) {
dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret);
return -1;
}
return (b1[0] << 8 | b1[1]); return (b1[0] << 8 | b1[1]);
} }
...@@ -124,7 +109,7 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg) ...@@ -124,7 +109,7 @@ static u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg)
static int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4]) static int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
{ {
int ret; int ret;
struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = 4 }; struct i2c_msg msg = { addr: 0x60, flags: 0, buf: data, len: 4 };
ret = i2c->xfer (i2c, &msg, 1); ret = i2c->xfer (i2c, &msg, 1);
...@@ -135,7 +120,7 @@ static int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4]) ...@@ -135,7 +120,7 @@ static int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
} }
static int sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq) static void sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
{ {
u32 div = (freq + 36200000) / 166666; u32 div = (freq + 36200000) / 166666;
u8 buf [4]; u8 buf [4];
...@@ -151,102 +136,146 @@ static int sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq) ...@@ -151,102 +136,146 @@ static int sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
buf[2] = 0x85; buf[2] = 0x85;
buf[3] = pwr << 6; buf[3] = pwr << 6;
return sp5659_write (i2c, buf); /* open i2c gate for PLL message transmission... */
sp8870_writereg(i2c, 0x206, 0x001);
sp5659_write (i2c, buf);
sp8870_writereg(i2c, 0x206, 0x000);
} }
static int sp8870_read_code(const char *fn, char **fp) static int sp8870_read_firmware_file (const char *fn, char **fp)
{ {
int fd; int fd;
loff_t l; loff_t filesize;
char *dp; char *dp;
fd = open(fn, 0, 0); fd = open(fn, 0, 0);
if (fd == -1) { if (fd == -1) {
printk(KERN_INFO "%s: Unable to load '%s'.\n", __FUNCTION__, fn); printk("%s: unable to open '%s'.\n", __FUNCTION__, fn);
return -1; return -EIO;
} }
l = lseek(fd, 0L, 2);
if (l <= 0 || l < SP8870_CODE_OFFSET+SP8870_CODE_SIZE) { filesize = lseek(fd, 0L, 2);
printk(KERN_INFO "%s: code file too small '%s'\n", __FUNCTION__, fn); if (filesize <= 0 || filesize < SP8870_FIRMWARE_OFFSET + SP8870_FIRMWARE_SIZE) {
printk("%s: firmware filesize to small '%s'\n", __FUNCTION__, fn);
sys_close(fd); sys_close(fd);
return -1; return -EIO;
} }
lseek(fd, SP8870_CODE_OFFSET, 0);
*fp= dp = vmalloc(SP8870_CODE_SIZE); *fp= dp = vmalloc(SP8870_FIRMWARE_SIZE);
if (dp == NULL) { if (dp == NULL) {
printk(KERN_INFO "%s: Out of memory loading '%s'.\n", __FUNCTION__, fn); printk("%s: out of memory loading '%s'.\n", __FUNCTION__, fn);
sys_close(fd); sys_close(fd);
return -1; return -EIO;
} }
if (read(fd, dp, SP8870_CODE_SIZE) != SP8870_CODE_SIZE) {
printk(KERN_INFO "%s: Failed to read '%s'.\n",__FUNCTION__, fn); lseek(fd, SP8870_FIRMWARE_OFFSET, 0);
if (read(fd, dp, SP8870_FIRMWARE_SIZE) != SP8870_FIRMWARE_SIZE) {
printk("%s: failed to read '%s'.\n",__FUNCTION__, fn);
vfree(dp); vfree(dp);
sys_close(fd); sys_close(fd);
return -1; return -EIO;
} }
sys_close(fd); sys_close(fd);
*fp = dp; *fp = dp;
return 0; return 0;
} }
static int sp8870_load_code(struct dvb_i2c_bus *i2c) static int sp8870_firmware_upload (struct dvb_i2c_bus *i2c)
{ {
/* this takes a long time. is there a way to do it faster? */
char *lcode;
struct i2c_msg msg; struct i2c_msg msg;
unsigned char buf[255]; char *fw_buf = NULL;
int err; int fw_pos;
int p=0; u8 tx_buf[255];
int c; int tx_len;
int err = 0;
mm_segment_t fs = get_fs(); mm_segment_t fs = get_fs();
dprintk ("%s: ...\n", __FUNCTION__);
// system controller stop // system controller stop
sp8870_writereg(i2c,0x0F00,0x0000); sp8870_writereg(i2c,0x0F00,0x0000);
// instruction RAM register hiword // instruction RAM register hiword
sp8870_writereg(i2c,0x8F08,((SP8870_CODE_SIZE/2) & 0xFFFF)); sp8870_writereg(i2c, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF));
// instruction RAM MWR // instruction RAM MWR
sp8870_writereg(i2c,0x8F0A,((SP8870_CODE_SIZE/2) >> 16)); sp8870_writereg(i2c, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16));
// reading firmware file to buffer
set_fs(get_ds()); set_fs(get_ds());
if (sp8870_read_code(mcfile,(char**) &lcode)<0) return -1; err = sp8870_read_firmware_file(firmware_file, (char**) &fw_buf);
set_fs(fs); set_fs(fs);
while (p<SP8870_CODE_SIZE){ if (err != 0) {
c = (p<=SP8870_CODE_SIZE-252) ? 252 : SP8870_CODE_SIZE-p; printk("%s: reading firmware file failed!\n", __FUNCTION__);
buf[0]=0xCF; return err;
buf[1]=0x0A; }
memcpy(&buf[2],lcode+p,c);
c+=2; // do firmware upload
fw_pos = 0;
while (fw_pos < SP8870_FIRMWARE_SIZE){
tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE - 252) ? 252 : SP8870_FIRMWARE_SIZE - fw_pos;
// write register 0xCF0A
tx_buf[0] = 0xCF;
tx_buf[1] = 0x0A;
memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len);
msg.addr=0x71; msg.addr=0x71;
msg.flags=0; msg.flags=0;
msg.buf=buf; msg.buf = tx_buf;
msg.len=c; msg.len = tx_len + 2;
if ((err = i2c->xfer (i2c, &msg, 1)) != 1) { if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
dprintk ("%s: i2c error (err == %i)\n", printk("%s: firmware upload failed!\n", __FUNCTION__);
__FUNCTION__, err); printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
vfree(lcode); vfree(fw_buf);
return -EREMOTEIO; return err;
} }
fw_pos += tx_len;
p+=252;
} }
vfree(lcode);
vfree(fw_buf);
dprintk ("%s: done!\n", __FUNCTION__);
return 0; return 0;
}; };
static int sp8870_init (struct dvb_i2c_bus *i2c) static void sp8870_microcontroller_stop (struct dvb_i2c_bus *i2c)
{ {
sp8870_writereg(i2c, 0x0F08, 0x000);
sp8870_writereg(i2c, 0x0F09, 0x000);
// microcontroller STOP
sp8870_writereg(i2c, 0x0F00, 0x000);
}
static void sp8870_microcontroller_start (struct dvb_i2c_bus *i2c)
{
sp8870_writereg(i2c, 0x0F08, 0x000);
sp8870_writereg(i2c, 0x0F09, 0x000);
// microcontroller START
sp8870_writereg(i2c, 0x0F00, 0x001);
// not documented but if we don't read 0x0D01 out here
// we don't get a correct data valid signal
sp8870_readreg(i2c, 0x0D01);
}
static int sp8870_init (struct dvb_i2c_bus *i2c)
{
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
/* enable TS output and interface pins */
sp8870_writereg(i2c, 0xc18, 0x00d);
// system controller stop // system controller stop
sp8870_writereg(i2c,0x0F00,0x0000); sp8870_microcontroller_stop(i2c);
// ADC mode: 2 for MT8872, 3 for MT8870/8871 // ADC mode
sp8870_writereg(i2c,0x0301,0x0003); sp8870_writereg(i2c,0x0301,0x0003);
// Reed Solomon parity bytes passed to output // Reed Solomon parity bytes passed to output
...@@ -255,103 +284,214 @@ static int sp8870_init (struct dvb_i2c_bus *i2c) ...@@ -255,103 +284,214 @@ static int sp8870_init (struct dvb_i2c_bus *i2c)
// MPEG clock is suppressed if no valid data // MPEG clock is suppressed if no valid data
sp8870_writereg(i2c,0x0C14,0x0001); sp8870_writereg(i2c,0x0C14,0x0001);
// sample rate correction bit [23..17] /* bit 0x010: enable data valid signal */
sp8870_writereg(i2c,0x0319,0x000A); sp8870_writereg(i2c, 0x0D00, 0x010);
sp8870_writereg(i2c, 0x0D01, 0x000);
// sample rate correction bit [16..0] return 0;
sp8870_writereg(i2c,0x031A,0x0AAB); }
// integer carrier offset
sp8870_writereg(i2c,0x0309,0x0400);
// fractional carrier offset static int sp8870_read_status (struct dvb_i2c_bus *i2c, fe_status_t * fe_status)
sp8870_writereg(i2c,0x030A,0x0000); {
int status;
int signal;
*fe_status = 0;
status = sp8870_readreg (i2c, 0x0200);
if (status < 0)
return -EIO;
// filter for 8 Mhz channel signal = sp8870_readreg (i2c, 0x0303);
sp8870_writereg(i2c,0x0311,0x0000); if (signal < 0)
return -EIO;
// scan order: 2k first = 0x0000, 8k first = 0x0001 if (signal > 0x0F)
sp8870_writereg(i2c,0x0338,0x0000); *fe_status |= FE_HAS_SIGNAL;
if (status & 0x08)
*fe_status |= FE_HAS_SYNC;
if (status & 0x04)
*fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI;
return 0; return 0;
} }
static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) static int sp8870_read_ber (struct dvb_i2c_bus *i2c, u32 * ber)
{ {
struct dvb_i2c_bus *i2c = fe->i2c; int ret;
u32 tmp;
switch (cmd) { *ber = 0;
case FE_GET_INFO:
memcpy (arg, &tdlb7_info, sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS: ret = sp8870_readreg(i2c, 0xC08);
{ if (ret < 0)
fe_status_t *status = arg; return -EIO;
int sync = sp8870_readreg (i2c, 0x0200);
int signal = 0xff-sp8870_readreg (i2c, 0x303);
*status=0; tmp = ret & 0x3F;
if (signal>10) // FIXME: is 10 the right value ?
*status |= FE_HAS_SIGNAL;
if (sync&0x04) // FIXME: find criteria ret = sp8870_readreg(i2c, 0xC07);
*status |= FE_HAS_CARRIER; if (ret < 0)
return -EIO;
if (sync&0x04) // FIXME tmp = ret << 6;
*status |= FE_HAS_VITERBI;
if (sync&0x08) // FIXME if (tmp >= 0x3FFF0)
*status |= FE_HAS_SYNC; tmp = ~0;
if (sync&0x04) *ber = tmp;
*status |= FE_HAS_LOCK;
break;
return 0;
} }
case FE_READ_BER:
static int sp8870_read_signal_strength (struct dvb_i2c_bus *i2c, u16 * signal)
{ {
u32 *ber=(u32 *) arg; int ret;
// bit error rate before Viterbi u16 tmp;
*ber=sp8870_readreg(i2c,0x0C07);
break;
} *signal = 0;
case FE_READ_SIGNAL_STRENGTH: // FIXME: correct registers ? ret = sp8870_readreg (i2c, 0x306);
{ if (ret < 0)
*((u16*) arg) = 0xffff-((sp8870_readreg (i2c, 0x306) << 8) | sp8870_readreg (i2c, 0x303)); return -EIO;
break;
tmp = ret << 8;
ret = sp8870_readreg (i2c, 0x303);
if (ret < 0)
return -EIO;
tmp |= ret;
if (tmp)
*signal = 0xFFFF - tmp;
return 0;
} }
case FE_READ_SNR: // not supported by hardware?
static int sp8870_read_snr(struct dvb_i2c_bus *i2c, u32* snr)
{ {
s32 *snr=(s32 *) arg;
*snr=0; *snr=0;
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
case FE_READ_UNCORRECTED_BLOCKS: // not supported by hardware?
static int sp8870_read_uncorrected_blocks (struct dvb_i2c_bus *i2c, u32* ublocks)
{ {
u32 *ublocks=(u32 *) arg; int ret;
*ublocks=0; *ublocks=0;
return -EOPNOTSUPP;
ret = sp8870_readreg(i2c, 0xC0C);
if (ret < 0)
return -EIO;
if (ret == 0xFFFF)
ret = ~0;
*ublocks = ret;
return 0;
} }
case FE_SET_FRONTEND:
static int sp8870_read_data_valid_signal(struct dvb_i2c_bus *i2c)
{
return (sp8870_readreg(i2c, 0x0D02) > 0);
}
static
int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05)
{
int known_parameters = 1;
*reg0xc05 = 0x000;
switch (p->u.ofdm.constellation) {
case QPSK:
break;
case QAM_16:
*reg0xc05 |= (1 << 10);
break;
case QAM_64:
*reg0xc05 |= (2 << 10);
break;
case QAM_AUTO:
known_parameters = 0;
break;
default:
return -EINVAL;
};
switch (p->u.ofdm.hierarchy_information) {
case HIERARCHY_NONE:
break;
case HIERARCHY_1:
*reg0xc05 |= (1 << 7);
break;
case HIERARCHY_2:
*reg0xc05 |= (2 << 7);
break;
case HIERARCHY_4:
*reg0xc05 |= (3 << 7);
break;
case HIERARCHY_AUTO:
known_parameters = 0;
break;
default:
return -EINVAL;
};
switch (p->u.ofdm.code_rate_HP) {
case FEC_1_2:
break;
case FEC_2_3:
*reg0xc05 |= (1 << 3);
break;
case FEC_3_4:
*reg0xc05 |= (2 << 3);
break;
case FEC_5_6:
*reg0xc05 |= (3 << 3);
break;
case FEC_7_8:
*reg0xc05 |= (4 << 3);
break;
case FEC_AUTO:
known_parameters = 0;
break;
default:
return -EINVAL;
};
if (known_parameters)
*reg0xc05 |= (2 << 1); /* use specified parameters */
else
*reg0xc05 |= (1 << 1); /* enable autoprobing */
return 0;
}
static int sp8870_set_frontend_parameters (struct dvb_i2c_bus *i2c,
struct dvb_frontend_parameters *p)
{ {
struct dvb_frontend_parameters *p = arg; int err;
u16 reg0xc05;
if ((err = configure_reg0xc05(p, &reg0xc05)))
return err;
// system controller stop // system controller stop
sp8870_writereg(i2c,0x0F00,0x0000); sp8870_microcontroller_stop(i2c);
// set tuner parameters
sp5659_set_tv_freq (i2c, p->frequency); sp5659_set_tv_freq (i2c, p->frequency);
// read status reg in order to clear pending irqs
sp8870_readreg(i2c, 0x200);
// sample rate correction bit [23..17] // sample rate correction bit [23..17]
sp8870_writereg(i2c,0x0319,0x000A); sp8870_writereg(i2c,0x0319,0x000A);
...@@ -378,28 +518,141 @@ static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) ...@@ -378,28 +518,141 @@ static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
else else
sp8870_writereg(i2c,0x0338,0x0001); sp8870_writereg(i2c,0x0338,0x0001);
// instruction RAM register loword sp8870_writereg(i2c, 0xc05, reg0xc05);
sp8870_writereg(i2c,0x0F09,0x0000);
// instruction RAM register hiword // read status reg in order to clear pending irqs
sp8870_writereg(i2c,0x0F08,0x0000); sp8870_readreg(i2c, 0x200);
// system controller start // system controller start
sp8870_writereg(i2c,0x0F00,0x0001); sp8870_microcontroller_start(i2c);
break; return 0;
} }
case FE_GET_FRONTEND: // FIXME: read known values back from Hardware...
// number of trials to recover from lockup
#define MAXTRIALS 5
// maximum checks for data valid signal
#define MAXCHECKS 100
// only for debugging: counter for detected lockups
static int lockups = 0;
// only for debugging: counter for channel switches
static int switches = 0;
static int sp8870_set_frontend (struct dvb_i2c_bus *i2c, struct dvb_frontend_parameters *p)
{ {
/*
The firmware of the sp8870 sometimes locks up after setting frontend parameters.
We try to detect this by checking the data valid signal.
If it is not set after MAXCHECKS we try to recover the lockup by setting
the frontend parameters again.
*/
int err = 0;
int valid = 0;
int trials = 0;
int check_count = 0;
dprintk("%s: frequency = %i\n", __FUNCTION__, p->frequency);
for (trials = 1; trials <= MAXTRIALS; trials++) {
if ((err = sp8870_set_frontend_parameters(i2c, p)))
return err;
for (check_count = 0; check_count < MAXCHECKS; check_count++) {
// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0);
valid = sp8870_read_data_valid_signal(i2c);
if (valid) {
dprintk("%s: delay = %i usec\n",
__FUNCTION__, check_count * 10);
break;
}
udelay(10);
}
if (valid)
break; break;
} }
case FE_SLEEP: // is this supported by hardware? if (!valid) {
printk("%s: firmware crash!!!!!!\n", __FUNCTION__);
return -EIO;
}
if (debug) {
if (valid) {
if (trials > 1) {
printk("%s: firmware lockup!!!\n", __FUNCTION__);
printk("%s: recovered after %i trial(s))\n", __FUNCTION__, trials - 1);
lockups++;
}
}
switches++;
printk("%s: switches = %i lockups = %i\n", __FUNCTION__, switches, lockups);
}
return 0;
}
static int sp8870_sleep(struct dvb_i2c_bus *i2c)
{
// tristate TS output and disable interface pins
return sp8870_writereg(i2c, 0xC18, 0x000);
}
static int sp8870_wake_up(struct dvb_i2c_bus *i2c)
{
// enable TS output and interface pins
return sp8870_writereg(i2c, 0xC18, 0x00D);
}
static int tdlb7_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, &tdlb7_info, sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
return sp8870_read_status(i2c, (fe_status_t *) arg);
case FE_READ_BER:
return sp8870_read_ber(i2c, (u32 *) arg);
case FE_READ_SIGNAL_STRENGTH:
return sp8870_read_signal_strength(i2c, (u16 *) arg);
case FE_READ_SNR: // not supported by hardware?
return sp8870_read_snr(i2c, (u32 *) arg);
case FE_READ_UNCORRECTED_BLOCKS:
return sp8870_read_uncorrected_blocks(i2c, (u32 *) arg);
case FE_SET_FRONTEND:
return sp8870_set_frontend(i2c, (struct dvb_frontend_parameters*) arg);
case FE_RESET:
return -EOPNOTSUPP; return -EOPNOTSUPP;
case FE_GET_FRONTEND: // FIXME: read known values back from Hardware...
return -EOPNOTSUPP;
case FE_SLEEP:
return sp8870_sleep(i2c);
case FE_INIT: case FE_INIT:
return sp8870_init (i2c); sp8870_wake_up(i2c);
if (fe->data == NULL) { // first time initialisation...
fe->data = (void*) ~0;
sp8870_init (i2c);
}
break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
...@@ -409,31 +662,22 @@ static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) ...@@ -409,31 +662,22 @@ static int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
} }
static int tdlb7_attach (struct dvb_i2c_bus *i2c) static int tdlb7_attach (struct dvb_i2c_bus *i2c, void **data)
{ {
struct i2c_msg msg = { addr: 0x71, flags: 0, buf: NULL, len: 0 };
struct i2c_msg msg = { .addr = 0x71, .flags = 0, .buf = NULL, .len = 0 };
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
if (i2c->xfer (i2c, &msg, 1) != 1) if (i2c->xfer (i2c, &msg, 1) != 1)
return -ENODEV; return -ENODEV;
if (loadcode) { sp8870_firmware_upload(i2c);
dprintk("%s: loading mcfile '%s' !\n", __FUNCTION__, mcfile);
if (sp8870_load_code(i2c)==0)
dprintk("%s: microcode loaded!\n", __FUNCTION__);
}else{
dprintk("%s: without loading mcfile!\n", __FUNCTION__);
}
dvb_register_frontend (tdlb7_ioctl, i2c, NULL, &tdlb7_info);
return 0; return dvb_register_frontend (tdlb7_ioctl, i2c, NULL, &tdlb7_info);
} }
static void tdlb7_detach (struct dvb_i2c_bus *i2c) static void tdlb7_detach (struct dvb_i2c_bus *i2c, void *data)
{ {
dprintk ("%s\n", __FUNCTION__); dprintk ("%s\n", __FUNCTION__);
...@@ -464,11 +708,8 @@ module_exit(exit_tdlb7); ...@@ -464,11 +708,8 @@ module_exit(exit_tdlb7);
MODULE_PARM(debug,"i"); MODULE_PARM(debug,"i");
MODULE_PARM_DESC(debug, "enable verbose debug messages"); MODULE_PARM_DESC(debug, "enable verbose debug messages");
MODULE_PARM(loadcode,"i"); MODULE_PARM(firmware_file,"s");
MODULE_PARM_DESC(loadcode, "load tuner microcode"); MODULE_PARM_DESC(firmware_file, "where to find the firmware file");
MODULE_PARM(mcfile,"s");
MODULE_PARM_DESC(mcfile, "where to find the microcode file");
MODULE_DESCRIPTION("TDLB7 DVB-T Frontend"); MODULE_DESCRIPTION("TDLB7 DVB-T Frontend");
MODULE_AUTHOR("Juergen Peitz"); MODULE_AUTHOR("Juergen Peitz");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment