root/drivers/media/tuners/fc0011.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fc0011_writereg
  2. fc0011_readreg
  3. fc0011_release
  4. fc0011_init
  5. fc0011_vcocal_trigger
  6. fc0011_vcocal_read
  7. fc0011_set_params
  8. fc0011_get_frequency
  9. fc0011_get_if_frequency
  10. fc0011_get_bandwidth
  11. fc0011_attach

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Fitipower FC0011 tuner driver
   4  *
   5  * Copyright (C) 2012 Michael Buesch <m@bues.ch>
   6  *
   7  * Derived from FC0012 tuner driver:
   8  * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
   9  */
  10 
  11 #include "fc0011.h"
  12 
  13 
  14 /* Tuner registers */
  15 enum {
  16         FC11_REG_0,
  17         FC11_REG_FA,            /* FA */
  18         FC11_REG_FP,            /* FP */
  19         FC11_REG_XINHI,         /* XIN high 8 bit */
  20         FC11_REG_XINLO,         /* XIN low 8 bit */
  21         FC11_REG_VCO,           /* VCO */
  22         FC11_REG_VCOSEL,        /* VCO select */
  23         FC11_REG_7,             /* Unknown tuner reg 7 */
  24         FC11_REG_8,             /* Unknown tuner reg 8 */
  25         FC11_REG_9,
  26         FC11_REG_10,            /* Unknown tuner reg 10 */
  27         FC11_REG_11,            /* Unknown tuner reg 11 */
  28         FC11_REG_12,
  29         FC11_REG_RCCAL,         /* RC calibrate */
  30         FC11_REG_VCOCAL,        /* VCO calibrate */
  31         FC11_REG_15,
  32         FC11_REG_16,            /* Unknown tuner reg 16 */
  33         FC11_REG_17,
  34 
  35         FC11_NR_REGS,           /* Number of registers */
  36 };
  37 
  38 enum FC11_REG_VCOSEL_bits {
  39         FC11_VCOSEL_2           = 0x08, /* VCO select 2 */
  40         FC11_VCOSEL_1           = 0x10, /* VCO select 1 */
  41         FC11_VCOSEL_CLKOUT      = 0x20, /* Fix clock out */
  42         FC11_VCOSEL_BW7M        = 0x40, /* 7MHz bw */
  43         FC11_VCOSEL_BW6M        = 0x80, /* 6MHz bw */
  44 };
  45 
  46 enum FC11_REG_RCCAL_bits {
  47         FC11_RCCAL_FORCE        = 0x10, /* force */
  48 };
  49 
  50 enum FC11_REG_VCOCAL_bits {
  51         FC11_VCOCAL_RUN         = 0,    /* VCO calibration run */
  52         FC11_VCOCAL_VALUEMASK   = 0x3F, /* VCO calibration value mask */
  53         FC11_VCOCAL_OK          = 0x40, /* VCO calibration Ok */
  54         FC11_VCOCAL_RESET       = 0x80, /* VCO calibration reset */
  55 };
  56 
  57 
  58 struct fc0011_priv {
  59         struct i2c_adapter *i2c;
  60         u8 addr;
  61 
  62         u32 frequency;
  63         u32 bandwidth;
  64 };
  65 
  66 
  67 static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
  68 {
  69         u8 buf[2] = { reg, val };
  70         struct i2c_msg msg = { .addr = priv->addr,
  71                 .flags = 0, .buf = buf, .len = 2 };
  72 
  73         if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
  74                 dev_err(&priv->i2c->dev,
  75                         "I2C write reg failed, reg: %02x, val: %02x\n",
  76                         reg, val);
  77                 return -EIO;
  78         }
  79 
  80         return 0;
  81 }
  82 
  83 static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
  84 {
  85         u8 dummy;
  86         struct i2c_msg msg[2] = {
  87                 { .addr = priv->addr,
  88                   .flags = 0, .buf = &reg, .len = 1 },
  89                 { .addr = priv->addr,
  90                   .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 },
  91         };
  92 
  93         if (i2c_transfer(priv->i2c, msg, 2) != 2) {
  94                 dev_err(&priv->i2c->dev,
  95                         "I2C read failed, reg: %02x\n", reg);
  96                 return -EIO;
  97         }
  98 
  99         return 0;
 100 }
 101 
 102 static void fc0011_release(struct dvb_frontend *fe)
 103 {
 104         kfree(fe->tuner_priv);
 105         fe->tuner_priv = NULL;
 106 }
 107 
 108 static int fc0011_init(struct dvb_frontend *fe)
 109 {
 110         struct fc0011_priv *priv = fe->tuner_priv;
 111         int err;
 112 
 113         if (WARN_ON(!fe->callback))
 114                 return -EINVAL;
 115 
 116         err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
 117                            FC0011_FE_CALLBACK_POWER, priv->addr);
 118         if (err) {
 119                 dev_err(&priv->i2c->dev, "Power-on callback failed\n");
 120                 return err;
 121         }
 122         err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
 123                            FC0011_FE_CALLBACK_RESET, priv->addr);
 124         if (err) {
 125                 dev_err(&priv->i2c->dev, "Reset callback failed\n");
 126                 return err;
 127         }
 128 
 129         return 0;
 130 }
 131 
 132 /* Initiate VCO calibration */
 133 static int fc0011_vcocal_trigger(struct fc0011_priv *priv)
 134 {
 135         int err;
 136 
 137         err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
 138         if (err)
 139                 return err;
 140         err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
 141         if (err)
 142                 return err;
 143 
 144         return 0;
 145 }
 146 
 147 /* Read VCO calibration value */
 148 static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value)
 149 {
 150         int err;
 151 
 152         err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
 153         if (err)
 154                 return err;
 155         usleep_range(10000, 20000);
 156         err = fc0011_readreg(priv, FC11_REG_VCOCAL, value);
 157         if (err)
 158                 return err;
 159 
 160         return 0;
 161 }
 162 
 163 static int fc0011_set_params(struct dvb_frontend *fe)
 164 {
 165         struct dtv_frontend_properties *p = &fe->dtv_property_cache;
 166         struct fc0011_priv *priv = fe->tuner_priv;
 167         int err;
 168         unsigned int i, vco_retries;
 169         u32 freq = p->frequency / 1000;
 170         u32 bandwidth = p->bandwidth_hz / 1000;
 171         u32 fvco, xin, frac, xdiv, xdivr;
 172         u8 fa, fp, vco_sel, vco_cal;
 173         u8 regs[FC11_NR_REGS] = { };
 174 
 175         regs[FC11_REG_7] = 0x0F;
 176         regs[FC11_REG_8] = 0x3E;
 177         regs[FC11_REG_10] = 0xB8;
 178         regs[FC11_REG_11] = 0x80;
 179         regs[FC11_REG_RCCAL] = 0x04;
 180         err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
 181         err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
 182         err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
 183         err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
 184         err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
 185         if (err)
 186                 return -EIO;
 187 
 188         /* Set VCO freq and VCO div */
 189         if (freq < 54000) {
 190                 fvco = freq * 64;
 191                 regs[FC11_REG_VCO] = 0x82;
 192         } else if (freq < 108000) {
 193                 fvco = freq * 32;
 194                 regs[FC11_REG_VCO] = 0x42;
 195         } else if (freq < 216000) {
 196                 fvco = freq * 16;
 197                 regs[FC11_REG_VCO] = 0x22;
 198         } else if (freq < 432000) {
 199                 fvco = freq * 8;
 200                 regs[FC11_REG_VCO] = 0x12;
 201         } else {
 202                 fvco = freq * 4;
 203                 regs[FC11_REG_VCO] = 0x0A;
 204         }
 205 
 206         /* Calc XIN. The PLL reference frequency is 18 MHz. */
 207         xdiv = fvco / 18000;
 208         WARN_ON(xdiv > 0xFF);
 209         frac = fvco - xdiv * 18000;
 210         frac = (frac << 15) / 18000;
 211         if (frac >= 16384)
 212                 frac += 32786;
 213         if (!frac)
 214                 xin = 0;
 215         else
 216                 xin = clamp_t(u32, frac, 512, 65024);
 217         regs[FC11_REG_XINHI] = xin >> 8;
 218         regs[FC11_REG_XINLO] = xin;
 219 
 220         /* Calc FP and FA */
 221         xdivr = xdiv;
 222         if (fvco - xdiv * 18000 >= 9000)
 223                 xdivr += 1; /* round */
 224         fp = xdivr / 8;
 225         fa = xdivr - fp * 8;
 226         if (fa < 2) {
 227                 fp -= 1;
 228                 fa += 8;
 229         }
 230         if (fp > 0x1F) {
 231                 fp = 0x1F;
 232                 fa = 0xF;
 233         }
 234         if (fa >= fp) {
 235                 dev_warn(&priv->i2c->dev,
 236                          "fa %02X >= fp %02X, but trying to continue\n",
 237                          (unsigned int)(u8)fa, (unsigned int)(u8)fp);
 238         }
 239         regs[FC11_REG_FA] = fa;
 240         regs[FC11_REG_FP] = fp;
 241 
 242         /* Select bandwidth */
 243         switch (bandwidth) {
 244         case 8000:
 245                 break;
 246         case 7000:
 247                 regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M;
 248                 break;
 249         default:
 250                 dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. Using 6000 kHz.\n",
 251                          bandwidth);
 252                 bandwidth = 6000;
 253                 /* fallthrough */
 254         case 6000:
 255                 regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M;
 256                 break;
 257         }
 258 
 259         /* Pre VCO select */
 260         if (fvco < 2320000) {
 261                 vco_sel = 0;
 262                 regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 263         } else if (fvco < 3080000) {
 264                 vco_sel = 1;
 265                 regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 266                 regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 267         } else {
 268                 vco_sel = 2;
 269                 regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 270                 regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
 271         }
 272 
 273         /* Fix for low freqs */
 274         if (freq < 45000) {
 275                 regs[FC11_REG_FA] = 0x6;
 276                 regs[FC11_REG_FP] = 0x11;
 277         }
 278 
 279         /* Clock out fix */
 280         regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT;
 281 
 282         /* Write the cached registers */
 283         for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) {
 284                 err = fc0011_writereg(priv, i, regs[i]);
 285                 if (err)
 286                         return err;
 287         }
 288 
 289         /* VCO calibration */
 290         err = fc0011_vcocal_trigger(priv);
 291         if (err)
 292                 return err;
 293         err = fc0011_vcocal_read(priv, &vco_cal);
 294         if (err)
 295                 return err;
 296         vco_retries = 0;
 297         while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) {
 298                 /* Reset the tuner and try again */
 299                 err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
 300                                    FC0011_FE_CALLBACK_RESET, priv->addr);
 301                 if (err) {
 302                         dev_err(&priv->i2c->dev, "Failed to reset tuner\n");
 303                         return err;
 304                 }
 305                 /* Reinit tuner config */
 306                 err = 0;
 307                 for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++)
 308                         err |= fc0011_writereg(priv, i, regs[i]);
 309                 err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
 310                 err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
 311                 err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
 312                 err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
 313                 err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
 314                 if (err)
 315                         return -EIO;
 316                 /* VCO calibration */
 317                 err = fc0011_vcocal_trigger(priv);
 318                 if (err)
 319                         return err;
 320                 err = fc0011_vcocal_read(priv, &vco_cal);
 321                 if (err)
 322                         return err;
 323                 vco_retries++;
 324         }
 325         if (!(vco_cal & FC11_VCOCAL_OK)) {
 326                 dev_err(&priv->i2c->dev,
 327                         "Failed to read VCO calibration value (got %02X)\n",
 328                         (unsigned int)vco_cal);
 329                 return -EIO;
 330         }
 331         vco_cal &= FC11_VCOCAL_VALUEMASK;
 332 
 333         switch (vco_sel) {
 334         default:
 335                 WARN_ON(1);
 336                 return -EINVAL;
 337         case 0:
 338                 if (vco_cal < 8) {
 339                         regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 340                         regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 341                         err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 342                                               regs[FC11_REG_VCOSEL]);
 343                         if (err)
 344                                 return err;
 345                         err = fc0011_vcocal_trigger(priv);
 346                         if (err)
 347                                 return err;
 348                 } else {
 349                         regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 350                         err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 351                                               regs[FC11_REG_VCOSEL]);
 352                         if (err)
 353                                 return err;
 354                 }
 355                 break;
 356         case 1:
 357                 if (vco_cal < 5) {
 358                         regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 359                         regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
 360                         err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 361                                               regs[FC11_REG_VCOSEL]);
 362                         if (err)
 363                                 return err;
 364                         err = fc0011_vcocal_trigger(priv);
 365                         if (err)
 366                                 return err;
 367                 } else if (vco_cal <= 48) {
 368                         regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 369                         regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 370                         err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 371                                               regs[FC11_REG_VCOSEL]);
 372                         if (err)
 373                                 return err;
 374                 } else {
 375                         regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 376                         err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 377                                               regs[FC11_REG_VCOSEL]);
 378                         if (err)
 379                                 return err;
 380                         err = fc0011_vcocal_trigger(priv);
 381                         if (err)
 382                                 return err;
 383                 }
 384                 break;
 385         case 2:
 386                 if (vco_cal > 53) {
 387                         regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 388                         regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
 389                         err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 390                                               regs[FC11_REG_VCOSEL]);
 391                         if (err)
 392                                 return err;
 393                         err = fc0011_vcocal_trigger(priv);
 394                         if (err)
 395                                 return err;
 396                 } else {
 397                         regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
 398                         regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
 399                         err = fc0011_writereg(priv, FC11_REG_VCOSEL,
 400                                               regs[FC11_REG_VCOSEL]);
 401                         if (err)
 402                                 return err;
 403                 }
 404                 break;
 405         }
 406         err = fc0011_vcocal_read(priv, NULL);
 407         if (err)
 408                 return err;
 409         usleep_range(10000, 50000);
 410 
 411         err = fc0011_readreg(priv, FC11_REG_RCCAL, &regs[FC11_REG_RCCAL]);
 412         if (err)
 413                 return err;
 414         regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
 415         err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
 416         if (err)
 417                 return err;
 418         regs[FC11_REG_16] = 0xB;
 419         err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]);
 420         if (err)
 421                 return err;
 422 
 423         dev_dbg(&priv->i2c->dev, "Tuned to fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X vcocal=%02X(%u) bw=%u\n",
 424                 (unsigned int)regs[FC11_REG_FA],
 425                 (unsigned int)regs[FC11_REG_FP],
 426                 (unsigned int)regs[FC11_REG_XINHI],
 427                 (unsigned int)regs[FC11_REG_XINLO],
 428                 (unsigned int)regs[FC11_REG_VCO],
 429                 (unsigned int)regs[FC11_REG_VCOSEL],
 430                 (unsigned int)vco_cal, vco_retries,
 431                 (unsigned int)bandwidth);
 432 
 433         priv->frequency = p->frequency;
 434         priv->bandwidth = p->bandwidth_hz;
 435 
 436         return 0;
 437 }
 438 
 439 static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 440 {
 441         struct fc0011_priv *priv = fe->tuner_priv;
 442 
 443         *frequency = priv->frequency;
 444 
 445         return 0;
 446 }
 447 
 448 static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 449 {
 450         *frequency = 0;
 451 
 452         return 0;
 453 }
 454 
 455 static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
 456 {
 457         struct fc0011_priv *priv = fe->tuner_priv;
 458 
 459         *bandwidth = priv->bandwidth;
 460 
 461         return 0;
 462 }
 463 
 464 static const struct dvb_tuner_ops fc0011_tuner_ops = {
 465         .info = {
 466                 .name             = "Fitipower FC0011",
 467 
 468                 .frequency_min_hz =   45 * MHz,
 469                 .frequency_max_hz = 1000 * MHz,
 470         },
 471 
 472         .release                = fc0011_release,
 473         .init                   = fc0011_init,
 474 
 475         .set_params             = fc0011_set_params,
 476 
 477         .get_frequency          = fc0011_get_frequency,
 478         .get_if_frequency       = fc0011_get_if_frequency,
 479         .get_bandwidth          = fc0011_get_bandwidth,
 480 };
 481 
 482 struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
 483                                    struct i2c_adapter *i2c,
 484                                    const struct fc0011_config *config)
 485 {
 486         struct fc0011_priv *priv;
 487 
 488         priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
 489         if (!priv)
 490                 return NULL;
 491 
 492         priv->i2c = i2c;
 493         priv->addr = config->i2c_address;
 494 
 495         fe->tuner_priv = priv;
 496         fe->ops.tuner_ops = fc0011_tuner_ops;
 497 
 498         dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n");
 499 
 500         return fe;
 501 }
 502 EXPORT_SYMBOL(fc0011_attach);
 503 
 504 MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
 505 MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
 506 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */