diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c index d69c775f8645419116f13befc0d4476cc2629016..d329d6abef8ad59b9986236ea8f09804d78dd7f7 100644 --- a/drivers/media/dvb/frontends/lgdt3305.c +++ b/drivers/media/dvb/frontends/lgdt3305.c @@ -1,5 +1,5 @@ /* - * Support for LGDT3305 - VSB/QAM + * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM * * Copyright (C) 2008, 2009 Michael Krufky * @@ -358,7 +358,10 @@ static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, case QAM_256: agcdelay = 0x046b; rfbw = 0x8889; - ifbw = 0x8888; + if (state->cfg->demod_chip == LGDT3305) + ifbw = 0x8888; + else + ifbw = 0x6666; break; default: return -EINVAL; @@ -410,8 +413,18 @@ static int lgdt3305_agc_setup(struct lgdt3305_state *state, lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); /* control agc function */ - lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); - lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); + switch (state->cfg->demod_chip) { + case LGDT3304: + lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); + lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); + break; + case LGDT3305: + lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); + lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); + break; + default: + return -EINVAL; + } return lgdt3305_rfagc_loop(state, param); } @@ -544,6 +557,11 @@ static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) enable ? 0 : 1); } +static int lgdt3304_sleep(struct dvb_frontend *fe) +{ + return 0; +} + static int lgdt3305_sleep(struct dvb_frontend *fe) { struct lgdt3305_state *state = fe->demodulator_priv; @@ -572,6 +590,55 @@ static int lgdt3305_sleep(struct dvb_frontend *fe) return 0; } +static int lgdt3304_init(struct dvb_frontend *fe) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + int ret; + + static struct lgdt3305_reg lgdt3304_init_data[] = { + { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, + { .reg = 0x000d, .val = 0x02, }, + { .reg = 0x000e, .val = 0x02, }, + { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, + { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, + { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, + { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, + { .reg = 0x0112, .val = 0x17, }, + { .reg = 0x0113, .val = 0x15, }, + { .reg = 0x0114, .val = 0x18, }, + { .reg = 0x0115, .val = 0xff, }, + { .reg = 0x0116, .val = 0x3c, }, + { .reg = 0x0214, .val = 0x67, }, + { .reg = 0x0424, .val = 0x8d, }, + { .reg = 0x0427, .val = 0x12, }, + { .reg = 0x0428, .val = 0x4f, }, + { .reg = LGDT3305_IFBW_1, .val = 0x80, }, + { .reg = LGDT3305_IFBW_2, .val = 0x00, }, + { .reg = 0x030a, .val = 0x08, }, + { .reg = 0x030b, .val = 0x9b, }, + { .reg = 0x030d, .val = 0x00, }, + { .reg = 0x030e, .val = 0x1c, }, + { .reg = 0x0314, .val = 0xe1, }, + { .reg = 0x000d, .val = 0x82, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, + }; + + lg_dbg("\n"); + + ret = lgdt3305_write_regs(state, lgdt3304_init_data, + ARRAY_SIZE(lgdt3304_init_data)); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_soft_reset(state); +fail: + return ret; +} + static int lgdt3305_init(struct dvb_frontend *fe) { struct lgdt3305_state *state = fe->demodulator_priv; @@ -640,6 +707,76 @@ static int lgdt3305_init(struct dvb_frontend *fe) return ret; } +static int lgdt3304_set_parameters(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("(%d, %d)\n", param->frequency, param->u.vsb.modulation); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe, param); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + if (lg_fail(ret)) + goto fail; + state->current_frequency = param->frequency; + } + + ret = lgdt3305_set_modulation(state, param); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_passband_digital_agc(state, param); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_agc_setup(state, param); + if (lg_fail(ret)) + goto fail; + + /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ + switch (param->u.vsb.modulation) { + case VSB_8: + lgdt3305_write_reg(state, 0x030d, 0x00); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); + break; + case QAM_64: + case QAM_256: + lgdt3305_write_reg(state, 0x030d, 0x14); + ret = lgdt3305_set_if(state, param); + if (lg_fail(ret)) + goto fail; + break; + default: + return -EINVAL; + } + + + ret = lgdt3305_spectral_inversion(state, param, + state->cfg->spectral_inversion + ? 1 : 0); + if (lg_fail(ret)) + goto fail; + + state->current_modulation = param->u.vsb.modulation; + + ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); + if (lg_fail(ret)) + goto fail; + + /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ + ret = lgdt3305_mpeg_mode_polarity(state, + state->cfg->tpclk_edge, + state->cfg->tpvalid_polarity); +fail: + return ret; +} + static int lgdt3305_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { @@ -993,6 +1130,7 @@ static void lgdt3305_release(struct dvb_frontend *fe) kfree(state); } +static struct dvb_frontend_ops lgdt3304_ops; static struct dvb_frontend_ops lgdt3305_ops; struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, @@ -1013,11 +1151,21 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, state->cfg = config; state->i2c_adap = i2c_adap; - memcpy(&state->frontend.ops, &lgdt3305_ops, - sizeof(struct dvb_frontend_ops)); + switch (config->demod_chip) { + case LGDT3304: + memcpy(&state->frontend.ops, &lgdt3304_ops, + sizeof(struct dvb_frontend_ops)); + break; + case LGDT3305: + memcpy(&state->frontend.ops, &lgdt3305_ops, + sizeof(struct dvb_frontend_ops)); + break; + default: + goto fail; + } state->frontend.demodulator_priv = state; - /* verify that we're talking to a lg dt3305 */ + /* verify that we're talking to a lg dt3304/5 */ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); if ((lg_fail(ret)) | (val == 0)) goto fail; @@ -1036,12 +1184,36 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, return &state->frontend; fail: - lg_warn("unable to detect LGDT3305 hardware\n"); + lg_warn("unable to detect %s hardware\n", + config->demod_chip ? "LGDT3304" : "LGDT3305"); kfree(state); return NULL; } EXPORT_SYMBOL(lgdt3305_attach); +static struct dvb_frontend_ops lgdt3304_ops = { + .info = { + .name = "LG Electronics LGDT3304 VSB/QAM Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, + .init = lgdt3304_init, + .sleep = lgdt3304_sleep, + .set_frontend = lgdt3304_set_parameters, + .get_frontend = lgdt3305_get_frontend, + .get_tune_settings = lgdt3305_get_tune_settings, + .read_status = lgdt3305_read_status, + .read_ber = lgdt3305_read_ber, + .read_signal_strength = lgdt3305_read_signal_strength, + .read_snr = lgdt3305_read_snr, + .read_ucblocks = lgdt3305_read_ucblocks, + .release = lgdt3305_release, +}; + static struct dvb_frontend_ops lgdt3305_ops = { .info = { .name = "LG Electronics LGDT3305 VSB/QAM Frontend", @@ -1065,7 +1237,7 @@ static struct dvb_frontend_ops lgdt3305_ops = { .release = lgdt3305_release, }; -MODULE_DESCRIPTION("LG Electronics LGDT3305 ATSC/QAM-B Demodulator Driver"); +MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h index 9cb11c9cae53eb4ad097165b9065e589bc5a3325..a7f30c2ed14e8315899a615449cb01733805a0a7 100644 --- a/drivers/media/dvb/frontends/lgdt3305.h +++ b/drivers/media/dvb/frontends/lgdt3305.h @@ -41,6 +41,11 @@ enum lgdt3305_tp_valid_polarity { LGDT3305_TP_VALID_HIGH = 1, }; +enum lgdt_demod_chip_type { + LGDT3305 = 0, + LGDT3304 = 1, +}; + struct lgdt3305_config { u8 i2c_addr; @@ -65,6 +70,7 @@ struct lgdt3305_config { enum lgdt3305_mpeg_mode mpeg_mode; enum lgdt3305_tp_clock_edge tpclk_edge; enum lgdt3305_tp_valid_polarity tpvalid_polarity; + enum lgdt_demod_chip_type demod_chip; }; #if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \