Skip to content

Commit

Permalink
[media] dib8096: enhancement
Browse files Browse the repository at this point in the history
The intend of this patch is to improve the support of the dib8096. The PLL
parameters are not automatically computed. The limit to set/unset external
diode for attenuation has been updated. The TFE8096P board is using the
new I2C API.

Signed-off-by: Olivier Grenie <olivier.grenie@parrot.com>
Signed-off-by: Patrick Boettcher <patrick.boettcher@parrot.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Olivier Grenie authored and Mauro Carvalho Chehab committed Apr 22, 2013
1 parent aedabf7 commit 5e9c85d
Showing 1 changed file with 175 additions and 33 deletions.
208 changes: 175 additions & 33 deletions drivers/media/usb/dvb-usb/dib0700_devices.c
Original file line number Diff line number Diff line change
Expand Up @@ -1431,13 +1431,22 @@ static int dib8090_get_adc_power(struct dvb_frontend *fe)
return dib8000_get_adc_power(fe, 1);
}

static void dib8090_agc_control(struct dvb_frontend *fe, u8 restart)
{
deb_info("AGC control callback: %i\n", restart);
dib0090_dcc_freq(fe, restart);

if (restart == 0) /* before AGC startup */
dib0090_set_dc_servo(fe, 1);
}

static struct dib8000_config dib809x_dib8000_config[2] = {
{
.output_mpeg2_in_188_bytes = 1,

.agc_config_count = 2,
.agc = dib8090_agc_config,
.agc_control = dib0090_dcc_freq,
.agc_control = dib8090_agc_control,
.pll = &dib8090_pll_config_12mhz,
.tuner_is_baseband = 1,

Expand All @@ -1456,7 +1465,7 @@ static struct dib8000_config dib809x_dib8000_config[2] = {

.agc_config_count = 2,
.agc = dib8090_agc_config,
.agc_control = dib0090_dcc_freq,
.agc_control = dib8090_agc_control,
.pll = &dib8090_pll_config_12mhz,
.tuner_is_baseband = 1,

Expand Down Expand Up @@ -1504,28 +1513,89 @@ static struct dib0090_config dib809x_dib0090_config = {
.fref_clock_ratio = 6,
};

static u8 dib8090_compute_pll_parameters(struct dvb_frontend *fe)
{
u8 optimal_pll_ratio = 20;
u32 freq_adc, ratio, rest, max = 0;
u8 pll_ratio;

for (pll_ratio = 17; pll_ratio <= 20; pll_ratio++) {
freq_adc = 12 * pll_ratio * (1 << 8) / 16;
ratio = ((fe->dtv_property_cache.frequency / 1000) * (1 << 8) / 1000) / freq_adc;
rest = ((fe->dtv_property_cache.frequency / 1000) * (1 << 8) / 1000) - ratio * freq_adc;

if (rest > freq_adc / 2)
rest = freq_adc - rest;
deb_info("PLL ratio=%i rest=%i\n", pll_ratio, rest);
if ((rest > max) && (rest > 717)) {
optimal_pll_ratio = pll_ratio;
max = rest;
}
}
deb_info("optimal PLL ratio=%i\n", optimal_pll_ratio);

return optimal_pll_ratio;
}

static int dib8096_set_param_override(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dib0700_adapter_state *state = adap->priv;
u8 band = BAND_OF_FREQUENCY(p->frequency/1000);
u16 target;
u8 pll_ratio, band = BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000);
u16 target, ltgain, rf_gain_limit;
u32 timf;
int ret = 0;
enum frontend_tune_state tune_state = CT_SHUTDOWN;
u16 ltgain, rf_gain_limit;

switch (band) {
default:
deb_info("Warning : Rf frequency (%iHz) is not in the supported range, using VHF switch ", fe->dtv_property_cache.frequency);
case BAND_VHF:
dib8000_set_gpio(fe, 3, 0, 1);
break;
case BAND_UHF:
dib8000_set_gpio(fe, 3, 0, 0);
break;
}

ret = state->set_param_save(fe);
if (ret < 0)
return ret;

target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2;
dib8000_set_wbd_ref(fe, target);
if (fe->dtv_property_cache.bandwidth_hz != 6000000) {
deb_info("only 6MHz bandwidth is supported\n");
return -EINVAL;
}

/** Update PLL if needed ratio **/
dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, 0);

/** Get optimize PLL ratio to remove spurious **/
pll_ratio = dib8090_compute_pll_parameters(fe);
if (pll_ratio == 17)
timf = 21387946;
else if (pll_ratio == 18)
timf = 20199727;
else if (pll_ratio == 19)
timf = 19136583;
else
timf = 18179756;

/** Update ratio **/
dib8000_update_pll(fe, &dib8090_pll_config_12mhz, fe->dtv_property_cache.bandwidth_hz / 1000, pll_ratio);

dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, timf);

if (band != BAND_CBAND) {
/* dib0090_get_wbd_target is returning any possible temperature compensated wbd-target */
target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2;
dib8000_set_wbd_ref(fe, target);
}

if (band == BAND_CBAND) {
deb_info("tuning in CBAND - soft-AGC startup\n");
dib0090_set_tune_state(fe, CT_AGC_START);

do {
ret = dib0090_gain_control(fe);
msleep(ret);
Expand All @@ -1534,14 +1604,17 @@ static int dib8096_set_param_override(struct dvb_frontend *fe)
dib8000_set_gpio(fe, 6, 0, 1);
else if (tune_state == CT_AGC_STEP_1) {
dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, &ltgain);
if (rf_gain_limit == 0)
if (rf_gain_limit < 2000) /* activate the external attenuator in case of very high input power */
dib8000_set_gpio(fe, 6, 0, 0);
}
} while (tune_state < CT_AGC_STOP);

deb_info("switching to PWM AGC\n");
dib0090_pwm_gain_reset(fe);
dib8000_pwm_agc_reset(fe);
dib8000_set_tune_state(fe, CT_DEMOD_START);
} else {
/* for everything else than CBAND we are using standard AGC */
deb_info("not tuning in CBAND - standard AGC startup\n");
dib0090_pwm_gain_reset(fe);
}
Expand Down Expand Up @@ -1814,21 +1887,92 @@ struct dibx090p_adc {
u32 pll_prediv; /* New loopdiv */
};

struct dibx090p_adc dib8090p_adc_tab[] = {
{ 50000, 17043521, 16, 3}, /* 64 MHz */
{878000, 20199729, 9, 1}, /* 60 MHz */
{0xffffffff, 0, 0, 0}, /* 60 MHz */
struct dibx090p_best_adc {
u32 timf;
u32 pll_loopdiv;
u32 pll_prediv;
};

static int dib8096p_get_best_sampling(struct dvb_frontend *fe, struct dibx090p_best_adc *adc)
{
u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1;
u16 xtal = 12000;
u16 fcp_min = 1900; /* PLL, Minimum Frequency of phase comparator (KHz) */
u16 fcp_max = 20000; /* PLL, Maximum Frequency of phase comparator (KHz) */
u32 fmem_max = 140000; /* 140MHz max SDRAM freq */
u32 fdem_min = 66000;
u32 fcp = 0, fs = 0, fdem = 0, fmem = 0;
u32 harmonic_id = 0;

adc->timf = 0;
adc->pll_loopdiv = loopdiv;
adc->pll_prediv = prediv;

deb_info("bandwidth = %d", fe->dtv_property_cache.bandwidth_hz);

/* Find Min and Max prediv */
while ((xtal / max_prediv) >= fcp_min)
max_prediv++;

max_prediv--;
min_prediv = max_prediv;
while ((xtal / min_prediv) <= fcp_max) {
min_prediv--;
if (min_prediv == 1)
break;
}
deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv);

min_prediv = 1;

for (prediv = min_prediv; prediv < max_prediv; prediv++) {
fcp = xtal / prediv;
if (fcp > fcp_min && fcp < fcp_max) {
for (loopdiv = 1; loopdiv < 64; loopdiv++) {
fmem = ((xtal/prediv) * loopdiv);
fdem = fmem / 2;
fs = fdem / 4;

/* test min/max system restrictions */
if ((fdem >= fdem_min) && (fmem <= fmem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz / 1000)) {
spur = 0;
/* test fs harmonics positions */
for (harmonic_id = (fe->dtv_property_cache.frequency / (1000 * fs)); harmonic_id <= ((fe->dtv_property_cache.frequency / (1000 * fs)) + 1); harmonic_id++) {
if (((fs * harmonic_id) >= (fe->dtv_property_cache.frequency / 1000 - (fe->dtv_property_cache.bandwidth_hz / 2000))) && ((fs * harmonic_id) <= (fe->dtv_property_cache.frequency / 1000 + (fe->dtv_property_cache.bandwidth_hz / 2000)))) {
spur = 1;
break;
}
}

if (!spur) {
adc->pll_loopdiv = loopdiv;
adc->pll_prediv = prediv;
adc->timf = (4260880253U / fdem) * (1 << 8);
adc->timf += ((4260880253U % fdem) << 8) / fdem;

deb_info("RF %6d; BW %6d; Xtal %6d; Fmem %6d; Fdem %6d; Fs %6d; Prediv %2d; Loopdiv %2d; Timf %8d;", fe->dtv_property_cache.frequency, fe->dtv_property_cache.bandwidth_hz, xtal, fmem, fdem, fs, prediv, loopdiv, adc->timf);
break;
}
}
}
}
if (!spur)
break;
}

if (adc->pll_loopdiv == 0 && adc->pll_prediv == 0)
return -EINVAL;
return 0;
}

static int dib8096p_agc_startup(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct dvb_usb_adapter *adap = fe->dvb->priv;
struct dib0700_adapter_state *state = adap->priv;
struct dibx000_bandwidth_config pll;
struct dibx090p_best_adc adc;
u16 target;
int better_sampling_freq = 0, ret;
struct dibx090p_adc *adc_table = &dib8090p_adc_tab[0];
int ret;

ret = state->set_param_save(fe);
if (ret < 0)
Expand All @@ -1841,23 +1985,27 @@ static int dib8096p_agc_startup(struct dvb_frontend *fe)
target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2;
dib8000_set_wbd_ref(fe, target);

if (dib8096p_get_best_sampling(fe, &adc) == 0) {
pll.pll_ratio = adc.pll_loopdiv;
pll.pll_prediv = adc.pll_prediv;

while (p->frequency / 1000 > adc_table->freq) {
better_sampling_freq = 1;
adc_table++;
}

if ((adc_table->freq != 0xffffffff) && better_sampling_freq) {
pll.pll_ratio = adc_table->pll_loopdiv;
pll.pll_prediv = adc_table->pll_prediv;
dib0700_set_i2c_speed(adap->dev, 200);
dib8000_update_pll(fe, &pll, fe->dtv_property_cache.bandwidth_hz / 1000, 0);
dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc_table->timf);
dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf);
dib0700_set_i2c_speed(adap->dev, 1000);
}
return 0;
}

static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap)
{
struct dib0700_state *st = adap->dev->priv;
u32 fw_version;

dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL);
if (fw_version >= 0x10200)
st->fw_use_new_i2c_api = 1;

dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
msleep(20);
dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
Expand Down Expand Up @@ -2242,13 +2390,7 @@ static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap)
}

/* NIM7090 */
struct dib7090p_best_adc {
u32 timf;
u32 pll_loopdiv;
u32 pll_prediv;
};

static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_best_adc *adc)
static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dibx090p_best_adc *adc)
{
u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1;

Expand Down Expand Up @@ -2327,7 +2469,7 @@ static int dib7090_agc_startup(struct dvb_frontend *fe)
struct dib0700_adapter_state *state = adap->priv;
struct dibx000_bandwidth_config pll;
u16 target;
struct dib7090p_best_adc adc;
struct dibx090p_best_adc adc;
int ret;

ret = state->set_param_save(fe);
Expand Down

0 comments on commit 5e9c85d

Please sign in to comment.