/*
- * Support for LGDT3305 - VSB/QAM
+ * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM
*
* Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org>
*
case QAM_256:
agcdelay = 0x046b;
rfbw = 0x8889;
- ifbw = 0x8888;
+ if (state->cfg->demod_chip == LGDT3305)
+ ifbw = 0x8888;
+ else
+ ifbw = 0x6666;
break;
default:
return -EINVAL;
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);
}
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;
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;
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)
{
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,
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;
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",
.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 <mkrufky@linuxtv.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");