diff options
-rw-r--r-- | drivers/media/dvb/frontends/lgdt3305.c | 190 | ||||
-rw-r--r-- | drivers/media/dvb/frontends/lgdt3305.h | 6 |
2 files changed, 187 insertions, 9 deletions
diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c index d69c775f864..d329d6abef8 100644 --- a/drivers/media/dvb/frontends/lgdt3305.c +++ b/drivers/media/dvb/frontends/lgdt3305.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Support for LGDT3305 - VSB/QAM | 2 | * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM |
3 | * | 3 | * |
4 | * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org> | 4 | * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org> |
5 | * | 5 | * |
@@ -358,7 +358,10 @@ static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, | |||
358 | case QAM_256: | 358 | case QAM_256: |
359 | agcdelay = 0x046b; | 359 | agcdelay = 0x046b; |
360 | rfbw = 0x8889; | 360 | rfbw = 0x8889; |
361 | ifbw = 0x8888; | 361 | if (state->cfg->demod_chip == LGDT3305) |
362 | ifbw = 0x8888; | ||
363 | else | ||
364 | ifbw = 0x6666; | ||
362 | break; | 365 | break; |
363 | default: | 366 | default: |
364 | return -EINVAL; | 367 | return -EINVAL; |
@@ -410,8 +413,18 @@ static int lgdt3305_agc_setup(struct lgdt3305_state *state, | |||
410 | lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); | 413 | lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); |
411 | 414 | ||
412 | /* control agc function */ | 415 | /* control agc function */ |
413 | lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); | 416 | switch (state->cfg->demod_chip) { |
414 | lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); | 417 | case LGDT3304: |
418 | lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); | ||
419 | lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); | ||
420 | break; | ||
421 | case LGDT3305: | ||
422 | lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); | ||
423 | lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); | ||
424 | break; | ||
425 | default: | ||
426 | return -EINVAL; | ||
427 | } | ||
415 | 428 | ||
416 | return lgdt3305_rfagc_loop(state, param); | 429 | return lgdt3305_rfagc_loop(state, param); |
417 | } | 430 | } |
@@ -544,6 +557,11 @@ static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) | |||
544 | enable ? 0 : 1); | 557 | enable ? 0 : 1); |
545 | } | 558 | } |
546 | 559 | ||
560 | static int lgdt3304_sleep(struct dvb_frontend *fe) | ||
561 | { | ||
562 | return 0; | ||
563 | } | ||
564 | |||
547 | static int lgdt3305_sleep(struct dvb_frontend *fe) | 565 | static int lgdt3305_sleep(struct dvb_frontend *fe) |
548 | { | 566 | { |
549 | struct lgdt3305_state *state = fe->demodulator_priv; | 567 | struct lgdt3305_state *state = fe->demodulator_priv; |
@@ -572,6 +590,55 @@ static int lgdt3305_sleep(struct dvb_frontend *fe) | |||
572 | return 0; | 590 | return 0; |
573 | } | 591 | } |
574 | 592 | ||
593 | static int lgdt3304_init(struct dvb_frontend *fe) | ||
594 | { | ||
595 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
596 | int ret; | ||
597 | |||
598 | static struct lgdt3305_reg lgdt3304_init_data[] = { | ||
599 | { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, | ||
600 | { .reg = 0x000d, .val = 0x02, }, | ||
601 | { .reg = 0x000e, .val = 0x02, }, | ||
602 | { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, | ||
603 | { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, | ||
604 | { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, | ||
605 | { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, | ||
606 | { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, | ||
607 | { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, | ||
608 | { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, | ||
609 | { .reg = 0x0112, .val = 0x17, }, | ||
610 | { .reg = 0x0113, .val = 0x15, }, | ||
611 | { .reg = 0x0114, .val = 0x18, }, | ||
612 | { .reg = 0x0115, .val = 0xff, }, | ||
613 | { .reg = 0x0116, .val = 0x3c, }, | ||
614 | { .reg = 0x0214, .val = 0x67, }, | ||
615 | { .reg = 0x0424, .val = 0x8d, }, | ||
616 | { .reg = 0x0427, .val = 0x12, }, | ||
617 | { .reg = 0x0428, .val = 0x4f, }, | ||
618 | { .reg = LGDT3305_IFBW_1, .val = 0x80, }, | ||
619 | { .reg = LGDT3305_IFBW_2, .val = 0x00, }, | ||
620 | { .reg = 0x030a, .val = 0x08, }, | ||
621 | { .reg = 0x030b, .val = 0x9b, }, | ||
622 | { .reg = 0x030d, .val = 0x00, }, | ||
623 | { .reg = 0x030e, .val = 0x1c, }, | ||
624 | { .reg = 0x0314, .val = 0xe1, }, | ||
625 | { .reg = 0x000d, .val = 0x82, }, | ||
626 | { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, | ||
627 | { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, | ||
628 | }; | ||
629 | |||
630 | lg_dbg("\n"); | ||
631 | |||
632 | ret = lgdt3305_write_regs(state, lgdt3304_init_data, | ||
633 | ARRAY_SIZE(lgdt3304_init_data)); | ||
634 | if (lg_fail(ret)) | ||
635 | goto fail; | ||
636 | |||
637 | ret = lgdt3305_soft_reset(state); | ||
638 | fail: | ||
639 | return ret; | ||
640 | } | ||
641 | |||
575 | static int lgdt3305_init(struct dvb_frontend *fe) | 642 | static int lgdt3305_init(struct dvb_frontend *fe) |
576 | { | 643 | { |
577 | struct lgdt3305_state *state = fe->demodulator_priv; | 644 | struct lgdt3305_state *state = fe->demodulator_priv; |
@@ -640,6 +707,76 @@ fail: | |||
640 | return ret; | 707 | return ret; |
641 | } | 708 | } |
642 | 709 | ||
710 | static int lgdt3304_set_parameters(struct dvb_frontend *fe, | ||
711 | struct dvb_frontend_parameters *param) | ||
712 | { | ||
713 | struct lgdt3305_state *state = fe->demodulator_priv; | ||
714 | int ret; | ||
715 | |||
716 | lg_dbg("(%d, %d)\n", param->frequency, param->u.vsb.modulation); | ||
717 | |||
718 | if (fe->ops.tuner_ops.set_params) { | ||
719 | ret = fe->ops.tuner_ops.set_params(fe, param); | ||
720 | if (fe->ops.i2c_gate_ctrl) | ||
721 | fe->ops.i2c_gate_ctrl(fe, 0); | ||
722 | if (lg_fail(ret)) | ||
723 | goto fail; | ||
724 | state->current_frequency = param->frequency; | ||
725 | } | ||
726 | |||
727 | ret = lgdt3305_set_modulation(state, param); | ||
728 | if (lg_fail(ret)) | ||
729 | goto fail; | ||
730 | |||
731 | ret = lgdt3305_passband_digital_agc(state, param); | ||
732 | if (lg_fail(ret)) | ||
733 | goto fail; | ||
734 | |||
735 | ret = lgdt3305_agc_setup(state, param); | ||
736 | if (lg_fail(ret)) | ||
737 | goto fail; | ||
738 | |||
739 | /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ | ||
740 | switch (param->u.vsb.modulation) { | ||
741 | case VSB_8: | ||
742 | lgdt3305_write_reg(state, 0x030d, 0x00); | ||
743 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); | ||
744 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); | ||
745 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); | ||
746 | lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); | ||
747 | break; | ||
748 | case QAM_64: | ||
749 | case QAM_256: | ||
750 | lgdt3305_write_reg(state, 0x030d, 0x14); | ||
751 | ret = lgdt3305_set_if(state, param); | ||
752 | if (lg_fail(ret)) | ||
753 | goto fail; | ||
754 | break; | ||
755 | default: | ||
756 | return -EINVAL; | ||
757 | } | ||
758 | |||
759 | |||
760 | ret = lgdt3305_spectral_inversion(state, param, | ||
761 | state->cfg->spectral_inversion | ||
762 | ? 1 : 0); | ||
763 | if (lg_fail(ret)) | ||
764 | goto fail; | ||
765 | |||
766 | state->current_modulation = param->u.vsb.modulation; | ||
767 | |||
768 | ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); | ||
769 | if (lg_fail(ret)) | ||
770 | goto fail; | ||
771 | |||
772 | /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ | ||
773 | ret = lgdt3305_mpeg_mode_polarity(state, | ||
774 | state->cfg->tpclk_edge, | ||
775 | state->cfg->tpvalid_polarity); | ||
776 | fail: | ||
777 | return ret; | ||
778 | } | ||
779 | |||
643 | static int lgdt3305_set_parameters(struct dvb_frontend *fe, | 780 | static int lgdt3305_set_parameters(struct dvb_frontend *fe, |
644 | struct dvb_frontend_parameters *param) | 781 | struct dvb_frontend_parameters *param) |
645 | { | 782 | { |
@@ -993,6 +1130,7 @@ static void lgdt3305_release(struct dvb_frontend *fe) | |||
993 | kfree(state); | 1130 | kfree(state); |
994 | } | 1131 | } |
995 | 1132 | ||
1133 | static struct dvb_frontend_ops lgdt3304_ops; | ||
996 | static struct dvb_frontend_ops lgdt3305_ops; | 1134 | static struct dvb_frontend_ops lgdt3305_ops; |
997 | 1135 | ||
998 | struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, | 1136 | struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, |
@@ -1013,11 +1151,21 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, | |||
1013 | state->cfg = config; | 1151 | state->cfg = config; |
1014 | state->i2c_adap = i2c_adap; | 1152 | state->i2c_adap = i2c_adap; |
1015 | 1153 | ||
1016 | memcpy(&state->frontend.ops, &lgdt3305_ops, | 1154 | switch (config->demod_chip) { |
1017 | sizeof(struct dvb_frontend_ops)); | 1155 | case LGDT3304: |
1156 | memcpy(&state->frontend.ops, &lgdt3304_ops, | ||
1157 | sizeof(struct dvb_frontend_ops)); | ||
1158 | break; | ||
1159 | case LGDT3305: | ||
1160 | memcpy(&state->frontend.ops, &lgdt3305_ops, | ||
1161 | sizeof(struct dvb_frontend_ops)); | ||
1162 | break; | ||
1163 | default: | ||
1164 | goto fail; | ||
1165 | } | ||
1018 | state->frontend.demodulator_priv = state; | 1166 | state->frontend.demodulator_priv = state; |
1019 | 1167 | ||
1020 | /* verify that we're talking to a lg dt3305 */ | 1168 | /* verify that we're talking to a lg dt3304/5 */ |
1021 | ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); | 1169 | ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); |
1022 | if ((lg_fail(ret)) | (val == 0)) | 1170 | if ((lg_fail(ret)) | (val == 0)) |
1023 | goto fail; | 1171 | goto fail; |
@@ -1036,12 +1184,36 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, | |||
1036 | 1184 | ||
1037 | return &state->frontend; | 1185 | return &state->frontend; |
1038 | fail: | 1186 | fail: |
1039 | lg_warn("unable to detect LGDT3305 hardware\n"); | 1187 | lg_warn("unable to detect %s hardware\n", |
1188 | config->demod_chip ? "LGDT3304" : "LGDT3305"); | ||
1040 | kfree(state); | 1189 | kfree(state); |
1041 | return NULL; | 1190 | return NULL; |
1042 | } | 1191 | } |
1043 | EXPORT_SYMBOL(lgdt3305_attach); | 1192 | EXPORT_SYMBOL(lgdt3305_attach); |
1044 | 1193 | ||
1194 | static struct dvb_frontend_ops lgdt3304_ops = { | ||
1195 | .info = { | ||
1196 | .name = "LG Electronics LGDT3304 VSB/QAM Frontend", | ||
1197 | .type = FE_ATSC, | ||
1198 | .frequency_min = 54000000, | ||
1199 | .frequency_max = 858000000, | ||
1200 | .frequency_stepsize = 62500, | ||
1201 | .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB | ||
1202 | }, | ||
1203 | .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, | ||
1204 | .init = lgdt3304_init, | ||
1205 | .sleep = lgdt3304_sleep, | ||
1206 | .set_frontend = lgdt3304_set_parameters, | ||
1207 | .get_frontend = lgdt3305_get_frontend, | ||
1208 | .get_tune_settings = lgdt3305_get_tune_settings, | ||
1209 | .read_status = lgdt3305_read_status, | ||
1210 | .read_ber = lgdt3305_read_ber, | ||
1211 | .read_signal_strength = lgdt3305_read_signal_strength, | ||
1212 | .read_snr = lgdt3305_read_snr, | ||
1213 | .read_ucblocks = lgdt3305_read_ucblocks, | ||
1214 | .release = lgdt3305_release, | ||
1215 | }; | ||
1216 | |||
1045 | static struct dvb_frontend_ops lgdt3305_ops = { | 1217 | static struct dvb_frontend_ops lgdt3305_ops = { |
1046 | .info = { | 1218 | .info = { |
1047 | .name = "LG Electronics LGDT3305 VSB/QAM Frontend", | 1219 | .name = "LG Electronics LGDT3305 VSB/QAM Frontend", |
@@ -1065,7 +1237,7 @@ static struct dvb_frontend_ops lgdt3305_ops = { | |||
1065 | .release = lgdt3305_release, | 1237 | .release = lgdt3305_release, |
1066 | }; | 1238 | }; |
1067 | 1239 | ||
1068 | MODULE_DESCRIPTION("LG Electronics LGDT3305 ATSC/QAM-B Demodulator Driver"); | 1240 | MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); |
1069 | MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); | 1241 | MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); |
1070 | MODULE_LICENSE("GPL"); | 1242 | MODULE_LICENSE("GPL"); |
1071 | MODULE_VERSION("0.1"); | 1243 | MODULE_VERSION("0.1"); |
diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h index 9cb11c9cae5..a7f30c2ed14 100644 --- a/drivers/media/dvb/frontends/lgdt3305.h +++ b/drivers/media/dvb/frontends/lgdt3305.h | |||
@@ -41,6 +41,11 @@ enum lgdt3305_tp_valid_polarity { | |||
41 | LGDT3305_TP_VALID_HIGH = 1, | 41 | LGDT3305_TP_VALID_HIGH = 1, |
42 | }; | 42 | }; |
43 | 43 | ||
44 | enum lgdt_demod_chip_type { | ||
45 | LGDT3305 = 0, | ||
46 | LGDT3304 = 1, | ||
47 | }; | ||
48 | |||
44 | struct lgdt3305_config { | 49 | struct lgdt3305_config { |
45 | u8 i2c_addr; | 50 | u8 i2c_addr; |
46 | 51 | ||
@@ -65,6 +70,7 @@ struct lgdt3305_config { | |||
65 | enum lgdt3305_mpeg_mode mpeg_mode; | 70 | enum lgdt3305_mpeg_mode mpeg_mode; |
66 | enum lgdt3305_tp_clock_edge tpclk_edge; | 71 | enum lgdt3305_tp_clock_edge tpclk_edge; |
67 | enum lgdt3305_tp_valid_polarity tpvalid_polarity; | 72 | enum lgdt3305_tp_valid_polarity tpvalid_polarity; |
73 | enum lgdt_demod_chip_type demod_chip; | ||
68 | }; | 74 | }; |
69 | 75 | ||
70 | #if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \ | 76 | #if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \ |