Commit 7d208bb2 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

[PATCH] update the dvb front end chips

(Again all DVB is Martin Hunold)
parent 28003225
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
......@@ -31,17 +35,29 @@ config DVB_ALPS_TDLB7
This tuner module needs some microcode located in a file called
"Sc_main.mc" in the windows driver. Please pass the module parameter
mcfile="/PATH/FILENAME" when loading alps_tdlb7.
mcfile="/PATH/FILENAME" when loading alps_tdlb7.o.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
right one will get autodetected.
config DVB_ALPS_TDMB7
tristate "Alps BSRV2 (OFDM)"
tristate "Alps TDMB7 (OFDM)"
depends on DVB_CORE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
A DVB-T tuner module. Say Y when you want to support this frontend.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
right one will get autodetected.
config DVB_ATMEL_AT76C651
tristate "Atmel AT76C651 (QAM)"
depends on DVB_CORE
help
The AT76C651 Demodulator is used in some DVB-C SetTopBoxes. Say Y
when you see this demodulator chip near your tuner module.
If you don't know what tuner module is soldered on your
DVB adapter simply enable all supported frontends, the
......
......@@ -4,10 +4,11 @@
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
obj-$(CONFIG_DVB_ATMEL_AT76C651) += at76c651.o
obj-$(CONFIG_DVB_GRUNDIG_29504_491) += grundig_29504-491.o
obj-$(CONFIG_DVB_GRUNDIG_29504_401) += grundig_29504-401.o
obj-$(CONFIG_DVB_VES1820) += ves1820.o
......@@ -23,7 +23,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
......@@ -32,19 +31,19 @@ static int debug = 0;
static
struct dvb_frontend_info bsrv2_info = {
.name = "Alps BSRV2",
.type = FE_QPSK,
.frequency_min = 950000,
.frequency_max = 2150000,
.frequency_stepsize = 250, /* kHz for QPSK frontends */
.frequency_tolerance = 29500,
.symbol_rate_min = 1000000,
.symbol_rate_max = 45000000,
.notifier_delay = 50, /* 1/20 s */
.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 |
name: "Alps BSRV2",
type: FE_QPSK,
frequency_min: 950000,
frequency_max: 2150000,
frequency_stepsize: 250, /* kHz for QPSK frontends */
frequency_tolerance: 29500,
symbol_rate_min: 1000000,
symbol_rate_max: 45000000,
/* symbol_rate_tolerance: ???,*/
notifier_delay: 50, /* 1/20 s */
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
};
......@@ -73,10 +72,10 @@ u8 init_1893_wtab[] =
static
int ves1893_writereg (struct dvb_i2c_bus *i2c, int reg, int data)
int ves1893_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
u8 buf [] = { 0x00, reg, data };
struct i2c_msg msg = { .addr = 0x08, .flags = 0, .buf = buf, .len = 3 };
struct i2c_msg msg = { addr: 0x08, flags: 0, buf: buf, len: 3 };
int err;
if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
......@@ -94,8 +93,8 @@ u8 ves1893_readreg (struct dvb_i2c_bus *i2c, u8 reg)
int ret;
u8 b0 [] = { 0x00, reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { .addr = 0x08, .flags = 0, .buf = b0, .len = 2 },
{ .addr = 0x08, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
struct i2c_msg msg [] = { { addr: 0x08, flags: 0, buf: b0, len: 2 },
{ addr: 0x08, flags: I2C_M_RD, buf: b1, len: 1 } };
ret = i2c->xfer (i2c, msg, 2);
......@@ -110,7 +109,7 @@ static
int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = 4 };
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
ret = i2c->xfer (i2c, &msg, 1);
......@@ -297,7 +296,7 @@ int ves1893_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
return ves1893_writereg (i2c, 0x1f, 0x30);
default:
return -EINVAL;
};
}
}
......@@ -314,7 +313,7 @@ int bsrv2_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_READ_STATUS:
{
fe_status_t *status = arg;
int sync = ves1893_readreg (i2c, 0x0e);
u8 sync = ves1893_readreg (i2c, 0x0e);
*status = 0;
......
......@@ -51,7 +51,6 @@
#include <linux/fs.h>
#include <linux/unistd.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
......@@ -63,7 +62,7 @@ static char * mcfile = "/usr/lib/DVB/driver/frontends/Sc_main.mc";
#define dprintk if (debug) printk
/* microcode size for sp8870 */
#define SP8870_CODE_SIZE 16384
#define SP8870_CODE_SIZE 16382
/* starting point for microcode in file 'Sc_main.mc' */
#define SP8870_CODE_OFFSET 0x0A
......@@ -73,14 +72,20 @@ static int errno;
static
struct dvb_frontend_info tdlb7_info = {
.name = "Alps TDLB7",
.type = FE_OFDM,
.frequency_min = 470000000,
.frequency_max = 860000000,
.frequency_stepsize = 166666,
.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 |
name: "Alps TDLB7",
type: FE_OFDM,
frequency_min: 470000000,
frequency_max: 860000000,
frequency_stepsize: 166666,
#if 0
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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64
};
......@@ -89,7 +94,7 @@ static
int sp8870_writereg (struct dvb_i2c_bus *i2c, u16 reg, u16 data)
{
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;
if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
......@@ -107,8 +112,8 @@ u16 sp8870_readreg (struct dvb_i2c_bus *i2c, u16 reg)
int ret;
u8 b0 [] = { reg >> 8 , reg & 0xff };
u8 b1 [] = { 0, 0 };
struct i2c_msg msg [] = { { .addr = 0x71, .flags = 0, .buf = b0, .len = 2 },
{ .addr = 0x71, .flags = I2C_M_RD, .buf = b1, .len = 2 } };
struct i2c_msg msg [] = { { addr: 0x71, flags: 0, buf: b0, len: 2 },
{ addr: 0x71, flags: I2C_M_RD, buf: b1, len: 2 } };
ret = i2c->xfer (i2c, msg, 2);
......@@ -123,7 +128,7 @@ static
int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
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);
......@@ -135,10 +140,21 @@ int sp5659_write (struct dvb_i2c_bus *i2c, u8 data [4])
static
int sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
int sp5659_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
{
u32 div = (freq + 36200000) / 166666;
u8 buf [4] = { (div >> 8) & 0x7f, div & 0xff, 0x85, (pwr << 5) | 0x30 };
u8 buf [4];
int pwr;
if (freq <= 782000000)
pwr = 1;
else
pwr = 2;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = 0x85;
buf[3] = pwr << 6;
return sp5659_write (i2c, buf);
}
......@@ -193,8 +209,14 @@ int sp8870_load_code(struct dvb_i2c_bus *i2c)
int c;
mm_segment_t fs = get_fs();
sp8870_writereg(i2c,0x8F08,0x1FFF);
sp8870_writereg(i2c,0x8F0A,0x0000);
// system controller stop
sp8870_writereg(i2c,0x0F00,0x0000);
// instruction RAM register hiword
sp8870_writereg(i2c,0x8F08,((SP8870_CODE_SIZE/2) & 0xFFFF));
// instruction RAM MWR
sp8870_writereg(i2c,0x8F0A,((SP8870_CODE_SIZE/2) >> 16));
set_fs(get_ds());
if (sp8870_read_code(mcfile,(char**) &lcode)<0) return -1;
......@@ -210,7 +232,8 @@ int sp8870_load_code(struct dvb_i2c_bus *i2c)
msg.buf=buf;
msg.len=c;
if ((err = i2c->xfer (i2c, &msg, 1)) != 1) {
dprintk ("%s: i2c error (err == %i)\n", __FUNCTION__, err);
dprintk ("%s: i2c error (err == %i)\n",
__FUNCTION__, err);
vfree(lcode);
return -EREMOTEIO;
}
......@@ -228,49 +251,40 @@ int sp8870_init (struct dvb_i2c_bus *i2c)
dprintk ("%s\n", __FUNCTION__);
sp8870_readreg(i2c,0x200);
sp8870_readreg(i2c,0x200);
sp8870_readreg(i2c,0x0F00); /* system controller stop */
sp8870_readreg(i2c,0x0301); /* ???????? */
sp8870_readreg(i2c,0x0309); /* integer carrier offset */
sp8870_readreg(i2c,0x030A); /* fractional carrier offset */
sp8870_readreg(i2c,0x0311); /* filter for 8 Mhz channel */
sp8870_readreg(i2c,0x0319); /* sample rate correction bit [23..17] */
sp8870_readreg(i2c,0x031A); /* sample rate correction bit [16..0] */
sp8870_readreg(i2c,0x0338); /* ???????? */
sp8870_readreg(i2c,0x0F00);
sp8870_readreg(i2c,0x0200);
// system controller stop
sp8870_writereg(i2c,0x0F00,0x0000);
if (loadcode) {
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__);
}
// ADC mode: 2 for MT8872, 3 for MT8870/8871
sp8870_writereg(i2c,0x0301,0x0003);
return 0;
}
// Reed Solomon parity bytes passed to output
sp8870_writereg(i2c,0x0C13,0x0001);
// MPEG clock is suppressed if no valid data
sp8870_writereg(i2c,0x0C14,0x0001);
static
int sp8870_reset (struct dvb_i2c_bus *i2c)
{
dprintk("%s\n", __FUNCTION__);
sp8870_writereg(i2c,0x0F00,0x0000); /* system controller stop */
sp8870_writereg(i2c,0x0301,0x0003); /* ???????? */
sp8870_writereg(i2c,0x0309,0x0400); /* integer carrier offset */
sp8870_writereg(i2c,0x030A,0x0000); /* fractional carrier offset */
sp8870_writereg(i2c,0x0311,0x0000); /* filter for 8 Mhz channel */
sp8870_writereg(i2c,0x0319,0x000A); /* sample rate correction bit [23..17] */
sp8870_writereg(i2c,0x031A,0x0AAB); /* sample rate correction bit [16..0] */
sp8870_writereg(i2c,0x0338,0x0000); /* ???????? */
sp8870_writereg(i2c,0x0201,0x0000); /* interrupts for change of lock or tuner adjustment disabled */
sp8870_writereg(i2c,0x0F00,0x0001); /* system controller start */
// sample rate correction bit [23..17]
sp8870_writereg(i2c,0x0319,0x000A);
// sample rate correction bit [16..0]
sp8870_writereg(i2c,0x031A,0x0AAB);
// integer carrier offset
sp8870_writereg(i2c,0x0309,0x0400);
// fractional carrier offset
sp8870_writereg(i2c,0x030A,0x0000);
// filter for 8 Mhz channel
sp8870_writereg(i2c,0x0311,0x0000);
// scan order: 2k first = 0x0000, 8k first = 0x0001
sp8870_writereg(i2c,0x0338,0x0000);
return 0;
}
static
int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
......@@ -285,10 +299,10 @@ int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
fe_status_t *status = arg;
int sync = sp8870_readreg (i2c, 0x0200);
int signal = 0xff-sp8870_readreg (i2c, 0x303);
*status=0;
if (sync&0x04) // FIXME: find criteria for having signal
if (signal>10) // FIXME: is 10 the right value ?
*status |= FE_HAS_SIGNAL;
if (sync&0x04) // FIXME: find criteria
......@@ -309,15 +323,15 @@ int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_READ_BER:
{
u32 *ber=(u32 *) arg;
*ber=sp8870_readreg(i2c,0x0C07); // bit error rate before Viterbi
// bit error rate before Viterbi
*ber=sp8870_readreg(i2c,0x0C07);
break;
}
case FE_READ_SIGNAL_STRENGTH: // not supported by hardware?
case FE_READ_SIGNAL_STRENGTH: // FIXME: correct registers ?
{
s32 *signal=(s32 *) arg;
*signal=0;
*((u16*) arg) = 0xffff-((sp8870_readreg (i2c, 0x306) << 8) | sp8870_readreg (i2c, 0x303));
break;
}
......@@ -325,27 +339,64 @@ int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
s32 *snr=(s32 *) arg;
*snr=0;
break;
return -EOPNOTSUPP;
}
case FE_READ_UNCORRECTED_BLOCKS: // not supported by hardware?
{
u32 *ublocks=(u32 *) arg;
*ublocks=0;
break;
return -EOPNOTSUPP;
}
case FE_SET_FRONTEND:
{
struct dvb_frontend_parameters *p = arg;
sp5659_set_tv_freq (i2c, p->frequency, 0);
// all other parameters are set by the on card
// system controller. Don't know how to pass
// distinct values to the card.
// system controller stop
sp8870_writereg(i2c,0x0F00,0x0000);
sp5659_set_tv_freq (i2c, p->frequency);
// sample rate correction bit [23..17]
sp8870_writereg(i2c,0x0319,0x000A);
// sample rate correction bit [16..0]
sp8870_writereg(i2c,0x031A,0x0AAB);
// integer carrier offset
sp8870_writereg(i2c,0x0309,0x0400);
// fractional carrier offset
sp8870_writereg(i2c,0x030A,0x0000);
// filter for 6/7/8 Mhz channel
if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
sp8870_writereg(i2c,0x0311,0x0002);
else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
sp8870_writereg(i2c,0x0311,0x0001);
else
sp8870_writereg(i2c,0x0311,0x0000);
// scan order: 2k first = 0x0000, 8k first = 0x0001
if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K)
sp8870_writereg(i2c,0x0338,0x0000);
else
sp8870_writereg(i2c,0x0338,0x0001);
// instruction RAM register loword
sp8870_writereg(i2c,0x0F09,0x0000);
// instruction RAM register hiword
sp8870_writereg(i2c,0x0F08,0x0000);
// system controller start
sp8870_writereg(i2c,0x0F00,0x0001);
break;
}
case FE_GET_FRONTEND: // how to do this?
case FE_GET_FRONTEND: // FIXME: read known values back from Hardware...
{
break;
}
......@@ -356,9 +407,6 @@ int tdlb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_INIT:
return sp8870_init (i2c);
case FE_RESET:
return sp8870_reset (i2c);
default:
return -EOPNOTSUPP;
};
......@@ -371,13 +419,21 @@ static
int tdlb7_attach (struct dvb_i2c_bus *i2c)
{
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__);
if (i2c->xfer (i2c, &msg, 1) != 1)
return -ENODEV;
if (loadcode) {
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;
......
......@@ -23,7 +23,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include "compat.h"
#include "dvb_frontend.h"
......@@ -33,15 +32,22 @@ static int debug = 0;
static
struct dvb_frontend_info tdmb7_info = {
.name = "Alps TDMB7",
.type = FE_OFDM,
.frequency_min = 470000000,
.frequency_max = 860000000,
.frequency_stepsize = 166667,
.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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64
name: "Alps TDMB7",
type: FE_OFDM,
frequency_min: 470000000,
frequency_max: 860000000,
frequency_stepsize: 166667,
#if 0
frequency_tolerance: ???,
symbol_rate_min: ???,
symbol_rate_max: ???,
symbol_rate_tolerance: 500, /* ppm */
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_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
FE_CAN_CLEAN_SETUP | FE_CAN_RECOVER
};
......@@ -81,7 +87,7 @@ int cx22700_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
u8 buf [] = { reg, data };
struct i2c_msg msg = { .addr = 0x43, .flags = 0, .buf = buf, .len = 2 };
struct i2c_msg msg = { addr: 0x43, flags: 0, buf: buf, len: 2 };
dprintk ("%s\n", __FUNCTION__);
......@@ -101,8 +107,8 @@ u8 cx22700_readreg (struct dvb_i2c_bus *i2c, u8 reg)
int ret;
u8 b0 [] = { reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { .addr = 0x43, .flags = 0, .buf = b0, .len = 1 },
{ .addr = 0x43, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
struct i2c_msg msg [] = { { addr: 0x43, flags: 0, buf: b0, len: 1 },
{ addr: 0x43, flags: I2C_M_RD, buf: b1, len: 1 } };
dprintk ("%s\n", __FUNCTION__);
......@@ -118,7 +124,7 @@ u8 cx22700_readreg (struct dvb_i2c_bus *i2c, u8 reg)
static
int pll_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = 4 };
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
int ret;
cx22700_writereg (i2c, 0x0a, 0x00); /* open i2c bus switch */
......@@ -350,6 +356,7 @@ int tdmb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
case FE_READ_BER:
*((uint32_t*) arg) = cx22700_readreg (i2c, 0x0c) & 0x7f;
cx22700_writereg (i2c, 0x0c, 0x00);
break;
case FE_READ_SIGNAL_STRENGTH:
......@@ -368,6 +375,7 @@ int tdmb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
}
case FE_READ_UNCORRECTED_BLOCKS:
*((uint32_t*) arg) = cx22700_readreg (i2c, 0x0f);
cx22700_writereg (i2c, 0x0f, 0x00);
break;
case FE_SET_FRONTEND:
......@@ -412,7 +420,7 @@ int tdmb7_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
static
int tdmb7_attach (struct dvb_i2c_bus *i2c)
{
struct i2c_msg msg = { .addr = 0x43, .flags = 0, .buf = NULL, .len = 0 };
struct i2c_msg msg = { addr: 0x43, flags: 0, buf: NULL, len: 0 };
dprintk ("%s\n", __FUNCTION__);
......
/*
* at76c651.c
*
* Atmel DVB-C Frontend Driver (at76c651/dat7021)
*
* Copyright (C) 2001 fnbrd <fnbrd@gmx.de>
* & 2002 Andreas Oberritter <andreas@oberritter.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/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#if defined(__powerpc__)
#include <asm/bitops.h>
#endif
#include "dvb_frontend.h"
#include "dvb_i2c.h"
static int debug = 0;
#define dprintk if (debug) printk
/*
* DAT7021
* -------
* Input Frequency Range (RF): 48.25 MHz to 863.25 MHz
* Band Width: 8 MHz
* Level Input (Range for Digital Signals): -61 dBm to -41 dBm
* Output Frequency (IF): 36 MHz
*
* (see http://www.atmel.com/atmel/acrobat/doc1320.pdf)
*/
static struct dvb_frontend_info at76c651_info = {
.name = "Atmel AT76c651(B) with DAT7021",
.type = FE_QAM,
.frequency_min = 48250000,
.frequency_max = 863250000,
.frequency_stepsize = 62500,
/*.frequency_tolerance = */ /* FIXME: 12% of SR */
.symbol_rate_min = 0, /* FIXME */
.symbol_rate_max = 9360000, /* FIXME */
.symbol_rate_tolerance = 4000,
.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_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 |
FE_CAN_QAM_256,
/* FE_CAN_QAM_512 | FE_CAN_QAM_1024 | */
};
#if ! defined(__powerpc__)
static __inline__ int
__ilog2(unsigned long x)
{
int i;
if (x == 0)
return -1;
for (i = 0; x != 0; i++)
x >>= 1;
return i - 1;
}
#endif
static int
at76c651_writereg(struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
u8 buf[] = { reg, data };
struct i2c_msg msg = { addr:0x1a >> 1, flags:0, buf:buf, len:2 };
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
at76c651_readreg(struct dvb_i2c_bus *i2c, u8 reg)
{
int ret;
u8 b0[] = { reg };
u8 b1[] = { 0 };
struct i2c_msg msg[] = { {addr: 0x1a >> 1, flags: 0, buf: b0, len:1},
{addr: 0x1a >> 1, 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
at76c651_set_auto_config(struct dvb_i2c_bus *i2c)
{
at76c651_writereg(i2c, 0x06, 0x01);
/*
* performance optimizations
*/
at76c651_writereg(i2c, 0x10, 0x06);
at76c651_writereg(i2c, 0x11, 0x10);
at76c651_writereg(i2c, 0x15, 0x28);
at76c651_writereg(i2c, 0x20, 0x09);
at76c651_writereg(i2c, 0x24, 0x90);
return 0;
}
static int
at76c651_set_bbfreq(struct dvb_i2c_bus *i2c)
{
at76c651_writereg(i2c, 0x04, 0x3f);
at76c651_writereg(i2c, 0x05, 0xee);
return 0;
}
static int
at76c651_reset(struct dvb_i2c_bus *i2c)
{
return at76c651_writereg(i2c, 0x07, 0x01);
}
static int
at76c651_disable_interrupts(struct dvb_i2c_bus *i2c)
{
return at76c651_writereg(i2c, 0x0b, 0x00);
}
static int
at76c651_switch_tuner_i2c(struct dvb_i2c_bus *i2c, u8 enable)
{
if (enable)
return at76c651_writereg(i2c, 0x0c, 0xc2 | 0x01);
else
return at76c651_writereg(i2c, 0x0c, 0xc2);
}
static int
dat7021_write(struct dvb_i2c_bus *i2c, u32 tw)
{
int ret;
struct i2c_msg msg =
{ addr:0xc2 >> 1, flags:0, buf:(u8 *) & tw, len:sizeof (tw) };
at76c651_switch_tuner_i2c(i2c, 1);
ret = i2c->xfer(i2c, &msg, 1);
at76c651_switch_tuner_i2c(i2c, 0);
if (ret != 4)
return -EFAULT;
at76c651_reset(i2c);
return 0;
}
static int
dat7021_set_tv_freq(struct dvb_i2c_bus *i2c, u32 freq)
{
u32 dw;
freq /= 1000;
if ((freq < 48250) || (freq > 863250))
return -EINVAL;
/*
* formula: dw=0x17e28e06+(freq-346000UL)/8000UL*0x800000
* or: dw=0x4E28E06+(freq-42000) / 125 * 0x20000
*/
dw = (freq - 42000) * 4096;
dw = dw / 125;
dw = dw * 32;
if (freq > 394000)
dw += 0x4E28E85;
else
dw += 0x4E28E06;
return dat7021_write(i2c, dw);
}
static int
at76c651_set_symbolrate(struct dvb_i2c_bus *i2c, u32 symbolrate)
{
u8 exponent;
u32 mantissa;
if (symbolrate > 9360000)
return -1;
/*
* FREF = 57800 kHz
* exponent = 10 + floor ( log2 ( symbolrate / FREF ) )
* mantissa = ( symbolrate / FREF) * ( 1 << ( 30 - exponent ) )
*/
exponent = __ilog2((symbolrate << 4) / 903125);
mantissa = ((symbolrate / 3125) * (1 << (24 - exponent))) / 289;
at76c651_writereg(i2c, 0x00, mantissa >> 13);
at76c651_writereg(i2c, 0x01, mantissa >> 5);
at76c651_writereg(i2c, 0x02, (mantissa << 3) | exponent);
return 0;
}
static int
at76c651_set_qam(struct dvb_i2c_bus *i2c, fe_modulation_t qam)
{
u8 qamsel = 0;
switch (qam) {
case QPSK:
qamsel = 0x02;
break;
case QAM_16:
qamsel = 0x04;
break;
case QAM_32:
qamsel = 0x05;
break;
case QAM_64:
qamsel = 0x06;
break;
case QAM_128:
qamsel = 0x07;
break;
case QAM_256:
qamsel = 0x08;
break;
#if 0
case QAM_512:
qamsel = 0x09;
break;
case QAM_1024:
qamsel = 0x0A;
break;
#endif
default:
return -EINVAL;
}
return at76c651_writereg(i2c, 0x03, qamsel);
}
static int
at76c651_set_inversion(struct dvb_i2c_bus *i2c,
fe_spectral_inversion_t inversion)
{
u8 feciqinv = at76c651_readreg(i2c, 0x60);
switch (inversion) {
case INVERSION_OFF:
feciqinv |= 0x02;
feciqinv &= 0xFE;
break;
case INVERSION_ON:
feciqinv |= 0x03;
break;
case INVERSION_AUTO:
feciqinv &= 0xFC;
break;
default:
return -EINVAL;
}
return at76c651_writereg(i2c, 0x60, feciqinv);
}
static int
at76c651_set_parameters(struct dvb_i2c_bus *i2c,
struct dvb_frontend_parameters *p)
{
dat7021_set_tv_freq(i2c, p->frequency);
at76c651_set_symbolrate(i2c, p->u.qam.symbol_rate);
at76c651_set_inversion(i2c, p->inversion);
at76c651_set_auto_config(i2c);
return 0;
}
static int
at76c651_set_defaults(struct dvb_i2c_bus *i2c)
{
at76c651_set_symbolrate(i2c, 6900000);
at76c651_set_qam(i2c, QAM_64);
at76c651_set_bbfreq(i2c);
at76c651_set_auto_config(i2c);
at76c651_disable_interrupts(i2c);
return 0;
}
static int
at76c651_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
switch (cmd) {
case FE_GET_INFO:
memcpy(arg, &at76c651_info, sizeof (struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status = (fe_status_t *) arg;
u8 sync;
/*
* Bits: FEC, CAR, EQU, TIM, AGC2, AGC1, ADC, PLL (PLL=0)
*/
sync = at76c651_readreg(fe->i2c, 0x80);
*status = 0;
if (sync & (0x04 | 0x10)) /* AGC1 || TIM */
*status |= FE_HAS_SIGNAL;
if (sync & 0x10) /* TIM */
*status |= FE_HAS_CARRIER;
if (sync & 0x80) /* FEC */
*status |= FE_HAS_VITERBI;
if (sync & 0x40) /* CAR */
*status |= FE_HAS_SYNC;
if ((sync & 0xF0) == 0xF0) /* TIM && EQU && CAR && FEC */
*status |= FE_HAS_LOCK;
break;
}
case FE_READ_BER:
{
u32 *ber = (u32 *) arg;
*ber = (at76c651_readreg(fe->i2c, 0x81) & 0x0F) << 16;
*ber |= at76c651_readreg(fe->i2c, 0x82) << 8;
*ber |= at76c651_readreg(fe->i2c, 0x83);
*ber *= 10;
break;
}
case FE_READ_SIGNAL_STRENGTH:
{
u8 gain = ~at76c651_readreg(fe->i2c, 0x91);
*(s32 *) arg = (gain << 8) | gain;
*(s32 *) arg = 0x0FFF;
break;
}
case FE_READ_SNR:
*(s32 *) arg =
0xFFFF -
((at76c651_readreg(fe->i2c, 0x8F) << 8) |
at76c651_readreg(fe->i2c, 0x90));
break;
case FE_READ_UNCORRECTED_BLOCKS:
*(u32 *) arg = at76c651_readreg(fe->i2c, 0x82);
break;
case FE_SET_FRONTEND:
return at76c651_set_parameters(fe->i2c, arg);
case FE_GET_FRONTEND:
break;
case FE_SLEEP:
break;
case FE_INIT:
return at76c651_set_defaults(fe->i2c);
case FE_RESET:
return at76c651_reset(fe->i2c);
default:
return -ENOSYS;
}
return 0;
}
static int
at76c651_attach(struct dvb_i2c_bus *i2c)
{
if (at76c651_readreg(i2c, 0x0e) != 0x65) {
dprintk("no AT76C651(B) found\n");
return -ENODEV;
}
if (at76c651_readreg(i2c, 0x0F) != 0x10) {
if (at76c651_readreg(i2c, 0x0F) == 0x11) {
dprintk("AT76C651B found\n");
} else {
dprintk("no AT76C651(B) found\n");
return -ENODEV;
}
} else {
dprintk("AT76C651B found\n");
}
at76c651_set_defaults(i2c);
dvb_register_frontend(at76c651_ioctl, i2c, NULL, &at76c651_info);
return 0;
}
static void
at76c651_detach(struct dvb_i2c_bus *i2c)
{
dvb_unregister_frontend(at76c651_ioctl, i2c);
at76c651_disable_interrupts(i2c);
}
static int __init
at76c651_init(void)
{
return dvb_register_i2c_device(THIS_MODULE, at76c651_attach,
at76c651_detach);
}
static void __exit
at76c651_exit(void)
{
dvb_unregister_i2c_device(at76c651_attach);
}
module_init(at76c651_init);
module_exit(at76c651_exit);
#ifdef MODULE
MODULE_DESCRIPTION("at76c651/dat7021 dvb-c frontend driver");
MODULE_AUTHOR("Andreas Oberritter <andreas@oberritter.de>");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
MODULE_PARM(debug, "i");
#endif
/*
* Driver for Dummy Frontend
*
* Written by Emard <emard@softhome.net>
*
* 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/module.h>
#include <linux/init.h>
#include "dvb_frontend.h"
static int sct = 0;
/* depending on module parameter sct deliver different infos
*/
static
struct dvb_frontend_info dvb_s_dummyfe_info = {
name: "DVB-S dummy frontend",
type: FE_QPSK,
frequency_min: 950000,
frequency_max: 2150000,
frequency_stepsize: 250, /* kHz for QPSK frontends */
frequency_tolerance: 29500,
symbol_rate_min: 1000000,
symbol_rate_max: 45000000,
/* symbol_rate_tolerance: ???,*/
notifier_delay: 50, /* 1/20 s */
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
};
static
struct dvb_frontend_info dvb_c_dummyfe_info = {
.name = "DVB-C dummy frontend",
.type = FE_QAM,
.frequency_stepsize = 62500,
.frequency_min = 51000000,
.frequency_max = 858000000,
.symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */
.symbol_rate_max = (57840000/2)/4, /* SACLK/4 */
#if 0
frequency_tolerance: ???,
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 |
FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO |
FE_CAN_CLEAN_SETUP
};
static struct dvb_frontend_info dvb_t_dummyfe_info = {
.name = "DVB-T dummy frontend",
.type = FE_OFDM,
.frequency_min = 0,
.frequency_max = 863250000,
.frequency_stepsize = 62500,
/*.frequency_tolerance = */ /* FIXME: 12% of SR */
.symbol_rate_min = 0, /* FIXME */
.symbol_rate_max = 9360000, /* FIXME */
.symbol_rate_tolerance = 4000,
.notifier_delay = 0,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
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,
};
struct dvb_frontend_info *frontend_info(void)
{
switch(sct)
{
case 2:
return &dvb_t_dummyfe_info;
case 1:
return &dvb_c_dummyfe_info;
case 0:
default:
return &dvb_s_dummyfe_info;
}
}
static
int dvbdummyfe_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
switch (cmd) {
case FE_GET_INFO:
memcpy (arg, frontend_info(),
sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status = arg;
*status = FE_HAS_SIGNAL
| FE_HAS_CARRIER
| FE_HAS_VITERBI
| FE_HAS_SYNC
| FE_HAS_LOCK;
break;
}
case FE_READ_BER:
{
u32 *ber = (u32 *) arg;
*ber = 0;
break;
}
case FE_READ_SIGNAL_STRENGTH:
{
u8 signal = 0xff;
*((u16*) arg) = (signal << 8) | signal;
break;
}
case FE_READ_SNR:
{
u8 snr = 0xf0;
*(u16*) arg = (snr << 8) | snr;
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
*(u32*) arg = 0;
break;
case FE_SET_FRONTEND:
break;
case FE_GET_FRONTEND:
break;
case FE_SLEEP:
return 0;
case FE_INIT:
return 0;
case FE_RESET:
return 0;
case FE_SET_TONE:
return -EOPNOTSUPP;
case FE_SET_VOLTAGE:
return 0;
default:
return -EOPNOTSUPP;
}
return 0;
}
static
int dvbdummyfe_attach (struct dvb_i2c_bus *i2c)
{
dvb_register_frontend (dvbdummyfe_ioctl, i2c, NULL, frontend_info());
return 0;
}
static
void dvbdummyfe_detach (struct dvb_i2c_bus *i2c)
{
dvb_unregister_frontend (dvbdummyfe_ioctl, i2c);
}
static
int __init init_dvbdummyfe (void)
{
return dvb_register_i2c_device (THIS_MODULE,
dvbdummyfe_attach,
dvbdummyfe_detach);
return 0;
}
static
void __exit exit_dvbdummyfe (void)
{
dvb_unregister_i2c_device (dvbdummyfe_attach);
return;
}
module_init(init_dvbdummyfe);
module_exit(exit_dvbdummyfe);
MODULE_DESCRIPTION("DVB DUMMY Frontend");
MODULE_AUTHOR("Emard");
MODULE_LICENSE("GPL");
MODULE_PARM(sct, "i");
......@@ -25,7 +25,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
......@@ -34,14 +33,18 @@ static int debug = 0;
struct dvb_frontend_info grundig_29504_401_info = {
.name = "Grundig 29504-401",
.type = FE_OFDM,
.frequency_stepsize = 166666,
.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_QAM_16 | FE_CAN_QAM_64 |
FE_CAN_MUTE_TS
name: "Grundig 29504-401",
type: FE_OFDM,
/* frequency_min: ???,*/
/* frequency_max: ???,*/
frequency_stepsize: 166666,
/* frequency_tolerance: ???,*/
/* symbol_rate_tolerance: ???,*/
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_QAM_16 | FE_CAN_QAM_64 |
FE_CAN_MUTE_TS /*| FE_CAN_CLEAN_SETUP*/
};
......@@ -50,7 +53,7 @@ int l64781_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
{
int ret;
u8 buf [] = { reg, data };
struct i2c_msg msg = { .addr = 0x55, .flags = 0, .buf = buf, .len = 2 };
struct i2c_msg msg = { addr: 0x55, flags: 0, buf: buf, len: 2 };
if ((ret = i2c->xfer (i2c, &msg, 1)) != 1)
dprintk ("%s: write_reg error (reg == %02x) = %02x!\n",
......@@ -66,8 +69,8 @@ u8 l64781_readreg (struct dvb_i2c_bus *i2c, u8 reg)
int ret;
u8 b0 [] = { reg };
u8 b1 [] = { 0 };
struct i2c_msg msg [] = { { .addr = 0x55, .flags = 0, .buf = b0, .len = 1 },
{ .addr = 0x55, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
struct i2c_msg msg [] = { { addr: 0x55, flags: 0, buf: b0, len: 1 },
{ addr: 0x55, flags: I2C_M_RD, buf: b1, len: 1 } };
ret = i2c->xfer (i2c, msg, 2);
......@@ -82,7 +85,7 @@ static
int tsa5060_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = 4 };
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
if ((ret = i2c->xfer (i2c, &msg, 1)) != 1)
dprintk ("%s: write_reg error == %02x!\n", __FUNCTION__, ret);
......@@ -97,24 +100,19 @@ int tsa5060_write (struct dvb_i2c_bus *i2c, u8 data [4])
* frequency offset is 36000000 Hz.
*/
static
int tsa5060_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, u8 pwr)
int tsa5060_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq)
{
u32 div;
u8 buf [4];
u8 cfg;
if (freq < 700000000) {
div = (36000000 + freq) / 31250;
cfg = 0x86;
} else {
div = (36000000 + freq) / 166666;
cfg = 0x88;
}
buf [0] = (div >> 8) & 0x7f;
buf [1] = div & 0xff;
buf [2] = ((div >> 10) & 0x60) | cfg;
buf [3] = pwr << 6;
buf [3] = 0xc0;
return tsa5060_write (i2c, buf);
}
......@@ -175,12 +173,13 @@ int apply_frontend_param (struct dvb_i2c_bus *i2c,
u8 val0x04;
u8 val0x05;
u8 val0x06;
int bw = p->bandwidth - BANDWIDTH_8_MHZ;
if (param->inversion != INVERSION_ON &&
param->inversion != INVERSION_OFF)
return -EINVAL;
if (p->bandwidth < BANDWIDTH_8_MHZ || p->bandwidth > BANDWIDTH_6_MHZ)
if (bw < 0 || bw > 2)
return -EINVAL;
if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 &&
......@@ -230,6 +229,7 @@ int apply_frontend_param (struct dvb_i2c_bus *i2c,
val0x04 = (p->transmission_mode << 2) | p->guard_interval;
val0x05 = fec_tab[p->code_rate_HP];
if (p->hierarchy_information != HIERARCHY_NONE)
val0x05 |= (p->code_rate_LP - FEC_1_2) << 3;
......@@ -269,7 +269,7 @@ static
void reset_and_configure (struct dvb_i2c_bus *i2c)
{
u8 buf [] = { 0x06 };
struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 };
struct i2c_msg msg = { addr: 0x00, flags: 0, buf: buf, len: 1 };
i2c->xfer (i2c, &msg, 1);
}
......@@ -307,7 +307,7 @@ int init (struct dvb_i2c_bus *i2c)
/*l64781_writereg (i2c, 0x19, 0x92);*/
/* Everything is two's complement, soft bit and CSI_OUT too */
l64781_writereg (i2c, 0x1e, 0x49);
l64781_writereg (i2c, 0x1e, 0x09);
return 0;
}
......@@ -390,9 +390,8 @@ int grundig_29504_401_ioctl (struct dvb_frontend *fe,
{
struct dvb_frontend_parameters *p = arg;
tsa5060_set_tv_freq (i2c, p->frequency, 3);
tsa5060_set_tv_freq (i2c, p->frequency);
apply_frontend_param (i2c, p);
// tsa5060_set_tv_freq (i2c, p->frequency, 0);
}
case FE_GET_FRONTEND:
/* we could correct the frequency here, but...
......@@ -407,13 +406,6 @@ int grundig_29504_401_ioctl (struct dvb_frontend *fe,
case FE_INIT:
return init (i2c);
case FE_RESET:
//reset_afc (i2c);
apply_tps (i2c);
l64781_readreg (i2c, 0x00); /* clear interrupt registers... */
l64781_readreg (i2c, 0x01); /* dto. */
break;
default:
dprintk ("%s: unknown command !!!\n", __FUNCTION__);
return -EINVAL;
......@@ -428,8 +420,8 @@ int l64781_attach (struct dvb_i2c_bus *i2c)
{
u8 b0 [] = { 0x1a };
u8 b1 [] = { 0x00 };
struct i2c_msg msg [] = { { .addr = 0x55, .flags = 0, .buf = b0, .len = 1 },
{ .addr = 0x55, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
struct i2c_msg msg [] = { { addr: 0x55, flags: 0, buf: b0, len: 1 },
{ addr: 0x55, flags: I2C_M_RD, buf: b1, len: 1 } };
if (i2c->xfer (i2c, msg, 2) == 2) /* probably an EEPROM... */
return -ENODEV;
......
......@@ -27,7 +27,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
......@@ -36,20 +35,22 @@ static int debug = 0;
static
struct dvb_frontend_info grundig_29504_491_info = {
.name = "Grundig 29504-491, (TDA8083 based)",
.type = FE_QPSK,
.frequency_min = 950000, /* FIXME: guessed! */
.frequency_max = 1400000, /* FIXME: guessed! */
.frequency_stepsize = 125, /* kHz for QPSK frontends */
.symbol_rate_min = 1000000, /* FIXME: guessed! */
.symbol_rate_max = 45000000, /* FIXME: guessed! */
.caps = FE_CAN_INVERSION_AUTO |
FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
FE_CAN_FEC_3_4 | FE_CAN_FEC_4_5 |
FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 |
FE_CAN_FEC_AUTO | FE_CAN_QPSK |
FE_CAN_MUTE_TS
name: "Grundig 29504-491, (TDA8083 based)",
type: FE_QPSK,
frequency_min: 950000, /* FIXME: guessed! */
frequency_max: 1400000, /* FIXME: guessed! */
frequency_stepsize: 125, /* kHz for QPSK frontends */
/* frequency_tolerance: ???,*/
symbol_rate_min: 1000000, /* FIXME: guessed! */
symbol_rate_max: 45000000, /* FIXME: guessed! */
/* 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_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
FE_CAN_QPSK |
FE_CAN_MUTE_TS | FE_CAN_CLEAN_SETUP
};
......@@ -71,7 +72,7 @@ int tda8083_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 };
struct i2c_msg msg = { addr: 0x68, flags: 0, buf: buf, len: 2 };
ret = i2c->xfer (i2c, &msg, 1);
......@@ -87,8 +88,8 @@ static
int tda8083_readregs (struct dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len)
{
int ret;
struct i2c_msg msg [] = { { .addr = 0x68, .flags = 0, .buf = &reg1, .len = 1 },
{ .addr = 0x68, .flags = I2C_M_RD, .buf = b, .len = len } };
struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: &reg1, len: 1 },
{ addr: 0x68, flags: I2C_M_RD, buf: b, len: len } };
ret = i2c->xfer (i2c, msg, 2);
......@@ -115,7 +116,7 @@ static
int tsa5522_write (struct dvb_i2c_bus *i2c, u8 data [4])
{
int ret;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = 4 };
struct i2c_msg msg = { addr: 0x61, flags: 0, buf: data, len: 4 };
ret = i2c->xfer (i2c, &msg, 1);
......
/*
NxtWave Communications - NXT6000 demodulator driver
This driver currently supports:
Alps TDME7 (Tuner: MITEL SP5659)
Alps TDED4 (Tuner: TI ALP510, external Nxt6000)
Copyright (C) 2002-2003 Florian Schirmer <schirmer@taytron.net>
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/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include "dvb_frontend.h"
#include "nxt6000.h"
static int debug = 0;
MODULE_DESCRIPTION("NxtWave NXT6000 DVB demodulator driver");
MODULE_AUTHOR("Florian Schirmer");
MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i");
static struct dvb_frontend_info nxt6000_info = {
.name = "NxtWave NXT6000",
.type = FE_OFDM,
.frequency_min = 0,
.frequency_max = 863250000,
.frequency_stepsize = 62500,
/*.frequency_tolerance = */ /* FIXME: 12% of SR */
.symbol_rate_min = 0, /* FIXME */
.symbol_rate_max = 9360000, /* FIXME */
.symbol_rate_tolerance = 4000,
.notifier_delay = 0,
.caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
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,
};
#pragma pack(1)
struct nxt6000_config {
u8 demod_addr;
u8 tuner_addr;
u8 tuner_type;
u8 clock_inversion;
};
#pragma pack()
#define TUNER_TYPE_ALP510 0
#define TUNER_TYPE_SP5659 1
#define FE2NXT(fe) ((struct nxt6000_config *)&(fe->data))
#define FREQ2DIV(freq) ((freq + 36166667) / 166667)
#define dprintk if (debug) printk
static int nxt6000_write(struct dvb_i2c_bus *i2c, u8 addr, u8 reg, u8 data)
{
u8 buf[] = {reg, data};
struct i2c_msg msg = {addr: addr >> 1, flags: 0, buf: buf, len: 2};
int ret;
if ((ret = i2c->xfer(i2c, &msg, 1)) != 1)
dprintk("nxt6000: nxt6000_write error (addr: 0x%02X, reg: 0x%02X, data: 0x%02X, ret: %d)\n", addr, reg, data, ret);
return (ret != 1) ? -EFAULT : 0;
}
static u8 nxt6000_writereg(struct dvb_frontend *fe, u8 reg, u8 data)
{
struct nxt6000_config *nxt = FE2NXT(fe);
return nxt6000_write(fe->i2c, nxt->demod_addr, reg, data);
}
static u8 nxt6000_read(struct dvb_i2c_bus *i2c, u8 addr, u8 reg)
{
int ret;
u8 b0[] = {reg};
u8 b1[] = {0};
struct i2c_msg msgs[] = {{addr: addr >> 1, flags: 0, buf: b0, len: 1},
{addr: addr >> 1, flags: I2C_M_RD, buf: b1, len: 1}};
ret = i2c->xfer(i2c, msgs, 2);
if (ret != 2)
dprintk("nxt6000: nxt6000_read error (addr: 0x%02X, reg: 0x%02X, ret: %d)\n", addr, reg, ret);
return b1[0];
}
static u8 nxt6000_readreg(struct dvb_frontend *fe, u8 reg)
{
struct nxt6000_config *nxt = FE2NXT(fe);
return nxt6000_read(fe->i2c, nxt->demod_addr, reg);
}
static int pll_write(struct dvb_i2c_bus *i2c, u8 demod_addr, u8 tuner_addr, u8 *buf, u8 len)
{
struct i2c_msg msg = {addr: tuner_addr >> 1, flags: 0, buf: buf, len: len};
int ret;
nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */
ret = i2c->xfer(i2c, &msg, 1);
nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */
if (ret != 1)
dprintk("nxt6000: pll_write error %d\n", ret);
return (ret != 1) ? -EFAULT : 0;
}
static int sp5659_set_tv_freq(struct dvb_frontend *fe, u32 freq)
{
u8 buf[4];
struct nxt6000_config *nxt = FE2NXT(fe);
buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F;
buf[1] = FREQ2DIV(freq) & 0xFF;
buf[2] = (((FREQ2DIV(freq) >> 15) & 0x03) << 5) | 0x85;
if ((freq >= 174000000) && (freq < 230000000))
buf[3] = 0x82;
else if ((freq >= 470000000) && (freq < 782000000))
buf[3] = 0x85;
else if ((freq >= 782000000) && (freq < 863000000))
buf[3] = 0xC5;
else
return -EINVAL;
return pll_write(fe->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4);
}
static int alp510_set_tv_freq(struct dvb_frontend *fe, u32 freq)
{
u8 buf[4];
struct nxt6000_config *nxt = FE2NXT(fe);
buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F;
buf[1] = FREQ2DIV(freq) & 0xFF;
buf[2] = 0x85;
#if 0
if ((freq >= 47000000) && (freq < 153000000))
buf[3] = 0x01;
else if ((freq >= 153000000) && (freq < 430000000))
buf[3] = 0x02;
else if ((freq >= 430000000) && (freq < 824000000))
buf[3] = 0x08;
else if ((freq >= 824000000) && (freq < 863000000))
buf[3] = 0x88;
else
return -EINVAL;
#else
if ((freq >= 47000000) && (freq < 153000000))
buf[3] = 0x01;
else if ((freq >= 153000000) && (freq < 430000000))
buf[3] = 0x02;
else if ((freq >= 430000000) && (freq < 824000000))
buf[3] = 0x0C;
else if ((freq >= 824000000) && (freq < 863000000))
buf[3] = 0x8C;
else
return -EINVAL;
#endif
return pll_write(fe->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4);
}
static void nxt6000_reset(struct dvb_frontend *fe)
{
u8 val;
val = nxt6000_readreg(fe, OFDM_COR_CTL);
nxt6000_writereg(fe, OFDM_COR_CTL, val & ~COREACT);
nxt6000_writereg(fe, OFDM_COR_CTL, val | COREACT);
}
static int nxt6000_set_bandwidth(struct dvb_frontend *fe, fe_bandwidth_t bandwidth)
{
u16 nominal_rate;
int result;
switch(bandwidth) {
case BANDWIDTH_6_MHZ:
nominal_rate = 0x55B7;
break;
case BANDWIDTH_7_MHZ:
nominal_rate = 0x6400;
break;
case BANDWIDTH_8_MHZ:
nominal_rate = 0x7249;
break;
default:
return -EINVAL;
}
if ((result = nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0)
return result;
return nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF);
}
static int nxt6000_set_guard_interval(struct dvb_frontend *fe, fe_guard_interval_t guard_interval)
{
switch(guard_interval) {
case GUARD_INTERVAL_1_32:
return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
case GUARD_INTERVAL_1_16:
return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
case GUARD_INTERVAL_AUTO:
case GUARD_INTERVAL_1_8:
return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
case GUARD_INTERVAL_1_4:
return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03));
default:
return -EINVAL;
}
}
static int nxt6000_set_inversion(struct dvb_frontend *fe, fe_spectral_inversion_t inversion)
{
switch(inversion) {
case INVERSION_OFF:
return nxt6000_writereg(fe, OFDM_ITB_CTL, 0x00);
case INVERSION_ON:
return nxt6000_writereg(fe, OFDM_ITB_CTL, ITBINV);
default:
return -EINVAL;
}
}
static int nxt6000_set_transmission_mode(struct dvb_frontend *fe, fe_transmit_mode_t transmission_mode)
{
int result;
switch(transmission_mode) {
case TRANSMISSION_MODE_2K:
if ((result = nxt6000_writereg(fe, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(fe, EN_DMD_RACQ) & ~0x03))) < 0)
return result;
return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04));
case TRANSMISSION_MODE_8K:
case TRANSMISSION_MODE_AUTO:
if ((result = nxt6000_writereg(fe, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(fe, EN_DMD_RACQ) & ~0x03))) < 0)
return result;
return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04));
default:
return -EINVAL;
}
}
static void nxt6000_setup(struct dvb_frontend *fe)
{
struct nxt6000_config *nxt = FE2NXT(fe);
nxt6000_writereg(fe, RS_COR_SYNC_PARAM, SYNC_PARAM);
nxt6000_writereg(fe, BER_CTRL, /*(1 << 2) |*/ (0x01 << 1) | 0x01);
nxt6000_writereg(fe, VIT_COR_CTL, VIT_COR_RESYNC);
nxt6000_writereg(fe, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(fe, OFDM_COR_CTL) & 0x0F));
nxt6000_writereg(fe, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02);
nxt6000_writereg(fe, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW);
nxt6000_writereg(fe, OFDM_ITB_FREQ_1, 0x06);
nxt6000_writereg(fe, OFDM_ITB_FREQ_2, 0x31);
nxt6000_writereg(fe, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04);
nxt6000_writereg(fe, CAS_FREQ, 0xBB); // CHECKME
nxt6000_writereg(fe, OFDM_SYR_CTL, 1 << 2);
nxt6000_writereg(fe, OFDM_PPM_CTL_1, PPM256);
nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_1, 0x49);
nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_2, 0x72);
nxt6000_writereg(fe, ANALOG_CONTROL_0, 1 << 5);
nxt6000_writereg(fe, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2);
nxt6000_writereg(fe, DIAG_CONFIG, TB_SET);
if (nxt->clock_inversion)
nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, CLKINVERSION);
else
nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, 0);
nxt6000_writereg(fe, TS_FORMAT, 0);
}
static void nxt6000_dump_status(struct dvb_frontend *fe)
{
u8 val;
// printk("RS_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, RS_COR_STAT));
// printk("VIT_SYNC_STATUS: 0x%02X\n", nxt6000_readreg(fe, VIT_SYNC_STATUS));
// printk("OFDM_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_COR_STAT));
// printk("OFDM_SYR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_SYR_STAT));
// printk("OFDM_TPS_RCVD_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_1));
// printk("OFDM_TPS_RCVD_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_2));
// printk("OFDM_TPS_RCVD_3: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_3));
// printk("OFDM_TPS_RCVD_4: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_4));
// printk("OFDM_TPS_RESERVED_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_1));
// printk("OFDM_TPS_RESERVED_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_2));
printk("NXT6000 status:");
val = nxt6000_readreg(fe, RS_COR_STAT);
printk(" DATA DESCR LOCK: %d,", val & 0x01);
printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01);
val = nxt6000_readreg(fe, VIT_SYNC_STATUS);
printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01);
switch((val >> 4) & 0x07) {
case 0x00:
printk(" VITERBI CODERATE: 1/2,");
break;
case 0x01:
printk(" VITERBI CODERATE: 2/3,");
break;
case 0x02:
printk(" VITERBI CODERATE: 3/4,");
break;
case 0x03:
printk(" VITERBI CODERATE: 5/6,");
case 0x04:
printk(" VITERBI CODERATE: 7/8,");
break;
default:
printk(" VITERBI CODERATE: Reserved,");
}
val = nxt6000_readreg(fe, OFDM_COR_STAT);
printk(" CHCTrack: %d,", (val >> 7) & 0x01);
printk(" TPSLock: %d,", (val >> 6) & 0x01);
printk(" SYRLock: %d,", (val >> 5) & 0x01);
printk(" AGCLock: %d,", (val >> 4) & 0x01);
switch(val & 0x0F) {
case 0x00:
printk(" CoreState: IDLE,");
break;
case 0x02:
printk(" CoreState: WAIT_AGC,");
break;
case 0x03:
printk(" CoreState: WAIT_SYR,");
break;
case 0x04:
printk(" CoreState: WAIT_PPM,");
case 0x01:
printk(" CoreState: WAIT_TRL,");
break;
case 0x05:
printk(" CoreState: WAIT_TPS,");
break;
case 0x06:
printk(" CoreState: MONITOR_TPS,");
break;
default:
printk(" CoreState: Reserved,");
}
val = nxt6000_readreg(fe, OFDM_SYR_STAT);
printk(" SYRLock: %d,", (val >> 4) & 0x01);
printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K");
switch((val >> 4) & 0x03) {
case 0x00:
printk(" SYRGuard: 1/32,");
break;
case 0x01:
printk(" SYRGuard: 1/16,");
break;
case 0x02:
printk(" SYRGuard: 1/8,");
break;
case 0x03:
printk(" SYRGuard: 1/4,");
break;
}
val = nxt6000_readreg(fe, OFDM_TPS_RCVD_3);
switch((val >> 4) & 0x07) {
case 0x00:
printk(" TPSLP: 1/2,");
break;
case 0x01:
printk(" TPSLP: 2/3,");
break;
case 0x02:
printk(" TPSLP: 3/4,");
break;
case 0x03:
printk(" TPSLP: 5/6,");
case 0x04:
printk(" TPSLP: 7/8,");
break;
default:
printk(" TPSLP: Reserved,");
}
switch(val & 0x07) {
case 0x00:
printk(" TPSHP: 1/2,");
break;
case 0x01:
printk(" TPSHP: 2/3,");
break;
case 0x02:
printk(" TPSHP: 3/4,");
break;
case 0x03:
printk(" TPSHP: 5/6,");
case 0x04:
printk(" TPSHP: 7/8,");
break;
default:
printk(" TPSHP: Reserved,");
}
val = nxt6000_readreg(fe, OFDM_TPS_RCVD_4);
printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K");
switch((val >> 4) & 0x03) {
case 0x00:
printk(" TPSGuard: 1/32,");
break;
case 0x01:
printk(" TPSGuard: 1/16,");
break;
case 0x02:
printk(" TPSGuard: 1/8,");
break;
case 0x03:
printk(" TPSGuard: 1/4,");
break;
}
// Strange magic required to gain access to RF_AGC_STATUS
nxt6000_readreg(fe, RF_AGC_VAL_1);
val = nxt6000_readreg(fe, RF_AGC_STATUS);
val = nxt6000_readreg(fe, RF_AGC_STATUS);
printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01);
printk("\n");
}
static int nxt6000_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
switch (cmd) {
case FE_GET_INFO:
memcpy(arg, &nxt6000_info, sizeof (struct dvb_frontend_info));
return 0;
case FE_READ_STATUS:
{
fe_status_t *status = (fe_status_t *)arg;
u8 core_status;
*status = 0;
core_status = nxt6000_readreg(fe, OFDM_COR_STAT);
if (core_status & AGCLOCKED)
*status |= FE_HAS_SIGNAL;
if (nxt6000_readreg(fe, OFDM_SYR_STAT) & GI14_SYR_LOCK)
*status |= FE_HAS_CARRIER;
if (nxt6000_readreg(fe, VIT_SYNC_STATUS) & VITINSYNC)
*status |= FE_HAS_VITERBI;
if (nxt6000_readreg(fe, RS_COR_STAT) & RSCORESTATUS)
*status |= FE_HAS_SYNC;
if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)))
*status |= FE_HAS_LOCK;
if (debug)
nxt6000_dump_status(fe);
return 0;
}
case FE_READ_BER:
{
u32 *ber = (u32 *)arg;
*ber=0;
return 0;
}
case FE_READ_SIGNAL_STRENGTH:
{
// s16 *signal = (s16 *)arg;
// *signal=(((signed char)readreg(client, 0x16))+128)<<8;
return 0;
}
case FE_READ_SNR:
{
// s16 *snr = (s16 *)arg;
// *snr=readreg(client, 0x24)<<8;
// *snr|=readreg(client, 0x25);
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
{
u32 *ublocks = (u32 *)arg;
*ublocks = 0;
break;
}
case FE_INIT:
case FE_RESET:
nxt6000_reset(fe);
nxt6000_setup(fe);
break;
case FE_SET_FRONTEND:
{
struct nxt6000_config *nxt = FE2NXT(fe);
struct dvb_frontend_parameters *param = (struct dvb_frontend_parameters *)arg;
int result;
switch(nxt->tuner_type) {
case TUNER_TYPE_ALP510:
if ((result = alp510_set_tv_freq(fe, param->frequency)) < 0)
return result;
break;
case TUNER_TYPE_SP5659:
if ((result = sp5659_set_tv_freq(fe, param->frequency)) < 0)
return result;
break;
default:
return -EFAULT;
}
if ((result = nxt6000_set_bandwidth(fe, param->u.ofdm.bandwidth)) < 0)
return result;
if ((result = nxt6000_set_guard_interval(fe, param->u.ofdm.guard_interval)) < 0)
return result;
if ((result = nxt6000_set_transmission_mode(fe, param->u.ofdm.transmission_mode)) < 0)
return result;
if ((result = nxt6000_set_inversion(fe, param->inversion)) < 0)
return result;
break;
}
default:
return -EOPNOTSUPP;
}
return 0;
}
static u8 demod_addr_tbl[] = {0x14, 0x18, 0x24, 0x28};
static int nxt6000_attach(struct dvb_i2c_bus *i2c)
{
u8 addr_nr;
u8 fe_count = 0;
struct nxt6000_config nxt;
dprintk("nxt6000: attach\n");
for (addr_nr = 0; addr_nr < sizeof(demod_addr_tbl); addr_nr++) {
if (nxt6000_read(i2c, demod_addr_tbl[addr_nr], OFDM_MSC_REV) != NXT6000ASICDEVICE)
continue;
if (pll_write(i2c, demod_addr_tbl[addr_nr], 0xC0, NULL, 0) == 0) {
nxt.tuner_addr = 0xC0;
nxt.tuner_type = TUNER_TYPE_ALP510;
nxt.clock_inversion = 1;
dprintk("nxt6000: detected TI ALP510 tuner at 0x%02X\n", nxt.tuner_addr);
} else if (pll_write(i2c, demod_addr_tbl[addr_nr], 0xC2, NULL, 0) == 0) {
nxt.tuner_addr = 0xC2;
nxt.tuner_type = TUNER_TYPE_SP5659;
nxt.clock_inversion = 0;
dprintk("nxt6000: detected MITEL SP5659 tuner at 0x%02X\n", nxt.tuner_addr);
} else {
printk("nxt6000: unable to detect tuner\n");
continue;
}
nxt.demod_addr = demod_addr_tbl[addr_nr];
dprintk("nxt6000: attached at %d:%d\n", i2c->adapter->num, i2c->id);
dvb_register_frontend(nxt6000_ioctl, i2c, (void *)(*((u32 *)&nxt)), &nxt6000_info);
}
return (fe_count > 0) ? 0 : -ENODEV;
}
static void nxt6000_detach(struct dvb_i2c_bus *i2c)
{
dprintk("nxt6000: detach\n");
dvb_unregister_frontend(nxt6000_ioctl, i2c);
}
static __init int nxt6000_init(void)
{
dprintk("nxt6000: init\n");
return dvb_register_i2c_device(THIS_MODULE, nxt6000_attach, nxt6000_detach);
}
static __exit void nxt6000_exit(void)
{
dprintk("nxt6000: cleanup\n");
dvb_unregister_i2c_device(nxt6000_attach);
}
module_init(nxt6000_init);
module_exit(nxt6000_exit);
/**********************************************************************/
* DRV6000reg.H
* Public Include File for DRV6000 users
*
* Copyright (C) 2001 NxtWave Communications, Inc.
*
* $Log: nxt6000.h,v $
* Revision 1.2 2003/01/27 12:32:42 fschirmer
* Lots of bugfixes and new features
*
* Revision 1.1 2003/01/21 18:43:09 fschirmer
* Nxt6000 based frontend driver
*
* Revision 1.1 2003/01/03 02:25:45 obi
* alps tdme7 driver
*
*
* Rev 1.10 Jun 12 2002 11:28:02 dkoeger
* Updated for SA in GUi work
*
* Rev 1.9 Apr 01 2002 10:38:46 dkoeger
* Updated for 1.0.31 GUI
*
* Rev 1.8 Mar 11 2002 10:04:56 dkoeger
* Updated for 1.0.31 GUI version
*
* Rev 1.5 Dec 07 2001 14:40:40 dkoeger
* Updated for 1.0.28 GUI
*
* Rev 1.4 Nov 13 2001 11:09:00 dkoeger
* No change.
*
* Rev 1.3 Aug 23 2001 14:21:02 dkoeger
* Updated for driver version 2.1.9
*
* Rev 1.2 Jul 09 2001 09:20:04 dkoeger
* Updated for 1.0.18
*
* Rev 1.1 Jun 13 2001 16:14:24 dkoeger
* Updated to reflect NXT6000 GUI BETA 1.0.11 6/13/2001
**********************************************************************/
/* Nxt6000 Register Addresses and Bit Masks */
/* Maximum Register Number */
#define MAXNXT6000REG (0x9A)
/* 0x1B A_VIT_BER_0 aka 0x3A */
#define A_VIT_BER_0 (0x1B)
/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */
#define A_VIT_BER_TIMER_0 (0x1D)
/* 0x21 RS_COR_STAT */
#define RS_COR_STAT (0x21)
#define RSCORESTATUS (0x03)
/* 0x22 RS_COR_INTEN */
#define RS_COR_INTEN (0x22)
/* 0x23 RS_COR_INSTAT */
#define RS_COR_INSTAT (0x23)
#define INSTAT_ERROR (0x04)
#define LOCK_LOSS_BITS (0x03)
/* 0x24 RS_COR_SYNC_PARAM */
#define RS_COR_SYNC_PARAM (0x24)
#define SYNC_PARAM (0x03)
/* 0x25 BER_CTRL */
#define BER_CTRL (0x25)
#define BER_ENABLE (0x02)
#define BER_RESET (0x01)
/* 0x26 BER_PAY */
#define BER_PAY (0x26)
/* 0x27 BER_PKT_L */
#define BER_PKT_L (0x27)
#define BER_PKTOVERFLOW (0x80)
/* 0x30 VIT_COR_CTL */
#define VIT_COR_CTL (0x30)
#define BER_CONTROL (0x02)
#define VIT_COR_MASK (0x82)
#define VIT_COR_RESYNC (0x80)
/* 0x32 VIT_SYNC_STATUS */
#define VIT_SYNC_STATUS (0x32)
#define VITINSYNC (0x80)
/* 0x33 VIT_COR_INTEN */
#define VIT_COR_INTEN (0x33)
#define GLOBAL_ENABLE (0x80)
/* 0x34 VIT_COR_INTSTAT */
#define VIT_COR_INTSTAT (0x34)
#define BER_DONE (0x08)
#define BER_OVERFLOW (0x10)
/* 0x38 OFDM_BERTimer */ /* Use the alias registers */
#define A_VIT_BER_TIMER_0 (0x1D)
/* 0x3A VIT_BER_TIMER_0 */ /* Use the alias registers */
#define A_VIT_BER_0 (0x1B)
/* 0x40 OFDM_COR_CTL */
#define OFDM_COR_CTL (0x40)
#define COREACT (0x20)
#define HOLDSM (0x10)
#define WAIT_AGC (0x02)
#define WAIT_SYR (0x03)
/* 0x41 OFDM_COR_STAT */
#define OFDM_COR_STAT (0x41)
#define COR_STATUS (0x0F)
#define MONITOR_TPS (0x06)
#define TPSLOCKED (0x40)
#define AGCLOCKED (0x10)
/* 0x42 OFDM_COR_INTEN */
#define OFDM_COR_INTEN (0x42)
#define TPSRCVBAD (0x04)
#define TPSRCVCHANGED (0x02)
#define TPSRCVUPDATE (0x01)
/* 0x43 OFDM_COR_INSTAT */
#define OFDM_COR_INSTAT (0x43)
/* 0x44 OFDM_COR_MODEGUARD */
#define OFDM_COR_MODEGUARD (0x44)
#define FORCEMODE (0x08)
#define FORCEMODE8K (0x04)
/* 0x45 OFDM_AGC_CTL */
#define OFDM_AGC_CTL (0x45)
#define INITIAL_AGC_BW (0x08)
#define AGCNEG (0x02)
#define AGCLAST (0x10)
/* 0x48 OFDM_AGC_TARGET */
#define OFDM_AGC_TARGET (0x48)
#define OFDM_AGC_TARGET_DEFAULT (0x28)
#define OFDM_AGC_TARGET_IMPULSE (0x38)
/* 0x49 OFDM_AGC_GAIN_1 */
#define OFDM_AGC_GAIN_1 (0x49)
/* 0x4B OFDM_ITB_CTL */
#define OFDM_ITB_CTL (0x4B)
#define ITBINV (0x01)
/* 0x4C OFDM_ITB_FREQ_1 */
#define OFDM_ITB_FREQ_1 (0x4C)
/* 0x4D OFDM_ITB_FREQ_2 */
#define OFDM_ITB_FREQ_2 (0x4D)
/* 0x4E OFDM_CAS_CTL */
#define OFDM_CAS_CTL (0x4E)
#define ACSDIS (0x40)
#define CCSEN (0x80)
/* 0x4F CAS_FREQ */
#define CAS_FREQ (0x4F)
/* 0x51 OFDM_SYR_CTL */
#define OFDM_SYR_CTL (0x51)
#define SIXTH_ENABLE (0x80)
#define SYR_TRACKING_DISABLE (0x01)
/* 0x52 OFDM_SYR_STAT */
#define OFDM_SYR_STAT (0x52)
#define GI14_2K_SYR_LOCK (0x13)
#define GI14_8K_SYR_LOCK (0x17)
#define GI14_SYR_LOCK (0x10)
/* 0x55 OFDM_SYR_OFFSET_1 */
#define OFDM_SYR_OFFSET_1 (0x55)
/* 0x56 OFDM_SYR_OFFSET_2 */
#define OFDM_SYR_OFFSET_2 (0x56)
/* 0x58 OFDM_SCR_CTL */
#define OFDM_SCR_CTL (0x58)
#define SYR_ADJ_DECAY_MASK (0x70)
#define SYR_ADJ_DECAY (0x30)
/* 0x59 OFDM_PPM_CTL_1 */
#define OFDM_PPM_CTL_1 (0x59)
#define PPMMAX_MASK (0x30)
#define PPM256 (0x30)
/* 0x5B OFDM_TRL_NOMINALRATE_1 */
#define OFDM_TRL_NOMINALRATE_1 (0x5B)
/* 0x5C OFDM_TRL_NOMINALRATE_2 */
#define OFDM_TRL_NOMINALRATE_2 (0x5C)
/* 0x5D OFDM_TRL_TIME_1 */
#define OFDM_TRL_TIME_1 (0x5D)
/* 0x60 OFDM_CRL_FREQ_1 */
#define OFDM_CRL_FREQ_1 (0x60)
/* 0x63 OFDM_CHC_CTL_1 */
#define OFDM_CHC_CTL_1 (0x63)
#define MANMEAN1 (0xF0);
#define CHCFIR (0x01)
/* 0x64 OFDM_CHC_SNR */
#define OFDM_CHC_SNR (0x64)
/* 0x65 OFDM_BDI_CTL */
#define OFDM_BDI_CTL (0x65)
#define LP_SELECT (0x02)
/* 0x67 OFDM_TPS_RCVD_1 */
#define OFDM_TPS_RCVD_1 (0x67)
#define TPSFRAME (0x03)
/* 0x68 OFDM_TPS_RCVD_2 */
#define OFDM_TPS_RCVD_2 (0x68)
/* 0x69 OFDM_TPS_RCVD_3 */
#define OFDM_TPS_RCVD_3 (0x69)
/* 0x6A OFDM_TPS_RCVD_4 */
#define OFDM_TPS_RCVD_4 (0x6A)
/* 0x6B OFDM_TPS_RESERVED_1 */
#define OFDM_TPS_RESERVED_1 (0x6B)
/* 0x6C OFDM_TPS_RESERVED_2 */
#define OFDM_TPS_RESERVED_2 (0x6C)
/* 0x73 OFDM_MSC_REV */
#define OFDM_MSC_REV (0x73)
/* 0x76 OFDM_SNR_CARRIER_2 */
#define OFDM_SNR_CARRIER_2 (0x76)
#define MEAN_MASK (0x80)
#define MEANBIT (0x80)
/* 0x80 ANALOG_CONTROL_0 */
#define ANALOG_CONTROL_0 (0x80)
#define POWER_DOWN_ADC (0x40)
/* 0x81 ENABLE_TUNER_IIC */
#define ENABLE_TUNER_IIC (0x81)
#define ENABLE_TUNER_BIT (0x01)
/* 0x82 EN_DMD_RACQ */
#define EN_DMD_RACQ (0x82)
#define EN_DMD_RACQ_REG_VAL (0x81)
#define EN_DMD_RACQ_REG_VAL_14 (0x01)
/* 0x84 SNR_COMMAND */
#define SNR_COMMAND (0x84)
#define SNRStat (0x80)
/* 0x85 SNRCARRIERNUMBER_LSB */
#define SNRCARRIERNUMBER_LSB (0x85)
/* 0x87 SNRMINTHRESHOLD_LSB */
#define SNRMINTHRESHOLD_LSB (0x87)
/* 0x89 SNR_PER_CARRIER_LSB */
#define SNR_PER_CARRIER_LSB (0x89)
/* 0x8B SNRBELOWTHRESHOLD_LSB */
#define SNRBELOWTHRESHOLD_LSB (0x8B)
/* 0x91 RF_AGC_VAL_1 */
#define RF_AGC_VAL_1 (0x91)
/* 0x92 RF_AGC_STATUS */
#define RF_AGC_STATUS (0x92)
/* 0x98 DIAG_CONFIG */
#define DIAG_CONFIG (0x98)
#define DIAG_MASK (0x70)
#define TB_SET (0x10)
#define TRAN_SELECT (0x07)
#define SERIAL_SELECT (0x01)
/* 0x99 SUB_DIAG_MODE_SEL */
#define SUB_DIAG_MODE_SEL (0x99)
#define CLKINVERSION (0x01)
/* 0x9A TS_FORMAT */
#define TS_FORMAT (0x9A)
#define ERROR_SENSE (0x08)
#define VALID_SENSE (0x04)
#define SYNC_SENSE (0x02)
#define GATED_CLOCK (0x01)
#define NXT6000ASICDEVICE (0x0b)
/*
Alps BSRU6 and LG TDQB-S00x DVB QPSK frontend driver
Universal driver for STV0299/TDA5059/SL1935 based
DVB QPSK frontends
Alps BSRU6, LG TDQB-S00x
Copyright (C) 2001-2002 Convergence Integrated Media GmbH
<ralph@convergence.de>, <holger@convergence.de>,
<ralph@convergence.de>,
<holger@convergence.de>,
<js@convergence.de>
Philips SU1278/SH
Copyright (C) 2002 by Peter Schildmann
<peter.schildmann@web.de>
LG TDQF-S001F
Copyright (C) 2002 Felix Domke <tmbinc@elitedvb.net>
& Andreas Oberritter <andreas@oberritter.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
......@@ -24,67 +38,80 @@
#include <linux/init.h>
#include <linux/module.h>
#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
#define LG_TDQF_S001F 2
/* Master Clock = 88 MHz */
#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 = {
#ifdef CONFIG_ALPS_BSRU6_IS_LG_TDQBS00X
.name = "LG TDQB-S00x",
#else
.name = "Alps BSRU6",
#endif
.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 */
.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_QPSK
struct dvb_frontend_info uni0299_info = {
name: "STV0299/TSA5059/SL1935 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 [] = {
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, 0x43, // DiSEqC
0x08, 0x03, // DiSEqC
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
/* 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,
0x16, 0x1d, /* AGC1 integrator value */
0x17, 0x00,
0x18, 0x14,
0x19, 0xf2,
......@@ -158,7 +185,7 @@ 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 };
struct i2c_msg msg = { addr: 0x68, flags: 0, buf: buf, len: 2 };
dprintk ("%s\n", __FUNCTION__);
......@@ -178,8 +205,8 @@ 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 } };
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__);
......@@ -196,8 +223,8 @@ 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 = &reg1, .len = 1 },
{ .addr = 0x68, .flags = I2C_M_RD, .buf = b, .len = len } };
struct i2c_msg msg [] = { { addr: 0x68, flags: 0, buf: &reg1, len: 1 },
{ addr: 0x68, flags: I2C_M_RD, buf: b, len: len } };
dprintk ("%s\n", __FUNCTION__);
......@@ -210,18 +237,25 @@ int stv0299_readregs (struct dvb_i2c_bus *i2c, u8 reg1, u8 *b, u8 len)
}
static
int tsa5059_write (struct dvb_i2c_bus *i2c, u8 data [4])
int pll_write (struct dvb_i2c_bus *i2c, u8 data [4], int ftype)
{
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 }};
/* 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__);
if (ftype == LG_TDQF_S001F || ftype == ALPS_BSRU6) {
ret = i2c->xfer (i2c, &msg[0], 1);
ret += i2c->xfer (i2c, &msg[1], 1);
}
else {
ret = i2c->xfer (i2c, msg, 2);
}
if (ret != 2)
dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret);
......@@ -230,24 +264,90 @@ int tsa5059_write (struct dvb_i2c_bus *i2c, u8 data [4])
}
static
int sl1935_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, int ftype)
{
u8 buf[4];
u32 div;
u32 ratios[] = { 2000, 1000, 500, 250, 125 };
u8 ratio;
for (ratio = 4; ratio > 0; ratio--)
if ((freq / ratios[ratio]) <= 0x3fff)
break;
div = freq / ratios[ratio];
buf[0] = (freq >> 8) & 0x7f;
buf[1] = freq & 0xff;
buf[2] = 0x80 | ratio;
if (freq < 1531000)
buf[3] = 0x10;
else
buf[3] = 0x00;
return pll_write (i2c, buf, ftype);
}
/**
* 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)
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, (pwr << 5) | 0x20 };
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);
return pll_write (i2c, buf, ftype);
}
static
int pll_set_tv_freq (struct dvb_i2c_bus *i2c, u32 freq, int ftype)
{
if (ftype == LG_TDQF_S001F)
return sl1935_set_tv_freq(i2c, freq, ftype);
else
return tsa5059_set_tv_freq(i2c, freq, ftype);
}
#if 0
static
int stv0299_init (struct dvb_i2c_bus *i2c)
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;
......@@ -256,45 +356,27 @@ int stv0299_init (struct dvb_i2c_bus *i2c)
for (i=0; i<sizeof(init_tab); i+=2)
stv0299_writereg (i2c, init_tab[i], init_tab[i+1]);
/* AGC1 reference register setup */
if (ftype == PHILIPS_SU1278SH)
stv0299_writereg (i2c, 0x0f, 0xd2); /* Iagc = Inverse, m1 = 18 */
else
stv0299_writereg (i2c, 0x0f, 0x52); /* Iagc = Normal, m1 = 18 */
return 0;
}
static
int stv0299_set_inversion (struct dvb_i2c_bus *i2c,
fe_spectral_inversion_t inversion)
int stv0299_check_inversion (struct dvb_i2c_bus *i2c)
{
u8 val;
dprintk ("%s\n", __FUNCTION__);
#ifdef CONFIG_ALPS_BSRU6_IS_LG_TDQBS00X /* reversed I/Q pins */
switch (inversion) {
case INVERSION_AUTO:
return -EOPNOTSUPP;
case INVERSION_OFF:
val = stv0299_readreg (i2c, 0x0c);
return stv0299_writereg (i2c, 0x0c, val & 0xfe);
case INVERSION_ON:
val = stv0299_readreg (i2c, 0x0c);
return stv0299_writereg (i2c, 0x0c, val | 0x01);
default:
return -EINVAL;
};
#else
switch (inversion) {
case INVERSION_AUTO:
return -EOPNOTSUPP;
case INVERSION_OFF:
val = stv0299_readreg (i2c, 0x0c);
return stv0299_writereg (i2c, 0x0c, val | 0x01);
case INVERSION_ON:
val = stv0299_readreg (i2c, 0x0c);
return stv0299_writereg (i2c, 0x0c, val & 0xfe);
default:
return -EINVAL;
};
#endif
if ((stv0299_readreg (i2c, 0x1b) & 0x98) != 0x98) {
u8 val = stv0299_readreg (i2c, 0x0c);
return stv0299_writereg (i2c, 0x0c, val ^ 0x01);
}
return 0;
}
......@@ -473,7 +555,7 @@ int stv0299_set_voltage (struct dvb_i2c_bus *i2c, fe_sec_voltage_t voltage)
val = stv0299_readreg (i2c, 0x0c);
val &= 0x0f;
val |= 0x40;
val |= 0x40; /* LNB power on */
switch (voltage) {
case SEC_VOLTAGE_13:
......@@ -558,15 +640,16 @@ int stv0299_get_symbolrate (struct dvb_i2c_bus *i2c)
static
int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
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, &bsru6_info, sizeof(struct dvb_frontend_info));
memcpy (arg, &uni0299_info, sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
......@@ -575,6 +658,8 @@ int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *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)
......@@ -604,6 +689,11 @@ int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
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;
......@@ -626,15 +716,17 @@ int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
struct dvb_frontend_parameters *p = arg;
tsa5059_set_tv_freq (i2c, p->frequency, 3);
stv0299_set_inversion (i2c, p->inversion);
pll_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);
tsa5059_set_tv_freq (i2c, p->frequency, 0);
stv0299_check_inversion (i2c);
/* pll_set_tv_freq (i2c, p->frequency, tuner_type); */
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;
}
......@@ -664,7 +756,7 @@ int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
break;
case FE_INIT:
return stv0299_init (i2c);
return stv0299_init (i2c, tuner_type);
case FE_RESET:
stv0299_writereg (i2c, 0x22, 0x00);
......@@ -692,54 +784,97 @@ int bsru6_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
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) {
if (0) { // if ((stat[0] & 0x3f) == 0) {
type = LG_TDQF_S001F;
printk ("%s: setup for tuner TDQF-S001F\n", __FILE__);
}
else {
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 <linuxdvb@linuxtv.org>!!\n",
__FILE__);
}
return type;
}
static
int bsru6_attach (struct dvb_i2c_bus *i2c)
int uni0299_attach (struct dvb_i2c_bus *i2c)
{
int tuner_type;
u8 id = stv0299_readreg (i2c, 0x00);
dprintk ("%s\n", __FUNCTION__);
if ((stv0299_readreg (i2c, 0x00)) != 0xa1)
/* register 0x00 contains 0xa1 for STV0299 and STV0299B */
/* register 0x00 might contain 0x80 when returning from standby */
if (id != 0xa1 && id != 0x80)
return -ENODEV;
dvb_register_frontend (bsru6_ioctl, i2c, NULL, &bsru6_info);
if ((tuner_type = probe_tuner(i2c)) < 0)
return -ENODEV;
dvb_register_frontend (uni0299_ioctl, i2c, (void*) tuner_type,
&uni0299_info);
return 0;
}
static
void bsru6_detach (struct dvb_i2c_bus *i2c)
void uni0299_detach (struct dvb_i2c_bus *i2c)
{
dprintk ("%s\n", __FUNCTION__);
dvb_unregister_frontend (bsru6_ioctl, i2c);
dvb_unregister_frontend (uni0299_ioctl, i2c);
}
static
int __init init_bsru6 (void)
int __init init_uni0299 (void)
{
dprintk ("%s\n", __FUNCTION__);
return dvb_register_i2c_device (THIS_MODULE, bsru6_attach, bsru6_detach);
return dvb_register_i2c_device (THIS_MODULE, uni0299_attach, uni0299_detach);
}
static
void __exit exit_bsru6 (void)
void __exit exit_uni0299 (void)
{
dprintk ("%s\n", __FUNCTION__);
dvb_unregister_i2c_device (bsru6_attach);
dvb_unregister_i2c_device (uni0299_attach);
}
module_init (init_bsru6);
module_exit (exit_bsru6);
module_init (init_uni0299);
module_exit (exit_uni0299);
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_DESCRIPTION("Universal STV0299/TSA5059/SL1935 DVB Frontend driver");
MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, Andreas Oberritter");
MODULE_LICENSE("GPL");
......@@ -23,12 +23,14 @@
#include <linux/module.h>
#include <linux/delay.h>
#include "compat.h"
#include "dvb_frontend.h"
static int debug = 0;
#define dprintk if (debug) printk
#if 0
#define dprintk(x...) printk(x)
#else
#define dprintk(x...)
#endif
/**
......@@ -36,36 +38,58 @@ static int debug = 0;
* extra memory but use frontend->data as bitfield
*/
#define SET_PWM(frontend,pwm) do { \
(int) frontend->data &= ~0xff; \
(int) frontend->data |= pwm; \
#define SET_PWM(data,pwm) do { \
(int) data &= ~0xff; \
(int) data |= pwm; \
} while (0)
#define SET_REG0(frontend,reg0) do { \
(int) frontend->data &= ~(0xff << 8); \
(int) frontend->data |= reg0 << 8; \
#define SET_REG0(data,reg0) do { \
(int) data &= ~(0xff << 8); \
(int) data |= reg0 << 8; \
} while (0)
#define SET_TUNER(frontend,type) do { \
(int) frontend->data &= ~(0xff << 16); \
(int) frontend->data |= type << 16; \
#define SET_TUNER(data,type) do { \
(int) data &= ~(0xff << 16); \
(int) 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))
#define SET_DEMOD_ADDR(data,type) do { \
(int) data &= ~(0xff << 24); \
(int) data |= type << 24; \
} while (0)
#define GET_PWM(data) ((u8) ((int) data & 0xff))
#define GET_REG0(data) ((u8) (((int) data >> 8) & 0xff))
#define GET_TUNER(data) ((u8) (((int) data >> 16) & 0xff))
#define GET_DEMOD_ADDR(data) ((u8) (((int) data >> 24) & 0xff))
static inline
void ddelay (int ms)
{
current->state=TASK_INTERRUPTIBLE;
schedule_timeout((HZ*ms)/1000);
}
static
struct dvb_frontend_info ves1820_info = {
.name = "VES1820/Grundig tuner as used on the Siemens DVB-C card",
.name = "VES1820 based DVB-C frontend",
.type = FE_QAM,
.frequency_stepsize = 62500,
.frequency_min = 51000000,
.frequency_max = 858000000,
.symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */
.symbol_rate_max = (57840000/2)/4, /* SACLK/4 */
#if 0
frequency_tolerance: ???,
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
FE_CAN_QAM_128 | FE_CAN_QAM_256 |
FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO |
FE_CAN_CLEAN_SETUP | FE_CAN_RECOVER
};
......@@ -84,11 +108,13 @@ u8 ves1820_inittab [] =
static
int ves1820_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
int ves1820_writereg (struct dvb_frontend *fe, u8 reg, u8 data)
{
int ret;
u8 addr = GET_DEMOD_ADDR(fe->data);
u8 buf[] = { 0x00, reg, data };
struct i2c_msg msg = { .addr = 0x09, .flags = 0, .buf = buf, .len = 3 };
struct i2c_msg msg = { addr: addr, flags: 0, buf: buf, len: 3 };
struct dvb_i2c_bus *i2c = fe->i2c;
int ret;
ret = i2c->xfer (i2c, &msg, 1);
......@@ -103,14 +129,15 @@ int ves1820_writereg (struct dvb_i2c_bus *i2c, u8 reg, u8 data)
static
u8 ves1820_readreg (struct dvb_i2c_bus *i2c, u8 reg)
u8 ves1820_readreg (struct dvb_frontend *fe, 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 } };
u8 addr = GET_DEMOD_ADDR(fe->data);
struct i2c_msg msg [] = { { addr: addr, flags: 0, buf: b0, len: 2 },
{ addr: addr, flags: I2C_M_RD, buf: b1, len: 1 } };
struct dvb_i2c_bus *i2c = fe->i2c;
int ret;
ret = i2c->xfer (i2c, msg, 2);
......@@ -125,7 +152,7 @@ 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 };
struct i2c_msg msg = { addr: addr, flags: 0, buf: data, len: 4 };
ret = i2c->xfer (i2c, &msg, 1);
......@@ -141,15 +168,18 @@ int tuner_write (struct dvb_i2c_bus *i2c, u8 addr, u8 data [4])
* reference clock comparision frequency of 62.5 kHz.
*/
static
int tuner_set_tv_freq (struct dvb_frontend *frontend, u32 freq)
int tuner_set_tv_freq (struct dvb_frontend *fe, u32 freq)
{
u32 div;
static u8 addr [] = { 0x61, 0x62 };
static u8 byte3 [] = { 0x8e, 0x85 };
int tuner_type = GET_TUNER(frontend);
int tuner_type = GET_TUNER(fe->data);
u8 buf [4];
div = (freq + 36250000 + 31250) / 62500;
if (tuner_type == 0xff) /* PLL not reachable over i2c ... */
return 0;
div = (freq + 36125000 + 31250) / 62500;
buf[0] = (div >> 8) & 0x7f;
buf[1] = div & 0xff;
buf[2] = byte3[tuner_type];
......@@ -163,115 +193,56 @@ int tuner_set_tv_freq (struct dvb_frontend *frontend, u32 freq)
freq < 454000000 ? 0x92 : 0x34);
}
return tuner_write (frontend->i2c, addr[tuner_type], buf);
return tuner_write (fe->i2c, addr[tuner_type], buf);
}
static
int probe_tuner (struct dvb_frontend *frontend)
int ves1820_setup_reg0 (struct dvb_frontend *fe, u8 reg0)
{
struct dvb_i2c_bus *i2c = frontend->i2c;
struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = NULL, .len = 0 };
reg0 |= GET_REG0(fe->data) & 0x62;
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__);
ves1820_writereg (fe, 0x00, reg0 & 0xfe);
ves1820_writereg (fe, 0x00, reg0 | 0x01);
/**
* check lock and toggle inversion bit if required...
*/
if (!(ves1820_readreg (fe, 0x11) & 0x08)) {
ddelay(1);
if (!(ves1820_readreg (fe, 0x11) & 0x08)) {
reg0 ^= 0x20;
ves1820_writereg (fe, 0x00, reg0 & 0xfe);
ves1820_writereg (fe, 0x00, reg0 | 0x01);
}
}
SET_REG0(fe->data, reg0);
return 0;
}
static
int ves1820_init (struct dvb_frontend *frontend)
int ves1820_init (struct dvb_frontend *fe)
{
struct dvb_i2c_bus *i2c = frontend->i2c;
u8 b0 [] = { 0xff };
u8 pwm;
int i;
struct i2c_msg msg [] = { { .addr = 0x50, .flags = 0, .buf = b0, .len = 1 },
{ .addr = 0x50, .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);
ves1820_writereg (fe, 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);
ves1820_writereg (fe, i, ves1820_inittab[i]);
probe_tuner (frontend);
ves1820_writereg (fe, 0x34, GET_PWM(fe->data));
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)
int ves1820_set_symbolrate (struct dvb_frontend *fe, u32 symbolrate)
{
s32 BDR;
s32 BDRI;
......@@ -280,7 +251,7 @@ int ves1820_set_symbolrate (struct dvb_i2c_bus *i2c, u32 symbolrate)
u32 tmp, ratio;
#define XIN 57840000UL
#define FIN (57840000UL>>4)
#define FIN (XIN >> 4)
if (symbolrate > XIN/2)
symbolrate = XIN/2;
......@@ -317,73 +288,42 @@ int ves1820_set_symbolrate (struct dvb_i2c_bus *i2c, u32 symbolrate)
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 (fe, 0x03, NDEC);
ves1820_writereg (fe, 0x0a, BDR&0xff);
ves1820_writereg (fe, 0x0b, (BDR>> 8)&0xff);
ves1820_writereg (fe, 0x0c, (BDR>>16)&0x3f);
ves1820_writereg (i2c, 0x0d, BDRI);
ves1820_writereg (i2c, 0x0e, SFIL);
ves1820_writereg (fe, 0x0d, BDRI);
ves1820_writereg (fe, 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,
int ves1820_set_parameters (struct dvb_frontend *fe,
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:
static const u8 reg0x00 [] = { 0x00, 0x04, 0x08, 0x0c, 0x10 };
static const u8 reg0x01 [] = { 140, 140, 106, 120, 92 };
static const u8 reg0x05 [] = { 135, 100, 70, 54, 38 };
static const u8 reg0x08 [] = { 162, 116, 67, 52, 35 };
static const u8 reg0x09 [] = { 145, 150, 106, 126, 107 };
int real_qam = p->u.qam.modulation - QAM_16;
if (real_qam < 0 || real_qam > 4)
return -EINVAL;
}
tuner_set_tv_freq (frontend, p->frequency);
ves1820_set_symbolrate (i2c, p->u.qam.symbol_rate);
ves1820_reset_pwm (frontend);
tuner_set_tv_freq (fe, p->frequency);
ves1820_set_symbolrate (fe, p->u.qam.symbol_rate);
ves1820_writereg (fe, 0x34, GET_PWM(fe->data));
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_writereg (fe, 0x01, reg0x01[real_qam]);
ves1820_writereg (fe, 0x05, reg0x05[real_qam]);
ves1820_writereg (fe, 0x08, reg0x08[real_qam]);
ves1820_writereg (fe, 0x09, reg0x09[real_qam]);
ves1820_setup_reg0 (frontend, real_qam, p->inversion);
ves1820_setup_reg0 (fe, reg0x00[real_qam]);
return 0;
}
......@@ -391,7 +331,7 @@ int ves1820_set_parameters (struct dvb_frontend *frontend,
static
int ves1820_ioctl (struct dvb_frontend *frontend, unsigned int cmd, void *arg)
int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
switch (cmd) {
case FE_GET_INFO:
......@@ -405,7 +345,7 @@ int ves1820_ioctl (struct dvb_frontend *frontend, unsigned int cmd, void *arg)
*status = 0;
sync = ves1820_readreg (frontend->i2c, 0x11);
sync = ves1820_readreg (fe, 0x11);
if (sync & 2)
*status |= FE_HAS_SIGNAL;
......@@ -427,35 +367,37 @@ int ves1820_ioctl (struct dvb_frontend *frontend, unsigned int cmd, void *arg)
case FE_READ_BER:
{
u32 ber = ves1820_readreg(frontend->i2c, 0x14) |
(ves1820_readreg(frontend->i2c, 0x15) << 8) |
((ves1820_readreg(frontend->i2c, 0x16) & 0x0f) << 16);
u32 ber = ves1820_readreg(fe, 0x14) |
(ves1820_readreg(fe, 0x15) << 8) |
((ves1820_readreg(fe, 0x16) & 0x0f) << 16);
*((u32*) arg) = 10 * ber;
break;
}
case FE_READ_SIGNAL_STRENGTH:
{
u8 gain = ves1820_readreg(frontend->i2c, 0x17);
u8 gain = ves1820_readreg(fe, 0x17);
*((u16*) arg) = (gain << 8) | gain;
break;
}
case FE_READ_SNR:
{
u8 quality = ~ves1820_readreg(frontend->i2c, 0x18);
u8 quality = ~ves1820_readreg(fe, 0x18);
*((u16*) arg) = (quality << 8) | quality;
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
*((u32*) arg) = ves1820_readreg (frontend->i2c, 0x13) & 0x7f;
*((u32*) arg) = ves1820_readreg (fe, 0x13) & 0x7f;
if (*((u32*) arg) == 0x7f)
*((u32*) arg) = 0xffffffff;
ves1820_reset_uncorrected_block_counter (frontend->i2c);
/* reset uncorrected block counter */
ves1820_writereg (fe, 0x10, ves1820_inittab[0x10] & 0xdf);
ves1820_writereg (fe, 0x10, ves1820_inittab[0x10]);
break;
case FE_SET_FRONTEND:
return ves1820_set_parameters (frontend, arg);
return ves1820_set_parameters (fe, arg);
case FE_GET_FRONTEND:
/* XXX FIXME: implement! */
......@@ -468,16 +410,12 @@ int ves1820_ioctl (struct dvb_frontend *frontend, unsigned int cmd, void *arg)
break;
case FE_SLEEP:
ves1820_writereg (frontend->i2c, 0x1b, 0x02); /* pdown ADC */
ves1820_writereg (frontend->i2c, 0x00, 0x80); /* standby */
ves1820_writereg (fe, 0x1b, 0x02); /* pdown ADC */
ves1820_writereg (fe, 0x00, 0x80); /* standby */
break;
case FE_INIT:
return ves1820_init (frontend);
case FE_RESET:
ves1820_reset (frontend);
break;
return ves1820_init (fe);
default:
return -EINVAL;
......@@ -487,13 +425,90 @@ int ves1820_ioctl (struct dvb_frontend *frontend, unsigned int cmd, void *arg)
}
static
int probe_tuner (struct dvb_i2c_bus *i2c)
{
static const
struct i2c_msg msg1 = { addr: 0x61, flags: 0, buf: NULL, len: 0 };
static const
struct i2c_msg msg2 = { addr: 0x62, flags: 0, buf: NULL, len: 0 };
int type;
if (i2c->xfer(i2c, &msg1, 1) == 1) {
type = 0;
printk ("%s: setup for tuner spXXXX\n", __FILE__);
} else if (i2c->xfer(i2c, &msg2, 1) == 1) {
type = 1;
printk ("%s: setup for tuner sp5659c\n", __FILE__);
} else {
type = -1;
printk ("%s: unknown PLL, "
"please report to <linuxdvb@linuxtv.org>!!\n",
__FILE__);
}
return type;
}
static
u8 read_pwm (struct dvb_i2c_bus *i2c)
{
u8 b = 0xff;
u8 pwm;
struct i2c_msg msg [] = { { addr: 0x50, flags: 0, buf: &b, len: 1 },
{ addr: 0x50, flags: I2C_M_RD, buf: &pwm, len: 1 } };
i2c->xfer (i2c, msg, 2);
dprintk("VES1820: pwm=%02x\n", pwm);
if (pwm == 0xff)
pwm = 0x48;
return pwm;
}
static
int probe_demod_addr (struct dvb_i2c_bus *i2c)
{
u8 b [] = { 0x00, 0x1a };
u8 id;
struct i2c_msg msg [] = { { addr: 0x08, flags: 0, buf: b, len: 2 },
{ addr: 0x08, flags: I2C_M_RD, buf: &id, len: 1 } };
if (i2c->xfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70)
return msg[0].addr;
msg[0].addr = msg[1].addr = 0x09;
if (i2c->xfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70)
return msg[0].addr;
return -1;
}
static
int ves1820_attach (struct dvb_i2c_bus *i2c)
{
if ((ves1820_readreg (i2c, 0x1a) & 0xf0) != 0x70)
void *data = NULL;
int demod_addr;
int tuner_type;
if ((demod_addr = probe_demod_addr(i2c)) < 0)
return -ENODEV;
dvb_register_frontend (ves1820_ioctl, i2c, NULL, &ves1820_info);
if ((tuner_type = probe_tuner(i2c)) < 0)
return -ENODEV;
SET_PWM(data, read_pwm(i2c));
SET_REG0(data, ves1820_inittab[0]);
SET_TUNER(data, tuner_type);
SET_DEMOD_ADDR(data, demod_addr);
dvb_register_frontend (ves1820_ioctl, i2c, data, &ves1820_info);
return 0;
}
......@@ -524,8 +539,7 @@ void __exit exit_ves1820 (void)
module_init(init_ves1820);
module_exit(exit_ves1820);
MODULE_DESCRIPTION("");
MODULE_AUTHOR("Ralph Metzler");
MODULE_DESCRIPTION("VES1820 DVB-C frontend driver");
MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");
MODULE_PARM(debug,"i");
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