From: Michael Hunold - [DVB] reafactor or add the following frontend drivers: at76c651, cx22700, ves1x93, ves1820, tda80xx, tda8083, cx22702, tda1004x, tda10021, cx24110, dvb_dummy_fe, l64781, mt312, mt352, stv0299, sp887x, stv0297, nxt6000, sp8870 - [DVB] remove dib3000mb from frontends, it's handled by the hw dependent driver now Signed-off-by: Michael Hunold Signed-off-by: Andrew Morton --- /dev/null | 657 -------- 25-akpm/drivers/media/dvb/frontends/at76c651.c | 468 ++---- 25-akpm/drivers/media/dvb/frontends/at76c651.h | 47 25-akpm/drivers/media/dvb/frontends/cx22700.c | 445 +++++ 25-akpm/drivers/media/dvb/frontends/cx22700.h | 41 25-akpm/drivers/media/dvb/frontends/cx22702.c | 794 ++-------- 25-akpm/drivers/media/dvb/frontends/cx22702.h | 46 25-akpm/drivers/media/dvb/frontends/cx24110.c | 699 +++------ 25-akpm/drivers/media/dvb/frontends/cx24110.h | 45 25-akpm/drivers/media/dvb/frontends/dib3000-common.h | 104 + 25-akpm/drivers/media/dvb/frontends/dib3000.h | 53 25-akpm/drivers/media/dvb/frontends/dib3000mb.c | 848 +++++------ 25-akpm/drivers/media/dvb/frontends/dib3000mb_priv.h | 467 ++++++ 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.c | 368 ++-- 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.h | 32 25-akpm/drivers/media/dvb/frontends/l64781.c | 607 ++++++++ 25-akpm/drivers/media/dvb/frontends/l64781.h | 43 25-akpm/drivers/media/dvb/frontends/mt312.c | 722 +++------ 25-akpm/drivers/media/dvb/frontends/mt312.h | 156 -- 25-akpm/drivers/media/dvb/frontends/mt312_priv.h | 162 ++ 25-akpm/drivers/media/dvb/frontends/mt352.c | 810 ++-------- 25-akpm/drivers/media/dvb/frontends/mt352.h | 156 -- 25-akpm/drivers/media/dvb/frontends/mt352_priv.h | 127 + 25-akpm/drivers/media/dvb/frontends/nxt6000.c | 632 ++------ 25-akpm/drivers/media/dvb/frontends/nxt6000.h | 303 ---- 25-akpm/drivers/media/dvb/frontends/nxt6000_priv.h | 265 +++ 25-akpm/drivers/media/dvb/frontends/sp8870.c | 613 ++++++++ 25-akpm/drivers/media/dvb/frontends/sp8870.h | 45 25-akpm/drivers/media/dvb/frontends/sp887x.c | 539 +++---- 25-akpm/drivers/media/dvb/frontends/sp887x.h | 29 25-akpm/drivers/media/dvb/frontends/stv0297.c | 774 ++++++++++ 25-akpm/drivers/media/dvb/frontends/stv0297.h | 41 25-akpm/drivers/media/dvb/frontends/stv0299.c | 1431 ++++--------------- 25-akpm/drivers/media/dvb/frontends/stv0299.h | 104 + 25-akpm/drivers/media/dvb/frontends/tda10021.c | 483 ++++++ 25-akpm/drivers/media/dvb/frontends/tda10021.h | 43 25-akpm/drivers/media/dvb/frontends/tda1004x.c | 1173 +++++---------- 25-akpm/drivers/media/dvb/frontends/tda1004x.h | 53 25-akpm/drivers/media/dvb/frontends/tda8083.c | 490 ++++++ 25-akpm/drivers/media/dvb/frontends/tda8083.h | 45 25-akpm/drivers/media/dvb/frontends/tda80xx.c | 749 +++++++++ 25-akpm/drivers/media/dvb/frontends/tda80xx.h | 51 25-akpm/drivers/media/dvb/frontends/ves1820.c | 607 ++------ 25-akpm/drivers/media/dvb/frontends/ves1820.h | 52 25-akpm/drivers/media/dvb/frontends/ves1x93.c | 657 +++----- 25-akpm/drivers/media/dvb/frontends/ves1x93.h | 50 46 files changed, 9503 insertions(+), 7623 deletions(-) diff -puN drivers/media/dvb/frontends/at76c651.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/at76c651.c --- 25/drivers/media/dvb/frontends/at76c651.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.373065136 -0800 +++ 25-akpm/drivers/media/dvb/frontends/at76c651.c 2004-11-21 17:13:23.408059816 -0800 @@ -24,9 +24,6 @@ * AT76C651 * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf * http://www.atmel.com/atmel/acrobat/doc1320.pdf - * - * TUA6010XS - * http://www.infineon.com/cgi/ecrm.dll/ecrm/scripts/public_download.jsp?oid=19512 */ #include @@ -36,48 +33,34 @@ #include #include #include - #include "dvb_frontend.h" +#include "at76c651.h" -#define FRONTEND_NAME "dvbfe_at76c651" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct at76c651_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; + const struct at76c651_config* config; -static struct dvb_frontend_info at76c651_info = { - .name = "Atmel AT76C651B with TUA6010XS", - .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_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER -}; + struct dvb_frontend frontend; -struct at76c651_state { + /* revision of the chip */ u8 revision; + + /* last QAM value set */ u8 qam; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; }; +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "at76c651: " args); \ + } while (0) + + #if ! defined(__powerpc__) static __inline__ int __ilog2(unsigned long x) { @@ -93,14 +76,14 @@ static __inline__ int __ilog2(unsigned l } #endif -static int at76c651_writereg(struct i2c_adapter *i2c, u8 reg, u8 data) +static int at76c651_writereg(struct at76c651_state* state, u8 reg, u8 data) { int ret; u8 buf[] = { reg, data }; struct i2c_msg msg = - { .addr = 0x1a >> 1, .flags = 0, .buf = buf, .len = 2 }; + { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; - ret = i2c_transfer(i2c, &msg, 1); + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) dprintk("%s: writereg error " @@ -112,16 +95,16 @@ static int at76c651_writereg(struct i2c_ return (ret != 1) ? -EREMOTEIO : 0; } -static u8 at76c651_readreg(struct i2c_adapter *i2c, u8 reg) +static u8 at76c651_readreg(struct at76c651_state* state, u8 reg) { int ret; u8 val; struct i2c_msg msg[] = { - { .addr = 0x1a >> 1, .flags = 0, .buf = ®, .len = 1 }, - { .addr = 0x1a >> 1, .flags = I2C_M_RD, .buf = &val, .len = 1 } + { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = &val, .len = 1 } }; - ret = i2c_transfer(i2c, msg, 2); + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); @@ -129,112 +112,64 @@ static u8 at76c651_readreg(struct i2c_ad return val; } -static int at76c651_reset(struct i2c_adapter *i2c) +static int at76c651_reset(struct at76c651_state* state) { - return at76c651_writereg(i2c, 0x07, 0x01); + return at76c651_writereg(state, 0x07, 0x01); } -static void at76c651_disable_interrupts(struct i2c_adapter *i2c) +static void at76c651_disable_interrupts(struct at76c651_state* state) { - at76c651_writereg(i2c, 0x0b, 0x00); + at76c651_writereg(state, 0x0b, 0x00); } static int at76c651_set_auto_config(struct at76c651_state *state) { - struct i2c_adapter *i2c = state->i2c; - /* * Autoconfig */ - at76c651_writereg(i2c, 0x06, 0x01); + at76c651_writereg(state, 0x06, 0x01); /* * Performance optimizations, should be done after autoconfig */ - at76c651_writereg(i2c, 0x10, 0x06); - at76c651_writereg(i2c, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10); - at76c651_writereg(i2c, 0x15, 0x28); - at76c651_writereg(i2c, 0x20, 0x09); - at76c651_writereg(i2c, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90); - at76c651_writereg(i2c, 0x30, 0x90); + at76c651_writereg(state, 0x10, 0x06); + at76c651_writereg(state, 0x11, ((state->qam == 5) || (state->qam == 7)) ? 0x12 : 0x10); + at76c651_writereg(state, 0x15, 0x28); + at76c651_writereg(state, 0x20, 0x09); + at76c651_writereg(state, 0x24, ((state->qam == 5) || (state->qam == 7)) ? 0xC0 : 0x90); + at76c651_writereg(state, 0x30, 0x90); if (state->qam == 5) - at76c651_writereg(i2c, 0x35, 0x2A); + at76c651_writereg(state, 0x35, 0x2A); /* * Initialize A/D-converter */ if (state->revision == 0x11) { - at76c651_writereg(i2c, 0x2E, 0x38); - at76c651_writereg(i2c, 0x2F, 0x13); + at76c651_writereg(state, 0x2E, 0x38); + at76c651_writereg(state, 0x2F, 0x13); } - at76c651_disable_interrupts(i2c); + at76c651_disable_interrupts(state); /* * Restart operation */ - at76c651_reset(i2c); + at76c651_reset(state); return 0; } -static void at76c651_set_bbfreq(struct i2c_adapter *i2c) +static void at76c651_set_bbfreq(struct at76c651_state* state) { - at76c651_writereg(i2c, 0x04, 0x3f); - at76c651_writereg(i2c, 0x05, 0xee); + at76c651_writereg(state, 0x04, 0x3f); + at76c651_writereg(state, 0x05, 0xee); } -static int at76c651_pll_write(struct i2c_adapter *i2c, u8 *buf, size_t len) -{ - int ret; - struct i2c_msg msg = - { .addr = 0xc2 >> 1, .flags = 0, .buf = buf, .len = len }; - - at76c651_writereg(i2c, 0x0c, 0xc3); - - ret = i2c_transfer(i2c, &msg, 1); - - at76c651_writereg(i2c, 0x0c, 0xc2); - - if (ret < 0) - return ret; - else if (ret != 1) - return -EREMOTEIO; - - return 0; -} - -static int tua6010_setfreq(struct i2c_adapter *i2c, u32 freq) -{ - u32 div; - u8 buf[4]; - u8 vu, p2, p1, p0; - - if ((freq < 50000000) || (freq > 900000000)) - return -EINVAL; - - div = (freq + 36125000) / 62500; - - if (freq > 400000000) - vu = 1, p2 = 1, p1 = 0, p0 = 1; - else if (freq > 140000000) - vu = 0, p2 = 1, p1 = 1, p0 = 0; - else - vu = 0, p2 = 0, p1 = 1, p0 = 1; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = (div >> 0) & 0xff; - buf[2] = 0x8e; - buf[3] = (vu << 7) | (p2 << 2) | (p1 << 1) | p0; - - return at76c651_pll_write(i2c, buf, 4); -} - -static int at76c651_set_symbol_rate(struct i2c_adapter *i2c, u32 symbol_rate) +static int at76c651_set_symbol_rate(struct at76c651_state* state, u32 symbol_rate) { u8 exponent; u32 mantissa; @@ -251,9 +186,9 @@ static int at76c651_set_symbol_rate(stru exponent = __ilog2((symbol_rate << 4) / 903125); mantissa = ((symbol_rate / 3125) * (1 << (24 - exponent))) / 289; - at76c651_writereg(i2c, 0x00, mantissa >> 13); - at76c651_writereg(i2c, 0x01, mantissa >> 5); - at76c651_writereg(i2c, 0x02, (mantissa << 3) | exponent); + at76c651_writereg(state, 0x00, mantissa >> 13); + at76c651_writereg(state, 0x01, mantissa >> 5); + at76c651_writereg(state, 0x02, (mantissa << 3) | exponent); return 0; } @@ -292,13 +227,12 @@ static int at76c651_set_qam(struct at76c } - return at76c651_writereg(state->i2c, 0x03, state->qam); + return at76c651_writereg(state, 0x03, state->qam); } -static int at76c651_set_inversion(struct i2c_adapter *i2c, - fe_spectral_inversion_t inversion) +static int at76c651_set_inversion(struct at76c651_state* state, fe_spectral_inversion_t inversion) { - u8 feciqinv = at76c651_readreg(i2c, 0x60); + u8 feciqinv = at76c651_readreg(state, 0x60); switch (inversion) { case INVERSION_OFF: @@ -318,59 +252,63 @@ static int at76c651_set_inversion(struct return -EINVAL; } - return at76c651_writereg(i2c, 0x60, feciqinv); + return at76c651_writereg(state, 0x60, feciqinv); } -static int at76c651_set_parameters(struct at76c651_state *state, + + + + + + + + +static int at76c651_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct i2c_adapter *i2c = state->i2c; int ret; + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - if ((ret = tua6010_setfreq(i2c, p->frequency))) - return ret; + at76c651_writereg(state, 0x0c, 0xc3); + state->config->pll_set(fe, p); + at76c651_writereg(state, 0x0c, 0xc2); - if ((ret = at76c651_set_symbol_rate(i2c, p->u.qam.symbol_rate))) + if ((ret = at76c651_set_symbol_rate(state, p->u.qam.symbol_rate))) return ret; - if ((ret = at76c651_set_inversion(i2c, p->inversion))) + if ((ret = at76c651_set_inversion(state, p->inversion))) return ret; return at76c651_set_auto_config(state); } -static int at76c651_set_defaults(struct at76c651_state *state) +static int at76c651_set_defaults(struct dvb_frontend* fe) { - struct i2c_adapter *i2c = state->i2c; + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - at76c651_set_symbol_rate(i2c, 6900000); + at76c651_set_symbol_rate(state, 6900000); at76c651_set_qam(state, QAM_64); - at76c651_set_bbfreq(i2c); + at76c651_set_bbfreq(state); at76c651_set_auto_config(state); - return 0; + if (state->config->pll_init) { + at76c651_writereg(state, 0x0c, 0xc3); + state->config->pll_init(fe); + at76c651_writereg(state, 0x0c, 0xc2); } -static int at76c651_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct at76c651_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy(arg, &at76c651_info, sizeof (struct dvb_frontend_info)); - break; + return 0; +} - case FE_READ_STATUS: +static int at76c651_read_status(struct dvb_frontend* fe, fe_status_t* status) { - fe_status_t *status = arg; + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; u8 sync; /* * Bits: FEC, CAR, EQU, TIM, AGC2, AGC1, ADC, PLL (PLL=0) */ - sync = at76c651_readreg(i2c, 0x80); - + sync = at76c651_readreg(state, 0x80); *status = 0; if (sync & (0x04 | 0x10)) /* AGC1 || TIM */ @@ -388,186 +326,138 @@ static int at76c651_ioctl(struct dvb_fro if ((sync & 0xF0) == 0xF0) /* TIM && EQU && CAR && FEC */ *status |= FE_HAS_LOCK; - break; - + return 0; } - case FE_READ_BER: +static int at76c651_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = arg; - *ber = (at76c651_readreg(i2c, 0x81) & 0x0F) << 16; - *ber |= at76c651_readreg(i2c, 0x82) << 8; - *ber |= at76c651_readreg(i2c, 0x83); + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; + + *ber = (at76c651_readreg(state, 0x81) & 0x0F) << 16; + *ber |= at76c651_readreg(state, 0x82) << 8; + *ber |= at76c651_readreg(state, 0x83); *ber *= 10; - break; - } - case FE_READ_SIGNAL_STRENGTH: - { - u8 gain = ~at76c651_readreg(i2c, 0x91); - *(u16 *) arg = (gain << 8) | gain; - break; + return 0; } - case FE_READ_SNR: - *(u16 *)arg = 0xFFFF - - ((at76c651_readreg(i2c, 0x8F) << 8) | - at76c651_readreg(i2c, 0x90)); - break; - - case FE_READ_UNCORRECTED_BLOCKS: - *(u32 *)arg = at76c651_readreg(i2c, 0x82); - break; - - case FE_SET_FRONTEND: - return at76c651_set_parameters(state, arg); - - case FE_GET_FRONTEND: - break; - - case FE_SLEEP: - break; - - case FE_INIT: - return at76c651_set_defaults(state); - - case FE_GET_TUNE_SETTINGS: - { - struct dvb_frontend_tune_settings *fesettings = arg; - fesettings->min_delay_ms = 50; - fesettings->step_size = 0; - fesettings->max_drift = 0; - break; - } +static int at76c651_read_signal_strength(struct dvb_frontend* fe, u16* strength) + { + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - default: - return -ENOIOCTLCMD; - } + u8 gain = ~at76c651_readreg(state, 0x91); + *strength = (gain << 8) | gain; return 0; } -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +static int at76c651_read_snr(struct dvb_frontend* fe, u16* snr) { - struct at76c651_state *state; - struct i2c_client *client; - int ret; - - if (at76c651_readreg(adapter, 0x0E) != 0x65) - return -ENODEV; + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - if (!(state = kmalloc(sizeof(struct at76c651_state), GFP_KERNEL))) - return -ENOMEM; + *snr = 0xFFFF - + ((at76c651_readreg(state, 0x8F) << 8) | + at76c651_readreg(state, 0x90)); - state->i2c = adapter; - state->revision = at76c651_readreg(adapter, 0x0F) & 0xFE; - - switch (state->revision) { - case 0x10: - at76c651_info.name[14] = 'A'; - break; - case 0x11: - at76c651_info.name[14] = 'B'; - break; - default: - kfree(state); - return -ENODEV; - } - - if (!(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; - } - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = 0x1a >> 1; - i2c_set_clientdata(client, state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(state); - kfree(client); - return ret; - } - - BUG_ON(!state->dvb); - - ret = dvb_register_frontend(at76c651_ioctl, state->dvb, state, - &at76c651_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; - } return 0; - } +} -static int detach_client(struct i2c_client *client) -{ - struct at76c651_state *state = i2c_get_clientdata(client); +static int at76c651_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) + { + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; - dvb_unregister_frontend(at76c651_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); + *ucblocks = at76c651_readreg(state, 0x82); return 0; } -static int command(struct i2c_client *client, unsigned int cmd, void *arg) +static int at76c651_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *fesettings) { - struct at76c651_state *state = i2c_get_clientdata(client); - - switch (cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - + fesettings->min_delay_ms = 50; + fesettings->step_size = 0; + fesettings->max_drift = 0; return 0; -} + } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_AT76C651, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; +static void at76c651_release(struct dvb_frontend* fe) +{ + struct at76c651_state* state = (struct at76c651_state*) fe->demodulator_priv; + kfree(state); + } -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; +static struct dvb_frontend_ops at76c651_ops; -static int __init at76c651_init(void) +struct dvb_frontend* at76c651_attach(const struct at76c651_config* config, + struct i2c_adapter* i2c) { - return i2c_add_driver(&driver); -} + struct at76c651_state* state = NULL; -static void __exit at76c651_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "at76c651: driver deregistration failed.\n"); -} + /* allocate memory for the internal state */ + state = (struct at76c651_state*) kmalloc(sizeof(struct at76c651_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->qam = 0; + + /* check if the demod is there */ + if (at76c651_readreg(state, 0x0e) != 0x65) goto error; + + /* finalise state setup */ + state->i2c = i2c; + state->revision = at76c651_readreg(state, 0x0f) & 0xfe; + memcpy(&state->ops, &at76c651_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops at76c651_ops = { + + .info = { + .name = "Atmel AT76C651B DVB-C", + .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, + .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_MUTE_TS | FE_CAN_QAM_256 | FE_CAN_RECOVER + }, + + .release = at76c651_release, + + .init = at76c651_set_defaults, + + .set_frontend = at76c651_set_parameters, + .get_tune_settings = at76c651_get_tune_settings, + + .read_status = at76c651_read_status, + .read_ber = at76c651_read_ber, + .read_signal_strength = at76c651_read_signal_strength, + .read_snr = at76c651_read_snr, + .read_ucblocks = at76c651_read_ucblocks, +}; -module_init(at76c651_init); -module_exit(at76c651_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("at76c651/tua6010xs dvb-c frontend driver"); +MODULE_DESCRIPTION("Atmel AT76C651 DVB-C Demodulator Driver"); MODULE_AUTHOR("Andreas Oberritter "); MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(at76c651_attach); diff -puN /dev/null drivers/media/dvb/frontends/at76c651.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/at76c651.h 2004-11-21 17:13:23.408059816 -0800 @@ -0,0 +1,47 @@ +/* + * at76c651.c + * + * Atmel DVB-C Frontend Driver (at76c651) + * + * Copyright (C) 2001 fnbrd + * & 2002-2004 Andreas Oberritter + * & 2003 Wolfram Joost + * + * 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. + * + * AT76C651 + * http://www.nalanda.nitc.ac.in/industry/datasheets/atmel/acrobat/doc1293.pdf + * http://www.atmel.com/atmel/acrobat/doc1320.pdf + */ + +#ifndef AT76C651_H +#define AT76C651_H + +#include + +struct at76c651_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* at76c651_attach(const struct at76c651_config* config, + struct i2c_adapter* i2c); + +#endif // AT76C651_H diff -puN /dev/null drivers/media/dvb/frontends/cx22700.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/cx22700.c 2004-11-21 17:13:23.410059512 -0800 @@ -0,0 +1,445 @@ +#/* + Conexant cx22700 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler + + 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 +#include +#include +#include +#include +#include +#include "dvb_frontend.h" +#include "cx22700.h" + + +struct cx22700_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct cx22700_config* config; + + struct dvb_frontend frontend; +}; + + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "cx22700: " args); \ + } while (0) + +static u8 init_tab [] = { + 0x04, 0x10, + 0x05, 0x09, + 0x06, 0x00, + 0x08, 0x04, + 0x09, 0x00, + 0x0a, 0x01, + 0x15, 0x40, + 0x16, 0x10, + 0x17, 0x87, + 0x18, 0x17, + 0x1a, 0x10, + 0x25, 0x04, + 0x2e, 0x00, + 0x39, 0x00, + 0x3a, 0x04, + 0x45, 0x08, + 0x46, 0x02, + 0x47, 0x05, +}; + + +static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + dprintk ("%s\n", __FUNCTION__); + + ret = i2c_transfer (state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", + __FUNCTION__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int cx22700_readreg (struct cx22700_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + dprintk ("%s\n", __FUNCTION__); + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) return -EIO; + + return b1[0]; +} + +static int cx22700_set_inversion (struct cx22700_state* state, int inversion) +{ + u8 val; + + dprintk ("%s\n", __FUNCTION__); + + switch (inversion) { + case INVERSION_AUTO: + return -EOPNOTSUPP; + case INVERSION_ON: + val = cx22700_readreg (state, 0x09); + return cx22700_writereg (state, 0x09, val | 0x01); + case INVERSION_OFF: + val = cx22700_readreg (state, 0x09); + return cx22700_writereg (state, 0x09, val & 0xfe); + default: + return -EINVAL; + } +} + +static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_parameters *p) +{ + static const u8 qam_tab [4] = { 0, 1, 0, 2 }; + static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 }; + u8 val; + + dprintk ("%s\n", __FUNCTION__); + + if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8) + return -EINVAL; + + if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8) + + if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5) + return -EINVAL; + + if (p->guard_interval < GUARD_INTERVAL_1_32 || + p->guard_interval > GUARD_INTERVAL_1_4) + return -EINVAL; + + if (p->transmission_mode != TRANSMISSION_MODE_2K && + p->transmission_mode != TRANSMISSION_MODE_8K) + return -EINVAL; + + if (p->constellation != QPSK && + p->constellation != QAM_16 && + p->constellation != QAM_64) + return -EINVAL; + + if (p->hierarchy_information < HIERARCHY_NONE || + p->hierarchy_information > HIERARCHY_4) + return -EINVAL; + + if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ) + return -EINVAL; + + if (p->bandwidth == BANDWIDTH_7_MHZ) + cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10)); + else + cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10)); + + val = qam_tab[p->constellation - QPSK]; + val |= p->hierarchy_information - HIERARCHY_NONE; + + cx22700_writereg (state, 0x04, val); + + val = fec_tab[p->code_rate_HP - FEC_1_2] << 3; + val |= fec_tab[p->code_rate_LP - FEC_1_2]; + + cx22700_writereg (state, 0x05, val); + + val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2; + val |= p->transmission_mode - TRANSMISSION_MODE_2K; + + cx22700_writereg (state, 0x06, val); + + cx22700_writereg (state, 0x08, 0x04 | 0x02); /* use user tps parameters */ + cx22700_writereg (state, 0x08, 0x04); /* restart aquisition */ + + return 0; +} + +static int cx22700_get_tps (struct cx22700_state* state, struct dvb_ofdm_parameters *p) +{ + static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 }; + static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4, + FEC_5_6, FEC_7_8 }; + u8 val; + + dprintk ("%s\n", __FUNCTION__); + + if (!(cx22700_readreg(state, 0x07) & 0x20)) /* tps valid? */ + return -EAGAIN; + + val = cx22700_readreg (state, 0x01); + + if ((val & 0x7) > 4) + p->hierarchy_information = HIERARCHY_AUTO; + else + p->hierarchy_information = HIERARCHY_NONE + (val & 0x7); + + if (((val >> 3) & 0x3) > 2) + p->constellation = QAM_AUTO; + else + p->constellation = qam_tab[(val >> 3) & 0x3]; + + val = cx22700_readreg (state, 0x02); + + if (((val >> 3) & 0x07) > 4) + p->code_rate_HP = FEC_AUTO; + else + p->code_rate_HP = fec_tab[(val >> 3) & 0x07]; + + if ((val & 0x07) > 4) + p->code_rate_LP = FEC_AUTO; + else + p->code_rate_LP = fec_tab[val & 0x07]; + + val = cx22700_readreg (state, 0x03); + + p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3); + p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1); + + return 0; +} + + + + + + + + + + + +static int cx22700_init (struct dvb_frontend* fe) + +{ struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + int i; + + dprintk("cx22700_init: init chip\n"); + + cx22700_writereg (state, 0x00, 0x02); /* soft reset */ + cx22700_writereg (state, 0x00, 0x00); + + msleep(10); + + for (i=0; iconfig->pll_init) { + cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ + state->config->pll_init(fe); + cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ + } + + return 0; +} + +static int cx22700_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + u8 sync = cx22700_readreg (state, 0x07); + + *status = 0; + + if (rs_ber < 0xff00) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x20) + *status |= FE_HAS_CARRIER; + + if (sync & 0x10) + *status |= FE_HAS_VITERBI; + + if (sync & 0x10) + *status |= FE_HAS_SYNC; + + if (*status == 0x0f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + *ber = cx22700_readreg (state, 0x0c) & 0x7f; + cx22700_writereg (state, 0x0c, 0x00); + + return 0; +} + +static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + *signal_strength = ~rs_ber; + + return 0; +} + +static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) + | (cx22700_readreg (state, 0x0e) << 1); + *snr = ~rs_ber; + + return 0; +} + +static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + *ucblocks = cx22700_readreg (state, 0x0f); + cx22700_writereg (state, 0x0f, 0x00); + + return 0; +} + +static int cx22700_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + + cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ + cx22700_writereg (state, 0x00, 0x00); + + cx22700_writereg (state, 0x0a, 0x00); /* open i2c bus switch */ + state->config->pll_set(fe, p); + cx22700_writereg (state, 0x0a, 0x01); /* close i2c bus switch */ + cx22700_set_inversion (state, p->inversion); + cx22700_set_tps (state, &p->u.ofdm); + cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ + cx22700_writereg (state, 0x00, 0x01); /* restart acquire */ + + return 0; +} + +static int cx22700_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + u8 reg09 = cx22700_readreg (state, 0x09); + + p->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF; + return cx22700_get_tps (state, &p->u.ofdm); +} + +static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 150; + fesettings->step_size = 166667; + fesettings->max_drift = 166667*2; + return 0; +} + +static void cx22700_release(struct dvb_frontend* fe) +{ + struct cx22700_state* state = (struct cx22700_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops cx22700_ops; + +struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c) +{ + struct cx22700_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct cx22700_state*) kmalloc(sizeof(struct cx22700_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &cx22700_ops, sizeof(struct dvb_frontend_ops)); + + /* check if the demod is there */ + if (cx22700_readreg(state, 0x07) < 0) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops cx22700_ops = { + + .info = { + .name = "Conexant CX22700 DVB-T", + .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 | + FE_CAN_RECOVER + }, + + .release = cx22700_release, + + .init = cx22700_init, + + .set_frontend = cx22700_set_frontend, + .get_frontend = cx22700_get_frontend, + .get_tune_settings = cx22700_get_tune_settings, + + .read_status = cx22700_read_status, + .read_ber = cx22700_read_ber, + .read_signal_strength = cx22700_read_signal_strength, + .read_snr = cx22700_read_snr, + .read_ucblocks = cx22700_read_ucblocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver"); +MODULE_AUTHOR("Holger Waechtler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(cx22700_attach); diff -puN /dev/null drivers/media/dvb/frontends/cx22700.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/cx22700.h 2004-11-21 17:13:23.411059360 -0800 @@ -0,0 +1,41 @@ +/* + Conexant CX22700 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler + + 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. + +*/ + +#ifndef CX22700_H +#define CX22700_H + +#include + +struct cx22700_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, + struct i2c_adapter* i2c); + +#endif // CX22700_H diff -puN drivers/media/dvb/frontends/cx22702.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/cx22702.c --- 25/drivers/media/dvb/frontends/cx22702.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.375064832 -0800 +++ 25-akpm/drivers/media/dvb/frontends/cx22702.c 2004-11-21 17:13:23.416058600 -0800 @@ -1,8 +1,8 @@ /* - Conexant 22702 DVB OFDM frontend driver + Conexant 22702 DVB OFDM demodulator driver based on: - Alps TDMB7 DVB OFDM frontend driver + Alps TDMB7 DVB OFDM demodulator driver Copyright (C) 2001-2002 Convergence Integrated Media GmbH Holger Waechtler @@ -31,44 +31,28 @@ #include #include #include - #include "dvb_frontend.h" +#include "cx22702.h" -#define FRONTEND_NAME "dvbfe_cx22702" -#define I2C_EEPROM_SLAVE_ADDR 0x50 +struct cx22702_state { -#define PLLTYPE_DTT7592 1 -#define PLLTYPE_DTT7595 2 -#define PLLTYPE_DTT7579 3 + struct i2c_adapter* i2c; -static int debug = 0; + struct dvb_frontend_ops ops; -#define dprintk if (debug) printk + /* configuration settings */ + const struct cx22702_config* config; -static struct dvb_frontend_info cx22702_info = { - .name = "CX22702 Demod Thomson 759x/7579 PLL", - .type = FE_OFDM, - .frequency_min = 177000000, - .frequency_max = 858000000, - .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 | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER -}; + struct dvb_frontend frontend; -struct cx22702_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - struct dvb_frontend_info cx22702_info; - char pll_type; - int pll_addr; - int demod_addr; + /* previous uncorrected block counter */ u8 prevUCBlocks; }; +static int debug = 0; +#define dprintk if (debug) printk + /* Register values to initialise the demod */ static u8 init_tab [] = { 0x00, 0x00, /* Stop aquisition */ @@ -99,15 +83,13 @@ static u8 init_tab [] = { 0xfd, 0x00, }; -static struct i2c_client client_template; - -static int cx22702_writereg (struct i2c_adapter *i2c, int demod_addr, u8 reg, u8 data) +static int cx22702_writereg (struct cx22702_state* state, u8 reg, u8 data) { int ret; u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = demod_addr, .flags = 0, .buf = buf, .len = 2 }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; - ret = i2c_transfer(i2c, &msg, 1); + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", @@ -116,17 +98,17 @@ static int cx22702_writereg (struct i2c_ return (ret != 1) ? -1 : 0; } -static u8 cx22702_readreg (struct i2c_adapter *i2c, int demod_addr, u8 reg) +static u8 cx22702_readreg (struct cx22702_state* state, u8 reg) { int ret; u8 b0 [] = { reg }; u8 b1 [] = { 0 }; struct i2c_msg msg [] = { - { .addr = demod_addr, .flags = 0, .buf = b0, .len = 1 }, - { .addr = demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - ret = i2c_transfer(i2c, msg, 2); + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); @@ -134,232 +116,121 @@ static u8 cx22702_readreg (struct i2c_ad return b1[0]; } -static int pll_readreg(struct i2c_adapter *i2c, int pll_addr, int demod_addr, u8 reg) +static int cx22702_set_inversion (struct cx22702_state *state, int inversion) { - u8 b0 [] = { reg }; - u8 b1 [] = { 0 }; + u8 val; - struct i2c_msg msg [] = { - { .addr = pll_addr, .flags = 0, .buf = b0, .len = 1 }, - { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } - }; - - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) &0xfe); // Enable PLL bus - if (i2c_transfer(i2c, msg, 2) != 2) { - printk ("%s i2c pll request failed\n", __FUNCTION__); - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus - return -ENODEV; - } - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus + switch (inversion) { - return b1[0]; -} + case INVERSION_AUTO: + return -EOPNOTSUPP; -static int pll_write (struct i2c_adapter *i2c, int pll_addr, int demod_addr, u8 data [4]) -{ - int ret=0; - struct i2c_msg msg = { .addr = pll_addr, .flags = 0, .buf = data, .len = 4 }; + case INVERSION_ON: + val = cx22702_readreg (state, 0x0C); + return cx22702_writereg (state, 0x0C, val | 0x01); - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) &0xfe); // Enable PLL bus - ret = i2c_transfer(i2c, &msg, 1); - cx22702_writereg (i2c, demod_addr, 0x0D, cx22702_readreg(i2c,demod_addr,0x0D) | 1); // Disable PLL bus + case INVERSION_OFF: + val = cx22702_readreg (state, 0x0C); + return cx22702_writereg (state, 0x0C, val & 0xfe); - if (ret != 1) - printk("%s: i/o error (addr == 0x%02x, ret == %i)\n", __FUNCTION__, msg.addr, ret); + default: + return -EINVAL; + + } - return (ret != 1) ? -1 : 0; } -static int pll_dtt759x_set_tv_freq (struct i2c_adapter *i2c, struct cx22702_state *state, u32 freq, int bandwidth) +/* Retrieve the demod settings */ +static int cx22702_get_tps (struct cx22702_state *state, struct dvb_ofdm_parameters *p) { - int ret; - - u32 div = (freq + 36166667) / 166666; - - /* dividerhigh, dividerlow, control, bandwidth switch tuner args */ - unsigned char buf [4] = { - (div >> 8) & 0x7f, - div & 0xff, - 0x84, - 0x00 - }; + u8 val; - if(freq < 470000000) { - buf[3] = 0x02; - } else { - buf[3] = 0x08; - } + /* Make sure the TPS regs are valid */ + if (!(cx22702_readreg(state, 0x0A) & 0x20)) + return -EAGAIN; - if(bandwidth == BANDWIDTH_7_MHZ) { - buf[3] |= 0x10; + val = cx22702_readreg (state, 0x01); + switch( (val&0x18)>>3) { + case 0: p->constellation = QPSK; break; + case 1: p->constellation = QAM_16; break; + case 2: p->constellation = QAM_64; break; } - - // Now compensate for the charge pump osc - if(freq <= 264000000) { - buf[2] = buf[2] | 0x30; - } else if (freq <= 735000000) { - buf[2] = buf[2] | 0x38; - } else if (freq <= 835000000) { - buf[2] = buf[2] | 0x70; - } else if (freq <= 896000000) { - buf[2] = buf[2] | 0x78; + switch( val&0x07 ) { + case 0: p->hierarchy_information = HIERARCHY_NONE; break; + case 1: p->hierarchy_information = HIERARCHY_1; break; + case 2: p->hierarchy_information = HIERARCHY_2; break; + case 3: p->hierarchy_information = HIERARCHY_4; break; } - dprintk ("%s: freq == %i, div == 0x%04x\n", __FUNCTION__, (int) freq, (int) div); - - ret= pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: first pll_write failed\n",__FUNCTION__); - return ret; - } - /* Set the AGC during search */ - buf[2]=(buf[2] & 0xc7) | 0x18; - buf[3]=0xa0; - ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: second pll_write failed\n",__FUNCTION__); - return ret; + val = cx22702_readreg (state, 0x02); + switch( (val&0x38)>>3 ) { + case 0: p->code_rate_HP = FEC_1_2; break; + case 1: p->code_rate_HP = FEC_2_3; break; + case 2: p->code_rate_HP = FEC_3_4; break; + case 3: p->code_rate_HP = FEC_5_6; break; + case 4: p->code_rate_HP = FEC_7_8; break; } - - /* Tuner needs a small amount of time */ - msleep(100); - - /* Set the AGC post-search */ - buf[3]=0x20; - ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: third pll_write failed\n",__FUNCTION__); - return ret; + switch( val&0x07 ) { + case 0: p->code_rate_LP = FEC_1_2; break; + case 1: p->code_rate_LP = FEC_2_3; break; + case 2: p->code_rate_LP = FEC_3_4; break; + case 3: p->code_rate_LP = FEC_5_6; break; + case 4: p->code_rate_LP = FEC_7_8; break; } - return ret; + val = cx22702_readreg (state, 0x03); + switch( (val&0x0c)>>2 ) { + case 0: p->guard_interval = GUARD_INTERVAL_1_32; break; + case 1: p->guard_interval = GUARD_INTERVAL_1_16; break; + case 2: p->guard_interval = GUARD_INTERVAL_1_8; break; + case 3: p->guard_interval = GUARD_INTERVAL_1_4; break; } - -static int pll_dtt7579_set_tv_freq (struct i2c_adapter *i2c, struct cx22702_state *state, u32 freq, int bandwidth) -{ - int ret; - - u32 div = (freq + 36166667) / 166666; - - /* dividerhigh, dividerlow */ - unsigned char buf [4] = { - div >> 8, - div & 0xff, - 0x00, - 0x00 - }; - - // FIXME: bandwidth setting unknown - - // Now compensate for the charge pump osc - if(freq <= 506000000) { - buf[2] = 0xb4; - buf[3] = 0x02; - } else if (freq <= 735000000) { - buf[2] = 0xbc; - buf[3] = 0x08; - } else if (freq <= 835000000) { - buf[2] = 0xf4; - buf[3] = 0x08; - } else if (freq <= 896000000) { - buf[2] = 0xfc; - buf[3] = 0x08; - } - - dprintk ("%s: freq == %i, div == 0x%04x\n", __FUNCTION__, (int) freq, (int) div); - - ret= pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: first pll_write failed\n",__FUNCTION__); - return ret; - } - - /* Set the AGC to search */ - buf[2]=(buf[2] & 0xdc) | 0x9c; - buf[3]=0xa0; - ret=pll_write (i2c, state->pll_addr, state->demod_addr, buf); - if(ret<0) { - dprintk ("%s: second pll_write failed\n",__FUNCTION__); - return ret; + switch( val&0x03 ) { + case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break; } - return ret; - + return 0; } -/* Reset the demod hardware and reset all of the configuration registers - to a default state. */ -static int cx22702_init (struct i2c_adapter *i2c, struct cx22702_state *state) -{ - int i; - - cx22702_writereg (i2c, state->demod_addr, 0x00, 0x02); - msleep(10); - for (i=0; idemod_addr, init_tab[i], init_tab[i+1]); - return 0; -} -static int cx22702_set_inversion (struct i2c_adapter *i2c, struct cx22702_state *state, int inversion) -{ - u8 val; - switch (inversion) { - case INVERSION_AUTO: - return -EOPNOTSUPP; - case INVERSION_ON: - val = cx22702_readreg (i2c, state->demod_addr, 0x0C); - return cx22702_writereg (i2c, state->demod_addr, 0x0C, val | 0x01); - case INVERSION_OFF: - val = cx22702_readreg (i2c, state->demod_addr, 0x0C); - return cx22702_writereg (i2c, state->demod_addr, 0x0C, val & 0xfe); - default: - return -EINVAL; - } -} /* Talk to the demod, set the FEC, GUARD, QAM settings etc */ -static int cx22702_set_tps (struct i2c_adapter *i2c, struct cx22702_state *state, - struct dvb_frontend_parameters *p) +static int cx22702_set_tps (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { u8 val; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; /* set PLL */ - switch(state->pll_type) { - case PLLTYPE_DTT7592: - case PLLTYPE_DTT7595: - pll_dtt759x_set_tv_freq (i2c, state, p->frequency, p->u.ofdm.bandwidth); - break; - - case PLLTYPE_DTT7579: - pll_dtt7579_set_tv_freq (i2c, state, p->frequency, p->u.ofdm.bandwidth); - break; - } + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe); + state->config->pll_set(fe, p); + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1); /* set inversion */ - cx22702_set_inversion (i2c, state, p->inversion); + cx22702_set_inversion (state, p->inversion); /* set bandwidth */ switch(p->u.ofdm.bandwidth) { case BANDWIDTH_6_MHZ: - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xcf) | 0x20 ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x20 ); break; case BANDWIDTH_7_MHZ: - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xcf) | 0x10 ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xcf) | 0x10 ); break; case BANDWIDTH_8_MHZ: - cx22702_writereg(i2c, state->demod_addr, 0x0C, cx22702_readreg(i2c, state->demod_addr, 0x0C) &0xcf ); + cx22702_writereg(state, 0x0C, cx22702_readreg(state, 0x0C) &0xcf ); break; default: dprintk ("%s: invalid bandwidth\n",__FUNCTION__); @@ -378,12 +249,12 @@ static int cx22702_set_tps (struct i2c_a (p->u.ofdm.transmission_mode==TRANSMISSION_MODE_AUTO) ) { /* TPS Source - use hardware driven values */ - cx22702_writereg(i2c, state->demod_addr, 0x06, 0x10); - cx22702_writereg(i2c, state->demod_addr, 0x07, 0x9); - cx22702_writereg(i2c, state->demod_addr, 0x08, 0xC1); - cx22702_writereg(i2c, state->demod_addr, 0x0B, cx22702_readreg(i2c, state->demod_addr, 0x0B) & 0xfc ); - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xBF) | 0x40 ); - cx22702_writereg(i2c, state->demod_addr, 0x00, 0x01); /* Begin aquisition */ + cx22702_writereg(state, 0x06, 0x10); + cx22702_writereg(state, 0x07, 0x9); + cx22702_writereg(state, 0x08, 0xC1); + cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) & 0xfc ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 ); + cx22702_writereg(state, 0x00, 0x01); /* Begin aquisition */ printk("%s: Autodetecting\n",__FUNCTION__); return 0; } @@ -407,7 +278,7 @@ static int cx22702_set_tps (struct i2c_a dprintk ("%s: invalid hierarchy\n",__FUNCTION__); return -EINVAL; } - cx22702_writereg (i2c, state->demod_addr, 0x06, val); + cx22702_writereg (state, 0x06, val); val=0; switch(p->u.ofdm.code_rate_HP) { @@ -432,7 +303,7 @@ static int cx22702_set_tps (struct i2c_a dprintk ("%s: invalid code_rate_LP\n",__FUNCTION__); return -EINVAL; } - cx22702_writereg (i2c, state->demod_addr, 0x07, val); + cx22702_writereg (state, 0x07, val); val=0; switch(p->u.ofdm.guard_interval) { @@ -451,93 +322,52 @@ static int cx22702_set_tps (struct i2c_a dprintk ("%s: invalid transmission_mode\n",__FUNCTION__); return -EINVAL; } - cx22702_writereg (i2c, state->demod_addr, 0x08, val); - cx22702_writereg(i2c, state->demod_addr, 0x0B, (cx22702_readreg(i2c, state->demod_addr, 0x0B) & 0xfc) | 0x02 ); - cx22702_writereg(i2c, state->demod_addr, 0x0C, (cx22702_readreg(i2c, state->demod_addr, 0x0C) & 0xBF) | 0x40 ); + cx22702_writereg(state, 0x08, val); + cx22702_writereg(state, 0x0B, (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02 ); + cx22702_writereg(state, 0x0C, (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40 ); /* Begin channel aquisition */ - cx22702_writereg(i2c, state->demod_addr, 0x00, 0x01); + cx22702_writereg(state, 0x00, 0x01); return 0; } -/* Retrieve the demod settings */ -static int cx22702_get_tps (struct i2c_adapter *i2c, struct cx22702_state *state, - struct dvb_ofdm_parameters *p) -{ - u8 val; - /* Make sure the TPS regs are valid */ - if (!(cx22702_readreg(i2c, state->demod_addr, 0x0A) & 0x20)) - return -EAGAIN; +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +static int cx22702_init (struct dvb_frontend* fe) +{ + int i; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - val = cx22702_readreg (i2c, state->demod_addr, 0x01); - switch( (val&0x18)>>3) { - case 0: p->constellation = QPSK; break; - case 1: p->constellation = QAM_16; break; - case 2: p->constellation = QAM_64; break; - } - switch( val&0x07 ) { - case 0: p->hierarchy_information = HIERARCHY_NONE; break; - case 1: p->hierarchy_information = HIERARCHY_1; break; - case 2: p->hierarchy_information = HIERARCHY_2; break; - case 3: p->hierarchy_information = HIERARCHY_4; break; - } + cx22702_writereg (state, 0x00, 0x02); + msleep(10); - val = cx22702_readreg (i2c, state->demod_addr, 0x02); - switch( (val&0x38)>>3 ) { - case 0: p->code_rate_HP = FEC_1_2; break; - case 1: p->code_rate_HP = FEC_2_3; break; - case 2: p->code_rate_HP = FEC_3_4; break; - case 3: p->code_rate_HP = FEC_5_6; break; - case 4: p->code_rate_HP = FEC_7_8; break; - } - switch( val&0x07 ) { - case 0: p->code_rate_LP = FEC_1_2; break; - case 1: p->code_rate_LP = FEC_2_3; break; - case 2: p->code_rate_LP = FEC_3_4; break; - case 3: p->code_rate_LP = FEC_5_6; break; - case 4: p->code_rate_LP = FEC_7_8; break; - } + for (i=0; idemod_addr, 0x03); - switch( (val&0x0c)>>2 ) { - case 0: p->guard_interval = GUARD_INTERVAL_1_32; break; - case 1: p->guard_interval = GUARD_INTERVAL_1_16; break; - case 2: p->guard_interval = GUARD_INTERVAL_1_8; break; - case 3: p->guard_interval = GUARD_INTERVAL_1_4; break; - } - switch( val&0x03 ) { - case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break; - case 1: p->transmission_mode = TRANSMISSION_MODE_8K; break; + /* init PLL */ + if (state->config->pll_init) { + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) &0xfe); + state->config->pll_init(fe); + cx22702_writereg (state, 0x0D, cx22702_readreg(state,0x0D) | 1); } return 0; } -static int cx22702_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int cx22702_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct cx22702_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; u8 reg0A; u8 reg23; - u8 ucblocks; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &state->cx22702_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; *status = 0; - reg0A = cx22702_readreg (i2c, state->demod_addr, 0x0A); - reg23 = cx22702_readreg (i2c, state->demod_addr, 0x23); + reg0A = cx22702_readreg (state, 0x0A); + reg23 = cx22702_readreg (state, 0x23); dprintk ("%s: status demod=0x%02x agc=0x%02x\n" ,__FUNCTION__,reg0A,reg23); @@ -554,347 +384,149 @@ static int cx22702_ioctl (struct dvb_fro if(reg23 < 0xf0) *status |= FE_HAS_SIGNAL; - break; - - } - - case FE_READ_BER: - if(cx22702_readreg (i2c, state->demod_addr, 0xE4) & 0x02) { - /* Realtime statistics */ - *((u32*) arg) = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7 - | (cx22702_readreg (i2c, state->demod_addr, 0xDF)&0x7F); - } else { - /* Averagtine statistics */ - *((u32*) arg) = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7 - | cx22702_readreg (i2c, state->demod_addr, 0xDF); + return 0; } - break; - case FE_READ_SIGNAL_STRENGTH: +static int cx22702_read_ber(struct dvb_frontend* fe, u32* ber) { - u16 ss = cx22702_readreg (i2c, state->demod_addr, 0x23); - *((u16*) arg) = ss; - break; - } + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - /* We don't have an register for this */ - /* We'll take the inverse of the BER register */ - case FE_READ_SNR: - { - u16 rs_ber=0; - if(cx22702_readreg (i2c, state->demod_addr, 0xE4) & 0x02) { + if(cx22702_readreg (state, 0xE4) & 0x02) { /* Realtime statistics */ - rs_ber = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 7 - | (cx22702_readreg (i2c, state->demod_addr, 0xDF)& 0x7F); + *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7 + | (cx22702_readreg (state, 0xDF)&0x7F); } else { - /* Averagine statistics */ - rs_ber = (cx22702_readreg (i2c, state->demod_addr, 0xDE) & 0x7F) << 8 - | cx22702_readreg (i2c, state->demod_addr, 0xDF); - } - *((u16*) arg) = ~rs_ber; - break; + /* Averagtine statistics */ + *ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7 + | cx22702_readreg (state, 0xDF); } - case FE_READ_UNCORRECTED_BLOCKS: - /* RS Uncorrectable Packet Count then reset */ - ucblocks = cx22702_readreg (i2c, state->demod_addr, 0xE3); - if (state->prevUCBlocks < ucblocks) *((u32*) arg) = (ucblocks - state->prevUCBlocks); - else *((u32*) arg) = state->prevUCBlocks - ucblocks; - state->prevUCBlocks = ucblocks; - break; - - case FE_SET_FRONTEND: - { - struct dvb_frontend_parameters *p = arg; - int ret; - - if((ret=cx22702_set_tps (i2c, state, p))<0) { - dprintk ("%s: set_tps failed ret=%d\n",__FUNCTION__,ret); - return ret; - } - break; + return 0; } - case FE_GET_FRONTEND: +static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) { - struct dvb_frontend_parameters *p = arg; - u8 reg0C = cx22702_readreg (i2c, state->demod_addr, 0x0C); - - p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; - return cx22702_get_tps (i2c, state, &p->u.ofdm); - } - - case FE_INIT: - return cx22702_init (i2c, state); + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - default: - return -EOPNOTSUPP; - }; + *signal_strength = cx22702_readreg (state, 0x23); return 0; } -/* Validate the eeprom contents, make sure content look ok. - Get the eeprom data. */ -static int cx22702_validate_eeprom(struct i2c_adapter *i2c, int* minfreq, int* pll_type, int* pll_addr, int* demod_addr) -{ - u8 b0 [] = { 0 }; - u8 b1 [128]; - u32 model=0; - u8 tuner=0; - int i,j; - - struct i2c_msg msg [] = { - { .addr = I2C_EEPROM_SLAVE_ADDR, .flags = 0, .buf = b0, .len = 1 }, - { .addr = I2C_EEPROM_SLAVE_ADDR, .flags = I2C_M_RD, .buf = b1, .len = 128 } - }; - - if (i2c_transfer(i2c, msg, 2) != 2) { - printk ("%s i2c eeprom request failed\n", __FUNCTION__); - return -ENODEV; - } - - if(debug) { - dprintk ("i2c eeprom content:\n"); - j=0; - for(i=0;i<128;i++) { - dprintk("%02x ",b1[i]); - if(j++==16) { - dprintk("\n"); - j=0; - } - } - dprintk("\n"); - } +static int cx22702_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - if( (b1[8]!=0x84) || (b1[10]!=0x00) ) { - printk ("%s eeprom content is not valid\n", __FUNCTION__); - return -ENODEV; - } - - /* Make sure we support the board model */ - model = b1[0x1f] << 24 | b1[0x1e] << 16 | b1[0x1d] << 8 | b1[0x1c]; - switch(model) { - case 90002: - case 90500: - case 90501: - dprintk ("%s: Model #%d\n",__FUNCTION__,model); - break; - default: - printk ("%s: Unknown model #%d not supported\n",__FUNCTION__,model); - return -ENODEV; + u16 rs_ber=0; + if(cx22702_readreg (state, 0xE4) & 0x02) { + /* Realtime statistics */ + rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 7 + | (cx22702_readreg (state, 0xDF)& 0x7F); + } else { + /* Averagine statistics */ + rs_ber = (cx22702_readreg (state, 0xDE) & 0x7F) << 8 + | cx22702_readreg (state, 0xDF); } + *snr = ~rs_ber; - /* Make sure we support the tuner */ - tuner = b1[0x2d]; - switch(tuner) { - case 0x4B: - dprintk ("%s: Tuner Thomson DTT 7595\n",__FUNCTION__); - *minfreq = 177000000; - *pll_type = PLLTYPE_DTT7595; - break; - case 0x4C: - dprintk ("%s: Tuner Thomson DTT 7592\n",__FUNCTION__); - *minfreq = 474000000; - *pll_type = PLLTYPE_DTT7592; - break; - default: - printk ("%s: Unknown tuner 0x%02x not supported\n",__FUNCTION__,tuner); - return -ENODEV; - } - *pll_addr = 0x61; - *demod_addr = 0x43; return 0; } - -/* Validate the demod, make sure we understand the hardware */ -static int cx22702_validate_demod(struct i2c_adapter *i2c, int demod_addr) +static int cx22702_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { - u8 b0 [] = { 0x1f }; - u8 b1 [] = { 0 }; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - struct i2c_msg msg [] = { - { .addr = demod_addr, .flags = 0, .buf = b0, .len = 1 }, - { .addr = demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } - }; - - if (i2c_transfer(i2c, msg, 2) != 2) { - printk ("%s i2c demod request failed\n", __FUNCTION__); - return -ENODEV; - } + u8 _ucblocks; - if( (b1[0]!=0x3) ) { - printk ("%s i2c demod type 0x(%02x) not known\n", __FUNCTION__,b1[0]); - return -ENODEV; - } + /* RS Uncorrectable Packet Count then reset */ + _ucblocks = cx22702_readreg (state, 0xE3); + if (state->prevUCBlocks < _ucblocks) *ucblocks = (_ucblocks - state->prevUCBlocks); + else *ucblocks = state->prevUCBlocks - _ucblocks; + state->prevUCBlocks = _ucblocks; return 0; } -/* Validate the tuner PLL, make sure we understand the hardware */ -static int cx22702_validate_pll(struct i2c_adapter *i2c, int pll_addr, int demod_addr) +static int cx22702_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - int result=0; + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; - if( (result=pll_readreg(i2c,pll_addr,demod_addr,0xc2)) < 0) - return result; + u8 reg0C = cx22702_readreg (state, 0x0C); - if( (result >= 0) && (result&0x30) ) - return 0; - - return result; + p->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; + return cx22702_get_tps (state, &p->u.ofdm); } -/* Check we can see the I2c clients */ -static int cx22702_attach_adapter(struct i2c_adapter *adapter) +static void cx22702_release(struct dvb_frontend* fe) { - struct cx22702_state *state; - struct i2c_client *client; - int ret; - int minfreq; - int pll_type; - int pll_addr; - int demod_addr; - - if (0 == (adapter->class & I2C_CLASS_TV_DIGITAL)) { - dprintk("Ignoring adapter 0x%x:%s (no digital tv card).\n", - adapter->id, adapter->name); - return 0; - } - - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - if (!strcmp(adapter->name, "Conexant DVB-T reference design")) { - printk("cx22702: Detected Conexant DVB-T card - PLL Thomson DTT7579\n"); - pll_type = PLLTYPE_DTT7579; - pll_addr = 0x60; - demod_addr = 0x43; - minfreq = 177000000; // guess - } else { - // default to Hauppauge Nova-T for the moment - printk("cx22702: Detected Hauppauge Nova-T DVB-T - PLL Thomson DTT759x\n"); - ret=cx22702_validate_eeprom(adapter, &minfreq, &pll_type, &pll_addr, &demod_addr); - if(ret < 0) - return ret; - } - - ret=cx22702_validate_demod(adapter, demod_addr); - if(ret < 0) - return ret; - - ret=cx22702_validate_pll(adapter, pll_addr, demod_addr); - if(ret < 0) - return ret; - - if ( !(state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL)) ) - return -ENOMEM; - - memset(state, 0, sizeof(struct cx22702_state)); - state->i2c = adapter; - memcpy(&state->cx22702_info, &cx22702_info, sizeof(struct dvb_frontend_info)); - state->cx22702_info.frequency_min = minfreq; - state->pll_type = pll_type; - state->pll_addr = pll_addr; - state->demod_addr = demod_addr; - - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; - } - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = state->demod_addr; - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - printk("cx22702: attach failed %i\n", ret); - kfree(client); + struct cx22702_state* state = (struct cx22702_state*) fe->demodulator_priv; kfree(state); - return ret; } - return 0; -} -static int cx22702_detach_client(struct i2c_client *client) -{ - struct cx22702_state *state = i2c_get_clientdata(client); - - if (NULL != state->dvb) { - dvb_unregister_frontend (cx22702_ioctl, state->dvb); - state->dvb = NULL; - } - i2c_detach_client(client); - kfree(client); - return 0; -} +static struct dvb_frontend_ops cx22702_ops; -static int command(struct i2c_client *client, unsigned int cmd, void *arg) +struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, + struct i2c_adapter* i2c) { - struct cx22702_state *state = i2c_get_clientdata(client); - int rc; - - switch(cmd) { - case FE_REGISTER: - if (NULL != state->dvb) - break; - state->dvb = arg; - rc = dvb_register_frontend(cx22702_ioctl, state->dvb, state, - &state->cx22702_info, THIS_MODULE); - if (0 != rc) { - printk("cx22702: dvb_register_frontend failed with rc=%d\n",rc); - state->dvb = NULL; - return rc; - } - break; - case FE_UNREGISTER: - if (NULL == state->dvb) - break; - dvb_unregister_frontend (cx22702_ioctl, state->dvb); - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} + struct cx22702_state* state = NULL; -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_CX22702, - .flags = I2C_DF_NOTIFY, - .attach_adapter = cx22702_attach_adapter, - .detach_client = cx22702_detach_client, - .command = command, + /* allocate memory for the internal state */ + state = (struct cx22702_state*) kmalloc(sizeof(struct cx22702_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &cx22702_ops, sizeof(struct dvb_frontend_ops)); + state->prevUCBlocks = 0; + + /* check if the demod is there */ + if (cx22702_readreg(state, 0x1f) != 0x3) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops cx22702_ops = { + + .info = { + .name = "Conexant CX22702 DVB-T", + .type = FE_OFDM, + .frequency_min = 177000000, + .frequency_max = 858000000, + .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 | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + + .release = cx22702_release, + + .init = cx22702_init, + + .set_frontend = cx22702_set_tps, + .get_frontend = cx22702_get_frontend, + + .read_status = cx22702_read_status, + .read_ber = cx22702_read_ber, + .read_signal_strength = cx22702_read_signal_strength, + .read_snr = cx22702_read_snr, + .read_ucblocks = cx22702_read_ucblocks, }; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_cx22702 (void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_cx22702 (void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "cx22702: driver deregistration failed.\n"); -} - -module_init (init_cx22702); -module_exit (exit_cx22702); - -MODULE_PARM(debug,"i"); +module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Enable verbose debug messages"); -MODULE_DESCRIPTION("CX22702 / Thomson DTT 759x / Thomson DTT 7579 PLL DVB Frontend driver"); + +MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver"); MODULE_AUTHOR("Steven Toth"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(cx22702_attach); diff -puN /dev/null drivers/media/dvb/frontends/cx22702.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/cx22702.h 2004-11-21 17:13:23.416058600 -0800 @@ -0,0 +1,46 @@ +/* + Conexant 22702 DVB OFDM demodulator driver + + based on: + Alps TDMB7 DVB OFDM demodulator driver + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + Holger Waechtler + + Copyright (C) 2004 Steven Toth + + 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. + +*/ + +#ifndef CX22702_H +#define CX22702_H + +#include + +struct cx22702_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config, + struct i2c_adapter* i2c); + +#endif // CX22702_H diff -puN drivers/media/dvb/frontends/cx24110.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/cx24110.c --- 25/drivers/media/dvb/frontends/cx24110.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.376064680 -0800 +++ 25-akpm/drivers/media/dvb/frontends/cx24110.c 2004-11-21 17:13:23.422057688 -0800 @@ -1,6 +1,5 @@ /* cx24110 - Single Chip Satellite Channel Receiver driver module - used on the the Pinnacle PCTV Sat cards Copyright (C) 2002 Peter Hettkamp based on work @@ -23,15 +22,6 @@ */ -/* currently drives the Conexant cx24110 and cx24106 QPSK decoder chips, - connected via i2c to a Conexant Fusion 878 (this uses the standard - linux bttv driver). The tuner chip is supposed to be the Conexant - cx24108 digital satellite tuner, driven through the tuner interface - of the cx24110. SEC is also supplied by the cx24110. - - Oct-2002: Migrate to API V3 (formerly known as NEWSTRUCT) -*/ - #include #include #include @@ -39,44 +29,30 @@ #include #include "dvb_frontend.h" +#include "cx24110.h" -#define FRONTEND_NAME "dvbfe_cx24110" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct cx24110_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; + const struct cx24110_config* config; -static struct dvb_frontend_info cx24110_info = { - .name = "Conexant CX24110 with CX24108 tuner, aka HM1221/HM1811", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 1011, /* kHz for QPSK frontends, can be reduced - to 253kHz on the cx24108 tuner */ - .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 | FE_CAN_RECOVER -}; -/* fixme: are these values correct? especially ..._tolerance and caps */ + struct dvb_frontend frontend; -struct cx24110_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; + u32 lastber; + u32 lastbler; + u32 lastesn0; }; +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "cx24110: " args); \ + } while (0) + static struct {u8 reg; u8 data;} cx24110_regdata[]= /* Comments beginning with @ denote this value should be the default */ @@ -140,15 +116,13 @@ static struct {u8 reg; u8 data;} cx24110 }; -static int cx24110_writereg (struct i2c_adapter *i2c, int reg, int data) +static int cx24110_writereg (struct cx24110_state* state, int reg, int data) { u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = 0x55, .flags = 0, .buf = buf, .len = 2 }; -/* fixme (medium): HW allows any i2c address. 0x55 is the default, but the - cx24110 might show up at any address */ + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; int err; - if ((err = i2c_transfer(i2c, &msg, 1)) != 1) { + if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { dprintk ("%s: writereg error (err == %i, reg == 0x%02x," " data == 0x%02x)\n", __FUNCTION__, err, reg, data); return -EREMOTEIO; @@ -158,161 +132,46 @@ static int cx24110_writereg (struct i2c_ } -static u8 cx24110_readreg (struct i2c_adapter *i2c, u8 reg) +static int cx24110_readreg (struct cx24110_state* state, 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 } }; -/* fixme (medium): address might be different from 0x55 */ - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return b1[0]; -} - - -static int cx24108_write (struct i2c_adapter *i2c, u32 data) -{ -/* tuner data is 21 bits long, must be left-aligned in data */ -/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ -/* FIXME (low): add error handling, avoid infinite loops if HW fails... */ - -dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data); - - cx24110_writereg(i2c,0x6d,0x30); /* auto mode at 62kHz */ - cx24110_writereg(i2c,0x70,0x15); /* auto mode 21 bits */ - /* if the auto tuner writer is still busy, clear it out */ - while (cx24110_readreg(i2c,0x6d)&0x80) - cx24110_writereg(i2c,0x72,0); - /* write the topmost 8 bits */ - cx24110_writereg(i2c,0x72,(data>>24)&0xff); - /* wait for the send to be completed */ - while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80) - ; - /* send another 8 bytes */ - cx24110_writereg(i2c,0x72,(data>>16)&0xff); - while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80) - ; - /* and the topmost 5 bits of this byte */ - cx24110_writereg(i2c,0x72,(data>>8)&0xff); - while ((cx24110_readreg(i2c,0x6d)&0xc0)==0x80) - ; - /* now strobe the enable line once */ - cx24110_writereg(i2c,0x6d,0x32); - cx24110_writereg(i2c,0x6d,0x30); - - return 0; -} - - -static int cx24108_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ -/* fixme (low): error handling */ - int i, a, n, pump; - u32 band, pll; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + ret = i2c_transfer(state->i2c, msg, 2); - static const u32 osci[]={ 950000,1019000,1075000,1178000, - 1296000,1432000,1576000,1718000, - 1856000,2036000,2150000}; - static const u32 bandsel[]={0,0x00020000,0x00040000,0x00100800, - 0x00101000,0x00102000,0x00104000, - 0x00108000,0x00110000,0x00120000, - 0x00140000}; - -#define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */ - dprintk("cx24110 debug: cx24108_set_tv_freq, freq=%d\n",freq); - - if (freq<950000) - freq=950000; /* kHz */ - if (freq>2150000) - freq=2150000; /* satellite IF is 950..2150MHz */ - /* decide which VCO to use for the input frequency */ - for (i=1;(i0) { - cx24110_writereg(i2c,0x05,(cx24110_readreg(i2c,0x05)&0xf0)|rate[fec]); + cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]); /* set nominal Viterbi rate */ - cx24110_writereg(i2c,0x22,(cx24110_readreg(i2c,0x22)&0xf0)|rate[fec]); + cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]); /* set current Viterbi rate */ - cx24110_writereg(i2c,0x1a,g1[fec]); - cx24110_writereg(i2c,0x1b,g2[fec]); + cx24110_writereg(state,0x1a,g1[fec]); + cx24110_writereg(state,0x1b,g2[fec]); /* not sure if this is the right way: I always used AutoAcq mode */ } else return -EOPNOTSUPP; @@ -369,11 +228,11 @@ static int cx24110_set_fec (struct i2c_a } -static fe_code_rate_t cx24110_get_fec (struct i2c_adapter *i2c) +static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state) { int i; - i=cx24110_readreg(i2c,0x22)&0x0f; + i=cx24110_readreg(state,0x22)&0x0f; if(!(i&0x08)) { return FEC_1_2 + i - 1; } else { @@ -386,16 +245,13 @@ static fe_code_rate_t cx24110_get_fec (s } -static int cx24110_set_symbolrate (struct i2c_adapter *i2c, u32 srate) +static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) { /* fixme (low): add error handling */ u32 ratio; u32 tmp, fclk, BDRI; static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; - static const u32 vca[]={0x80f03800,0x81f0f800,0x83f1f800}; - static const u32 vga[]={0x5f8fc000,0x580f0000,0x500c0000}; - static const u8 filtune[]={0xa2,0xcc,0x66}; int i; dprintk("cx24110 debug: entering %s(%d)\n",__FUNCTION__,srate); @@ -409,22 +265,22 @@ dprintk("cx24110 debug: entering %s(%d)\ /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz, and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult, R06[3:0] PLLphaseDetGain */ - tmp=cx24110_readreg(i2c,0x07)&0xfc; + tmp=cx24110_readreg(state,0x07)&0xfc; if(srate<90999000UL/4) { /* sample rate 45MHz*/ - cx24110_writereg(i2c,0x07,tmp); - cx24110_writereg(i2c,0x06,0x78); + cx24110_writereg(state,0x07,tmp); + cx24110_writereg(state,0x06,0x78); fclk=90999000UL/2; } else if(srate<60666000UL/2) { /* sample rate 60MHz */ - cx24110_writereg(i2c,0x07,tmp|0x1); - cx24110_writereg(i2c,0x06,0xa5); + cx24110_writereg(state,0x07,tmp|0x1); + cx24110_writereg(state,0x06,0xa5); fclk=60666000UL; } else if(srate<80888000UL/2) { /* sample rate 80MHz */ - cx24110_writereg(i2c,0x07,tmp|0x2); - cx24110_writereg(i2c,0x06,0x87); + cx24110_writereg(state,0x07,tmp|0x2); + cx24110_writereg(state,0x06,0x87); fclk=80888000UL; } else { /* sample rate 90MHz */ - cx24110_writereg(i2c,0x07,tmp|0x3); - cx24110_writereg(i2c,0x06,0x78); + cx24110_writereg(state,0x07,tmp|0x3); + cx24110_writereg(state,0x06,0x78); fclk=90999000UL; }; dprintk("cx24110 debug: fclk %d Hz\n",fclk); @@ -453,64 +309,123 @@ dprintk("cx24110 debug: entering %s(%d)\ dprintk("fclk = %d\n", fclk); dprintk("ratio= %08x\n", ratio); - cx24110_writereg(i2c, 0x1, (ratio>>16)&0xff); - cx24110_writereg(i2c, 0x2, (ratio>>8)&0xff); - cx24110_writereg(i2c, 0x3, (ratio)&0xff); - - /* please see the cx24108 data sheet, this controls tuner gain - and bandwidth settings depending on the symbol rate */ - cx24108_write(i2c,vga[i]); - cx24108_write(i2c,vca[i]); /* gain is set on tuner chip */ - cx24110_writereg(i2c,0x56,filtune[i]); /* bw is contolled by filtune voltage */ + cx24110_writereg(state, 0x1, (ratio>>16)&0xff); + cx24110_writereg(state, 0x2, (ratio>>8)&0xff); + cx24110_writereg(state, 0x3, (ratio)&0xff); return 0; } -static int cx24110_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage) + + + + + + + + + + +int cx24110_pll_write (struct dvb_frontend* fe, u32 data) { + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + +/* tuner data is 21 bits long, must be left-aligned in data */ +/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ +/* FIXME (low): add error handling, avoid infinite loops if HW fails... */ + + dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data); + + cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */ + cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */ + + /* if the auto tuner writer is still busy, clear it out */ + while (cx24110_readreg(state,0x6d)&0x80) + cx24110_writereg(state,0x72,0); + + /* write the topmost 8 bits */ + cx24110_writereg(state,0x72,(data>>24)&0xff); + + /* wait for the send to be completed */ + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* send another 8 bytes */ + cx24110_writereg(state,0x72,(data>>16)&0xff); + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* and the topmost 5 bits of this byte */ + cx24110_writereg(state,0x72,(data>>8)&0xff); + while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) + ; + + /* now strobe the enable line once */ + cx24110_writereg(state,0x6d,0x32); + cx24110_writereg(state,0x6d,0x30); + + return 0; +} + + + +static int cx24110_initfe(struct dvb_frontend* fe) +{ + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; +/* fixme (low): error handling */ + int i; + + dprintk("%s: init chip\n", __FUNCTION__); + + for(i=0;iconfig->pll_init) state->config->pll_init(fe); + + return 0; +} + + +static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + switch (voltage) { case SEC_VOLTAGE_13: - return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0xc0); + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0); case SEC_VOLTAGE_18: - return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&0x3b)|0x40); + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40); default: return -EINVAL; }; } -static void cx24110_send_diseqc_msg(struct i2c_adapter *i2c, +static int cx24110_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) { int i, rv; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; for (i = 0; i < cmd->msg_len; i++) - cx24110_writereg(i2c, 0x79 + i, cmd->msg[i]); + cx24110_writereg(state, 0x79 + i, cmd->msg[i]); - rv = cx24110_readreg(i2c, 0x76); + rv = cx24110_readreg(state, 0x76); - cx24110_writereg(i2c, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); - for (i=500; i-- > 0 && !(cx24110_readreg(i2c,0x76)&0x40);) + cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); + for (i=500; i-- > 0 && !(cx24110_readreg(state,0x76)&0x40);) ; /* wait for LNB ready */ -} + return 0; +} -static int cx24110_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct cx24110_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - static int lastber=0, lastbyer=0,lastbler=0, lastesn0=0, sum_bler=0; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &cx24110_info, sizeof(struct dvb_frontend_info)); - break; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - case FE_READ_STATUS: - { - fe_status_t *status = arg; - int sync = cx24110_readreg (i2c, 0x55); + int sync = cx24110_readreg (state, 0x55); *status = 0; @@ -520,7 +435,7 @@ static int cx24110_ioctl (struct dvb_fro if (sync & 0x08) *status |= FE_HAS_CARRIER; - sync = cx24110_readreg (i2c, 0x08); + sync = cx24110_readreg (state, 0x08); if (sync & 0x40) *status |= FE_HAS_VITERBI; @@ -531,87 +446,97 @@ static int cx24110_ioctl (struct dvb_fro if ((sync & 0x60) == 0x60) *status |= FE_HAS_LOCK; - if(cx24110_readreg(i2c,0x10)&0x40) { - /* the RS error counter has finished one counting window */ - cx24110_writereg(i2c,0x10,0x60); /* select the byer reg */ - lastbyer=cx24110_readreg(i2c,0x12)| - (cx24110_readreg(i2c,0x13)<<8)| - (cx24110_readreg(i2c,0x14)<<16); - cx24110_writereg(i2c,0x10,0x70); /* select the bler reg */ - lastbler=cx24110_readreg(i2c,0x12)| - (cx24110_readreg(i2c,0x13)<<8)| - (cx24110_readreg(i2c,0x14)<<16); - cx24110_writereg(i2c,0x10,0x20); /* start new count window */ - sum_bler += lastbler; - } - if(cx24110_readreg(i2c,0x24)&0x10) { - /* the Viterbi error counter has finished one counting window */ - cx24110_writereg(i2c,0x24,0x04); /* select the ber reg */ - lastber=cx24110_readreg(i2c,0x25)| - (cx24110_readreg(i2c,0x26)<<8); - cx24110_writereg(i2c,0x24,0x04); /* start new count window */ - cx24110_writereg(i2c,0x24,0x14); - } - if(cx24110_readreg(i2c,0x6a)&0x80) { - /* the Es/N0 error counter has finished one counting window */ - lastesn0=cx24110_readreg(i2c,0x69)| - (cx24110_readreg(i2c,0x68)<<8); - cx24110_writereg(i2c,0x6a,0x84); /* start new count window */ - } - break; + return 0; } - case FE_READ_BER: +static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = (u32 *) arg; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - *ber = lastber; /* fixme (maybe): value range is 16 bit. Scale? */ - break; + if(cx24110_readreg(state,0x24)&0x10) { + /* the Viterbi error counter has finished one counting window */ + cx24110_writereg(state,0x24,0x04); /* select the ber reg */ + state->lastber=cx24110_readreg(state,0x25)| + (cx24110_readreg(state,0x26)<<8); + cx24110_writereg(state,0x24,0x04); /* start new count window */ + cx24110_writereg(state,0x24,0x14); } + *ber = state->lastber; - case FE_READ_SIGNAL_STRENGTH: + return 0; + } + +static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) { + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + /* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */ - u8 signal = cx24110_readreg (i2c, 0x27)+128; - *((u16*) arg) = (signal << 8) | signal; - break; + u8 signal = cx24110_readreg (state, 0x27)+128; + *signal_strength = (signal << 8) | signal; + + return 0; } - case FE_READ_SNR: +static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) { + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */ - *(u16*) arg = lastesn0; - break; + if(cx24110_readreg(state,0x6a)&0x80) { + /* the Es/N0 error counter has finished one counting window */ + state->lastesn0=cx24110_readreg(state,0x69)| + (cx24110_readreg(state,0x68)<<8); + cx24110_writereg(state,0x6a,0x84); /* start new count window */ + } + *snr = state->lastesn0; + + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: +static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { - *(u16*) arg = sum_bler&0xffff; - sum_bler=0; - break; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; + u32 lastbyer; + + if(cx24110_readreg(state,0x10)&0x40) { + /* the RS error counter has finished one counting window */ + cx24110_writereg(state,0x10,0x60); /* select the byer reg */ + lastbyer=cx24110_readreg(state,0x12)| + (cx24110_readreg(state,0x13)<<8)| + (cx24110_readreg(state,0x14)<<16); + cx24110_writereg(state,0x10,0x70); /* select the bler reg */ + state->lastbler=cx24110_readreg(state,0x12)| + (cx24110_readreg(state,0x13)<<8)| + (cx24110_readreg(state,0x14)<<16); + cx24110_writereg(state,0x10,0x20); /* start new count window */ } + *ucblocks = state->lastbler; - case FE_SET_FRONTEND: + return 0; +} + +static int cx24110_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - cx24108_set_tv_freq (i2c, p->frequency); - cx24110_set_inversion (i2c, p->inversion); - cx24110_set_fec (i2c, p->u.qpsk.fec_inner); - cx24110_set_symbolrate (i2c, p->u.qpsk.symbol_rate); - cx24110_writereg(i2c,0x04,0x05); /* start aquisition */ - break; + state->config->pll_set(fe, p); + cx24110_set_inversion (state, p->inversion); + cx24110_set_fec (state, p->u.qpsk.fec_inner); + cx24110_set_symbolrate (state, p->u.qpsk.symbol_rate); + cx24110_writereg(state,0x04,0x05); /* start aquisition */ + + return 0; } - case FE_GET_FRONTEND: +static int cx24110_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; s32 afc; unsigned sclk; /* cannot read back tuner settings (freq). Need to have some private storage */ - sclk = cx24110_readreg (i2c, 0x07) & 0x03; + sclk = cx24110_readreg (state, 0x07) & 0x03; /* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz. * Need 64 bit arithmetic. Is thiss possible in the kernel? */ if (sclk==0) sclk=90999000L/2L; @@ -619,152 +544,104 @@ static int cx24110_ioctl (struct dvb_fro else if (sclk==2) sclk=80888000L; else sclk=90999000L; sclk>>=8; - afc = sclk*(cx24110_readreg (i2c, 0x44)&0x1f)+ - ((sclk*cx24110_readreg (i2c, 0x45))>>8)+ - ((sclk*cx24110_readreg (i2c, 0x46))>>16); + afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+ + ((sclk*cx24110_readreg (state, 0x45))>>8)+ + ((sclk*cx24110_readreg (state, 0x46))>>16); p->frequency += afc; - p->inversion = (cx24110_readreg (i2c, 0x22) & 0x10) ? + p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ? INVERSION_ON : INVERSION_OFF; - p->u.qpsk.fec_inner = cx24110_get_fec (i2c); - break; - } - - case FE_SLEEP: -/* cannot do this from the FE end. How to communicate this to the place where it can be done? */ - break; - case FE_INIT: - return cx24110_initfe(i2c); - - case FE_SET_TONE: - return cx24110_writereg(i2c,0x76,(cx24110_readreg(i2c,0x76)&~0x10)|((((fe_sec_tone_mode_t) arg)==SEC_TONE_ON)?0x10:0)); - case FE_SET_VOLTAGE: - return cx24110_set_voltage (i2c, (fe_sec_voltage_t) arg); - - case FE_DISEQC_SEND_MASTER_CMD: - // FIXME Status? - cx24110_send_diseqc_msg(i2c, (struct dvb_diseqc_master_cmd*) arg); - return 0; - - default: - return -EOPNOTSUPP; - }; + p->u.qpsk.fec_inner = cx24110_get_fec (state); return 0; } -static struct i2c_client client_template; - -static int attach_adapter (struct i2c_adapter *adapter) +static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { - struct cx24110_state *state; - struct i2c_client *client; - int ret = 0; - u8 sig; - - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - sig = cx24110_readreg (adapter, 0x00); - if ( sig != 0x5a && sig != 0x69 ) - return -ENODEV; + struct cx24110_state *state = (struct cx24110_state*) fe->demodulator_priv; - if ( !(state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL)) ) - return -ENOMEM; - - memset(state, 0, sizeof(struct cx24110_state)); - state->i2c = adapter; - - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; + return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0)); } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = 0x55; - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - kfree(client); - kfree(state); - return ret; -} - - BUG_ON(!state->dvb); - - if ((ret = dvb_register_frontend(cx24110_ioctl, state->dvb, state, - &cx24110_info, THIS_MODULE))) { - i2c_detach_client(client); - kfree(client); +static void cx24110_release(struct dvb_frontend* fe) +{ + struct cx24110_state* state = (struct cx24110_state*) fe->demodulator_priv; kfree(state); - return ret; -} - - return 0; } -static int detach_client (struct i2c_client *client) -{ - struct cx24110_state *state = i2c_get_clientdata(client); - - dvb_unregister_frontend(cx24110_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} +static struct dvb_frontend_ops cx24110_ops; -static int command(struct i2c_client *client, unsigned int cmd, void *arg) +struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c) { - struct cx24110_state *state = i2c_get_clientdata(client); - - switch(cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_CX24110, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; + struct cx24110_state* state = NULL; + int ret; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + /* allocate memory for the internal state */ + state = (struct cx24110_state*) kmalloc(sizeof(struct cx24110_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &cx24110_ops, sizeof(struct dvb_frontend_ops)); + state->lastber = 0; + state->lastbler = 0; + state->lastesn0 = 0; + + /* check if the demod is there */ + ret = cx24110_readreg(state, 0x00); + if ((ret != 0x5a) && (ret != 0x69)) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops cx24110_ops = { + + .info = { + .name = "Conexant CX24110 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1011, /* kHz for QPSK frontends */ + .frequency_tolerance = 29500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = cx24110_release, + + .init = cx24110_initfe, + .set_frontend = cx24110_set_frontend, + .get_frontend = cx24110_get_frontend, + .read_status = cx24110_read_status, + .read_ber = cx24110_read_ber, + .read_signal_strength = cx24110_read_signal_strength, + .read_snr = cx24110_read_snr, + .read_ucblocks = cx24110_read_ucblocks, + + .diseqc_send_master_cmd = cx24110_send_diseqc_msg, + .set_tone = cx24110_set_tone, + .set_voltage = cx24110_set_voltage, }; -static int __init cx24110_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit cx24110_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "cx24110: driver deregistration failed.\n"); -} - -module_init(cx24110_init); -module_exit(cx24110_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("DVB Frontend driver module for the Conexant cx24108/cx24110 chipset"); +MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver"); MODULE_AUTHOR("Peter Hettkamp"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(cx24110_attach); +EXPORT_SYMBOL(cx24110_pll_write); diff -puN /dev/null drivers/media/dvb/frontends/cx24110.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/cx24110.h 2004-11-21 17:13:23.422057688 -0800 @@ -0,0 +1,45 @@ +/* + cx24110 - Single Chip Satellite Channel Receiver driver module + + Copyright (C) 2002 Peter Hettkamp based on + work + Copyright (C) 1999 Convergence Integrated Media GmbH + + 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. + +*/ + +#ifndef CX24110_H +#define CX24110_H + +#include + +struct cx24110_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, + struct i2c_adapter* i2c); + +extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data); + +#endif // CX24110_H diff -puN /dev/null drivers/media/dvb/frontends/dib3000-common.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/dib3000-common.h 2004-11-21 17:13:23.423057536 -0800 @@ -0,0 +1,104 @@ +/* + * .h-files for the common use of the frontend drivers made by DiBcom + * DiBcom 3000-MB/MC/P + * + * DiBcom (http://www.dibcom.fr/) + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DibCom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * 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, version 2. + * + * Acknowledgements + * + * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver + * sources, on which this driver (and the dvb-dibusb) are based. + * + * see Documentation/dvb/README.dibusb for more information + * + */ + +#ifndef DIB3000_COMMON_H +#define DIB3000_COMMON_H + + +/* info and err, taken from usb.h, if there is anything available like by default, + * please change ! + */ +#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg) + +/* a PID for the pid_filter list, when in use */ +struct dib3000_pid +{ + u16 pid; + int active; +}; + +/* mask for enabling a specific pid for the pid_filter */ +#define DIB3000_ACTIVATE_PID_FILTERING (0x2000) + +/* common values for tuning */ +#define DIB3000_ALPHA_0 ( 0) +#define DIB3000_ALPHA_1 ( 1) +#define DIB3000_ALPHA_2 ( 2) +#define DIB3000_ALPHA_4 ( 4) + +#define DIB3000_CONSTELLATION_QPSK ( 0) +#define DIB3000_CONSTELLATION_16QAM ( 1) +#define DIB3000_CONSTELLATION_64QAM ( 2) + +#define DIB3000_GUARD_TIME_1_32 ( 0) +#define DIB3000_GUARD_TIME_1_16 ( 1) +#define DIB3000_GUARD_TIME_1_8 ( 2) +#define DIB3000_GUARD_TIME_1_4 ( 3) + +#define DIB3000_TRANSMISSION_MODE_2K ( 0) +#define DIB3000_TRANSMISSION_MODE_8K ( 1) + +#define DIB3000_SELECT_LP ( 0) +#define DIB3000_SELECT_HP ( 1) + +#define DIB3000_FEC_1_2 ( 1) +#define DIB3000_FEC_2_3 ( 2) +#define DIB3000_FEC_3_4 ( 3) +#define DIB3000_FEC_5_6 ( 5) +#define DIB3000_FEC_7_8 ( 7) + +#define DIB3000_HRCH_OFF ( 0) +#define DIB3000_HRCH_ON ( 1) + +#define DIB3000_DDS_INVERSION_OFF ( 0) +#define DIB3000_DDS_INVERSION_ON ( 1) + +#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 7)) +#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 7) | (1 << 7))) + +/* for auto search */ +static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ + { /* fft */ + { /* gua */ + { 0, 1 }, /* 0 0 { 0,1 } */ + { 3, 9 }, /* 0 1 { 0,1 } */ + }, + { + { 2, 5 }, /* 1 0 { 0,1 } */ + { 6, 11 }, /* 1 1 { 0,1 } */ + } + }; + +#define DIB3000_REG_MANUFACTOR_ID ( 1025) +#define DIB3000_I2C_ID_DIBCOM (0x01b3) + +#define DIB3000_REG_DEVICE_ID ( 1026) +#define DIB3000MB_DEVICE_ID (0x3000) +#define DIB3000MC_DEVICE_ID (0x3001) +#define DIB3000P_DEVICE_ID (0x3002) + +#endif // DIB3000_COMMON_H diff -puN /dev/null drivers/media/dvb/frontends/dib3000.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/dib3000.h 2004-11-21 17:13:23.423057536 -0800 @@ -0,0 +1,53 @@ +/* + * public header file of the frontend drivers for mobile DVB-T demodulators + * DiBcom 3000-MB and DiBcom 3000-MC/P (http://www.dibcom.fr/) + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de) + * + * based on GPL code from DibCom, which has + * + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * 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, version 2. + * + * Acknowledgements + * + * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver + * sources, on which this driver (and the dvb-dibusb) are based. + * + * see Documentation/dvb/README.dibusb for more information + * + */ + +#ifndef DIB3000_H +#define DIB3000_H + +#include + +struct dib3000_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* The i2c address of the PLL */ + u8 pll_addr; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend *fe); + int (*pll_set)(struct dvb_frontend *fe, struct dvb_frontend_parameters* params); +}; + +struct dib3000_xfer_ops +{ + /* pid and transfer handling is done in the demodulator */ + int (*pid_filter)(struct dvb_frontend *fe, int onoff); + int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff); + int (*pid_ctrl)(struct dvb_frontend *fe, int pid, int onoff); +}; + +extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops); + +#endif // DIB3000_H diff -puN drivers/media/dvb/frontends/dib3000mb.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/dib3000mb.c --- 25/drivers/media/dvb/frontends/dib3000mb.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.378064376 -0800 +++ 25-akpm/drivers/media/dvb/frontends/dib3000mb.c 2004-11-21 17:13:23.430056472 -0800 @@ -17,8 +17,6 @@ * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver * sources, on which this driver (and the dvb-dibusb) are based. * - * - * * see Documentation/dvb/README.dibusb for more information * */ @@ -32,8 +30,26 @@ #include #include "dvb_frontend.h" +#include "dib3000-common.h" +#include "dib3000mb_priv.h" +#include "dib3000.h" + +struct dib3000mb_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct dib3000_config* config; + + spinlock_t pid_list_lock; + struct dib3000_pid pid_list[DIB3000MB_NUM_PIDS]; + int feedcount; + + struct dvb_frontend frontend; +}; -#include "dib3000mb.h" /* debug */ @@ -56,34 +72,13 @@ MODULE_PARM_DESC(debug, "set debugging l /* Version information */ #define DRIVER_VERSION "0.1" -#define DRIVER_DESC "DiBcom 3000-MB DVB-T frontend" +#define DRIVER_DESC "DiBcom 3000-MB DVB-T demodulator driver" #define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" -struct dib3000mb_state { - struct i2c_client *i2c; - struct dvb_adapter *dvb; - u16 manufactor_id; - u16 device_id; -}; - -static struct dvb_frontend_info dib3000mb_info = { - .name = "DiBcom 3000-MB DVB-T", - .type = FE_OFDM, - .frequency_min = 44250000, - .frequency_max = 867250000, - .frequency_stepsize = 62500, - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_HIERARCHY_AUTO, -}; - -#define rd(reg) dib3000mb_read_reg(state->i2c,reg) -#define wr(reg,val) if (dib3000mb_write_reg(state->i2c,reg,val)) \ +/* handy shortcuts */ +#define rd(reg) dib3000mb_read_reg(state,reg) +#define wr(reg,val) if (dib3000mb_write_reg(state,reg,val)) \ { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; } #define wr_foreach(a,v) { int i; \ deb_alot("sizeof: %d %d\n",sizeof(a),sizeof(v));\ @@ -91,228 +86,53 @@ static struct dvb_frontend_info dib3000m wr(a[i],v[i]); \ } -static u16 dib3000mb_read_reg(struct i2c_client *i2c, u16 reg) +static int dib3000mb_read_reg(struct dib3000mb_state *state, u16 reg) { u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; u8 rb[2]; struct i2c_msg msg[] = { - { .addr = i2c->addr, .flags = 0, .buf = wb, .len = 2 }, - { .addr = i2c->addr, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + { .addr = state->config->demod_address, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 }, }; deb_alot("reading from i2c bus (reg: %d)\n",reg); - if (i2c_transfer(i2c->adapter,msg,2) != 2) + if (i2c_transfer(state->i2c, msg, 2) != 2) deb_alot("i2c read error\n"); return (rb[0] << 8) | rb[1]; } -static int dib3000mb_write_reg(struct i2c_client *i2c,u16 reg, u16 val) +static int dib3000mb_write_reg(struct dib3000mb_state *state, u16 reg, u16 val) { u8 b[] = { (reg >> 8) & 0xff, reg & 0xff, (val >> 8) & 0xff, val & 0xff, }; - struct i2c_msg msg[] = { { .addr = i2c->addr, .flags = 0, .buf = b, .len = 4 } }; + struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 4 } }; deb_alot("writing to i2c bus (reg: %d, val: %d)\n",reg,val); - return i2c_transfer(i2c->adapter,msg,1) != 1 ? -EREMOTEIO : 0 ; + return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0; } -static int dib3000mb_tuner_thomson_cable_eu(struct dib3000mb_state *state, - u32 freq) -{ - u32 tfreq = (freq + 36125000) / 62500; - unsigned int addr; - int vu,p0,p1,p2; - - if (freq > 403250000) - vu = 1, p2 = 1, p1 = 0, p0 = 1; - else if (freq > 115750000) - vu = 0, p2 = 1, p1 = 1, p0 = 0; - else if (freq > 44250000) - vu = 0, p2 = 0, p1 = 1, p0 = 1; - else - return -EINVAL; - /* TODO better solution for i2c->addr handling */ - addr = state->i2c->addr; - state->i2c->addr = DIB3000MB_TUNER_ADDR_DEFAULT; - wr(tfreq & 0x7fff,(0x8e << 8) + ((vu << 7) | (p2 << 2) | (p1 << 1) | p0) ); - state->i2c->addr = addr; - - return 0; -} - -static int dib3000mb_get_frontend(struct dib3000mb_state *state, - struct dvb_frontend_parameters *fep) -{ - struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; - fe_code_rate_t *cr; - u16 tps_val; - int inv_test1,inv_test2; - u32 dds_val, threshold = 0x800000; - - if (!rd(DIB3000MB_REG_TPS_LOCK)) - return 0; - - dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB); - if (dds_val & threshold) - inv_test1 = 0; - else if (dds_val == threshold) - inv_test1 = 1; - else - inv_test1 = 2; - - dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB); - if (dds_val & threshold) - inv_test2 = 0; - else if (dds_val == threshold) - inv_test2 = 1; - else - inv_test2 = 2; - - fep->inversion = - ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) - || - ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)); - - deb_getf("inversion %d %d, %d\n",inv_test2,inv_test1, fep->inversion); - - switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) { - case DIB3000MB_QAM_QPSK: - deb_getf("QPSK "); - ofdm->constellation = QPSK; - break; - case DIB3000MB_QAM_QAM16: - deb_getf("QAM16 "); - ofdm->constellation = QAM_16; - break; - case DIB3000MB_QAM_QAM64: - deb_getf("QAM64 "); - ofdm->constellation = QAM_64; - break; - default: - err("Unexpected constellation returned by TPS (%d)",tps_val); - break; - } - deb_getf("TPS: %d\n",tps_val); - - if (rd(DIB3000MB_REG_TPS_HRCH)) { - deb_getf("HRCH ON\n"); - tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP); - cr = &ofdm->code_rate_LP; - ofdm->code_rate_HP = FEC_NONE; - - switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) { - case DIB3000MB_VIT_ALPHA_OFF: - deb_getf("HIERARCHY_NONE "); - ofdm->hierarchy_information = HIERARCHY_NONE; - break; - case DIB3000MB_VIT_ALPHA_1: - deb_getf("HIERARCHY_1 "); - ofdm->hierarchy_information = HIERARCHY_1; - break; - case DIB3000MB_VIT_ALPHA_2: - deb_getf("HIERARCHY_2 "); - ofdm->hierarchy_information = HIERARCHY_2; - break; - case DIB3000MB_VIT_ALPHA_4: - deb_getf("HIERARCHY_4 "); - ofdm->hierarchy_information = HIERARCHY_4; - break; - default: - err("Unexpected ALPHA value returned by TPS (%d)",tps_val); - } - deb_getf("TPS: %d\n",tps_val); - } else { - deb_getf("HRCH OFF\n"); - tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP); - cr = &ofdm->code_rate_HP; - ofdm->code_rate_LP = FEC_NONE; - ofdm->hierarchy_information = HIERARCHY_NONE; - } - - switch (tps_val) { - case DIB3000MB_FEC_1_2: - deb_getf("FEC_1_2 "); - *cr = FEC_1_2; - break; - case DIB3000MB_FEC_2_3: - deb_getf("FEC_2_3 "); - *cr = FEC_2_3; - break; - case DIB3000MB_FEC_3_4: - deb_getf("FEC_3_4 "); - *cr = FEC_3_4; - break; - case DIB3000MB_FEC_5_6: - deb_getf("FEC_5_6 "); - *cr = FEC_4_5; - break; - case DIB3000MB_FEC_7_8: - deb_getf("FEC_7_8 "); - *cr = FEC_7_8; - break; - default: - err("Unexpected FEC returned by TPS (%d)",tps_val); - break; - } - deb_getf("TPS: %d\n",tps_val); - - switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) { - case DIB3000MB_GUARD_TIME_1_32: - deb_getf("GUARD_INTERVAL_1_32 "); - ofdm->guard_interval = GUARD_INTERVAL_1_32; - break; - case DIB3000MB_GUARD_TIME_1_16: - deb_getf("GUARD_INTERVAL_1_16 "); - ofdm->guard_interval = GUARD_INTERVAL_1_16; - break; - case DIB3000MB_GUARD_TIME_1_8: - deb_getf("GUARD_INTERVAL_1_8 "); - ofdm->guard_interval = GUARD_INTERVAL_1_8; - break; - case DIB3000MB_GUARD_TIME_1_4: - deb_getf("GUARD_INTERVAL_1_4 "); - ofdm->guard_interval = GUARD_INTERVAL_1_4; - break; - default: - err("Unexpected Guard Time returned by TPS (%d)",tps_val); - break; - } - deb_getf("TPS: %d\n",tps_val); - - switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) { - case DIB3000MB_FFT_2K: - deb_getf("TRANSMISSION_MODE_2K "); - ofdm->transmission_mode = TRANSMISSION_MODE_2K; - break; - case DIB3000MB_FFT_8K: - deb_getf("TRANSMISSION_MODE_8K "); - ofdm->transmission_mode = TRANSMISSION_MODE_8K; - break; - default: - err("unexpected transmission mode return by TPS (%d)",tps_val); - } - deb_getf("TPS: %d\n",tps_val); - return 0; -} - -static int dib3000mb_set_frontend(struct dib3000mb_state *state, +static int dib3000mb_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep, int tuner); -static int dib3000mb_fe_read_search_status(struct dib3000mb_state *state) +static int dib3000mb_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep); + +static int dib3000mb_fe_read_search_status(struct dvb_frontend* fe) { u16 irq; struct dvb_frontend_parameters fep; + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; irq = rd(DIB3000MB_REG_AS_IRQ_PENDING); if (irq & 0x02) { if (rd(DIB3000MB_REG_LOCK2_VALUE) & 0x01) { - if (dib3000mb_get_frontend(state,&fep) == 0) { + if (dib3000mb_get_frontend(fe, &fep) == 0) { deb_setf("reading tuning data from frontend succeeded.\n"); - return dib3000mb_set_frontend(state,&fep,0) == 0; + return dib3000mb_set_frontend(fe, &fep, 0) == 0; } else { deb_setf("reading tuning data failed -> tuning failed.\n"); return 0; @@ -329,22 +149,20 @@ static int dib3000mb_fe_read_search_stat return -1; } -static int dib3000mb_set_frontend(struct dib3000mb_state *state, +static int dib3000mb_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep, int tuner) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; fe_code_rate_t fe_cr = FEC_NONE; int search_state,seq; if (tuner) { wr(DIB3000MB_REG_TUNER, - DIB3000MB_ACTIVATE_TUNER_XFER( DIB3000MB_TUNER_ADDR_DEFAULT ) ); - dib3000mb_tuner_thomson_cable_eu(state,fep->frequency); - - /* wait for tuner */ - msleep(1); + DIB3000_TUNER_WRITE_ENABLE(state->config->pll_addr)); + state->config->pll_set(fe, fep); wr(DIB3000MB_REG_TUNER, - DIB3000MB_DEACTIVATE_TUNER_XFER( DIB3000MB_TUNER_ADDR_DEFAULT ) ); + DIB3000_TUNER_WRITE_DISABLE(state->config->pll_addr)); deb_setf("bandwidth: "); switch (ofdm->bandwidth) { @@ -376,15 +194,14 @@ static int dib3000mb_set_frontend(struct switch (ofdm->transmission_mode) { case TRANSMISSION_MODE_2K: deb_setf("2k\n"); - wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_2K); + wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K); break; case TRANSMISSION_MODE_8K: deb_setf("8k\n"); - wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_8K); + wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K); break; case TRANSMISSION_MODE_AUTO: deb_setf("auto\n"); - wr(DIB3000MB_REG_FFT,DIB3000MB_FFT_AUTO); break; default: return -EINVAL; @@ -394,40 +211,39 @@ static int dib3000mb_set_frontend(struct switch (ofdm->guard_interval) { case GUARD_INTERVAL_1_32: deb_setf("1_32\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_32); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32); break; case GUARD_INTERVAL_1_16: deb_setf("1_16\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_16); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16); break; case GUARD_INTERVAL_1_8: deb_setf("1_8\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_8); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8); break; case GUARD_INTERVAL_1_4: deb_setf("1_4\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_1_4); + wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4); break; case GUARD_INTERVAL_AUTO: deb_setf("auto\n"); - wr(DIB3000MB_REG_GUARD_TIME,DIB3000MB_GUARD_TIME_AUTO); break; default: return -EINVAL; } - deb_setf("invsersion: "); + deb_setf("inversion: "); switch (fep->inversion) { - case INVERSION_AUTO: - deb_setf("auto\n"); - break; case INVERSION_OFF: - deb_setf("on\n"); - wr(DIB3000MB_REG_DDS_INV,DIB3000MB_DDS_INV_OFF); + deb_setf("off\n"); + wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF); + break; + case INVERSION_AUTO: + deb_setf("auto "); break; case INVERSION_ON: deb_setf("on\n"); - wr(DIB3000MB_REG_DDS_INV,DIB3000MB_DDS_INV_ON); + wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON); break; default: return -EINVAL; @@ -437,15 +253,15 @@ static int dib3000mb_set_frontend(struct switch (ofdm->constellation) { case QPSK: deb_setf("qpsk\n"); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QPSK); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK); break; case QAM_16: deb_setf("qam16\n"); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QAM16); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM); break; case QAM_64: deb_setf("qam64\n"); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_QAM64); + wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM); break; case QAM_AUTO: break; @@ -456,22 +272,21 @@ static int dib3000mb_set_frontend(struct switch (ofdm->hierarchy_information) { case HIERARCHY_NONE: deb_setf("none "); - /* fall through alpha is 1, even when HIERARCHY is NONE */ + /* fall through */ case HIERARCHY_1: deb_setf("alpha=1\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_1); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1); break; case HIERARCHY_2: deb_setf("alpha=2\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_2); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2); break; case HIERARCHY_4: deb_setf("alpha=4\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_4); + wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4); break; case HIERARCHY_AUTO: deb_setf("alpha=auto\n"); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_AUTO); break; default: return -EINVAL; @@ -480,39 +295,40 @@ static int dib3000mb_set_frontend(struct deb_setf("hierarchy: "); if (ofdm->hierarchy_information == HIERARCHY_NONE) { deb_setf("none\n"); - wr(DIB3000MB_REG_VIT_HRCH,DIB3000MB_VIT_HRCH_OFF); - wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_HP); + wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF); + wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP); fe_cr = ofdm->code_rate_HP; } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) { deb_setf("on\n"); - wr(DIB3000MB_REG_VIT_HRCH,DIB3000MB_VIT_HRCH_ON); - wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_LP); + wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON); + wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP); fe_cr = ofdm->code_rate_LP; } deb_setf("fec: "); switch (fe_cr) { case FEC_1_2: deb_setf("1_2\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_1_2); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2); break; case FEC_2_3: deb_setf("2_3\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_2_3); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3); break; case FEC_3_4: deb_setf("3_4\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_3_4); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4); break; case FEC_5_6: deb_setf("5_6\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_5_6); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6); break; case FEC_7_8: deb_setf("7_8\n"); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_7_8); + wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8); break; case FEC_NONE: deb_setf("none "); + break; case FEC_AUTO: deb_setf("auto\n"); break; @@ -520,7 +336,7 @@ static int dib3000mb_set_frontend(struct return -EINVAL; } - seq = dib3000mb_seq + seq = dib3000_seq [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO] [ofdm->guard_interval == GUARD_INTERVAL_AUTO] [fep->inversion == INVERSION_AUTO]; @@ -564,6 +380,7 @@ static int dib3000mb_set_frontend(struct ofdm->hierarchy_information == HIERARCHY_AUTO || fe_cr == FEC_AUTO || fep->inversion == INVERSION_AUTO) { + int as_count=0; deb_setf("autosearch enabled.\n"); @@ -572,20 +389,22 @@ static int dib3000mb_set_frontend(struct wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_AUTO_SEARCH); wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF); - while ((search_state = dib3000mb_fe_read_search_status(state)) < 0); - deb_info("search_state after autosearch %d\n",search_state); - return search_state ? 0 : -EINVAL; + while ((search_state = dib3000mb_fe_read_search_status(fe)) < 0 && as_count++ < 100) + msleep(1); + + deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count); } else { wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_CTRL); wr(DIB3000MB_REG_RESTART,DIB3000MB_RESTART_OFF); - msleep(70); } + return 0; } - -static int dib3000mb_fe_init(struct dib3000mb_state *state,int mobile_mode) +static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; + wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_UP); wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC); @@ -597,9 +416,6 @@ static int dib3000mb_fe_init(struct dib3 wr(DIB3000MB_REG_ELECT_OUT_MODE,DIB3000MB_ELECT_OUT_MODE_ON); - wr(DIB3000MB_REG_QAM,DIB3000MB_QAM_RESERVED); - wr(DIB3000MB_REG_VIT_ALPHA,DIB3000MB_VIT_ALPHA_AUTO); - wr(DIB3000MB_REG_DDS_FREQ_MSB,DIB3000MB_DDS_FREQ_MSB); wr(DIB3000MB_REG_DDS_FREQ_LSB,DIB3000MB_DDS_FREQ_LSB); @@ -621,7 +437,7 @@ static int dib3000mb_fe_init(struct dib3 wr(DIB3000MB_REG_LOCK0_MASK,DIB3000MB_LOCK0_DEFAULT); wr(DIB3000MB_REG_LOCK1_MASK,DIB3000MB_LOCK1_SEARCH_4); wr(DIB3000MB_REG_LOCK2_MASK,DIB3000MB_LOCK2_DEFAULT); - wr(DIB3000MB_REG_SEQ,dib3000mb_seq[1][1][1]); + wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]); wr_foreach(dib3000mb_reg_bandwidth,dib3000mb_bandwidth_8mhz); @@ -639,8 +455,6 @@ static int dib3000mb_fe_init(struct dib3 wr(DIB3000MB_REG_UNK_108,DIB3000MB_UNK_108); wr(DIB3000MB_REG_UNK_122,DIB3000MB_UNK_122); wr(DIB3000MB_REG_MOBILE_MODE_QAM,DIB3000MB_MOBILE_MODE_QAM_OFF); - wr(DIB3000MB_REG_VIT_CODE_RATE,DIB3000MB_FEC_1_2); - wr(DIB3000MB_REG_VIT_HP,DIB3000MB_VIT_HP); wr(DIB3000MB_REG_BERLEN,DIB3000MB_BERLEN_DEFAULT); wr_foreach(dib3000mb_reg_filter_coeffs,dib3000mb_filter_coeffs); @@ -653,17 +467,186 @@ static int dib3000mb_fe_init(struct dib3 wr(DIB3000MB_REG_FIFO_142,DIB3000MB_FIFO_142); wr(DIB3000MB_REG_MPEG2_OUT_MODE,DIB3000MB_MPEG2_OUT_MODE_188); - wr(DIB3000MB_REG_FIFO_144,DIB3000MB_FIFO_144); + wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE); wr(DIB3000MB_REG_FIFO,DIB3000MB_FIFO_INHIBIT); wr(DIB3000MB_REG_FIFO_146,DIB3000MB_FIFO_146); wr(DIB3000MB_REG_FIFO_147,DIB3000MB_FIFO_147); wr(DIB3000MB_REG_DATA_IN_DIVERSITY,DIB3000MB_DATA_DIVERSITY_IN_OFF); + + if (state->config->pll_init) { + wr(DIB3000MB_REG_TUNER, + DIB3000_TUNER_WRITE_ENABLE(state->config->pll_addr)); + state->config->pll_init(fe); + wr(DIB3000MB_REG_TUNER, + DIB3000_TUNER_WRITE_DISABLE(state->config->pll_addr)); + } + return 0; } -static int dib3000mb_read_status(struct dib3000mb_state *state,fe_status_t *stat) +static int dib3000mb_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; + struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm; + fe_code_rate_t *cr; + u16 tps_val; + int inv_test1,inv_test2; + u32 dds_val, threshold = 0x800000; + + if (!rd(DIB3000MB_REG_TPS_LOCK)) + return 0; + + dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB); + if (dds_val & threshold) + inv_test1 = 0; + else if (dds_val == threshold) + inv_test1 = 1; + else + inv_test1 = 2; + + dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB); + if (dds_val & threshold) + inv_test2 = 0; + else if (dds_val == threshold) + inv_test2 = 1; + else + inv_test2 = 2; + + fep->inversion = + ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) || + ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)); + + deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) { + case DIB3000_CONSTELLATION_QPSK: + deb_getf("QPSK "); + ofdm->constellation = QPSK; + break; + case DIB3000_CONSTELLATION_16QAM: + deb_getf("QAM16 "); + ofdm->constellation = QAM_16; + break; + case DIB3000_CONSTELLATION_64QAM: + deb_getf("QAM64 "); + ofdm->constellation = QAM_64; + break; + default: + err("Unexpected constellation returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + if (rd(DIB3000MB_REG_TPS_HRCH)) { + deb_getf("HRCH ON\n"); + tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP); + cr = &ofdm->code_rate_LP; + ofdm->code_rate_HP = FEC_NONE; + + switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) { + case DIB3000_ALPHA_0: + deb_getf("HIERARCHY_NONE "); + ofdm->hierarchy_information = HIERARCHY_NONE; + break; + case DIB3000_ALPHA_1: + deb_getf("HIERARCHY_1 "); + ofdm->hierarchy_information = HIERARCHY_1; + break; + case DIB3000_ALPHA_2: + deb_getf("HIERARCHY_2 "); + ofdm->hierarchy_information = HIERARCHY_2; + break; + case DIB3000_ALPHA_4: + deb_getf("HIERARCHY_4 "); + ofdm->hierarchy_information = HIERARCHY_4; + break; + default: + err("Unexpected ALPHA value returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + } else { + deb_getf("HRCH OFF\n"); + tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP); + cr = &ofdm->code_rate_HP; + ofdm->code_rate_LP = FEC_NONE; + ofdm->hierarchy_information = HIERARCHY_NONE; + } + + switch (tps_val) { + case DIB3000_FEC_1_2: + deb_getf("FEC_1_2 "); + *cr = FEC_1_2; + break; + case DIB3000_FEC_2_3: + deb_getf("FEC_2_3 "); + *cr = FEC_2_3; + break; + case DIB3000_FEC_3_4: + deb_getf("FEC_3_4 "); + *cr = FEC_3_4; + break; + case DIB3000_FEC_5_6: + deb_getf("FEC_5_6 "); + *cr = FEC_4_5; + break; + case DIB3000_FEC_7_8: + deb_getf("FEC_7_8 "); + *cr = FEC_7_8; + break; + default: + err("Unexpected FEC returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n",tps_val); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) { + case DIB3000_GUARD_TIME_1_32: + deb_getf("GUARD_INTERVAL_1_32 "); + ofdm->guard_interval = GUARD_INTERVAL_1_32; + break; + case DIB3000_GUARD_TIME_1_16: + deb_getf("GUARD_INTERVAL_1_16 "); + ofdm->guard_interval = GUARD_INTERVAL_1_16; + break; + case DIB3000_GUARD_TIME_1_8: + deb_getf("GUARD_INTERVAL_1_8 "); + ofdm->guard_interval = GUARD_INTERVAL_1_8; + break; + case DIB3000_GUARD_TIME_1_4: + deb_getf("GUARD_INTERVAL_1_4 "); + ofdm->guard_interval = GUARD_INTERVAL_1_4; + break; + default: + err("Unexpected Guard Time returned by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) { + case DIB3000_TRANSMISSION_MODE_2K: + deb_getf("TRANSMISSION_MODE_2K "); + ofdm->transmission_mode = TRANSMISSION_MODE_2K; + break; + case DIB3000_TRANSMISSION_MODE_8K: + deb_getf("TRANSMISSION_MODE_8K "); + ofdm->transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + err("unexpected transmission mode return by TPS (%d)", tps_val); + break; + } + deb_getf("TPS: %d\n", tps_val); + + return 0; +} + +static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat) +{ + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; + *stat = 0; if (rd(DIB3000MB_REG_AGC_LOCK)) @@ -699,8 +682,10 @@ static int dib3000mb_read_status(struct return 0; } -static int dib3000mb_read_ber(struct dib3000mb_state *state,u32 *ber) +static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; + *ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB) ); return 0; } @@ -714,8 +699,10 @@ static int dib3000mb_read_ber(struct dib #define DIB3000MB_AGC_REF_dBm -14 #define DIB3000MB_GAIN_SLOPE_dBm 100 #define DIB3000MB_GAIN_DELTA_dBm -2 -static int dib3000mb_read_signal_strength(struct dib3000mb_state *state, u16 *strength) +static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; + /* TODO log10 u16 sigpow = rd(DIB3000MB_REG_SIGNAL_POWER), n_agc_power = rd(DIB3000MB_REG_AGC_POWER), @@ -748,8 +735,9 @@ static int dib3000mb_read_signal_strengt * If SNR is above 20dB, BER should be always 0. * choose 0dB as the minimum */ -static int dib3000mb_read_snr(struct dib3000mb_state *state,u16 *snr) +static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER); int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) | rd(DIB3000MB_REG_NOISE_POWER_LSB); @@ -763,24 +751,27 @@ static int dib3000mb_read_snr(struct dib *snr = (u16) ((snr_dBm / 35) * 0xffff); */ - *snr = (sigpow<<8) / (icipow > 0 ? icipow : 1); + *snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1); return 0; } -static int dib3000mb_read_unc_blocks(struct dib3000mb_state *state,u32 *unc) +static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; + *unc = rd(DIB3000MB_REG_UNC); return 0; } -static int dib3000mb_sleep(struct dib3000mb_state *state) +static int dib3000mb_sleep(struct dvb_frontend* fe) { + struct dib3000mb_state* state = (struct dib3000mb_state*) fe->demodulator_priv; + wr(DIB3000MB_REG_POWER_CONTROL,DIB3000MB_POWER_DOWN); return 0; } -static int dib3000mb_fe_get_tune_settings(struct dib3000mb_state *state, - struct dvb_frontend_tune_settings *tune) +static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) { tune->min_delay_ms = 800; tune->step_size = 166667; @@ -789,205 +780,176 @@ static int dib3000mb_fe_get_tune_setting return 0; } -static int dib3000mb_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe) { - struct dib3000mb_state *state = fe->data; - switch (cmd) { - case FE_GET_INFO: - deb_info("FE_GET_INFO\n"); - memcpy(arg, &dib3000mb_info, sizeof(struct dvb_frontend_info)); - return 0; - break; - - case FE_READ_STATUS: - deb_info("FE_READ_STATUS\n"); - return dib3000mb_read_status(state,(fe_status_t *)arg); - break; - - case FE_READ_BER: - deb_info("FE_READ_BER\n"); - return dib3000mb_read_ber(state,(u32 *)arg); - break; - - case FE_READ_SIGNAL_STRENGTH: - deb_info("FE_READ_SIG_STRENGTH\n"); - return dib3000mb_read_signal_strength(state,(u16 *) arg); - break; - - case FE_READ_SNR: - deb_info("FE_READ_SNR\n"); - return dib3000mb_read_snr(state,(u16 *) arg); - break; - - case FE_READ_UNCORRECTED_BLOCKS: - deb_info("FE_READ_UNCORRECTED_BLOCKS\n"); - return dib3000mb_read_unc_blocks(state,(u32 *) arg); - break; - - case FE_SET_FRONTEND: - deb_info("FE_SET_FRONTEND\n"); - return dib3000mb_set_frontend(state,(struct dvb_frontend_parameters *) arg,1); - break; - - case FE_GET_FRONTEND: - deb_info("FE_GET_FRONTEND\n"); - return dib3000mb_get_frontend(state,(struct dvb_frontend_parameters *) arg); - break; + return dib3000mb_fe_init(fe, 0); +} - case FE_SLEEP: - deb_info("FE_SLEEP\n"); - return dib3000mb_sleep(state); - break; +static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) +{ + return dib3000mb_set_frontend(fe, fep, 1); +} - case FE_INIT: - deb_info("FE_INIT\n"); - return dib3000mb_fe_init(state,0); - break; +static void dib3000mb_release(struct dvb_frontend* fe) +{ + struct dib3000mb_state *state = (struct dib3000mb_state*) fe->demodulator_priv; + kfree(state); +} - case FE_GET_TUNE_SETTINGS: - deb_info("GET_TUNE_SETTINGS"); - return dib3000mb_fe_get_tune_settings(state, (struct - dvb_frontend_tune_settings *) arg); +/* pid filter and transfer stuff */ +/* fetch a pid from pid_list */ +static int dib3000_get_pid_index(struct dib3000_pid pid_list[], + int num_pids, int pid, spinlock_t *pid_list_lock,int onoff) +{ + int i,ret = -1; + unsigned long flags; + + spin_lock_irqsave(pid_list_lock,flags); + for (i=0; i < num_pids; i++) + if (onoff) { + if (!pid_list[i].active) { + pid_list[i].pid = pid; + pid_list[i].active = 1; + ret = i; break; - case FE_SET_TONE: - case FE_SET_VOLTAGE: - default: - return -EOPNOTSUPP; + } + } else { + if (pid_list[i].active && pid_list[i].pid == pid) { + pid_list[i].pid = 0; + pid_list[i].active = 0; + ret = i; break; } - return 0; } -static struct i2c_client client_template; + spin_unlock_irqrestore(pid_list_lock,flags); + return ret; +} -static int dib3000mb_attach_adapter(struct i2c_adapter *adapter) +static int dib3000mb_pid_control(struct dvb_frontend *fe,int pid,int onoff) { - struct i2c_client *client; - struct dib3000mb_state *state; - int ret = -ENOMEM; + struct dib3000mb_state *state = fe->demodulator_priv; + int index = dib3000_get_pid_index(state->pid_list, DIB3000MB_NUM_PIDS, pid, &state->pid_list_lock,onoff); + pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0); - deb_info("i2c probe with adapter '%s'.\n",adapter->name); + deb_info("setting pid 0x%x on index %d\n",pid,index); - if ((state = kmalloc(sizeof(struct dib3000mb_state),GFP_KERNEL)) == NULL) + if (index >= 0) { + wr(index+DIB3000MB_REG_FIRST_PID,pid); + } else { + err("no more pids for filtering."); return -ENOMEM; + } + return 0; +} +static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff) +{ + struct dib3000mb_state *state = (struct dib3000mb_state*) fe->demodulator_priv; - if ((client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL) - goto i2c_kmalloc_err; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - - client->adapter = adapter; - client->addr = 0x10; - state->i2c = client; - - i2c_set_clientdata(client,state); - - state->manufactor_id = dib3000mb_read_reg(client, DIB3000MB_REG_MANUFACTOR_ID); - if (state->manufactor_id != 0x01b3) { - ret = -ENODEV; - goto probe_err; + if (onoff) { + wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE); + } else { + wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); } - - state->device_id = dib3000mb_read_reg(client,DIB3000MB_REG_DEVICE_ID); - if (state->device_id != 0x3000) { - ret = -ENODEV; - goto probe_err; + return 0; } - info("found a DiBCom (0x%04x) 3000-MB DVB-T frontend (ver: %x).", - state->manufactor_id, state->device_id); - - if ((ret = i2c_attach_client(client))) - goto i2c_attach_err; - - if (state->dvb == NULL) - goto i2c_attach_err; - - if ((ret = dvb_register_frontend(dib3000mb_ioctl, state->dvb, state, - &dib3000mb_info, THIS_MODULE))) - goto dvb_fe_err; - - - goto success; -dvb_fe_err: - i2c_detach_client(client); -i2c_attach_err: -probe_err: - kfree(client); -i2c_kmalloc_err: - kfree(state); - return ret; -success: +static int dib3000mb_pid_filter(struct dvb_frontend *fe, int onoff) +{ + //struct dib3000mb_state *state = fe->demodulator_priv; + /* switch it off and on */ return 0; -} + } +static struct dvb_frontend_ops dib3000mb_ops; -static int dib3000mb_detach_client(struct i2c_client *client) +struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, + struct i2c_adapter* i2c, struct dib3000_xfer_ops *xfer_ops) { - struct dib3000mb_state *state = i2c_get_clientdata(client); + struct dib3000mb_state* state = NULL; + int i; - deb_info("i2c detach\n"); + /* allocate memory for the internal state */ + state = (struct dib3000mb_state*) kmalloc(sizeof(struct dib3000mb_state), GFP_KERNEL); + if (state == NULL) + goto error; - dvb_unregister_frontend(dib3000mb_ioctl, state->dvb); - i2c_detach_client(client); - kfree(client); - kfree(state); + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops)); - return 0; -} + /* check for the correct demod */ + if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM) + goto error; -static int dib3000mb_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct dib3000mb_state *state = i2c_get_clientdata(client); - deb_info("i2c command.\n"); - switch(cmd) { - case FE_REGISTER: - state->dvb = arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } + if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID) + goto error; - return 0; + /* initialize the id_list */ + deb_info("initializing %d pids for the pid_list.\n",DIB3000MB_NUM_PIDS); + state->pid_list_lock = SPIN_LOCK_UNLOCKED; + memset(state->pid_list,0,DIB3000MB_NUM_PIDS*(sizeof(struct dib3000_pid))); + for (i=0; i < DIB3000MB_NUM_PIDS; i++) { + state->pid_list[i].pid = 0; + state->pid_list[i].active = 0; } + state->feedcount = 0; -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "dib3000mb", - .id = I2C_DRIVERID_DVBFE_DIB3000MB, - .flags = I2C_DF_NOTIFY, - .attach_adapter = dib3000mb_attach_adapter, - .detach_client = dib3000mb_detach_client, - .command = dib3000mb_command, -}; + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; -static struct i2c_client client_template = { - .name = "dib3000mb", - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + /* set the xfer operations */ + xfer_ops->pid_filter = dib3000mb_pid_filter; + xfer_ops->fifo_ctrl = dib3000mb_fifo_control; + xfer_ops->pid_ctrl = dib3000mb_pid_control; -/* module stuff */ -static int __init dib3000mb_init(void) -{ - deb_info("debugging level: %d\n",debug); - return i2c_add_driver(&driver); -} + return &state->frontend; -static void __exit dib3000mb_exit(void) -{ - i2c_del_driver(&driver); -} +error: + if (state) + kfree(state); + return NULL; + } -module_init (dib3000mb_init); -module_exit (dib3000mb_exit); +static struct dvb_frontend_ops dib3000mb_ops = { + + .info = { + .name = "DiBcom 3000-MB DVB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib3000mb_release, + + .init = dib3000mb_fe_init_nonmobile, + .sleep = dib3000mb_sleep, + + .set_frontend = dib3000mb_set_frontend_and_tuner, + .get_frontend = dib3000mb_get_frontend, + .get_tune_settings = dib3000mb_fe_get_tune_settings, + + .read_status = dib3000mb_read_status, + .read_ber = dib3000mb_read_ber, + .read_signal_strength = dib3000mb_read_signal_strength, + .read_snr = dib3000mb_read_snr, + .read_ucblocks = dib3000mb_read_unc_blocks, +}; MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(dib3000mb_attach); diff -L drivers/media/dvb/frontends/dib3000mb.h -puN drivers/media/dvb/frontends/dib3000mb.h~dvb-frontend-driver-refactoring /dev/null --- 25/drivers/media/dvb/frontends/dib3000mb.h +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,657 +0,0 @@ -/* - * dib3000mb.h - * - * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.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, version 2. - * - * - * - * for more information see dib3000mb.c . - */ - -#ifndef __DIB3000MB_H_INCLUDED__ -#define __DIB3000MB_H_INCLUDED__ - -/* info and err, taken from usb.h, if there is anything available like by default, - * please change ! - */ -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , __FILE__ , ## arg) -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n" , __FILE__ , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n" , __FILE__ , ## arg) - -/* register addresses and some of their default values */ - -/* restart subsystems */ -#define DIB3000MB_REG_RESTART ( 0) - -#define DIB3000MB_RESTART_OFF ( 0) -#define DIB3000MB_RESTART_AUTO_SEARCH (1 << 1) -#define DIB3000MB_RESTART_CTRL (1 << 2) -#define DIB3000MB_RESTART_AGC (1 << 3) - -/* FFT size */ -#define DIB3000MB_REG_FFT ( 1) -#define DIB3000MB_FFT_2K ( 0) -#define DIB3000MB_FFT_8K ( 1) -#define DIB3000MB_FFT_AUTO ( 1) - -/* Guard time */ -#define DIB3000MB_REG_GUARD_TIME ( 2) -#define DIB3000MB_GUARD_TIME_1_32 ( 0) -#define DIB3000MB_GUARD_TIME_1_16 ( 1) -#define DIB3000MB_GUARD_TIME_1_8 ( 2) -#define DIB3000MB_GUARD_TIME_1_4 ( 3) -#define DIB3000MB_GUARD_TIME_AUTO ( 0) - -/* QAM */ -#define DIB3000MB_REG_QAM ( 3) -#define DIB3000MB_QAM_QPSK ( 0) -#define DIB3000MB_QAM_QAM16 ( 1) -#define DIB3000MB_QAM_QAM64 ( 2) -#define DIB3000MB_QAM_RESERVED ( 3) - -/* Alpha coefficient high priority Viterbi algorithm */ -#define DIB3000MB_REG_VIT_ALPHA ( 4) -#define DIB3000MB_VIT_ALPHA_OFF ( 0) -#define DIB3000MB_VIT_ALPHA_1 ( 1) -#define DIB3000MB_VIT_ALPHA_2 ( 2) -#define DIB3000MB_VIT_ALPHA_4 ( 4) -#define DIB3000MB_VIT_ALPHA_AUTO ( 7) - -/* spectrum inversion */ -#define DIB3000MB_REG_DDS_INV ( 5) -#define DIB3000MB_DDS_INV_OFF ( 0) -#define DIB3000MB_DDS_INV_ON ( 1) - -/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */ -#define DIB3000MB_REG_DDS_FREQ_MSB ( 6) -#define DIB3000MB_REG_DDS_FREQ_LSB ( 7) -#define DIB3000MB_DDS_FREQ_MSB ( 178) -#define DIB3000MB_DDS_FREQ_LSB ( 8990) - -/* timing frequency (carrier spacing) */ -#define DIB3000MB_REG_TIMING_FREQ_MSB ( 8) -#define DIB3000MB_REG_TIMING_FREQ_LSB ( 9) - -static u16 dib3000mb_reg_timing_freq[] = { - DIB3000MB_REG_TIMING_FREQ_MSB, DIB3000MB_REG_TIMING_FREQ_LSB -}; -static u16 dib3000mb_timing_freq[][2] = { - { 126 , 48873 }, /* 6 MHz */ - { 147 , 57019 }, /* 7 MHz */ - { 168 , 65164 }, /* 8 MHz */ -}; - -/* impulse noise parameter */ -#define DIB3000MB_REG_IMPNOISE_10 ( 10) -#define DIB3000MB_REG_IMPNOISE_11 ( 11) -#define DIB3000MB_REG_IMPNOISE_12 ( 12) -#define DIB3000MB_REG_IMPNOISE_13 ( 13) -#define DIB3000MB_REG_IMPNOISE_14 ( 14) -#define DIB3000MB_REG_IMPNOISE_15 ( 15) -/* 36 ??? */ -#define DIB3000MB_REG_IMPNOISE_36 ( 36) - -enum dib3000mb_impulse_noise_type { - DIB3000MB_IMPNOISE_OFF, - DIB3000MB_IMPNOISE_MOBILE, - DIB3000MB_IMPNOISE_FIXED, - DIB3000MB_IMPNOISE_DEFAULT -}; - -static u16 dib3000mb_reg_impulse_noise[] = { - DIB3000MB_REG_IMPNOISE_10, DIB3000MB_REG_IMPNOISE_11, - DIB3000MB_REG_IMPNOISE_12, DIB3000MB_REG_IMPNOISE_15, - DIB3000MB_REG_IMPNOISE_36 -}; - -static u16 dib3000mb_impulse_noise_values[][5] = { - { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */ - { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */ - { 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */ - { 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */ -}; - -/* - * Dual Automatic-Gain-Control - * - gains RF in tuner (AGC1) - * - gains IF after filtering (AGC2) - */ - -/* also from 16 to 18 */ -#define DIB3000MB_REG_AGC_GAIN_19 ( 19) -#define DIB3000MB_REG_AGC_GAIN_20 ( 20) -#define DIB3000MB_REG_AGC_GAIN_21 ( 21) -#define DIB3000MB_REG_AGC_GAIN_22 ( 22) -#define DIB3000MB_REG_AGC_GAIN_23 ( 23) -#define DIB3000MB_REG_AGC_GAIN_24 ( 24) -#define DIB3000MB_REG_AGC_GAIN_25 ( 25) -#define DIB3000MB_REG_AGC_GAIN_26 ( 26) -#define DIB3000MB_REG_AGC_GAIN_27 ( 27) -#define DIB3000MB_REG_AGC_GAIN_28 ( 28) -#define DIB3000MB_REG_AGC_GAIN_29 ( 29) -#define DIB3000MB_REG_AGC_GAIN_30 ( 30) -#define DIB3000MB_REG_AGC_GAIN_31 ( 31) -#define DIB3000MB_REG_AGC_GAIN_32 ( 32) - -static u16 dib3000mb_reg_agc_gain[] = { - DIB3000MB_REG_AGC_GAIN_19, DIB3000MB_REG_AGC_GAIN_20, DIB3000MB_REG_AGC_GAIN_21, - DIB3000MB_REG_AGC_GAIN_22, DIB3000MB_REG_AGC_GAIN_23, DIB3000MB_REG_AGC_GAIN_24, - DIB3000MB_REG_AGC_GAIN_25, DIB3000MB_REG_AGC_GAIN_26, DIB3000MB_REG_AGC_GAIN_27, - DIB3000MB_REG_AGC_GAIN_28, DIB3000MB_REG_AGC_GAIN_29, DIB3000MB_REG_AGC_GAIN_30, - DIB3000MB_REG_AGC_GAIN_31, DIB3000MB_REG_AGC_GAIN_32 }; - -static u16 dib3000mb_default_agc_gain[] = - { 0x0001, 52429, 623, 128, 166, 195, 61, /* RF ??? */ - 0x0001, 53766, 38011, 0, 90, 33, 23 }; /* IF ??? */ - -/* phase noise */ -#define DIB3000MB_REG_PHASE_NOISE_33 ( 33) -#define DIB3000MB_REG_PHASE_NOISE_34 ( 34) -#define DIB3000MB_REG_PHASE_NOISE_35 ( 35) -#define DIB3000MB_REG_PHASE_NOISE_36 ( 36) -#define DIB3000MB_REG_PHASE_NOISE_37 ( 37) -#define DIB3000MB_REG_PHASE_NOISE_38 ( 38) - -/* DIB3000MB_REG_PHASE_NOISE_36 is set when setting the impulse noise */ -static u16 dib3000mb_reg_phase_noise[] = { - DIB3000MB_REG_PHASE_NOISE_33, DIB3000MB_REG_PHASE_NOISE_34, DIB3000MB_REG_PHASE_NOISE_35, - DIB3000MB_REG_PHASE_NOISE_37, DIB3000MB_REG_PHASE_NOISE_38 -}; - -static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 }; - -/* lock duration */ -#define DIB3000MB_REG_LOCK_DURATION_39 ( 39) -#define DIB3000MB_REG_LOCK_DURATION_40 ( 40) - -static u16 dib3000mb_reg_lock_duration[] = { - DIB3000MB_REG_LOCK_DURATION_39, DIB3000MB_REG_LOCK_DURATION_40 -}; - -static u16 dib3000mb_default_lock_duration[] = { 135, 135 }; - -/* AGC loop bandwidth */ - -#define DIB3000MB_REG_AGC_BW_43 ( 43) -#define DIB3000MB_REG_AGC_BW_44 ( 44) -#define DIB3000MB_REG_AGC_BW_45 ( 45) -#define DIB3000MB_REG_AGC_BW_46 ( 46) -#define DIB3000MB_REG_AGC_BW_47 ( 47) -#define DIB3000MB_REG_AGC_BW_48 ( 48) -#define DIB3000MB_REG_AGC_BW_49 ( 49) -#define DIB3000MB_REG_AGC_BW_50 ( 50) - -static u16 dib3000mb_reg_agc_bandwidth[] = { - DIB3000MB_REG_AGC_BW_43, DIB3000MB_REG_AGC_BW_44, DIB3000MB_REG_AGC_BW_45, - DIB3000MB_REG_AGC_BW_46, DIB3000MB_REG_AGC_BW_47, DIB3000MB_REG_AGC_BW_48, - DIB3000MB_REG_AGC_BW_49, DIB3000MB_REG_AGC_BW_50 -}; - -static u16 dib3000mb_agc_bandwidth_low[] = - { 2088, 10, 2088, 10, 3448, 5, 3448, 5 }; -static u16 dib3000mb_agc_bandwidth_high[] = - { 2349, 5, 2349, 5, 2586, 2, 2586, 2 }; - -/* - * lock0 definition (coff_lock) - */ -#define DIB3000MB_REG_LOCK0_MASK ( 51) -#define DIB3000MB_LOCK0_DEFAULT ( 4) - -/* - * lock1 definition (cpil_lock) - * for auto search - * which values hide behind the lock masks - */ -#define DIB3000MB_REG_LOCK1_MASK ( 52) -#define DIB3000MB_LOCK1_SEARCH_4 (0x0004) -#define DIB3000MB_LOCK1_SEARCH_2048 (0x0800) -#define DIB3000MB_LOCK1_DEFAULT (0x0001) - -/* - * lock2 definition (fec_lock) */ -#define DIB3000MB_REG_LOCK2_MASK ( 53) -#define DIB3000MB_LOCK2_DEFAULT (0x0080) - -/* - * SEQ ? what was that again ... :) - * changes when, inversion, guard time and fft is - * either automatically detected or not - */ -#define DIB3000MB_REG_SEQ ( 54) - -/* all values have been set manually */ -static u16 dib3000mb_seq[2][2][2] = /* fft,gua, inv */ - { /* fft */ - { /* gua */ - { 0, 1 }, /* 0 0 { 0,1 } */ - { 3, 9 }, /* 0 1 { 0,1 } */ - }, - { - { 2, 5 }, /* 1 0 { 0,1 } */ - { 6, 11 }, /* 1 1 { 0,1 } */ - } - }; - -/* bandwidth */ -#define DIB3000MB_REG_BW_55 ( 55) -#define DIB3000MB_REG_BW_56 ( 56) -#define DIB3000MB_REG_BW_57 ( 57) -#define DIB3000MB_REG_BW_58 ( 58) -#define DIB3000MB_REG_BW_59 ( 59) -#define DIB3000MB_REG_BW_60 ( 60) -#define DIB3000MB_REG_BW_61 ( 61) -#define DIB3000MB_REG_BW_62 ( 62) -#define DIB3000MB_REG_BW_63 ( 63) -#define DIB3000MB_REG_BW_64 ( 64) -#define DIB3000MB_REG_BW_65 ( 65) -#define DIB3000MB_REG_BW_66 ( 66) -#define DIB3000MB_REG_BW_67 ( 67) - -static u16 dib3000mb_reg_bandwidth[] = { - DIB3000MB_REG_BW_55, DIB3000MB_REG_BW_56, DIB3000MB_REG_BW_57, - DIB3000MB_REG_BW_58, DIB3000MB_REG_BW_59, DIB3000MB_REG_BW_60, - DIB3000MB_REG_BW_61, DIB3000MB_REG_BW_62, DIB3000MB_REG_BW_63, - DIB3000MB_REG_BW_64, DIB3000MB_REG_BW_65, DIB3000MB_REG_BW_66, - DIB3000MB_REG_BW_67 -}; - -static u16 dib3000mb_bandwidth_6mhz[] = - { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 }; - -static u16 dib3000mb_bandwidth_7mhz[] = - { 0, 28, 64421, 96, 39973, 483, 3255, 0, 1000, 0, 1010, 1, 45264 }; - -static u16 dib3000mb_bandwidth_8mhz[] = - { 0, 25, 23600, 84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 }; - -#define DIB3000MB_REG_UNK_68 ( 68) -#define DIB3000MB_UNK_68 ( 0) - -#define DIB3000MB_REG_UNK_69 ( 69) -#define DIB3000MB_UNK_69 ( 0) - -#define DIB3000MB_REG_UNK_71 ( 71) -#define DIB3000MB_UNK_71 ( 0) - -#define DIB3000MB_REG_UNK_77 ( 77) -#define DIB3000MB_UNK_77 ( 6) - -#define DIB3000MB_REG_UNK_78 ( 78) -#define DIB3000MB_UNK_78 (0x0080) - -/* isi */ -#define DIB3000MB_REG_ISI ( 79) -#define DIB3000MB_ISI_ACTIVATE ( 0) -#define DIB3000MB_ISI_INHIBIT ( 1) - -/* sync impovement */ -#define DIB3000MB_REG_SYNC_IMPROVEMENT ( 84) -#define DIB3000MB_SYNC_IMPROVE_2K_1_8 ( 3) -#define DIB3000MB_SYNC_IMPROVE_DEFAULT ( 0) - -/* phase noise compensation inhibition */ -#define DIB3000MB_REG_PHASE_NOISE ( 87) -#define DIB3000MB_PHASE_NOISE_DEFAULT ( 0) - -#define DIB3000MB_REG_UNK_92 ( 92) -#define DIB3000MB_UNK_92 (0x0080) - -#define DIB3000MB_REG_UNK_96 ( 96) -#define DIB3000MB_UNK_96 (0x0010) - -#define DIB3000MB_REG_UNK_97 ( 97) -#define DIB3000MB_UNK_97 (0x0009) - -/* mobile mode ??? */ -#define DIB3000MB_REG_MOBILE_MODE ( 101) -#define DIB3000MB_MOBILE_MODE_ON ( 1) -#define DIB3000MB_MOBILE_MODE_OFF ( 0) - -#define DIB3000MB_REG_UNK_106 ( 106) -#define DIB3000MB_UNK_106 (0x0080) - -#define DIB3000MB_REG_UNK_107 ( 107) -#define DIB3000MB_UNK_107 (0x0080) - -#define DIB3000MB_REG_UNK_108 ( 108) -#define DIB3000MB_UNK_108 (0x0080) - -/* fft */ -#define DIB3000MB_REG_UNK_121 ( 121) -#define DIB3000MB_UNK_121_2K ( 7) -#define DIB3000MB_UNK_121_DEFAULT ( 5) - -#define DIB3000MB_REG_UNK_122 ( 122) -#define DIB3000MB_UNK_122 ( 2867) - -/* QAM for mobile mode */ -#define DIB3000MB_REG_MOBILE_MODE_QAM ( 126) -#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3) -#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16 ( 1) -#define DIB3000MB_MOBILE_MODE_QAM_OFF ( 0) - -/* - * data diversity when having more than one chip on-board - * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY - */ -#define DIB3000MB_REG_DATA_IN_DIVERSITY ( 127) -#define DIB3000MB_DATA_DIVERSITY_IN_OFF ( 0) -#define DIB3000MB_DATA_DIVERSITY_IN_ON ( 2) - -/* vit hrch */ -#define DIB3000MB_REG_VIT_HRCH ( 128) -#define DIB3000MB_VIT_HRCH_ON ( 1) -#define DIB3000MB_VIT_HRCH_OFF ( 0) - -/* vit code rate */ -#define DIB3000MB_REG_VIT_CODE_RATE ( 129) - -/* forward error correction code rates */ -#define DIB3000MB_FEC_1_2 ( 1) -#define DIB3000MB_FEC_2_3 ( 2) -#define DIB3000MB_FEC_3_4 ( 3) -#define DIB3000MB_FEC_5_6 ( 5) -#define DIB3000MB_FEC_7_8 ( 7) - -/* vit select hp */ -#define DIB3000MB_REG_VIT_HP ( 130) -#define DIB3000MB_VIT_LP ( 0) -#define DIB3000MB_VIT_HP ( 1) - -/* time frame for Bit-Error-Rate calculation */ -#define DIB3000MB_REG_BERLEN ( 135) -#define DIB3000MB_BERLEN_LONG ( 0) -#define DIB3000MB_BERLEN_DEFAULT ( 1) -#define DIB3000MB_BERLEN_MEDIUM ( 2) -#define DIB3000MB_BERLEN_SHORT ( 3) - -/* 142 - 152 FIFO parameters - * which is what ? - */ - -#define DIB3000MB_REG_FIFO_142 ( 142) -#define DIB3000MB_FIFO_142 ( 0) - -/* MPEG2 TS output mode */ -#define DIB3000MB_REG_MPEG2_OUT_MODE ( 143) -#define DIB3000MB_MPEG2_OUT_MODE_204 ( 0) -#define DIB3000MB_MPEG2_OUT_MODE_188 ( 1) - -#define DIB3000MB_REG_FIFO_144 ( 144) -#define DIB3000MB_FIFO_144 ( 1) - -#define DIB3000MB_REG_FIFO ( 145) -#define DIB3000MB_FIFO_INHIBIT ( 1) -#define DIB3000MB_FIFO_ACTIVATE ( 0) - -#define DIB3000MB_REG_FIFO_146 ( 146) -#define DIB3000MB_FIFO_146 ( 3) - -#define DIB3000MB_REG_FIFO_147 ( 147) -#define DIB3000MB_FIFO_147 (0x0100) - -/* - * pidfilter - * it is not a hardware pidfilter but a filter which drops all pids - * except the ones set. Necessary because of the limited USB1.1 bandwidth. - */ -#define DIB3000MB_REG_FILTER_PID_0 ( 153) -#define DIB3000MB_REG_FILTER_PID_1 ( 154) -#define DIB3000MB_REG_FILTER_PID_2 ( 155) -#define DIB3000MB_REG_FILTER_PID_3 ( 156) -#define DIB3000MB_REG_FILTER_PID_4 ( 157) -#define DIB3000MB_REG_FILTER_PID_5 ( 158) -#define DIB3000MB_REG_FILTER_PID_6 ( 159) -#define DIB3000MB_REG_FILTER_PID_7 ( 160) -#define DIB3000MB_REG_FILTER_PID_8 ( 161) -#define DIB3000MB_REG_FILTER_PID_9 ( 162) -#define DIB3000MB_REG_FILTER_PID_10 ( 163) -#define DIB3000MB_REG_FILTER_PID_11 ( 164) -#define DIB3000MB_REG_FILTER_PID_12 ( 165) -#define DIB3000MB_REG_FILTER_PID_13 ( 166) -#define DIB3000MB_REG_FILTER_PID_14 ( 167) -#define DIB3000MB_REG_FILTER_PID_15 ( 168) - -#define DIB3000MB_ACTIVATE_FILTERING (0x2000) - -/* - * output mode - * USB devices have to use 'slave'-mode - * see also DIB3000MB_REG_ELECT_OUT_MODE - */ -#define DIB3000MB_REG_OUTPUT_MODE ( 169) -#define DIB3000MB_OUTPUT_MODE_GATED_CLK ( 0) -#define DIB3000MB_OUTPUT_MODE_CONT_CLK ( 1) -#define DIB3000MB_OUTPUT_MODE_SERIAL ( 2) -#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY ( 5) -#define DIB3000MB_OUTPUT_MODE_SLAVE ( 6) - -/* irq event mask */ -#define DIB3000MB_REG_IRQ_EVENT_MASK ( 170) -#define DIB3000MB_IRQ_EVENT_MASK ( 0) - -/* filter coefficients */ -#define DIB3000MB_REG_FILTER_COEF_171 ( 171) -#define DIB3000MB_REG_FILTER_COEF_172 ( 172) -#define DIB3000MB_REG_FILTER_COEF_173 ( 173) -#define DIB3000MB_REG_FILTER_COEF_174 ( 174) -#define DIB3000MB_REG_FILTER_COEF_175 ( 175) -#define DIB3000MB_REG_FILTER_COEF_176 ( 176) -#define DIB3000MB_REG_FILTER_COEF_177 ( 177) -#define DIB3000MB_REG_FILTER_COEF_178 ( 178) -#define DIB3000MB_REG_FILTER_COEF_179 ( 179) -#define DIB3000MB_REG_FILTER_COEF_180 ( 180) -#define DIB3000MB_REG_FILTER_COEF_181 ( 181) -#define DIB3000MB_REG_FILTER_COEF_182 ( 182) -#define DIB3000MB_REG_FILTER_COEF_183 ( 183) -#define DIB3000MB_REG_FILTER_COEF_184 ( 184) -#define DIB3000MB_REG_FILTER_COEF_185 ( 185) -#define DIB3000MB_REG_FILTER_COEF_186 ( 186) -#define DIB3000MB_REG_FILTER_COEF_187 ( 187) -#define DIB3000MB_REG_FILTER_COEF_188 ( 188) -#define DIB3000MB_REG_FILTER_COEF_189 ( 189) -#define DIB3000MB_REG_FILTER_COEF_190 ( 190) -#define DIB3000MB_REG_FILTER_COEF_191 ( 191) -#define DIB3000MB_REG_FILTER_COEF_192 ( 192) -#define DIB3000MB_REG_FILTER_COEF_193 ( 193) -#define DIB3000MB_REG_FILTER_COEF_194 ( 194) - -static u16 dib3000mb_reg_filter_coeffs[] = { - DIB3000MB_REG_FILTER_COEF_171, DIB3000MB_REG_FILTER_COEF_172, DIB3000MB_REG_FILTER_COEF_173, - DIB3000MB_REG_FILTER_COEF_174, DIB3000MB_REG_FILTER_COEF_175, DIB3000MB_REG_FILTER_COEF_176, - DIB3000MB_REG_FILTER_COEF_177, DIB3000MB_REG_FILTER_COEF_178, DIB3000MB_REG_FILTER_COEF_179, - DIB3000MB_REG_FILTER_COEF_180, DIB3000MB_REG_FILTER_COEF_181, DIB3000MB_REG_FILTER_COEF_182, - DIB3000MB_REG_FILTER_COEF_183, DIB3000MB_REG_FILTER_COEF_184, DIB3000MB_REG_FILTER_COEF_185, - DIB3000MB_REG_FILTER_COEF_186, DIB3000MB_REG_FILTER_COEF_188, - DIB3000MB_REG_FILTER_COEF_189, DIB3000MB_REG_FILTER_COEF_190, DIB3000MB_REG_FILTER_COEF_191, - DIB3000MB_REG_FILTER_COEF_192, DIB3000MB_REG_FILTER_COEF_194 -}; - -static u16 dib3000mb_filter_coeffs[] = { - 226, 160, 29, - 979, 998, 19, - 22, 1019, 1006, - 1022, 12, 6, - 1017, 1017, 3, - 6, 1019, - 1021, 2, 3, - 1, 0, -}; - -/* - * mobile algorithm (when you are moving with your device) - * but not faster than 90 km/h - */ -#define DIB3000MB_REG_MOBILE_ALGO ( 195) -#define DIB3000MB_MOBILE_ALGO_ON ( 0) -#define DIB3000MB_MOBILE_ALGO_OFF ( 1) - -/* multiple demodulators algorithm */ -#define DIB3000MB_REG_MULTI_DEMOD_MSB ( 206) -#define DIB3000MB_REG_MULTI_DEMOD_LSB ( 207) - -/* terminator, no more demods */ -#define DIB3000MB_MULTI_DEMOD_MSB ( 32767) -#define DIB3000MB_MULTI_DEMOD_LSB ( 4095) - -/* bring the device into a known */ -#define DIB3000MB_REG_RESET_DEVICE ( 1024) -#define DIB3000MB_RESET_DEVICE (0x812c) -#define DIB3000MB_RESET_DEVICE_RST ( 0) - -/* identification registers, manufactor an the device */ -#define DIB3000MB_REG_MANUFACTOR_ID ( 1025) -#define DIB3000MB_MANUFACTOR_ID_DIBCOM (0x01B3) - -#define DIB3000MB_REG_DEVICE_ID ( 1026) -#define DIB3000MB_DEVICE_ID (0x3000) - -/* hardware clock configuration */ -#define DIB3000MB_REG_CLOCK ( 1027) -#define DIB3000MB_CLOCK_DEFAULT (0x9000) -#define DIB3000MB_CLOCK_DIVERSITY (0x92b0) - -/* power down config */ -#define DIB3000MB_REG_POWER_CONTROL ( 1028) -#define DIB3000MB_POWER_DOWN ( 1) -#define DIB3000MB_POWER_UP ( 0) - -/* electrical output mode */ -#define DIB3000MB_REG_ELECT_OUT_MODE ( 1029) -#define DIB3000MB_ELECT_OUT_MODE_OFF ( 0) -#define DIB3000MB_ELECT_OUT_MODE_ON ( 1) - -/* set the tuner i2c address */ -#define DIB3000MB_REG_TUNER ( 1089) -#define DIB3000MB_TUNER_ADDR_DEFAULT ( 194) -#define DIB3000MB_ACTIVATE_TUNER_XFER(a) (0xffff & (a << 7)) -#define DIB3000MB_DEACTIVATE_TUNER_XFER(a) (0xffff & ((a << 7) + 0x80)) - -/* monitoring registers (read only) */ - -/* agc loop locked (size: 1) */ -#define DIB3000MB_REG_AGC_LOCK ( 324) - -/* agc power (size: 16) */ -#define DIB3000MB_REG_AGC_POWER ( 325) - -/* agc1 value (16) */ -#define DIB3000MB_REG_AGC1_VALUE ( 326) - -/* agc2 value (16) */ -#define DIB3000MB_REG_AGC2_VALUE ( 327) - -/* total RF power (16), can be used for signal strength */ -#define DIB3000MB_REG_RF_POWER ( 328) - -/* dds_frequency with offset (24) */ -#define DIB3000MB_REG_DDS_VALUE_MSB ( 339) -#define DIB3000MB_REG_DDS_VALUE_LSB ( 340) - -/* timing offset signed (24) */ -#define DIB3000MB_REG_TIMING_OFFSET_MSB ( 341) -#define DIB3000MB_REG_TIMING_OFFSET_LSB ( 342) - -/* fft start position (13) */ -#define DIB3000MB_REG_FFT_WINDOW_POS ( 353) - -/* carriers locked (1) */ -#define DIB3000MB_REG_CARRIER_LOCK ( 355) - -/* noise power (24) */ -#define DIB3000MB_REG_NOISE_POWER_MSB ( 372) -#define DIB3000MB_REG_NOISE_POWER_LSB ( 373) - -#define DIB3000MB_REG_MOBILE_NOISE_MSB ( 374) -#define DIB3000MB_REG_MOBILE_NOISE_LSB ( 375) - -/* - * signal power (16), this and the above can be - * used to calculate the signal/noise - ratio - */ -#define DIB3000MB_REG_SIGNAL_POWER ( 380) - -/* mer (24) */ -#define DIB3000MB_REG_MER_MSB ( 381) -#define DIB3000MB_REG_MER_LSB ( 382) - -/* - * Transmission Parameter Signalling (TPS) - * the following registers can be used to get TPS-information. - * The values are according to the DVB-T standard. - */ - -/* TPS locked (1) */ -#define DIB3000MB_REG_TPS_LOCK ( 394) - -/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */ -#define DIB3000MB_REG_TPS_QAM ( 398) - -/* hierarchy from TPS (1) */ -#define DIB3000MB_REG_TPS_HRCH ( 399) - -/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */ -#define DIB3000MB_REG_TPS_VIT_ALPHA ( 400) - -/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */ -#define DIB3000MB_REG_TPS_CODE_RATE_HP ( 401) - -/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */ -#define DIB3000MB_REG_TPS_CODE_RATE_LP ( 402) - -/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */ -#define DIB3000MB_REG_TPS_GUARD_TIME ( 403) - -/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */ -#define DIB3000MB_REG_TPS_FFT ( 404) - -/* cell id from TPS (16) */ -#define DIB3000MB_REG_TPS_CELL_ID ( 406) - -/* TPS (68) */ -#define DIB3000MB_REG_TPS_1 ( 408) -#define DIB3000MB_REG_TPS_2 ( 409) -#define DIB3000MB_REG_TPS_3 ( 410) -#define DIB3000MB_REG_TPS_4 ( 411) -#define DIB3000MB_REG_TPS_5 ( 412) - -/* bit error rate (before RS correction) (21) */ -#define DIB3000MB_REG_BER_MSB ( 414) -#define DIB3000MB_REG_BER_LSB ( 415) - -/* packet error rate (uncorrected TS packets) (16) */ -#define DIB3000MB_REG_PACKET_ERROR_RATE ( 417) - -/* uncorrected packet count (16) */ -#define DIB3000MB_REG_UNC ( 420) - -/* viterbi locked (1) */ -#define DIB3000MB_REG_VIT_LCK ( 421) - -/* viterbi inidcator (16) */ -#define DIB3000MB_REG_VIT_INDICATOR ( 422) - -/* transport stream sync lock (1) */ -#define DIB3000MB_REG_TS_SYNC_LOCK ( 423) - -/* transport stream RS lock (1) */ -#define DIB3000MB_REG_TS_RS_LOCK ( 424) - -/* lock mask 0 value (1) */ -#define DIB3000MB_REG_LOCK0_VALUE ( 425) - -/* lock mask 1 value (1) */ -#define DIB3000MB_REG_LOCK1_VALUE ( 426) - -/* lock mask 2 value (1) */ -#define DIB3000MB_REG_LOCK2_VALUE ( 427) - -/* interrupt pending for auto search */ -#define DIB3000MB_REG_AS_IRQ_PENDING ( 434) - -#endif diff -puN /dev/null drivers/media/dvb/frontends/dib3000mb_priv.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/dib3000mb_priv.h 2004-11-21 17:13:23.436055560 -0800 @@ -0,0 +1,467 @@ +/* + * dib3000mb_priv.h + * + * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.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, version 2. + * + * for more information see dib3000mb.c . + */ + +#ifndef __DIB3000MB_PRIV_H_INCLUDED__ +#define __DIB3000MB_PRIV_H_INCLUDED__ + +/* register addresses and some of their default values */ + +/* restart subsystems */ +#define DIB3000MB_REG_RESTART ( 0) + +#define DIB3000MB_RESTART_OFF ( 0) +#define DIB3000MB_RESTART_AUTO_SEARCH (1 << 1) +#define DIB3000MB_RESTART_CTRL (1 << 2) +#define DIB3000MB_RESTART_AGC (1 << 3) + +/* FFT size */ +#define DIB3000MB_REG_FFT ( 1) + +/* Guard time */ +#define DIB3000MB_REG_GUARD_TIME ( 2) + +/* QAM */ +#define DIB3000MB_REG_QAM ( 3) + +/* Alpha coefficient high priority Viterbi algorithm */ +#define DIB3000MB_REG_VIT_ALPHA ( 4) + +/* spectrum inversion */ +#define DIB3000MB_REG_DDS_INV ( 5) + +/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */ +#define DIB3000MB_REG_DDS_FREQ_MSB ( 6) +#define DIB3000MB_REG_DDS_FREQ_LSB ( 7) +#define DIB3000MB_DDS_FREQ_MSB ( 178) +#define DIB3000MB_DDS_FREQ_LSB ( 8990) + +/* timing frequency (carrier spacing) */ +static u16 dib3000mb_reg_timing_freq[] = { 8,9 }; +static u16 dib3000mb_timing_freq[][2] = { + { 126 , 48873 }, /* 6 MHz */ + { 147 , 57019 }, /* 7 MHz */ + { 168 , 65164 }, /* 8 MHz */ +}; + +/* impulse noise parameter */ +/* 36 ??? */ + +static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 }; + +enum dib3000mb_impulse_noise_type { + DIB3000MB_IMPNOISE_OFF, + DIB3000MB_IMPNOISE_MOBILE, + DIB3000MB_IMPNOISE_FIXED, + DIB3000MB_IMPNOISE_DEFAULT +}; + +static u16 dib3000mb_impulse_noise_values[][5] = { + { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */ + { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */ + { 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */ + { 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */ +}; + +/* + * Dual Automatic-Gain-Control + * - gains RF in tuner (AGC1) + * - gains IF after filtering (AGC2) + */ + +/* also from 16 to 18 */ +static u16 dib3000mb_reg_agc_gain[] = { + 19,20,21,22,23,24,25,26,27,28,29,30,31,32 +}; + +static u16 dib3000mb_default_agc_gain[] = + { 0x0001, 52429, 623, 128, 166, 195, 61, /* RF ??? */ + 0x0001, 53766, 38011, 0, 90, 33, 23 }; /* IF ??? */ + +/* phase noise */ +/* 36 is set when setting the impulse noise */ +static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 }; + +static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 }; + +/* lock duration */ +static u16 dib3000mb_reg_lock_duration[] = { 39,40 }; +static u16 dib3000mb_default_lock_duration[] = { 135, 135 }; + +/* AGC loop bandwidth */ +static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 }; + +static u16 dib3000mb_agc_bandwidth_low[] = + { 2088, 10, 2088, 10, 3448, 5, 3448, 5 }; +static u16 dib3000mb_agc_bandwidth_high[] = + { 2349, 5, 2349, 5, 2586, 2, 2586, 2 }; + +/* + * lock0 definition (coff_lock) + */ +#define DIB3000MB_REG_LOCK0_MASK ( 51) +#define DIB3000MB_LOCK0_DEFAULT ( 4) + +/* + * lock1 definition (cpil_lock) + * for auto search + * which values hide behind the lock masks + */ +#define DIB3000MB_REG_LOCK1_MASK ( 52) +#define DIB3000MB_LOCK1_SEARCH_4 (0x0004) +#define DIB3000MB_LOCK1_SEARCH_2048 (0x0800) +#define DIB3000MB_LOCK1_DEFAULT (0x0001) + +/* + * lock2 definition (fec_lock) */ +#define DIB3000MB_REG_LOCK2_MASK ( 53) +#define DIB3000MB_LOCK2_DEFAULT (0x0080) + +/* + * SEQ ? what was that again ... :) + * changes when, inversion, guard time and fft is + * either automatically detected or not + */ +#define DIB3000MB_REG_SEQ ( 54) + +/* bandwidth */ +static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 }; +static u16 dib3000mb_bandwidth_6mhz[] = + { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 }; + +static u16 dib3000mb_bandwidth_7mhz[] = + { 0, 28, 64421, 96, 39973, 483, 3255, 0, 1000, 0, 1010, 1, 45264 }; + +static u16 dib3000mb_bandwidth_8mhz[] = + { 0, 25, 23600, 84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 }; + +#define DIB3000MB_REG_UNK_68 ( 68) +#define DIB3000MB_UNK_68 ( 0) + +#define DIB3000MB_REG_UNK_69 ( 69) +#define DIB3000MB_UNK_69 ( 0) + +#define DIB3000MB_REG_UNK_71 ( 71) +#define DIB3000MB_UNK_71 ( 0) + +#define DIB3000MB_REG_UNK_77 ( 77) +#define DIB3000MB_UNK_77 ( 6) + +#define DIB3000MB_REG_UNK_78 ( 78) +#define DIB3000MB_UNK_78 (0x0080) + +/* isi */ +#define DIB3000MB_REG_ISI ( 79) +#define DIB3000MB_ISI_ACTIVATE ( 0) +#define DIB3000MB_ISI_INHIBIT ( 1) + +/* sync impovement */ +#define DIB3000MB_REG_SYNC_IMPROVEMENT ( 84) +#define DIB3000MB_SYNC_IMPROVE_2K_1_8 ( 3) +#define DIB3000MB_SYNC_IMPROVE_DEFAULT ( 0) + +/* phase noise compensation inhibition */ +#define DIB3000MB_REG_PHASE_NOISE ( 87) +#define DIB3000MB_PHASE_NOISE_DEFAULT ( 0) + +#define DIB3000MB_REG_UNK_92 ( 92) +#define DIB3000MB_UNK_92 (0x0080) + +#define DIB3000MB_REG_UNK_96 ( 96) +#define DIB3000MB_UNK_96 (0x0010) + +#define DIB3000MB_REG_UNK_97 ( 97) +#define DIB3000MB_UNK_97 (0x0009) + +/* mobile mode ??? */ +#define DIB3000MB_REG_MOBILE_MODE ( 101) +#define DIB3000MB_MOBILE_MODE_ON ( 1) +#define DIB3000MB_MOBILE_MODE_OFF ( 0) + +#define DIB3000MB_REG_UNK_106 ( 106) +#define DIB3000MB_UNK_106 (0x0080) + +#define DIB3000MB_REG_UNK_107 ( 107) +#define DIB3000MB_UNK_107 (0x0080) + +#define DIB3000MB_REG_UNK_108 ( 108) +#define DIB3000MB_UNK_108 (0x0080) + +/* fft */ +#define DIB3000MB_REG_UNK_121 ( 121) +#define DIB3000MB_UNK_121_2K ( 7) +#define DIB3000MB_UNK_121_DEFAULT ( 5) + +#define DIB3000MB_REG_UNK_122 ( 122) +#define DIB3000MB_UNK_122 ( 2867) + +/* QAM for mobile mode */ +#define DIB3000MB_REG_MOBILE_MODE_QAM ( 126) +#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3) +#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16 ( 1) +#define DIB3000MB_MOBILE_MODE_QAM_OFF ( 0) + +/* + * data diversity when having more than one chip on-board + * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY + */ +#define DIB3000MB_REG_DATA_IN_DIVERSITY ( 127) +#define DIB3000MB_DATA_DIVERSITY_IN_OFF ( 0) +#define DIB3000MB_DATA_DIVERSITY_IN_ON ( 2) + +/* vit hrch */ +#define DIB3000MB_REG_VIT_HRCH ( 128) + +/* vit code rate */ +#define DIB3000MB_REG_VIT_CODE_RATE ( 129) + +/* vit select hp */ +#define DIB3000MB_REG_VIT_HP ( 130) + +/* time frame for Bit-Error-Rate calculation */ +#define DIB3000MB_REG_BERLEN ( 135) +#define DIB3000MB_BERLEN_LONG ( 0) +#define DIB3000MB_BERLEN_DEFAULT ( 1) +#define DIB3000MB_BERLEN_MEDIUM ( 2) +#define DIB3000MB_BERLEN_SHORT ( 3) + +/* 142 - 152 FIFO parameters + * which is what ? + */ + +#define DIB3000MB_REG_FIFO_142 ( 142) +#define DIB3000MB_FIFO_142 ( 0) + +/* MPEG2 TS output mode */ +#define DIB3000MB_REG_MPEG2_OUT_MODE ( 143) +#define DIB3000MB_MPEG2_OUT_MODE_204 ( 0) +#define DIB3000MB_MPEG2_OUT_MODE_188 ( 1) + +#define DIB3000MB_REG_PID_PARSE ( 144) +#define DIB3000MB_PID_PARSE_INHIBIT ( 0) +#define DIB3000MB_PID_PARSE_ACTIVATE ( 1) + +#define DIB3000MB_REG_FIFO ( 145) +#define DIB3000MB_FIFO_INHIBIT ( 1) +#define DIB3000MB_FIFO_ACTIVATE ( 0) + +#define DIB3000MB_REG_FIFO_146 ( 146) +#define DIB3000MB_FIFO_146 ( 3) + +#define DIB3000MB_REG_FIFO_147 ( 147) +#define DIB3000MB_FIFO_147 (0x0100) + +/* + * pidfilter + * it is not a hardware pidfilter but a filter which drops all pids + * except the ones set. Necessary because of the limited USB1.1 bandwidth. + * regs 153-168 + */ + +#define DIB3000MB_REG_FIRST_PID ( 153) +#define DIB3000MB_NUM_PIDS ( 16) + +/* + * output mode + * USB devices have to use 'slave'-mode + * see also DIB3000MB_REG_ELECT_OUT_MODE + */ +#define DIB3000MB_REG_OUTPUT_MODE ( 169) +#define DIB3000MB_OUTPUT_MODE_GATED_CLK ( 0) +#define DIB3000MB_OUTPUT_MODE_CONT_CLK ( 1) +#define DIB3000MB_OUTPUT_MODE_SERIAL ( 2) +#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY ( 5) +#define DIB3000MB_OUTPUT_MODE_SLAVE ( 6) + +/* irq event mask */ +#define DIB3000MB_REG_IRQ_EVENT_MASK ( 170) +#define DIB3000MB_IRQ_EVENT_MASK ( 0) + +/* filter coefficients */ +static u16 dib3000mb_reg_filter_coeffs[] = { + 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, + 188, 189, 190, 191, 192, 194 +}; + +static u16 dib3000mb_filter_coeffs[] = { + 226, 160, 29, + 979, 998, 19, + 22, 1019, 1006, + 1022, 12, 6, + 1017, 1017, 3, + 6, 1019, + 1021, 2, 3, + 1, 0, +}; + +/* + * mobile algorithm (when you are moving with your device) + * but not faster than 90 km/h + */ +#define DIB3000MB_REG_MOBILE_ALGO ( 195) +#define DIB3000MB_MOBILE_ALGO_ON ( 0) +#define DIB3000MB_MOBILE_ALGO_OFF ( 1) + +/* multiple demodulators algorithm */ +#define DIB3000MB_REG_MULTI_DEMOD_MSB ( 206) +#define DIB3000MB_REG_MULTI_DEMOD_LSB ( 207) + +/* terminator, no more demods */ +#define DIB3000MB_MULTI_DEMOD_MSB ( 32767) +#define DIB3000MB_MULTI_DEMOD_LSB ( 4095) + +/* bring the device into a known */ +#define DIB3000MB_REG_RESET_DEVICE ( 1024) +#define DIB3000MB_RESET_DEVICE (0x812c) +#define DIB3000MB_RESET_DEVICE_RST ( 0) + +/* hardware clock configuration */ +#define DIB3000MB_REG_CLOCK ( 1027) +#define DIB3000MB_CLOCK_DEFAULT (0x9000) +#define DIB3000MB_CLOCK_DIVERSITY (0x92b0) + +/* power down config */ +#define DIB3000MB_REG_POWER_CONTROL ( 1028) +#define DIB3000MB_POWER_DOWN ( 1) +#define DIB3000MB_POWER_UP ( 0) + +/* electrical output mode */ +#define DIB3000MB_REG_ELECT_OUT_MODE ( 1029) +#define DIB3000MB_ELECT_OUT_MODE_OFF ( 0) +#define DIB3000MB_ELECT_OUT_MODE_ON ( 1) + +/* set the tuner i2c address */ +#define DIB3000MB_REG_TUNER ( 1089) + +/* monitoring registers (read only) */ + +/* agc loop locked (size: 1) */ +#define DIB3000MB_REG_AGC_LOCK ( 324) + +/* agc power (size: 16) */ +#define DIB3000MB_REG_AGC_POWER ( 325) + +/* agc1 value (16) */ +#define DIB3000MB_REG_AGC1_VALUE ( 326) + +/* agc2 value (16) */ +#define DIB3000MB_REG_AGC2_VALUE ( 327) + +/* total RF power (16), can be used for signal strength */ +#define DIB3000MB_REG_RF_POWER ( 328) + +/* dds_frequency with offset (24) */ +#define DIB3000MB_REG_DDS_VALUE_MSB ( 339) +#define DIB3000MB_REG_DDS_VALUE_LSB ( 340) + +/* timing offset signed (24) */ +#define DIB3000MB_REG_TIMING_OFFSET_MSB ( 341) +#define DIB3000MB_REG_TIMING_OFFSET_LSB ( 342) + +/* fft start position (13) */ +#define DIB3000MB_REG_FFT_WINDOW_POS ( 353) + +/* carriers locked (1) */ +#define DIB3000MB_REG_CARRIER_LOCK ( 355) + +/* noise power (24) */ +#define DIB3000MB_REG_NOISE_POWER_MSB ( 372) +#define DIB3000MB_REG_NOISE_POWER_LSB ( 373) + +#define DIB3000MB_REG_MOBILE_NOISE_MSB ( 374) +#define DIB3000MB_REG_MOBILE_NOISE_LSB ( 375) + +/* + * signal power (16), this and the above can be + * used to calculate the signal/noise - ratio + */ +#define DIB3000MB_REG_SIGNAL_POWER ( 380) + +/* mer (24) */ +#define DIB3000MB_REG_MER_MSB ( 381) +#define DIB3000MB_REG_MER_LSB ( 382) + +/* + * Transmission Parameter Signalling (TPS) + * the following registers can be used to get TPS-information. + * The values are according to the DVB-T standard. + */ + +/* TPS locked (1) */ +#define DIB3000MB_REG_TPS_LOCK ( 394) + +/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */ +#define DIB3000MB_REG_TPS_QAM ( 398) + +/* hierarchy from TPS (1) */ +#define DIB3000MB_REG_TPS_HRCH ( 399) + +/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */ +#define DIB3000MB_REG_TPS_VIT_ALPHA ( 400) + +/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */ +#define DIB3000MB_REG_TPS_CODE_RATE_HP ( 401) + +/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */ +#define DIB3000MB_REG_TPS_CODE_RATE_LP ( 402) + +/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */ +#define DIB3000MB_REG_TPS_GUARD_TIME ( 403) + +/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */ +#define DIB3000MB_REG_TPS_FFT ( 404) + +/* cell id from TPS (16) */ +#define DIB3000MB_REG_TPS_CELL_ID ( 406) + +/* TPS (68) */ +#define DIB3000MB_REG_TPS_1 ( 408) +#define DIB3000MB_REG_TPS_2 ( 409) +#define DIB3000MB_REG_TPS_3 ( 410) +#define DIB3000MB_REG_TPS_4 ( 411) +#define DIB3000MB_REG_TPS_5 ( 412) + +/* bit error rate (before RS correction) (21) */ +#define DIB3000MB_REG_BER_MSB ( 414) +#define DIB3000MB_REG_BER_LSB ( 415) + +/* packet error rate (uncorrected TS packets) (16) */ +#define DIB3000MB_REG_PACKET_ERROR_RATE ( 417) + +/* uncorrected packet count (16) */ +#define DIB3000MB_REG_UNC ( 420) + +/* viterbi locked (1) */ +#define DIB3000MB_REG_VIT_LCK ( 421) + +/* viterbi inidcator (16) */ +#define DIB3000MB_REG_VIT_INDICATOR ( 422) + +/* transport stream sync lock (1) */ +#define DIB3000MB_REG_TS_SYNC_LOCK ( 423) + +/* transport stream RS lock (1) */ +#define DIB3000MB_REG_TS_RS_LOCK ( 424) + +/* lock mask 0 value (1) */ +#define DIB3000MB_REG_LOCK0_VALUE ( 425) + +/* lock mask 1 value (1) */ +#define DIB3000MB_REG_LOCK1_VALUE ( 426) + +/* lock mask 2 value (1) */ +#define DIB3000MB_REG_LOCK2_VALUE ( 427) + +/* interrupt pending for auto search */ +#define DIB3000MB_REG_AS_IRQ_PENDING ( 434) + +#endif diff -puN drivers/media/dvb/frontends/dvb_dummy_fe.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/dvb_dummy_fe.c --- 25/drivers/media/dvb/frontends/dvb_dummy_fe.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.381063920 -0800 +++ 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.c 2004-11-21 17:13:23.438055256 -0800 @@ -24,244 +24,258 @@ #include #include "dvb_frontend.h" +#include "dvb_dummy_fe.h" -#define FRONTEND_NAME "dvbfe_dummy" -static int frontend_type; -module_param(frontend_type, int, 0444); -MODULE_PARM_DESC(frontend_type, "0 == DVB-S, 1 == DVB-C, 2 == DVB-T"); - -/* 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 -}; -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_dummy_fe_state { -struct dvb_frontend_info *frontend_info(void) -{ - switch(frontend_type) { - case 2: - return &dvb_t_dummyfe_info; - case 1: - return &dvb_c_dummyfe_info; - case 0: - default: - return &dvb_s_dummyfe_info; - } -} + struct dvb_frontend_ops ops; + struct dvb_frontend frontend; +}; -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: +static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) { - fe_status_t *status = arg; *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - break; + + return 0; } - case FE_READ_BER: +static int dvb_dummy_fe_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = (u32 *) arg; *ber = 0; - break; + return 0; } - case FE_READ_SIGNAL_STRENGTH: +static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - u8 signal = 0xff; - *((u16*) arg) = (signal << 8) | signal; - break; + *strength = 0; + return 0; } - case FE_READ_SNR: +static int dvb_dummy_fe_read_snr(struct dvb_frontend* fe, u16* snr) { - u8 snr = 0xf0; - *(u16*) arg = (snr << 8) | snr; - break; + *snr = 0; + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: - *(u32*) arg = 0; - break; - - case FE_SET_FRONTEND: - break; - - case FE_GET_FRONTEND: - break; - - case FE_SLEEP: +static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + *ucblocks = 0; return 0; +} - case FE_INIT: +static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ return 0; +} - case FE_SET_TONE: - return -EOPNOTSUPP; +static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + return 0; +} - case FE_SET_VOLTAGE: +static int dvb_dummy_fe_sleep(struct dvb_frontend* fe) +{ return 0; +} - default: - return -EOPNOTSUPP; +static int dvb_dummy_fe_init(struct dvb_frontend* fe) +{ + return 0; } + +static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ return 0; } -static struct i2c_client client_template; - -static int dvbdummyfe_attach_adapter(struct i2c_adapter *adapter) +static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) { - struct dvb_adapter *dvb; - struct i2c_client *client; - int ret; - - if ((client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL) - return -ENOMEM; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; + return 0; +} - if ((ret = i2c_attach_client(client))) { - kfree(client); - return ret; +static void dvb_dummy_fe_release(struct dvb_frontend* fe) +{ + struct dvb_dummy_fe_state* state = (struct dvb_dummy_fe_state*) fe->demodulator_priv; + kfree(state); } - dvb = i2c_get_clientdata(client); - BUG_ON(!dvb); +static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops; - if ((ret = dvb_register_frontend(dvbdummyfe_ioctl, dvb, NULL, - frontend_info(), THIS_MODULE))) { - kfree(client); - return ret; - } +struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void) +{ + struct dvb_dummy_fe_state* state = NULL; - return 0; + /* allocate memory for the internal state */ + state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + memcpy(&state->ops, &dvb_dummy_fe_ofdm_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; } +static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops; -static int dvbdummyfe_detach_client(struct i2c_client *client) +struct dvb_frontend* dvb_dummy_fe_qpsk_attach() { - struct dvb_adapter *dvb = i2c_get_clientdata(client); + struct dvb_dummy_fe_state* state = NULL; - dvb_unregister_frontend(dvbdummyfe_ioctl, dvb); - i2c_detach_client(client); - kfree(client); - return 0; + /* allocate memory for the internal state */ + state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + memcpy(&state->ops, &dvb_dummy_fe_qpsk_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; } -static int dvbdummyfe_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static struct dvb_frontend_ops dvb_dummy_fe_qam_ops; + +struct dvb_frontend* dvb_dummy_fe_qam_attach() { - switch(cmd) { - case FE_REGISTER: - i2c_set_clientdata(client, arg); - break; - case FE_UNREGISTER: - break; - default: - return -EOPNOTSUPP; - } + struct dvb_dummy_fe_state* state = NULL; - return 0; + /* allocate memory for the internal state */ + state = (struct dvb_dummy_fe_state*) kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + memcpy(&state->ops, &dvb_dummy_fe_qam_ops, sizeof(struct dvb_frontend_ops)); + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_DUMMY, - .flags = I2C_DF_NOTIFY, - .attach_adapter = dvbdummyfe_attach_adapter, - .detach_client = dvbdummyfe_detach_client, - .command = dvbdummyfe_command, -}; +static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = { -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + .info = { + .name = "Dummy DVB-T", + .type = FE_OFDM, + .frequency_min = 0, + .frequency_max = 863250000, + .frequency_stepsize = 62500, + .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, + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, }; -static int __init init_dvbdummyfe (void) -{ - return i2c_add_driver(&driver); -} +static struct dvb_frontend_ops dvb_dummy_fe_qam_ops = { -static void __exit exit_dvbdummyfe (void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "dummyfe: driver deregistration failed.\n"); -} + .info = { + .name = "Dummy DVB-C", + .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 */ + .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 + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, +}; +static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = { -module_init(init_dvbdummyfe); -module_exit(exit_dvbdummyfe); + .info = { + .name = "Dummy DVB-S", + .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, + .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 + }, + + .release = dvb_dummy_fe_release, + + .init = dvb_dummy_fe_init, + .sleep = dvb_dummy_fe_sleep, + + .set_frontend = dvb_dummy_fe_set_frontend, + .get_frontend = dvb_dummy_fe_get_frontend, + + .read_status = dvb_dummy_fe_read_status, + .read_ber = dvb_dummy_fe_read_ber, + .read_signal_strength = dvb_dummy_fe_read_signal_strength, + .read_snr = dvb_dummy_fe_read_snr, + .read_ucblocks = dvb_dummy_fe_read_ucblocks, + .set_voltage = dvb_dummy_fe_set_voltage, + .set_tone = dvb_dummy_fe_set_tone, +}; MODULE_DESCRIPTION("DVB DUMMY Frontend"); MODULE_AUTHOR("Emard"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach); +EXPORT_SYMBOL(dvb_dummy_fe_qam_attach); +EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach); diff -puN /dev/null drivers/media/dvb/frontends/dvb_dummy_fe.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/dvb_dummy_fe.h 2004-11-21 17:13:23.438055256 -0800 @@ -0,0 +1,32 @@ +/* + * Driver for Dummy Frontend + * + * Written by Emard + * + * 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.= + */ + +#ifndef DVB_DUMMY_FE_H +#define DVB_DUMMY_FE_H + +#include +#include "dvb_frontend.h" + +extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void); +extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void); +extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void); + +#endif // DVB_DUMMY_FE_H diff -puN /dev/null drivers/media/dvb/frontends/l64781.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/l64781.c 2004-11-21 17:13:23.442054648 -0800 @@ -0,0 +1,607 @@ +/* + driver for LSI L64781 COFDM demodulator + + Copyright (C) 2001 Holger Waechtler + for Convergence Integrated Media GmbH + Marko Kohtala + + 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 +#include +#include +#include +#include +#include +#include "dvb_frontend.h" +#include "l64781.h" + + +struct l64781_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct l64781_config* config; + + struct dvb_frontend frontend; + + /* private demodulator data */ + int first:1; +}; + +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "l64781: " args); \ + } while (0) + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + + +static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) + dprintk ("%s: write_reg error (reg == %02x) = %02x!\n", + __FUNCTION__, reg, ret); + + return (ret != 1) ? -1 : 0; +} + +static int l64781_readreg (struct l64781_state* state, u8 reg) +{ + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) return ret; + + return b1[0]; +} + +static void apply_tps (struct l64781_state* state) +{ + l64781_writereg (state, 0x2a, 0x00); + l64781_writereg (state, 0x2a, 0x01); + + /* This here is a little bit questionable because it enables + the automatic update of TPS registers. I think we'd need to + handle the IRQ from FE to update some other registers as + well, or at least implement some magic to tuning to correct + to the TPS received from transmission. */ + l64781_writereg (state, 0x2a, 0x02); +} + + +static void reset_afc (struct l64781_state* state) +{ + /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for + timing offset */ + l64781_writereg (state, 0x07, 0x9e); /* stall AFC */ + l64781_writereg (state, 0x08, 0); /* AFC INIT FREQ */ + l64781_writereg (state, 0x09, 0); + l64781_writereg (state, 0x0a, 0); + l64781_writereg (state, 0x07, 0x8e); + l64781_writereg (state, 0x0e, 0); /* AGC gain to zero in beginning */ + l64781_writereg (state, 0x11, 0x80); /* stall TIM */ + l64781_writereg (state, 0x10, 0); /* TIM_OFFSET_LSB */ + l64781_writereg (state, 0x12, 0); + l64781_writereg (state, 0x13, 0); + l64781_writereg (state, 0x11, 0x00); +} + +static int reset_and_configure (struct l64781_state* state) +{ + u8 buf [] = { 0x06 }; + struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 }; + // NOTE: this is correct in writing to address 0x00 + + return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV; +} + +static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_parameters *param) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */ + static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 }; + /* QPSK, QAM_16, QAM_64 */ + static const u8 qam_tab [] = { 2, 4, 0, 6 }; + static const u8 bw_tab [] = { 8, 7, 6 }; /* 8Mhz, 7MHz, 6MHz */ + static const u8 guard_tab [] = { 1, 2, 4, 8 }; + /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */ + static const u32 ppm = 8000; + struct dvb_ofdm_parameters *p = ¶m->u.ofdm; + u32 ddfs_offset_fixed; +/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */ +/* bw_tab[p->bandWidth]<<10)/15625; */ + u32 init_freq; + u32 spi_bias; + u8 val0x04; + u8 val0x05; + u8 val0x06; + int bw = p->bandwidth - BANDWIDTH_8_MHZ; + + state->config->pll_set(fe, param); + + if (param->inversion != INVERSION_ON && + param->inversion != INVERSION_OFF) + return -EINVAL; + + if (bw < 0 || bw > 2) + return -EINVAL; + + if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 && + p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 && + p->code_rate_HP != FEC_7_8) + return -EINVAL; + + if (p->hierarchy_information != HIERARCHY_NONE && + (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 && + p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 && + p->code_rate_LP != FEC_7_8)) + return -EINVAL; + + if (p->constellation != QPSK && p->constellation != QAM_16 && + p->constellation != QAM_64) + return -EINVAL; + + if (p->transmission_mode != TRANSMISSION_MODE_2K && + p->transmission_mode != TRANSMISSION_MODE_8K) + return -EINVAL; + + if (p->guard_interval < GUARD_INTERVAL_1_32 || + p->guard_interval > GUARD_INTERVAL_1_4) + return -EINVAL; + + if (p->hierarchy_information < HIERARCHY_NONE || + p->hierarchy_information > HIERARCHY_4) + return -EINVAL; + + ddfs_offset_fixed = 0x4000-(ppm<<16)/bw_tab[p->bandwidth]/1000000; + + /* This works up to 20000 ppm, it overflows if too large ppm! */ + init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) / + bw_tab[p->bandwidth] & 0xFFFFFF); + + /* SPI bias calculation is slightly modified to fit in 32bit */ + /* will work for high ppm only... */ + spi_bias = 378 * (1 << 10); + spi_bias *= 16; + spi_bias *= bw_tab[p->bandwidth]; + spi_bias *= qam_tab[p->constellation]; + spi_bias /= p->code_rate_HP + 1; + spi_bias /= (guard_tab[p->guard_interval] + 32); + spi_bias *= 1000ULL; + spi_bias /= 1000ULL + ppm/1000; + spi_bias *= p->code_rate_HP; + + 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; + + val0x06 = (p->hierarchy_information << 2) | p->constellation; + + l64781_writereg (state, 0x04, val0x04); + l64781_writereg (state, 0x05, val0x05); + l64781_writereg (state, 0x06, val0x06); + + reset_afc (state); + + /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */ + l64781_writereg (state, 0x15, + p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3); + l64781_writereg (state, 0x16, init_freq & 0xff); + l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff); + l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff); + + l64781_writereg (state, 0x1b, spi_bias & 0xff); + l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff); + l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) | + (param->inversion == INVERSION_ON ? 0x80 : 0x00)); + + l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff); + l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f); + + l64781_readreg (state, 0x00); /* clear interrupt registers... */ + l64781_readreg (state, 0x01); /* dto. */ + + apply_tps (state); + + return 0; +} + +static int get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* param) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + int tmp; + + + tmp = l64781_readreg(state, 0x04); + switch(tmp & 3) { + case 0: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + param->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; + break; + } + switch((tmp >> 2) & 3) { + case 0: + param->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + param->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + break; + default: + printk("Unexpected value for transmission_mode\n"); + } + + + + tmp = l64781_readreg(state, 0x05); + switch(tmp & 7) { + case 0: + param->u.ofdm.code_rate_HP = FEC_1_2; + break; + case 1: + param->u.ofdm.code_rate_HP = FEC_2_3; + break; + case 2: + param->u.ofdm.code_rate_HP = FEC_3_4; + break; + case 3: + param->u.ofdm.code_rate_HP = FEC_5_6; + break; + case 4: + param->u.ofdm.code_rate_HP = FEC_7_8; + break; + default: + printk("Unexpected value for code_rate_HP\n"); + } + switch((tmp >> 3) & 7) { + case 0: + param->u.ofdm.code_rate_LP = FEC_1_2; + break; + case 1: + param->u.ofdm.code_rate_LP = FEC_2_3; + break; + case 2: + param->u.ofdm.code_rate_LP = FEC_3_4; + break; + case 3: + param->u.ofdm.code_rate_LP = FEC_5_6; + break; + case 4: + param->u.ofdm.code_rate_LP = FEC_7_8; + break; + default: + printk("Unexpected value for code_rate_LP\n"); + } + + + tmp = l64781_readreg(state, 0x06); + switch(tmp & 3) { + case 0: + param->u.ofdm.constellation = QPSK; + break; + case 1: + param->u.ofdm.constellation = QAM_16; + break; + case 2: + param->u.ofdm.constellation = QAM_64; + break; + default: + printk("Unexpected value for constellation\n"); + } + switch((tmp >> 2) & 7) { + case 0: + param->u.ofdm.hierarchy_information = HIERARCHY_NONE; + break; + case 1: + param->u.ofdm.hierarchy_information = HIERARCHY_1; + break; + case 2: + param->u.ofdm.hierarchy_information = HIERARCHY_2; + break; + case 3: + param->u.ofdm.hierarchy_information = HIERARCHY_4; + break; + default: + printk("Unexpected value for hierarchy\n"); + } + + + tmp = l64781_readreg (state, 0x1d); + param->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF; + + tmp = (int) (l64781_readreg (state, 0x08) | + (l64781_readreg (state, 0x09) << 8) | + (l64781_readreg (state, 0x0a) << 16)); + param->frequency += tmp; + + return 0; +} + +static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + int sync = l64781_readreg (state, 0x32); + int gain = l64781_readreg (state, 0x0e); + + l64781_readreg (state, 0x00); /* clear interrupt registers... */ + l64781_readreg (state, 0x01); /* dto. */ + + *status = 0; + + if (gain > 5) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x02) /* VCXO locked, this criteria should be ok */ + *status |= FE_HAS_CARRIER; + + if (sync & 0x20) + *status |= FE_HAS_VITERBI; + + if (sync & 0x40) + *status |= FE_HAS_SYNC; + + if (sync == 0x7f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int l64781_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + /* XXX FIXME: set up counting period (reg 0x26...0x28) + */ + *ber = l64781_readreg (state, 0x39) + | (l64781_readreg (state, 0x3a) << 8); + + return 0; +} + +static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + u8 gain = l64781_readreg (state, 0x0e); + *signal_strength = (gain << 8) | gain; + + return 0; +} + +static int l64781_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + u8 avg_quality = 0xff - l64781_readreg (state, 0x33); + *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/ + + return 0; +} + +static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + *ucblocks = l64781_readreg (state, 0x37) + | (l64781_readreg (state, 0x38) << 8); + + return 0; +} + +static int l64781_sleep(struct dvb_frontend* fe) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + /* Power down */ + return l64781_writereg (state, 0x3e, 0x5a); +} + +static int l64781_init(struct dvb_frontend* fe) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + + reset_and_configure (state); + + /* Power up */ + l64781_writereg (state, 0x3e, 0xa5); + + /* Reset hard */ + l64781_writereg (state, 0x2a, 0x04); + l64781_writereg (state, 0x2a, 0x00); + + /* Set tuner specific things */ + /* AFC_POL, set also in reset_afc */ + l64781_writereg (state, 0x07, 0x8e); + + /* Use internal ADC */ + l64781_writereg (state, 0x0b, 0x81); + + /* AGC loop gain, and polarity is positive */ + l64781_writereg (state, 0x0c, 0x84); + + /* Internal ADC outputs two's complement */ + l64781_writereg (state, 0x0d, 0x8c); + + /* With ppm=8000, it seems the DTR_SENSITIVITY will result in + value of 2 with all possible bandwidths and guard + intervals, which is the initial value anyway. */ + /*l64781_writereg (state, 0x19, 0x92);*/ + + /* Everything is two's complement, soft bit and CSI_OUT too */ + l64781_writereg (state, 0x1e, 0x09); + + if (state->config->pll_init) state->config->pll_init(fe); + + /* delay a bit after first init attempt */ + if (state->first) { + state->first = 0; + msleep(200); + } + + return 0; +} + +static int l64781_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 200; + fesettings->step_size = 166667; + fesettings->max_drift = 166667*2; + return 0; +} + +static void l64781_release(struct dvb_frontend* fe) +{ + struct l64781_state* state = (struct l64781_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops l64781_ops; + +struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c) +{ + struct l64781_state* state = NULL; + int reg0x3e = -1; + u8 b0 [] = { 0x1a }; + u8 b1 [] = { 0x00 }; + struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + /* allocate memory for the internal state */ + state = (struct l64781_state*) kmalloc(sizeof(struct l64781_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &l64781_ops, sizeof(struct dvb_frontend_ops)); + state->first = 1; + + /** + * the L64781 won't show up before we send the reset_and_configure() + * broadcast. If nothing responds there is no L64781 on the bus... + */ + if (reset_and_configure(state) < 0) { + dprintk("No response to reset and configure broadcast...\n"); + goto error; + } + + /* The chip always responds to reads */ + if (i2c_transfer(state->i2c, msg, 2) != 2) { + dprintk("No response to read on I2C bus\n"); + goto error; + } + + /* Save current register contents for bailout */ + reg0x3e = l64781_readreg(state, 0x3e); + + /* Reading the POWER_DOWN register always returns 0 */ + if (reg0x3e != 0) { + dprintk("Device doesn't look like L64781\n"); + goto error; + } + + /* Turn the chip off */ + l64781_writereg (state, 0x3e, 0x5a); + + /* Responds to all reads with 0 */ + if (l64781_readreg(state, 0x1a) != 0) { + dprintk("Read 1 returned unexpcted value\n"); + goto error; + } + + /* Turn the chip on */ + l64781_writereg (state, 0x3e, 0xa5); + + /* Responds with register default value */ + if (l64781_readreg(state, 0x1a) != 0xa1) { + dprintk("Read 2 returned unexpcted value\n"); + goto error; + } + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (reg0x3e >= 0) l64781_writereg (state, 0x3e, reg0x3e); /* restore reg 0x3e */ + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops l64781_ops = { + + .info = { + .name = "LSI L64781 DVB-T", + .type = FE_OFDM, + /* .frequency_min = ???,*/ + /* .frequency_max = ???,*/ + .frequency_stepsize = 166666, + /* .frequency_tolerance = ???,*/ + /* .symbol_rate_tolerance = ???,*/ + .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 + }, + + .release = l64781_release, + + .init = l64781_init, + .sleep = l64781_sleep, + + .set_frontend = apply_frontend_param, + .get_frontend = get_frontend, + .get_tune_settings = l64781_get_tune_settings, + + .read_status = l64781_read_status, + .read_ber = l64781_read_ber, + .read_signal_strength = l64781_read_signal_strength, + .read_snr = l64781_read_snr, + .read_ucblocks = l64781_read_ucblocks, +}; + +MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver"); +MODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(l64781_attach); diff -puN /dev/null drivers/media/dvb/frontends/l64781.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/l64781.h 2004-11-21 17:13:23.442054648 -0800 @@ -0,0 +1,43 @@ +/* + driver for LSI L64781 COFDM demodulator + + Copyright (C) 2001 Holger Waechtler + for Convergence Integrated Media GmbH + Marko Kohtala + + 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. + +*/ + +#ifndef L64781_H +#define L64781_H + +#include + +struct l64781_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + + +extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, + struct i2c_adapter* i2c); + +#endif // L64781_H diff -puN drivers/media/dvb/frontends/mt312.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt312.c --- 25/drivers/media/dvb/frontends/mt312.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.383063616 -0800 +++ 25-akpm/drivers/media/dvb/frontends/mt312.c 2004-11-21 17:13:23.447053888 -0800 @@ -1,5 +1,5 @@ /* - Driver for Zarlink MT312 Satellite Channel Decoder + Driver for Zarlink VP310/MT312 Satellite Channel Decoder Copyright (C) 2003 Andreas Oberritter @@ -31,71 +31,52 @@ #include #include "dvb_frontend.h" +#include "mt312_priv.h" #include "mt312.h" -#define FRONTEND_NAME "dvbfe_mt312" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct mt312_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct mt312_config* config; + struct dvb_frontend frontend; -#define I2C_ADDR_MT312 0x0e -#define I2C_ADDR_SL1935 0x61 -#define I2C_ADDR_TSA5059 0x61 + u8 id; + u8 frequency; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "mt312: " args); \ + } while (0) #define MT312_SYS_CLK 90000000UL /* 90 MHz */ #define MT312_LPOWER_SYS_CLK 60000000UL /* 60 MHz */ #define MT312_PLL_CLK 10000000UL /* 10 MHz */ -static struct dvb_frontend_info mt312_info = { - .name = "Zarlink MT312", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, - /*.frequency_tolerance = 29500, FIXME: binary compatibility waste? */ - .symbol_rate_min = MT312_SYS_CLK / 128, - .symbol_rate_max = MT312_SYS_CLK / 2, - /*.symbol_rate_tolerance = 500, FIXME: binary compatibility waste? 2% */ - .notifier_delay = 0, - .caps = - FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS | - FE_CAN_RECOVER -}; - -struct mt312_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - int id; -}; - -static int mt312_read(struct i2c_adapter *i2c, - const enum mt312_reg_addr reg, void *buf, - const size_t count) +static int mt312_read(struct mt312_state* state, const enum mt312_reg_addr reg, + void *buf, const size_t count) { int ret; struct i2c_msg msg[2]; u8 regbuf[1] = { reg }; - msg[0].addr = I2C_ADDR_MT312; + msg[0].addr = state->config->demod_address; msg[0].flags = 0; msg[0].buf = regbuf; msg[0].len = 1; - msg[1].addr = I2C_ADDR_MT312; + msg[1].addr = state->config->demod_address; msg[1].flags = I2C_M_RD; msg[1].buf = buf; msg[1].len = count; - ret = i2c_transfer(i2c, msg, 2); + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { printk(KERN_ERR "%s: ret == %d\n", __FUNCTION__, ret); @@ -113,9 +94,8 @@ static int mt312_read(struct i2c_adapter return 0; } -static int mt312_write(struct i2c_adapter *i2c, - const enum mt312_reg_addr reg, const void *src, - const size_t count) +static int mt312_write(struct mt312_state* state, const enum mt312_reg_addr reg, + const void *src, const size_t count) { int ret; u8 buf[count + 1]; @@ -132,12 +112,12 @@ static int mt312_write(struct i2c_adapte buf[0] = reg; memcpy(&buf[1], src, count); - msg.addr = I2C_ADDR_MT312; + msg.addr = state->config->demod_address; msg.flags = 0; msg.buf = buf; msg.len = count + 1; - ret = i2c_transfer(i2c, &msg, 1); + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) { dprintk("%s: ret == %d\n", __FUNCTION__, ret); @@ -147,123 +127,136 @@ static int mt312_write(struct i2c_adapte return 0; } -static inline int mt312_readreg(struct i2c_adapter *i2c, +static inline int mt312_readreg(struct mt312_state* state, const enum mt312_reg_addr reg, u8 * val) { - return mt312_read(i2c, reg, val, 1); + return mt312_read(state, reg, val, 1); } -static inline int mt312_writereg(struct i2c_adapter *i2c, +static inline int mt312_writereg(struct mt312_state* state, const enum mt312_reg_addr reg, const u8 val) { - return mt312_write(i2c, reg, &val, 1); + return mt312_write(state, reg, &val, 1); } -static int mt312_pll_write(struct i2c_adapter *i2c, const u8 addr, - u8 * buf, const u8 len) +static inline u32 mt312_div(u32 a, u32 b) +{ + return (a + (b / 2)) / b; +} + +static int mt312_reset(struct mt312_state* state, const u8 full) +{ + return mt312_writereg(state, RESET, full ? 0x80 : 0x40); +} + +static int mt312_get_inversion(struct mt312_state* state, + fe_spectral_inversion_t *i) { int ret; - struct i2c_msg msg; + u8 vit_mode; - msg.addr = addr; - msg.flags = 0; - msg.buf = buf; - msg.len = len; + if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0) + return ret; + + if (vit_mode & 0x80) /* auto inversion was used */ + *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF; + + return 0; +} + +static int mt312_get_symbol_rate(struct mt312_state* state, u32 *sr) +{ + int ret; + u8 sym_rate_h; + u8 dec_ratio; + u16 sym_rat_op; + u16 monitor; + u8 buf[2]; - if ((ret = mt312_writereg(i2c, GPP_CTRL, 0x40)) < 0) + if ((ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h)) < 0) return ret; - if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) - printk(KERN_ERR "%s: i/o error (ret == %d)\n", __FUNCTION__, ret); + if (sym_rate_h & 0x80) { /* symbol rate search was used */ + if ((ret = mt312_writereg(state, MON_CTRL, 0x03)) < 0) + return ret; - if ((ret = mt312_writereg(i2c, GPP_CTRL, 0x00)) < 0) + if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0) return ret; - return 0; + monitor = (buf[0] << 8) | buf[1]; + + dprintk(KERN_DEBUG "sr(auto) = %u\n", + mt312_div(monitor * 15625, 4)); + } else { + if ((ret = mt312_writereg(state, MON_CTRL, 0x05)) < 0) + return ret; + + if ((ret = mt312_read(state, MONITOR_H, buf, sizeof(buf))) < 0) + return ret; + + dec_ratio = ((buf[0] >> 5) & 0x07) * 32; + + if ((ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf))) < 0) + return ret; + + sym_rat_op = (buf[0] << 8) | buf[1]; + + dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n", + sym_rat_op, dec_ratio); + dprintk(KERN_DEBUG "*sr(manual) = %lu\n", + (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) * + 2) - dec_ratio); } -static inline u32 mt312_div(u32 a, u32 b) -{ - return (a + (b / 2)) / b; + return 0; } -static int sl1935_set_tv_freq(struct i2c_adapter *i2c, u32 freq, u32 sr) +static int mt312_get_code_rate(struct mt312_state* state, fe_code_rate_t *cr) { - /* 155 uA, Baseband Path B */ - u8 buf[4] = { 0x00, 0x00, 0x80, 0x00 }; + const fe_code_rate_t fec_tab[8] = + { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, + FEC_AUTO, FEC_AUTO }; - u8 exp; - u32 ref; - u32 div; - - if (sr < 10000000) { /* 1-10 MSym/s: ratio 2 ^ 3 */ - exp = 3; - buf[2] |= 0x40; /* 690 uA */ - } else if (sr < 15000000) { /* 10-15 MSym/s: ratio 2 ^ 4 */ - exp = 4; - buf[2] |= 0x20; /* 330 uA */ - } else { /* 15-45 MSym/s: ratio 2 ^ 7 */ - exp = 7; - buf[3] |= 0x08; /* Baseband Path A */ + int ret; + u8 fec_status; + + if ((ret = mt312_readreg(state, FEC_STATUS, &fec_status)) < 0) + return ret; + + *cr = fec_tab[(fec_status >> 4) & 0x07]; + + return 0; } - div = mt312_div(MT312_PLL_CLK, 1 << exp); - ref = mt312_div(freq * 1000, div); - mt312_info.frequency_stepsize = mt312_div(div, 1000); - buf[0] = (ref >> 8) & 0x7f; - buf[1] = (ref >> 0) & 0xff; - buf[2] |= (exp - 1); - if (freq < 1550000) - buf[3] |= 0x10; - dprintk(KERN_INFO "synth dword = %02x%02x%02x%02x\n", buf[0], - buf[1], buf[2], buf[3]); - return mt312_pll_write(i2c, I2C_ADDR_SL1935, buf, sizeof(buf)); -} -static int tsa5059_set_tv_freq(struct i2c_adapter *i2c, u32 freq, u32 sr) -{ - u8 buf[4]; - u32 ref = mt312_div(freq, 125); - buf[0] = (ref >> 8) & 0x7f; - buf[1] = (ref >> 0) & 0xff; - buf[2] = 0x84 | ((ref >> 10) & 0x60); - buf[3] = 0x80; - if (freq < 1550000) - buf[3] |= 0x02; - dprintk(KERN_INFO "synth dword = %02x%02x%02x%02x\n", buf[0], - buf[1], buf[2], buf[3]); - return mt312_pll_write(i2c, I2C_ADDR_TSA5059, buf, sizeof(buf)); -} -static int mt312_reset(struct i2c_adapter *i2c, const u8 full) -{ - return mt312_writereg(i2c, RESET, full ? 0x80 : 0x40); -} -static int mt312_initfe(struct mt312_state *state, u8 pll) + + +static int mt312_initfe(struct dvb_frontend* fe) { - struct i2c_adapter *i2c = state->i2c; + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[2]; /* wake up */ - if ((ret = mt312_writereg(i2c, CONFIG, (pll == 60 ? 0x88 : 0x8c))) < 0) + if ((ret = mt312_writereg(state, CONFIG, (state->frequency == 60 ? 0x88 : 0x8c))) < 0) return ret; /* wait at least 150 usec */ udelay(150); /* full reset */ - if ((ret = mt312_reset(i2c, 1)) < 0) + if ((ret = mt312_reset(state, 1)) < 0) return ret; // Per datasheet, write correct values. 09/28/03 ACCJr. @@ -271,56 +264,63 @@ static int mt312_initfe(struct mt312_sta { u8 buf_def[8]={0x14, 0x12, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00}; - if ((ret = mt312_write(i2c, VIT_SETUP, buf_def, sizeof(buf_def))) < 0) + if ((ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def))) < 0) return ret; } /* SYS_CLK */ - buf[0] = mt312_div((pll == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000); + buf[0] = mt312_div((state->frequency == 60 ? MT312_LPOWER_SYS_CLK : MT312_SYS_CLK) * 2, 1000000); /* DISEQC_RATIO */ buf[1] = mt312_div(MT312_PLL_CLK, 15000 * 4); - if ((ret = mt312_write(i2c, SYS_CLK, buf, sizeof(buf))) < 0) + if ((ret = mt312_write(state, SYS_CLK, buf, sizeof(buf))) < 0) return ret; - if ((ret = mt312_writereg(i2c, SNR_THS_HIGH, 0x32)) < 0) + if ((ret = mt312_writereg(state, SNR_THS_HIGH, 0x32)) < 0) return ret; - if ((ret = mt312_writereg(i2c, OP_CTRL, 0x53)) < 0) + if ((ret = mt312_writereg(state, OP_CTRL, 0x53)) < 0) return ret; /* TS_SW_LIM */ buf[0] = 0x8c; buf[1] = 0x98; - if ((ret = mt312_write(i2c, TS_SW_LIM_L, buf, sizeof(buf))) < 0) + if ((ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf))) < 0) return ret; - if ((ret = mt312_writereg(i2c, CS_SW_LIM, 0x69)) < 0) + if ((ret = mt312_writereg(state, CS_SW_LIM, 0x69)) < 0) return ret; + if (state->config->pll_init) { + mt312_writereg(state, GPP_CTRL, 0x40); + state->config->pll_init(fe); + mt312_writereg(state, GPP_CTRL, 0x00); + } + return 0; } -static int mt312_send_master_cmd(struct i2c_adapter *i2c, - const struct dvb_diseqc_master_cmd *c) +static int mt312_send_master_cmd(struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *c) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 diseqc_mode; if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg))) return -EINVAL; - if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0) + if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0) return ret; if ((ret = - mt312_write(i2c, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0) + mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len)) < 0) return ret; if ((ret = - mt312_writereg(i2c, DISEQC_MODE, + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3) | 0x04)) < 0) return ret; @@ -328,21 +328,15 @@ static int mt312_send_master_cmd(struct /* set DISEQC_MODE[2:0] to zero if a return message is expected */ if (c->msg[0] & 0x02) if ((ret = - mt312_writereg(i2c, DISEQC_MODE, (diseqc_mode & 0x40))) < 0) + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40))) < 0) return ret; return 0; } -static int mt312_recv_slave_reply(struct i2c_adapter *i2c, - struct dvb_diseqc_slave_reply *r) -{ - /* TODO */ - return -EOPNOTSUPP; -} - -static int mt312_send_burst(struct i2c_adapter *i2c, const fe_sec_mini_cmd_t c) +static int mt312_send_burst(struct dvb_frontend* fe, const fe_sec_mini_cmd_t c) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; const u8 mini_tab[2] = { 0x02, 0x03 }; int ret; @@ -351,19 +345,20 @@ static int mt312_send_burst(struct i2c_a if (c > SEC_MINI_B) return -EINVAL; - if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0) + if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0) return ret; if ((ret = - mt312_writereg(i2c, DISEQC_MODE, + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) | mini_tab[c])) < 0) return ret; return 0; } -static int mt312_set_tone(struct i2c_adapter *i2c, const fe_sec_tone_mode_t t) +static int mt312_set_tone(struct dvb_frontend* fe, const fe_sec_tone_mode_t t) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; const u8 tone_tab[2] = { 0x01, 0x00 }; int ret; @@ -372,36 +367,37 @@ static int mt312_set_tone(struct i2c_ada if (t > SEC_TONE_OFF) return -EINVAL; - if ((ret = mt312_readreg(i2c, DISEQC_MODE, &diseqc_mode)) < 0) + if ((ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode)) < 0) return ret; if ((ret = - mt312_writereg(i2c, DISEQC_MODE, + mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40) | tone_tab[t])) < 0) return ret; return 0; } -static int mt312_set_voltage(struct i2c_adapter *i2c, const fe_sec_voltage_t v) +static int mt312_set_voltage(struct dvb_frontend* fe, const fe_sec_voltage_t v) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; const u8 volt_tab[3] = { 0x00, 0x40, 0x00 }; if (v > SEC_VOLTAGE_OFF) return -EINVAL; - return mt312_writereg(i2c, DISEQC_MODE, volt_tab[v]); + return mt312_writereg(state, DISEQC_MODE, volt_tab[v]); } -static int mt312_read_status(struct mt312_state *state, fe_status_t *s) +static int mt312_read_status(struct dvb_frontend* fe, fe_status_t *s) { - struct i2c_adapter *i2c = state->i2c; + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 status[3], vit_mode; *s = 0; - if ((ret = mt312_read(i2c, QPSK_STAT_H, status, sizeof(status))) < 0) + if ((ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status))) < 0) return ret; dprintk(KERN_DEBUG "QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x, FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]); @@ -419,24 +415,25 @@ static int mt312_read_status(struct mt31 // VP310 doesn't have AUTO, so we "implement it here" ACCJr if ((state->id == ID_VP310) && !(status[0] & 0x01)) { - if ((ret = mt312_readreg(i2c, VIT_MODE, &vit_mode)) < 0) + if ((ret = mt312_readreg(state, VIT_MODE, &vit_mode)) < 0) return ret; vit_mode ^= 0x40; - if ((ret = mt312_writereg(i2c, VIT_MODE, vit_mode)) < 0) + if ((ret = mt312_writereg(state, VIT_MODE, vit_mode)) < 0) return ret; - if ((ret = mt312_writereg(i2c, GO, 0x01)) < 0) + if ((ret = mt312_writereg(state, GO, 0x01)) < 0) return ret; } return 0; } -static int mt312_read_bercnt(struct i2c_adapter *i2c, u32 *ber) +static int mt312_read_ber(struct dvb_frontend* fe, u32 *ber) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[3]; - if ((ret = mt312_read(i2c, RS_BERCNT_H, buf, 3)) < 0) + if ((ret = mt312_read(state, RS_BERCNT_H, buf, 3)) < 0) return ret; *ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64; @@ -444,14 +441,15 @@ static int mt312_read_bercnt(struct i2c_ return 0; } -static int mt312_read_agc(struct i2c_adapter *i2c, u16 *signal_strength) +static int mt312_read_signal_strength(struct dvb_frontend* fe, u16 *signal_strength) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[3]; u16 agc; s16 err_db; - if ((ret = mt312_read(i2c, AGC_H, buf, sizeof(buf))) < 0) + if ((ret = mt312_read(state, AGC_H, buf, sizeof(buf))) < 0) return ret; agc = (buf[0] << 6) | (buf[1] >> 2); @@ -464,12 +462,13 @@ static int mt312_read_agc(struct i2c_ada return 0; } -static int mt312_read_snr(struct i2c_adapter *i2c, u16 *snr) +static int mt312_read_snr(struct dvb_frontend* fe, u16 *snr) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[2]; - if ((ret = mt312_read(i2c, M_SNR_H, &buf, sizeof(buf))) < 0) + if ((ret = mt312_read(state, M_SNR_H, &buf, sizeof(buf))) < 0) return ret; *snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1); @@ -477,12 +476,13 @@ static int mt312_read_snr(struct i2c_ada return 0; } -static int mt312_read_ubc(struct i2c_adapter *i2c, u32 *ubc) +static int mt312_read_ucblocks(struct dvb_frontend* fe, u32 *ubc) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[2]; - if ((ret = mt312_read(i2c, RS_UBC_H, &buf, sizeof(buf))) < 0) + if ((ret = mt312_read(state, RS_UBC_H, &buf, sizeof(buf))) < 0) return ret; *ubc = (buf[0] << 8) | buf[1]; @@ -490,10 +490,10 @@ static int mt312_read_ubc(struct i2c_ada return 0; } -static int mt312_set_frontend(struct mt312_state *state, - const struct dvb_frontend_parameters *p) +static int mt312_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) { - struct i2c_adapter *i2c = state->i2c; + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 buf[5], config_val; u16 sr; @@ -502,20 +502,18 @@ static int mt312_set_frontend(struct mt3 { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f }; const u8 inv_tab[3] = { 0x00, 0x40, 0x80 }; - int (*set_tv_freq)(struct i2c_adapter *i2c, u32 freq, u32 sr); - dprintk("%s: Freq %d\n", __FUNCTION__, p->frequency); - if ((p->frequency < mt312_info.frequency_min) - || (p->frequency > mt312_info.frequency_max)) + if ((p->frequency < fe->ops->info.frequency_min) + || (p->frequency > fe->ops->info.frequency_max)) return -EINVAL; if ((p->inversion < INVERSION_OFF) - || (p->inversion > INVERSION_AUTO)) + || (p->inversion > INVERSION_ON)) return -EINVAL; - if ((p->u.qpsk.symbol_rate < mt312_info.symbol_rate_min) - || (p->u.qpsk.symbol_rate > mt312_info.symbol_rate_max)) + if ((p->u.qpsk.symbol_rate < fe->ops->info.symbol_rate_min) + || (p->u.qpsk.symbol_rate > fe->ops->info.symbol_rate_max)) return -EINVAL; if ((p->u.qpsk.fec_inner < FEC_NONE) @@ -530,31 +528,36 @@ static int mt312_set_frontend(struct mt3 case ID_VP310: // For now we will do this only for the VP310. // It should be better for the mt312 as well, but tunning will be slower. ACCJr 09/29/03 - if ((ret = mt312_readreg(i2c, CONFIG, &config_val) < 0)) + if ((ret = mt312_readreg(state, CONFIG, &config_val) < 0)) return ret; if (p->u.qpsk.symbol_rate >= 30000000) //Note that 30MS/s should use 90MHz { - if ((config_val & 0x0c) == 0x08) //We are running 60MHz - if ((ret = mt312_initfe(state, (u8) 90)) < 0) + if ((config_val & 0x0c) == 0x08) { //We are running 60MHz + state->frequency = 90; + if ((ret = mt312_initfe(fe)) < 0) return ret; } + } else { - if ((config_val & 0x0c) == 0x0C) //We are running 90MHz - if ((ret = mt312_initfe(state, (u8) 60)) < 0) + if ((config_val & 0x0c) == 0x0C) { //We are running 90MHz + state->frequency = 60; + if ((ret = mt312_initfe(fe)) < 0) return ret; } - set_tv_freq = tsa5059_set_tv_freq; + } break; + case ID_MT312: - set_tv_freq = sl1935_set_tv_freq; break; + default: return -EINVAL; } - if ((ret = set_tv_freq(i2c, p->frequency, p->u.qpsk.symbol_rate)) < 0) - return ret; + mt312_writereg(state, GPP_CTRL, 0x40); + state->config->pll_set(fe, p); + mt312_writereg(state, GPP_CTRL, 0x00); /* sr = (u16)(sr * 256.0 / 1000000.0) */ sr = mt312_div(p->u.qpsk.symbol_rate * 4, 15625); @@ -575,333 +578,154 @@ static int mt312_set_frontend(struct mt3 /* GO */ buf[4] = 0x01; - if ((ret = mt312_write(i2c, SYM_RATE_H, buf, sizeof(buf))) < 0) - return ret; - - mt312_reset(i2c, 0); - - return 0; -} - -static int mt312_get_inversion(struct i2c_adapter *i2c, - fe_spectral_inversion_t * i) -{ - int ret; - u8 vit_mode; - - if ((ret = mt312_readreg(i2c, VIT_MODE, &vit_mode)) < 0) - return ret; - - if (vit_mode & 0x80) /* auto inversion was used */ - *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF; - - return 0; -} - -static int mt312_get_symbol_rate(struct i2c_adapter *i2c, u32 *sr) -{ - int ret; - u8 sym_rate_h; - u8 dec_ratio; - u16 sym_rat_op; - u16 monitor; - u8 buf[2]; - - if ((ret = mt312_readreg(i2c, SYM_RATE_H, &sym_rate_h)) < 0) + if ((ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf))) < 0) return ret; - if (sym_rate_h & 0x80) { /* symbol rate search was used */ - if ((ret = mt312_writereg(i2c, MON_CTRL, 0x03)) < 0) - return ret; - - if ((ret = mt312_read(i2c, MONITOR_H, buf, sizeof(buf))) < 0) - return ret; - - monitor = (buf[0] << 8) | buf[1]; - - dprintk(KERN_DEBUG "sr(auto) = %u\n", - mt312_div(monitor * 15625, 4)); - } else { - if ((ret = mt312_writereg(i2c, MON_CTRL, 0x05)) < 0) - return ret; - - if ((ret = mt312_read(i2c, MONITOR_H, buf, sizeof(buf))) < 0) - return ret; - - dec_ratio = ((buf[0] >> 5) & 0x07) * 32; - - if ((ret = mt312_read(i2c, SYM_RAT_OP_H, buf, sizeof(buf))) < 0) - return ret; - - sym_rat_op = (buf[0] << 8) | buf[1]; - - dprintk(KERN_DEBUG "sym_rat_op=%d dec_ratio=%d\n", - sym_rat_op, dec_ratio); - dprintk(KERN_DEBUG "*sr(manual) = %lu\n", - (((MT312_PLL_CLK * 8192) / (sym_rat_op + 8192)) * - 2) - dec_ratio); - } - - return 0; -} - -static int mt312_get_code_rate(struct i2c_adapter *i2c, fe_code_rate_t *cr) -{ - const fe_code_rate_t fec_tab[8] = - { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, - FEC_AUTO, FEC_AUTO }; - - int ret; - u8 fec_status; - - if ((ret = mt312_readreg(i2c, FEC_STATUS, &fec_status)) < 0) - return ret; - - *cr = fec_tab[(fec_status >> 4) & 0x07]; + mt312_reset(state, 0); return 0; } -static int mt312_get_frontend(struct i2c_adapter *i2c, +static int mt312_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; - if ((ret = mt312_get_inversion(i2c, &p->inversion)) < 0) + if ((ret = mt312_get_inversion(state, &p->inversion)) < 0) return ret; - if ((ret = mt312_get_symbol_rate(i2c, &p->u.qpsk.symbol_rate)) < 0) + if ((ret = mt312_get_symbol_rate(state, &p->u.qpsk.symbol_rate)) < 0) return ret; - if ((ret = mt312_get_code_rate(i2c, &p->u.qpsk.fec_inner)) < 0) + if ((ret = mt312_get_code_rate(state, &p->u.qpsk.fec_inner)) < 0) return ret; return 0; } -static int mt312_sleep(struct i2c_adapter *i2c) +static int mt312_sleep(struct dvb_frontend* fe) { + struct mt312_state *state = (struct mt312_state*) fe->demodulator_priv; int ret; u8 config; /* reset all registers to defaults */ - if ((ret = mt312_reset(i2c, 1)) < 0) + if ((ret = mt312_reset(state, 1)) < 0) return ret; - if ((ret = mt312_readreg(i2c, CONFIG, &config)) < 0) + if ((ret = mt312_readreg(state, CONFIG, &config)) < 0) return ret; /* enter standby */ - if ((ret = mt312_writereg(i2c, CONFIG, config & 0x7f)) < 0) + if ((ret = mt312_writereg(state, CONFIG, config & 0x7f)) < 0) return ret; return 0; } -static int mt312_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct mt312_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy(arg, &mt312_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_DISEQC_RESET_OVERLOAD: - return -EOPNOTSUPP; - - case FE_DISEQC_SEND_MASTER_CMD: - return mt312_send_master_cmd(i2c, arg); - - case FE_DISEQC_RECV_SLAVE_REPLY: - if (state->id == ID_MT312) - return mt312_recv_slave_reply(i2c, arg); - else - return -EOPNOTSUPP; - - case FE_DISEQC_SEND_BURST: - return mt312_send_burst(i2c, (fe_sec_mini_cmd_t) arg); - - case FE_SET_TONE: - return mt312_set_tone(i2c, (fe_sec_tone_mode_t) arg); - - case FE_SET_VOLTAGE: - return mt312_set_voltage(i2c, (fe_sec_voltage_t) arg); - - case FE_ENABLE_HIGH_LNB_VOLTAGE: - return -EOPNOTSUPP; - - case FE_READ_STATUS: - return mt312_read_status(state, arg); - - case FE_READ_BER: - return mt312_read_bercnt(i2c, arg); - - case FE_READ_SIGNAL_STRENGTH: - return mt312_read_agc(i2c, arg); - - case FE_READ_SNR: - return mt312_read_snr(i2c, arg); - - case FE_READ_UNCORRECTED_BLOCKS: - return mt312_read_ubc(i2c, arg); - - case FE_SET_FRONTEND: - return mt312_set_frontend(state, arg); - - case FE_GET_FRONTEND: - return mt312_get_frontend(i2c, arg); - - case FE_GET_EVENT: - return -EOPNOTSUPP; - - case FE_SLEEP: - return mt312_sleep(i2c); - - case FE_INIT: - /* For the VP310 we should run at 60MHz when ever possible. - * It should be better to run the mt312 ar lower speed when - * ever possible, but tunning will be slower. ACCJr 09/29/03 - */ - if (state->id == ID_MT312) - return mt312_initfe(state, (u8) 90); - else - return mt312_initfe(state, (u8) 60); - - case FE_GET_TUNE_SETTINGS: +static int mt312_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; fesettings->min_delay_ms = 50; fesettings->step_size = 0; fesettings->max_drift = 0; return 0; } - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -static struct i2c_client client_template; - -static int mt312_attach_adapter(struct i2c_adapter *adapter) +static void mt312_release(struct dvb_frontend* fe) { - struct mt312_state *state; - struct i2c_client *client; - int ret; - u8 id; - - dprintk("Trying to attach to adapter 0x%x:%s.\n", - adapter->id, adapter->name); - - if (mt312_readreg(adapter, ID, &id) < 0) - return -ENODEV; - - if ((id != ID_VP310) && (id != ID_MT312)) - return -ENODEV; - - if ( !(state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL)) ) - return -ENOMEM; - - memset(state, 0, sizeof(struct mt312_state)); - state->i2c = adapter; - state->id = id; - - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; - } - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = I2C_ADDR_MT312; - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - kfree(client); - kfree(state); - return ret; - } - - BUG_ON(!state->dvb); - - if ((ret = dvb_register_frontend(mt312_ioctl, state->dvb, state, - &mt312_info, THIS_MODULE))) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; - } - - return 0; -} - -static int mt312_detach_client(struct i2c_client *client) -{ - struct mt312_state *state = i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - dvb_unregister_frontend (mt312_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); + struct mt312_state* state = (struct mt312_state*) fe->demodulator_priv; kfree(state); - return 0; } -static int mt312_command (struct i2c_client *client, unsigned int cmd, void *arg) +static struct dvb_frontend_ops mt312_ops; + +struct dvb_frontend* mt312_attach(const struct mt312_config* config, + struct i2c_adapter* i2c) { - struct mt312_state *state = i2c_get_clientdata(client); + struct mt312_state* state = NULL; - dprintk ("%s\n", __FUNCTION__); + /* allocate memory for the internal state */ + state = (struct mt312_state*) kmalloc(sizeof(struct mt312_state), GFP_KERNEL); + if (state == NULL) goto error; - switch (cmd) { - case FE_REGISTER: - state->dvb = arg; + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &mt312_ops, sizeof(struct dvb_frontend_ops)); + + /* check if the demod is there */ + if (mt312_readreg(state, ID, &state->id) < 0) goto error; + switch(state->id) { + case ID_VP310: + state->frequency = 90; + printk("mt312: Detected Zarlink VP310\n"); break; - case FE_UNREGISTER: - state->dvb = NULL; + + case ID_MT312: + state->frequency = 60; + printk("mt312: Detected Zarlink MT312\n"); break; + default: - return -EOPNOTSUPP; - } - return 0; + goto error; } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_MT312, - .flags = I2C_DF_NOTIFY, - .attach_adapter = mt312_attach_adapter, - .detach_client = mt312_detach_client, - .command = mt312_command, + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops mt312_ops = { + + .info = { + .name = "Zarlink VP310/MT312 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, + /*.frequency_tolerance = 29500, FIXME: binary compatibility waste? */ + .symbol_rate_min = MT312_SYS_CLK / 128, + .symbol_rate_max = MT312_SYS_CLK / 2, + /*.symbol_rate_tolerance = 500, FIXME: binary compatibility waste? 2% */ + .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_MUTE_TS | + FE_CAN_RECOVER + }, + + .release = mt312_release, + + .init = mt312_initfe, + .sleep = mt312_sleep, + + .set_frontend = mt312_set_frontend, + .get_frontend = mt312_get_frontend, + .get_tune_settings = mt312_get_tune_settings, + + .read_status = mt312_read_status, + .read_ber = mt312_read_ber, + .read_signal_strength = mt312_read_signal_strength, + .read_snr = mt312_read_snr, + .read_ucblocks = mt312_read_ucblocks, + + .diseqc_send_master_cmd = mt312_send_master_cmd, + .diseqc_send_burst = mt312_send_burst, + .set_tone = mt312_set_tone, + .set_voltage = mt312_set_voltage, }; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init mt312_module_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit mt312_module_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "mt312: driver deregistration failed.\n"); -} - -module_init(mt312_module_init); -module_exit(mt312_module_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("MT312 Satellite Channel Decoder Driver"); +MODULE_DESCRIPTION("Zarlink VP310/MT312 DVB-S Demodulator driver"); MODULE_AUTHOR("Andreas Oberritter "); MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(mt312_attach); diff -puN drivers/media/dvb/frontends/mt312.h~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt312.h --- 25/drivers/media/dvb/frontends/mt312.h~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.384063464 -0800 +++ 25-akpm/drivers/media/dvb/frontends/mt312.h 2004-11-21 17:13:23.448053736 -0800 @@ -1,5 +1,5 @@ /* - Driver for Zarlink MT312 QPSK Frontend + Driver for Zarlink MT312 Satellite Channel Decoder Copyright (C) 2003 Andreas Oberritter @@ -18,145 +18,27 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + References: + http://products.zarlink.com/product_profiles/MT312.htm + http://products.zarlink.com/product_profiles/SL1935.htm */ -#ifndef _DVB_FRONTENDS_MT312 -#define _DVB_FRONTENDS_MT312 +#ifndef MT312_H +#define MT312_H -enum mt312_reg_addr { - QPSK_INT_H = 0, - QPSK_INT_M = 1, - QPSK_INT_L = 2, - FEC_INT = 3, - QPSK_STAT_H = 4, - QPSK_STAT_L = 5, - FEC_STATUS = 6, - LNB_FREQ_H = 7, - LNB_FREQ_L = 8, - M_SNR_H = 9, - M_SNR_L = 10, - VIT_ERRCNT_H = 11, - VIT_ERRCNT_M = 12, - VIT_ERRCNT_L = 13, - RS_BERCNT_H = 14, - RS_BERCNT_M = 15, - RS_BERCNT_L = 16, - RS_UBC_H = 17, - RS_UBC_L = 18, - SIG_LEVEL = 19, - GPP_CTRL = 20, - RESET = 21, - DISEQC_MODE = 22, - SYM_RATE_H = 23, - SYM_RATE_L = 24, - VIT_MODE = 25, - QPSK_CTRL = 26, - GO = 27, - IE_QPSK_H = 28, - IE_QPSK_M = 29, - IE_QPSK_L = 30, - IE_FEC = 31, - QPSK_STAT_EN = 32, - FEC_STAT_EN = 33, - SYS_CLK = 34, - DISEQC_RATIO = 35, - DISEQC_INSTR = 36, - FR_LIM = 37, - FR_OFF = 38, - AGC_CTRL = 39, - AGC_INIT = 40, - AGC_REF = 41, - AGC_MAX = 42, - AGC_MIN = 43, - AGC_LK_TH = 44, - TS_AGC_LK_TH = 45, - AGC_PWR_SET = 46, - QPSK_MISC = 47, - SNR_THS_LOW = 48, - SNR_THS_HIGH = 49, - TS_SW_RATE = 50, - TS_SW_LIM_L = 51, - TS_SW_LIM_H = 52, - CS_SW_RATE_1 = 53, - CS_SW_RATE_2 = 54, - CS_SW_RATE_3 = 55, - CS_SW_RATE_4 = 56, - CS_SW_LIM = 57, - TS_LPK = 58, - TS_LPK_M = 59, - TS_LPK_L = 60, - CS_KPROP_H = 61, - CS_KPROP_L = 62, - CS_KINT_H = 63, - CS_KINT_L = 64, - QPSK_SCALE = 65, - TLD_OUTCLK_TH = 66, - TLD_INCLK_TH = 67, - FLD_TH = 68, - PLD_OUTLK3 = 69, - PLD_OUTLK2 = 70, - PLD_OUTLK1 = 71, - PLD_OUTLK0 = 72, - PLD_INLK3 = 73, - PLD_INLK2 = 74, - PLD_INLK1 = 75, - PLD_INLK0 = 76, - PLD_ACC_TIME = 77, - SWEEP_PAR = 78, - STARTUP_TIME = 79, - LOSSLOCK_TH = 80, - FEC_LOCK_TM = 81, - LOSSLOCK_TM = 82, - VIT_ERRPER_H = 83, - VIT_ERRPER_M = 84, - VIT_ERRPER_L = 85, - VIT_SETUP = 86, - VIT_REF0 = 87, - VIT_REF1 = 88, - VIT_REF2 = 89, - VIT_REF3 = 90, - VIT_REF4 = 91, - VIT_REF5 = 92, - VIT_REF6 = 93, - VIT_MAXERR = 94, - BA_SETUPT = 95, - OP_CTRL = 96, - FEC_SETUP = 97, - PROG_SYNC = 98, - AFC_SEAR_TH = 99, - CSACC_DIF_TH = 100, - QPSK_LK_CT = 101, - QPSK_ST_CT = 102, - MON_CTRL = 103, - QPSK_RESET = 104, - QPSK_TST_CT = 105, - QPSK_TST_ST = 106, - TEST_R = 107, - AGC_H = 108, - AGC_M = 109, - AGC_L = 110, - FREQ_ERR1_H = 111, - FREQ_ERR1_M = 112, - FREQ_ERR1_L = 113, - FREQ_ERR2_H = 114, - FREQ_ERR2_L = 115, - SYM_RAT_OP_H = 116, - SYM_RAT_OP_L = 117, - DESEQC2_INT = 118, - DISEQC2_STAT = 119, - DISEQC2_FIFO = 120, - DISEQC2_CTRL1 = 121, - DISEQC2_CTRL2 = 122, - MONITOR_H = 123, - MONITOR_L = 124, - TEST_MODE = 125, - ID = 126, - CONFIG = 127 -}; +#include -enum mt312_model_id { - ID_VP310 = 1, - ID_MT312 = 3 +struct mt312_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); }; -#endif /* DVB_FRONTENDS_MT312 */ +extern struct dvb_frontend* mt312_attach(const struct mt312_config* config, + struct i2c_adapter* i2c); + +#endif // MT312_H diff -puN /dev/null drivers/media/dvb/frontends/mt312_priv.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/mt312_priv.h 2004-11-21 17:13:23.449053584 -0800 @@ -0,0 +1,162 @@ +/* + Driver for Zarlink MT312 QPSK Frontend + + Copyright (C) 2003 Andreas Oberritter + + 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. + +*/ + +#ifndef _DVB_FRONTENDS_MT312_PRIV +#define _DVB_FRONTENDS_MT312_PRIV + +enum mt312_reg_addr { + QPSK_INT_H = 0, + QPSK_INT_M = 1, + QPSK_INT_L = 2, + FEC_INT = 3, + QPSK_STAT_H = 4, + QPSK_STAT_L = 5, + FEC_STATUS = 6, + LNB_FREQ_H = 7, + LNB_FREQ_L = 8, + M_SNR_H = 9, + M_SNR_L = 10, + VIT_ERRCNT_H = 11, + VIT_ERRCNT_M = 12, + VIT_ERRCNT_L = 13, + RS_BERCNT_H = 14, + RS_BERCNT_M = 15, + RS_BERCNT_L = 16, + RS_UBC_H = 17, + RS_UBC_L = 18, + SIG_LEVEL = 19, + GPP_CTRL = 20, + RESET = 21, + DISEQC_MODE = 22, + SYM_RATE_H = 23, + SYM_RATE_L = 24, + VIT_MODE = 25, + QPSK_CTRL = 26, + GO = 27, + IE_QPSK_H = 28, + IE_QPSK_M = 29, + IE_QPSK_L = 30, + IE_FEC = 31, + QPSK_STAT_EN = 32, + FEC_STAT_EN = 33, + SYS_CLK = 34, + DISEQC_RATIO = 35, + DISEQC_INSTR = 36, + FR_LIM = 37, + FR_OFF = 38, + AGC_CTRL = 39, + AGC_INIT = 40, + AGC_REF = 41, + AGC_MAX = 42, + AGC_MIN = 43, + AGC_LK_TH = 44, + TS_AGC_LK_TH = 45, + AGC_PWR_SET = 46, + QPSK_MISC = 47, + SNR_THS_LOW = 48, + SNR_THS_HIGH = 49, + TS_SW_RATE = 50, + TS_SW_LIM_L = 51, + TS_SW_LIM_H = 52, + CS_SW_RATE_1 = 53, + CS_SW_RATE_2 = 54, + CS_SW_RATE_3 = 55, + CS_SW_RATE_4 = 56, + CS_SW_LIM = 57, + TS_LPK = 58, + TS_LPK_M = 59, + TS_LPK_L = 60, + CS_KPROP_H = 61, + CS_KPROP_L = 62, + CS_KINT_H = 63, + CS_KINT_L = 64, + QPSK_SCALE = 65, + TLD_OUTCLK_TH = 66, + TLD_INCLK_TH = 67, + FLD_TH = 68, + PLD_OUTLK3 = 69, + PLD_OUTLK2 = 70, + PLD_OUTLK1 = 71, + PLD_OUTLK0 = 72, + PLD_INLK3 = 73, + PLD_INLK2 = 74, + PLD_INLK1 = 75, + PLD_INLK0 = 76, + PLD_ACC_TIME = 77, + SWEEP_PAR = 78, + STARTUP_TIME = 79, + LOSSLOCK_TH = 80, + FEC_LOCK_TM = 81, + LOSSLOCK_TM = 82, + VIT_ERRPER_H = 83, + VIT_ERRPER_M = 84, + VIT_ERRPER_L = 85, + VIT_SETUP = 86, + VIT_REF0 = 87, + VIT_REF1 = 88, + VIT_REF2 = 89, + VIT_REF3 = 90, + VIT_REF4 = 91, + VIT_REF5 = 92, + VIT_REF6 = 93, + VIT_MAXERR = 94, + BA_SETUPT = 95, + OP_CTRL = 96, + FEC_SETUP = 97, + PROG_SYNC = 98, + AFC_SEAR_TH = 99, + CSACC_DIF_TH = 100, + QPSK_LK_CT = 101, + QPSK_ST_CT = 102, + MON_CTRL = 103, + QPSK_RESET = 104, + QPSK_TST_CT = 105, + QPSK_TST_ST = 106, + TEST_R = 107, + AGC_H = 108, + AGC_M = 109, + AGC_L = 110, + FREQ_ERR1_H = 111, + FREQ_ERR1_M = 112, + FREQ_ERR1_L = 113, + FREQ_ERR2_H = 114, + FREQ_ERR2_L = 115, + SYM_RAT_OP_H = 116, + SYM_RAT_OP_L = 117, + DESEQC2_INT = 118, + DISEQC2_STAT = 119, + DISEQC2_FIFO = 120, + DISEQC2_CTRL1 = 121, + DISEQC2_CTRL2 = 122, + MONITOR_H = 123, + MONITOR_L = 124, + TEST_MODE = 125, + ID = 126, + CONFIG = 127 +}; + +enum mt312_model_id { + ID_VP310 = 1, + ID_MT312 = 3 +}; + +#endif /* DVB_FRONTENDS_MT312_PRIV */ diff -puN drivers/media/dvb/frontends/mt352.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt352.c --- 25/drivers/media/dvb/frontends/mt352.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.386063160 -0800 +++ 25-akpm/drivers/media/dvb/frontends/mt352.c 2004-11-21 17:13:23.454052824 -0800 @@ -37,438 +37,90 @@ #include #include "dvb_frontend.h" +#include "mt352_priv.h" #include "mt352.h" -#define FRONTEND_NAME "dvbfe_mt352" +struct mt352_state { -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) + struct i2c_adapter* i2c; -static int debug; -#define MAX_CARDS 4 -static int force_card[MAX_CARDS] = { -1, -1, -1, -1 }; -static int force_card_count = 0; + struct dvb_frontend_ops ops; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -module_param_array(force_card, int, &force_card_count, 0444); -MODULE_PARM_DESC(force_card, "Forces the type of each attached mt352 frontend.\n\t" - "If your card is not autodetected, then you must specify its type here.\n\t" - "Valid card types are: 0 == AVDVBT771, 1 == TUA6034, 2 == TDTC9251DH01C,\n\t" - "3 == DVICO FusionHDTV DVB-T1, 4 == DVICO FusionHDTV DVB-T Lite."); + /* configuration settings */ + const struct mt352_config* config; -struct mt352_state { - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - struct dvb_frontend_info fe_info; - int card_type; + struct dvb_frontend frontend; }; -#define mt352_write(ibuf, ilen) \ +static int debug; +#define dprintk(args...) \ do { \ - struct i2c_msg msg = { .addr = I2C_MT352_ADDR, .flags = 0, \ - .buf = ibuf, .len = ilen }; \ - int err = i2c_transfer(i2c, &msg, 1); \ - if (err != 1) { \ - printk(KERN_WARNING \ - "mt352_write() failed (err = %d)!\n", err); \ - return err; \ - } \ + if (debug) printk(KERN_DEBUG "mt352: " args); \ } while (0) -static struct _tuner_info tuner_info [] = { - { - .fe_name = "AverMedia DVB-T 771", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_AVERMEDIA771, - .mt352_charge_pump = mt352_cp_AVERMEDIA771, - .mt352_band_select = mt352_bs_AVERMEDIA771 - }, - { - .fe_name = "Zarlink MT352 + TUA6034 DVB-T", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_TUA6034, - .mt352_charge_pump = mt352_cp_TUA6034, - .mt352_band_select = mt352_bs_TUA6034 - }, - { - .fe_name = "Zarlink MT352 + Samsung TDTC9251DH01C DVB-T", - .fe_frequency_min = 474000000, - .fe_frequency_max = 858000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_TDTC9251DH01C, - .mt352_charge_pump = mt352_cp_TDTC9251DH01C, - .mt352_band_select = mt352_bs_TDTC9251DH01C - }, - { - .fe_name = "DVICO FusionHDTV DVB-T1", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc2, - .mt352_init = mt352_init_DVICODVBT1, - .mt352_charge_pump = mt352_cp_DVICODVBT1, - .mt352_band_select = mt352_bs_DVICODVBT1, - }, - { - .fe_name = "DVICO FusionHDTV DVB-T Lite", - .fe_frequency_min = 174000000, - .fe_frequency_max = 862000000, - .fe_frequency_stepsize = 166667, - .pll_i2c_addr = 0xc0, - .mt352_init = mt352_init_DVICODVBTLITE, - .mt352_charge_pump = mt352_cp_DVICODVBTLITE, - .mt352_band_select = mt352_bs_DVICODVBTLITE, - } -}; - -static struct dvb_frontend_info mt352_info_template = { - .name = "DVB-T Zarlink MT352 demodulator driver", - .type = FE_OFDM, -/* - .frequency_min = 0, - .frequency_max = 0, - .frequency_stepsize = 0, - .frequency_tolerance = 0, - .symbol_rate_min = 1000000, - .symbol_rate_max = 45000000, - .symbol_rate_tolerance = ???, -*/ - .notifier_delay = 0, - .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | - FE_CAN_MUTE_TS -}; - -static u8 mt352_reset [] = { RESET, 0x80 }; -static u8 mt352_adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; -static u8 mt352_capt_range_cfg[] = { CAPT_RANGE, 0x32 }; - -static int mt352_init_TUA6034(struct i2c_adapter *i2c) -{ - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x19, 0xa0 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static int mt352_init_AVERMEDIA771(struct i2c_adapter *i2c) -{ - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x10, 0x23, 0x00, 0xFF, 0xFF, - 0x00, 0xFF, 0x00, 0x40, 0x40 }; - static u8 mt352_av771_extra[] = { 0xB5, 0x7A }; - static u8 mt352_capt_range_cfg[] = { CAPT_RANGE, 0x32 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg,sizeof(mt352_agc_cfg)); - udelay(2000); - mt352_write(mt352_av771_extra,sizeof(mt352_av771_extra)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static int mt352_init_TDTC9251DH01C(struct i2c_adapter *i2c) +int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen) { - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x10, 0x2d }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x28, 0xa1 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(2000); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, + .buf = ibuf, .len = ilen }; + int err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_WARNING + "mt352_write() failed (err = %d)!\n", err); + return err; } -static int mt352_init_DVICODVBT1(struct i2c_adapter *i2c) -{ - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x39 }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; - static u8 mt352_gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(200); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - return 0; } -static int mt352_init_DVICODVBTLITE(struct i2c_adapter *i2c) -{ - static u8 mt352_clock_config [] = { CLOCK_CTL, 0x38, 0x38 }; - static u8 mt352_agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; - static u8 mt352_gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; - - mt352_write(mt352_clock_config, sizeof(mt352_clock_config)); - udelay(200); - mt352_write(mt352_reset, sizeof(mt352_reset)); - mt352_write(mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg)); - - mt352_write(mt352_agc_cfg, sizeof(mt352_agc_cfg)); - mt352_write(mt352_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); - mt352_write(mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); - - return 0; -} - -static unsigned char mt352_cp_TUA6034(u32 freq) -{ - unsigned char cp = 0; - - if (freq < 542000000) - cp = 0xbe; - else if (freq < 830000000) - cp = 0xf6; - else - cp = 0xfe; - - return cp; -} - -static unsigned char mt352_cp_AVERMEDIA771(u32 freq) -{ - unsigned char cp = 0; - - if (freq < 150000000) - cp = 0xB4; - else if (freq < 173000000) - cp = 0xBC; - else if (freq < 250000000) - cp = 0xB4; - else if (freq < 400000000) - cp = 0xBC; - else if (freq < 420000000) - cp = 0xF4; - else if (freq < 470000000) - cp = 0xFC; - else if (freq < 600000000) - cp = 0xBC; - else if (freq < 730000000) - cp = 0xF4; - else - cp = 0xFC; - - return cp; -} - -static unsigned char mt352_cp_TDTC9251DH01C(u32 freq) -{ - return(0xcc); -} - -static unsigned char mt352_cp_DVICODVBT1(u32 freq) +static u8 mt352_read_register(struct mt352_state* state, u8 reg) { - unsigned char cp = 0; - - if (freq < 542000000) - cp = 0xbc; - else if (freq < 830000000) - cp = 0xf4; - else - cp = 0xfc; - - return cp; -} - -static unsigned char mt352_cp_DVICODVBTLITE(u32 freq) -{ - unsigned char cp = 0; - - if (freq < 542000000) - cp = 0xb4; - else if (freq < 771000000) - cp = 0xbc; - else - cp = 0xf4; - - return cp; -} + int ret; + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, + .flags = 0, + .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, .len = 1 } }; -static unsigned char mt352_bs_TUA6034(u32 freq) -{ - unsigned char bs = 0; + ret = i2c_transfer(state->i2c, msg, 2); - if (freq < 250000000) - bs = 0x01; - else - bs = 0x08; + if (ret != 2) + printk(KERN_WARNING + "%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - return bs; + return b1[0]; } -static unsigned char mt352_bs_AVERMEDIA771(u32 freq) -{ - unsigned char bs = 0; - if (freq < 150000000) - bs = 0x01; - else if (freq < 173000000) - bs = 0x01; - else if (freq < 250000000) - bs = 0x02; - else if (freq < 400000000) - bs = 0x02; - else if (freq < 420000000) - bs = 0x02; - else if (freq < 470000000) - bs = 0x02; - else if (freq < 600000000) - bs = 0x08; - else if (freq < 730000000) - bs = 0x08; - else - bs = 0x08; - return bs; -} -static unsigned char mt352_bs_TDTC9251DH01C(u32 freq) -{ - unsigned char bs = 0; - if (freq >= 48000000 && freq <= 154000000) /* low band */ - bs = 0x09; - if (freq >= 161000000 && freq <= 439000000) /* medium band */ - bs = 0x0a; - if (freq >= 447000000 && freq <= 863000000) /* high band */ - bs = 0x08; - return bs; -} -static unsigned char mt352_bs_DVICODVBT1(u32 freq) -{ - unsigned char bs = 0; - if (freq == 0) /* power down PLL */ - bs = 0x03; - else if (freq < 157500000) /* low band */ - bs = 0x01; - else if (freq < 443250000) /* mid band */ - bs = 0x02; - else /* high band */ - bs = 0x04; - return bs; -} -static unsigned char mt352_bs_DVICODVBTLITE(u32 freq) -{ - unsigned char bs = 0; - - if (freq == 0) /* power down PLL */ - bs = 0x03; - else if (freq < 443250000) /* mid band */ - bs = 0x02; - else /* high band */ - bs = 0x08; - - return bs; -} - -static u32 mt352_read_eeprom_dword(struct i2c_adapter *i2c, int dword_base) - { - int i; - u32 dword = 0; - u8 reg, val; - struct i2c_msg msg[2] = { - { - .addr = 0x50, - .flags = 0, - .buf = ®, - .len = 1 - }, - { - .addr = 0x50, - .flags = I2C_M_RD, - .buf = &val, - .len = 1 - } - }; - - for (i = 0; i < 4; i++) { - reg = dword_base + i; - if (i2c_transfer(i2c,msg,2) != 2) - return 0; - dword = (dword << 8) | val; - } - - return dword; -} - -static int mt352_init(struct i2c_adapter *i2c, int card_type) -{ - /** - * all register write sequence have the register address of the - * first register in the first byte, thenafter the value to write - * into this and the following registers. - * - * - * We only write non-default settings, all default settings are - * restored by the full mt352_reset sequence. - * - * - * The optimal AGC target value and slope might vary from tuner - * type to tuner type, so check whether you need to adjust this one... - **/ - - return(MT352_INIT(i2c)); -} - -static int mt352_sleep(struct i2c_adapter *i2c) +static int mt352_sleep(struct dvb_frontend* fe) { static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 }; - mt352_write(mt352_softdown, sizeof(mt352_softdown)); + mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); return 0; } -static int mt352_set_parameters(struct i2c_adapter *i2c, - struct dvb_frontend_parameters *param, - int card_type) +static int mt352_set_parameters(struct dvb_frontend* fe, + struct dvb_frontend_parameters *param) { + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; unsigned char buf[14]; unsigned int tps = 0; struct dvb_ofdm_parameters *op = ¶m->u.ofdm; - uint16_t tmp; int i; switch (op->code_rate_HP) { @@ -600,22 +252,7 @@ static int mt352_set_parameters(struct i buf[6] = 0x31; /* INPUT_FREQ_(1|0), 20.48MHz clock, 36.166667MHz IF */ buf[7] = 0x05; /* see MT352 Design Manual page 32 for details */ - buf[8] = PLL_I2C_ADDR; - - /** - * All the following settings are tuner module dependent, - * check the datasheet... - */ - - /* here we assume 1/6MHz == 166.66kHz stepsize */ - #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ - tmp = (((param->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; - - buf[9] = msb(tmp); /* CHAN_START_(1|0) */ - buf[10] = lsb(tmp); - - buf[11] = MT352_CHARGE_PUMP(param->frequency); - buf[12] = MT352_BAND_SELECT(param->frequency); + state->config->pll_set(fe, param, buf+8); buf[13] = 0x01; /* TUNER_GO!! */ @@ -623,39 +260,18 @@ static int mt352_set_parameters(struct i * parameters already set. Enhances tuning time and prevents stream * breakup when retuning the same transponder. */ for (i = 1; i < 13; i++) - if (buf[i] != mt352_read_register(i2c, i + 0x50)) { - mt352_write(buf, sizeof(buf)); + if (buf[i] != mt352_read_register(state, i + 0x50)) { + mt352_write(fe, buf, sizeof(buf)); break; } return 0; } -static u8 mt352_read_register(struct i2c_adapter *i2c, u8 reg) -{ - int ret; - u8 b0 [] = { reg }; - u8 b1 [] = { 0 }; - struct i2c_msg msg [] = { { .addr = I2C_MT352_ADDR, - .flags = 0, - .buf = b0, .len = 1 }, - { .addr = I2C_MT352_ADDR, - .flags = I2C_M_RD, - .buf = b1, .len = 1 } }; - - ret = i2c_transfer(i2c, msg, 2); - - if (ret != 2) - printk(KERN_WARNING - "%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return b1[0]; -} - - -static int mt352_get_parameters(struct i2c_adapter *i2c, +static int mt352_get_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; u16 tps; u16 div; u8 trl; @@ -672,7 +288,7 @@ static int mt352_get_parameters(struct i FEC_AUTO }; - if ( (mt352_read_register(i2c,0x00) & 0xC0) != 0xC0 ) + if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 ) { return -EINVAL; } @@ -680,9 +296,9 @@ static int mt352_get_parameters(struct i /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because * the mt352 sometimes works with the wrong parameters */ - tps = (mt352_read_register(i2c, TPS_RECEIVED_1) << 8) | mt352_read_register(i2c, TPS_RECEIVED_0); - div = (mt352_read_register(i2c, CHAN_START_1) << 8) | mt352_read_register(i2c, CHAN_START_0); - trl = mt352_read_register(i2c, TRL_NOMINAL_RATE_1); + tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0); + div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0); + trl = mt352_read_register(state, TRL_NOMINAL_RATE_1); op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; @@ -759,7 +375,7 @@ static int mt352_get_parameters(struct i } - if (mt352_read_register(i2c, STATUS_2) & 0x02) + if (mt352_read_register(state, STATUS_2) & 0x02) param->inversion = INVERSION_OFF; else param->inversion = INVERSION_ON; @@ -767,26 +383,13 @@ static int mt352_get_parameters(struct i return 0; } - -static int mt352_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct mt352_state *state = fe->data; - struct i2c_adapter *i2c = state->i2c; - int card_type = state->card_type; - u8 r,snr; - fe_status_t *status; - u16 signal; - struct dvb_frontend_tune_settings *fe_tune_settings; - - switch (cmd) { - case FE_GET_INFO: - memcpy(arg, &state->fe_info, sizeof(struct dvb_frontend_info)); - break; + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; + u8 r; - case FE_READ_STATUS: - status = arg; *status = 0; - r = mt352_read_register (i2c, STATUS_0); + r = mt352_read_register (state, STATUS_0); if (r & (1 << 4)) *status = FE_HAS_CARRIER; if (r & (1 << 1)) @@ -794,243 +397,164 @@ static int mt352_ioctl(struct dvb_fronte if (r & (1 << 5)) *status |= FE_HAS_LOCK; - r = mt352_read_register (i2c, STATUS_1); + r = mt352_read_register (state, STATUS_1); if (r & (1 << 1)) *status |= FE_HAS_SYNC; - r = mt352_read_register (i2c, STATUS_3); + r = mt352_read_register (state, STATUS_3); if (r & (1 << 6)) *status |= FE_HAS_SIGNAL; - break; + if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != + (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) + *status &= ~FE_HAS_LOCK; - case FE_READ_BER: - *((u32 *) arg) = (mt352_read_register (i2c, RS_ERR_CNT_2) << 16) | - (mt352_read_register (i2c, RS_ERR_CNT_1) << 8) | - (mt352_read_register (i2c, RS_ERR_CNT_0)); - break; - - case FE_READ_SIGNAL_STRENGTH: - signal = (mt352_read_register (i2c, AGC_GAIN_3) << 8) | - (mt352_read_register (i2c, AGC_GAIN_2)); - *((u16*) arg) = ~signal; - break; - - case FE_READ_SNR: - snr = mt352_read_register (i2c, SNR); - *((u16*) arg) = (snr << 8) | snr; - break; - - case FE_READ_UNCORRECTED_BLOCKS: - *(u32*) arg = (mt352_read_register (i2c, RS_UBC_1) << 8) | - (mt352_read_register (i2c, RS_UBC_0)); - break; - - case FE_SET_FRONTEND: - return mt352_set_parameters (i2c, - (struct dvb_frontend_parameters *) arg, - card_type); - - case FE_GET_FRONTEND: - return mt352_get_parameters (i2c, - (struct dvb_frontend_parameters *) arg); - - case FE_GET_TUNE_SETTINGS: - fe_tune_settings = (struct dvb_frontend_tune_settings *) arg; - fe_tune_settings->min_delay_ms = 800; - fe_tune_settings->step_size = 0; - fe_tune_settings->max_drift = 0; - break; - - case FE_SLEEP: - return mt352_sleep(i2c); - - case FE_INIT: - /* Only send the initialisation command if the demodulator - * isn't already enabled. Greatly enhances tuning time. */ - if ((mt352_read_register(i2c, CLOCK_CTL) & 0x10) == 0 || - (mt352_read_register(i2c, CONFIG) & 0x20) == 0) - return mt352_init(i2c, card_type); - else - return 0; + return 0; +} + +static int mt352_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; - default: - return -EOPNOTSUPP; + *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) | + (mt352_read_register (state, RS_ERR_CNT_1) << 8) | + (mt352_read_register (state, RS_ERR_CNT_0)); + + return 0; } +static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; + + u16 signal = (mt352_read_register (state, AGC_GAIN_3) << 8) | + (mt352_read_register (state, AGC_GAIN_2)); + + *strength = ~signal; return 0; } -static struct i2c_client client_template; - -static int mt352_attach_adapter(struct i2c_adapter *i2c) +static int mt352_read_snr(struct dvb_frontend* fe, u16* snr) { - static int num_cards_probed; - struct mt352_state *state; - struct i2c_client *client; - static u8 mt352_reset_attach [] = { RESET, 0xC0 }; - int ret; - int card_type, forced_card = -1; + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; - dprintk("Trying to attach to adapter 0x%x:%s.\n", - i2c->id, i2c->name); + u8 _snr = mt352_read_register (state, SNR); + *snr = (_snr << 8) | _snr; - if (mt352_read_register(i2c, CHIP_ID) != ID_MT352) - return -ENODEV; + return 0; +} - if ( !(state = kmalloc(sizeof(struct mt352_state), GFP_KERNEL)) ) - return -ENOMEM; +static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; - memset(state, 0, sizeof(struct mt352_state)); - state->i2c = i2c; - state->card_type = -1; - memcpy(&state->fe_info, &mt352_info_template, sizeof(struct dvb_frontend_info)); + *ucblocks = (mt352_read_register (state, RS_UBC_1) << 8) | + (mt352_read_register (state, RS_UBC_0)); - /* Attempt autodetection of card type based on PCI ID information - * stored in any on-board EEPROM. */ - switch (mt352_read_eeprom_dword(i2c, 0xFC)) { /* BT878A chipset */ - case 0x07711461: - state->card_type = CARD_AVDVBT771; - break; - case 0xdb1018ac: - state->card_type = CARD_DVICODVBTLITE; - break; - default: - break; - } - - switch (mt352_read_eeprom_dword(i2c, 0x04)) { /* CX2388x chipset */ - case 0xac1800db: - state->card_type = CARD_DVICODVBT1; - break; - default: - break; - } - - if (num_cards_probed < force_card_count) - forced_card = force_card[num_cards_probed++]; - - if (state->card_type == -1 && forced_card < 0) { - dprintk("Card type not automatically detected. You " - "must use the 'force_card' module parameter.\n"); - kfree(state); - return -ENODEV; + return 0; } - if (forced_card >= 0) { - if (state->card_type >= 0 && forced_card != state->card_type) - printk(KERN_WARNING FRONTEND_NAME ": Warning, overriding" - " detected card type.\n"); - state->card_type = forced_card; +static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 800; + fe_tune_settings->step_size = 0; + fe_tune_settings->max_drift = 0; + + return 0; } - card_type = state->card_type; - printk(KERN_INFO FRONTEND_NAME ": Setup for %s\n", FE_NAME); +static int mt352_init(struct dvb_frontend* fe) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; - /* set the frontend name and card-specific frequency info */ - strlcpy(state->fe_info.name, FE_NAME, sizeof(state->fe_info.name)); - state->fe_info.frequency_min = FE_FREQ_MIN; - state->fe_info.frequency_max = FE_FREQ_MAX; - state->fe_info.frequency_stepsize = FE_FREQ_STEPSIZE; + static u8 mt352_reset_attach [] = { RESET, 0xC0 }; - /* Do a "hard" reset */ - mt352_write(mt352_reset_attach, sizeof(mt352_reset_attach)); + if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 || + (mt352_read_register(state, CONFIG) & 0x20) == 0) { - /* Try to intiialise the device */ - if (mt352_init(i2c, card_type) != 0) { - kfree(state); - return -ENODEV; + /* Do a "hard" reset */ + mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); + return state->config->demod_init(fe); } - if ( !(client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)) ) { - kfree(state); - return -ENOMEM; + return 0; } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = i2c; - client->addr = 0; // XXX - i2c_set_clientdata(client, state); - - if ((ret = i2c_attach_client(client))) { - kfree(client); +static void mt352_release(struct dvb_frontend* fe) +{ + struct mt352_state* state = (struct mt352_state*) fe->demodulator_priv; kfree(state); - return ret; } - return 0; -} +static struct dvb_frontend_ops mt352_ops; -static int mt352_detach_client(struct i2c_client *client) +struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c) { - struct mt352_state *state = i2c_get_clientdata(client); + struct mt352_state* state = NULL; - if (state->dvb) - dvb_unregister_frontend (mt352_ioctl, state->dvb); - i2c_detach_client(client); - kfree(client); - kfree(state); - return 0; -} + /* allocate memory for the internal state */ + state = (struct mt352_state*) kmalloc(sizeof(struct mt352_state), GFP_KERNEL); + if (state == NULL) goto error; -static int mt352_command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct mt352_state *state = i2c_get_clientdata(client); - int ret; + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &mt352_ops, sizeof(struct dvb_frontend_ops)); - switch (cmd) { - case FE_REGISTER: - if (!state->dvb) { - if ((ret = dvb_register_frontend(mt352_ioctl, arg, - state, &state->fe_info, - THIS_MODULE))) - return ret; - state->dvb = arg; - } - break; - case FE_UNREGISTER: - if (state->dvb == arg) { - dvb_unregister_frontend(mt352_ioctl, state->dvb); - state->dvb = NULL; - } - break; - default: - return -EOPNOTSUPP; - } - return 0; -} + /* check if the demod is there */ + if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error; -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_MT352, - .flags = I2C_DF_NOTIFY, - .attach_adapter = mt352_attach_adapter, - .detach_client = mt352_detach_client, - .command = mt352_command, -}; + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops mt352_ops = { + + .info = { + .name = "Zarlink MT352 DVB-T", + .type = FE_OFDM, + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 166667, + .frequency_tolerance = 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_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; + .release = mt352_release, -static int __init mt352_module_init(void) -{ - return i2c_add_driver(&driver); -} + .init = mt352_init, + .sleep = mt352_sleep, -static void __exit mt352_module_exit(void) -{ - if (i2c_del_driver(&driver)) - printk(KERN_ERR "mt352: driver deregistration failed.\n"); -} + .set_frontend = mt352_set_parameters, + .get_frontend = mt352_get_parameters, + .get_tune_settings = mt352_get_tune_settings, + + .read_status = mt352_read_status, + .read_ber = mt352_read_ber, + .read_signal_strength = mt352_read_signal_strength, + .read_snr = mt352_read_snr, + .read_ucblocks = mt352_read_ucblocks, +}; -module_init(mt352_module_init); -module_exit(mt352_module_exit); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("DVB-T MT352 Zarlink"); +MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver"); MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(mt352_attach); +EXPORT_SYMBOL(mt352_write); diff -puN drivers/media/dvb/frontends/mt352.h~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/mt352.h --- 25/drivers/media/dvb/frontends/mt352.h~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.387063008 -0800 +++ 25-akpm/drivers/media/dvb/frontends/mt352.h 2004-11-21 17:13:23.455052672 -0800 @@ -30,145 +30,29 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= */ -#ifndef _MT352_ -#define _MT352_ +#ifndef MT352_H +#define MT352_H -#define I2C_MT352_ADDR 0x0f -#define ID_MT352 0x13 +#include -#define CARD_AVDVBT771 0x00 -#define CARD_TUA6034 0x01 -#define CARD_TDTC9251DH01C 0x02 -#define CARD_DVICODVBT1 0x03 -#define CARD_DVICODVBTLITE 0x04 - -#define msb(x) (((x) >> 8) & 0xff) -#define lsb(x) ((x) & 0xff) - -enum mt352_reg_addr { - STATUS_0 = 0x00, - STATUS_1 = 0x01, - STATUS_2 = 0x02, - STATUS_3 = 0x03, - STATUS_4 = 0x04, - INTERRUPT_0 = 0x05, - INTERRUPT_1 = 0x06, - INTERRUPT_2 = 0x07, - INTERRUPT_3 = 0x08, - SNR = 0x09, - VIT_ERR_CNT_2 = 0x0A, - VIT_ERR_CNT_1 = 0x0B, - VIT_ERR_CNT_0 = 0x0C, - RS_ERR_CNT_2 = 0x0D, - RS_ERR_CNT_1 = 0x0E, - RS_ERR_CNT_0 = 0x0F, - RS_UBC_1 = 0x10, - RS_UBC_0 = 0x11, - AGC_GAIN_3 = 0x12, - AGC_GAIN_2 = 0x13, - AGC_GAIN_1 = 0x14, - AGC_GAIN_0 = 0x15, - FREQ_OFFSET_2 = 0x17, - FREQ_OFFSET_1 = 0x18, - FREQ_OFFSET_0 = 0x19, - TIMING_OFFSET_1 = 0x1A, - TIMING_OFFSET_0 = 0x1B, - CHAN_FREQ_1 = 0x1C, - CHAN_FREQ_0 = 0x1D, - TPS_RECEIVED_1 = 0x1E, - TPS_RECEIVED_0 = 0x1F, - TPS_CURRENT_1 = 0x20, - TPS_CURRENT_0 = 0x21, - TPS_CELL_ID_1 = 0x22, - TPS_CELL_ID_0 = 0x23, - TPS_MISC_DATA_2 = 0x24, - TPS_MISC_DATA_1 = 0x25, - TPS_MISC_DATA_0 = 0x26, - RESET = 0x50, - TPS_GIVEN_1 = 0x51, - TPS_GIVEN_0 = 0x52, - ACQ_CTL = 0x53, - TRL_NOMINAL_RATE_1 = 0x54, - TRL_NOMINAL_RATE_0 = 0x55, - INPUT_FREQ_1 = 0x56, - INPUT_FREQ_0 = 0x57, - TUNER_ADDR = 0x58, - CHAN_START_1 = 0x59, - CHAN_START_0 = 0x5A, - CONT_1 = 0x5B, - CONT_0 = 0x5C, - TUNER_GO = 0x5D, - STATUS_EN_0 = 0x5F, - STATUS_EN_1 = 0x60, - INTERRUPT_EN_0 = 0x61, - INTERRUPT_EN_1 = 0x62, - INTERRUPT_EN_2 = 0x63, - INTERRUPT_EN_3 = 0x64, - AGC_TARGET = 0x67, - AGC_CTL = 0x68, - CAPT_RANGE = 0x75, - SNR_SELECT_1 = 0x79, - SNR_SELECT_0 = 0x7A, - RS_ERR_PER_1 = 0x7C, - RS_ERR_PER_0 = 0x7D, - CHIP_ID = 0x7F, - CHAN_STOP_1 = 0x80, - CHAN_STOP_0 = 0x81, - CHAN_STEP_1 = 0x82, - CHAN_STEP_0 = 0x83, - FEC_LOCK_TIME = 0x85, - OFDM_LOCK_TIME = 0x86, - ACQ_DELAY = 0x87, - SCAN_CTL = 0x88, - CLOCK_CTL = 0x89, - CONFIG = 0x8A, - MCLK_RATIO = 0x8B, - GPP_CTL = 0x8C, - ADC_CTL_1 = 0x8E, - ADC_CTL_0 = 0x8F +struct mt352_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* Initialise the demodulator and PLL. Cannot be NULL */ + int (*demod_init)(struct dvb_frontend* fe); + + /* PLL setup - fill out the supplied 5 byte buffer with your PLL settings. + * byte0: Set to pll i2c address (nonlinux; left shifted by 1) + * byte1-4: PLL configuration. + */ + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, u8* pllbuf); }; -struct _tuner_info { - char *fe_name; -#define FE_NAME tuner_info[card_type].fe_name +extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, + struct i2c_adapter* i2c); - __u32 fe_frequency_min; -#define FE_FREQ_MIN tuner_info[card_type].fe_frequency_min +extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen); - __u32 fe_frequency_max; -#define FE_FREQ_MAX tuner_info[card_type].fe_frequency_max - - __u32 fe_frequency_stepsize; //verificare se u32 e' corretto -#define FE_FREQ_STEPSIZE tuner_info[card_type].fe_frequency_stepsize - - u8 pll_i2c_addr; -#define PLL_I2C_ADDR tuner_info[card_type].pll_i2c_addr - - int (* mt352_init) (struct i2c_adapter *i2c); -#define MT352_INIT tuner_info[card_type].mt352_init - - unsigned char (* mt352_charge_pump) (u32 freq); -#define MT352_CHARGE_PUMP tuner_info[card_type].mt352_charge_pump - - unsigned char (* mt352_band_select) (u32 freq); -#define MT352_BAND_SELECT tuner_info[card_type].mt352_band_select -}; - -static int mt352_init_TUA6034(struct i2c_adapter *i2c); -static int mt352_init_AVERMEDIA771(struct i2c_adapter *i2c); -static int mt352_init_TDTC9251DH01C(struct i2c_adapter *i2c); -static int mt352_init_DVICODVBT1(struct i2c_adapter *i2c); -static int mt352_init_DVICODVBTLITE(struct i2c_adapter *i2c); -static unsigned char mt352_cp_TUA6034(u32 freq); -static unsigned char mt352_cp_AVERMEDIA771(u32 freq); -static unsigned char mt352_cp_TDTC9251DH01C(u32 freq); -static unsigned char mt352_cp_DVICODVBT1(u32 freq); -static unsigned char mt352_cp_DVICODVBTLITE(u32 freq); -static unsigned char mt352_bs_TUA6034(u32 freq); -static unsigned char mt352_bs_AVERMEDIA771(u32 freq); -static unsigned char mt352_bs_TDTC9251DH01C(u32 freq); -static unsigned char mt352_bs_DVICODVBT1(u32 freq); -static unsigned char mt352_bs_DVICODVBTLITE(u32 freq); -static u8 mt352_read_register(struct i2c_adapter *i2c, u8 reg); - -#endif /* _MT352_ */ +#endif // MT352_H diff -puN /dev/null drivers/media/dvb/frontends/mt352_priv.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/mt352_priv.h 2004-11-21 17:13:23.456052520 -0800 @@ -0,0 +1,127 @@ +/* + * Driver for Zarlink DVB-T MT352 demodulator + * + * Written by Holger Waechtler + * and Daniel Mack + * + * AVerMedia AVerTV DVB-T 771 support by + * Wolfram Joost + * + * Support for Samsung TDTC9251DH01C(M) tuner + * Copyright (C) 2004 Antonio Mancuso + * Amauri Celani + * + * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by + * Christopher Pascoe + * + * 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.= + */ + +#ifndef _MT352_PRIV_ +#define _MT352_PRIV_ + +#define ID_MT352 0x13 + +#define msb(x) (((x) >> 8) & 0xff) +#define lsb(x) ((x) & 0xff) + +enum mt352_reg_addr { + STATUS_0 = 0x00, + STATUS_1 = 0x01, + STATUS_2 = 0x02, + STATUS_3 = 0x03, + STATUS_4 = 0x04, + INTERRUPT_0 = 0x05, + INTERRUPT_1 = 0x06, + INTERRUPT_2 = 0x07, + INTERRUPT_3 = 0x08, + SNR = 0x09, + VIT_ERR_CNT_2 = 0x0A, + VIT_ERR_CNT_1 = 0x0B, + VIT_ERR_CNT_0 = 0x0C, + RS_ERR_CNT_2 = 0x0D, + RS_ERR_CNT_1 = 0x0E, + RS_ERR_CNT_0 = 0x0F, + RS_UBC_1 = 0x10, + RS_UBC_0 = 0x11, + AGC_GAIN_3 = 0x12, + AGC_GAIN_2 = 0x13, + AGC_GAIN_1 = 0x14, + AGC_GAIN_0 = 0x15, + FREQ_OFFSET_2 = 0x17, + FREQ_OFFSET_1 = 0x18, + FREQ_OFFSET_0 = 0x19, + TIMING_OFFSET_1 = 0x1A, + TIMING_OFFSET_0 = 0x1B, + CHAN_FREQ_1 = 0x1C, + CHAN_FREQ_0 = 0x1D, + TPS_RECEIVED_1 = 0x1E, + TPS_RECEIVED_0 = 0x1F, + TPS_CURRENT_1 = 0x20, + TPS_CURRENT_0 = 0x21, + TPS_CELL_ID_1 = 0x22, + TPS_CELL_ID_0 = 0x23, + TPS_MISC_DATA_2 = 0x24, + TPS_MISC_DATA_1 = 0x25, + TPS_MISC_DATA_0 = 0x26, + RESET = 0x50, + TPS_GIVEN_1 = 0x51, + TPS_GIVEN_0 = 0x52, + ACQ_CTL = 0x53, + TRL_NOMINAL_RATE_1 = 0x54, + TRL_NOMINAL_RATE_0 = 0x55, + INPUT_FREQ_1 = 0x56, + INPUT_FREQ_0 = 0x57, + TUNER_ADDR = 0x58, + CHAN_START_1 = 0x59, + CHAN_START_0 = 0x5A, + CONT_1 = 0x5B, + CONT_0 = 0x5C, + TUNER_GO = 0x5D, + STATUS_EN_0 = 0x5F, + STATUS_EN_1 = 0x60, + INTERRUPT_EN_0 = 0x61, + INTERRUPT_EN_1 = 0x62, + INTERRUPT_EN_2 = 0x63, + INTERRUPT_EN_3 = 0x64, + AGC_TARGET = 0x67, + AGC_CTL = 0x68, + CAPT_RANGE = 0x75, + SNR_SELECT_1 = 0x79, + SNR_SELECT_0 = 0x7A, + RS_ERR_PER_1 = 0x7C, + RS_ERR_PER_0 = 0x7D, + CHIP_ID = 0x7F, + CHAN_STOP_1 = 0x80, + CHAN_STOP_0 = 0x81, + CHAN_STEP_1 = 0x82, + CHAN_STEP_0 = 0x83, + FEC_LOCK_TIME = 0x85, + OFDM_LOCK_TIME = 0x86, + ACQ_DELAY = 0x87, + SCAN_CTL = 0x88, + CLOCK_CTL = 0x89, + CONFIG = 0x8A, + MCLK_RATIO = 0x8B, + GPP_CTL = 0x8C, + ADC_CTL_1 = 0x8E, + ADC_CTL_0 = 0x8F +}; + +/* here we assume 1/6MHz == 166.66kHz stepsize */ +#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ + +#endif /* _MT352_PRIV_ */ diff -puN drivers/media/dvb/frontends/nxt6000.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/nxt6000.c --- 25/drivers/media/dvb/frontends/nxt6000.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.389062704 -0800 +++ 25-akpm/drivers/media/dvb/frontends/nxt6000.c 2004-11-21 17:13:23.460051912 -0800 @@ -1,13 +1,6 @@ /* - NxtWave Communications - NXT6000 demodulator driver - This driver currently supports: - - Alps TDME7 (Tuner: MITEL SP5659) - Alps TDED4 (Tuner: TI ALP510, external Nxt6000) - Comtech DVBT-6k07 (PLL IC: SP5730) - Copyright (C) 2002-2003 Florian Schirmer Copyright (C) 2003 Paul Andreassen @@ -34,217 +27,68 @@ #include #include "dvb_frontend.h" +#include "nxt6000_priv.h" #include "nxt6000.h" -MODULE_DESCRIPTION("NxtWave NXT6000 DVB demodulator driver"); -MODULE_AUTHOR("Florian Schirmer"); -MODULE_LICENSE("GPL"); - -static int debug = 0; -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, -}; +struct nxt6000_state { -struct nxt6000_config { - u8 demod_addr; - u8 tuner_addr; - u8 tuner_type; - u8 clock_inversion; struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; -#define TUNER_TYPE_ALP510 0 -#define TUNER_TYPE_SP5659 1 -#define TUNER_TYPE_SP5730 2 + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct nxt6000_config* config; -// #define FE2NXT(fe) ((struct nxt6000_config *)((fe)->data)) -#define FREQ2DIV(freq) ((freq + 36166667) / 166667) + struct dvb_frontend frontend; +}; + +static int debug = 0; #define dprintk if (debug) printk -static int nxt6000_write(struct i2c_adapter *i2c, u8 addr, u8 reg, u8 data) +static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data) { u8 buf[] = {reg, data}; - struct i2c_msg msg = {.addr = addr >> 1, .flags = 0, .buf = buf, .len = 2}; + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; int ret; - if ((ret = i2c_transfer(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); + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) + dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret); return (ret != 1) ? -EFAULT : 0; } -static u8 nxt6000_writereg(struct nxt6000_config *nxt, u8 reg, u8 data) -{ - return nxt6000_write(nxt->i2c, nxt->demod_addr, reg, data); -} - -static u8 nxt6000_read(struct i2c_adapter *i2c, u8 addr, u8 reg) +static u8 nxt6000_readreg(struct nxt6000_state* state, 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} + {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} }; - ret = i2c_transfer(i2c, msgs, 2); + ret = i2c_transfer(state->i2c, msgs, 2); if (ret != 2) - dprintk("nxt6000: nxt6000_read error (.addr = 0x%02X, reg: 0x%02X, ret: %d)\n", addr, reg, ret); + dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret); return b1[0]; } -static u8 nxt6000_readreg(struct nxt6000_config *nxt, u8 reg) -{ - return nxt6000_read(nxt->i2c, nxt->demod_addr, reg); -} - -static int pll_test(struct i2c_adapter *i2c, u8 demod_addr, u8 tuner_addr) -{ - u8 buf [1]; - struct i2c_msg msg = {.addr = tuner_addr >> 1,.flags = I2C_M_RD,.buf = buf,.len = 1 }; - int ret; - - nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ - ret = i2c_transfer(i2c, &msg, 1); - nxt6000_write(i2c, demod_addr, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ - - return (ret != 1) ? -EFAULT : 0; -} - -static int pll_write(struct i2c_adapter *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_transfer(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 nxt6000_config *nxt, u32 freq) -{ - u8 buf[4]; - - 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(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4); -} - -static int alp510_set_tv_freq(struct nxt6000_config *nxt, u32 freq) -{ - u8 buf[4]; - - 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(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4); -} - -static int sp5730_set_tv_freq(struct nxt6000_config *nxt, u32 freq) -{ - u8 buf[4]; - - buf[0] = (FREQ2DIV(freq) >> 8) & 0x7F; - buf[1] = FREQ2DIV(freq) & 0xFF; - buf[2] = 0x93; - - if ((freq >= 51000000) && (freq < 132100000)) - buf[3] = 0x05; - else if ((freq >= 132100000) && (freq < 143000000)) - buf[3] = 0x45; - else if ((freq >= 146000000) && (freq < 349100000)) - buf[3] = 0x06; - else if ((freq >= 349100000) && (freq < 397100000)) - buf[3] = 0x46; - else if ((freq >= 397100000) && (freq < 426000000)) - buf[3] = 0x86; - else if ((freq >= 430000000) && (freq < 659100000)) - buf[3] = 0x03; - else if ((freq >= 659100000) && (freq < 759100000)) - buf[3] = 0x43; - else if ((freq >= 759100000) && (freq < 858000000)) - buf[3] = 0x83; - else - return -EINVAL; - - return pll_write(nxt->i2c, nxt->demod_addr, nxt->tuner_addr, buf, 4); -} - -static void nxt6000_reset(struct nxt6000_config *fe) +static void nxt6000_reset(struct nxt6000_state* state) { u8 val; - val = nxt6000_readreg(fe, OFDM_COR_CTL); + val = nxt6000_readreg(state, OFDM_COR_CTL); - nxt6000_writereg(fe, OFDM_COR_CTL, val & ~COREACT); - nxt6000_writereg(fe, OFDM_COR_CTL, val | COREACT); + nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT); + nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT); } -static int nxt6000_set_bandwidth(struct nxt6000_config *fe, fe_bandwidth_t bandwidth) +static int nxt6000_set_bandwidth(struct nxt6000_state* state, fe_bandwidth_t bandwidth) { u16 nominal_rate; int result; @@ -270,119 +114,115 @@ static int nxt6000_set_bandwidth(struct break; default: - return -EINVAL; - } - if ((result = nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0) + if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0) return result; - return nxt6000_writereg(fe, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); + return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); } -static int nxt6000_set_guard_interval(struct nxt6000_config *fe, fe_guard_interval_t guard_interval) +static int nxt6000_set_guard_interval(struct nxt6000_state* state, 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)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); case GUARD_INTERVAL_1_16: - - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, 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)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); case GUARD_INTERVAL_1_4: - - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x03)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); default: return -EINVAL; } } -static int nxt6000_set_inversion(struct nxt6000_config *fe, fe_spectral_inversion_t inversion) +static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion) { switch(inversion) { case INVERSION_OFF: - - return nxt6000_writereg(fe, OFDM_ITB_CTL, 0x00); + return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00); case INVERSION_ON: - - return nxt6000_writereg(fe, OFDM_ITB_CTL, ITBINV); + return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV); default: - return -EINVAL; } } -static int nxt6000_set_transmission_mode(struct nxt6000_config *fe, fe_transmit_mode_t transmission_mode) +static int nxt6000_set_transmission_mode(struct nxt6000_state* state, 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) + if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) return result; - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, 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) + if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) return result; - return nxt6000_writereg(fe, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(fe, OFDM_COR_MODEGUARD) & ~0x04)); + return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); default: - return -EINVAL; } } -static void nxt6000_setup(struct nxt6000_config *fe) +static void nxt6000_setup(struct dvb_frontend* 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); + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; - if (fe->clock_inversion) - nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, CLKINVERSION); + nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM); + nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01); + nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC); + nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F)); + nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02); + nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW); + nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06); + nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31); + nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04); + nxt6000_writereg(state, CAS_FREQ, 0xBB); /* CHECKME */ + nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2); + nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256); + nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49); + nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72); + nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5); + nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2); + nxt6000_writereg(state, DIAG_CONFIG, TB_SET); + + if (state->config->clock_inversion) + nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION); else - nxt6000_writereg(fe, SUB_DIAG_MODE_SEL, 0); + nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0); + + nxt6000_writereg(state, TS_FORMAT, 0); - nxt6000_writereg(fe, TS_FORMAT, 0); + if (state->config->pll_init) { + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ + state->config->pll_init(fe); + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ + } } -static void nxt6000_dump_status(struct nxt6000_config *fe) +static void nxt6000_dump_status(struct nxt6000_state *state) { u8 val; @@ -400,12 +240,12 @@ static void nxt6000_dump_status(struct n */ printk("NXT6000 status:"); - val = nxt6000_readreg(fe, RS_COR_STAT); + val = nxt6000_readreg(state, 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); + val = nxt6000_readreg(state, VIT_SYNC_STATUS); printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01); @@ -443,7 +283,7 @@ static void nxt6000_dump_status(struct n } - val = nxt6000_readreg(fe, OFDM_COR_STAT); + val = nxt6000_readreg(state, OFDM_COR_STAT); printk(" CHCTrack: %d,", (val >> 7) & 0x01); printk(" TPSLock: %d,", (val >> 6) & 0x01); @@ -496,7 +336,7 @@ static void nxt6000_dump_status(struct n } - val = nxt6000_readreg(fe, OFDM_SYR_STAT); + val = nxt6000_readreg(state, OFDM_SYR_STAT); printk(" SYRLock: %d,", (val >> 4) & 0x01); printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K"); @@ -522,14 +362,11 @@ static void nxt6000_dump_status(struct n break; case 0x03: - printk(" SYRGuard: 1/4,"); - break; - } - val = nxt6000_readreg(fe, OFDM_TPS_RCVD_3); + val = nxt6000_readreg(state, OFDM_TPS_RCVD_3); switch((val >> 4) & 0x07) { @@ -599,7 +436,7 @@ static void nxt6000_dump_status(struct n } - val = nxt6000_readreg(fe, OFDM_TPS_RCVD_4); + val = nxt6000_readreg(state, OFDM_TPS_RCVD_4); printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K"); @@ -632,299 +469,156 @@ static void nxt6000_dump_status(struct n } /* 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); + nxt6000_readreg(state, RF_AGC_VAL_1); + val = nxt6000_readreg(state, RF_AGC_STATUS); + val = nxt6000_readreg(state, RF_AGC_STATUS); printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01); printk("\n"); } -static int nxt6000_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg) -{ - struct nxt6000_config *fe = (struct nxt6000_config *) f->data; - 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; + + + + +static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ u8 core_status; + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; *status = 0; - core_status = nxt6000_readreg(fe, OFDM_COR_STAT); + core_status = nxt6000_readreg(state, OFDM_COR_STAT); if (core_status & AGCLOCKED) *status |= FE_HAS_SIGNAL; - if (nxt6000_readreg(fe, OFDM_SYR_STAT) & GI14_SYR_LOCK) + if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK) *status |= FE_HAS_CARRIER; - if (nxt6000_readreg(fe, VIT_SYNC_STATUS) & VITINSYNC) + if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC) *status |= FE_HAS_VITERBI; - if (nxt6000_readreg(fe, RS_COR_STAT) & RSCORESTATUS) + if (nxt6000_readreg(state, 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); + nxt6000_dump_status(state); return 0; - } - case FE_READ_BER: +static int nxt6000_init(struct dvb_frontend* fe) { - u32 *ber = (u32 *)arg; - - *ber=0; + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; - return 0; - - } + nxt6000_reset(state); + nxt6000_setup(fe); - case FE_READ_SIGNAL_STRENGTH: - { - s16 *signal = (s16 *) arg; -/* - *signal=(((signed char)readreg(client, 0x16))+128)<<8; -*/ - *signal = 0; return 0; - - } - - case FE_READ_SNR: - { - s16 *snr = (s16 *) arg; -/* - *snr=readreg(client, 0x24)<<8; - *snr|=readreg(client, 0x25); -*/ - *snr = 0; - break; - } - - case FE_READ_UNCORRECTED_BLOCKS: - { - u32 *ublocks = (u32 *)arg; - - *ublocks = 0; - - break; } - case FE_INIT: - nxt6000_reset(fe); - nxt6000_setup(fe); - break; - case FE_SET_FRONTEND: +static int nxt6000_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { - struct dvb_frontend_parameters *param = (struct dvb_frontend_parameters *)arg; + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; int result; - switch (fe->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; - - case TUNER_TYPE_SP5730: + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); /* open i2c bus switch */ + state->config->pll_set(fe, param); + nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); /* close i2c bus switch */ - if ((result = sp5730_set_tv_freq(fe, param->frequency)) < 0) + if ((result = nxt6000_set_bandwidth(state, param->u.ofdm.bandwidth)) < 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) + if ((result = nxt6000_set_guard_interval(state, param->u.ofdm.guard_interval)) < 0) return result; - if ((result = nxt6000_set_transmission_mode(fe, param->u.ofdm.transmission_mode)) < 0) + if ((result = nxt6000_set_transmission_mode(state, param->u.ofdm.transmission_mode)) < 0) return result; - if ((result = nxt6000_set_inversion(fe, param->inversion)) < 0) + if ((result = nxt6000_set_inversion(state, param->inversion)) < 0) return result; - - break; - } - - default: - - return -EOPNOTSUPP; - - } return 0; - } -static u8 demod_addr_tbl[] = {0x14, 0x18, 0x24, 0x28}; - -static struct i2c_client client_template; -static int attach_adapter(struct i2c_adapter *adapter) +static void nxt6000_release(struct dvb_frontend* fe) { - struct i2c_client *client; - struct nxt6000_config *nxt; - u8 addr_nr; - int ret; - - if ((nxt = kmalloc(sizeof(struct nxt6000_config), GFP_KERNEL)) == NULL) - return -ENOMEM; - - memset(nxt, 0, sizeof(*nxt)); - nxt->i2c = adapter; - - for (addr_nr = 0; addr_nr < sizeof(demod_addr_tbl); addr_nr++) { - - if (nxt6000_read(adapter, demod_addr_tbl[addr_nr], OFDM_MSC_REV) != NXT6000ASICDEVICE) - continue; - - if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC0) == 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_test(adapter, demod_addr_tbl[addr_nr], 0xC2) == 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 if (pll_test(adapter, demod_addr_tbl[addr_nr], 0xC0) == 0) { - nxt->tuner_addr = 0xC0; - nxt->tuner_type = TUNER_TYPE_SP5730; - nxt->clock_inversion = 0; - - dprintk("nxt6000: detected SP5730 tuner at 0x%02X\n", nxt->tuner_addr); - - } else { - printk("nxt6000: unable to detect tuner\n"); - continue; - } + struct nxt6000_state* state = (struct nxt6000_state*) fe->demodulator_priv; + kfree(state); } - if (addr_nr == sizeof(demod_addr_tbl)) { - kfree(nxt); - return -ENODEV; - } +static struct dvb_frontend_ops nxt6000_ops; - nxt->demod_addr = demod_addr_tbl[addr_nr]; - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(nxt); - return -ENOMEM; -} - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = demod_addr_tbl[addr_nr]; - i2c_set_clientdata(client, (void *) nxt); - - ret = i2c_attach_client(client); - if (ret) - goto out; - - BUG_ON(!nxt->dvb); - - ret = dvb_register_frontend(nxt6000_ioctl, nxt->dvb, nxt, &nxt6000_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - goto out; - } - - ret = 0; -out: - kfree(client); - kfree(nxt); - return ret; -} - -static int detach_client(struct i2c_client *client) +struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c) { - struct nxt6000_config *state = (struct nxt6000_config *) i2c_get_clientdata(client); - dvb_unregister_frontend(nxt6000_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} + struct nxt6000_state* state = NULL; -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct nxt6000_config *state = (struct nxt6000_config *) i2c_get_clientdata(client); + /* allocate memory for the internal state */ + state = (struct nxt6000_state*) kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops)); + + /* check if the demod is there */ + if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; + } + +static struct dvb_frontend_ops nxt6000_ops = { + + .info = { + .name = "NxtWave NXT6000 DVB-T", + .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, + .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, + }, - switch (cmd) { - case FE_REGISTER:{ - state->dvb = (struct dvb_adapter *) arg; - break; - } - case FE_UNREGISTER:{ - state->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; - } - return 0; -} + .release = nxt6000_release, + + .init = nxt6000_init, -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "nxt6000", - .id = I2C_DRIVERID_DVBFE_NXT6000, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; + .set_frontend = nxt6000_set_frontend, -static struct i2c_client client_template = { - I2C_DEVNAME("nxt6000"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + .read_status = nxt6000_read_status, }; -static __init int nxt6000_init(void) -{ - return i2c_add_driver(&driver); -} +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -static __exit void nxt6000_exit(void) -{ - if (i2c_del_driver(&driver)) - printk("nxt6000: driver deregistration failed\n"); -} +MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver"); +MODULE_AUTHOR("Florian Schirmer"); +MODULE_LICENSE("GPL"); -module_init(nxt6000_init); -module_exit(nxt6000_exit); +EXPORT_SYMBOL(nxt6000_attach); diff -puN drivers/media/dvb/frontends/nxt6000.h~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/nxt6000.h --- 25/drivers/media/dvb/frontends/nxt6000.h~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.390062552 -0800 +++ 25-akpm/drivers/media/dvb/frontends/nxt6000.h 2004-11-21 17:13:23.462051608 -0800 @@ -1,266 +1,43 @@ /* - * Public Include File for DRV6000 users - * (ie. NxtWave Communications - NXT6000 demodulator driver) - * - * Copyright (C) 2001 NxtWave Communications, Inc. - * - */ - -/* 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) + NxtWave Communications - NXT6000 demodulator driver -#define NXT6000ASICDEVICE (0x0b) + Copyright (C) 2002-2003 Florian Schirmer + Copyright (C) 2003 Paul Andreassen + 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. +*/ + +#ifndef NXT6000_H +#define NXT6000_H + +#include + +struct nxt6000_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* should clock inversion be used? */ + u8 clock_inversion:1; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, + struct i2c_adapter* i2c); + +#endif // NXT6000_H diff -puN /dev/null drivers/media/dvb/frontends/nxt6000_priv.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/nxt6000_priv.h 2004-11-21 17:13:23.463051456 -0800 @@ -0,0 +1,265 @@ +/* + * Public Include File for DRV6000 users + * (ie. NxtWave Communications - NXT6000 demodulator driver) + * + * Copyright (C) 2001 NxtWave Communications, Inc. + * + */ + +/* 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) diff -puN /dev/null drivers/media/dvb/frontends/sp8870.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/sp8870.c 2004-11-21 17:13:23.466051000 -0800 @@ -0,0 +1,613 @@ +/* + Driver for Spase SP8870 demodulator + + Copyright (C) 1999 Juergen Peitz + + 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. + +*/ +/* + * This driver needs external firmware. Please use the command + * "/Documentation/dvb/get_dvb_firmware alps_tdlb7" to + * download/extract it, and then copy it to /usr/lib/hotplug/firmware. + */ +#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw" + +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "sp8870.h" + + +struct sp8870_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct sp8870_config* config; + + struct dvb_frontend frontend; + + /* demodulator private data */ + u8 initialised:1; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "sp8870: " args); \ + } while (0) + +/* firmware size for sp8870 */ +#define SP8870_FIRMWARE_SIZE 16382 + +/* starting point for firmware in file 'Sc_main.mc' */ +#define SP8870_FIRMWARE_OFFSET 0x0A + +static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) +{ + u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; + int err; + + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int sp8870_readreg (struct sp8870_state* state, u16 reg) +{ + int ret; + u8 b0 [] = { reg >> 8 , reg & 0xff }; + u8 b1 [] = { 0, 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; + + ret = i2c_transfer (state->i2c, msg, 2); + + if (ret != 2) { + dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); + return -1; + } + + return (b1[0] << 8 | b1[1]); +} + +static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw) +{ + struct i2c_msg msg; + char *fw_buf = fw->data; + int fw_pos; + u8 tx_buf[255]; + int tx_len; + int err = 0; + + dprintk ("%s: ...\n", __FUNCTION__); + + if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) + return -EINVAL; + + // system controller stop + sp8870_writereg(state, 0x0F00, 0x0000); + + // instruction RAM register hiword + sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF)); + + // instruction RAM MWR + sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16)); + + // do firmware upload + fw_pos = SP8870_FIRMWARE_OFFSET; + while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ + tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 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 = state->config->demod_address; + msg.flags = 0; + msg.buf = tx_buf; + msg.len = tx_len + 2; + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { + printk("%s: firmware upload failed!\n", __FUNCTION__); + printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err); + return err; + } + fw_pos += tx_len; + } + + dprintk ("%s: done!\n", __FUNCTION__); + return 0; +}; + +static void sp8870_microcontroller_stop (struct sp8870_state* state) +{ + sp8870_writereg(state, 0x0F08, 0x000); + sp8870_writereg(state, 0x0F09, 0x000); + + // microcontroller STOP + sp8870_writereg(state, 0x0F00, 0x000); +} + +static void sp8870_microcontroller_start (struct sp8870_state* state) +{ + sp8870_writereg(state, 0x0F08, 0x000); + sp8870_writereg(state, 0x0F09, 0x000); + + // microcontroller START + sp8870_writereg(state, 0x0F00, 0x001); + // not documented but if we don't read 0x0D01 out here + // we don't get a correct data valid signal + sp8870_readreg(state, 0x0D01); +} + +static int sp8870_read_data_valid_signal(struct sp8870_state* state) +{ + return (sp8870_readreg(state, 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_wake_up(struct sp8870_state* state) +{ + // enable TS output and interface pins + return sp8870_writereg(state, 0xC18, 0x00D); +} + +static int sp8870_set_frontend_parameters (struct dvb_frontend* fe, + struct dvb_frontend_parameters *p) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int err; + u16 reg0xc05; + + if ((err = configure_reg0xc05(p, ®0xc05))) + return err; + + // system controller stop + sp8870_microcontroller_stop(state); + + // set tuner parameters + sp8870_writereg(state, 0x206, 0x001); + state->config->pll_set(fe, p); + sp8870_writereg(state, 0x206, 0x000); + + // sample rate correction bit [23..17] + sp8870_writereg(state, 0x0319, 0x000A); + + // sample rate correction bit [16..0] + sp8870_writereg(state, 0x031A, 0x0AAB); + + // integer carrier offset + sp8870_writereg(state, 0x0309, 0x0400); + + // fractional carrier offset + sp8870_writereg(state, 0x030A, 0x0000); + + // filter for 6/7/8 Mhz channel + if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) + sp8870_writereg(state, 0x0311, 0x0002); + else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ) + sp8870_writereg(state, 0x0311, 0x0001); + else + sp8870_writereg(state, 0x0311, 0x0000); + + // scan order: 2k first = 0x0000, 8k first = 0x0001 + if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K) + sp8870_writereg(state, 0x0338, 0x0000); + else + sp8870_writereg(state, 0x0338, 0x0001); + + sp8870_writereg(state, 0xc05, reg0xc05); + + // read status reg in order to clear pending irqs + sp8870_readreg(state, 0x200); + + // system controller start + sp8870_microcontroller_start(state); + + return 0; +} + +static int sp8870_init (struct dvb_frontend* fe) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + const struct firmware *fw = NULL; + + sp8870_wake_up(state); + if (state->initialised) return 0; + state->initialised = 1; + + dprintk ("%s\n", __FUNCTION__); + + + /* request the firmware, this will block until someone uploads it */ + printk("sp8870: waiting for firmware upload...\n"); + if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { + printk("sp8870: no firmware upload (timeout or file not found?)\n"); + release_firmware(fw); + return -EIO; + } + + if (sp8870_firmware_upload(state, fw)) { + printk("sp8870: writing firmware to device failed\n"); + release_firmware(fw); + return -EIO; + } + + /* enable TS output and interface pins */ + sp8870_writereg(state, 0xc18, 0x00d); + + // system controller stop + sp8870_microcontroller_stop(state); + + // ADC mode + sp8870_writereg(state, 0x0301, 0x0003); + + // Reed Solomon parity bytes passed to output + sp8870_writereg(state, 0x0C13, 0x0001); + + // MPEG clock is suppressed if no valid data + sp8870_writereg(state, 0x0C14, 0x0001); + + /* bit 0x010: enable data valid signal */ + sp8870_writereg(state, 0x0D00, 0x010); + sp8870_writereg(state, 0x0D01, 0x000); + + /* setup PLL */ + if (state->config->pll_init) { + sp8870_writereg(state, 0x206, 0x001); + state->config->pll_init(fe); + sp8870_writereg(state, 0x206, 0x000); + } + + return 0; +} + +static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int status; + int signal; + + *fe_status = 0; + + status = sp8870_readreg (state, 0x0200); + if (status < 0) + return -EIO; + + signal = sp8870_readreg (state, 0x0303); + if (signal < 0) + return -EIO; + + if (signal > 0x0F) + *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; +} + +static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int ret; + u32 tmp; + + *ber = 0; + + ret = sp8870_readreg(state, 0xC08); + if (ret < 0) + return -EIO; + + tmp = ret & 0x3F; + + ret = sp8870_readreg(state, 0xC07); + if (ret < 0) + return -EIO; + + tmp = ret << 6; + + if (tmp >= 0x3FFF0) + tmp = ~0; + + *ber = tmp; + + return 0; +} + +static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int ret; + u16 tmp; + + *signal = 0; + + ret = sp8870_readreg (state, 0x306); + if (ret < 0) + return -EIO; + + tmp = ret << 8; + + ret = sp8870_readreg (state, 0x303); + if (ret < 0) + return -EIO; + + tmp |= ret; + + if (tmp) + *signal = 0xFFFF - tmp; + + return 0; +} + +static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + int ret; + + *ublocks = 0; + + ret = sp8870_readreg(state, 0xC0C); + if (ret < 0) + return -EIO; + + if (ret == 0xFFFF) + ret = ~0; + + *ublocks = ret; + + return 0; +} + +// 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_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + + /* + 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(fe, 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(state); + if (valid) { + dprintk("%s: delay = %i usec\n", + __FUNCTION__, check_count * 10); + break; + } + udelay(10); + } + if (valid) + break; + } + + 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_frontend* fe) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + + // tristate TS output and disable interface pins + return sp8870_writereg(state, 0xC18, 0x000); +} + +static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +{ + fesettings->min_delay_ms = 350; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static void sp8870_release(struct dvb_frontend* fe) +{ + struct sp8870_state* state = (struct sp8870_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops sp8870_ops; + +struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c) +{ + struct sp8870_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct sp8870_state*) kmalloc(sizeof(struct sp8870_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + + /* check if the demod is there */ + if (sp8870_readreg(state, 0x0200) < 0) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops sp8870_ops = { + + .info = { + .name = "Spase SP8870 DVB-T", + .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 | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER + }, + + .release = sp8870_release, + + .init = sp8870_init, + .sleep = sp8870_sleep, + + .set_frontend = sp8870_set_frontend, + .get_tune_settings = sp8870_get_tune_settings, + + .read_status = sp8870_read_status, + .read_ber = sp8870_read_ber, + .read_signal_strength = sp8870_read_signal_strength, + .read_ucblocks = sp8870_read_uncorrected_blocks, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); +MODULE_AUTHOR("Juergen Peitz"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(sp8870_attach); diff -puN /dev/null drivers/media/dvb/frontends/sp8870.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/sp8870.h 2004-11-21 17:13:23.466051000 -0800 @@ -0,0 +1,45 @@ +/* + Driver for Spase SP8870 demodulator + + Copyright (C) 1999 Juergen Peitz + + 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. + +*/ + +#ifndef SP8870_H +#define SP8870_H + +#include +#include + +struct sp8870_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, + struct i2c_adapter* i2c); + +#endif // SP8870_H diff -puN drivers/media/dvb/frontends/sp887x.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/sp887x.c --- 25/drivers/media/dvb/frontends/sp887x.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.392062248 -0800 +++ 25-akpm/drivers/media/dvb/frontends/sp887x.c 2004-11-21 17:13:23.470050392 -0800 @@ -1,5 +1,5 @@ /* - Driver for the Microtune 7202D Frontend + Driver for the Spase sp887x demodulator */ /* @@ -16,74 +16,50 @@ #include #include "dvb_frontend.h" +#include "sp887x.h" -#define FRONTEND_NAME "dvbfe_sp887x" -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) +struct sp887x_state { -static int debug; + struct i2c_adapter* i2c; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + struct dvb_frontend_ops ops; -#if 0 -#define LOG(dir,addr,buf,len) \ - do { \ - int i; \ - printk("%s (%02x):", dir, addr & 0xff); \ - for (i=0; iconfig->demod_address, .flags = 0, .buf = buf, .len = len }; int err; - LOG("i2c_writebytes", msg.addr, msg.buf, msg.len); - - if ((err = i2c_transfer (i2c, &msg, 1)) != 1) { + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { printk ("%s: i2c write error (addr %02x, err == %i)\n", - __FUNCTION__, addr, err); + __FUNCTION__, state->config->demod_address, err); return -EREMOTEIO; } return 0; } -static int sp887x_writereg (struct i2c_adapter *i2c, u16 reg, u16 data) +static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data) { u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff }; - struct i2c_msg msg = { .addr = 0x70, .flags = 0, .buf = b0, .len = 4 }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 }; int ret; - LOG("sp887x_writereg", msg.addr, msg.buf, msg.len); - - if ((ret = i2c_transfer(i2c, &msg, 1)) != 1) { + if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { /** * in case of soft reset we ignore ACK errors... */ @@ -100,61 +76,60 @@ static int sp887x_writereg (struct i2c_a return 0; } -static u16 sp887x_readreg (struct i2c_adapter *i2c, u16 reg) +static int sp887x_readreg (struct sp887x_state* state, u16 reg) { u8 b0 [] = { reg >> 8 , reg & 0xff }; u8 b1 [2]; int ret; - struct i2c_msg msg[] = {{ .addr = 0x70, .flags = 0, .buf = b0, .len = 2 }, - { .addr = 0x70, .flags = I2C_M_RD, .buf = b1, .len = 2 }}; - - LOG("sp887x_readreg (w)", msg[0].addr, msg[0].buf, msg[0].len); - LOG("sp887x_readreg (r)", msg[1].addr, msg[1].buf, msg[1].len); + struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }}; - if ((ret = i2c_transfer(i2c, msg, 2)) != 2) + if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { printk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); + return -1; + } return (((b1[0] << 8) | b1[1]) & 0xfff); } -static void sp887x_microcontroller_stop (struct i2c_adapter *fe) +static void sp887x_microcontroller_stop (struct sp887x_state* state) { dprintk("%s\n", __FUNCTION__); - sp887x_writereg(fe, 0xf08, 0x000); - sp887x_writereg(fe, 0xf09, 0x000); + sp887x_writereg(state, 0xf08, 0x000); + sp887x_writereg(state, 0xf09, 0x000); /* microcontroller STOP */ - sp887x_writereg(fe, 0xf00, 0x000); + sp887x_writereg(state, 0xf00, 0x000); } -static void sp887x_microcontroller_start (struct i2c_adapter *fe) +static void sp887x_microcontroller_start (struct sp887x_state* state) { dprintk("%s\n", __FUNCTION__); - sp887x_writereg(fe, 0xf08, 0x000); - sp887x_writereg(fe, 0xf09, 0x000); + sp887x_writereg(state, 0xf08, 0x000); + sp887x_writereg(state, 0xf09, 0x000); /* microcontroller START */ - sp887x_writereg(fe, 0xf00, 0x001); + sp887x_writereg(state, 0xf00, 0x001); } -static void sp887x_setup_agc (struct i2c_adapter *fe) +static void sp887x_setup_agc (struct sp887x_state* state) { /* setup AGC parameters */ dprintk("%s\n", __FUNCTION__); - sp887x_writereg(fe, 0x33c, 0x054); - sp887x_writereg(fe, 0x33b, 0x04c); - sp887x_writereg(fe, 0x328, 0x000); - sp887x_writereg(fe, 0x327, 0x005); - sp887x_writereg(fe, 0x326, 0x001); - sp887x_writereg(fe, 0x325, 0x001); - sp887x_writereg(fe, 0x324, 0x001); - sp887x_writereg(fe, 0x318, 0x050); - sp887x_writereg(fe, 0x317, 0x3fe); - sp887x_writereg(fe, 0x316, 0x001); - sp887x_writereg(fe, 0x313, 0x005); - sp887x_writereg(fe, 0x312, 0x002); - sp887x_writereg(fe, 0x306, 0x000); - sp887x_writereg(fe, 0x303, 0x000); + sp887x_writereg(state, 0x33c, 0x054); + sp887x_writereg(state, 0x33b, 0x04c); + sp887x_writereg(state, 0x328, 0x000); + sp887x_writereg(state, 0x327, 0x005); + sp887x_writereg(state, 0x326, 0x001); + sp887x_writereg(state, 0x325, 0x001); + sp887x_writereg(state, 0x324, 0x001); + sp887x_writereg(state, 0x318, 0x050); + sp887x_writereg(state, 0x317, 0x3fe); + sp887x_writereg(state, 0x316, 0x001); + sp887x_writereg(state, 0x313, 0x005); + sp887x_writereg(state, 0x312, 0x002); + sp887x_writereg(state, 0x306, 0x000); + sp887x_writereg(state, 0x303, 0x000); } #define BLOCKSIZE 30 @@ -162,8 +137,9 @@ static void sp887x_setup_agc (struct i2c /** * load firmware and setup MPEG interface... */ -static int sp887x_initial_setup (struct i2c_adapter *fe, const struct firmware *fw) +static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw) { + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; u8 buf [BLOCKSIZE+2]; int i; int fw_size = fw->size; @@ -178,18 +154,18 @@ static int sp887x_initial_setup (struct mem = fw->data + 10; /* soft reset */ - sp887x_writereg(fe, 0xf1a, 0x000); + sp887x_writereg(state, 0xf1a, 0x000); - sp887x_microcontroller_stop (fe); + sp887x_microcontroller_stop (state); printk ("%s: firmware upload... ", __FUNCTION__); /* setup write pointer to -1 (end of memory) */ /* bit 0x8000 in address is set to enable 13bit mode */ - sp887x_writereg(fe, 0x8f08, 0x1fff); + sp887x_writereg(state, 0x8f08, 0x1fff); /* dummy write (wrap around to start of memory) */ - sp887x_writereg(fe, 0x8f0a, 0x0000); + sp887x_writereg(state, 0x8f0a, 0x0000); for (i = 0; i < FW_SIZE; i += BLOCKSIZE) { int c = BLOCKSIZE; @@ -206,7 +182,7 @@ static int sp887x_initial_setup (struct memcpy(&buf[2], mem + i, c); - if ((err = i2c_writebytes (fe, 0x70, buf, c+2)) < 0) { + if ((err = i2c_writebytes (state, buf, c+2)) < 0) { printk ("failed.\n"); printk ("%s: i2c error (err == %i)\n", __FUNCTION__, err); return err; @@ -214,61 +190,37 @@ static int sp887x_initial_setup (struct } /* don't write RS bytes between packets */ - sp887x_writereg(fe, 0xc13, 0x001); + sp887x_writereg(state, 0xc13, 0x001); /* suppress clock if (!data_valid) */ - sp887x_writereg(fe, 0xc14, 0x000); + sp887x_writereg(state, 0xc14, 0x000); /* setup MPEG interface... */ - sp887x_writereg(fe, 0xc1a, 0x872); - sp887x_writereg(fe, 0xc1b, 0x001); - sp887x_writereg(fe, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */ - sp887x_writereg(fe, 0xc1a, 0x871); + sp887x_writereg(state, 0xc1a, 0x872); + sp887x_writereg(state, 0xc1b, 0x001); + sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */ + sp887x_writereg(state, 0xc1a, 0x871); /* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */ - sp887x_writereg(fe, 0x301, 0x002); + sp887x_writereg(state, 0x301, 0x002); - sp887x_setup_agc(fe); + sp887x_setup_agc(state); /* bit 0x010: enable data valid signal */ - sp887x_writereg(fe, 0xd00, 0x010); - sp887x_writereg(fe, 0x0d1, 0x000); + sp887x_writereg(state, 0xd00, 0x010); + sp887x_writereg(state, 0x0d1, 0x000); + + /* setup the PLL */ + if (state->config->pll_init) { + sp887x_writereg(state, 0x206, 0x001); + state->config->pll_init(fe); + sp887x_writereg(state, 0x206, 0x000); + } printk ("done.\n"); return 0; }; -/** - * returns the actual tuned center frequency which can be used - * to initialise the AFC registers - */ -static int tsa5060_setup_pll (struct i2c_adapter *fe, int freq) -{ - u8 cfg, cpump, band_select; - u8 buf [4]; - u32 div; - - div = (36000000 + freq + 83333) / 166666; - cfg = 0x88; - - cpump = freq < 175000000 ? 2 : freq < 390000000 ? 1 : - freq < 470000000 ? 2 : freq < 750000000 ? 2 : 3; - - band_select = freq < 175000000 ? 0x0e : freq < 470000000 ? 0x05 : 0x03; - - buf [0] = (div >> 8) & 0x7f; - buf [1] = div & 0xff; - buf [2] = ((div >> 10) & 0x60) | cfg; - buf [3] = cpump | band_select; - - /* open i2c gate for PLL message transmission... */ - sp887x_writereg(fe, 0x206, 0x001); - i2c_writebytes(fe, 0x60, buf, 4); - sp887x_writereg(fe, 0x206, 0x000); - - return (div * 166666 - 36000000); -} - static int configure_reg0xc05 (struct dvb_frontend_parameters *p, u16 *reg0xc05) { int known_parameters = 1; @@ -362,7 +314,7 @@ static void divide (int n, int d, int *q } } -static void sp887x_correct_offsets (struct i2c_adapter *fe, +static void sp887x_correct_offsets (struct sp887x_state* state, struct dvb_frontend_parameters *p, int actual_freq) { @@ -385,17 +337,31 @@ static void sp887x_correct_offsets (stru frequency_shift = -frequency_shift; /* sample rate correction */ - sp887x_writereg(fe, 0x319, srate_correction[bw_index] >> 12); - sp887x_writereg(fe, 0x31a, srate_correction[bw_index] & 0xfff); + sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12); + sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff); /* carrier offset correction */ - sp887x_writereg(fe, 0x309, frequency_shift >> 12); - sp887x_writereg(fe, 0x30a, frequency_shift & 0xfff); + sp887x_writereg(state, 0x309, frequency_shift >> 12); + sp887x_writereg(state, 0x30a, frequency_shift & 0xfff); } -static int sp887x_setup_frontend_parameters (struct i2c_adapter *fe, + + + + + + + + + + + + + +static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; int actual_freq, err; u16 val, reg0xc05; @@ -407,14 +373,17 @@ static int sp887x_setup_frontend_paramet if ((err = configure_reg0xc05(p, ®0xc05))) return err; - sp887x_microcontroller_stop(fe); + sp887x_microcontroller_stop(state); - actual_freq = tsa5060_setup_pll(fe, p->frequency); + /* setup the PLL */ + sp887x_writereg(state, 0x206, 0x001); + actual_freq = state->config->pll_set(fe, p); + sp887x_writereg(state, 0x206, 0x000); - /* read status reg in order to clear pending irqs */ - sp887x_readreg(fe, 0x200); + /* read status reg in order to clear u.ofdm.bandwidth == BANDWIDTH_6_MHZ) @@ -424,15 +393,15 @@ static int sp887x_setup_frontend_paramet else val = 0; - sp887x_writereg(fe, 0x311, val); + sp887x_writereg(state, 0x311, val); /* scan order: 2k first = 0, 8k first = 1 */ if (p->u.ofdm.transmission_mode == TRANSMISSION_MODE_2K) - sp887x_writereg(fe, 0x338, 0x000); + sp887x_writereg(state, 0x338, 0x000); else - sp887x_writereg(fe, 0x338, 0x001); + sp887x_writereg(state, 0x338, 0x001); - sp887x_writereg(fe, 0xc05, reg0xc05); + sp887x_writereg(state, 0xc05, reg0xc05); if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ) val = 2 << 3; @@ -444,29 +413,19 @@ static int sp887x_setup_frontend_paramet /* enable OFDM and SAW bits as lock indicators in sync register 0xf17, * optimize algorithm for given bandwidth... */ - sp887x_writereg(fe, 0xf14, 0x160 | val); - sp887x_writereg(fe, 0xf15, 0x000); + sp887x_writereg(state, 0xf14, 0x160 | val); + sp887x_writereg(state, 0xf15, 0x000); - sp887x_microcontroller_start(fe); + sp887x_microcontroller_start(state); return 0; } -static int sp887x_ioctl(struct dvb_frontend *f, unsigned int cmd, void *arg) -{ - struct sp887x_state *state = (struct sp887x_state *) f->data; - struct i2c_adapter *fe = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &sp887x_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: +static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status) { - u16 snr12 = sp887x_readreg(fe, 0xf16); - u16 sync0x200 = sp887x_readreg(fe, 0x200); - u16 sync0xf17 = sp887x_readreg(fe, 0xf17); - fe_status_t *status = arg; + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + u16 snr12 = sp887x_readreg(state, 0xf16); + u16 sync0x200 = sp887x_readreg(state, 0x200); + u16 sync0xf17 = sp887x_readreg(state, 0xf17); *status = 0; @@ -492,212 +451,172 @@ static int sp887x_ioctl(struct dvb_front steps); } - break; - + return 0; } - case FE_READ_BER: +static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber) { - u32* ber = arg; - *ber = (sp887x_readreg(fe, 0xc08) & 0x3f) | - (sp887x_readreg(fe, 0xc07) << 6); - sp887x_writereg(fe, 0xc08, 0x000); - sp887x_writereg(fe, 0xc07, 0x000); + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + + *ber = (sp887x_readreg(state, 0xc08) & 0x3f) | + (sp887x_readreg(state, 0xc07) << 6); + sp887x_writereg(state, 0xc08, 0x000); + sp887x_writereg(state, 0xc07, 0x000); if (*ber >= 0x3fff0) *ber = ~0; - break; + return 0; } - case FE_READ_SIGNAL_STRENGTH: // FIXME: correct registers ? +static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - u16 snr12 = sp887x_readreg(fe, 0xf16); + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + + u16 snr12 = sp887x_readreg(state, 0xf16); u32 signal = 3 * (snr12 << 4); - *((u16*) arg) = (signal < 0xffff) ? signal : 0xffff; - break; - } + *strength = (signal < 0xffff) ? signal : 0xffff; - case FE_READ_SNR: - { - u16 snr12 = sp887x_readreg(fe, 0xf16); - *(u16*) arg = (snr12 << 4) | (snr12 >> 8); - break; + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: +static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr) { - u32 *ublocks = (u32 *) arg; - *ublocks = sp887x_readreg(fe, 0xc0c); - if (*ublocks == 0xfff) - *ublocks = ~0; - break; - } + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; - case FE_SET_FRONTEND: - return sp887x_setup_frontend_parameters(fe, arg); - - case FE_GET_FRONTEND: // FIXME: read known values back from Hardware... - break; + u16 snr12 = sp887x_readreg(state, 0xf16); + *snr = (snr12 << 4) | (snr12 >> 8); - case FE_SLEEP: - /* tristate TS output and disable interface pins */ - sp887x_writereg(fe, 0xc18, 0x000); - break; - - case FE_INIT: - /* enable TS output and interface pins */ - sp887x_writereg(fe, 0xc18, 0x00d); - break; - - case FE_GET_TUNE_SETTINGS: - { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; - fesettings->min_delay_ms = 350; - fesettings->step_size = 166666*2; - fesettings->max_drift = (166666*2)+1; return 0; } - default: - return -EOPNOTSUPP; - }; +static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + + *ucblocks = sp887x_readreg(state, 0xc0c); + if (*ucblocks == 0xfff) + *ucblocks = ~0; return 0; } -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +static int sp887x_sleep(struct dvb_frontend* fe) { - struct i2c_client *client; - struct sp887x_state *state; - const struct firmware *fw; - int ret; - - struct i2c_msg msg = {.addr = 0x70, .flags = 0, .buf = NULL, .len = 0 }; - - dprintk ("%s\n", __FUNCTION__); - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - return -ENOMEM; - } + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; - if (NULL == (state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL))) { - kfree(client); - return -ENOMEM; - } - state->i2c = adapter; + /* tristate TS output and disable interface pins */ + sp887x_writereg(state, 0xc18, 0x000); - if (i2c_transfer (adapter, &msg, 1) != 1) { - kfree(state); - kfree(client); - return -ENODEV; + return 0; } - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return ret; - } +static int sp887x_init(struct dvb_frontend* fe) +{ + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + const struct firmware *fw = NULL; + int ret; + if (!state->initialised) { /* request the firmware, this will block until someone uploads it */ printk("sp887x: waiting for firmware upload...\n"); - ret = request_firmware(&fw, SP887X_DEFAULT_FIRMWARE, &client->dev); + ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE); if (ret) { printk("sp887x: no firmware upload (timeout or file not found?)\n"); - goto out; + return ret; } - ret = sp887x_initial_setup(adapter, fw); + ret = sp887x_initial_setup(fe, fw); if (ret) { printk("sp887x: writing firmware to device failed\n"); - goto out; + release_firmware(fw); + return ret; } - - ret = dvb_register_frontend(sp887x_ioctl, state->dvb, state, - &sp887x_info, THIS_MODULE); - if (ret) { - printk("sp887x: registering frontend to dvb-core failed.\n"); - goto out; + state->initialised = 1; } + /* enable TS output and interface pins */ + sp887x_writereg(state, 0xc18, 0x00d); + return 0; -out: - release_firmware(fw); - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; } -static int detach_client(struct i2c_client *client) +static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - dvb_unregister_frontend (sp887x_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; + fesettings->min_delay_ms = 350; + fesettings->step_size = 166666*2; + fesettings->max_drift = (166666*2)+1; + return 0; } -static int command (struct i2c_client *client, unsigned int cmd, void *arg) +static void sp887x_release(struct dvb_frontend* fe) { - struct sp887x_state *state = (struct sp887x_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: - state->dvb = (struct dvb_adapter*)arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - return 0; + struct sp887x_state* state = (struct sp887x_state*) fe->demodulator_priv; + kfree(state); } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_SP887X, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; +static struct dvb_frontend_ops sp887x_ops; -static int __init init_sp887x(void) +struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c) { - return i2c_add_driver(&driver); -} + struct sp887x_state* state = NULL; -static void __exit exit_sp887x(void) -{ - if (i2c_del_driver(&driver)) - printk("sp887x: driver deregistration failed\n"); -} + /* allocate memory for the internal state */ + state = (struct sp887x_state*) kmalloc(sizeof(struct sp887x_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &sp887x_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + + /* check if the demod is there */ + if (sp887x_readreg(state, 0x0200) < 0) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops sp887x_ops = { + + .info = { + .name = "Spase SP887x DVB-T", + .type = FE_OFDM, + .frequency_min = 50500000, + .frequency_max = 858000000, + .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 | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_RECOVER + }, + + .release = sp887x_release, + + .init = sp887x_init, + .sleep = sp887x_sleep, + + .set_frontend = sp887x_setup_frontend_parameters, + .get_tune_settings = sp887x_get_tune_settings, + + .read_status = sp887x_read_status, + .read_ber = sp887x_read_ber, + .read_signal_strength = sp887x_read_signal_strength, + .read_snr = sp887x_read_snr, + .read_ucblocks = sp887x_read_ucblocks, +}; -module_init(init_sp887x); -module_exit(exit_sp887x); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("sp887x DVB-T demodulator driver"); +MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(sp887x_attach); diff -puN /dev/null drivers/media/dvb/frontends/sp887x.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/sp887x.h 2004-11-21 17:13:23.471050240 -0800 @@ -0,0 +1,29 @@ +/* + Driver for the Spase sp887x demodulator +*/ + +#ifndef SP887X_H +#define SP887X_H + +#include +#include + +struct sp887x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + + /* this should return the actual frequency tuned to */ + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, + struct i2c_adapter* i2c); + +#endif // SP887X_H diff -puN /dev/null drivers/media/dvb/frontends/stv0297.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/stv0297.c 2004-11-21 17:13:23.474049784 -0800 @@ -0,0 +1,774 @@ +/* + Driver for STV0297 demodulator + + Copyright (C) 2004 Andrew de Quincey + Copyright (C) 2003-2004 Dennis Noermann + + 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 +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "stv0297.h" + +struct stv0297_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct stv0297_config* config; + + struct dvb_frontend frontend; + + int freq_off; + + unsigned long base_freq; + + u8 pwm; +}; + +#if 1 +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +#define STV0297_CLOCK 28900 + +static u8 init_tab [] = { + 0x00, 0x09, + 0x01, 0x69, + 0x03, 0x00, + 0x04, 0x00, + 0x07, 0x00, + 0x08, 0x00, + 0x20, 0x00, + 0x21, 0x40, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x40, + 0x25, 0x88, + 0x30, 0xff, + 0x31, 0x00, + 0x32, 0xff, + 0x33, 0x00, + 0x34, 0x50, + 0x35, 0x7f, + 0x36, 0x00, + 0x37, 0x20, + 0x38, 0x00, + 0x40, 0x1c, + 0x41, 0xff, + 0x42, 0x29, + 0x43, 0x00,// check + 0x44, 0xff, + 0x45, 0x00, + 0x46, 0x00, + 0x49, 0x04, + 0x4a, 0xff, + 0x4b, 0x7f, + 0x52, 0x30, + 0x55, 0xae, + 0x56, 0x47, + 0x57, 0xe1, + 0x58, 0x3a, + 0x5a, 0x1e, + 0x5b, 0x34, + 0x60, 0x00, + 0x63, 0x00, + 0x64, 0x00, + 0x65, 0x00, + 0x66, 0x00, + 0x67, 0x00, + 0x68, 0x00, + 0x69, 0x00, + 0x6a, 0x02, + 0x6b, 0x00, + 0x70, 0xff, + 0x71, 0x00, + 0x72, 0x00, + 0x73, 0x00, + 0x74, 0x0c, + 0x80, 0x00, + 0x81, 0x00, + 0x82, 0x00, + 0x83, 0x00, + 0x84, 0x04, + 0x85, 0x80, + 0x86, 0x24, + 0x87, 0x78, + 0x88, 0x00, + 0x89, 0x00, + 0x90, 0x01, + 0x91, 0x01, + 0xa0, 0x00, + 0xa1, 0x00, + 0xa2, 0x00, + 0xb0, 0x91, + 0xb1, 0x0b, + 0xc0, 0x53, + 0xc1, 0x70, + 0xc2, 0x12, + 0xd0, 0x00, + 0xd1, 0x00, + 0xd2, 0x00, + 0xd3, 0x00, + 0xd4, 0x00, + 0xd5, 0x00, + 0xde, 0x00, + 0xdf, 0x00, + 0x61, 0x49, + 0x62, 0x0b, + 0x53, 0x08, + 0x59, 0x08, +}; + + +static int stv0297_writereg (struct stv0297_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + ret = i2c_transfer (state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " + "ret == %i)\n", __FUNCTION__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int stv0297_readreg (struct stv0297_state* state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + + // this device needs a STOP between the register and data + if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __FUNCTION__, reg, ret); + return -1; + } + if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __FUNCTION__, reg, ret); + return -1; + } + + return b1[0]; +} + +static int stv0297_writereg_mask (struct stv0297_state* state, u8 reg, u8 mask, u8 data) +{ + int val; + + val = stv0297_readreg(state, reg); + val &= ~mask; + val |= (data & mask); + stv0297_writereg(state, reg, val); + + return 0; +} + +static int stv0297_readregs (struct stv0297_state* state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; + + // this device needs a STOP between the register and data + if ((ret = i2c_transfer (state->i2c, &msg[0], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __FUNCTION__, reg1, ret); + return -1; + } + if ((ret = i2c_transfer (state->i2c, &msg[1], 1)) != 1) { + dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", + __FUNCTION__, reg1, ret); + return -1; + } + + return 0; +} + +static int stv0297_set_symbolrate (struct stv0297_state* state, u32 srate) +{ + u64 tmp; + + tmp = srate; + tmp <<= 32; + do_div(tmp, STV0297_CLOCK); + + stv0297_writereg (state, 0x55,(unsigned char)(tmp & 0xFF)); + stv0297_writereg (state, 0x56,(unsigned char)(tmp>> 8)); + stv0297_writereg (state, 0x57,(unsigned char)(tmp>>16)); + stv0297_writereg (state, 0x58,(unsigned char)(tmp>>24)); + + return 0; +} + +static u32 stv0297_get_symbolrate (struct stv0297_state* state) +{ + u64 tmp; + + tmp = stv0297_readreg(state, 0x55); + tmp |= (stv0297_readreg(state, 0x56) << 8); + tmp |= (stv0297_readreg(state, 0x57) << 16); + tmp |= (stv0297_readreg(state, 0x57) << 24); + + tmp *= STV0297_CLOCK; + tmp >>= 32; + return tmp; +} + +static void stv0297_set_sweeprate(struct stv0297_state* state, short fshift) +{ + s64 tmp; + u32 symrate; + + symrate = stv0297_get_symbolrate(state); + + // cannot use shifts - it is signed + tmp = fshift * (1<<28); + do_div(tmp, symrate); + + // adjust + if (tmp >= 0) { + tmp += 500000; + } else { + tmp -= 500000; + } + do_div(tmp, 1000000); + + stv0297_writereg(state, 0x60, tmp & 0xFF); + stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0); + + return; +} + +static void stv0297_set_carrieroffset(struct stv0297_state* state, long offset) +{ + long long_tmp; + + // symrate is hardcoded to 10000 here - don't ask me why + long_tmp = offset * 26844L ; /* (2**28)/10000 */ + if(long_tmp < 0) long_tmp += 0x10000000 ; + long_tmp &= 0x0FFFFFFF ; + + stv0297_writereg (state,0x66,(unsigned char)(long_tmp & 0xFF)); // iphase0 + stv0297_writereg (state,0x67,(unsigned char)(long_tmp>>8)); // iphase1 + stv0297_writereg (state,0x68,(unsigned char)(long_tmp>>16)); // iphase2 + stv0297_writereg_mask(state, 0x69, 0x0F, (long_tmp >> 24) & 0x0f); + + return; +} + +static long stv0297_get_carrieroffset(struct stv0297_state* state) +{ + s32 raw; + s64 tmp; + u32 symbol_rate; + + stv0297_writereg(state,0x6B, 0x00); + + symbol_rate = stv0297_get_symbolrate(state); + + raw = stv0297_readreg(state,0x66); + raw |= (stv0297_readreg(state,0x67) << 8); + raw |= (stv0297_readreg(state,0x68) << 16); + raw |= (stv0297_readreg(state,0x69) & 0x0F) << 24; + + // cannot just use a shift here 'cos it is signed + tmp = raw; + tmp *= symbol_rate; + do_div(tmp, 1<<28); + + return (s32) tmp; +} + +static void stv0297_set_initialdemodfreq(struct stv0297_state* state, long freq) +{ + u64 tmp; + + if (freq > 10000) freq -= STV0297_CLOCK; + if (freq < 0) freq = 0; + + tmp = freq << 16; + do_div(tmp, STV0297_CLOCK); + if (tmp > 0xffff) tmp = 0xffff; + + stv0297_writereg_mask(state, 0x25, 0x80, 0x80); + stv0297_writereg(state, 0x21, tmp >> 8); + stv0297_writereg(state, 0x20, tmp); +} + +static int stv0297_set_qam(struct stv0297_state* state, fe_modulation_t modulation) +{ + int val = 0; + + switch(modulation) { + case QAM_16: + val = 0; + break; + + case QAM_32: + val = 1; + break; + + case QAM_64: + val = 4; + break; + + case QAM_128: + val = 2; + break; + + case QAM_256: + val = 3; + break; + + default: + return -EINVAL; + } + + stv0297_writereg_mask(state, 0x00, 0x70, val << 4); + + return 0; +} + +static int stv0297_set_inversion(struct stv0297_state* state, fe_spectral_inversion_t inversion) +{ + int val = 0; + + switch(inversion) { + case INVERSION_OFF: + val = 0; + break; + + case INVERSION_ON: + val = 1; + break; + + default: + return -EINVAL; + } + + stv0297_writereg_mask(state, 0x83, 0x08, val << 3); + + return 0; +} + + + + + + + + + + + + + +int stv0297_enable_plli2c(struct dvb_frontend* fe) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + + stv0297_writereg(state, 0x87, 0x78); + stv0297_writereg(state, 0x86, 0xc8); + + return 0; +} + +static int stv0297_init (struct dvb_frontend* fe) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + int i; + + stv0297_writereg_mask(state, 0x80, 1, 1); + stv0297_writereg_mask(state, 0x80, 1, 0); + stv0297_writereg_mask(state, 0x81, 1, 1); + stv0297_writereg_mask(state, 0x81, 1, 0); + + for (i=0; ipwm); + msleep(200); + + if (state->config->pll_init) state->config->pll_init(fe); + + return 0; +} + +static int stv0297_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + + u8 sync = stv0297_readreg (state, 0xDF); + + *status = 0; + if (sync & 0x80) + *status |= FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; + return 0; +} + +static int stv0297_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + u8 BER[3]; + + stv0297_writereg (state, 0xA0, 0x80); // Start Counting bit errors for 4096 Bytes + mdelay(25); // Hopefully got 4096 Bytes + stv0297_readregs (state, 0xA0, BER, 3); + mdelay(25); + *ber = (BER[2] << 8 | BER[1]) / ( 8 * 4096); + + return 0; +} + + +static int stv0297_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + u8 STRENGTH[2]; + + stv0297_readregs (state, 0x41, STRENGTH, 2); + *strength = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0]; + + return 0; +} + +static int stv0297_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + u8 SNR[2]; + + stv0297_readregs (state, 0x07, SNR, 2); + *snr = SNR[1] << 8 | SNR[0]; + + return 0; +} + +static int stv0297_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + + *ucblocks = (stv0297_readreg (state, 0xD5) << 8) + | stv0297_readreg (state, 0xD4); + + return 0; +} + +static int stv0297_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + int u_threshold; + int initial_u; + int blind_u; + int delay; + int locked; + int sweeprate; + int carrieroffset; + unsigned long starttime; + unsigned long timeout; + + switch(p->u.qam.modulation) { + case QAM_16: + case QAM_32: + case QAM_64: + delay = 100; + sweeprate = 1500; + break; + + case QAM_128: + delay = 150; + sweeprate = 1000; + break; + + case QAM_256: + delay = 200; + sweeprate = 500; + break; + + default: + return -EINVAL; + } + + // determine inversion dependant parameters + carrieroffset = -330; + switch(p->inversion) { + case INVERSION_OFF: + break; + + case INVERSION_ON: + sweeprate = -sweeprate; + carrieroffset = -carrieroffset; + break; + + default: + return -EINVAL; + } + + state->config->pll_set(fe, p); + + // reset everything + stv0297_writereg_mask(state, 0x82, 0x4, 0x4); + stv0297_set_initialdemodfreq(state, state->freq_off + 7250); + stv0297_writereg_mask(state, 0x43, 0x10, 0x00); + stv0297_writereg(state, 0x41, 0x00); + stv0297_writereg_mask(state, 0x42, 0x03, 0x01); + stv0297_writereg_mask(state, 0x36, 0x60, 0x00); + stv0297_writereg_mask(state, 0x36, 0x18, 0x00); + stv0297_writereg_mask(state, 0x71, 0x80, 0x80); + stv0297_writereg(state, 0x72, 0x00); + stv0297_writereg(state, 0x73, 0x00); + stv0297_writereg_mask(state, 0x74, 0x0F, 0x00); + stv0297_writereg_mask(state, 0x43, 0x08, 0x00); + stv0297_writereg_mask(state, 0x71, 0x80, 0x00); + stv0297_writereg_mask(state, 0x5a, 0x20, 0x20); + stv0297_writereg_mask(state, 0x5b, 0x02, 0x02); + stv0297_writereg_mask(state, 0x5b, 0x02, 0x00); + stv0297_writereg_mask(state, 0x5b, 0x01, 0x00); + stv0297_writereg_mask(state, 0x5a, 0x40, 0x40); + stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); + stv0297_writereg_mask(state, 0x81, 0x01, 0x01); + stv0297_writereg_mask(state, 0x81, 0x01, 0x00); + stv0297_writereg_mask(state, 0x83, 0x20, 0x20); + stv0297_writereg_mask(state, 0x83, 0x20, 0x00); + u_threshold = stv0297_readreg(state, 0x00) & 0xf; + initial_u = stv0297_readreg(state, 0x01) >> 4; + blind_u = stv0297_readreg(state, 0x01) & 0xf; + stv0297_writereg_mask(state, 0x84, 0x01, 0x01); + stv0297_writereg_mask(state, 0x84, 0x01, 0x00); + stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold); + stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4); + stv0297_writereg_mask(state, 0x01, 0x0f, blind_u); + stv0297_writereg_mask(state, 0x87, 0x80, 0x00); + stv0297_writereg(state, 0x63, 0x00); + stv0297_writereg(state, 0x64, 0x00); + stv0297_writereg(state, 0x65, 0x00); + stv0297_writereg(state, 0x66, 0x00); + stv0297_writereg(state, 0x67, 0x00); + stv0297_writereg(state, 0x68, 0x00); + stv0297_writereg_mask(state, 0x69, 0x0f, 0x00); + + // set parameters + stv0297_set_qam(state, p->u.qam.modulation); + stv0297_set_symbolrate(state, p->u.qam.symbol_rate/1000); + stv0297_set_sweeprate(state, sweeprate); + stv0297_set_carrieroffset(state, carrieroffset); + stv0297_set_inversion(state, p->inversion); + + // kick off lock + stv0297_writereg_mask(state, 0x88, 0x08, 0x08); + stv0297_writereg_mask(state, 0x5a, 0x20, 0x00); + stv0297_writereg_mask(state, 0x6a, 0x01, 0x01); + stv0297_writereg_mask(state, 0x43, 0x40, 0x40); + stv0297_writereg_mask(state, 0x5b, 0x30, 0x00); + stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c); + stv0297_writereg_mask(state, 0x03, 0x03, 0x03); + stv0297_writereg_mask(state, 0x43, 0x10, 0x10); + + // wait for WGAGC lock + starttime = jiffies; + timeout = jiffies + (200*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + if (stv0297_readreg(state, 0x43) & 0x08) break; + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + msleep(20); + + // wait for equaliser partial convergence + locked = 0; + timeout = jiffies + (50*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0x82) & 0x04) { + locked = 1; + } + } + if (time_after(jiffies, timeout) && (!locked)) { + goto timeout; + } + + // wait for equaliser full convergence + timeout = jiffies + (delay*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0x82) & 0x08) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + + // disable sweep + stv0297_writereg_mask(state, 0x6a, 1, 0); + stv0297_writereg_mask(state, 0x88, 8, 0); + + // wait for main lock + timeout = jiffies + (20*HZ)/1000; + while(time_before(jiffies, timeout)) { + msleep(10); + + if (stv0297_readreg(state, 0xDF) & 0x80) { + break; + } + } + if (time_after(jiffies, timeout)) { + goto timeout; + } + msleep(100); + + // is it still locked after that delay? + if (!(stv0297_readreg(state, 0xDF) & 0x80)) { + goto timeout; + } + + // success!! + stv0297_writereg_mask(state, 0x5a, 0x40, 0x00); + state->freq_off = stv0297_get_carrieroffset(state); + state->base_freq = p->frequency; + return 0; + +timeout: + stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); + return 0; +} + +static int stv0297_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + int reg_00, reg_83; + + reg_00 = stv0297_readreg(state, 0x00); + reg_83 = stv0297_readreg(state, 0x83); + + p->frequency = state->base_freq + state->freq_off; + p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF; + p->u.qam.symbol_rate = stv0297_get_symbolrate(state); + p->u.qam.fec_inner = 0; + + switch((reg_00 >> 4) & 0x7) { + case 0: p->u.qam.modulation = QAM_16; break; + case 1: p->u.qam.modulation = QAM_32; break; + case 2: p->u.qam.modulation = QAM_128; break; + case 3: p->u.qam.modulation = QAM_256; break; + case 4: p->u.qam.modulation = QAM_64; break; + } + + return 0; +} + +static void stv0297_release(struct dvb_frontend* fe) +{ + struct stv0297_state* state = (struct stv0297_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops stv0297_ops; + +struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, + struct i2c_adapter* i2c, + int pwm) +{ + struct stv0297_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct stv0297_state*) kmalloc(sizeof(struct stv0297_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &stv0297_ops, sizeof(struct dvb_frontend_ops)); + state->freq_off = 0; + state->base_freq = 0; + state->pwm = pwm; + + /* check if the demod is there */ + if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops stv0297_ops = { + + .info = { + .name = "ST STV0297 DVB-C", + .type = FE_QAM, + .frequency_min = 64000000, + .frequency_max = 1300000000, + .frequency_stepsize = 62500, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000, + .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_RECOVER + }, + + .release = stv0297_release, + + .init = stv0297_init, + + .set_frontend = stv0297_set_frontend, + .get_frontend = stv0297_get_frontend, + + .read_status = stv0297_read_status, + .read_ber = stv0297_read_ber, + .read_signal_strength = stv0297_read_signal_strength, + .read_snr = stv0297_read_snr, + .read_ucblocks = stv0297_read_ucblocks, +}; + +MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver"); +MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(stv0297_attach); +EXPORT_SYMBOL(stv0297_enable_plli2c); diff -puN /dev/null drivers/media/dvb/frontends/stv0297.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/stv0297.h 2004-11-21 17:13:23.475049632 -0800 @@ -0,0 +1,41 @@ +/* + Driver for STV0297 demodulator + + Copyright (C) 2003-2004 Dennis Noermann + + 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. +*/ + +#ifndef STV0297_H +#define STV0297_H + +#include +#include "dvb_frontend.h" + +struct stv0297_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, + struct i2c_adapter* i2c, int pwm); +extern int stv0297_enable_plli2c(struct dvb_frontend* fe); + +#endif // STV0297_H diff -puN drivers/media/dvb/frontends/stv0299.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/stv0299.c --- 25/drivers/media/dvb/frontends/stv0299.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.394061944 -0800 +++ 25-akpm/drivers/media/dvb/frontends/stv0299.c 2004-11-21 17:13:23.483048416 -0800 @@ -1,8 +1,5 @@ /* - Universal driver for STV0299/TDA5059/SL1935 based - DVB QPSK frontends - - Alps BSRU6, LG TDQB-S00x + Driver for ST STV0299 demodulator Copyright (C) 2001-2002 Convergence Integrated Media GmbH , @@ -54,236 +51,42 @@ #include #include "dvb_frontend.h" +#include "stv0299.h" -#define FRONTEND_NAME "dvbfe_stv0299" - -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) - -static int debug; -static int stv0299_status; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -module_param(stv0299_status, int, 0444); -MODULE_PARM_DESC(stv0299_status, "Which status value to support " - "(0 == BER (default), 1 == UCBLOCKS)"); +struct stv0299_state { -#define STATUS_BER 0 -#define STATUS_UCBLOCKS 1 + struct i2c_adapter* i2c; + struct dvb_frontend_ops ops; -/* frontend types */ -#define UNKNOWN_FRONTEND -1 -#define PHILIPS_SU1278_TSA 0 // SU1278 with TSA5059 synth and datasheet recommended settings -#define ALPS_BSRU6 1 -#define LG_TDQF_S001F 2 -#define PHILIPS_SU1278_TUA 3 // SU1278 with TUA6100 synth -#define SAMSUNG_TBMU24112IMB 4 -#define PHILIPS_SU1278_TSA_TT 5 // SU1278 with TSA5059 synth and TechnoTrend settings -#define PHILIPS_SU1278_TSA_TY 6 // SU1278 with TUA5059 synth and Typhoon wiring -#define PHILIPS_SU1278_TSA_CI 7 // SU1278 with TUA5059 synth and TerraTec Cinergy wiring - -/* Master Clock = 88 MHz */ -#define M_CLK (88000000UL) - -/* Master Clock for TT cards = 64 MHz */ -#define M_CLK_SU1278_TSA_TT (64000000UL) - -static 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 -}; + const struct stv0299_config* config; + struct dvb_frontend frontend; -struct stv0299_state { - u8 tuner_type; u8 initialised:1; u32 tuner_frequency; u32 symbol_rate; fe_code_rate_t fec_inner; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - - -static u8 init_tab [] = { - 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, LNB power on OP2/LOCK pin on */ - 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 - - 0x15, 0xc9, // lock detector threshold - - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - - 0x1f, 0x50, - - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - - 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 - - 0x29, 0x1e, // 1/2 threshold - 0x2a, 0x14, // 2/3 threshold - 0x2b, 0x0f, // 3/4 threshold - 0x2c, 0x09, // 5/6 threshold - 0x2d, 0x05, // 7/8 threshold - 0x2e, 0x01, - - 0x31, 0x1f, // test all FECs - - 0x32, 0x19, // viterbi and synchro search - 0x33, 0xfc, // rs control - 0x34, 0x93, // error control -}; - - -static u8 init_tab_samsung [] = { - 0x01, 0x15, - 0x02, 0x00, - 0x03, 0x00, - 0x04, 0x7D, - 0x05, 0x35, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0xC3, - 0x0C, 0x00, - 0x0D, 0x81, - 0x0E, 0x23, - 0x0F, 0x12, - 0x10, 0x7E, - 0x11, 0x84, - 0x12, 0xB9, - 0x13, 0x88, - 0x14, 0x89, - 0x15, 0xC9, - 0x16, 0x00, - 0x17, 0x5C, - 0x18, 0x00, - 0x19, 0x00, - 0x1A, 0x00, - 0x1C, 0x00, - 0x1D, 0x00, - 0x1E, 0x00, - 0x1F, 0x3A, - 0x20, 0x2E, - 0x21, 0x80, - 0x22, 0xFF, - 0x23, 0xC1, - 0x28, 0x00, - 0x29, 0x1E, - 0x2A, 0x14, - 0x2B, 0x0F, - 0x2C, 0x09, - 0x2D, 0x05, - 0x31, 0x1F, - 0x32, 0x19, - 0x33, 0xFE, - 0x34, 0x93 + int errmode; }; +#define STATUS_BER 0 +#define STATUS_UCBLOCKS 1 -static u8 init_tab_su1278_tsa_tt [] = { - 0x01, 0x0f, - 0x02, 0x30, - 0x03, 0x00, - 0x04, 0x5b, - 0x05, 0x85, - 0x06, 0x02, - 0x07, 0x00, - 0x08, 0x02, - 0x09, 0x00, - 0x0C, 0x01, - 0x0D, 0x81, - 0x0E, 0x44, - 0x0f, 0x14, - 0x10, 0x3c, - 0x11, 0x84, - 0x12, 0xda, - 0x13, 0x97, - 0x14, 0x95, - 0x15, 0xc9, - 0x16, 0x19, - 0x17, 0x8c, - 0x18, 0x59, - 0x19, 0xf8, - 0x1a, 0xfe, - 0x1c, 0x7f, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0x50, - 0x20, 0x00, - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x28, 0x00, - 0x29, 0x28, - 0x2a, 0x14, - 0x2b, 0x0f, - 0x2c, 0x09, - 0x2d, 0x09, - 0x31, 0x1f, - 0x32, 0x19, - 0x33, 0xfc, - 0x34, 0x13 -}; +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "stv0299: " args); \ + } while (0) -static int stv0299_set_FEC (struct i2c_adapter *i2c, fe_code_rate_t fec); -static int stv0299_set_symbolrate (struct i2c_adapter *i2c, u32 srate, int tuner_type); -static int stv0299_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) +static int stv0299_writeregI (struct stv0299_state* state, 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 = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; - ret = i2c_transfer (i2c, &msg, 1); + ret = i2c_transfer (state->i2c, &msg, 1); if (ret != 1) dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " @@ -292,16 +95,23 @@ static int stv0299_writereg (struct i2c_ return (ret != 1) ? -EREMOTEIO : 0; } +int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + return stv0299_writeregI(state, reg, data); +} + -static u8 stv0299_readreg (struct i2c_adapter *i2c, u8 reg) +static u8 stv0299_readreg (struct stv0299_state* state, 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 = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - ret = i2c_transfer (i2c, msg, 2); + ret = i2c_transfer (state->i2c, msg, 2); if (ret != 2) dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", @@ -311,13 +121,13 @@ static u8 stv0299_readreg (struct i2c_ad } -static int stv0299_readregs (struct i2c_adapter *i2c, u8 reg1, u8 *b, u8 len) +static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len) { int ret; - struct i2c_msg msg [] = { { .addr = 0x68, .flags = 0, .buf = ®1, .len = 1 }, - { .addr = 0x68, .flags = I2C_M_RD, .buf = b, .len = len } }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; - ret = i2c_transfer (i2c, msg, 2); + ret = i2c_transfer (state->i2c, msg, 2); if (ret != 2) dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); @@ -326,373 +136,44 @@ static int stv0299_readregs (struct i2c_ } -static int pll_write (struct i2c_adapter *i2c, u8 addr, u8 *data, int len) -{ - int ret; - struct i2c_msg msg = { .addr = addr, .buf = data, .len = len }; - - - stv0299_writereg(i2c, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ - - ret = i2c_transfer (i2c, &msg, 1); - - stv0299_writereg(i2c, 0x05, 0x35); /* disable i2c repeater on stv0299 */ - - if (ret != 1) - dprintk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 1) ? -1 : 0; -} - - -static int sl1935_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype) -{ - u8 buf[4]; - u32 div; - - div = freq / 125; - - dprintk("%s : freq = %i, div = %i\n", __FUNCTION__, freq, div); - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x84; // 0xC4 - buf[3] = 0x08; - - if (freq < 1500000) buf[3] |= 0x10; - - return pll_write (i2c, 0x61, buf, sizeof(buf)); -} - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 125 kHz. - */ -static int tsa5059_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype, int srate) -{ - u8 addr; - u32 div; - u8 buf[4]; - int divisor, regcode; - - dprintk ("%s: freq %i, ftype %i\n", __FUNCTION__, freq, ftype); - - if ((freq < 950000) || (freq > 2150000)) return -EINVAL; - - if (ftype == PHILIPS_SU1278_TSA_TT) { - divisor = 500; - regcode = 2; - } else { - divisor = 125; - regcode = 4; - } - - // setup frequency divisor - div = (freq + (divisor - 1)) / divisor; // round correctly - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = 0x80 | ((div & 0x18000) >> 10) | regcode; - buf[3] = 0; - - // tuner-specific settings - switch(ftype) { - case PHILIPS_SU1278_TSA: - case PHILIPS_SU1278_TSA_TT: - case PHILIPS_SU1278_TSA_TY: - case PHILIPS_SU1278_TSA_CI: - if (ftype == PHILIPS_SU1278_TSA_TY || ftype == PHILIPS_SU1278_TSA_CI) - addr = 0x61; - else - addr = 0x60; - - buf[3] |= 0x20; - - if (srate < 4000000) buf[3] |= 1; - - if (freq < 1250000) buf[3] |= 0; - else if (freq < 1550000) buf[3] |= 0x40; - else if (freq < 2050000) buf[3] |= 0x80; - else if (freq < 2150000) buf[3] |= 0xC0; - break; - - case ALPS_BSRU6: - addr = 0x61; - buf[3] = 0xC4; - if (freq > 1530000) buf[3] = 0xc0; - break; - - default: - return -EINVAL; - } - - return pll_write (i2c, addr, buf, sizeof(buf)); -} - - -#define MIN2(a,b) ((a) < (b) ? (a) : (b)) -#define MIN3(a,b,c) MIN2(MIN2(a,b),c) - -static int tua6100_set_tv_freq (struct i2c_adapter *i2c, u32 freq, - int ftype, int srate) -{ - u8 reg0 [2] = { 0x00, 0x00 }; - u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 }; - u8 reg2 [3] = { 0x02, 0x00, 0x00 }; - int _fband; - int first_ZF; - int R, A, N, P, M; - int err; - - first_ZF = (freq) / 1000; - - if (abs(MIN2(abs(first_ZF-1190),abs(first_ZF-1790))) < - abs(MIN3(abs(first_ZF-1202),abs(first_ZF-1542),abs(first_ZF-1890)))) - _fband = 2; - else - _fband = 3; - - if (_fband == 2) { - if (((first_ZF >= 950) && (first_ZF < 1350)) || - ((first_ZF >= 1430) && (first_ZF < 1950))) - reg0[1] = 0x07; - else if (((first_ZF >= 1350) && (first_ZF < 1430)) || - ((first_ZF >= 1950) && (first_ZF < 2150))) - reg0[1] = 0x0B; - } - - if(_fband == 3) { - if (((first_ZF >= 950) && (first_ZF < 1350)) || - ((first_ZF >= 1455) && (first_ZF < 1950))) - reg0[1] = 0x07; - else if (((first_ZF >= 1350) && (first_ZF < 1420)) || - ((first_ZF >= 1950) && (first_ZF < 2150))) - reg0[1] = 0x0B; - else if ((first_ZF >= 1420) && (first_ZF < 1455)) - reg0[1] = 0x0F; -} - - if (first_ZF > 1525) - reg1[1] |= 0x80; - else - reg1[1] &= 0x7F; - - if (_fband == 2) { - if (first_ZF > 1430) { /* 1430MHZ */ - reg1[1] &= 0xCF; /* N2 */ - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } else { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } -} - - if (_fband == 3) { - if ((first_ZF >= 1455) && - (first_ZF < 1630)) { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - } else { - if (first_ZF < 1455) { - reg1[1] &= 0xCF; /* N2 */ - reg1[1] |= 0x20; - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } else { - if (first_ZF >= 1630) { - reg1[1] &= 0xCF; /* N2 */ - reg2[1] &= 0xCF; /* R2 */ - reg2[1] |= 0x10; - } - } - } - } - - /* set ports, enable P0 for symbol rates > 4Ms/s */ - if (srate >= 4000000) - reg1[1] |= 0x0c; - else - reg1[1] |= 0x04; - - reg2[1] |= 0x0c; - - R = 64; - A = 64; - P = 64; //32 - - M = (freq * R) / 4; /* in Mhz */ - N = (M - A * 1000) / (P * 1000); - - reg1[1] |= (N >> 9) & 0x03; - reg1[2] = (N >> 1) & 0xff; - reg1[3] = (N << 7) & 0x80; - - reg2[1] |= (R >> 8) & 0x03; - reg2[2] = R & 0xFF; /* R */ - - reg1[3] |= A & 0x7f; /* A */ - - if (P == 64) - reg1[1] |= 0x40; /* Prescaler 64/65 */ - - reg0[1] |= 0x03; - - if ((err = pll_write(i2c, 0x60, reg0, sizeof(reg0)))) - return err; - - if ((err = pll_write(i2c, 0x60, reg1, sizeof(reg1)))) - return err; - - if ((err = pll_write(i2c, 0x60, reg2, sizeof(reg2)))) - return err; - - return 0; -} - - -static int pll_set_tv_freq (struct i2c_adapter *i2c, u32 freq, int ftype, int srate) -{ - switch(ftype) { - case SAMSUNG_TBMU24112IMB: - return sl1935_set_tv_freq(i2c, freq, ftype); - - case LG_TDQF_S001F: - return sl1935_set_tv_freq(i2c, freq, ftype); - - case PHILIPS_SU1278_TUA: - return tua6100_set_tv_freq(i2c, freq, ftype, srate); - - default: - return tsa5059_set_tv_freq(i2c, freq, ftype, srate); -} -} - -#if 0 -static int tsa5059_read_status (struct i2c_adapter *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_transfer (i2c, msg, 2); - - if (ret != 2) - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); - - return stat[0]; -} -#endif - - -static int stv0299_init (struct i2c_adapter *i2c, int ftype) -{ - int i; - - dprintk("stv0299: init chip\n"); - - switch(ftype) { - case SAMSUNG_TBMU24112IMB: - dprintk("%s: init stv0299 chip for Samsung TBMU24112IMB\n", __FUNCTION__); - - for (i=0; i 4) @@ -710,13 +191,13 @@ static fe_code_rate_t stv0299_get_fec (s } -static int stv0299_wait_diseqc_fifo (struct i2c_adapter *i2c, int timeout) +static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout) { unsigned long start = jiffies; dprintk ("%s\n", __FUNCTION__); - while (stv0299_readreg(i2c, 0x0a) & 1) { + while (stv0299_readreg(state, 0x0a) & 1) { if (jiffies - start > timeout) { dprintk ("%s: timeout!!\n", __FUNCTION__); return -ETIMEDOUT; @@ -728,13 +209,13 @@ static int stv0299_wait_diseqc_fifo (str } -static int stv0299_wait_diseqc_idle (struct i2c_adapter *i2c, int timeout) +static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout) { unsigned long start = jiffies; dprintk ("%s\n", __FUNCTION__); - while ((stv0299_readreg(i2c, 0x0a) & 3) != 2 ) { + while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) { if (jiffies - start > timeout) { dprintk ("%s: timeout!!\n", __FUNCTION__); return -ETIMEDOUT; @@ -745,101 +226,156 @@ static int stv0299_wait_diseqc_idle (str return 0; } +static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + u64 big = srate; + u32 ratio; + + // check rate is within limits + if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; + + // calculate value to program + big = big << 20; + big += (state->config->mclk-1); // round correctly + do_div(big, state->config->mclk); + ratio = big << 4; + + return state->config->set_symbol_rate(fe, srate, ratio); +} + + +static int stv0299_get_symbolrate (struct stv0299_state* state) +{ + u32 Mclk = state->config->mclk / 4096L; + u32 srate; + s32 offset; + u8 sfr[3]; + s8 rtf; + + dprintk ("%s\n", __FUNCTION__); + + stv0299_readregs (state, 0x1f, sfr, 3); + stv0299_readregs (state, 0x1a, &rtf, 1); + + srate = (sfr[0] << 8) | sfr[1]; + srate *= Mclk; + srate /= 16; + srate += (sfr[2] >> 4) * Mclk / 256; + offset = (s32) rtf * (srate / 4096L); + offset /= 128; + + dprintk ("%s : srate = %i\n", __FUNCTION__, srate); + dprintk ("%s : ofset = %i\n", __FUNCTION__, offset); + + srate += offset; + + srate += 1000; + srate /= 2000; + srate *= 2000; + + return srate; +} + + + + + + + + + + -static int stv0299_send_diseqc_msg (struct i2c_adapter *i2c, + + + +static int stv0299_send_diseqc_msg (struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *m) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 val; int i; dprintk ("%s\n", __FUNCTION__); - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - val = stv0299_readreg (i2c, 0x08); + val = stv0299_readreg (state, 0x08); - if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ + if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ return -EREMOTEIO; for (i=0; imsg_len; i++) { - if (stv0299_wait_diseqc_fifo (i2c, 100) < 0) + if (stv0299_wait_diseqc_fifo (state, 100) < 0) return -ETIMEDOUT; - if (stv0299_writereg (i2c, 0x09, m->msg[i])) + if (stv0299_writeregI (state, 0x09, m->msg[i])) return -EREMOTEIO; } - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; return 0; } -static int stv0299_send_diseqc_burst (struct i2c_adapter *i2c, fe_sec_mini_cmd_t burst) +static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 val; dprintk ("%s\n", __FUNCTION__); - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - val = stv0299_readreg (i2c, 0x08); + val = stv0299_readreg (state, 0x08); - if (stv0299_writereg (i2c, 0x08, (val & ~0x7) | 0x2)) /* burst mode */ + if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2)) /* burst mode */ return -EREMOTEIO; - if (stv0299_writereg (i2c, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff)) + if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff)) return -EREMOTEIO; - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - if (stv0299_writereg (i2c, 0x08, val)) + if (stv0299_writeregI (state, 0x08, val)) return -EREMOTEIO; return 0; } -static int stv0299_set_tone (struct i2c_adapter *i2c, fe_sec_tone_mode_t tone) +static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 val; - dprintk("%s: %s\n", __FUNCTION__, - tone == SEC_TONE_ON ? "SEC_TONE_ON" : - tone == SEC_TONE_OFF ? "SEC_TONE_OFF" : "??"); - - if (stv0299_wait_diseqc_idle (i2c, 100) < 0) + if (stv0299_wait_diseqc_idle (state, 100) < 0) return -ETIMEDOUT; - val = stv0299_readreg (i2c, 0x08); + val = stv0299_readreg (state, 0x08); switch (tone) { case SEC_TONE_ON: - { - dprintk("%s: TONE_ON\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x08, val | 0x3); - } + return stv0299_writeregI (state, 0x08, val | 0x3); + case SEC_TONE_OFF: - { - dprintk("%s: TONE_OFF\n", __FUNCTION__); - return stv0299_writereg (i2c, 0x08, (val & ~0x3) | 0x02); - } + return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02); + default: - { - dprintk("%s: TONE INVALID\n", __FUNCTION__); return -EINVAL; } - }; } -static int stv0299_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage, - int tuner_type) +static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) { + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; u8 reg0x08; u8 reg0x0c; @@ -847,8 +383,8 @@ static int stv0299_set_voltage (struct i voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); - reg0x08 = stv0299_readreg (i2c, 0x08); - reg0x0c = stv0299_readreg (i2c, 0x0c); + reg0x08 = stv0299_readreg (state, 0x08); + reg0x0c = stv0299_readreg (state, 0x0c); /** * H/V switching over OP0, OP1 and OP2 are LNB power enable bits @@ -856,37 +392,28 @@ static int stv0299_set_voltage (struct i reg0x0c &= 0x0f; if (voltage == SEC_VOLTAGE_OFF) { - stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */ - return stv0299_writereg (i2c, 0x08, 0x00); /* LNB power off! */ + stv0299_writeregI (state, 0x0c, 0x00); /* LNB power off! */ + return stv0299_writeregI (state, 0x08, 0x00); /* LNB power off! */ } - if (tuner_type == PHILIPS_SU1278_TSA_CI) - { - stv0299_writereg (i2c, 0x08, reg0x08 & 0xBF); // switch LNB power on OP2/LOCK pin off - } - else - { - stv0299_writereg (i2c, 0x08, reg0x08 | 0x40); - } + stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6)); switch (voltage) { case SEC_VOLTAGE_13: - if (tuner_type == PHILIPS_SU1278_TSA_TY || tuner_type == PHILIPS_SU1278_TSA_CI) - return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x10); - else - return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x40); + if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) reg0x0c |= 0x10; + else reg0x0c |= 0x40; - case SEC_VOLTAGE_18: - return stv0299_writereg (i2c, 0x0c, reg0x0c | 0x50); + return stv0299_writeregI(state, 0x0c, reg0x0c); + case SEC_VOLTAGE_18: + return stv0299_writeregI(state, 0x0c, reg0x0c | 0x50); default: return -EINVAL; }; } -static int stv0299_send_legacy_dish_cmd(struct i2c_adapter *i2c, u32 cmd, - int tuner_type) +static int stv0299_send_legacy_dish_cmd(struct dvb_frontend* fe, u32 cmd) { u8 last = 1; int i; @@ -901,15 +428,14 @@ static int stv0299_send_legacy_dish_cmd( cmd = cmd << 1; dprintk("%s switch command: 0x%04x\n",__FUNCTION__, cmd); - stv0299_set_voltage(i2c,SEC_VOLTAGE_18,tuner_type); + stv0299_set_voltage(fe,SEC_VOLTAGE_18); msleep(32); for (i=0; i<9; i++) { if((cmd & 0x01) != last) { - stv0299_set_voltage(i2c, + stv0299_set_voltage(fe, last ? SEC_VOLTAGE_13 : - SEC_VOLTAGE_18, - tuner_type); + SEC_VOLTAGE_18); last = (last) ? 0 : 1; } @@ -922,160 +448,35 @@ static int stv0299_send_legacy_dish_cmd( return 0; } -static int stv0299_set_symbolrate (struct i2c_adapter *i2c, u32 srate, int tuner_type) + +static int stv0299_init (struct dvb_frontend* fe) { - u64 big = srate; - u32 ratio; - u8 aclk = 0; - u8 bclk = 0; - u8 m1; - int Mclk = M_CLK; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + int i; - // check rate is within limits - if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; + dprintk("stv0299: init chip\n"); - // calculate value to program - if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT; - big = big << 20; - big += (Mclk-1); // round correctly - do_div(big, Mclk); - ratio = big << 4; - - // program registers - switch(tuner_type) { - case PHILIPS_SU1278_TSA_TT: - stv0299_writereg (i2c, 0x0e, 0x44); - if (srate >= 10000000) { - stv0299_writereg (i2c, 0x13, 0x97); - stv0299_writereg (i2c, 0x14, 0x95); - stv0299_writereg (i2c, 0x15, 0xc9); - stv0299_writereg (i2c, 0x17, 0x8c); - stv0299_writereg (i2c, 0x1a, 0xfe); - stv0299_writereg (i2c, 0x1c, 0x7f); - stv0299_writereg (i2c, 0x2d, 0x09); - } else { - stv0299_writereg (i2c, 0x13, 0x99); - stv0299_writereg (i2c, 0x14, 0x8d); - stv0299_writereg (i2c, 0x15, 0xce); - stv0299_writereg (i2c, 0x17, 0x43); - stv0299_writereg (i2c, 0x1a, 0x1d); - stv0299_writereg (i2c, 0x1c, 0x12); - stv0299_writereg (i2c, 0x2d, 0x05); - } - stv0299_writereg (i2c, 0x0e, 0x23); - stv0299_writereg (i2c, 0x0f, 0x94); - stv0299_writereg (i2c, 0x10, 0x39); - stv0299_writereg (i2c, 0x15, 0xc9); - - stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); - break; - - case PHILIPS_SU1278_TSA_TY: - case PHILIPS_SU1278_TSA_CI: - case PHILIPS_SU1278_TSA: - aclk = 0xb5; - if (srate < 2000000) bclk = 0x86; - else if (srate < 5000000) bclk = 0x89; - else if (srate < 15000000) bclk = 0x8f; - else if (srate < 45000000) bclk = 0x95; - - m1 = 0x14; - if (srate < 4000000) m1 = 0x10; - - stv0299_writereg (i2c, 0x13, aclk); - stv0299_writereg (i2c, 0x14, bclk); - stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); - stv0299_writereg (i2c, 0x0f, (stv0299_readreg(i2c, 0x0f) & 0xc0) | m1); - break; + for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) + stv0299_writeregI(state, state->config->inittab[i], state->config->inittab[i+1]); - case ALPS_BSRU6: - default: - if (srate < 1500000) { aclk = 0xb7; bclk = 0x47; } - else if (srate < 3000000) { aclk = 0xb7; bclk = 0x4b; } - else if (srate < 7000000) { aclk = 0xb7; bclk = 0x4f; } - else if (srate < 14000000) { aclk = 0xb7; bclk = 0x53; } - else if (srate < 30000000) { aclk = 0xb6; bclk = 0x53; } - else if (srate < 45000000) { aclk = 0xb4; bclk = 0x51; } - - stv0299_writereg (i2c, 0x13, aclk); - stv0299_writereg (i2c, 0x14, bclk); - stv0299_writereg (i2c, 0x1f, (ratio >> 16) & 0xff); - stv0299_writereg (i2c, 0x20, (ratio >> 8) & 0xff); - stv0299_writereg (i2c, 0x21, (ratio ) & 0xf0); - break; + if (state->config->pll_init) { + stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ + state->config->pll_init(fe); + stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ } - return 0; } -static int stv0299_get_symbolrate (struct i2c_adapter *i2c, int tuner_type) -{ - u32 Mclk = M_CLK / 4096L; - u32 srate; - s32 offset; - u8 sfr[3]; - s8 rtf; - - dprintk ("%s\n", __FUNCTION__); - - if (tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT / 4096L; - - stv0299_readregs (i2c, 0x1f, sfr, 3); - stv0299_readregs (i2c, 0x1a, &rtf, 1); - - srate = (sfr[0] << 8) | sfr[1]; - srate *= Mclk; - srate /= 16; - srate += (sfr[2] >> 4) * Mclk / 256; - - offset = (s32) rtf * (srate / 4096L); - offset /= 128; - - dprintk ("%s : srate = %i\n", __FUNCTION__, srate); - dprintk ("%s : ofset = %i\n", __FUNCTION__, offset); - - srate += offset; - - srate += 1000; - srate /= 2000; - srate *= 2000; - - return srate; -} - -static int uni0299_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct stv0299_state *state = (struct stv0299_state *) fe->data; - struct i2c_adapter *i2c = state->i2c; - - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_GET_INFO: +static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct dvb_frontend_info* tmp = (struct dvb_frontend_info*) arg; - memcpy (arg, &uni0299_info, sizeof(struct dvb_frontend_info)); - - if (state->tuner_type == PHILIPS_SU1278_TSA_TT) { - tmp->frequency_tolerance = M_CLK_SU1278_TSA_TT / 2000; - } - break; - } + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; - u8 signal = 0xff - stv0299_readreg (i2c, 0x18); - u8 sync = stv0299_readreg (i2c, 0x1b); + u8 signal = 0xff - stv0299_readreg (state, 0x18); + u8 sync = stv0299_readreg (state, 0x1b); dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __FUNCTION__, sync); - *status = 0; if (signal > 10) @@ -1093,53 +494,61 @@ static int uni0299_ioctl (struct dvb_fro if ((sync & 0x98) == 0x98) *status |= FE_HAS_LOCK; - break; + return 0; } - case FE_READ_BER: - if (stv0299_status == STATUS_BER) { - *((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8) - | stv0299_readreg (i2c, 0x1e); - } else { - *((u32*) arg) = 0; +static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + if (state->errmode != STATUS_BER) return 0; + *ber = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + + return 0; } - break; - case FE_READ_SIGNAL_STRENGTH: +static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - s32 signal = 0xffff - ((stv0299_readreg (i2c, 0x18) << 8) - | stv0299_readreg (i2c, 0x19)); + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + s32 signal = 0xffff - ((stv0299_readreg (state, 0x18) << 8) + | stv0299_readreg (state, 0x19)); dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __FUNCTION__, - stv0299_readreg (i2c, 0x18), - stv0299_readreg (i2c, 0x19), (int) signal); + stv0299_readreg (state, 0x18), + stv0299_readreg (state, 0x19), (int) signal); signal = signal * 5 / 4; - *((u16*) arg) = (signal > 0xffff) ? 0xffff : - (signal < 0) ? 0 : signal; - break; - } - case FE_READ_SNR: - { - s32 snr = 0xffff - ((stv0299_readreg (i2c, 0x24) << 8) - | stv0299_readreg (i2c, 0x25)); - snr = 3 * (snr - 0xa100); - *((u16*) arg) = (snr > 0xffff) ? 0xffff : - (snr < 0) ? 0 : snr; - break; - } - case FE_READ_UNCORRECTED_BLOCKS: - if (stv0299_status == STATUS_UCBLOCKS) { - *((u32*) arg) = (stv0299_readreg (i2c, 0x1d) << 8) - | stv0299_readreg (i2c, 0x1e); - } else { - *((u32*) arg) = 0; + *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; + + return 0; } - break; - case FE_SET_FRONTEND: +static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr) { - struct dvb_frontend_parameters *p = arg; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8) + | stv0299_readreg (state, 0x25)); + xsnr = 3 * (xsnr - 0xa100); + *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; + + return 0; +} + +static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + + if (state->errmode != STATUS_UCBLOCKS) *ucblocks = 0; + else *ucblocks = (stv0299_readreg (state, 0x1d) << 8) | stv0299_readreg (state, 0x1e); + + return 0; +} + +static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; int invval = 0; dprintk ("%s : FE_SET_FRONTEND\n", __FUNCTION__); @@ -1151,12 +560,10 @@ static int uni0299_ioctl (struct dvb_fro printk("stv0299 does not support auto-inversion\n"); return -EINVAL; } - if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1; - stv0299_writereg(i2c, 0x0c, (stv0299_readreg(i2c, 0x0c) & 0xfe) | invval); + if (state->config->invert) invval = (~invval) & 1; + stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval); - switch(state->tuner_type) { - case PHILIPS_SU1278_TSA_TT: - { + if (state->config->enhanced_tuning) { /* check if we should do a finetune */ int frequency_delta = p->frequency - state->tuner_frequency; int minmax = p->u.qpsk.symbol_rate / 2000; @@ -1165,130 +572,88 @@ static int uni0299_ioctl (struct dvb_fro if ((frequency_delta > -minmax) && (frequency_delta < minmax) && (frequency_delta != 0) && (state->fec_inner == p->u.qpsk.fec_inner) && (state->symbol_rate == p->u.qpsk.symbol_rate)) { - int Drot_freq = (frequency_delta << 16) / (M_CLK_SU1278_TSA_TT / 1000); + int Drot_freq = (frequency_delta << 16) / (state->config->mclk / 1000); // zap the derotator registers first - stv0299_writereg (i2c, 0x22, 0x00); - stv0299_writereg (i2c, 0x23, 0x00); + stv0299_writeregI(state, 0x22, 0x00); + stv0299_writeregI(state, 0x23, 0x00); // now set them as we want - stv0299_writereg (i2c, 0x22, Drot_freq >> 8); - stv0299_writereg (i2c, 0x23, Drot_freq); + stv0299_writeregI(state, 0x22, Drot_freq >> 8); + stv0299_writeregI(state, 0x23, Drot_freq); } else { /* A "normal" tune is requested */ - pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate); - stv0299_writereg (i2c, 0x32, 0x80); - stv0299_writereg (i2c, 0x22, 0x00); - stv0299_writereg (i2c, 0x23, 0x00); - stv0299_writereg (i2c, 0x32, 0x19); - stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type); - stv0299_set_FEC (i2c, p->u.qpsk.fec_inner); - } - break; + stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ + state->config->pll_set(fe, p); + stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ + + stv0299_writeregI(state, 0x32, 0x80); + stv0299_writeregI(state, 0x22, 0x00); + stv0299_writeregI(state, 0x23, 0x00); + stv0299_writeregI(state, 0x32, 0x19); + stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate); + stv0299_set_FEC (state, p->u.qpsk.fec_inner); } - - default: - pll_set_tv_freq (i2c, p->frequency, state->tuner_type, p->u.qpsk.symbol_rate); - stv0299_set_FEC (i2c, p->u.qpsk.fec_inner); - stv0299_set_symbolrate (i2c, p->u.qpsk.symbol_rate, state->tuner_type); - stv0299_writereg (i2c, 0x22, 0x00); - stv0299_writereg (i2c, 0x23, 0x00); - stv0299_readreg (i2c, 0x23); - stv0299_writereg (i2c, 0x12, 0xb9); - break; + } else { + stv0299_writeregI(state, 0x05, 0xb5); /* enable i2c repeater on stv0299 */ + state->config->pll_set(fe, p); + stv0299_writeregI(state, 0x05, 0x35); /* disable i2c repeater on stv0299 */ + + stv0299_set_FEC (state, p->u.qpsk.fec_inner); + stv0299_set_symbolrate (fe, p->u.qpsk.symbol_rate); + stv0299_writeregI(state, 0x22, 0x00); + stv0299_writeregI(state, 0x23, 0x00); + stv0299_readreg (state, 0x23); + stv0299_writeregI(state, 0x12, 0xb9); } state->tuner_frequency = p->frequency; state->fec_inner = p->u.qpsk.fec_inner; state->symbol_rate = p->u.qpsk.symbol_rate; - break; + + return 0; } - case FE_GET_FRONTEND: +static int stv0299_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters * p) { - struct dvb_frontend_parameters *p = arg; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; s32 derot_freq; - int Mclk = M_CLK; int invval; - if (state->tuner_type == PHILIPS_SU1278_TSA_TT) Mclk = M_CLK_SU1278_TSA_TT; + derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8) + | stv0299_readreg (state, 0x23)); - derot_freq = (s32)(s16) ((stv0299_readreg (i2c, 0x22) << 8) - | stv0299_readreg (i2c, 0x23)); - - derot_freq *= (Mclk >> 16); + derot_freq *= (state->config->mclk >> 16); derot_freq += 500; derot_freq /= 1000; p->frequency += derot_freq; - invval = stv0299_readreg (i2c, 0x0c) & 1; - if (state->tuner_type == ALPS_BSRU6) invval = (~invval) & 1; + invval = stv0299_readreg (state, 0x0c) & 1; + if (state->config->invert) invval = (~invval) & 1; p->inversion = invval ? INVERSION_ON : INVERSION_OFF; - p->u.qpsk.fec_inner = stv0299_get_fec (i2c); - p->u.qpsk.symbol_rate = stv0299_get_symbolrate (i2c, state->tuner_type); - break; - } - - case FE_SLEEP: - stv0299_writereg (i2c, 0x0c, 0x00); /* LNB power off! */ - stv0299_writereg (i2c, 0x08, 0x00); /* LNB power off! */ - stv0299_writereg (i2c, 0x02, 0x80); - state->initialised = 0; - break; - - case FE_INIT: - switch(state->tuner_type) { - case PHILIPS_SU1278_TSA_TT: - state->tuner_frequency = 0; - if (!state->initialised) { - state->initialised = 1; - return stv0299_init (i2c, state->tuner_type); - } - break; + p->u.qpsk.fec_inner = stv0299_get_fec (state); + p->u.qpsk.symbol_rate = stv0299_get_symbolrate (state); - default: - return stv0299_init (i2c, state->tuner_type); + return 0; } - break; - - case FE_DISEQC_SEND_MASTER_CMD: - return stv0299_send_diseqc_msg (i2c, arg); - case FE_DISEQC_SEND_BURST: - return stv0299_send_diseqc_burst (i2c, (fe_sec_mini_cmd_t) arg); - - case FE_SET_TONE: - return stv0299_set_tone (i2c, (fe_sec_tone_mode_t) arg); +static int stv0299_sleep(struct dvb_frontend* fe) +{ + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; - case FE_SET_VOLTAGE: - return stv0299_set_voltage (i2c, (fe_sec_voltage_t) arg, - state->tuner_type); + stv0299_writeregI(state, 0x02, 0x80); + state->initialised = 0; - case FE_DISHNETWORK_SEND_LEGACY_CMD: - return stv0299_send_legacy_dish_cmd (i2c, - (u32)(unsigned long)arg, - state->tuner_type); + return 0; +} - case FE_GET_TUNE_SETTINGS: +static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; - - switch(state->tuner_type) { - case PHILIPS_SU1278_TSA_TT: - fesettings->min_delay_ms = 50; - if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) { - fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000; - fesettings->max_drift = 5000; - } else { - fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000; - fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000; - } - break; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; - default: - fesettings->min_delay_ms = 100; + fesettings->min_delay_ms = state->config->min_delay_ms; if (fesettings->parameters.u.qpsk.symbol_rate < 10000000) { fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 32000; fesettings->max_drift = 5000; @@ -1296,225 +661,103 @@ static int uni0299_ioctl (struct dvb_fro fesettings->step_size = fesettings->parameters.u.qpsk.symbol_rate / 16000; fesettings->max_drift = fesettings->parameters.u.qpsk.symbol_rate / 2000; } - break; - } - - return 0; - } - - default: - return -EOPNOTSUPP; - }; - return 0; } -static long probe_tuner (struct i2c_adapter *adapter) -{ - struct i2c_adapter *i2c = adapter; /* superfluous */ - - /* read the status register of TSA5059 */ - u8 rpt[] = { 0x05, 0xb5 }; - u8 stat [] = { 0 }; - u8 tda6100_buf [] = { 0, 0 }; - int ret; - 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 }}; - struct i2c_msg msg3 [] = {{ .addr = 0x68, .flags = 0, .buf = rpt, .len = 2 }, - { .addr = 0x60, .flags = 0, .buf = tda6100_buf, .len = 2 }}; - - stv0299_writereg (i2c, 0x01, 0x15); - stv0299_writereg (i2c, 0x02, 0x30); - stv0299_writereg (i2c, 0x03, 0x00); - - - printk("stv0299: try to attach to %s\n", adapter->name); - - if (!strcmp(adapter->name, "SkyStar2")) { - printk ("stv0299: setup for tuner Samsung TBMU24112IMB\n"); - return SAMSUNG_TBMU24112IMB; - } - - if ((ret = i2c_transfer(i2c, msg1, 2)) == 2) { - if ( strcmp(adapter->name, "TT-Budget/WinTV-NOVA-CI PCI") == 0 ) { - // technotrend cards require non-datasheet settings - printk ("stv0299: setup for tuner SU1278 (TSA5059 synth) on TechnoTrend hardware\n"); - return PHILIPS_SU1278_TSA_TT; - } else { - // fall back to datasheet-recommended settings - printk ("stv0299: setup for tuner SU1278 (TSA5059 synth)\n"); - return PHILIPS_SU1278_TSA; - } - } - - if ((ret = i2c_transfer(i2c, msg2, 2)) == 2) { - if ( strcmp(adapter->name, "KNC1 DVB-S") == 0 ) +static void stv0299_release(struct dvb_frontend* fe) { - // Typhoon cards have unusual wiring. - printk ("stv0299: setup for tuner SU1278 (TSA5059 synth) on Typhoon hardware\n"); - return PHILIPS_SU1278_TSA_TY; - } - else if ( strcmp(adapter->name, "TerraTec Cinergy 1200 DVB-S") == 0 ) - { - // Cinergy cards have unusual wiring. - printk ("%s: setup for tuner SU1278 (TSA5059 synth) on" - " TerraTec hardware\n", __FILE__); - return PHILIPS_SU1278_TSA_CI; - } - //else if ((stat[0] & 0x3f) == 0) { - else if (0) { - printk ("stv0299: setup for tuner TDQF-S001F\n"); - return LG_TDQF_S001F; - } else { - printk ("stv0299: setup for tuner BSRU6, TDQB-S00x\n"); - return ALPS_BSRU6; - } - } - - /** - * setup i2c timing for SU1278... - */ - stv0299_writereg (i2c, 0x02, 0x00); - - if ((ret = i2c_transfer(i2c, msg3, 2)) == 2) { - printk ("stv0299: setup for tuner Philips SU1278 (TUA6100 synth)\n"); - return PHILIPS_SU1278_TUA; - } - - printk ("stv0299: unknown PLL synthesizer (ret == %i), please report to !!\n", ret); - - return UNKNOWN_FRONTEND; + struct stv0299_state* state = (struct stv0299_state*) fe->demodulator_priv; + kfree(state); } -static struct i2c_client client_template; +static struct dvb_frontend_ops stv0299_ops; -static int attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, + struct i2c_adapter* i2c) { - struct i2c_client *client; - struct stv0299_state* state; - int tuner_type; - int ret; - u8 id; + struct stv0299_state* state = NULL; + int id; - stv0299_writereg(adapter, 0x02, 0x34); /* standby off */ - msleep(200); - id = stv0299_readreg(adapter, 0x00); + /* allocate memory for the internal state */ + state = (struct stv0299_state*) kmalloc(sizeof(struct stv0299_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &stv0299_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + state->tuner_frequency = 0; + state->symbol_rate = 0; + state->fec_inner = 0; + state->errmode = STATUS_BER; - dprintk ("%s: id == 0x%02x\n", __FUNCTION__, id); + /* check if the demod is there */ + stv0299_writeregI(state, 0x02, 0x34); /* standby off */ + msleep(200); + id = stv0299_readreg(state, 0x00); /* register 0x00 contains 0xa1 for STV0299 and STV0299B */ /* register 0x00 might contain 0x80 when returning from standby */ - if (id != 0xa1 && id != 0x80) - return -ENODEV; - - if ((tuner_type = probe_tuner(adapter)) < 0) - return -ENODEV; - - if ((state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; - } - - state->tuner_type = tuner_type; - state->tuner_frequency = 0; - state->initialised = 0; - state->i2c = adapter; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = (0x68>>1); - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return -EFAULT; - } - - BUG_ON(!state->dvb); - - ret = dvb_register_frontend(uni0299_ioctl, state->dvb, state, - &uni0299_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return -EFAULT; -} + if (id != 0xa1 && id != 0x80) goto error; - return 0; -} - -static int detach_client(struct i2c_client *client) -{ - struct stv0299_state *state = (struct stv0299_state*)i2c_get_clientdata(client); - - dvb_unregister_frontend (uni0299_ioctl, state->dvb); - i2c_detach_client(client); - kfree(client); - kfree(state); - return 0; -} - -static int command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct stv0299_state *data = (struct stv0299_state*)i2c_get_clientdata(client); - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: { - data->dvb = (struct dvb_adapter*)arg; - break; - } - case FE_UNREGISTER: { - data->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; - } - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_STV0299, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; - -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops stv0299_ops = { + + .info = { + .name = "ST STV0299 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .frequency_tolerance = 0, + .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_QPSK | + FE_CAN_FEC_AUTO + }, + + .release = stv0299_release, + + .init = stv0299_init, + .sleep = stv0299_sleep, + + .set_frontend = stv0299_set_frontend, + .get_frontend = stv0299_get_frontend, + .get_tune_settings = stv0299_get_tune_settings, + + .read_status = stv0299_read_status, + .read_ber = stv0299_read_ber, + .read_signal_strength = stv0299_read_signal_strength, + .read_snr = stv0299_read_snr, + .read_ucblocks = stv0299_read_ucblocks, + + .diseqc_send_master_cmd = stv0299_send_diseqc_msg, + .diseqc_send_burst = stv0299_send_diseqc_burst, + .set_tone = stv0299_set_tone, + .set_voltage = stv0299_set_voltage, + .dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd, }; -static int __init init_uni0299 (void) -{ - return i2c_add_driver(&driver); -} - -static void __exit exit_uni0299 (void) -{ - if (i2c_del_driver(&driver)) - printk("stv0299: driver deregistration failed\n"); -} - -module_init (init_uni0299); -module_exit (exit_uni0299); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("Universal STV0299/TSA5059/SL1935 DVB Frontend driver"); +MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, " "Andreas Oberritter, Andrew de Quincey, Kenneth Aafløy"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(stv0299_writereg); +EXPORT_SYMBOL(stv0299_attach); diff -puN /dev/null drivers/media/dvb/frontends/stv0299.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/stv0299.h 2004-11-21 17:13:23.484048264 -0800 @@ -0,0 +1,104 @@ +/* + Driver for ST STV0299 demodulator + + Copyright (C) 2001-2002 Convergence Integrated Media GmbH + , + , + + + + Philips SU1278/SH + + Copyright (C) 2002 by Peter Schildmann + + + LG TDQF-S001F + + Copyright (C) 2002 Felix Domke + & Andreas Oberritter + + + Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B + + Copyright (C) 2003 Vadim Catana : + + Support for Philips SU1278 on Technotrend hardware + + Copyright (C) 2004 Andrew de Quincey + + 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. + +*/ + +#ifndef STV0299_H +#define STV0299_H + +#include +#include "dvb_frontend.h" + +#define STV0229_LOCKOUTPUT_0 0 +#define STV0229_LOCKOUTPUT_1 1 +#define STV0229_LOCKOUTPUT_CF 2 +#define STV0229_LOCKOUTPUT_LK 3 + +#define STV0299_VOLT13_OP0 0 +#define STV0299_VOLT13_OP1 1 + +struct stv0299_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* inittab - array of pairs of values. + * First of each pair is the register, second is the value. + * List should be terminated with an 0xff, 0xff pair. + */ + u8* inittab; + + /* master clock to use */ + u32 mclk; + + /* does the inversion require inversion? */ + u8 invert:1; + + /* Should the enhanced tuning code be used? */ + u8 enhanced_tuning:1; + + /* Skip reinitialisation? */ + u8 skip_reinit:1; + + /* LOCK OUTPUT setting */ + u8 lock_output:2; + + /* Is 13v controlled by OP0 or OP1? */ + u8 volt13_op0_op1:1; + + /* minimum delay before retuning */ + int min_delay_ms; + + /* Set the symbol rate */ + int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio); + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data); + +extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, + struct i2c_adapter* i2c); + +#endif // STV0299_H diff -puN /dev/null drivers/media/dvb/frontends/tda10021.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/tda10021.c 2004-11-21 17:13:23.486047960 -0800 @@ -0,0 +1,483 @@ +/* + TDA10021 - Single Chip Cable Channel Receiver driver module + used on the the Siemens DVB-C cards + + Copyright (C) 1999 Convergence Integrated Media GmbH + Copyright (C) 2004 Markus Schulz + Suppport for TDA10021 + + 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 +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "tda10021.h" + + +struct tda10021_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct tda10021_config* config; + + struct dvb_frontend frontend; + + u8 pwm; + u8 reg0; +}; + + +#if 0 +#define dprintk(x...) printk(x) +#else +#define dprintk(x...) +#endif + +static int verbose; + +#define XIN 57840000UL +#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) +#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) +#define HAS_INVERSION(reg0) (!(reg0 & 0x20)) + +#define FIN (XIN >> 4) + +int tda10021_inittab_size = 0x40; +static u8 tda10021_inittab[0x40]= +{ + 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, + 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, + 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, + 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, + 0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00, + 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00, +}; + +static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer (state->i2c, &msg, 1); + if (ret != 1) + printk("DVB: TDA10021(%d): %s, writereg error " + "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", + state->frontend.dvb->num, __FUNCTION__, reg, data, ret); + + msleep(10); + return (ret != 1) ? -EREMOTEIO : 0; +} + + +static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) +{ + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + int ret; + + ret = i2c_transfer (state->i2c, msg, 2); + if (ret != 2) + printk("DVB: TDA10021(%d): %s: readreg error (ret == %i)\n", + state->frontend.dvb->num, __FUNCTION__, ret); + return b1[0]; +} + +//get access to tuner +static int lock_tuner(struct tda10021_state* state) +{ + u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 }; + struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg, 1) != 1) + { + printk("tda10021: lock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +//release access from tuner +static int unlock_tuner(struct tda10021_state* state) +{ + u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f }; + struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg_post, 1) != 1) + { + printk("tda10021: unlock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, + fe_spectral_inversion_t inversion) +{ + reg0 |= state->reg0 & 0x63; + + if (INVERSION_ON == inversion) + ENABLE_INVERSION(reg0); + else if (INVERSION_OFF == inversion) + DISABLE_INVERSION(reg0); + + tda10021_writereg (state, 0x00, reg0 & 0xfe); + tda10021_writereg (state, 0x00, reg0 | 0x01); + + state->reg0 = reg0; + return 0; +} + +static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate) +{ + s32 BDR; + s32 BDRI; + s16 SFIL=0; + u16 NDEC = 0; + u32 tmp, ratio; + + if (symbolrate > XIN/2) + symbolrate = XIN/2; + if (symbolrate < 500000) + symbolrate = 500000; + + if (symbolrate < XIN/16) NDEC = 1; + if (symbolrate < XIN/32) NDEC = 2; + if (symbolrate < XIN/64) NDEC = 3; + + if (symbolrate < (u32)(XIN/12.3)) SFIL = 1; + if (symbolrate < (u32)(XIN/16)) SFIL = 0; + if (symbolrate < (u32)(XIN/24.6)) SFIL = 1; + if (symbolrate < (u32)(XIN/32)) SFIL = 0; + if (symbolrate < (u32)(XIN/49.2)) SFIL = 1; + if (symbolrate < (u32)(XIN/64)) SFIL = 0; + if (symbolrate < (u32)(XIN/98.4)) SFIL = 1; + + symbolrate <<= NDEC; + ratio = (symbolrate << 4) / FIN; + tmp = ((symbolrate << 4) % FIN) << 8; + ratio = (ratio << 8) + tmp / FIN; + tmp = (tmp % FIN) << 8; + ratio = (ratio << 8) + (tmp + FIN/2) / FIN; + + BDR = ratio; + BDRI = (((XIN << 5) / symbolrate) + 1) / 2; + + if (BDRI > 0xFF) + BDRI = 0xFF; + + SFIL = (SFIL << 4) | tda10021_inittab[0x0E]; + + NDEC = (NDEC << 6) | tda10021_inittab[0x03]; + + tda10021_writereg (state, 0x03, NDEC); + tda10021_writereg (state, 0x0a, BDR&0xff); + tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); + tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); + + tda10021_writereg (state, 0x0d, BDRI); + tda10021_writereg (state, 0x0e, SFIL); + + return 0; +} + + + + + + + + + + +static int tda10021_init (struct dvb_frontend *fe) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + int i; + + dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num); + + //tda10021_writereg (fe, 0, 0); + + for (i=0; ipwm); + + //Comment by markus + //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0) + //0x2A[4] == BYPPLL -> Power down mode (default 1) + //0x2A[5] == LCK -> PLL Lock Flag + //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0) + + //Activate PLL + tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); + + if (state->config->pll_init) { + lock_tuner(state); + state->config->pll_init(fe); + unlock_tuner(state); + } + + return 0; +} + +static int tda10021_set_parameters (struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + //table for QAM4-QAM256 ready QAM4 QAM16 QAM32 QAM64 QAM128 QAM256 + //CONF + static const u8 reg0x00 [] = { 0x14, 0x00, 0x04, 0x08, 0x0c, 0x10 }; + //AGCREF value + static const u8 reg0x01 [] = { 0x78, 0x8c, 0x8c, 0x6a, 0x78, 0x5c }; + //LTHR value + static const u8 reg0x05 [] = { 0x78, 0x87, 0x64, 0x46, 0x36, 0x26 }; + //MSETH + static const u8 reg0x08 [] = { 0x8c, 0xa2, 0x74, 0x43, 0x34, 0x23 }; + //AREF + static const u8 reg0x09 [] = { 0x96, 0x91, 0x96, 0x6a, 0x7e, 0x6b }; + + int qam = p->u.qam.modulation; + + if (qam < 0 || qam > 5) + return -EINVAL; + + //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate); + + lock_tuner(state); + state->config->pll_set(fe, p); + unlock_tuner(state); + + tda10021_set_symbolrate (state, p->u.qam.symbol_rate); + tda10021_writereg (state, 0x34, state->pwm); + + tda10021_writereg (state, 0x01, reg0x01[qam]); + tda10021_writereg (state, 0x05, reg0x05[qam]); + tda10021_writereg (state, 0x08, reg0x08[qam]); + tda10021_writereg (state, 0x09, reg0x09[qam]); + + tda10021_setup_reg0 (state, reg0x00[qam], p->inversion); + + return 0; +} + +static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + int sync; + + *status = 0; + //0x11[0] == EQALGO -> Equalizer algorithms state + //0x11[1] == CARLOCK -> Carrier locked + //0x11[2] == FSYNC -> Frame synchronisation + //0x11[3] == FEL -> Front End locked + //0x11[6] == NODVB -> DVB Mode Information + sync = tda10021_readreg (state, 0x11); + + if (sync & 2) + *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; + + if (sync & 4) + *status |= FE_HAS_SYNC|FE_HAS_VITERBI; + + if (sync & 8) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + u32 _ber = tda10021_readreg(state, 0x14) | + (tda10021_readreg(state, 0x15) << 8) | + ((tda10021_readreg(state, 0x16) & 0x0f) << 16); + *ber = 10 * _ber; + + return 0; +} + +static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + u8 gain = tda10021_readreg(state, 0x17); + *strength = (gain << 8) | gain; + + return 0; +} + +static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + u8 quality = ~tda10021_readreg(state, 0x18); + *snr = (quality << 8) | quality; + + return 0; +} + +static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + *ucblocks = tda10021_readreg (state, 0x13) & 0x7f; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; + + /* reset uncorrected block counter */ + tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); + tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); + + return 0; +} + +static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + int sync; + s8 afc = 0; + + sync = tda10021_readreg(state, 0x11); + afc = tda10021_readreg(state, 0x19); + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" : + "DVB: TDA10021(%d): [AFC (%d) %dHz]\n", + state->frontend.dvb->num, afc, + -((s32)p->u.qam.symbol_rate * afc) >> 10); + } + + p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF; + p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; + + p->u.qam.fec_inner = FEC_NONE; + p->frequency = ((p->frequency + 31250) / 62500) * 62500; + + if (sync & 2) + p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10; + + return 0; +} + +static int tda10021_sleep(struct dvb_frontend* fe) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + + tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ + tda10021_writereg (state, 0x00, 0x80); /* standby */ + + return 0; +} + +static void tda10021_release(struct dvb_frontend* fe) +{ + struct tda10021_state* state = (struct tda10021_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops tda10021_ops; + +struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, + struct i2c_adapter* i2c, + u8 pwm) +{ + struct tda10021_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct tda10021_state*) kmalloc(sizeof(struct tda10021_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); + state->pwm = pwm; + state->reg0 = tda10021_inittab[0]; + + /* check if the demod is there */ + if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda10021_ops = { + + .info = { + .name = "Philips TDA10021 DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ + .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ + #if 0 + .frequency_tolerance = ???, + .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ + #endif + .caps = 0x400 | //FE_CAN_QAM_4 + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = tda10021_release, + + .init = tda10021_init, + .sleep = tda10021_sleep, + + .set_frontend = tda10021_set_parameters, + .get_frontend = tda10021_get_frontend, + + .read_status = tda10021_read_status, + .read_ber = tda10021_read_ber, + .read_signal_strength = tda10021_read_signal_strength, + .read_snr = tda10021_read_snr, + .read_ucblocks = tda10021_read_ucblocks, +}; + +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); + +MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda10021_attach); diff -puN /dev/null drivers/media/dvb/frontends/tda10021.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/tda10021.h 2004-11-21 17:13:23.487047808 -0800 @@ -0,0 +1,43 @@ +/* + TDA10021 - Single Chip Cable Channel Receiver driver module + used on the the Siemens DVB-C cards + + Copyright (C) 1999 Convergence Integrated Media GmbH + Copyright (C) 2004 Markus Schulz + Suppport for TDA10021 + + 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. +*/ + +#ifndef TDA10021_H +#define TDA10021_H + +#include + +struct tda10021_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, + struct i2c_adapter* i2c, + u8 pwm); + +#endif // TDA10021_H diff -puN drivers/media/dvb/frontends/tda1004x.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/tda1004x.c --- 25/drivers/media/dvb/frontends/tda1004x.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.396061640 -0800 +++ 25-akpm/drivers/media/dvb/frontends/tda1004x.c 2004-11-21 17:13:23.497046288 -0800 @@ -1,5 +1,5 @@ /* - Driver for Philips tda1004xh OFDM Frontend + Driver for Philips tda1004xh OFDM Demodulator (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach @@ -32,23 +32,35 @@ #include #include #include -#include - #include "dvb_frontend.h" +#include "tda1004x.h" -#define FRONTEND_NAME "dvbfe_tda1004x" +#define TDA1004X_DEMOD_TDA10045 0 +#define TDA1004X_DEMOD_TDA10046 1 -#define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG FRONTEND_NAME ": " args); \ - } while (0) -static int debug; +struct tda1004x_state +{ + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + const struct tda1004x_config* config; + + struct dvb_frontend frontend; + + /* private demod data */ + u8 initialised:1; + + u8 demod_type; +}; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -#define MC44BC374_ADDRESS 0x65 +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda1004x: " args); \ + } while (0) #define TDA1004X_CHIPID 0x00 #define TDA1004X_AUTO 0x01 @@ -128,53 +140,7 @@ MODULE_PARM_DESC(debug, "Turn on/off fro #define TDA10046H_CODE_IN 0x58 -#define FE_TYPE_TDA10045H 0 -#define FE_TYPE_TDA10046H 1 - -#define TUNER_TYPE_TD1344 0 -#define TUNER_TYPE_TD1316 1 - -static struct dvb_frontend_info tda10045h_info = { - .name = "Philips TDA10045H", - .type = FE_OFDM, - .frequency_min = 51000000, - .frequency_max = 858000000, - .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 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO -}; - -static struct dvb_frontend_info tda10046h_info = { - .name = "Philips TDA10046H", - .type = FE_OFDM, - .frequency_min = 51000000, - .frequency_max = 858000000, - .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 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO -}; - -struct tda1004x_state { - u8 tda1004x_address; - u8 tuner_address; - u8 initialised; - u8 tuner_type; - u8 fe_type; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; - - int dspCodeCounterReg; - int dspCodeInReg; - int dspVersion; -}; - -static int tda1004x_write_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int data) +static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data) { int ret; u8 buf[] = { reg, data }; @@ -182,8 +148,8 @@ static int tda1004x_write_byte(struct i2 dprintk("%s: reg=0x%x, data=0x%x\n", __FUNCTION__, reg, data); - msg.addr = tda_state->tda1004x_address; - ret = i2c_transfer(i2c, &msg, 1); + msg.addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, &msg, 1); if (ret != 1) dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", @@ -194,7 +160,7 @@ static int tda1004x_write_byte(struct i2 return (ret != 1) ? -1 : 0; } -static int tda1004x_read_byte(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg) +static int tda1004x_read_byte(struct tda1004x_state *state, int reg) { int ret; u8 b0[] = { reg }; @@ -204,9 +170,9 @@ static int tda1004x_read_byte(struct i2c dprintk("%s: reg=0x%x\n", __FUNCTION__, reg); - msg[0].addr = tda_state->tda1004x_address; - msg[1].addr = tda_state->tda1004x_address; - ret = i2c_transfer(i2c, msg, 2); + msg[0].addr = state->config->demod_address; + msg[1].addr = state->config->demod_address; + ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg, @@ -219,14 +185,14 @@ static int tda1004x_read_byte(struct i2c return b1[0]; } -static int tda1004x_write_mask(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, int mask, int data) +static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data) { int val; dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __FUNCTION__, reg, mask, data); // read a byte and check - val = tda1004x_read_byte(i2c, tda_state, reg); + val = tda1004x_read_byte(state, reg); if (val < 0) return val; @@ -235,10 +201,10 @@ static int tda1004x_write_mask(struct i2 val |= data & 0xff; // write it out again - return tda1004x_write_byte(i2c, tda_state, reg, val); + return tda1004x_write_byteI(state, reg, val); } -static int tda1004x_write_buf(struct i2c_adapter *i2c, struct tda1004x_state *tda_state, int reg, unsigned char *buf, int len) +static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len) { int i; int result; @@ -247,7 +213,7 @@ static int tda1004x_write_buf(struct i2c result = 0; for (i = 0; i < len; i++) { - result = tda1004x_write_byte(i2c, tda_state, reg + i, buf[i]); + result = tda1004x_write_byteI(state, reg + i, buf[i]); if (result != 0) break; } @@ -255,25 +221,24 @@ static int tda1004x_write_buf(struct i2c return result; } -static int tda1004x_enable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) +static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state) { int result; dprintk("%s\n", __FUNCTION__); - result = tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 2); + result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2); msleep(1); return result; } -static int tda1004x_disable_tuner_i2c(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) +static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state) { dprintk("%s\n", __FUNCTION__); - return tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 2, 0); + return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0); } -static int tda10045h_set_bandwidth(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, +static int tda10045h_set_bandwidth(struct tda1004x_state *state, fe_bandwidth_t bandwidth) { static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f }; @@ -282,31 +247,27 @@ static int tda10045h_set_bandwidth(struc switch (bandwidth) { case BANDWIDTH_6_MHZ: - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); - tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); break; case BANDWIDTH_7_MHZ: - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x80); - tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); break; case BANDWIDTH_8_MHZ: - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0x14); - tda1004x_write_buf(i2c, tda_state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); + tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); break; default: return -EINVAL; } - tda1004x_write_byte(i2c, tda_state, TDA10045H_IOFFSET, 0); + tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0); return 0; } -static int tda10046h_set_bandwidth(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, +static int tda10046h_set_bandwidth(struct tda1004x_state *state, fe_bandwidth_t bandwidth) { static u8 bandwidth_6mhz[] = { 0x80, 0x15, 0xfe, 0xab, 0x8e }; @@ -315,18 +276,15 @@ static int tda10046h_set_bandwidth(struc switch (bandwidth) { case BANDWIDTH_6_MHZ: - tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz)); - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0); + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz, sizeof(bandwidth_6mhz)); break; case BANDWIDTH_7_MHZ: - tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz)); - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0); + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz, sizeof(bandwidth_7mhz)); break; case BANDWIDTH_8_MHZ: - tda1004x_write_buf(i2c, tda_state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz)); - tda1004x_write_byte(i2c, tda_state, TDA1004X_DSSPARE2, 0xFF); + tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz, sizeof(bandwidth_8mhz)); break; default: @@ -336,7 +294,9 @@ static int tda10046h_set_bandwidth(struc return 0; } -static int tda1004x_do_upload(struct i2c_adapter *i2c, struct tda1004x_state *state, unsigned char *mem, unsigned int len) +static int tda1004x_do_upload(struct tda1004x_state *state, + unsigned char *mem, unsigned int len, + u8 dspCodeCounterReg, u8 dspCodeInReg) { u8 buf[65]; struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 }; @@ -344,10 +304,10 @@ static int tda1004x_do_upload(struct i2c int pos = 0; /* clear code counter */ - tda1004x_write_byte(i2c, state, state->dspCodeCounterReg, 0); - fw_msg.addr = state->tda1004x_address; + tda1004x_write_byteI(state, dspCodeCounterReg, 0); + fw_msg.addr = state->config->demod_address; - buf[0] = state->dspCodeInReg; + buf[0] = dspCodeInReg; while (pos != len) { // work out how much to send this time @@ -359,7 +319,7 @@ static int tda1004x_do_upload(struct i2c // send the chunk memcpy(buf + 1, mem + pos, tx_size); fw_msg.len = tx_size + 1; - if (i2c_transfer(i2c, &fw_msg, 1) != 1) { + if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) { printk("tda1004x: Error during firmware upload\n"); return -EIO; } @@ -370,18 +330,17 @@ static int tda1004x_do_upload(struct i2c return 0; } -static int tda1004x_check_upload_ok(struct i2c_adapter *i2c, struct tda1004x_state *state) +static int tda1004x_check_upload_ok(struct tda1004x_state *state, u8 dspVersion) { u8 data1, data2; // check upload was OK - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP - tda1004x_write_byte(i2c, state, TDA1004X_DSP_CMD, 0x67); + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP + tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67); - data1 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA1); - data2 = tda1004x_read_byte(i2c, state, TDA1004X_DSP_DATA2); - if (data1 != 0x67 || data2 != state->dspVersion) { - printk("tda1004x: firmware upload failed!\n"); + data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1); + data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2); + if (data1 != 0x67 || data2 != dspVersion) { return -EIO; } @@ -389,31 +348,34 @@ static int tda1004x_check_upload_ok(stru } -static int tda10045_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, struct i2c_client *client) +static int tda10045_fwupload(struct dvb_frontend* fe) { + struct tda1004x_state* state = fe->demodulator_priv; int ret; const struct firmware *fw; + + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state, 0x2c) == 0) return 0; + /* request the firmware, this will block until someone uploads it */ printk("tda1004x: waiting for firmware upload...\n"); - ret = request_firmware(&fw, TDA10045_DEFAULT_FIRMWARE, &client->dev); + ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); if (ret) { printk("tda1004x: no firmware upload (timeout or file not found?)\n"); return ret; } - /* set some valid bandwith parameters before uploading */ - /* reset chip */ - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 0x10, 0); - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8); - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 0); + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); msleep(10); /* set parameters */ - tda10045h_set_bandwidth(i2c, state, BANDWIDTH_8_MHZ); + tda10045h_set_bandwidth(state, BANDWIDTH_8_MHZ); - ret = tda1004x_do_upload(i2c, state, fw->data, fw->size); + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN); if (ret) return ret; @@ -421,49 +383,47 @@ static int tda10045_fwupload(struct i2c_ /* DSPREADY doesn't seem to work on the TDA10045H */ msleep(100); - ret = tda1004x_check_upload_ok(i2c, state); - if (ret) - return ret; - - return 0; + return tda1004x_check_upload_ok(state, 0x2c); } -static int tda10046_fwupload(struct i2c_adapter *i2c, struct tda1004x_state *state, struct i2c_client *client) +static int tda10046_fwupload(struct dvb_frontend* fe) { + struct tda1004x_state* state = fe->demodulator_priv; unsigned long timeout; int ret; const struct firmware *fw; + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state, 0x20) == 0) return 0; + /* request the firmware, this will block until someone uploads it */ printk("tda1004x: waiting for firmware upload...\n"); - ret = request_firmware(&fw, TDA10046_DEFAULT_FIRMWARE, &client->dev); + ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); if (ret) { printk("tda1004x: no firmware upload (timeout or file not found?)\n"); return ret; } - /* set some valid bandwith parameters before uploading */ - /* reset chip */ - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 1, 0); - tda1004x_write_mask(i2c, state, TDA10046H_CONF_TRISTATE1, 1, 0); + tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); msleep(10); /* set parameters */ - tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL2, 10); - tda1004x_write_byte(i2c, state, TDA10046H_CONFPLL3, 0); - tda1004x_write_byte(i2c, state, TDA10046H_FREQ_OFFSET, 99); - tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_MSB, 0xd4); - tda1004x_write_byte(i2c, state, TDA10046H_FREQ_PHY2_LSB, 0x2c); - tda1004x_write_mask(i2c, state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST + tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); + tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); + tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4); + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST - ret = tda1004x_do_upload(i2c, state, fw->data, fw->size); + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); if (ret) return ret; /* wait for DSP to initialise */ timeout = jiffies + HZ; - while(!(tda1004x_read_byte(i2c, state, TDA1004X_STATUS_CD) & 0x20)) { + while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) { if (time_after(jiffies, timeout)) { printk("tda1004x: DSP failed to initialised.\n"); return -EIO; @@ -471,95 +431,7 @@ static int tda10046_fwupload(struct i2c_ msleep(1); } - ret = tda1004x_check_upload_ok(i2c, state); - if (ret) - return ret; - - return 0; -} - -static int tda10045h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) -{ - struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = NULL,.len = 0 }; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; - - dprintk("%s\n", __FUNCTION__); - - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC - - // Disable the MC44BC374C - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = MC44BC374_ADDRESS; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (i2c_transfer(i2c, &tuner_msg, 1) != 1) { - i2c_transfer(i2c, &tuner_msg, 1); - } - tda1004x_disable_tuner_i2c(i2c, tda_state); - - // tda setup - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0); // no frequency inversion - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface - tda1004x_write_mask(i2c, tda_state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONFADC1, 0x2e); - - return 0; -} - -static int tda10046h_init(struct i2c_adapter *i2c, struct tda1004x_state *tda_state) -{ - struct i2c_msg tuner_msg = {.addr = 0,.flags = 0,.buf = NULL,.len = 0 }; - static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; - - dprintk("%s\n", __FUNCTION__); - - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 0); // wake up the chip - - // Disable the MC44BC374C - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = MC44BC374_ADDRESS; - tuner_msg.buf = disable_mc44BC374c; - tuner_msg.len = sizeof(disable_mc44BC374c); - if (i2c_transfer(i2c, &tuner_msg, 1) != 1) { - i2c_transfer(i2c, &tuner_msg, 1); - } - tda1004x_disable_tuner_i2c(i2c, tda_state); - - // tda setup - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x40, 0x40); // TT TDA10046H needs inversion ON - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 8, 0); // select HP stream - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer - tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL2, 10); // PLL M = 10 - tda1004x_write_byte(i2c, tda_state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 - tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99 - tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221 - tda1004x_write_byte(i2c, tda_state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // } - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_CONF, 0); // AGC setup - tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MIN, 0); // } - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MIN, 0); // } - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_IF_MAX, 0xff); // } - tda1004x_write_mask(i2c, tda_state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits - tda1004x_write_byte(i2c, tda_state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1 - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm - tda1004x_write_byte(i2c, tda_state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config - tda1004x_write_mask(i2c, tda_state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN - tda1004x_write_byte(i2c, tda_state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup - tda1004x_write_byte(i2c, tda_state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config - tda1004x_write_mask(i2c, tda_state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select - tda10046h_set_bandwidth(i2c, tda_state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz - - return 0; + return tda1004x_check_upload_ok(state, 0x20); } static int tda1004x_encode_fec(int fec) @@ -602,174 +474,147 @@ static int tda1004x_decode_fec(int tdafe return -1; } -static int tda1004x_set_frequency(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, - struct dvb_frontend_parameters *fe_params) -{ - u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; - int tuner_frequency = 0; - u8 band, cp, filter; - int counter, counter2; - dprintk("%s\n", __FUNCTION__); - // setup the frequency buffer - switch (tda_state->tuner_type) { - case TUNER_TYPE_TD1344: - - // setup tuner buffer - // ((Fif+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = - (((fe_params->frequency / 1000) * 6) + 217502) / 1000; - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0x88; - if (fe_params->frequency < 550000000) { - tuner_buf[3] = 0xab; - } else { - tuner_buf[3] = 0xeb; - } - // tune it - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = tda_state->tuner_address; - tuner_msg.len = 4; - i2c_transfer(i2c, &tuner_msg, 1); - - // wait for it to finish - tuner_msg.len = 1; - tuner_msg.flags = I2C_M_RD; - counter = 0; - counter2 = 0; - while (counter++ < 100) { - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - if (tuner_buf[0] & 0x40) { - counter2++; - } else { - counter2 = 0; - } - } - - if (counter2 > 10) { - break; - } - } - tda1004x_disable_tuner_i2c(i2c, tda_state); - break; - case TUNER_TYPE_TD1316: - // determine charge pump - tuner_frequency = fe_params->frequency + 36130000; - if (tuner_frequency < 87000000) { - return -EINVAL; - } else if (tuner_frequency < 130000000) { - cp = 3; - } else if (tuner_frequency < 160000000) { - cp = 5; - } else if (tuner_frequency < 200000000) { - cp = 6; - } else if (tuner_frequency < 290000000) { - cp = 3; - } else if (tuner_frequency < 420000000) { - cp = 5; - } else if (tuner_frequency < 480000000) { - cp = 6; - } else if (tuner_frequency < 620000000) { - cp = 3; - } else if (tuner_frequency < 830000000) { - cp = 5; - } else if (tuner_frequency < 895000000) { - cp = 7; - } else { - return -EINVAL; - } - // determine band - if (fe_params->frequency < 49000000) { - return -EINVAL; - } else if (fe_params->frequency < 159000000) { - band = 1; - } else if (fe_params->frequency < 444000000) { - band = 2; - } else if (fe_params->frequency < 861000000) { - band = 4; - } else { - return -EINVAL; + + + + + + + + + + + + +int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data) +{ + struct tda1004x_state* state = fe->demodulator_priv; + + return tda1004x_write_byteI(state, reg, data); } - // work out filter - switch (fe_params->u.ofdm.bandwidth) { - case BANDWIDTH_6_MHZ: - filter = 0; - break; +static int tda10045_init(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; - case BANDWIDTH_7_MHZ: - filter = 0; - break; + dprintk("%s\n", __FUNCTION__); - case BANDWIDTH_8_MHZ: - filter = 1; - break; + if (state->initialised) return 0; - default: - return -EINVAL; + if (tda10045_fwupload(fe)) { + printk("tda1004x: firmware upload failed\n"); + return -EIO; } - // calculate divisor - // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) - tuner_frequency = - (((fe_params->frequency / 1000) * 6) + 217280) / 1000; - - // setup tuner buffer - tuner_buf[0] = tuner_frequency >> 8; - tuner_buf[1] = tuner_frequency & 0xff; - tuner_buf[2] = 0xca; - tuner_buf[3] = (cp << 5) | (filter << 3) | band; - - // tune it - if (tda_state->fe_type == FE_TYPE_TDA10046H) { - // setup auto offset - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x10, 0x10); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x80, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0xC0, 0); + tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC - // disable agc_conf[2] - tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 0); + // Init the PLL + if (state->config->pll_init) { + tda1004x_enable_tuner_i2c(state); + state->config->pll_init(fe); + tda1004x_disable_tuner_i2c(state); + } + + // tda setup + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer + tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset + tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface + tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface + tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity + tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e); + + state->initialised = 1; + return 0; } - tda1004x_enable_tuner_i2c(i2c, tda_state); - tuner_msg.addr = tda_state->tuner_address; - tuner_msg.len = 4; - if (i2c_transfer(i2c, &tuner_msg, 1) != 1) { + +static int tda10046_init(struct dvb_frontend* fe) +{ + struct tda1004x_state* state = fe->demodulator_priv; + dprintk("%s\n", __FUNCTION__); + + if (state->initialised) return 0; + + if (tda10046_fwupload(fe)) { + printk("tda1004x: firmware upload failed\n"); return -EIO; } - msleep(1); - tda1004x_disable_tuner_i2c(i2c, tda_state); - if (tda_state->fe_type == FE_TYPE_TDA10046H) - tda1004x_write_mask(i2c, tda_state, TDA10046H_AGC_CONF, 4, 4); - break; - default: - return -EINVAL; + tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 0); // wake up the chip + + // Init the PLL + if (state->config->pll_init) { + tda1004x_enable_tuner_i2c(state); + state->config->pll_init(fe); + tda1004x_disable_tuner_i2c(state); } - dprintk("%s: success\n", __FUNCTION__); + // tda setup + tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0x40); + tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0); // disable pulse killer + tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 10); // PLL M = 10 + tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 + tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 99); // FREQOFFS = 99 + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd4); // } PHY2 = -11221 + tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x2c); // } + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0); // AGC setup + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x60, 0x60); // set AGC polarities + tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // } + tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values + tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // } + tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // } + tda1004x_write_mask(state, TDA10046H_CVBER_CTRL, 0x30, 0x10); // 10^6 VBER measurement bits + tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 1); // IF gain 2, TUN gain 1 + tda1004x_write_mask(state, TDA1004X_AUTO, 0x80, 0); // crystal is 50ppm + tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config + tda1004x_write_mask(state, TDA1004X_CONF_TS2, 0x31, 0); // MPEG2 interface config + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0x9e, 0); // disable AGC_TUN + tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0xe1); // tristate setup + tda1004x_write_byteI(state, TDA10046H_GPIO_OUT_SEL, 0xcc); // GPIO output config + tda1004x_write_mask(state, TDA10046H_GPIO_SELECT, 8, 8); // GPIO select + tda10046h_set_bandwidth(state, BANDWIDTH_8_MHZ); // default bandwidth 8 MHz + state->initialised = 1; return 0; } -static int tda1004x_set_fe(struct i2c_adapter *i2c, - struct tda1004x_state *tda_state, +static int tda1004x_set_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; int inversion; dprintk("%s\n", __FUNCTION__); + if (state->demod_type == TDA1004X_DEMOD_TDA10046) { + // setup auto offset + tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0); + + // disable agc_conf[2] + tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0); + } + // set frequency - if ((tmp = tda1004x_set_frequency(i2c, tda_state, fe_params)) < 0) - return tmp; + tda1004x_enable_tuner_i2c(state); + state->config->pll_set(fe, fe_params); + tda1004x_disable_tuner_i2c(state); + + if (state->demod_type == TDA1004X_DEMOD_TDA10046) + tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 4); // Hardcoded to use auto as much as possible // The TDA10045 is very unreliable if AUTO mode is _not_ used. I have not @@ -783,35 +628,35 @@ static int tda1004x_set_fe(struct i2c_ad (fe_params->u.ofdm.code_rate_LP == FEC_AUTO) || (fe_params->u.ofdm.constellation == QAM_AUTO) || (fe_params->u.ofdm.hierarchy_information == HIERARCHY_AUTO)) { - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 1, 1); // enable auto - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x03, 0); // turn off constellation bits - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits + tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); // turn off constellation bits + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits } else { - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 1, 0); // disable auto + tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto // set HP FEC tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_HP); if (tmp < 0) return tmp; - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 7, tmp); + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp); // set LP FEC tmp = tda1004x_encode_fec(fe_params->u.ofdm.code_rate_LP); if (tmp < 0) return tmp; - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF2, 0x38, tmp << 3); + tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3); // set constellation switch (fe_params->u.ofdm.constellation) { case QPSK: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0); break; case QAM_16: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 1); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1); break; case QAM_64: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 3, 2); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2); break; default: @@ -821,19 +666,19 @@ static int tda1004x_set_fe(struct i2c_ad // set hierarchy switch (fe_params->u.ofdm.hierarchy_information) { case HIERARCHY_NONE: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 0 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5); break; case HIERARCHY_1: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 1 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5); break; case HIERARCHY_2: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 2 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5); break; case HIERARCHY_4: - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x60, 3 << 5); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5); break; default: @@ -842,30 +687,26 @@ static int tda1004x_set_fe(struct i2c_ad } // set bandwidth - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - tda10045h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth); + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda10045h_set_bandwidth(state, fe_params->u.ofdm.bandwidth); break; - case FE_TYPE_TDA10046H: - tda10046h_set_bandwidth(i2c, tda_state, fe_params->u.ofdm.bandwidth); + case TDA1004X_DEMOD_TDA10046: + tda10046h_set_bandwidth(state, fe_params->u.ofdm.bandwidth); break; } - // need to invert the inversion for TT TDA10046H - inversion = fe_params->inversion; - if (tda_state->fe_type == FE_TYPE_TDA10046H) { - inversion = inversion ? INVERSION_OFF : INVERSION_ON; - } - // set inversion + inversion = fe_params->inversion; + if (state->config->invert) inversion = inversion ? INVERSION_OFF : INVERSION_ON; switch (inversion) { case INVERSION_OFF: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0); + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0); break; case INVERSION_ON: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC1, 0x20, 0x20); + tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20); break; default: @@ -875,28 +716,28 @@ static int tda1004x_set_fe(struct i2c_ad // set guard interval switch (fe_params->u.ofdm.guard_interval) { case GUARD_INTERVAL_1_32: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); break; case GUARD_INTERVAL_1_16: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 1 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2); break; case GUARD_INTERVAL_1_8: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 2 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2); break; case GUARD_INTERVAL_1_4: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 3 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2); break; case GUARD_INTERVAL_AUTO: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 2, 2); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); + tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); break; default: @@ -906,18 +747,18 @@ static int tda1004x_set_fe(struct i2c_ad // set transmission mode switch (fe_params->u.ofdm.transmission_mode) { case TRANSMISSION_MODE_2K: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 0 << 4); + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4); break; case TRANSMISSION_MODE_8K: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 1 << 4); + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4); break; case TRANSMISSION_MODE_AUTO: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 4, 4); - tda1004x_write_mask(i2c, tda_state, TDA1004X_IN_CONF1, 0x10, 0); + tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4); + tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0); break; default: @@ -925,15 +766,15 @@ static int tda1004x_set_fe(struct i2c_ad } // start the lock - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 8); - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 8, 0); + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); msleep(10); break; - case FE_TYPE_TDA10046H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_AUTO, 0x40, 0x40); + case TDA1004X_DEMOD_TDA10046: + tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40); msleep(10); break; } @@ -941,25 +782,22 @@ static int tda1004x_set_fe(struct i2c_ad return 0; } -static int tda1004x_get_fe(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, struct dvb_frontend_parameters *fe_params) +static int tda1004x_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params) { + struct tda1004x_state* state = fe->demodulator_priv; dprintk("%s\n", __FUNCTION__); // inversion status fe_params->inversion = INVERSION_OFF; - if (tda1004x_read_byte(i2c, tda_state, TDA1004X_CONFC1) & 0x20) { + if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) { fe_params->inversion = INVERSION_ON; } - - // need to invert the inversion for TT TDA10046H - if (tda_state->fe_type == FE_TYPE_TDA10046H) { - fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; - } + if (state->config->invert) fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; // bandwidth - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - switch (tda1004x_read_byte(i2c, tda_state, TDA10045H_WREF_LSB)) { + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) { case 0x14: fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; break; @@ -972,8 +810,8 @@ static int tda1004x_get_fe(struct i2c_ad } break; - case FE_TYPE_TDA10046H: - switch (tda1004x_read_byte(i2c, tda_state, TDA10046H_TIME_WREF1)) { + case TDA1004X_DEMOD_TDA10046: + switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) { case 0x60: fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; break; @@ -989,12 +827,12 @@ static int tda1004x_get_fe(struct i2c_ad // FEC fe_params->u.ofdm.code_rate_HP = - tda1004x_decode_fec(tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF2) & 7); + tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7); fe_params->u.ofdm.code_rate_LP = - tda1004x_decode_fec((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF2) >> 3) & 7); + tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7); // constellation - switch (tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 3) { + switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) { case 0: fe_params->u.ofdm.constellation = QPSK; break; @@ -1008,12 +846,12 @@ static int tda1004x_get_fe(struct i2c_ad // transmission mode fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; - if (tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x10) { + if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) { fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; } // guard interval - switch ((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { + switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { case 0: fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; @@ -1029,7 +867,7 @@ static int tda1004x_get_fe(struct i2c_ad } // hierarchy - switch ((tda1004x_read_byte(i2c, tda_state, TDA1004X_OUT_CONF1) & 0x60) >> 5) { + switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) { case 0: fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; break; @@ -1047,8 +885,9 @@ static int tda1004x_get_fe(struct i2c_ad return 0; } -static int tda1004x_read_status(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, fe_status_t * fe_status) +static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status) { + struct tda1004x_state* state = fe->demodulator_priv; int status; int cber; int vber; @@ -1056,7 +895,7 @@ static int tda1004x_read_status(struct i dprintk("%s\n", __FUNCTION__); // read status - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_STATUS_CD); + status = tda1004x_read_byte(state, TDA1004X_STATUS_CD); if (status == -1) { return -EIO; } @@ -1071,12 +910,12 @@ static int tda1004x_read_status(struct i // is getting anything valid if (!(*fe_status & FE_HAS_VITERBI)) { // read the CBER - cber = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_LSB); + cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB); if (cber == -1) return -EIO; - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_MSB); + status = tda1004x_read_byte(state, TDA1004X_CBER_MSB); if (status == -1) return -EIO; cber |= (status << 8); - tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_RESET); + tda1004x_read_byte(state, TDA1004X_CBER_RESET); if (cber != 65535) { *fe_status |= FE_HAS_VITERBI; @@ -1087,15 +926,15 @@ static int tda1004x_read_status(struct i // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid. if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) { // read the VBER - vber = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_LSB); + vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB); if (vber == -1) return -EIO; - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_MID); + status = tda1004x_read_byte(state, TDA1004X_VBER_MID); if (status == -1) return -EIO; vber |= (status << 8); - status = tda1004x_read_byte(i2c, tda_state, TDA1004X_VBER_MSB); + status = tda1004x_read_byte(state, TDA1004X_VBER_MSB); if (status == -1) return -EIO; vber |= ((status << 16) & 0x0f); - tda1004x_read_byte(i2c, tda_state, TDA1004X_CVBER_LUT); + tda1004x_read_byte(state, TDA1004X_CVBER_LUT); // if RS has passed some valid TS packets, then we must be // getting some SYNC bytes @@ -1109,26 +948,27 @@ static int tda1004x_read_status(struct i return 0; } -static int tda1004x_read_signal_strength(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * signal) +static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; int reg = 0; dprintk("%s\n", __FUNCTION__); // determine the register to use - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: reg = TDA10045H_S_AGC; break; - case FE_TYPE_TDA10046H: + case TDA1004X_DEMOD_TDA10046: reg = TDA10046H_AGC_IF_LEVEL; break; } // read it - tmp = tda1004x_read_byte(i2c, tda_state, reg); + tmp = tda1004x_read_byte(state, reg); if (tmp < 0) return -EIO; @@ -1137,14 +977,15 @@ static int tda1004x_read_signal_strength return 0; } -static int tda1004x_read_snr(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u16 * snr) +static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; dprintk("%s\n", __FUNCTION__); // read it - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_SNR); + tmp = tda1004x_read_byte(state, TDA1004X_SNR); if (tmp < 0) return -EIO; if (tmp) { @@ -1156,8 +997,9 @@ static int tda1004x_read_snr(struct i2c_ return 0; } -static int tda1004x_read_ucblocks(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ucblocks) +static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; int tmp2; int counter; @@ -1166,16 +1008,16 @@ static int tda1004x_read_ucblocks(struct // read the UCBLOCKS and reset counter = 0; - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_UNCOR); + tmp = tda1004x_read_byte(state, TDA1004X_UNCOR); if (tmp < 0) return -EIO; tmp &= 0x7f; while (counter++ < 5) { - tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0); - tda1004x_write_mask(i2c, tda_state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); + tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); - tmp2 = tda1004x_read_byte(i2c, tda_state, TDA1004X_UNCOR); + tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR); if (tmp2 < 0) return -EIO; tmp2 &= 0x7f; @@ -1192,373 +1034,188 @@ static int tda1004x_read_ucblocks(struct return 0; } -static int tda1004x_read_ber(struct i2c_adapter *i2c, struct tda1004x_state* tda_state, u32* ber) +static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber) { + struct tda1004x_state* state = fe->demodulator_priv; int tmp; dprintk("%s\n", __FUNCTION__); // read it in - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_LSB); + tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB); if (tmp < 0) return -EIO; *ber = tmp << 1; - tmp = tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_MSB); + tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB); if (tmp < 0) return -EIO; *ber |= (tmp << 9); - tda1004x_read_byte(i2c, tda_state, TDA1004X_CBER_RESET); + tda1004x_read_byte(state, TDA1004X_CBER_RESET); dprintk("%s: ber=0x%x\n", __FUNCTION__, *ber); return 0; } -static int tda1004x_sleep(struct i2c_adapter *i2c, struct tda1004x_state* tda_state) +static int tda1004x_sleep(struct dvb_frontend* fe) { - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFADC1, 0x10, 0x10); - break; - - case FE_TYPE_TDA10046H: - tda1004x_write_mask(i2c, tda_state, TDA1004X_CONFC4, 1, 1); - break; - } + struct tda1004x_state* state = fe->demodulator_priv; - return 0; -} - -static int tda1004x_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg) -{ - struct tda1004x_state *tda_state = (struct tda1004x_state *) fe->data; - struct i2c_adapter *i2c = tda_state->i2c; - int status = 0; - - dprintk("%s: cmd=0x%x\n", __FUNCTION__, cmd); - - switch (cmd) { - case FE_GET_INFO: - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - memcpy(arg, &tda10045h_info, sizeof(struct dvb_frontend_info)); + switch(state->demod_type) { + case TDA1004X_DEMOD_TDA10045: + tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10); break; - case FE_TYPE_TDA10046H: - memcpy(arg, &tda10046h_info, sizeof(struct dvb_frontend_info)); + case TDA1004X_DEMOD_TDA10046: + tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); break; } - break; - - case FE_READ_STATUS: - return tda1004x_read_status(i2c, tda_state, (fe_status_t *) arg); - - case FE_READ_BER: - return tda1004x_read_ber(i2c, tda_state, (u32 *) arg); - - case FE_READ_SIGNAL_STRENGTH: - return tda1004x_read_signal_strength(i2c, tda_state, (u16 *) arg); - - case FE_READ_SNR: - return tda1004x_read_snr(i2c, tda_state, (u16 *) arg); - - case FE_READ_UNCORRECTED_BLOCKS: - return tda1004x_read_ucblocks(i2c, tda_state, (u32 *) arg); - - case FE_SET_FRONTEND: - return tda1004x_set_fe(i2c, tda_state, (struct dvb_frontend_parameters*) arg); - - case FE_GET_FRONTEND: - return tda1004x_get_fe(i2c, tda_state, (struct dvb_frontend_parameters*) arg); - - case FE_SLEEP: - tda_state->initialised = 0; - return tda1004x_sleep(i2c, tda_state); - - case FE_INIT: + state->initialised = 0; - // don't bother reinitialising - if (tda_state->initialised) return 0; - - // OK, perform initialisation - switch(tda_state->fe_type) { - case FE_TYPE_TDA10045H: - status = tda10045h_init(i2c, tda_state); - break; - - case FE_TYPE_TDA10046H: - status = tda10046h_init(i2c, tda_state); - break; } - if (status == 0) - tda_state->initialised = 1; - return status; - case FE_GET_TUNE_SETTINGS: +static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct dvb_frontend_tune_settings* fesettings = (struct dvb_frontend_tune_settings*) arg; fesettings->min_delay_ms = 800; fesettings->step_size = 166667; fesettings->max_drift = 166667*2; return 0; } - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int tda1004x_attach(struct i2c_adapter *i2c, struct tda1004x_state* state) +static void tda1004x_release(struct dvb_frontend* fe) { - int tda1004x_address = -1; - int tuner_address = -1; - int fe_type = -1; - int tuner_type = -1; - struct i2c_msg tuner_msg = {.addr=0, .flags=0, .buf=NULL, .len=0 }; - static u8 td1344_init[] = { 0x0b, 0xf5, 0x88, 0xab }; - static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; - static u8 td1316_init_tda10046h[] = { 0x0b, 0xf5, 0x80, 0xab }; - - dprintk("%s\n", __FUNCTION__); - - // probe for tda10045h - if (tda1004x_address == -1) { - state->tda1004x_address = 0x08; - if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x25) { - tda1004x_address = 0x08; - fe_type = FE_TYPE_TDA10045H; - printk("tda1004x: Detected Philips TDA10045H.\n"); - } - } - - // probe for tda10046h - if (tda1004x_address == -1) { - state->tda1004x_address = 0x08; - if (tda1004x_read_byte(i2c, state, TDA1004X_CHIPID) == 0x46) { - tda1004x_address = 0x08; - fe_type = FE_TYPE_TDA10046H; - printk("tda1004x: Detected Philips TDA10046H.\n"); - } - } - - // did we find a frontend? - if (tda1004x_address == -1) { - return -ENODEV; - } - - // enable access to the tuner - tda1004x_enable_tuner_i2c(i2c, state); - - // check for a TD1344 first - if (tuner_address == -1) { - tuner_msg.addr = 0x61; - tuner_msg.buf = td1344_init; - tuner_msg.len = sizeof(td1344_init); - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - msleep(1); - tuner_address = 0x61; - tuner_type = TUNER_TYPE_TD1344; - printk("tda1004x: Detected Philips TD1344 tuner.\n"); - } - } - - // OK, try a TD1316 on address 0x63 - if (tuner_address == -1) { - tuner_msg.addr = 0x63; - tuner_msg.buf = td1316_init; - tuner_msg.len = sizeof(td1316_init); - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - msleep(1); - tuner_address = 0x63; - tuner_type = TUNER_TYPE_TD1316; - printk("tda1004x: Detected Philips TD1316 tuner.\n"); - } - } - - // OK, TD1316 again, on address 0x60 (TDA10046H) - if (tuner_address == -1) { - tuner_msg.addr = 0x60; - tuner_msg.buf = td1316_init_tda10046h; - tuner_msg.len = sizeof(td1316_init_tda10046h); - if (i2c_transfer(i2c, &tuner_msg, 1) == 1) { - msleep(1); - tuner_address = 0x60; - tuner_type = TUNER_TYPE_TD1316; - printk("tda1004x: Detected Philips TD1316 tuner.\n"); - } - } - tda1004x_disable_tuner_i2c(i2c, state); - - // did we find a tuner? - if (tuner_address == -1) { - printk("tda1004x: Detected, but with unknown tuner.\n"); - return -ENODEV; + struct tda1004x_state* state = (struct tda1004x_state*) fe->demodulator_priv; + kfree(state); } - // create state - state->tda1004x_address = tda1004x_address; - state->fe_type = fe_type; - state->tuner_address = tuner_address; - state->tuner_type = tuner_type; - state->initialised = 0; - - return 0; -} - -static struct i2c_client client_template; +static struct dvb_frontend_ops tda10045_ops; -static int attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) { - struct i2c_client *client; - struct tda1004x_state *state; - int ret; + struct tda1004x_state* state = NULL; - dprintk ("%s\n", __FUNCTION__); - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - return -ENOMEM; - } + /* allocate memory for the internal state */ + state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + if (state == NULL) goto error; - if (NULL == (state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL))) { - kfree(client); - return -ENOMEM; - } - state->i2c = adapter; - - ret = tda1004x_attach(adapter, state); - if (ret) { - kfree(state); - kfree(client); - return -ENODEV; - } - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = state->tda1004x_address; - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return ret; - } - - // upload firmware - BUG_ON(!state->dvb); - - switch(state->fe_type) { - case FE_TYPE_TDA10045H: - state->dspCodeCounterReg = TDA10045H_FWPAGE; - state->dspCodeInReg = TDA10045H_CODE_IN; - state->dspVersion = 0x2c; - - ret = tda10045_fwupload(adapter, state, client); - if (ret) { - printk("tda1004x: firmware upload failed\n"); - goto out; - } + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + state->demod_type = TDA1004X_DEMOD_TDA10045; - ret = dvb_register_frontend(tda1004x_ioctl, state->dvb, - state, &tda10045h_info, - THIS_MODULE); - break; - case FE_TYPE_TDA10046H: - state->dspCodeCounterReg = TDA10046H_CODE_CPT; - state->dspCodeInReg = TDA10046H_CODE_IN; - state->dspVersion = 0x20; - - ret = tda10046_fwupload(adapter, state, client); - if (ret) { - printk("tda1004x: firmware upload failed\n"); - goto out; - } + /* check if the demod is there */ + if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x25) goto error; - ret = dvb_register_frontend(tda1004x_ioctl, state->dvb, - state, &tda10046h_info, - THIS_MODULE); - break; - default: - BUG_ON(1); - } + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; - if (ret) { - printk("tda1004x: registering frontend failed\n"); - goto out; +error: + if (state) kfree(state); + return NULL; } - return 0; -out: - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; -} - -static int detach_client(struct i2c_client *client) -{ - struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); +static struct dvb_frontend_ops tda10046_ops; - dvb_unregister_frontend (tda1004x_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int command (struct i2c_client *client, unsigned int cmd, void *arg) +struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c) { - struct tda1004x_state *state = (struct tda1004x_state*)i2c_get_clientdata(client); + struct tda1004x_state* state = NULL; - dprintk ("%s\n", __FUNCTION__); + /* allocate memory for the internal state */ + state = (struct tda1004x_state*) kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + if (state == NULL) goto error; - switch (cmd) { - case FE_REGISTER: - state->dvb = (struct dvb_adapter*)arg; - break; - case FE_UNREGISTER: - state->dvb = NULL; - break; - default: - return -EOPNOTSUPP; - } - return 0; -} + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); + state->initialised = 0; + state->demod_type = TDA1004X_DEMOD_TDA10046; -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = FRONTEND_NAME, - .id = I2C_DRIVERID_DVBFE_TDA1004X, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; + /* check if the demod is there */ + if (tda1004x_read_byte(state, TDA1004X_CHIPID) != 0x46) goto error; -static struct i2c_client client_template = { - .name = FRONTEND_NAME, - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; + } + +static struct dvb_frontend_ops tda10045_ops = { + + .info = { + .name = "Philips TDA10045H DVB-T", + .type = FE_OFDM, + .frequency_min = 51000000, + .frequency_max = 858000000, + .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 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = tda1004x_release, + + .init = tda10045_init, + .sleep = tda1004x_sleep, + + .set_frontend = tda1004x_set_fe, + .get_frontend = tda1004x_get_fe, + + .read_status = tda1004x_read_status, + .read_ber = tda1004x_read_ber, + .read_signal_strength = tda1004x_read_signal_strength, + .read_snr = tda1004x_read_snr, + .read_ucblocks = tda1004x_read_ucblocks, }; -static int __init init_tda1004x(void) -{ - return i2c_add_driver(&driver); -} +static struct dvb_frontend_ops tda10046_ops = { -static void __exit exit_tda1004x(void) -{ - if (i2c_del_driver(&driver)) - printk("tda1004x: driver deregistration failed\n"); -} + .info = { + .name = "Philips TDA10046H DVB-T", + .type = FE_OFDM, + .frequency_min = 51000000, + .frequency_max = 858000000, + .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 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = tda1004x_release, + + .init = tda10046_init, + .sleep = tda1004x_sleep, + + .set_frontend = tda1004x_set_fe, + .get_frontend = tda1004x_get_fe, + .get_tune_settings = tda1004x_get_tune_settings, + + .read_status = tda1004x_read_status, + .read_ber = tda1004x_read_ber, + .read_signal_strength = tda1004x_read_signal_strength, + .read_snr = tda1004x_read_snr, + .read_ucblocks = tda1004x_read_ucblocks, +}; -module_init(init_tda1004x); -module_exit(exit_tda1004x); +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); -MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Frontend"); +MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator"); MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(tda10045_attach); +EXPORT_SYMBOL(tda10046_attach); +EXPORT_SYMBOL(tda1004x_write_byte); diff -puN /dev/null drivers/media/dvb/frontends/tda1004x.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/tda1004x.h 2004-11-21 17:13:23.497046288 -0800 @@ -0,0 +1,53 @@ + /* + Driver for Philips tda1004xh OFDM Frontend + + (c) 2004 Andrew de Quincey + + 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. + + */ + +#ifndef TDA1004X_H +#define TDA1004X_H + +#include +#include + +struct tda1004x_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* does the "inversion" need inverted? */ + u8 invert:1; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); + + /* request firmware for device */ + int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +}; + +extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c); + +extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, + struct i2c_adapter* i2c); + +extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data); + +#endif // TDA1004X_H diff -puN /dev/null drivers/media/dvb/frontends/tda8083.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/tda8083.c 2004-11-21 17:13:23.500045832 -0800 @@ -0,0 +1,490 @@ +/* + Driver for Philips TDA8083 based QPSK Demodulator + + Copyright (C) 2001 Convergence Integrated Media GmbH + + written by Ralph Metzler + + adoption to the new DVB frontend API and diagnostic ioctl's + by Holger Waechtler + + 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 +#include +#include +#include +#include +#include +#include "dvb_frontend.h" +#include "tda8083.h" + + +struct tda8083_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct tda8083_config* config; + + struct dvb_frontend frontend; +}; + +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda8083: " args); \ + } while (0) + + +static u8 tda8083_init_tab [] = { + 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, + 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, + 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, + 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, + 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + + +static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data) +{ + int ret; + u8 buf [] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk ("%s: writereg error (reg %02x, ret == %i)\n", + __FUNCTION__, reg, ret); + + return (ret != 1) ? -1 : 0; +} + + +static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len) +{ + int ret; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk ("%s: readreg error (reg %02x, ret == %i)\n", + __FUNCTION__, reg1, ret); + + return ret == 2 ? 0 : -1; +} + + +static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg) +{ + u8 val; + + tda8083_readregs (state, reg, &val, 1); + + return val; +} + + + +static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion) +{ + /* XXX FIXME: implement other modes than FEC_AUTO */ + if (inversion == INVERSION_AUTO) + return 0; + + return -EINVAL; +} + + +static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec) +{ + if (fec == FEC_AUTO) + return tda8083_writereg (state, 0x07, 0xff); + + if (fec >= FEC_1_2 && fec <= FEC_8_9) + return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec)); + + return -EINVAL; +} + + +static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state) +{ + u8 index; + static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 }; + + index = tda8083_readreg(state, 0x0e) & 0x07; + + return fec_tab [index]; +} + + +static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate) +{ + u32 ratio; + u32 tmp; + u8 filter; + + if (srate > 32000000) + srate = 32000000; + if (srate < 500000) + srate = 500000; + + filter = 0; + if (srate < 24000000) + filter = 2; + if (srate < 16000000) + filter = 3; + + tmp = 31250 << 16; + ratio = tmp / srate; + + tmp = (tmp % srate) << 8; + ratio = (ratio << 8) + tmp / srate; + + tmp = (tmp % srate) << 8; + ratio = (ratio << 8) + tmp / srate; + + dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio); + + tda8083_writereg (state, 0x05, filter); + tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff); + tda8083_writereg (state, 0x03, (ratio >> 8) & 0xff); + tda8083_writereg (state, 0x04, (ratio ) & 0xff); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 1; +} + + +static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) +{ + unsigned long start = jiffies; + + while (jiffies - start < timeout && + !(tda8083_readreg(state, 0x02) & 0x80)) + { + msleep(50); + }; +} + +static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone) +{ + tda8083_writereg (state, 0x26, 0xf1); + + switch (tone) { + case SEC_TONE_OFF: + return tda8083_writereg (state, 0x29, 0x00); + case SEC_TONE_ON: + return tda8083_writereg (state, 0x29, 0x80); + default: + return -EINVAL; + }; +} + + +static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage) +{ + switch (voltage) { + case SEC_VOLTAGE_13: + return tda8083_writereg (state, 0x20, 0x00); + case SEC_VOLTAGE_18: + return tda8083_writereg (state, 0x20, 0x11); + default: + return -EINVAL; + }; +} + +static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst) +{ + switch (burst) { + case SEC_MINI_A: + tda8083_writereg (state, 0x29, (5 << 2)); /* send burst A */ + break; + case SEC_MINI_B: + tda8083_writereg (state, 0x29, (7 << 2)); /* send B */ + break; + default: + return -EINVAL; + }; + + tda8083_wait_diseqc_fifo (state, 100); + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + +static int tda8083_send_diseqc_msg (struct dvb_frontend* fe, + struct dvb_diseqc_master_cmd *m) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + int i; + + tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */ + + for (i=0; imsg_len; i++) + tda8083_writereg (state, 0x23 + i, m->msg[i]); + + tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */ + + tda8083_wait_diseqc_fifo (state, 100); + + return 0; +} + +static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + u8 signal = ~tda8083_readreg (state, 0x01); + u8 sync = tda8083_readreg (state, 0x02); + + *status = 0; + + if (signal > 10) + *status |= FE_HAS_SIGNAL; + + if (sync & 0x01) + *status |= FE_HAS_CARRIER; + + if (sync & 0x02) + *status |= FE_HAS_VITERBI; + + if (sync & 0x10) + *status |= FE_HAS_SYNC; + + if ((sync & 0x1f) == 0x1f) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + u8 signal = ~tda8083_readreg (state, 0x01); + *strength = (signal << 8) | signal; + + return 0; +} + +static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + u8 _snr = tda8083_readreg (state, 0x08); + *snr = (_snr << 8) | _snr; + + return 0; +} + +static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + state->config->pll_set(fe, p); + tda8083_set_inversion (state, p->inversion); + tda8083_set_fec (state, p->u.qpsk.fec_inner); + tda8083_set_symbolrate (state, p->u.qpsk.symbol_rate); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + /* FIXME: get symbolrate & frequency offset...*/ + /*p->frequency = ???;*/ + p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ? + INVERSION_ON : INVERSION_OFF; + p->u.qpsk.fec_inner = tda8083_get_fec (state); + /*p->u.qpsk.symbol_rate = tda8083_get_symbolrate (state);*/ + + return 0; +} + +static int tda8083_sleep(struct dvb_frontend* fe) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_writereg (state, 0x00, 0x02); + return 0; +} + +static int tda8083_init(struct dvb_frontend* fe) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + int i; + + for (i=0; i<44; i++) + tda8083_writereg (state, i, tda8083_init_tab[i]); + + if (state->config->pll_init) state->config->pll_init(fe); + + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_send_diseqc_burst (state, burst); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_set_tone (state, tone); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + + tda8083_set_voltage (state, voltage); + tda8083_writereg (state, 0x00, 0x3c); + tda8083_writereg (state, 0x00, 0x04); + + return 0; +} + +static void tda8083_release(struct dvb_frontend* fe) +{ + struct tda8083_state* state = (struct tda8083_state*) fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops tda8083_ops; + +struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c) +{ + struct tda8083_state* state = NULL; + + /* allocate memory for the internal state */ + state = (struct tda8083_state*) kmalloc(sizeof(struct tda8083_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda8083_ops, sizeof(struct dvb_frontend_ops)); + + /* check if the demod is there */ + if ((tda8083_readreg(state, 0x00)) != 0x05) goto error; + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda8083_ops = { + + .info = { + .name = "Philips TDA8083 DVB-S", + .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 = ???,*/ + .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 + }, + + .release = tda8083_release, + + .init = tda8083_init, + .sleep = tda8083_sleep, + + .set_frontend = tda8083_set_frontend, + .get_frontend = tda8083_get_frontend, + + .read_status = tda8083_read_status, + .read_signal_strength = tda8083_read_signal_strength, + .read_snr = tda8083_read_snr, + + .diseqc_send_master_cmd = tda8083_send_diseqc_msg, + .diseqc_send_burst = tda8083_diseqc_send_burst, + .set_tone = tda8083_diseqc_set_tone, + .set_voltage = tda8083_diseqc_set_voltage, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator"); +MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda8083_attach); diff -puN /dev/null drivers/media/dvb/frontends/tda8083.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/tda8083.h 2004-11-21 17:13:23.500045832 -0800 @@ -0,0 +1,45 @@ +/* + Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend + + Copyright (C) 2001 Convergence Integrated Media GmbH + + written by Ralph Metzler + + adoption to the new DVB frontend API and diagnostic ioctl's + by Holger Waechtler + + 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. + +*/ + +#ifndef TDA8083_H +#define TDA8083_H + +#include + +struct tda8083_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, + struct i2c_adapter* i2c); + +#endif // TDA8083_H diff -puN /dev/null drivers/media/dvb/frontends/tda80xx.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/tda80xx.c 2004-11-21 17:13:23.503045376 -0800 @@ -0,0 +1,749 @@ +/* + * tda80xx.c + * + * Philips TDA8044 / TDA8083 QPSK demodulator driver + * + * Copyright (C) 2001 Felix Domke + * Copyright (C) 2002-2004 Andreas Oberritter + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "tda80xx.h" + +enum { + ID_TDA8044 = 0x04, + ID_TDA8083 = 0x05, +}; + + +struct tda80xx_state { + + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct tda80xx_config* config; + + struct dvb_frontend frontend; + + u32 clk; + int afc_loop; + struct work_struct worklet; + fe_code_rate_t code_rate; + fe_spectral_inversion_t spectral_inversion; + fe_status_t status; + u8 id; +}; + +static int debug = 1; +#define dprintk if (debug) printk + +static u8 tda8044_inittab_pre[] = { + 0x02, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea, + 0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x58, + 0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68, + 0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x00, + 0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +static u8 tda8044_inittab_post[] = { + 0x04, 0x00, 0x6f, 0xb5, 0x86, 0x22, 0x00, 0xea, + 0x30, 0x42, 0x98, 0x68, 0x70, 0x42, 0x99, 0x50, + 0x95, 0x10, 0xf5, 0xe7, 0x93, 0x0b, 0x15, 0x68, + 0x9a, 0x90, 0x61, 0x80, 0x00, 0xe0, 0x40, 0x6c, + 0x0f, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +static u8 tda8083_inittab[] = { + 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, + 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, + 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, + 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, + 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +static __inline__ u32 tda80xx_div(u32 a, u32 b) +{ + return (a + (b / 2)) / b; +} + +static __inline__ u32 tda80xx_gcd(u32 a, u32 b) +{ + u32 r; + + while ((r = a % b)) { + a = b; + b = r; + } + + return b; +} + +static int tda80xx_read(struct tda80xx_state* state, u8 reg, u8 *buf, u8 len) +{ + int ret; + struct i2c_msg msg[] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) + dprintk("%s: readreg error (reg %02x, ret == %i)\n", + __FUNCTION__, reg, ret); + + mdelay(10); + + return (ret == 2) ? 0 : -EREMOTEIO; +} + +static int tda80xx_write(struct tda80xx_state* state, u8 reg, const u8 *buf, u8 len) +{ + int ret; + u8 wbuf[len + 1]; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = wbuf, .len = len + 1 }; + + wbuf[0] = reg; + memcpy(&wbuf[1], buf, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: i2c xfer error (ret == %i)\n", __FUNCTION__, ret); + + mdelay(10); + + return (ret == 1) ? 0 : -EREMOTEIO; +} + +static __inline__ u8 tda80xx_readreg(struct tda80xx_state* state, u8 reg) +{ + u8 val; + + tda80xx_read(state, reg, &val, 1); + + return val; +} + +static __inline__ int tda80xx_writereg(struct tda80xx_state* state, u8 reg, u8 data) +{ + return tda80xx_write(state, reg, &data, 1); +} + +static int tda80xx_set_parameters(struct tda80xx_state* state, + fe_spectral_inversion_t inversion, + u32 symbol_rate, + fe_code_rate_t fec_inner) +{ + u8 buf[15]; + u64 ratio; + u32 clk; + u32 k; + u32 sr = symbol_rate; + u32 gcd; + u8 scd; + + if (symbol_rate > (state->clk * 3) / 16) + scd = 0; + else if (symbol_rate > (state->clk * 3) / 32) + scd = 1; + else if (symbol_rate > (state->clk * 3) / 64) + scd = 2; + else + scd = 3; + + clk = scd ? (state->clk / (scd * 2)) : state->clk; + + /* + * Viterbi decoder: + * Differential decoding off + * Spectral inversion unknown + * QPSK modulation + */ + if (inversion == INVERSION_ON) + buf[0] = 0x60; + else if (inversion == INVERSION_OFF) + buf[0] = 0x20; + else + buf[0] = 0x00; + + /* + * CLK ratio: + * system clock frequency is up to 64 or 96 MHz + * + * formula: + * r = k * clk / symbol_rate + * + * k: 2^21 for caa 0..3, + * 2^20 for caa 4..5, + * 2^19 for caa 6..7 + */ + if (symbol_rate <= (clk * 3) / 32) + k = (1 << 19); + else if (symbol_rate <= (clk * 3) / 16) + k = (1 << 20); + else + k = (1 << 21); + + gcd = tda80xx_gcd(clk, sr); + clk /= gcd; + sr /= gcd; + + gcd = tda80xx_gcd(k, sr); + k /= gcd; + sr /= gcd; + + ratio = (u64)k * (u64)clk; + do_div(ratio, sr); + + buf[1] = ratio >> 16; + buf[2] = ratio >> 8; + buf[3] = ratio; + + /* nyquist filter roll-off factor 35% */ + buf[4] = 0x20; + + clk = scd ? (state->clk / (scd * 2)) : state->clk; + + /* Anti Alias Filter */ + if (symbol_rate < (clk * 3) / 64) + printk("tda80xx: unsupported symbol rate: %u\n", symbol_rate); + else if (symbol_rate <= clk / 16) + buf[4] |= 0x07; + else if (symbol_rate <= (clk * 3) / 32) + buf[4] |= 0x06; + else if (symbol_rate <= clk / 8) + buf[4] |= 0x05; + else if (symbol_rate <= (clk * 3) / 16) + buf[4] |= 0x04; + else if (symbol_rate <= clk / 4) + buf[4] |= 0x03; + else if (symbol_rate <= (clk * 3) / 8) + buf[4] |= 0x02; + else if (symbol_rate <= clk / 2) + buf[4] |= 0x01; + else + buf[4] |= 0x00; + + /* Sigma Delta converter */ + buf[5] = 0x00; + + /* FEC: Possible puncturing rates */ + if (fec_inner == FEC_NONE) + buf[6] = 0x00; + else if ((fec_inner >= FEC_1_2) && (fec_inner <= FEC_8_9)) + buf[6] = (1 << (8 - fec_inner)); + else if (fec_inner == FEC_AUTO) + buf[6] = 0xff; + else + return -EINVAL; + + /* carrier lock detector threshold value */ + buf[7] = 0x30; + /* AFC1: proportional part settings */ + buf[8] = 0x42; + /* AFC1: integral part settings */ + buf[9] = 0x98; + /* PD: Leaky integrator SCPC mode */ + buf[10] = 0x28; + /* AFC2, AFC1 controls */ + buf[11] = 0x30; + /* PD: proportional part settings */ + buf[12] = 0x42; + /* PD: integral part settings */ + buf[13] = 0x99; + /* AGC */ + buf[14] = 0x50 | scd; + + printk("symbol_rate=%u clk=%u\n", symbol_rate, clk); + + return tda80xx_write(state, 0x01, buf, sizeof(buf)); +} + +static int tda80xx_set_clk(struct tda80xx_state* state) +{ + u8 buf[2]; + + /* CLK proportional part */ + buf[0] = (0x06 << 5) | 0x08; /* CMP[2:0], CSP[4:0] */ + /* CLK integral part */ + buf[1] = (0x04 << 5) | 0x1a; /* CMI[2:0], CSI[4:0] */ + + return tda80xx_write(state, 0x17, buf, sizeof(buf)); +} + +#if 0 +static int tda80xx_set_scpc_freq_offset(struct tda80xx_state* state) +{ + /* a constant value is nonsense here imho */ + return tda80xx_writereg(state, 0x22, 0xf9); +} +#endif + +static int tda80xx_close_loop(struct tda80xx_state* state) +{ + u8 buf[2]; + + /* PD: Loop closed, LD: lock detect enable, SCPC: Sweep mode - AFC1 loop closed */ + buf[0] = 0x68; + /* AFC1: Loop closed, CAR Feedback: 8192 */ + buf[1] = 0x70; + + return tda80xx_write(state, 0x0b, buf, sizeof(buf)); +} + +static irqreturn_t tda80xx_irq(int irq, void *priv, struct pt_regs *pt) +{ + schedule_work(priv); + + return IRQ_HANDLED; +} + +static void tda80xx_read_status_int(struct tda80xx_state* state) +{ + u8 val; + + static const fe_spectral_inversion_t inv_tab[] = { + INVERSION_OFF, INVERSION_ON + }; + + static const fe_code_rate_t fec_tab[] = { + FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, + FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8, + }; + + val = tda80xx_readreg(state, 0x02); + + state->status = 0; + + if (val & 0x01) /* demodulator lock */ + state->status |= FE_HAS_SIGNAL; + if (val & 0x02) /* clock recovery lock */ + state->status |= FE_HAS_CARRIER; + if (val & 0x04) /* viterbi lock */ + state->status |= FE_HAS_VITERBI; + if (val & 0x08) /* deinterleaver lock (packet sync) */ + state->status |= FE_HAS_SYNC; + if (val & 0x10) /* derandomizer lock (frame sync) */ + state->status |= FE_HAS_LOCK; + if (val & 0x20) /* frontend can not lock */ + state->status |= FE_TIMEDOUT; + + if ((state->status & (FE_HAS_CARRIER)) && (state->afc_loop)) { + printk("tda80xx: closing loop\n"); + tda80xx_close_loop(state); + state->afc_loop = 0; + } + + if (state->status & (FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK)) { + val = tda80xx_readreg(state, 0x0e); + state->code_rate = fec_tab[val & 0x07]; + if (state->status & (FE_HAS_SYNC | FE_HAS_LOCK)) + state->spectral_inversion = inv_tab[(val >> 7) & 0x01]; + else + state->spectral_inversion = INVERSION_AUTO; + } + else { + state->code_rate = FEC_AUTO; + } +} + +static void tda80xx_worklet(void *priv) +{ + struct tda80xx_state *state = priv; + + tda80xx_writereg(state, 0x00, 0x04); + enable_irq(state->config->irq); + + tda80xx_read_status_int(state); +} + +static void tda80xx_wait_diseqc_fifo(struct tda80xx_state* state) +{ + size_t i; + + for (i = 0; i < 100; i++) { + if (tda80xx_readreg(state, 0x02) & 0x80) + break; + msleep(10); + } +} + +static int tda8044_init(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + int ret; + + /* + * this function is a mess... + */ + + if ((ret = tda80xx_write(state, 0x00, tda8044_inittab_pre, sizeof(tda8044_inittab_pre)))) + return ret; + + tda80xx_writereg(state, 0x0f, 0x50); +#if 1 + tda80xx_writereg(state, 0x20, 0x8F); /* FIXME */ + tda80xx_writereg(state, 0x20, state->config->volt18setting); /* FIXME */ + //tda80xx_writereg(state, 0x00, 0x04); + tda80xx_writereg(state, 0x00, 0x0C); +#endif + //tda80xx_writereg(state, 0x00, 0x08); /* Reset AFC1 loop filter */ + + tda80xx_write(state, 0x00, tda8044_inittab_post, sizeof(tda8044_inittab_post)); + + if (state->config->pll_init) { + tda80xx_writereg(state, 0x1c, 0x80); + state->config->pll_init(fe); + tda80xx_writereg(state, 0x1c, 0x00); + } + + return 0; +} + +static int tda8083_init(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + tda80xx_write(state, 0x00, tda8083_inittab, sizeof(tda8083_inittab)); + + if (state->config->pll_init) { + tda80xx_writereg(state, 0x1c, 0x80); + state->config->pll_init(fe); + tda80xx_writereg(state, 0x1c, 0x00); + } + + return 0; +} + + + + + + + + + + + + + + + + +static int tda80xx_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_13: + return tda80xx_writereg(state, 0x20, state->config->volt13setting); + case SEC_VOLTAGE_18: + return tda80xx_writereg(state, 0x20, state->config->volt18setting); + case SEC_VOLTAGE_OFF: + return tda80xx_writereg(state, 0x20, 0); + default: + return -EINVAL; + } +} + +static int tda80xx_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch (tone) { + case SEC_TONE_OFF: + return tda80xx_writereg(state, 0x29, 0x00); + case SEC_TONE_ON: + return tda80xx_writereg(state, 0x29, 0x80); + default: + return -EINVAL; + } +} + +static int tda80xx_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (cmd->msg_len > 6) + return -EINVAL; + + tda80xx_writereg(state, 0x29, 0x08 | (cmd->msg_len - 3)); + tda80xx_write(state, 0x23, cmd->msg, cmd->msg_len); + tda80xx_writereg(state, 0x29, 0x0c | (cmd->msg_len - 3)); + tda80xx_wait_diseqc_fifo(state); + + return 0; +} + +static int tda80xx_send_diseqc_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t cmd) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch (cmd) { + case SEC_MINI_A: + tda80xx_writereg(state, 0x29, 0x14); + break; + case SEC_MINI_B: + tda80xx_writereg(state, 0x29, 0x1c); + break; + default: + return -EINVAL; + } + + tda80xx_wait_diseqc_fifo(state); + + return 0; +} + +static int tda80xx_sleep(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + tda80xx_writereg(state, 0x00, 0x02); /* enter standby */ + + return 0; +} + +static int tda80xx_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + tda80xx_writereg(state, 0x1c, 0x80); + state->config->pll_set(fe, p); + tda80xx_writereg(state, 0x1c, 0x00); + + tda80xx_set_parameters(state, p->inversion, p->u.qpsk.symbol_rate, p->u.qpsk.fec_inner); + tda80xx_set_clk(state); + //tda80xx_set_scpc_freq_offset(state); + state->afc_loop = 1; + + return 0; +} + +static int tda80xx_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (!state->config->irq) + tda80xx_read_status_int(state); + + p->inversion = state->spectral_inversion; + p->u.qpsk.fec_inner = state->code_rate; + + return 0; +} + +static int tda80xx_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (!state->config->irq) + tda80xx_read_status_int(state); + *status = state->status; + + return 0; +} + +static int tda80xx_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + int ret; + u8 buf[3]; + + if ((ret = tda80xx_read(state, 0x0b, buf, sizeof(buf)))) + return ret; + + *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + +static int tda80xx_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + u8 gain = ~tda80xx_readreg(state, 0x01); + *strength = (gain << 8) | gain; + + return 0; +} + +static int tda80xx_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + u8 quality = tda80xx_readreg(state, 0x08); + *snr = (quality << 8) | quality; + + return 0; +} + +static int tda80xx_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + *ucblocks = tda80xx_readreg(state, 0x0f); + if (*ucblocks == 0xff) + *ucblocks = 0xffffffff; + + return 0; +} + +static int tda80xx_init(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + switch(state->id) { + case ID_TDA8044: + return tda8044_init(fe); + + case ID_TDA8083: + return tda8083_init(fe); + } + return 0; +} + +static void tda80xx_release(struct dvb_frontend* fe) +{ + struct tda80xx_state* state = (struct tda80xx_state*) fe->demodulator_priv; + + if (state->config->irq) + free_irq(state->config->irq, &state->worklet); + + kfree(state); +} + +static struct dvb_frontend_ops tda80xx_ops; + +struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, + struct i2c_adapter* i2c) +{ + struct tda80xx_state* state = NULL; + int ret; + + /* allocate memory for the internal state */ + state = (struct tda80xx_state*) kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &tda80xx_ops, sizeof(struct dvb_frontend_ops)); + state->spectral_inversion = INVERSION_AUTO; + state->code_rate = FEC_AUTO; + state->status = 0; + state->afc_loop = 0; + + /* check if the demod is there */ + if (tda80xx_writereg(state, 0x89, 0x00) < 0) goto error; + state->id = tda80xx_readreg(state, 0x00); + + switch (state->id) { + case ID_TDA8044: + state->clk = 96000000; + printk("tda80xx: Detected tda8044\n"); + break; + + case ID_TDA8083: + state->clk = 64000000; + printk("tda80xx: Detected tda8083\n"); + break; + + default: + goto error; + } + + /* setup IRQ */ + if (state->config->irq) { + INIT_WORK(&state->worklet, tda80xx_worklet, state); + if ((ret = request_irq(state->config->irq, tda80xx_irq, SA_ONESHOT, "tda80xx", &state->worklet)) < 0) { + printk(KERN_ERR "tda80xx: request_irq failed (%d)\n", ret); + goto error; + } + } + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda80xx_ops = { + + .info = { + .name = "Philips TDA80xx DVB-S", + .type = FE_QPSK, + .frequency_min = 500000, + .frequency_max = 2700000, + .frequency_stepsize = 125, + .symbol_rate_min = 4500000, + .symbol_rate_max = 45000000, + .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 + }, + + .release = tda80xx_release, + + .init = tda80xx_init, + .sleep = tda80xx_sleep, + + .set_frontend = tda80xx_set_frontend, + .get_frontend = tda80xx_get_frontend, + + .read_status = tda80xx_read_status, + .read_ber = tda80xx_read_ber, + .read_signal_strength = tda80xx_read_signal_strength, + .read_snr = tda80xx_read_snr, + .read_ucblocks = tda80xx_read_ucblocks, + + .diseqc_send_master_cmd = tda80xx_send_diseqc_msg, + .diseqc_send_burst = tda80xx_send_diseqc_burst, + .set_tone = tda80xx_set_tone, + .set_voltage = tda80xx_set_voltage, +}; + +module_param(debug, int, 0644); + +MODULE_DESCRIPTION("Philips TDA8044 / TDA8083 DVB-S Demodulator driver"); +MODULE_AUTHOR("Felix Domke, Andreas Oberritter"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda80xx_attach); diff -puN /dev/null drivers/media/dvb/frontends/tda80xx.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/tda80xx.h 2004-11-21 17:13:23.504045224 -0800 @@ -0,0 +1,51 @@ +/* + * tda80xx.c + * + * Philips TDA8044 / TDA8083 QPSK demodulator driver + * + * Copyright (C) 2001 Felix Domke + * Copyright (C) 2002-2004 Andreas Oberritter + * + * 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. + */ + +#ifndef TDA80XX_H +#define TDA80XX_H + +#include + +struct tda80xx_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* IRQ to use (0=>no IRQ used) */ + u32 irq; + + /* Register setting to use for 13v */ + u8 volt13setting; + + /* Register setting to use for 18v */ + u8 volt18setting; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config, + struct i2c_adapter* i2c); + +#endif // TDA80XX_H diff -puN drivers/media/dvb/frontends/ves1820.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/ves1820.c --- 25/drivers/media/dvb/frontends/ves1820.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.398061336 -0800 +++ 25-akpm/drivers/media/dvb/frontends/ves1820.c 2004-11-21 17:13:23.507044768 -0800 @@ -1,6 +1,5 @@ /* VES1820 - Single Chip Cable Channel Receiver driver module - used on the the Siemens DVB-C cards Copyright (C) 1999 Convergence Integrated Media GmbH @@ -27,70 +26,34 @@ #include #include #include +#include #include "dvb_frontend.h" +#include "ves1820.h" -/* I2C_DRIVERID_VES1820 is already defined in i2c-id.h */ -#if 0 -static int debug = 0; -#define dprintk if (debug) printk -#endif - -static int verbose; struct ves1820_state { - int pwm; - u8 reg0; - int tuner; - u8 demod_addr; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; -/* possible ves1820 adresses */ -static u8 addr[] = { 0x61, 0x62 }; + struct i2c_adapter *i2c; -#if defined(CONFIG_DBOX2) -#define XIN 69600000UL -#define DISABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) -#define ENABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) -#define HAS_INVERSION(reg0) (reg0 & 0x20) -#else /* PCI cards */ -#define XIN 57840000UL -#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) -#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) -#define HAS_INVERSION(reg0) (!(reg0 & 0x20)) -#endif - -#define FIN (XIN >> 4) + struct dvb_frontend_ops ops; + /* configuration settings */ + const struct ves1820_config* config; + struct dvb_frontend frontend; -static struct dvb_frontend_info ves1820_info = { - .name = "VES1820 based DVB-C frontend", - .type = FE_QAM, - .frequency_stepsize = 62500, - .frequency_min = 51000000, - .frequency_max = 858000000, - .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ - .symbol_rate_max = (XIN/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, + /* private demodulator data */ + u8 reg0; + u8 pwm; }; + +static int verbose; + static u8 ves1820_inittab[] = { - 0x69, 0x6A, 0x9B, 0x12, 0x12, 0x46, 0x26, 0x1A, + 0x69, 0x6A, 0x93, 0x12, 0x12, 0x46, 0x26, 0x1A, 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20, 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, @@ -102,7 +65,7 @@ static u8 ves1820_inittab[] = { static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data) { u8 buf[] = { 0x00, reg, data }; - struct i2c_msg msg = {.addr = state->demod_addr,.flags = 0,.buf = buf,.len = 3 }; + struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 }; int ret; ret = i2c_transfer(state->i2c, &msg, 1); @@ -120,8 +83,8 @@ static u8 ves1820_readreg(struct ves1820 u8 b0 [] = { 0x00, reg }; u8 b1 [] = { 0 }; struct i2c_msg msg[] = { - {.addr = state->demod_addr,.flags = 0,.buf = b0,.len = 2}, - {.addr = state->demod_addr,.flags = I2C_M_RD,.buf = b1,.len = 1} + {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2}, + {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} }; int ret; @@ -134,148 +97,88 @@ static u8 ves1820_readreg(struct ves1820 return b1[0]; } -static int tuner_write(struct ves1820_state *state, u8 addr, u8 data[4]) -{ - int ret; - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; - - ret = i2c_transfer(state->i2c, &msg, 1); - - if (ret != 1) - printk("ves1820: %s(): i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 1) ? -EREMOTEIO : 0; -} - - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 62.5 kHz. - */ -static int tuner_set_tv_freq(struct ves1820_state *state, u32 freq) -{ - u32 div, ifreq; - static u8 byte3 [] = { 0x8e, 0x85 }; - int tuner_type = state->tuner; - u8 buf [4]; - - if (tuner_type == 0xff) /* PLL not reachable over i2c ... */ - return 0; - - if (strstr(state->i2c->name, "Technotrend") - || strstr(state->i2c->name, "TT-Budget")) - ifreq = 35937500; - else - ifreq = 36125000; - - div = (freq + ifreq + 31250) / 62500; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = byte3[tuner_type]; - - if (tuner_type == 1) { - buf[2] |= (div >> 10) & 0x60; - buf[3] = (freq < 174000000 ? 0x88 : freq < 470000000 ? 0x84 : 0x81); - } else { - buf[3] = (freq < 174000000 ? 0xa1 : freq < 454000000 ? 0x92 : 0x34); - } - - return tuner_write(state, addr[tuner_type], buf); -} static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion) { reg0 |= state->reg0 & 0x62; - if (INVERSION_ON == inversion) - ENABLE_INVERSION(reg0); - else if (INVERSION_OFF == inversion) - DISABLE_INVERSION(reg0); + if (INVERSION_ON == inversion) { + if (!state->config->invert) reg0 |= 0x20; + else reg0 &= ~0x20; - ves1820_writereg(state, 0x00, reg0 & 0xfe); - ves1820_writereg(state, 0x00, reg0 | 0x01); + } else if (INVERSION_OFF == inversion) { + + if (!state->config->invert) reg0 &= ~0x20; + else reg0 |= 0x20; + } - /** - * check lock and toggle inversion bit if required... - */ - if (INVERSION_AUTO == inversion && !(ves1820_readreg(state, 0x11) & 0x08)) { - mdelay(50); - if (!(ves1820_readreg(state, 0x11) & 0x08)) { - reg0 ^= 0x20; ves1820_writereg(state, 0x00, reg0 & 0xfe); ves1820_writereg(state, 0x00, reg0 | 0x01); - } - } state->reg0 = reg0; return 0; } -static int ves1820_init(struct ves1820_state *state) -{ - int i; - - ves1820_writereg(state, 0, 0); - -#if defined(CONFIG_DBOX2) - ves1820_inittab[2] &= ~0x08; -#endif - - for (i=0; i<53; i++) - ves1820_writereg(state, i, ves1820_inittab[i]); - - ves1820_writereg(state, 0x34, state->pwm); - - return 0; -} - static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) { s32 BDR; s32 BDRI; s16 SFIL=0; u16 NDEC = 0; - u32 tmp, ratio; + u32 ratio; + u32 fin; + u32 tmp; + u64 fptmp; + u64 fpxin; - if (symbolrate > XIN/2) - symbolrate = XIN/2; + if (symbolrate > state->config->xin / 2) + symbolrate = state->config->xin / 2; if (symbolrate < 500000) symbolrate = 500000; - if (symbolrate < XIN / 16) + if (symbolrate < state->config->xin / 16) NDEC = 1; - if (symbolrate < XIN / 32) + if (symbolrate < state->config->xin / 32) NDEC = 2; - if (symbolrate < XIN / 64) + if (symbolrate < state->config->xin / 64) NDEC = 3; - if (symbolrate < (u32) (XIN / 12.3)) + /* yeuch! */ + fpxin = state->config->xin * 10; + fptmp = fpxin; do_div(fptmp, 123); + if (symbolrate < fptmp); SFIL = 1; - if (symbolrate < (u32) (XIN / 16)) + fptmp = fpxin; do_div(fptmp, 160); + if (symbolrate < fptmp); SFIL = 0; - if (symbolrate < (u32) (XIN / 24.6)) + fptmp = fpxin; do_div(fptmp, 246); + if (symbolrate < fptmp); SFIL = 1; - if (symbolrate < (u32) (XIN / 32)) + fptmp = fpxin; do_div(fptmp, 320); + if (symbolrate < fptmp); SFIL = 0; - if (symbolrate < (u32) (XIN / 49.2)) + fptmp = fpxin; do_div(fptmp, 492); + if (symbolrate < fptmp); SFIL = 1; - if (symbolrate < (u32) (XIN / 64)) + fptmp = fpxin; do_div(fptmp, 640); + if (symbolrate < fptmp); SFIL = 0; - if (symbolrate < (u32) (XIN / 98.4)) + fptmp = fpxin; do_div(fptmp, 984); + if (symbolrate < fptmp); SFIL = 1; + fin = state->config->xin >> 4; symbolrate <<= NDEC; - ratio = (symbolrate << 4) / FIN; - tmp = ((symbolrate << 4) % FIN) << 8; - ratio = (ratio << 8) + tmp / FIN; - tmp = (tmp % FIN) << 8; - ratio = (ratio << 8) + (tmp + FIN/2) / FIN; + ratio = (symbolrate << 4) / fin; + tmp = ((symbolrate << 4) % fin) << 8; + ratio = (ratio << 8) + tmp / fin; + tmp = (tmp % fin) << 8; + ratio = (ratio << 8) + (tmp + fin / 2) / fin; BDR = ratio; - BDRI = (((XIN << 5) / symbolrate) + 1) / 2; + BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2; if (BDRI > 0xFF) BDRI = 0xFF; @@ -295,8 +198,42 @@ static int ves1820_set_symbolrate(struct return 0; } -static int ves1820_set_parameters(struct ves1820_state *state, struct dvb_frontend_parameters *p) + + + + + + + + + + + + +static int ves1820_init(struct dvb_frontend* fe) { + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + int i; + int val; + + ves1820_writereg(state, 0, 0); + + for (i = 0; i < 53; i++) { + val = ves1820_inittab[i]; + if ((i == 2) && (state->config->selagc)) val |= 0x08; + ves1820_writereg(state, i, val); + } + + ves1820_writereg(state, 0x34, state->pwm); + + if (state->config->pll_init) state->config->pll_init(fe); + + return 0; +} + +static int ves1820_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; static const u8 reg0x00 [] = { 0x00, 0x04, 0x08, 0x0c, 0x10 }; static const u8 reg0x01 [] = { 140, 140, 106, 100, 92 }; static const u8 reg0x05 [] = { 135, 100, 70, 54, 38 }; @@ -307,7 +244,7 @@ static int ves1820_set_parameters(struct if (real_qam < 0 || real_qam > 4) return -EINVAL; - tuner_set_tv_freq(state, p->frequency); + state->config->pll_set(fe, p); ves1820_set_symbolrate(state, p->u.qam.symbol_rate); ves1820_writereg(state, 0x34, state->pwm); @@ -318,25 +255,12 @@ static int ves1820_set_parameters(struct ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion); - /* yes, this speeds things up: userspace reports lock in about 8 ms - instead of 500 to 1200 ms after calling FE_SET_FRONTEND. */ - mdelay(50); - return 0; } -static int ves1820_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct ves1820_state *state = (struct ves1820_state *) fe->data; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &ves1820_info, sizeof(struct dvb_frontend_info)); - break; - - case FE_READ_STATUS: - { - fe_status_t *status = (fe_status_t *) arg; + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; int sync; *status = 0; @@ -358,46 +282,59 @@ static int ves1820_ioctl (struct dvb_fro if (sync & 8) *status |= FE_HAS_LOCK; - break; + return 0; } - case FE_READ_BER: +static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 ber = ves1820_readreg(state, 0x14) | + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + + u32 _ber = ves1820_readreg(state, 0x14) | (ves1820_readreg(state, 0x15) << 8) | ((ves1820_readreg(state, 0x16) & 0x0f) << 16); - *((u32*) arg) = 10 * ber; - break; + *ber = 10 * _ber; + + return 0; } - case FE_READ_SIGNAL_STRENGTH: + +static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength) { + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + u8 gain = ves1820_readreg(state, 0x17); - *((u16*) arg) = (gain << 8) | gain; - break; + *strength = (gain << 8) | gain; + + return 0; } - case FE_READ_SNR: +static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr) { + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + u8 quality = ~ves1820_readreg(state, 0x18); - *((u16*) arg) = (quality << 8) | quality; - break; + *snr = (quality << 8) | quality; + + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: - *((u32 *) arg) = ves1820_readreg(state, 0x13) & 0x7f; - if (*((u32*) arg) == 0x7f) - *((u32*) arg) = 0xffffffff; +static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; + + *ucblocks = ves1820_readreg(state, 0x13) & 0x7f; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; + /* reset uncorrected block counter */ ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf); ves1820_writereg(state, 0x10, ves1820_inittab[0x10]); - break; - case FE_SET_FRONTEND: - return ves1820_set_parameters(state, arg); + return 0; +} - case FE_GET_FRONTEND: +static int ves1820_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = (struct dvb_frontend_parameters *)arg; + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; int sync; s8 afc = 0; @@ -409,7 +346,12 @@ static int ves1820_ioctl (struct dvb_fro "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10); } - p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF; + if (!state->config->invert) { + p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF; + } else { + p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF; + } + p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; p->u.qam.fec_inner = FEC_NONE; @@ -417,229 +359,112 @@ static int ves1820_ioctl (struct dvb_fro p->frequency = ((p->frequency + 31250) / 62500) * 62500; if (sync & 2) p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10; - break; - } - case FE_SLEEP: - ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */ - ves1820_writereg(state, 0x00, 0x80); /* standby */ - break; - - case FE_INIT: - return ves1820_init(state); - - default: - return -EINVAL; - } return 0; } -static long probe_tuner(struct i2c_adapter *i2c) +static int ves1820_sleep(struct dvb_frontend* fe) { - struct i2c_msg msg1 = {.addr = 0x61,.flags = 0,.buf = NULL,.len = 0 }; - struct i2c_msg msg2 = {.addr = 0x62,.flags = 0,.buf = NULL,.len = 0 }; - int type; - - if (i2c_transfer(i2c, &msg1, 1) == 1) { - type = 0; - printk("ves1820: setup for tuner spXXXX\n"); - } else if (i2c_transfer(i2c, &msg2, 1) == 1) { - type = 1; - printk("ves1820: setup for tuner sp5659c\n"); - } else { - type = -1; - } - - return type; -} + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; -static u8 read_pwm(struct i2c_adapter *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} - }; - - if ((i2c_transfer(i2c, msg, 2) != 2) || (pwm == 0xff)) - pwm = 0x48; - - printk("ves1820: pwm=0x%02x\n", pwm); - - return pwm; -} - -static long probe_demod_addr(struct i2c_adapter *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_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70) - return msg[0].addr; - - msg[0].addr = msg[1].addr = 0x09; - - if (i2c_transfer(i2c, msg, 2) == 2 && (id & 0xf0) == 0x70) - return msg[0].addr; - - return -1; -} - -static ssize_t attr_read_pwm(struct device *dev, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); - return sprintf(buf, "0x%02x\n", state->pwm); -} + ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */ + ves1820_writereg(state, 0x00, 0x80); /* standby */ -static ssize_t attr_write_pwm(struct device *dev, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); - unsigned long pwm; - pwm = simple_strtoul(buf, NULL, 0); - state->pwm = pwm & 0xff; - return strlen(buf)+1; + return 0; } -static struct device_attribute dev_attr_client_name = { - .attr = { .name = "pwm", .mode = S_IRUGO|S_IWUGO, .owner = THIS_MODULE }, - .show = &attr_read_pwm, - .store = &attr_write_pwm, -}; - -static struct i2c_client client_template; - -static int attach_adapter(struct i2c_adapter *adapter) +static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) { - struct i2c_client *client; - struct ves1820_state *state; - long demod_addr; - int tuner_type; - int ret; - - demod_addr = probe_demod_addr(adapter); - if (demod_addr < 0) - return -ENODEV; - - tuner_type = probe_tuner(adapter); - if (tuner_type < 0) { - printk("ves1820: demod found, but unknown tuner type.\n"); - return -ENODEV; - } - - if ((state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; - } - - memset(state, 0, sizeof(*state)); - state->i2c = adapter; - state->tuner = tuner_type; - state->pwm = read_pwm(adapter); - state->reg0 = ves1820_inittab[0]; - state->demod_addr = demod_addr; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = addr[tuner_type]; - - i2c_set_clientdata(client, (void *) state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return ret; -} - - BUG_ON(!state->dvb); - - device_create_file(&client->dev, &dev_attr_client_name); - - ret = dvb_register_frontend(ves1820_ioctl, state->dvb, state, &ves1820_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return ret; - } + fesettings->min_delay_ms = 200; + fesettings->step_size = 0; + fesettings->max_drift = 0; return 0; } -static int detach_client(struct i2c_client *client) +static void ves1820_release(struct dvb_frontend* fe) { - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); - dvb_unregister_frontend(ves1820_ioctl, state->dvb); - device_remove_file(&client->dev, &dev_attr_client_name); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); + struct ves1820_state* state = (struct ves1820_state*) fe->demodulator_priv; kfree(state); - return 0; -} - -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct ves1820_state *state = (struct ves1820_state *) i2c_get_clientdata(client); - - switch (cmd) { - case FE_REGISTER:{ - state->dvb = (struct dvb_adapter *) arg; - break; - } - case FE_UNREGISTER:{ - state->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; - } - return 0; } -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "ves1820", - .id = I2C_DRIVERID_VES1820, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; +static struct dvb_frontend_ops ves1820_ops; -static struct i2c_client client_template = { - I2C_DEVNAME("ves1820"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_ves1820 (void) +struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, + u8 pwm) { - return i2c_add_driver(&driver); -} + struct ves1820_state* state = NULL; -static void __exit exit_ves1820 (void) -{ - if (i2c_del_driver(&driver)) - printk("ves1820: driver deregistration failed\n"); -} + /* allocate memory for the internal state */ + state = (struct ves1820_state*) kmalloc(sizeof(struct ves1820_state), GFP_KERNEL); + if (state == NULL) + goto error; -module_init(init_ves1820); -module_exit(exit_ves1820); + /* setup the state */ + memcpy(&state->ops, &ves1820_ops, sizeof(struct dvb_frontend_ops)); + state->reg0 = ves1820_inittab[0]; + state->config = config; + state->i2c = i2c; + state->pwm = pwm; + + /* check if the demod is there */ + if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70) + goto error; + + if (verbose) + printk("ves1820: pwm=0x%02x\n", state->pwm); + + state->ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */ + state->ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */ + + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; + } + +static struct dvb_frontend_ops ves1820_ops = { + + .info = { + .name = "VLSI VES1820 DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .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 + }, + + .release = ves1820_release, + + .init = ves1820_init, + .sleep = ves1820_sleep, + + .set_frontend = ves1820_set_parameters, + .get_frontend = ves1820_get_frontend, + .get_tune_settings = ves1820_get_tune_settings, + + .read_status = ves1820_read_status, + .read_ber = ves1820_read_ber, + .read_signal_strength = ves1820_read_signal_strength, + .read_snr = ves1820_read_snr, + .read_ucblocks = ves1820_read_ucblocks, +}; -MODULE_PARM(verbose, "i"); +module_param(verbose, int, 0644); MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); -MODULE_DESCRIPTION("VES1820 DVB-C frontend driver"); +MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); MODULE_LICENSE("GPL"); +EXPORT_SYMBOL(ves1820_attach); diff -puN /dev/null drivers/media/dvb/frontends/ves1820.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/ves1820.h 2004-11-21 17:13:23.508044616 -0800 @@ -0,0 +1,52 @@ +/* + VES1820 - Single Chip Cable Channel Receiver driver module + + Copyright (C) 1999 Convergence Integrated Media GmbH + + 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. +*/ + +#ifndef VES1820_H +#define VES1820_H + +#include + +#define VES1820_SELAGC_PWM 0 +#define VES1820_SELAGC_SIGNAMPERR 1 + +struct ves1820_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* value of XIN to use */ + u32 xin; + + /* does inversion need inverted? */ + u8 invert:1; + + /* SELAGC control */ + u8 selagc:1; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, + struct i2c_adapter* i2c, + u8 pwm); + +#endif // VES1820_H diff -puN drivers/media/dvb/frontends/ves1x93.c~dvb-frontend-driver-refactoring drivers/media/dvb/frontends/ves1x93.c --- 25/drivers/media/dvb/frontends/ves1x93.c~dvb-frontend-driver-refactoring 2004-11-21 17:13:23.400061032 -0800 +++ 25-akpm/drivers/media/dvb/frontends/ves1x93.c 2004-11-21 17:13:23.512044008 -0800 @@ -1,5 +1,5 @@ /* - Driver for VES1893 and VES1993 QPSK Frontends + Driver for VES1893 and VES1993 QPSK Demodulators Copyright (C) 1999 Convergence Integrated Media GmbH Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> @@ -31,41 +31,33 @@ #include #include "dvb_frontend.h" +#include "ves1x93.h" -static int debug = 0; -#define dprintk if (debug) printk -static int board_type = 0; -#define BOARD_SIEMENS_PCI 0 -#define BOARD_NOKIA_DBOX2 1 -#define BOARD_SAGEM_DBOX2 2 +struct ves1x93_state { -static int demod_type = 0; -#define DEMOD_VES1893 0 -#define DEMOD_VES1993 1 + struct i2c_adapter* i2c; + + struct dvb_frontend_ops ops; + + /* configuration settings */ + const struct ves1x93_config* config; -static struct dvb_frontend_info ves1x93_info = { - .name = "VES1x93", - .type = FE_QPSK, - .frequency_min = 950000, - .frequency_max = 2150000, - .frequency_stepsize = 125, /* 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 + struct dvb_frontend frontend; + + /* previous uncorrected block counter */ + fe_spectral_inversion_t inversion; + u8 *init_1x93_tab; + u8 *init_1x93_wtab; + u8 tab_size; + u8 demod_type; }; +static int debug = 0; +#define dprintk if (debug) printk -/** - * nokia dbox2 (ves1893) and sagem dbox2 (ves1993) - * need bit AGCR[PWMS] set to 1 - */ +#define DEMOD_VES1893 0 +#define DEMOD_VES1993 1 static u8 init_1893_tab [] = { 0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4, @@ -89,10 +81,6 @@ static u8 init_1993_tab [] = { 0x00, 0x00, 0x0e, 0x80, 0x00 }; - -static u8 * init_1x93_tab; - - static u8 init_1893_wtab[] = { 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, @@ -110,21 +98,13 @@ static u8 init_1993_wtab[] = 1,1,1,0,1,1,1,1, 1,1,1,1,1 }; -struct ves1x93_state { - fe_spectral_inversion_t inversion; - struct i2c_adapter *i2c; - struct dvb_adapter *dvb; -}; - - - -static int ves1x93_writereg (struct i2c_adapter *i2c, u8 reg, u8 data) +static int ves1x93_writereg (struct ves1x93_state* state, 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 = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 }; int err; - if ((err = i2c_transfer (i2c, &msg, 1)) != 1) { + if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data); return -EREMOTEIO; } @@ -133,151 +113,31 @@ static int ves1x93_writereg (struct i2c_ } -static u8 ves1x93_readreg (struct i2c_adapter *i2c, u8 reg) +static u8 ves1x93_readreg (struct ves1x93_state* state, 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 = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; - ret = i2c_transfer (i2c, msg, 2); + ret = i2c_transfer (state->i2c, msg, 2); - if (ret != 2) - dprintk("%s: readreg error (ret == %i)\n", __FUNCTION__, ret); + if (ret != 2) return ret; return b1[0]; } - -static int tuner_write (struct i2c_adapter *i2c, u8 *data, u8 len) -{ - int ret; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = len }; - - ves1x93_writereg(i2c, 0x00, 0x11); - ret = i2c_transfer (i2c, &msg, 1); - ves1x93_writereg(i2c, 0x00, 0x01); - - if (ret != 1) - printk("%s: i/o error (ret == %i)\n", __FUNCTION__, ret); - - return (ret != 1) ? -1 : 0; -} - - - -/** - * set up the downconverter frequency divisor for a - * reference clock comparision frequency of 125 kHz. - */ -static int sp5659_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - u8 pwr = 0; - u8 buf[4]; - u32 div = (freq + 479500) / 125; - - if (freq > 2000000) pwr = 3; - else if (freq > 1800000) pwr = 2; - else if (freq > 1600000) pwr = 1; - else if (freq > 1200000) pwr = 0; - else if (freq >= 1100000) pwr = 1; - else pwr = 2; - - buf[0] = (div >> 8) & 0x7f; - buf[1] = div & 0xff; - buf[2] = ((div & 0x18000) >> 10) | 0x95; - buf[3] = (pwr << 6) | 0x30; - - // NOTE: since we're using a prescaler of 2, we set the - // divisor frequency to 62.5kHz and divide by 125 above - - return tuner_write (i2c, buf, sizeof(buf)); -} - - -static int tsa5059_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - int ret; - u8 buf [2]; - - freq /= 1000; - - buf[0] = (freq >> 8) & 0x7F; - buf[1] = freq & 0xFF; - - ret = tuner_write(i2c, buf, sizeof(buf)); - - return ret; -} - - -static int tuner_set_tv_freq (struct i2c_adapter *i2c, u32 freq) -{ - if ((demod_type == DEMOD_VES1893) && (board_type == BOARD_SIEMENS_PCI)) - return sp5659_set_tv_freq (i2c, freq); - else if (demod_type == DEMOD_VES1993) - return tsa5059_set_tv_freq (i2c, freq); - - return -EINVAL; -} - - -static int ves1x93_init (struct i2c_adapter *i2c) -{ - int i; - int size; - u8 *init_1x93_wtab; - - dprintk("%s: init chip\n", __FUNCTION__); - - switch (demod_type) { - case DEMOD_VES1893: - init_1x93_tab = init_1893_tab; - init_1x93_wtab = init_1893_wtab; - size = sizeof(init_1893_tab); - if (board_type == BOARD_NOKIA_DBOX2) - init_1x93_tab[0x05] |= 0x20; /* invert PWM */ - break; - - case DEMOD_VES1993: - init_1x93_tab = init_1993_tab; - init_1x93_wtab = init_1993_wtab; - size = sizeof(init_1993_tab); - if (board_type == BOARD_SAGEM_DBOX2) - init_1x93_tab[0x05] |= 0x20; /* invert PWM */ - break; - - default: - return -EINVAL; - } - - for (i = 0; i < size; i++) - if (init_1x93_wtab[i]) - ves1x93_writereg (i2c, i, init_1x93_tab[i]); - - if (demod_type == DEMOD_VES1993) { - if (board_type == BOARD_NOKIA_DBOX2) - tuner_write(i2c, "\x06\x5c\x83\x60", 4); - else if (board_type == BOARD_SAGEM_DBOX2) - tuner_write(i2c, "\x25\x70\x92\x40", 4); - } - - return 0; -} - - -static int ves1x93_clr_bit (struct i2c_adapter *i2c) +static int ves1x93_clr_bit (struct ves1x93_state* state) { msleep(10); - ves1x93_writereg (i2c, 0, init_1x93_tab[0] & 0xfe); - ves1x93_writereg (i2c, 0, init_1x93_tab[0]); + ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe); + ves1x93_writereg (state, 0, state->init_1x93_tab[0]); msleep(50); return 0; } - -static int ves1x93_set_inversion (struct i2c_adapter *i2c, fe_spectral_inversion_t inversion) +static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion) { u8 val; @@ -300,66 +160,47 @@ static int ves1x93_set_inversion (struct return -EINVAL; } - return ves1x93_writereg (i2c, 0x0c, (init_1x93_tab[0x0c] & 0x3f) | val); + return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val); } -static int ves1x93_set_fec (struct i2c_adapter *i2c, fe_code_rate_t fec) +static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec) { if (fec == FEC_AUTO) - return ves1x93_writereg (i2c, 0x0d, 0x08); + return ves1x93_writereg (state, 0x0d, 0x08); else if (fec < FEC_1_2 || fec > FEC_8_9) return -EINVAL; else - return ves1x93_writereg (i2c, 0x0d, fec - FEC_1_2); + return ves1x93_writereg (state, 0x0d, fec - FEC_1_2); } -static fe_code_rate_t ves1x93_get_fec (struct i2c_adapter *i2c) +static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state) { - return FEC_1_2 + ((ves1x93_readreg (i2c, 0x0d) >> 4) & 0x7); + return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7); } -static int ves1x93_set_symbolrate (struct i2c_adapter *i2c, u32 srate) +static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate) { u32 BDR; u32 ratio; u8 ADCONF, FCONF, FNR; u32 BDRI; u32 tmp; - u32 XIN, FIN; + u32 FIN; dprintk("%s: srate == %d\n", __FUNCTION__, (unsigned int) srate); - switch (board_type) { - case BOARD_SIEMENS_PCI: - XIN = 90100000UL; - break; - case BOARD_NOKIA_DBOX2: - if (demod_type == DEMOD_VES1893) - XIN = 91000000UL; - else if (demod_type == DEMOD_VES1993) - XIN = 96000000UL; - else - return -EINVAL; - break; - case BOARD_SAGEM_DBOX2: - XIN = 92160000UL; - break; - default: - return -EINVAL; - } - - if (srate > XIN/2) - srate = XIN/2; + if (srate > state->config->xin/2) + srate = state->config->xin/2; if (srate < 500000) srate = 500000; #define MUL (1UL<<26) - FIN = (XIN + 6000) >> 4; + FIN = (state->config->xin + 6000) >> 4; tmp = srate << 6; ratio = tmp / FIN; @@ -404,55 +245,87 @@ static int ves1x93_set_symbolrate (struc if (BDRI > 0xff) BDRI = 0xff; - ves1x93_writereg (i2c, 0x06, 0xff & BDR); - ves1x93_writereg (i2c, 0x07, 0xff & (BDR >> 8)); - ves1x93_writereg (i2c, 0x08, 0x0f & (BDR >> 16)); - - ves1x93_writereg (i2c, 0x09, BDRI); - ves1x93_writereg (i2c, 0x20, ADCONF); - ves1x93_writereg (i2c, 0x21, FCONF); + ves1x93_writereg (state, 0x06, 0xff & BDR); + ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8)); + ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16)); + + ves1x93_writereg (state, 0x09, BDRI); + ves1x93_writereg (state, 0x20, ADCONF); + ves1x93_writereg (state, 0x21, FCONF); if (srate < 6000000) - ves1x93_writereg (i2c, 0x05, init_1x93_tab[0x05] | 0x80); + ves1x93_writereg (state, 0x05, state->init_1x93_tab[0x05] | 0x80); else - ves1x93_writereg (i2c, 0x05, init_1x93_tab[0x05] & 0x7f); + ves1x93_writereg (state, 0x05, state->init_1x93_tab[0x05] & 0x7f); /* ves1993 hates this, will lose lock */ - if (demod_type != DEMOD_VES1993) - ves1x93_clr_bit (i2c); + if (state->demod_type != DEMOD_VES1993) + ves1x93_clr_bit (state); return 0; } -static int ves1x93_set_voltage (struct i2c_adapter *i2c, fe_sec_voltage_t voltage) + + + + + + + + + + + + + + +static int ves1x93_init (struct dvb_frontend* fe) { + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + int i; + int val; + + dprintk("%s: init chip\n", __FUNCTION__); + + for (i = 0; i < state->tab_size; i++) { + if (state->init_1x93_wtab[i]) { + val = state->init_1x93_tab[i]; + + if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */ + ves1x93_writereg (state, i, val); + } + } + + if (state->config->pll_init) { + ves1x93_writereg(state, 0x00, 0x11); + state->config->pll_init(fe); + ves1x93_writereg(state, 0x00, 0x01); + } + + return 0; +} + +static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +{ + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + switch (voltage) { case SEC_VOLTAGE_13: - return ves1x93_writereg (i2c, 0x1f, 0x20); + return ves1x93_writereg (state, 0x1f, 0x20); case SEC_VOLTAGE_18: - return ves1x93_writereg (i2c, 0x1f, 0x30); + return ves1x93_writereg (state, 0x1f, 0x30); case SEC_VOLTAGE_OFF: - return ves1x93_writereg (i2c, 0x1f, 0x00); + return ves1x93_writereg (state, 0x1f, 0x00); default: return -EINVAL; } } - -static int ves1x93_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status) { - struct ves1x93_state *state = (struct ves1x93_state*) fe->data; - struct i2c_adapter *i2c = state->i2c; - - switch (cmd) { - case FE_GET_INFO: - memcpy (arg, &ves1x93_info, sizeof(struct dvb_frontend_info)); - break; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - case FE_READ_STATUS: - { - fe_status_t *status = arg; - u8 sync = ves1x93_readreg (i2c, 0x0e); + u8 sync = ves1x93_readreg (state, 0x0e); /* * The ves1893 sometimes returns sync values that make no sense, @@ -466,7 +339,7 @@ static int ves1x93_ioctl (struct dvb_fro int maxtry = 10; /* just for safety - let's not get stuck here */ while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) { msleep(10); - sync = ves1x93_readreg (i2c, 0x0e); + sync = ves1x93_readreg (state, 0x0e); } *status = 0; @@ -486,64 +359,78 @@ static int ves1x93_ioctl (struct dvb_fro if ((sync & 0x1f) == 0x1f) *status |= FE_HAS_LOCK; - break; + return 0; } - case FE_READ_BER: + +static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber) { - u32 *ber = (u32 *) arg; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - *ber = ves1x93_readreg (i2c, 0x15); - *ber |= (ves1x93_readreg (i2c, 0x16) << 8); - *ber |= ((ves1x93_readreg (i2c, 0x17) & 0x0F) << 16); + *ber = ves1x93_readreg (state, 0x15); + *ber |= (ves1x93_readreg (state, 0x16) << 8); + *ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16); *ber *= 10; - break; + + return 0; } - case FE_READ_SIGNAL_STRENGTH: +static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - u8 signal = ~ves1x93_readreg (i2c, 0x0b); - *((u16*) arg) = (signal << 8) | signal; - break; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + + u8 signal = ~ves1x93_readreg (state, 0x0b); + *strength = (signal << 8) | signal; + + return 0; } - case FE_READ_SNR: +static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr) { - u8 snr = ~ves1x93_readreg (i2c, 0x1c); - *(u16*) arg = (snr << 8) | snr; - break; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + + u8 _snr = ~ves1x93_readreg (state, 0x1c); + *snr = (_snr << 8) | _snr; + + return 0; } - case FE_READ_UNCORRECTED_BLOCKS: +static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { - *(u32*) arg = ves1x93_readreg (i2c, 0x18) & 0x7f; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - if (*(u32*) arg == 0x7f) - *(u32*) arg = 0xffffffff; /* counter overflow... */ + *ucblocks = ves1x93_readreg (state, 0x18) & 0x7f; - ves1x93_writereg (i2c, 0x18, 0x00); /* reset the counter */ - ves1x93_writereg (i2c, 0x18, 0x80); /* dto. */ - break; + if (*ucblocks == 0x7f) + *ucblocks = 0xffffffff; /* counter overflow... */ + + ves1x93_writereg (state, 0x18, 0x00); /* reset the counter */ + ves1x93_writereg (state, 0x18, 0x80); /* dto. */ + + return 0; } - case FE_SET_FRONTEND: +static int ves1x93_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - tuner_set_tv_freq (i2c, p->frequency); - ves1x93_set_inversion (i2c, p->inversion); - ves1x93_set_fec (i2c, p->u.qpsk.fec_inner); - ves1x93_set_symbolrate (i2c, p->u.qpsk.symbol_rate); + ves1x93_writereg(state, 0x00, 0x11); + state->config->pll_set(fe, p); + ves1x93_writereg(state, 0x00, 0x01); + ves1x93_set_inversion (state, p->inversion); + ves1x93_set_fec (state, p->u.qpsk.fec_inner); + ves1x93_set_symbolrate (state, p->u.qpsk.symbol_rate); state->inversion = p->inversion; - break; + + return 0; } - case FE_GET_FRONTEND: +static int ves1x93_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { - struct dvb_frontend_parameters *p = arg; + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; int afc; - afc = ((int)((char)(ves1x93_readreg (i2c, 0x0a) << 1)))/2; + afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2; afc = (afc * (int)(p->u.qpsk.symbol_rate/1000/8))/16; p->frequency -= afc; @@ -553,185 +440,125 @@ static int ves1x93_ioctl (struct dvb_fro * if auto inversion was used */ if (state->inversion == INVERSION_AUTO) - p->inversion = (ves1x93_readreg (i2c, 0x0f) & 2) ? + p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ? INVERSION_OFF : INVERSION_ON; - p->u.qpsk.fec_inner = ves1x93_get_fec (i2c); + p->u.qpsk.fec_inner = ves1x93_get_fec (state); /* XXX FIXME: timing offset !! */ - break; - } - case FE_SLEEP: - if (board_type == BOARD_SIEMENS_PCI) - ves1x93_writereg (i2c, 0x1f, 0x00); /* LNB power off */ - return ves1x93_writereg (i2c, 0x00, 0x08); - - case FE_INIT: - return ves1x93_init (i2c); - - case FE_SET_TONE: - return -EOPNOTSUPP; /* the ves1893 can generate the 22k */ - /* let's implement this when we have */ - /* a box that uses the 22K_0 pin... */ + return 0; +} - case FE_SET_VOLTAGE: - return ves1x93_set_voltage (i2c, (fe_sec_voltage_t) arg); +static int ves1x93_sleep(struct dvb_frontend* fe) +{ + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; - default: - return -EOPNOTSUPP; - }; + return ves1x93_writereg (state, 0x00, 0x08); +} - return 0; +static void ves1x93_release(struct dvb_frontend* fe) +{ + struct ves1x93_state* state = (struct ves1x93_state*) fe->demodulator_priv; + kfree(state); } -static struct i2c_client client_template; +static struct dvb_frontend_ops ves1x93_ops; -static int attach_adapter(struct i2c_adapter *adapter) +struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c) { - struct i2c_client *client; - struct ves1x93_state* state; - u8 identity = ves1x93_readreg(adapter, 0x1e); - int ret; + struct ves1x93_state* state = NULL; + u8 identity; + /* allocate memory for the internal state */ + state = (struct ves1x93_state*) kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops)); + state->inversion = INVERSION_OFF; + + /* check if the demod is there + identify it */ + identity = ves1x93_readreg(state, 0x1e); switch (identity) { case 0xdc: /* VES1893A rev1 */ printk("ves1x93: Detected ves1893a rev1\n"); - demod_type = DEMOD_VES1893; - ves1x93_info.name[4] = '8'; + state->demod_type = DEMOD_VES1893; + state->init_1x93_tab = init_1893_tab; + state->init_1x93_wtab = init_1893_wtab; + state->tab_size = sizeof(init_1893_tab); break; + case 0xdd: /* VES1893A rev2 */ printk("ves1x93: Detected ves1893a rev2\n"); - demod_type = DEMOD_VES1893; - ves1x93_info.name[4] = '8'; + state->demod_type = DEMOD_VES1893; + state->init_1x93_tab = init_1893_tab; + state->init_1x93_wtab = init_1893_wtab; + state->tab_size = sizeof(init_1893_tab); break; + case 0xde: /* VES1993 */ printk("ves1x93: Detected ves1993\n"); - demod_type = DEMOD_VES1993; - ves1x93_info.name[4] = '9'; - break; - default: - dprintk("VES1x93 not found (identity %02x)\n", identity); - return -ENODEV; - } - - if ((state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL)) == NULL) { - return -ENOMEM; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) { - kfree(state); - return -ENOMEM; - } - - state->inversion = INVERSION_OFF; - state->i2c = adapter; - - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->adapter = adapter; - client->addr = (0x08>>1); - i2c_set_clientdata(client, (void*)state); - - ret = i2c_attach_client(client); - if (ret) { - kfree(client); - kfree(state); - return -EFAULT; - } - - BUG_ON(!state->dvb); - - ret = dvb_register_frontend(ves1x93_ioctl, state->dvb, state, - &ves1x93_info, THIS_MODULE); - if (ret) { - i2c_detach_client(client); - kfree(client); - kfree(state); - return -EFAULT; -} - - return 0; -} - -static int detach_client(struct i2c_client *client) -{ - struct ves1x93_state *state = (struct ves1x93_state*)i2c_get_clientdata(client); - dvb_unregister_frontend(ves1x93_ioctl, state->dvb); - i2c_detach_client(client); - BUG_ON(state->dvb); - kfree(client); - kfree(state); - return 0; -} - -static int command (struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct ves1x93_state *state = (struct ves1x93_state*)i2c_get_clientdata(client); - - dprintk ("%s\n", __FUNCTION__); - - switch (cmd) { - case FE_REGISTER: { - state->dvb = (struct dvb_adapter*)arg; + state->demod_type = DEMOD_VES1993; + state->init_1x93_tab = init_1993_tab; + state->init_1x93_wtab = init_1993_wtab; + state->tab_size = sizeof(init_1993_tab); break; - } - case FE_UNREGISTER: { - state->dvb = NULL; - break; - } - default: - return -EOPNOTSUPP; -} - return 0; -} - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "ves1x93", - .id = I2C_DRIVERID_DVBFE_VES1X93, - .flags = I2C_DF_NOTIFY, - .attach_adapter = attach_adapter, - .detach_client = detach_client, - .command = command, -}; -static struct i2c_client client_template = { - I2C_DEVNAME("ves1x93"), - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int __init init_ves1x93 (void) -{ - switch (board_type) { - case BOARD_NOKIA_DBOX2: - dprintk("%s: NOKIA_DBOX2\n", __FILE__); - break; - case BOARD_SAGEM_DBOX2: - dprintk("%s: SAGEM_DBOX2\n", __FILE__); - break; - case BOARD_SIEMENS_PCI: - dprintk("%s: SIEMENS_PCI\n", __FILE__); - break; default: - return -EIO; + goto error; } - return i2c_add_driver(&driver); -} - - -static void __exit exit_ves1x93 (void) -{ - if (i2c_del_driver(&driver)) - printk("vex1x93: driver deregistration failed\n"); -} + /* create dvb_frontend */ + state->frontend.ops = &state->ops; + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + if (state) kfree(state); + return NULL; + } + +static struct dvb_frontend_ops ves1x93_ops = { + + .info = { + .name = "VLSI VES1x93 DVB-S", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 125, /* kHz for QPSK frontends */ + .frequency_tolerance = 29500, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + /* .symbol_rate_tolerance = ???,*/ + .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 + }, + + .release = ves1x93_release, + + .init = ves1x93_init, + .sleep = ves1x93_sleep, + + .set_frontend = ves1x93_set_frontend, + .get_frontend = ves1x93_get_frontend, + + .read_status = ves1x93_read_status, + .read_ber = ves1x93_read_ber, + .read_signal_strength = ves1x93_read_signal_strength, + .read_snr = ves1x93_read_snr, + .read_ucblocks = ves1x93_read_ucblocks, -module_init(init_ves1x93); -module_exit(exit_ves1x93); + .set_voltage = ves1x93_set_voltage, +}; +module_param(debug, int, 0644); -MODULE_DESCRIPTION("VES1x93 DVB-S Frontend"); +MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver"); MODULE_AUTHOR("Ralph Metzler"); MODULE_LICENSE("GPL"); -MODULE_PARM(debug,"i"); -MODULE_PARM(board_type,"i"); +EXPORT_SYMBOL(ves1x93_attach); diff -puN /dev/null drivers/media/dvb/frontends/ves1x93.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/media/dvb/frontends/ves1x93.h 2004-11-21 17:13:23.513043856 -0800 @@ -0,0 +1,50 @@ +/* + Driver for VES1893 and VES1993 QPSK Demodulators + + Copyright (C) 1999 Convergence Integrated Media GmbH + Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> + Copyright (C) 2002 Dennis Noermann + Copyright (C) 2002-2003 Andreas Oberritter + + 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. + +*/ + +#ifndef VES1X93_H +#define VES1X93_H + +#include + +struct ves1x93_config +{ + /* the demodulator's i2c address */ + u8 demod_address; + + /* value of XIN to use */ + u32 xin; + + /* should PWM be inverted? */ + u8 invert_pwm:1; + + /* PLL maintenance */ + int (*pll_init)(struct dvb_frontend* fe); + int (*pll_set)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params); +}; + +extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, + struct i2c_adapter* i2c); + +#endif // VES1X93_H _