2  * Fitipower FC0013 tuner driver
 
   4  * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
 
   5  * partially based on driver code from Fitipower
 
   6  * Copyright (C) 2010 Fitipower Integrated Technology Inc
 
   8  * modified for use in librtlsdr
 
   9  * Copyright (C) 2012 Steve Markgraf <steve@steve-m.de>
 
  11  *    This program is free software; you can redistribute it and/or modify
 
  12  *    it under the terms of the GNU General Public License as published by
 
  13  *    the Free Software Foundation; either version 2 of the License, or
 
  14  *    (at your option) any later version.
 
  16  *    This program is distributed in the hope that it will be useful,
 
  17  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  18  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  19  *    GNU General Public License for more details.
 
  21  *    You should have received a copy of the GNU General Public License
 
  22  *    along with this program; if not, write to the Free Software
 
  23  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
  30 #include "rtlsdr_i2c.h"
 
  31 #include "tuner_fc0013.h"
 
  33 static int fc0013_writereg(void *dev, uint8_t reg, uint8_t val)
 
  39         if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, data, 2) < 0)
 
  45 static int fc0013_readreg(void *dev, uint8_t reg, uint8_t *val)
 
  49         if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0)
 
  52         if (rtlsdr_i2c_read_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0)
 
  60 int fc0013_init(void *dev)
 
  65                 0x00,   /* reg. 0x00: dummy */
 
  71                 0x02,   /* reg. 0x06: LPF bandwidth */
 
  72                 0x0a,   /* reg. 0x07: CHECK */
 
  73                 0xff,   /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
 
  75                 0x6e,   /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */
 
  76                 0xb8,   /* reg. 0x0a: Disable LO Test Buffer */
 
  77                 0x82,   /* reg. 0x0b: CHECK */
 
  78                 0xfc,   /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
 
  79                 0x01,   /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */
 
  86                 0x50,   /* reg. 0x14: DVB-t High Gain, UHF.
 
  87                            Middle Gain: 0x48, Low Gain: 0x40 */
 
  91         switch (rtlsdr_get_tuner_clock(dev)) {
 
  93         case FC_XTAL_28_8_MHZ:
 
 103 //      if (dev->dual_master)
 
 106         for (i = 1; i < sizeof(reg); i++) {
 
 107                 ret = fc0013_writereg(dev, i, reg[i]);
 
 115 int fc0013_rc_cal_add(void *dev, int rc_val)
 
 121         /* push rc_cal value, get rc_cal value */
 
 122         ret = fc0013_writereg(dev, 0x10, 0x00);
 
 126         /* get rc_cal value */
 
 127         ret = fc0013_readreg(dev, 0x10, &rc_cal);
 
 133         val = (int)rc_cal + rc_val;
 
 136         ret = fc0013_writereg(dev, 0x0d, 0x11);
 
 140         /* modify rc_cal value */
 
 142                 ret = fc0013_writereg(dev, 0x10, 0x0f);
 
 144                 ret = fc0013_writereg(dev, 0x10, 0x00);
 
 146                 ret = fc0013_writereg(dev, 0x10, (uint8_t)val);
 
 152 int fc0013_rc_cal_reset(void *dev)
 
 156         ret = fc0013_writereg(dev, 0x0d, 0x01);
 
 158                 ret = fc0013_writereg(dev, 0x10, 0x00);
 
 163 static int fc0013_set_vhf_track(void *dev, uint32_t freq)
 
 168         ret = fc0013_readreg(dev, 0x1d, &tmp);
 
 172         if (freq <= 177500000) {                /* VHF Track: 7 */
 
 173                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c);
 
 174         } else if (freq <= 184500000) { /* VHF Track: 6 */
 
 175                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x18);
 
 176         } else if (freq <= 191500000) { /* VHF Track: 5 */
 
 177                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x14);
 
 178         } else if (freq <= 198500000) { /* VHF Track: 4 */
 
 179                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x10);
 
 180         } else if (freq <= 205500000) { /* VHF Track: 3 */
 
 181                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x0c);
 
 182         } else if (freq <= 219500000) { /* VHF Track: 2 */
 
 183                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x08);
 
 184         } else if (freq < 300000000) {          /* VHF Track: 1 */
 
 185                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x04);
 
 186         } else {                                /* UHF and GPS */
 
 187                 ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c);
 
 194 int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth)
 
 197         uint8_t reg[7], am, pm, multi, tmp;
 
 199         uint32_t xtal_freq_div_2;
 
 203         xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2;
 
 206         ret = fc0013_set_vhf_track(dev, freq);
 
 210         if (freq < 300000000) {
 
 211                 /* enable VHF filter */
 
 212                 ret = fc0013_readreg(dev, 0x07, &tmp);
 
 215                 ret = fc0013_writereg(dev, 0x07, tmp | 0x10);
 
 219                 /* disable UHF & disable GPS */
 
 220                 ret = fc0013_readreg(dev, 0x14, &tmp);
 
 223                 ret = fc0013_writereg(dev, 0x14, tmp & 0x1f);
 
 226         } else if (freq <= 862000000) {
 
 227                 /* disable VHF filter */
 
 228                 ret = fc0013_readreg(dev, 0x07, &tmp);
 
 231                 ret = fc0013_writereg(dev, 0x07, tmp & 0xef);
 
 235                 /* enable UHF & disable GPS */
 
 236                 ret = fc0013_readreg(dev, 0x14, &tmp);
 
 239                 ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x40);
 
 243                 /* disable VHF filter */
 
 244                 ret = fc0013_readreg(dev, 0x07, &tmp);
 
 247                 ret = fc0013_writereg(dev, 0x07, tmp & 0xef);
 
 251                 /* disable UHF & enable GPS */
 
 252                 ret = fc0013_readreg(dev, 0x14, &tmp);
 
 255                 ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x20);
 
 260         /* select frequency divider and the frequency of VCO */
 
 261         if (freq < 37084000) {          /* freq * 96 < 3560000000 */
 
 265         } else if (freq < 55625000) {   /* freq * 64 < 3560000000 */
 
 269         } else if (freq < 74167000) {   /* freq * 48 < 3560000000 */
 
 273         } else if (freq < 111250000) {  /* freq * 32 < 3560000000 */
 
 277         } else if (freq < 148334000) {  /* freq * 24 < 3560000000 */
 
 281         } else if (freq < 222500000) {  /* freq * 16 < 3560000000 */
 
 285         } else if (freq < 296667000) {  /* freq * 12 < 3560000000 */
 
 289         } else if (freq < 445000000) {  /* freq * 8 < 3560000000 */
 
 293         } else if (freq < 593334000) {  /* freq * 6 < 3560000000 */
 
 297         } else if (freq < 950000000) {  /* freq * 4 < 3800000000 */
 
 307         f_vco = freq * multi;
 
 309         if (f_vco >= 3060000000U) {
 
 314         /* From divided value (XDIV) determined the FA and FP value */
 
 315         xdiv = (uint16_t)(f_vco / xtal_freq_div_2);
 
 316         if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2))
 
 319         pm = (uint8_t)(xdiv / 8);
 
 320         am = (uint8_t)(xdiv - (8 * pm));
 
 328                 reg[1] = am + (8 * (pm - 31));
 
 335         if ((reg[1] > 15) || (reg[2] < 0x0b)) {
 
 336                 fprintf(stderr, "[FC0013] no valid PLL combination "
 
 337                                 "found for %u Hz!\n", freq);
 
 344         /* From VCO frequency determines the XIN ( fractional part of Delta
 
 345            Sigma PLL) and divided value (XDIV) */
 
 346         xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000);
 
 347         xin = (xin << 15) / (xtal_freq_div_2 / 1000);
 
 354         reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */
 
 367         /* modified for Realtek demod */
 
 370         for (i = 1; i <= 6; i++) {
 
 371                 ret = fc0013_writereg(dev, i, reg[i]);
 
 376         ret = fc0013_readreg(dev, 0x11, &tmp);
 
 380                 ret = fc0013_writereg(dev, 0x11, tmp | 0x04);
 
 382                 ret = fc0013_writereg(dev, 0x11, tmp & 0xfb);
 
 386         /* VCO Calibration */
 
 387         ret = fc0013_writereg(dev, 0x0e, 0x80);
 
 389                 ret = fc0013_writereg(dev, 0x0e, 0x00);
 
 391         /* VCO Re-Calibration if needed */
 
 393                 ret = fc0013_writereg(dev, 0x0e, 0x00);
 
 397                 ret = fc0013_readreg(dev, 0x0e, &tmp);
 
 408                         ret = fc0013_writereg(dev, 0x06, reg[6]);
 
 410                                 ret = fc0013_writereg(dev, 0x0e, 0x80);
 
 412                                 ret = fc0013_writereg(dev, 0x0e, 0x00);
 
 417                         ret = fc0013_writereg(dev, 0x06, reg[6]);
 
 419                                 ret = fc0013_writereg(dev, 0x0e, 0x80);
 
 421                                 ret = fc0013_writereg(dev, 0x0e, 0x00);
 
 429 int fc0013_set_gain_mode(void *dev, int manual)
 
 434         ret |= fc0013_readreg(dev, 0x0d, &tmp);
 
 441         ret |= fc0013_writereg(dev, 0x0d, tmp);
 
 443         /* set a fixed IF-gain for now */
 
 444         ret |= fc0013_writereg(dev, 0x13, 0x0a);
 
 449 int fc0013_lna_gains[] ={
 
 476 #define GAIN_CNT        (sizeof(fc0013_lna_gains) / sizeof(int) / 2)
 
 478 int fc0013_set_lna_gain(void *dev, int gain)
 
 484         ret |= fc0013_readreg(dev, 0x14, &tmp);
 
 489         for (i = 0; i < GAIN_CNT; i++) {
 
 490                 if ((fc0013_lna_gains[i*2] >= gain) || (i+1 == GAIN_CNT)) {
 
 491                         tmp |= fc0013_lna_gains[i*2 + 1];
 
 497         ret |= fc0013_writereg(dev, 0x14, tmp);