diff --git a/README b/README index 7f042032..bfe945fd 100644 --- a/README +++ b/README @@ -4,3 +4,132 @@ turns your Realtek RTL2832 based DVB dongle into a SDR receiver For more information see: http://sdr.osmocom.org/trac/wiki/rtl-sdr + + +Notes on the mutability tree (2014/09/29) +========================================= + +Since there seems to be some wider interest in this, some quick notes on +my changes here. + +TL;DR: + +0Hz + . + . "no mod direct sampling" + . no mixing at all, tuner is mostly disabled, just ram as much signal + . as you can at it and maybe some will leak through. + . YMMV a lot + . +~13MHz (PLL lower limit - 14MHz) + . + . normal tuning, large IF. + . high-side mixing + . you will see nasty aliasing / attentuation / harmonics + . near the lower edge of the range + . you will see lots of noise from the dongle itself near 14.4MHz + . +~21MHz (PLL lower limit - 6MHz) <- upstream tuner lower limit + . + . normal tuning, regular IF + . high-side mixing + . this should behave much like upstream + . +~1844Mhz (PLL upper limit - 6MHz) <- upstream tuner upper limit + . + . normal tuning, small IF (getting squashed against the PLL upper bound) + . high-side mixing + . +~1848MHz (PLL upper limit - 2MHz) + . + . tuning with low-side mixing (PLL frequency below tuned frequency) + . you will see nasty aliasing / attentuation / harmonics + . near the upper edge of the range + . +~1864MHz (PLL upper limit + 14MHz) <- that's all, folks + +(PLL limits vary by dongle - some go as high as 1885MHz) + +This tree is a collection of random tuner hacks which are really exploratory +more than anything. They may or may not work for you. + +First some background for those unfamiliar with the internals of the dongle: + + * The R820T tuner has a tunable PLL that can generate frequencies between + around 27MHz .. 1850MHz. The exact range varies from dongle to dongle. + * The tuner mixes the incoming RF signal with this PLL output. This shifts + the RF signal down to a frequency that is the difference between the PLL + frequency and the RF signal's frequency. + * The tuner sends this intermediate frequency (IF) signal to the RTL2838U + * The RTL2838U digitizes the IF signal and does a digital downconversion + step to generate I/Q samples that are centered around "zero". The + downconverter can theoretically handle IF signals up to 14.4MHz. + +The main change is to feed information about the actually-tuned frequency back +from the tuner (R820T) code to the core (RTL2832U) code, allowing the core +code to adjust to the actual IF in use, rather than requiring a fixed IF. The +core code has also been taught how to handle low-side mixing, where the PLL +frequency is lower than the requested frequency (e.g. the spectrum inversion +changes then) + +When tuning, the R820T tuner will try to tune to the requested frequency + +6MHz, producing a 6MHz IF. If the PLL cannot handle that frequency, the tuner +will try a few things to produce something useful. + +At the top of the tuning range: + + * It will tune as high as it can go normally, then stick there producing a + smaller effective IF as the requested frequency gets higher and higher. + * Once the IF starts getting too small (below about 1.5MHz things start to + go bad), it will switch to low-side mixing and try to put the PLL frequency + *below* the target frequency. This gives you another 14-15 MHz at the top + of the range. + +I've had reports that some dongles can tune to within a whisker of 2000MHz +with this. + +At the bottom of the range: + + * It will tune as low as it can go normally, then stick there producing a + larger effective IF as the requested frequency gets lower and lower. + * Once the required IF exceeds 14.4MHz, it will switch to a variant of tejeez' + et al "no mod direct sampling" mode. This essentially disables the PLL in + the tuner entirely, and relies on some RF signal leaking through the tuner + unchanged. The tuner claims to be tuned to 0Hz in this mode, and the core + does all the real tuning. The dongle is almost deaf in this mode, you will + have to turn the RF gain WAY UP. You do not need to mess with direct sampling + settings to enable this, it's integrated with normal tuning. + +The success of the "no mod direct sampling" mode seems to vary a lot from dongle +to dongle. Mine is almost completely deaf so I can't test this much. Others have +had success in tuning to shortwave stations as low as 3.5MHz. + +The actual PLL limits vary from dongle to dongle, and from day to day (they're +probably temperature related). The tuner has three sets of tuning limits: + + * a hardcoded "do not exceed this" set of limits - see PLL_INITIAL_LOW / + PLL_INITIAL_HIGH in tuner_r82xx.c. These are in place because, especially at + the low end of the range, the PLL can get into a state where you ask for (for + example) 25MHz, the PLL claims to be locked OK at that frequency, but in + reality it's actually producing 27MHz, which screws up the core's calculations + of the IF offset needed. + * a hardcoded "known to be OK" set of limits - see PLL_SAFE_LOW / PLL_SAFE_HIGH + in tuner_r82xx.c. This is a range where the PLL should always work; if the PLL + doesn't work in this range it is treated as a tuning error and tuning fails. + * a runtime "seems to be OK at the moment" set of limits. This varies from run + to run and initially starts at the "do not exceed" limits. Whenever a failure + to get PLL lock is seen, the runtime limits are shrunk accordingly and we try + again. This allows the tuner to adapt to the particular dongle in use. + +Remember that at the extreme edges of these ranges you will be fighting an uphill +battle against the dongle design itself, as you will be far out of the intended +range of the filters etc and nothing works particularly well. + +You are likely to see small frequency shifts (maybe 1-2ppm) from what you get in +upstream. This is because the tuner+2832 combination can be tuned a little more +accurately than upstream does, so some errors that you will have previously been +correcting for will disappear. + + +-- Oliver (oliver.jowett@gmail.com / oliver@mutability.co.uk) + diff --git a/include/rtl-sdr.h b/include/rtl-sdr.h index 656c55b9..22da878b 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 79591cea..da4a6542 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 77a87e36..2b4bc79a 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 a3b5359b..6e5d607a 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); - } else { - r |= rtlsdr_set_if_freq(dev, 0); + /* only enable In-phase ADC input */ + r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); + /* Already configured */ + } else { /* 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 @@ int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) /* 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/rtl_test.c b/src/rtl_test.c index ba253909..bcb85ec5 100644 --- a/src/rtl_test.c +++ b/src/rtl_test.c @@ -221,79 +221,157 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) #endif } -void e4k_benchmark(void) -{ - uint32_t freq, gap_start = 0, gap_end = 0; - uint32_t range_start = 0, range_end = 0; +/* Controls the smallest band or band gap that tuner_benchmark() will notice */ +static uint32_t max_step(uint32_t freq) { + if (freq < 1e6) + return 1e4; + if (freq > 1e8) + return 1e6; + return freq / 1e2; +} - fprintf(stderr, "Benchmarking E4000 PLL...\n"); +/* Controls the precision with which tuner_benchmark() will measure the edges of tuning bands */ +static uint32_t min_step(uint32_t freq) { + return 100; +} - /* find tuner range start */ - for (freq = MHZ(70); freq > MHZ(1); freq -= MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - range_start = freq; - break; - } - } +static void report_band_start(uint32_t start) { + fprintf(stderr, "Found a new band starting at %u Hz\n", start); +} - /* find tuner range end */ - for (freq = MHZ(2000); freq < MHZ(2300UL); freq += MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - range_end = freq; - break; - } - } +static void report_band(uint32_t low, uint32_t high) { + fprintf(stderr, "Tuning band: %u - %u Hz\n", low, high); +} - /* find start of L-band gap */ - for (freq = MHZ(1000); freq < MHZ(1300); freq += MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - gap_start = freq; - break; - } +void tuner_benchmark(void) +{ + uint32_t current = max_step(0); + uint32_t band_start = 0; + uint32_t low_bound = 0, high_bound = 0; + char buf[20]; + enum { FIND_START, REFINE_START, FIND_END, REFINE_END } state; + + fprintf(stderr, "Testing tuner range. This may take a couple of minutes..\n"); + + /* Scan for tuneable frequencies coarsely. When we find something, + * do a binary search to narrow down the exact edge of the band. + * + * This can potentially miss bands or band gaps that are smaller than max_step(freq) + * but it is a lot faster than exhaustively scanning everything. + */ + + /* handle bands starting at 0Hz */ + if (rtlsdr_set_center_freq(dev, 0) < 0) + state = FIND_START; + else { + band_start = 0; + report_band_start(band_start); + state = FIND_END; } - /* find end of L-band gap */ - for (freq = MHZ(1300); freq > MHZ(1000); freq -= MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq) < 0) { - gap_end = freq; + while (current < 3e9 && !do_exit) { + switch (state) { + case FIND_START: + /* scanning for the start of a new band */ + if (rtlsdr_set_center_freq(dev, current) < 0) { + /* still looking for a band */ + low_bound = current; + current += max_step(current); + } else { + /* new band, starting somewhere at or before current */ + /* low_bound < start <= current, refine it */ + high_bound = current; + state = REFINE_START; + } break; - } - } - - fprintf(stderr, "E4K range: %i to %i MHz\n", - range_start/MHZ(1) + 1, range_end/MHZ(1) - 1); - fprintf(stderr, "E4K L-band gap: %i to %i MHz\n", - gap_start/MHZ(1), gap_end/MHZ(1)); -} - -void r820t_benchmark(void) -{ - uint32_t freq; - uint32_t range_start = 0, range_end = 0; - - fprintf(stderr, "Benchmarking R820T PLL...\n"); + case REFINE_START: + /* refining the start of a band */ + /* low_bound < bandstart <= high_bound */ + if (rtlsdr_set_center_freq(dev, current) == 0) { + /* current is inside the band */ + /* low_bound < bandstart <= current */ + if (current - low_bound <= min_step(current)) { + /* close enough. Say the band starts at current and go looking for the end of it. */ + band_start = current; + report_band_start(band_start); + low_bound = current; + state = FIND_END; + } else { + /* try halfway between low_bound and current */ + high_bound = current; + current = (current + low_bound) / 2; + } + } else { + /* current is outside the band */ + /* current < bandstart <= high_bound */ + if (high_bound - current <= min_step(current)) { + /* close enough. Say the band starts at high_bound and go looking for the end of it. */ + current = low_bound = band_start = high_bound; + report_band_start(band_start); + state = FIND_END; + } else { + /* try halfway betwen current and high_bound */ + low_bound = current; + current = (current + high_bound) / 2; + } + } + break; - /* find tuner range start */ - for (freq = MHZ(30); freq > MHZ(1); freq -= MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq)) { - break;} - range_start = freq; - } + case FIND_END: + /* scanning for the end of the current band */ + if (rtlsdr_set_center_freq(dev, current) == 0) { + /* still looking for the end of the band */ + low_bound = current; + current += max_step(current); + } else { + /* ran off the end of the band somewhere before current */ + /* low_bound <= bandend < current, refine it */ + high_bound = current; + state = REFINE_END; + } + break; - /* find tuner range end */ - for (freq = MHZ(1750); freq < MHZ(1950UL); freq += MHZ(1)) { - if (rtlsdr_set_center_freq(dev, freq)) { - break;} - range_end = freq; + case REFINE_END: + /* refining the end of a band */ + /* low_bound <= bandend < high_bound */ + if (rtlsdr_set_center_freq(dev, current) < 0) { + /* current is outside the band */ + /* low_bound <= bandend < current */ + if (current - low_bound <= min_step(current)) { + /* close enough. Say the band ends at low_bound and go looking for another band. */ + report_band(band_start, low_bound); + low_bound = current; + state = FIND_START; + } else { + /* try halfway between low_bound and current */ + high_bound = current; + current = (current + low_bound) / 2; + } + } else { + /* current is inside the band */ + /* current <= bandend < high_bound */ + if (high_bound - current <= min_step(current)) { + /* close enough. Say the band ends at current and go looking for another band. */ + report_band(band_start, current); + current = low_bound = high_bound; + state = FIND_START; + } else { + /* try halfway betwen current and high_bound */ + low_bound = current; + current = (current + high_bound) / 2; + } + } + break; + } } - fprintf(stderr, "R820T range: %i to %i MHz\n", - range_start/MHZ(1), range_end/MHZ(1)); + if (state == FIND_END) + report_band(band_start, current); + else if (state == REFINE_END) + report_band(band_start, low_bound); } - - int main(int argc, char **argv) { #ifndef _WIN32 @@ -387,16 +465,7 @@ int main(int argc, char **argv) verbose_set_sample_rate(dev, samp_rate); if (test_mode == TUNER_BENCHMARK) { - switch (rtlsdr_get_tuner_type(dev)) { - case RTLSDR_TUNER_E4000: - e4k_benchmark(); - break; - case RTLSDR_TUNER_R820T: - r820t_benchmark(); - break; - default: - fprintf(stderr, "No testable tuner found, aborting.\n"); - } + tuner_benchmark(); goto exit; } diff --git a/src/tuner_e4k.c b/src/tuner_e4k.c index c2ec0442..561a480b 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 e91198e8..269fb196 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 1885e6 + +/* 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 -1; + 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,25 +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++; - } + + for (mix_div = 2, div_num = 0; mix_div < 64; mix_div <<= 1, div_num++) + if ((freq_khz * mix_div) >= vco_min) break; - } - mix_div = mix_div << 1; - } if (priv->cfg->rafael_chip == CHIP_R828D) vco_power_ref = 1; @@ -542,10 +557,9 @@ static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) nint = (uint32_t) (vco_div / 65536); sdm = (uint32_t) (vco_div % 65536); - if (priv->cfg->rafael_chip == CHIP_R828D && nint > 127) { - fprintf(stderr, "[R828D] No valid PLL values for %u Hz!\n", freq); - return -1; - } else if (nint > 76) { + if (nint < 13 || + (priv->cfg->rafael_chip == CHIP_R828D && nint > 127) || + (priv->cfg->rafael_chip != CHIP_R828D && nint > 76)) { fprintf(stderr, "[R82XX] No valid PLL values for %u Hz!\n", freq); return -1; } @@ -553,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; @@ -570,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; @@ -607,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 */ @@ -902,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) { @@ -946,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) @@ -963,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) @@ -1070,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 @@ -1095,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 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); + rc |= r82xx_write_batch_sync(priv); } + err: if (rc < 0) fprintf(stderr, "%s: failed=%d\n", __FUNCTION__, rc); @@ -1123,7 +1240,7 @@ int r82xx_set_nomod(struct r82xx_priv *priv) if (rc < 0) goto err; - r82xx_set_pll(priv, 25000000); + r82xx_set_pll(priv, 25000000, NULL); err: if (rc < 0) @@ -1273,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;