// SPDX-License-Identifier: GPL-2.0-or-later /* * Support for LGDT3306A - 8VSB/QAM-B * * Copyright (C) 2013 Fred Richter <frichter@hauppauge.com> * - driver structure based on lgdt3305.[ch] by Michael Krufky * - code based on LG3306_V0.35 API by LG Electronics Inc.
*/
/* * Older drivers treated QAM64 and QAM256 the same; that is the HW always * used "Auto" mode during detection. Setting "forced_manual"=1 allows * the user to treat these modes as separate. For backwards compatibility, * it's off by default. QAM_AUTO can now be specified to achive that * effect even if "forced_manual"=1
*/ staticint forced_manual;
module_param(forced_manual, int, 0644);
MODULE_PARM_DESC(forced_manual, "if set, QAM64 and QAM256 will only lock to modulation specified");
#define DBG_INFO 1 #define DBG_REG 2 #define DBG_DUMP 4 /* FGR - comment out to remove dump code */
/* * LG3306A Register Usage * (LG does not really name the registers, so this code does not either) * * 0000 -> 00FF Common control and status * 1000 -> 10FF Synchronizer control and status * 1F00 -> 1FFF Smart Antenna control and status * 2100 -> 21FF VSB Equalizer control and status * 2800 -> 28FF QAM Equalizer control and status * 3000 -> 30FF FEC control and status
*/
staticint lgdt3306a_set_vsb(struct lgdt3306a_state *state)
{
u8 val; int ret;
dbg_info("\n");
/* 0. Spectrum inversion detection manual; spectrum inverted */
ret = lgdt3306a_read_reg(state, 0x0002, &val);
val &= 0xf7; /* SPECINVAUTO Off */
val |= 0x04; /* SPECINV On */
ret = lgdt3306a_write_reg(state, 0x0002, val); if (lg_chkerr(ret)) goto fail;
/* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */
ret = lgdt3306a_write_reg(state, 0x0008, 0x80); if (lg_chkerr(ret)) goto fail;
/* 2. Bandwidth mode for VSB(6MHz) */
ret = lgdt3306a_read_reg(state, 0x0009, &val);
val &= 0xe3;
val |= 0x0c; /* STDOPDETTMODE[2:0]=3 */
ret = lgdt3306a_write_reg(state, 0x0009, val); if (lg_chkerr(ret)) goto fail;
/* 3. QAM mode detection mode(None) */
ret = lgdt3306a_read_reg(state, 0x0009, &val);
val &= 0xfc; /* STDOPDETCMODE[1:0]=0 */
ret = lgdt3306a_write_reg(state, 0x0009, val); if (lg_chkerr(ret)) goto fail;
/* 4. ADC sampling frequency rate(2x sampling) */
ret = lgdt3306a_read_reg(state, 0x000d, &val);
val &= 0xbf; /* SAMPLING4XFEN=0 */
ret = lgdt3306a_write_reg(state, 0x000d, val); if (lg_chkerr(ret)) goto fail;
#if 0 /* FGR - disable any AICC filtering, testing only */
ret = lgdt3306a_write_reg(state, 0x0024, 0x00); if (lg_chkerr(ret)) goto fail;
/* AICCFIXFREQ0 NT N-1(Video rejection) */
ret = lgdt3306a_write_reg(state, 0x002e, 0x00);
ret = lgdt3306a_write_reg(state, 0x002f, 0x00);
ret = lgdt3306a_write_reg(state, 0x0030, 0x00);
/* AICCFIXFREQ1 NT N-1(Audio rejection) */
ret = lgdt3306a_write_reg(state, 0x002b, 0x00);
ret = lgdt3306a_write_reg(state, 0x002c, 0x00);
ret = lgdt3306a_write_reg(state, 0x002d, 0x00);
/* AICCFIXFREQ2 NT Co-Channel(Video rejection) */
ret = lgdt3306a_write_reg(state, 0x0028, 0x00);
ret = lgdt3306a_write_reg(state, 0x0029, 0x00);
ret = lgdt3306a_write_reg(state, 0x002a, 0x00);
/* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */
ret = lgdt3306a_write_reg(state, 0x0025, 0x00);
ret = lgdt3306a_write_reg(state, 0x0026, 0x00);
ret = lgdt3306a_write_reg(state, 0x0027, 0x00);
#else /* FGR - this works well for HVR-1955,1975 */
/* 5. AICCOPMODE NT N-1 Adj. */
ret = lgdt3306a_write_reg(state, 0x0024, 0x5A); if (lg_chkerr(ret)) goto fail;
/* AICCFIXFREQ0 NT N-1(Video rejection) */
ret = lgdt3306a_write_reg(state, 0x002e, 0x5A);
ret = lgdt3306a_write_reg(state, 0x002f, 0x00);
ret = lgdt3306a_write_reg(state, 0x0030, 0x00);
/* AICCFIXFREQ1 NT N-1(Audio rejection) */
ret = lgdt3306a_write_reg(state, 0x002b, 0x36);
ret = lgdt3306a_write_reg(state, 0x002c, 0x00);
ret = lgdt3306a_write_reg(state, 0x002d, 0x00);
/* AICCFIXFREQ2 NT Co-Channel(Video rejection) */
ret = lgdt3306a_write_reg(state, 0x0028, 0x2A);
ret = lgdt3306a_write_reg(state, 0x0029, 0x00);
ret = lgdt3306a_write_reg(state, 0x002a, 0x00);
/* AICCFIXFREQ3 NT Co-Channel(Audio rejection) */
ret = lgdt3306a_write_reg(state, 0x0025, 0x06);
ret = lgdt3306a_write_reg(state, 0x0026, 0x00);
ret = lgdt3306a_write_reg(state, 0x0027, 0x00); #endif
ret = lgdt3306a_read_reg(state, 0x001e, &val);
val &= 0x0f;
val |= 0xa0;
ret = lgdt3306a_write_reg(state, 0x001e, val);
ret = lgdt3306a_write_reg(state, 0x0022, 0x08);
ret = lgdt3306a_write_reg(state, 0x0023, 0xFF);
ret = lgdt3306a_read_reg(state, 0x211f, &val);
val &= 0xef;
ret = lgdt3306a_write_reg(state, 0x211f, val);
ret = lgdt3306a_write_reg(state, 0x2173, 0x01);
ret = lgdt3306a_read_reg(state, 0x1061, &val);
val &= 0xf8;
val |= 0x04;
ret = lgdt3306a_write_reg(state, 0x1061, val);
ret = lgdt3306a_read_reg(state, 0x103d, &val);
val &= 0xcf;
ret = lgdt3306a_write_reg(state, 0x103d, val);
ret = lgdt3306a_write_reg(state, 0x2122, 0x40);
ret = lgdt3306a_read_reg(state, 0x2141, &val);
val &= 0x3f;
ret = lgdt3306a_write_reg(state, 0x2141, val);
ret = lgdt3306a_read_reg(state, 0x2135, &val);
val &= 0x0f;
val |= 0x70;
ret = lgdt3306a_write_reg(state, 0x2135, val);
ret = lgdt3306a_read_reg(state, 0x0003, &val);
val &= 0xf7;
ret = lgdt3306a_write_reg(state, 0x0003, val);
ret = lgdt3306a_read_reg(state, 0x001c, &val);
val &= 0x7f;
ret = lgdt3306a_write_reg(state, 0x001c, val);
/* 6. EQ step size */
ret = lgdt3306a_read_reg(state, 0x2179, &val);
val &= 0xf8;
ret = lgdt3306a_write_reg(state, 0x2179, val);
ret = lgdt3306a_read_reg(state, 0x217a, &val);
val &= 0xf8;
ret = lgdt3306a_write_reg(state, 0x217a, val);
/* 7. Reset */
ret = lgdt3306a_soft_reset(state); if (lg_chkerr(ret)) goto fail;
dbg_info("complete\n");
fail: return ret;
}
staticint lgdt3306a_set_qam(struct lgdt3306a_state *state, int modulation)
{
u8 val; int ret;
dbg_info("modulation=%d\n", modulation);
/* 1. Selection of standard mode(0x08=QAM, 0x80=VSB) */
ret = lgdt3306a_write_reg(state, 0x0008, 0x08); if (lg_chkerr(ret)) goto fail;
/* 1a. Spectrum inversion detection to Auto */
ret = lgdt3306a_read_reg(state, 0x0002, &val);
val &= 0xfb; /* SPECINV Off */
val |= 0x08; /* SPECINVAUTO On */
ret = lgdt3306a_write_reg(state, 0x0002, val); if (lg_chkerr(ret)) goto fail;
/* 2. Bandwidth mode for QAM */
ret = lgdt3306a_read_reg(state, 0x0009, &val);
val &= 0xe3; /* STDOPDETTMODE[2:0]=0 VSB Off */
ret = lgdt3306a_write_reg(state, 0x0009, val); if (lg_chkerr(ret)) goto fail;
/* 3. : 64QAM/256QAM detection(manual, auto) */
ret = lgdt3306a_read_reg(state, 0x0009, &val);
val &= 0xfc; /* Check for forced Manual modulation modes; otherwise always "auto" */ if(forced_manual && (modulation != QAM_AUTO)){
val |= 0x01; /* STDOPDETCMODE[1:0]= 1=Manual */
} else {
val |= 0x02; /* STDOPDETCMODE[1:0]= 2=Auto */
}
ret = lgdt3306a_write_reg(state, 0x0009, val); if (lg_chkerr(ret)) goto fail;
/* 3a. : 64QAM/256QAM selection for manual */
ret = lgdt3306a_read_reg(state, 0x101a, &val);
val &= 0xf8; if (modulation == QAM_64)
val |= 0x02; /* QMDQMODE[2:0]=2=QAM64 */ else
val |= 0x04; /* QMDQMODE[2:0]=4=QAM256 */
ret = lgdt3306a_write_reg(state, 0x101a, val); if (lg_chkerr(ret)) goto fail;
/* 4. ADC sampling frequency rate(4x sampling) */
ret = lgdt3306a_read_reg(state, 0x000d, &val);
val &= 0xbf;
val |= 0x40; /* SAMPLING4XFEN=1 */
ret = lgdt3306a_write_reg(state, 0x000d, val); if (lg_chkerr(ret)) goto fail;
/* 5. No AICC operation in QAM mode */
ret = lgdt3306a_read_reg(state, 0x0024, &val);
val &= 0x00;
ret = lgdt3306a_write_reg(state, 0x0024, val); if (lg_chkerr(ret)) goto fail;
/* 5.1 V0.36 SRDCHKALWAYS : For better QAM detection */
ret = lgdt3306a_read_reg(state, 0x000a, &val);
val &= 0xfd;
val |= 0x02;
ret = lgdt3306a_write_reg(state, 0x000a, val); if (lg_chkerr(ret)) goto fail;
/* 5.2 V0.36 Control of "no signal" detector function */
ret = lgdt3306a_read_reg(state, 0x2849, &val);
val &= 0xdf;
ret = lgdt3306a_write_reg(state, 0x2849, val); if (lg_chkerr(ret)) goto fail;
/* 5.3 Fix for Blonder Tongue HDE-2H-QAM and AQM modulators */
ret = lgdt3306a_read_reg(state, 0x302b, &val);
val &= 0x7f; /* SELFSYNCFINDEN_CQS=0; disable auto reset */
ret = lgdt3306a_write_reg(state, 0x302b, val); if (lg_chkerr(ret)) goto fail;
/* 6. Reset */
ret = lgdt3306a_soft_reset(state); if (lg_chkerr(ret)) goto fail;
dbg_info("complete\n");
fail: return ret;
}
staticint lgdt3306a_set_modulation(struct lgdt3306a_state *state, struct dtv_frontend_properties *p)
{ int ret;
dbg_info("\n");
switch (p->modulation) { case VSB_8:
ret = lgdt3306a_set_vsb(state); break; case QAM_64: case QAM_256: case QAM_AUTO:
ret = lgdt3306a_set_qam(state, p->modulation); break; default: return -EINVAL;
} if (lg_chkerr(ret)) goto fail;
/* 1. Normal operation mode */
ret = lgdt3306a_set_reg_bit(state, 0x0001, 0, 1); /* SIMFASTENB=0x01 */ if (lg_chkerr(ret)) goto fail;
/* 2. Spectrum inversion auto detection (Not valid for VSB) */
ret = lgdt3306a_set_inversion_auto(state, 0); if (lg_chkerr(ret)) goto fail;
/* 3. Spectrum inversion(According to the tuner configuration) */
ret = lgdt3306a_set_inversion(state, 1); if (lg_chkerr(ret)) goto fail;
/* 4. Peak-to-peak voltage of ADC input signal */
/* ADCSEL1V=0x80=1Vpp; 0x00=2Vpp */
ret = lgdt3306a_set_reg_bit(state, 0x0004, 7, 1); if (lg_chkerr(ret)) goto fail;
/* 5. ADC output data capture clock phase */
/* 0=same phase as ADC clock */
ret = lgdt3306a_set_reg_bit(state, 0x0004, 2, 0); if (lg_chkerr(ret)) goto fail;
/* 5a. ADC sampling clock source */
/* ADCCLKPLLSEL=0x08; 0=use ext clock, not PLL */
ret = lgdt3306a_set_reg_bit(state, 0x0004, 3, 0); if (lg_chkerr(ret)) goto fail;
/* 6. Automatic PLL set */
/* PLLSETAUTO=0x40; 0=off */
ret = lgdt3306a_set_reg_bit(state, 0x0005, 6, 0); if (lg_chkerr(ret)) goto fail;
if (state->cfg->xtalMHz == 24) { /* 24MHz */ /* 7. Frequency for PLL output(0x2564 for 192MHz for 24MHz) */
ret = lgdt3306a_read_reg(state, 0x0005, &val); if (lg_chkerr(ret)) goto fail;
val &= 0xc0;
val |= 0x25;
ret = lgdt3306a_write_reg(state, 0x0005, val); if (lg_chkerr(ret)) goto fail;
ret = lgdt3306a_write_reg(state, 0x0006, 0x64); if (lg_chkerr(ret)) goto fail;
/* 8. ADC sampling frequency(0x180000 for 24MHz sampling) */
ret = lgdt3306a_read_reg(state, 0x000d, &val); if (lg_chkerr(ret)) goto fail;
val &= 0xc0;
val |= 0x18;
ret = lgdt3306a_write_reg(state, 0x000d, val); if (lg_chkerr(ret)) goto fail;
} elseif (state->cfg->xtalMHz == 25) { /* 25MHz */ /* 7. Frequency for PLL output */
ret = lgdt3306a_read_reg(state, 0x0005, &val); if (lg_chkerr(ret)) goto fail;
val &= 0xc0;
val |= 0x25;
ret = lgdt3306a_write_reg(state, 0x0005, val); if (lg_chkerr(ret)) goto fail;
ret = lgdt3306a_write_reg(state, 0x0006, 0x64); if (lg_chkerr(ret)) goto fail;
/* 8. ADC sampling frequency(0x190000 for 25MHz sampling) */
ret = lgdt3306a_read_reg(state, 0x000d, &val); if (lg_chkerr(ret)) goto fail;
val &= 0xc0;
val |= 0x19;
ret = lgdt3306a_write_reg(state, 0x000d, val); if (lg_chkerr(ret)) goto fail;
} else {
pr_err("Bad xtalMHz=%d\n", state->cfg->xtalMHz);
} #if 0
ret = lgdt3306a_write_reg(state, 0x000e, 0x00);
ret = lgdt3306a_write_reg(state, 0x000f, 0x00); #endif
/* 9. Center frequency of input signal of ADC */
ret = lgdt3306a_write_reg(state, 0x0010, 0x34); /* 3.25MHz */
ret = lgdt3306a_write_reg(state, 0x0011, 0x00);
/* 10. Fixed gain error value */
ret = lgdt3306a_write_reg(state, 0x0014, 0); /* gain error=0 */
/* 10a. VSB TR BW gear shift initial step */
ret = lgdt3306a_read_reg(state, 0x103c, &val);
val &= 0x0f;
val |= 0x20; /* SAMGSAUTOSTL_V[3:0] = 2 */
ret = lgdt3306a_write_reg(state, 0x103c, val);
/* 10b. Timing offset calibration in low temperature for VSB */
ret = lgdt3306a_read_reg(state, 0x103d, &val);
val &= 0xfc;
val |= 0x03;
ret = lgdt3306a_write_reg(state, 0x103d, val);
/* 10c. Timing offset calibration in low temperature for QAM */
ret = lgdt3306a_read_reg(state, 0x1036, &val);
val &= 0xf0;
val |= 0x0c;
ret = lgdt3306a_write_reg(state, 0x1036, val);
/* 11. Using the imaginary part of CIR in CIR loading */
ret = lgdt3306a_read_reg(state, 0x211f, &val);
val &= 0xef; /* do not use imaginary of CIR */
ret = lgdt3306a_write_reg(state, 0x211f, val);
/* 12. Control of no signal detector function */
ret = lgdt3306a_read_reg(state, 0x2849, &val);
val &= 0xef; /* NOUSENOSIGDET=0, enable no signal detector */
ret = lgdt3306a_write_reg(state, 0x2849, val);
/* FGR - put demod in some known mode */
ret = lgdt3306a_set_vsb(state);
/* 13. TP stream format */
ret = lgdt3306a_mpeg_mode(state, state->cfg->mpeg_mode);
/* 14. disable output buses */
ret = lgdt3306a_mpeg_tristate(state, 1);
/* 15. Sleep (in reset) */
ret = lgdt3306a_sleep(state);
lg_chkerr(ret);
/* Carrier offset sub loop bandwidth */
ret = lgdt3306a_read_reg(state, 0x1061, &val); if (ret) return ret;
val &= 0xf8; if ((snrRef > 18) && (maxPowerMan > 0x68)
&& (nCombDet == 0x01)
&& ((fbDlyCir == 0x03FF) || (fbDlyCir < 0x6C))) { /* SNR is over 18dB and no ghosting */
val |= 0x00; /* final bandwidth = 0 */
} else {
val |= 0x04; /* final bandwidth = 4 */
}
ret = lgdt3306a_write_reg(state, 0x1061, val); if (ret) return ret;
/* Adjust Notch Filter */
ret = lgdt3306a_read_reg(state, 0x0024, &val); if (ret) return ret;
val &= 0x0f; if (nCombDet == 0) { /* Turn on the Notch Filter */
val |= 0x50;
}
ret = lgdt3306a_write_reg(state, 0x0024, val); if (ret) return ret;
/* VSB Timing Recovery output normalization */
ret = lgdt3306a_read_reg(state, 0x103d, &val); if (ret) return ret;
val &= 0xcf;
val |= 0x20;
ret = lgdt3306a_write_reg(state, 0x103d, val);
return ret;
}
staticenum lgdt3306a_modulation
lgdt3306a_check_oper_mode(struct lgdt3306a_state *state)
{
u8 val = 0; int ret;
ret = lgdt3306a_read_reg(state, 0x0081, &val); if (ret) goto err;
if (val & 0x80) {
dbg_info("VSB\n"); return LG3306_VSB;
} if (val & 0x08) {
ret = lgdt3306a_read_reg(state, 0x00a6, &val); if (ret) goto err;
val = val >> 2; if (val & 0x01) {
dbg_info("QAM256\n"); return LG3306_QAM256;
}
dbg_info("QAM64\n"); return LG3306_QAM64;
}
err:
pr_warn("UNKNOWN\n"); return LG3306_UNKNOWN_MODE;
}
#if 0 /* Dynamic ghost exists */ if ((mainStrong == 0) && (currChDiffACQ > 0x70)) #endif if (mainStrong == 0) {
ret = lgdt3306a_read_reg(state, 0x2135, &val); if (ret) return ret;
val &= 0x0f;
val |= 0xa0;
ret = lgdt3306a_write_reg(state, 0x2135, val); if (ret) return ret;
ret = lgdt3306a_read_reg(state, 0x2141, &val); if (ret) return ret;
val &= 0x3f;
val |= 0x80;
ret = lgdt3306a_write_reg(state, 0x2141, val); if (ret) return ret;
ret = lgdt3306a_write_reg(state, 0x2122, 0x70); if (ret) return ret;
} else { /* Weak ghost or static channel */
ret = lgdt3306a_read_reg(state, 0x2135, &val); if (ret) return ret;
val &= 0x0f;
val |= 0x70;
ret = lgdt3306a_write_reg(state, 0x2135, val); if (ret) return ret;
ret = lgdt3306a_read_reg(state, 0x2141, &val); if (ret) return ret;
val &= 0x3f;
val |= 0x40;
ret = lgdt3306a_write_reg(state, 0x2141, val); if (ret) return ret;
ret = lgdt3306a_write_reg(state, 0x2122, 0x40); if (ret) return ret;
} return 0;
}
if (x < 10) { while (x < 10) {
x = x * 10;
log_val--;
}
} else { /* x > 10 */ while (x >= 100) {
x = x / 10;
log_val++;
}
}
log_val *= 1000;
if (x == 10) /* was our input an exact multiple of 10 */ return log_val; /* don't need to interpolate */
/* find our place on the log curve */ for (i = 1; i < ARRAY_SIZE(valx_x10); i++) { if (valx_x10[i] >= x) break;
} if (i == ARRAY_SIZE(valx_x10)) return log_val + log10x_x1000[i - 1];
switch (state->current_modulation) { case QAM_256: case QAM_64: case QAM_AUTO: if (lgdt3306a_qam_lock_poll(state) == LG3306_LOCK) {
*status |= FE_HAS_VITERBI;
*status |= FE_HAS_SYNC;
*status |= FE_HAS_LOCK;
} break; case VSB_8: if (lgdt3306a_vsb_lock_poll(state) == LG3306_LOCK) {
*status |= FE_HAS_VITERBI;
*status |= FE_HAS_SYNC;
*status |= FE_HAS_LOCK;
ret = lgdt3306a_monitor_vsb(state);
} break; default:
ret = -EINVAL;
}
state->snr = lgdt3306a_calculate_snr_x100(state); /* report SNR in dB * 10 */
*snr = state->snr/10;
return 0;
}
staticint lgdt3306a_read_signal_strength(struct dvb_frontend *fe,
u16 *strength)
{ /* * Calculate some sort of "strength" from SNR
*/ struct lgdt3306a_state *state = fe->demodulator_priv;
u8 val;
u16 snr; /* snr_x10 */ int ret;
u32 ref_snr; /* snr*100 */
u32 str;
*strength = 0;
switch (state->current_modulation) { case VSB_8:
ref_snr = 1600; /* 16dB */ break; case QAM_64: case QAM_256: case QAM_AUTO: /* need to know actual modulation to set proper SNR baseline */
ret = lgdt3306a_read_reg(state, 0x00a6, &val); if (lg_chkerr(ret)) goto fail;
*ucblocks = 0; #if 1 /* FGR - FIXME - I don't know what value is expected by dvb_core
* what happens when value wraps? */
*ucblocks = read_reg(state, 0x00f4); /* TPIFTPERRCNT[0-7] */
dbg_info("ucblocks=%u\n", *ucblocks); #endif
/* verify that we're talking to a lg3306a */ /* FGR - NOTE - there is no obvious ChipId to check; we check
* some "known" bits after reset, but it's still just a guess */
ret = lgdt3306a_read_reg(state, 0x0000, &val); if (lg_chkerr(ret)) goto fail; if ((val & 0x74) != 0x74) {
pr_warn("expected 0x74, got 0x%x\n", (val & 0x74)); #if 0 /* FIXME - re-enable when we know this is right */ goto fail; #endif
}
ret = lgdt3306a_read_reg(state, 0x0001, &val); if (lg_chkerr(ret)) goto fail; if ((val & 0xf6) != 0xc6) {
pr_warn("expected 0xc6, got 0x%x\n", (val & 0xf6)); #if 0 /* FIXME - re-enable when we know this is right */ goto fail; #endif
}
ret = lgdt3306a_read_reg(state, 0x0002, &val); if (lg_chkerr(ret)) goto fail; if ((val & 0x73) != 0x03) {
pr_warn("expected 0x03, got 0x%x\n", (val & 0x73)); #if 0 /* FIXME - re-enable when we know this is right */ goto fail; #endif
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.