From e096ddb7f8b7bd986912bd9c78fb5bd39d437443 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Mon, 1 Sep 2014 20:51:34 -0400 Subject: [PATCH] driver: Jowett's tuning This is a squashing of roughly 25 commits to assist with merging and organization. It will be rebased as various sections are mainlined. --- include/rtl-sdr.h | 4 - include/tuner_e4k.h | 2 +- include/tuner_r82xx.h | 10 +- src/librtlsdr.c | 179 +++++++++++++++++---------------- src/tuner_e4k.c | 34 ++++--- src/tuner_r82xx.c | 229 +++++++++++++++++++++++++++++++----------- 6 files changed, 293 insertions(+), 165 deletions(-) diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index 656c55b..22da878 100644 --- a/include/rtl-sdr.h +++ b/include/rtl-sdr.h @@ -144,10 +144,6 @@ RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); -RTLSDR_API int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq); - -RTLSDR_API int rtlsdr_set_if_bandwidth(rtlsdr_dev_t *dev, int bw); - /*! * Get actual frequency the device is tuned to. * diff --git a/include/tuner_e4k.h b/include/tuner_e4k.h index 79591ce..da4a654 100644 --- a/include/tuner_e4k.h +++ b/include/tuner_e4k.h @@ -203,7 +203,7 @@ int e4k_standby(struct e4k_state *e4k, int enable); int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value); int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value); int e4k_commonmode_set(struct e4k_state *e4k, int8_t value); -int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq); +int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq, uint32_t *lo_freq); int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p); uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo); int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter); diff --git a/include/tuner_r82xx.h b/include/tuner_r82xx.h index 77a87e3..2b4bc79 100644 --- a/include/tuner_r82xx.h +++ b/include/tuner_r82xx.h @@ -93,6 +93,13 @@ struct r82xx_priv { enum r82xx_tuner_type type; uint32_t bw; /* in MHz */ + uint32_t if_filter_freq; /* in Hz */ + + int pll_off; + + /* current PLL limits */ + uint32_t pll_low_limit; + uint32_t pll_high_limit; void *rtl_dev; }; @@ -116,11 +123,10 @@ enum r82xx_delivery_system { int r82xx_standby(struct r82xx_priv *priv); int r82xx_init(struct r82xx_priv *priv); -int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); +int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq, uint32_t *lo_freq_out); int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain); int r82xx_set_nomod(struct r82xx_priv *priv); int r82xx_set_dither(struct r82xx_priv *priv, int dither); int r82xx_set_bw(struct r82xx_priv *priv, uint32_t bw); -int r82xx_set_if_freq(struct r82xx_priv *priv, uint32_t freq); #endif diff --git a/src/librtlsdr.c b/src/librtlsdr.c index a3b5359..6e5d607 100644 --- a/src/librtlsdr.c +++ b/src/librtlsdr.c @@ -59,12 +59,11 @@ typedef struct rtlsdr_tuner_iface { /* tuner interface */ int (*init)(void *); int (*exit)(void *); - int (*set_freq)(void *, uint32_t freq /* Hz */); + int (*set_freq)(void *, uint32_t freq /* Hz */, uint32_t *lo_freq_out); int (*set_bw)(void *, int bw /* Hz */); int (*set_gain)(void *, int gain /* tenth dB */); int (*set_if_gain)(void *, int stage, int gain /* tenth dB */); int (*set_gain_mode)(void *, int manual); - int (*set_if_freq)(void *, uint32_t freq /* Hz */); } rtlsdr_tuner_iface_t; enum rtlsdr_async_status { @@ -115,6 +114,7 @@ struct rtlsdr_dev { uint32_t tun_xtal; /* Hz */ uint32_t freq; /* Hz */ uint32_t offs_freq; /* Hz */ + uint32_t effective_freq; /* Hz */ int corr; /* ppm */ int gain; /* tenth dB */ struct e4k_state e4k_s; @@ -126,6 +126,7 @@ struct rtlsdr_dev { unsigned int xfer_errors; int tuner_initialized; int i2c_repeater_on; + int spectrum_inversion; }; void rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); @@ -142,9 +143,9 @@ int e4000_exit(void *dev) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return e4k_standby(&devt->e4k_s, 1); } -int e4000_set_freq(void *dev, uint32_t freq) { +int e4000_set_freq(void *dev, uint32_t freq, uint32_t *lo_freq_out) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - return e4k_tune_freq(&devt->e4k_s, freq); + return e4k_tune_freq(&devt->e4k_s, freq, lo_freq_out); } int e4000_set_bw(void *dev, int bw) { @@ -186,8 +187,9 @@ int e4000_set_gain_mode(void *dev, int manual) { int _fc0012_init(void *dev) { return fc0012_init(dev); } int fc0012_exit(void *dev) { return 0; } -int fc0012_set_freq(void *dev, uint32_t freq) { +int fc0012_set_freq(void *dev, uint32_t freq, uint32_t *lo_freq_out) { /* select V-band/U-band filter */ + if (lo_freq_out) *lo_freq_out = freq; rtlsdr_set_gpio_bit(dev, 6, (freq > 300000000) ? 1 : 0); return fc0012_set_params(dev, freq, 6000000); } @@ -197,7 +199,8 @@ int fc0012_set_gain_mode(void *dev, int manual) { return 0; } int _fc0013_init(void *dev) { return fc0013_init(dev); } int fc0013_exit(void *dev) { return 0; } -int fc0013_set_freq(void *dev, uint32_t freq) { +int fc0013_set_freq(void *dev, uint32_t freq, uint32_t *lo_freq_out) { + if (lo_freq_out) *lo_freq_out = freq; return fc0013_set_params(dev, freq, 6000000); } int fc0013_set_bw(void *dev, int bw) { return 0; } @@ -205,7 +208,8 @@ int _fc0013_set_gain(void *dev, int gain) { return fc0013_set_lna_gain(dev, gain int fc2580_init(void *dev) { return fc2580_Initialize(dev); } int fc2580_exit(void *dev) { return 0; } -int _fc2580_set_freq(void *dev, uint32_t freq) { +int _fc2580_set_freq(void *dev, uint32_t freq, uint32_t *lo_freq_out) { + if (lo_freq_out) *lo_freq_out = freq; return fc2580_SetRfFreqHz(dev, freq); } int fc2580_set_bw(void *dev, int bw) { return fc2580_SetBandwidthMode(dev, 1); } @@ -237,9 +241,9 @@ int r820t_exit(void *dev) { return r82xx_standby(&devt->r82xx_p); } -int r820t_set_freq(void *dev, uint32_t freq) { +int r820t_set_freq(void *dev, uint32_t freq, uint32_t *lo_freq_out) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - return r82xx_set_freq(&devt->r82xx_p, freq); + return r82xx_set_freq(&devt->r82xx_p, freq, lo_freq_out); } int r820t_set_bw(void *dev, int bw) { @@ -247,11 +251,6 @@ int r820t_set_bw(void *dev, int bw) { return r82xx_set_bw(&devt->r82xx_p, bw); } -int r820t_set_if_freq(void *dev, uint32_t freq) { - rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - return r82xx_set_if_freq(&devt->r82xx_p, freq); -} - int r820t_set_gain(void *dev, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_set_gain(&devt->r82xx_p, 1, gain); @@ -264,37 +263,37 @@ int r820t_set_gain_mode(void *dev, int manual) { /* definition order must match enum rtlsdr_tuner */ static rtlsdr_tuner_iface_t tuners[] = { { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL /* dummy for unknown tuners */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL /* dummy for unknown tuners */ }, { e4000_init, e4000_exit, e4000_set_freq, e4000_set_bw, e4000_set_gain, e4000_set_if_gain, - e4000_set_gain_mode, NULL + e4000_set_gain_mode }, { _fc0012_init, fc0012_exit, fc0012_set_freq, fc0012_set_bw, _fc0012_set_gain, NULL, - fc0012_set_gain_mode, NULL + fc0012_set_gain_mode }, { _fc0013_init, fc0013_exit, fc0013_set_freq, fc0013_set_bw, _fc0013_set_gain, NULL, - fc0013_set_gain_mode, NULL + fc0013_set_gain_mode }, { fc2580_init, fc2580_exit, _fc2580_set_freq, fc2580_set_bw, fc2580_set_gain, NULL, - fc2580_set_gain_mode, NULL + fc2580_set_gain_mode }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, - r820t_set_gain_mode, r820t_set_if_freq + r820t_set_gain_mode }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_bw, r820t_set_gain, NULL, - r820t_set_gain_mode, r820t_set_if_freq + r820t_set_gain_mode }, }; @@ -633,6 +632,7 @@ void rtlsdr_init_baseband(rtlsdr_dev_t *dev) /* disable spectrum inversion and adjacent channel rejection */ rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); + dev->spectrum_inversion = 0; rtlsdr_demod_write_reg(dev, 1, 0x16, 0x0000, 2); /* clear both DDC shift and IF frequency registers */ @@ -690,7 +690,7 @@ int rtlsdr_deinit_baseband(rtlsdr_dev_t *dev) return r; } -int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) +static int rtl2832_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq, uint32_t *freq_out) { uint32_t rtl_xtal; int32_t if_freq; @@ -705,7 +705,11 @@ int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) if (rtlsdr_get_xtal_freq(dev, &rtl_xtal, NULL)) return -2; - if_freq = ((freq * TWO_POW(22)) / rtl_xtal) * (-1); + if_freq = ((rtl_xtal/2 + (uint64_t)freq * TWO_POW(22)) / rtl_xtal) * (-1); + if (if_freq <= -0x200000) { + /* fprintf(stderr, "rtl2832_set_if_freq(): %u Hz out of range for downconverter (divisor would be %x)\n", freq, if_freq); */ + return -2; + } tmp = (if_freq >> 16) & 0x3f; r = rtlsdr_demod_write_reg(dev, 1, 0x19, tmp, 1); @@ -714,13 +718,8 @@ int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) tmp = if_freq & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x1b, tmp, 1); - /* Tell the R820T driver which IF frequency we are currently using - * so that it can choose the optimal IF filter settings. - * Works for normal tuning as well as no-mod direct sampling! */ - if(dev->tuner_initialized && dev->tuner && dev->tuner->set_if_freq) { - rtlsdr_set_i2c_repeater(dev, 1); - dev->tuner->set_if_freq(dev, freq); - } + if (freq_out) *freq_out = ((int64_t)if_freq * rtl_xtal * -1 + TWO_POW(21)) / TWO_POW(22); + return r; } @@ -900,25 +899,60 @@ int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_ return r; } +static int set_spectrum_inversion(rtlsdr_dev_t *dev, int inverted) +{ + int r = 0; + + if (dev->spectrum_inversion == inverted) + return r; + + r |= rtlsdr_demod_write_reg(dev, 1, 0x15, inverted, 1); + + /* reset demod (bit 3, soft_rst) */ + r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); + r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); + + dev->spectrum_inversion = inverted; + return r; +} + int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) { int r = -1; + uint32_t tuner_lo; + uint32_t tuner_if, actual_if = 0; + int inverted; if (!dev || !dev->tuner) return -1; if (dev->direct_sampling) { - rtlsdr_set_i2c_repeater(dev, 0); - r = rtlsdr_set_if_freq(dev, freq); + tuner_lo = 0; } else if (dev->tuner && dev->tuner->set_freq) { rtlsdr_set_i2c_repeater(dev, 1); - r = dev->tuner->set_freq(dev, freq - dev->offs_freq); + r = dev->tuner->set_freq(dev, freq - dev->offs_freq, &tuner_lo); + rtlsdr_set_i2c_repeater(dev, 0); } - if (!r) - dev->freq = freq; + if (tuner_lo > freq) { + /* high-side mixing, enable spectrum inversion */ + tuner_if = tuner_lo - freq; + inverted = 1; + } else { + /* low-side mixing, or zero-IF, or direct sampling; disable spectrum inversion */ + tuner_if = freq - tuner_lo; + inverted = 0; + } + + r |= set_spectrum_inversion(dev, inverted); + r |= rtl2832_set_if_freq(dev, tuner_if, &actual_if); + + dev->freq = freq; + + if (inverted) + dev->effective_freq = tuner_lo - actual_if; else - dev->freq = 0; + dev->effective_freq = tuner_lo + actual_if; return r; } @@ -928,7 +962,7 @@ uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) if (!dev) return 0; - return dev->freq; + return dev->effective_freq; } int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) @@ -1171,58 +1205,36 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) if (!dev) return -1; - /* set up normal direct sampling */ - if (on == 1 || on == 2) { + if (on == dev->direct_sampling) + return 0; + + rtlsdr_set_i2c_repeater(dev, 0); + + /* common to all direct modes */ + if (on) { if (dev->tuner && dev->tuner->exit) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); rtlsdr_set_i2c_repeater(dev, 0); dev->tuner_initialized = 0; } - } - /* set up no-mod direct sampling */ - if (on == 3 && dev->tuner) { - if (dev->tuner_type == RTLSDR_TUNER_E4000) { - fprintf(stderr, "Tuning E4000 to 3708 MHz\n"); - rtlsdr_set_i2c_repeater(dev, 1); - dev->tuner->init(dev); - dev->tuner_initialized = 1; - dev->tuner->set_freq(dev, 3708000000u); - e4000_set_bw(dev, 15000000); - rtlsdr_set_i2c_repeater(dev, 0); - } - if (dev->tuner_type == RTLSDR_TUNER_R820T) { - rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; - rtlsdr_set_i2c_repeater(dev, 1); - dev->tuner->init(dev); - dev->tuner_initialized = 1; - r82xx_set_nomod(&devt->r82xx_p); - rtlsdr_set_i2c_repeater(dev, 0); - } - } - rtlsdr_set_i2c_repeater(dev, 0); - - /* common to all direct modes */ - if (on) { /* disable Zero-IF mode */ r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); - /* disable spectrum inversion */ - r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); - /* only enable In-phase ADC input */ r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); /* swap I and Q ADC, this allows to select between two inputs */ r |= rtlsdr_demod_write_reg(dev, 0, 0x06, (on == 2) ? 0x90 : 0x80, 1); + /* disable spectrum inversion */ + r |= set_spectrum_inversion(dev, 0); + fprintf(stderr, "Enabled direct sampling mode, input %i\n", on); dev->direct_sampling = on; - } - - /* disable direct sampling */ - if (!on) { + } else { + /* disable direct sampling */ if (dev->tuner && dev->tuner->init) { rtlsdr_set_i2c_repeater(dev, 1); r |= dev->tuner->init(dev); @@ -1232,13 +1244,14 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) if ((dev->tuner_type == RTLSDR_TUNER_R820T) || (dev->tuner_type == RTLSDR_TUNER_R828D)) { - r |= rtlsdr_set_if_freq(dev, R82XX_DEFAULT_IF_FREQ); + /* disable Zero-IF mode */ + r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); - /* enable spectrum inversion */ - r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); + /* only enable In-phase ADC input */ + r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); + + /* Already configured */ } else { - r |= rtlsdr_set_if_freq(dev, 0); - /* enable In-phase + Quadrature ADC input */ r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0xcd, 1); @@ -1253,6 +1266,7 @@ int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) dev->direct_sampling = 0; } + /* retune now that we have changed the config */ r |= rtlsdr_set_center_freq(dev, dev->freq); return r; @@ -1283,17 +1297,14 @@ int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) /* based on keenerds 1/f noise measurements */ dev->offs_freq = on ? ((dev->rate / 2) * 170 / 100) : 0; - r |= rtlsdr_set_if_freq(dev, dev->offs_freq); if (dev->tuner && dev->tuner->set_bw) { rtlsdr_set_i2c_repeater(dev, 1); - dev->tuner->set_bw(dev, on ? (2 * dev->offs_freq) : dev->rate); + r |= dev->tuner->set_bw(dev, on ? (2 * dev->offs_freq) : dev->rate); rtlsdr_set_i2c_repeater(dev, 0); } - if (dev->freq > dev->offs_freq) - r |= rtlsdr_set_center_freq(dev, dev->freq); - + r |= rtlsdr_set_center_freq(dev, dev->freq); return r; } @@ -1622,12 +1633,6 @@ found: /* only enable In-phase ADC input */ rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); - /* the R82XX use 3.57 MHz IF for the DVB-T 6 MHz mode, and - * 4.57 MHz for the 8 MHz mode */ - rtlsdr_set_if_freq(dev, R82XX_DEFAULT_IF_FREQ); - - /* enable spectrum inversion */ - rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); break; case RTLSDR_TUNER_UNKNOWN: fprintf(stderr, "No supported tuner found\n"); diff --git a/src/tuner_e4k.c b/src/tuner_e4k.c index c2ec044..561a480 100644 --- a/src/tuner_e4k.c +++ b/src/tuner_e4k.c @@ -213,7 +213,7 @@ int e4k_rf_filter_set(struct e4k_state *e4k) { int rc; - rc = choose_rf_filter(e4k->band, e4k->vco.flo); + rc = choose_rf_filter(e4k->band, e4k->vco.intended_flo); if (rc < 0) return rc; @@ -338,16 +338,17 @@ int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter) /*********************************************************************** * Frequency Control */ -#define E4K_FVCO_MIN_KHZ 2600000 /* 2.6 GHz */ -#define E4K_FVCO_MAX_KHZ 3900000 /* 3.9 GHz */ #define E4K_PLL_Y 65536 #ifdef OUT_OF_SPEC -#define E4K_FLO_MIN_MHZ 50 -#define E4K_FLO_MAX_MHZ 2200UL +#define E4K_FVCO_MIN_KHZ 2400000UL /* 2.4 GHz; min FLO is 2400/48 = 50MHz */ +#define E4K_FVCO_MAX_KHZ 4400000UL /* 4.4 GHz; max FLO is 4400/2 = 2200MHz */ #else -#define E4K_FLO_MIN_MHZ 64 -#define E4K_FLO_MAX_MHZ 1700 +/* NB: Datasheet values for RF input and LO ranges are 64 - 1700MHz. + * The values below are from the slightly wider VCO ranges. + */ +#define E4K_FVCO_MIN_KHZ 2600000UL /* 2.6 GHz; min FLO is 2600/48 = 54MHz */ +#define E4K_FVCO_MAX_KHZ 3900000UL /* 3.9 GHz; max FLO is 3900/2 = 1950MHz */ #endif struct pll_settings { @@ -505,6 +506,11 @@ uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint /* flo(max) = 1700MHz, R(max) = 48, we need 64bit! */ intended_fvco = (uint64_t)intended_flo * r; + if (intended_fvco < KHZ(E4K_FVCO_MIN_KHZ)) { + intended_fvco = KHZ(E4K_FVCO_MIN_KHZ); + } else if (intended_fvco > KHZ(E4K_FVCO_MAX_KHZ)) { + intended_fvco = KHZ(E4K_FVCO_MAX_KHZ); + } /* compute integral component of multiplier */ z = intended_fvco / fosc; @@ -545,11 +551,11 @@ int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p) memcpy(&e4k->vco, p, sizeof(e4k->vco)); /* set the band */ - if (e4k->vco.flo < MHZ(140)) + if (e4k->vco.intended_flo < MHZ(140)) e4k_band_set(e4k, E4K_BAND_VHF2); - else if (e4k->vco.flo < MHZ(350)) + else if (e4k->vco.intended_flo < MHZ(350)) e4k_band_set(e4k, E4K_BAND_VHF3); - else if (e4k->vco.flo < MHZ(1135)) + else if (e4k->vco.intended_flo < MHZ(1135)) e4k_band_set(e4k, E4K_BAND_UHF); else e4k_band_set(e4k, E4K_BAND_L); @@ -567,9 +573,10 @@ int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p) * * \param[in] e4k reference to tuner * \param[in] freq frequency in Hz - * \returns actual tuned frequency, negative in case of error + * \param[out] lo_freq if non-NULL, set to actually tuned frequency in Hz + * \returns zero on success, negative on error */ -int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq) +int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq, uint32_t *lo_freq) { uint32_t rc; struct e4k_pll_params p; @@ -589,6 +596,9 @@ int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq) return -1; } + if (lo_freq) + *lo_freq = e4k->vco.flo; + return 0; } diff --git a/src/tuner_r82xx.c b/src/tuner_r82xx.c index 86cfffb..203afa5 100644 --- a/src/tuner_r82xx.c +++ b/src/tuner_r82xx.c @@ -37,6 +37,30 @@ * Static constants */ +/* + * These should be "safe" values, always. If we fail to get PLL lock in this range, + * it's a hard error. + */ +#define PLL_SAFE_LOW 28e6 +#define PLL_SAFE_HIGH 1845e6 + +/* + * These are the initial, widest, PLL limits that we will try. + * + * Be cautious with lowering the low bound further - the PLL can claim to be locked + * when configured to a lower frequency, but actually be running at around 26.6MHz + * regardless of what it was configured for. + * + * This shows up as a tuning offset at low frequencies, and a "dead zone" about + * 6MHz below the PLL lower bound where retuning within that region has no effect. + */ +#define PLL_INITIAL_LOW 26.7e6 +#define PLL_INITIAL_HIGH 1860e6 + +/* We shrink the range edges by at least this much each time there is a soft PLL lock failure */ +#define PLL_STEP_LOW 0.1e6 +#define PLL_STEP_HIGH 1.0e6 + /* Those initial values start from REG_SHADOW_START */ static const uint8_t r82xx_init_array[NUM_REGS] = { 0x83, 0x32, 0x75, /* 05 to 07 */ @@ -344,7 +368,7 @@ static int r82xx_write_batch_sync(struct r82xx_priv *priv) return -1; priv->reg_batch = 0; if (priv->reg_low > priv->reg_high) - return 0; /* No registers were changed */ + return 0; /* No work to do */ offset = priv->reg_low - REG_SHADOW_START; len = priv->reg_high - priv->reg_low + 1; rc = r82xx_write(priv, priv->reg_low, priv->regs+offset, len); @@ -451,13 +475,13 @@ static int r82xx_set_mux(struct r82xx_priv *priv, uint32_t freq) return rc; } -static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) +static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq, uint32_t *freq_out) { int rc, i; unsigned sleep_time = 10000; uint64_t vco_freq; uint64_t vco_div; - uint32_t vco_min = 1770000; /* kHz */ + uint32_t vco_min = 1750000; /* kHz */ uint32_t vco_max = vco_min * 2; /* kHz */ uint32_t freq_khz, pll_ref; uint32_t sdm = 0; @@ -485,30 +509,16 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) return rc; /* set VCO current = 100 */ + priv->pll_off = 0; rc = r82xx_write_reg_mask(priv, 0x12, 0x80, 0xe0); if (rc < 0) return rc; /* Calculate divider */ - if(freq_khz < vco_min/64) vco_min /= 2; - if(freq_khz >= vco_max/2) vco_max *= 2; - while (mix_div <= 64) { - if (((freq_khz * mix_div) >= vco_min) && - ((freq_khz * mix_div) < vco_max)) { - div_buf = mix_div; - while (div_buf > 2) { - div_buf = div_buf >> 1; - div_num++; - } - break; - } - mix_div = mix_div << 1; - } - if (mix_div > 64) { - fprintf(stderr, "[R82XX] No valid PLL values for %u Hz!\n", freq); - return -1; - } + for (mix_div = 2, div_num = 0; mix_div < 64; mix_div <<= 1, div_num++) + if ((freq_khz * mix_div) >= vco_min) + break; if (priv->cfg->rafael_chip == CHIP_R828D) vco_power_ref = 1; @@ -557,6 +567,11 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) ni = (nint - 13) / 4; si = nint - 4 * ni - 13; + if (freq_out) { + uint64_t actual_vco = (uint64_t)2 * pll_ref * nint + (uint64_t)2 * pll_ref * sdm / 65536; + *freq_out = (uint32_t) ((actual_vco + mix_div/2) / mix_div); + } + rc = r82xx_write_reg(priv, 0x14, ni + (si << 6)); if (rc < 0) return rc; @@ -574,8 +589,6 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) if (rc < 0) return rc; - //fprintf(stderr, "LO: %u kHz, MixDiv: %u, PLLDiv: %u, VCO %u kHz, SDM: %u \n", (uint32_t)(freq/1000), mix_div, nint, (uint32_t)(vco_freq/1000), sdm); - rc = r82xx_write_reg(priv, 0x16, sdm >> 8); if (rc < 0) return rc; @@ -611,8 +624,7 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) } if (!(data[2] & 0x40)) { - fprintf(stderr, "[R82XX] PLL not locked!\n"); - return -1; + return -42; } /* set pll autotune = 8kHz */ @@ -906,10 +918,14 @@ static int r82xx_init_tv_standard(struct r82xx_priv *priv, return 0; } -static int r82xx_set_if_filter(struct r82xx_priv *priv, int hpf, int lpf) { - int rc, i; +static int update_if_filter(struct r82xx_priv *priv) { + int rc, i, hpf, lpf; uint8_t filt_q, hp_cor; int cal; + + hpf = ((int)priv->if_filter_freq - (int)priv->bw/2)/1000; + lpf = ((int)priv->if_filter_freq + (int)priv->bw/2)/1000; + filt_q = 0x10; /* r10[4]:low q(1'b1) */ if(lpf <= 2500) { @@ -950,8 +966,6 @@ static int r82xx_set_if_filter(struct r82xx_priv *priv, int hpf, int lpf) { else if(cal > 15) cal = 15; priv->fil_cal_code = cal; - //fprintf(stderr, "Setting IF filter for %d...%d kHz: hp_cor=0x%02x, fil_cal_code=%d\n", hpf, lpf, hp_cor, cal); - rc = r82xx_write_reg_mask(priv, 0x0a, filt_q | priv->fil_cal_code, 0x1f); if (rc < 0) @@ -967,12 +981,7 @@ static int r82xx_set_if_filter(struct r82xx_priv *priv, int hpf, int lpf) { int r82xx_set_bw(struct r82xx_priv *priv, uint32_t bw) { priv->bw = bw; - return r82xx_set_if_filter(priv, ((int)priv->int_freq - (int)bw/2)/1000, ((int)priv->int_freq + (int)bw/2)/1000); -} - -int r82xx_set_if_freq(struct r82xx_priv *priv, uint32_t freq) { - priv->int_freq = freq; - return r82xx_set_if_filter(priv, ((int)freq - (int)priv->bw/2)/1000, ((int)freq + (int)priv->bw/2)/1000); + return update_if_filter(priv); } static int r82xx_read_gain(struct r82xx_priv *priv) @@ -1074,21 +1083,19 @@ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain) return 0; } -int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) +int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq, uint32_t *lo_freq_out) { - int rc = -1; + int rc; uint32_t lo_freq = freq + priv->int_freq; + uint32_t margin = 1e6 + priv->bw/2; uint8_t air_cable1_in; + int changed_pll_limits = 0; r82xx_write_batch_init(priv); - rc = r82xx_set_mux(priv, lo_freq); - if (rc < 0) - goto err; + /* RF input settings */ - rc = r82xx_set_pll(priv, lo_freq); - if (rc < 0) - goto err; + rc = r82xx_set_mux(priv, freq); /* switch between 'Cable1' and 'Air-In' inputs on sticks with * R828D tuner. We switch at 345 MHz, because that's where the @@ -1099,12 +1106,118 @@ int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) if ((priv->cfg->rafael_chip == CHIP_R828D) && (air_cable1_in != priv->input)) { priv->input = air_cable1_in; - rc = r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); + rc |= r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); } - if (priv->reg_batch) { - rc = r82xx_write_batch_sync(priv); + /* IF generation settings */ + + retune: + if (freq < 14.4e6 && freq < (priv->pll_low_limit - 14.4e6)) { + /* Previously "no-mod direct sampling" - confuse the VCO/PLL + * sufficiently that we get the HF signal leaking through + * the tuner, then sample that directly. + * + * Disable the VCO, as far as we can. + * This throws a big spike of noise into the signal, + * so only do it once when crossing the 14.4MHz boundary, + * not on every retune. + */ + if (!priv->pll_off) { + rc |= r82xx_set_pll(priv, 50e6, NULL); /* Might influence the noise floor? */ + rc |= r82xx_write_reg_mask(priv, 0x10, 0xd0, 0xe0); /* impossible mix_div setting */ + rc |= r82xx_write_reg_mask(priv, 0x12, 0xe0, 0xe0); /* VCO current = 0 */ + priv->pll_off = 1; + } + + /* We are effectively tuned to 0Hz - the downconverter must do all the heavy lifting now */ + lo_freq = 0; + if (lo_freq_out) *lo_freq_out = 0; + } else { + /* Normal tuning case */ + int pll_error = 0; + + if (priv->pll_off) { + /* Crossed the 14.4MHz boundary, power the VCO back on */ + rc |= r82xx_write_reg_mask(priv, 0x12, 0x80, 0xe0); + priv->pll_off = 0; + } + + /* + * Keep PLL within empirically stable bounds; outside those bounds, + * we prefer to tune to the "wrong" frequency; the difference will be + * mopped up by the 2832 downconverter. + * + * Beware that outside the stable range, the PLL can claim to be locked + * while it is actually stuck at a different frequency (e.g. sometimes + * it can claim to get PLL lock when configured anywhere between 24 and + * 26MHz, but it actually always locks to 26.6-ish). + * + * Make sure to keep the LO away from tuned frequency as there seems + * to be a ~600kHz high-pass filter in the IF path, so you don't want + * any interesting frequencies to land near the IF. + */ + + if (lo_freq < priv->pll_low_limit) { + if (freq > (priv->pll_low_limit-margin) && freq < (priv->pll_low_limit+margin)) { + lo_freq = freq + margin; + } else { + lo_freq = priv->pll_low_limit; + } + } else if (lo_freq > priv->pll_high_limit) { + if (freq > (priv->pll_high_limit-margin) && freq < (priv->pll_high_limit+margin)) { + lo_freq = freq - margin; + } else { + lo_freq = priv->pll_high_limit; + } + } + + pll_error = r82xx_set_pll(priv, lo_freq, lo_freq_out); + if (pll_error == -42) { + /* Magic return value to say that the PLL didn't lock. + * If we are close to the edge of the PLL range, shift the range and try again. + */ + if (lo_freq < PLL_SAFE_LOW) { + priv->pll_low_limit = lo_freq + PLL_STEP_LOW; + if (priv->pll_low_limit > PLL_SAFE_LOW) + priv->pll_low_limit = PLL_SAFE_LOW; + changed_pll_limits = 1; + goto retune; + } else if (lo_freq > PLL_SAFE_HIGH) { + priv->pll_high_limit = lo_freq - PLL_STEP_HIGH; + if (priv->pll_high_limit < PLL_SAFE_HIGH) + priv->pll_high_limit = PLL_SAFE_HIGH; + changed_pll_limits = 1; + goto retune; + } else { + fprintf(stderr, "[r82xx] Failed to get PLL lock at %u Hz\n", lo_freq); + } + } + + rc |= pll_error; } + + if (changed_pll_limits) { + fprintf(stderr, "[r82xx] Updated PLL limits to %u .. %u Hz\n", priv->pll_low_limit, priv->pll_high_limit); + } + + /* IF filter / image rejection settings */ + + if (lo_freq > freq) { + /* high-side mixing, image negative */ + rc |= r82xx_write_reg_mask(priv, 0x07, 0x00, 0x80); + priv->if_filter_freq = lo_freq - freq; + } else { + /* low-side mixing, image positive */ + rc |= r82xx_write_reg_mask(priv, 0x07, 0x80, 0x80); + priv->if_filter_freq = freq - lo_freq; + } + + update_if_filter(priv); + + if (priv->reg_batch) { + rc |= r82xx_write_batch_sync(priv); + } + err: if (rc < 0) fprintf(stderr, "%s: failed=%d\n", __FUNCTION__, rc); @@ -1117,22 +1230,17 @@ int r82xx_set_nomod(struct r82xx_priv *priv) fprintf(stderr, "Using R820T no-mod direct sampling mode\n"); - /* should probably play a bit more with the mux settings - to see if something works even better than this */ + /*rc = r82xx_set_bw(priv, 1000000); + if (rc < 0) + goto err;*/ + /* experimentally determined magic numbers + * needs more experimenting with all the registers */ rc = r82xx_set_mux(priv, 300000000); - if (rc < 0) goto err; + if (rc < 0) + goto err; - /* the VCO frequency setting still seems to have some effect on the noise floor */ - rc = r82xx_set_pll(priv, 50000000); - if (rc < 0) goto err; - - /* the most important part: set a divider number that does not really work */ - rc = r82xx_write_reg_mask(priv, 0x10, 0xd0, 0xe0); - if (rc < 0) goto err; - - /* VCO power off */ - rc = r82xx_write_reg_mask(priv, 0x12, 0xe0, 0xe0); + r82xx_set_pll(priv, 25000000, NULL); err: if (rc < 0) @@ -1282,6 +1390,9 @@ int r82xx_init(struct r82xx_priv *priv) rc |= r82xx_sysfreq_sel(priv, 0, TUNER_DIGITAL_TV, SYS_DVBT); + priv->pll_low_limit = PLL_INITIAL_LOW; + priv->pll_high_limit = PLL_INITIAL_HIGH; + priv->init_done = 1; priv->reg_cache = 1;