linux/drivers/media/tuners/fc0011.c
<<
>>
Prefs
   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 */
  15enum {
  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
  38enum 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
  46enum FC11_REG_RCCAL_bits {
  47        FC11_RCCAL_FORCE        = 0x10, /* force */
  48};
  49
  50enum 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
  58struct fc0011_priv {
  59        struct i2c_adapter *i2c;
  60        u8 addr;
  61
  62        u32 frequency;
  63        u32 bandwidth;
  64};
  65
  66
  67static 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
  83static 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
 102static void fc0011_release(struct dvb_frontend *fe)
 103{
 104        kfree(fe->tuner_priv);
 105        fe->tuner_priv = NULL;
 106}
 107
 108static 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 */
 133static 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 */
 148static 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
 163static 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
 439static 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
 448static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
 449{
 450        *frequency = 0;
 451
 452        return 0;
 453}
 454
 455static 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
 464static 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
 482struct 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}
 502EXPORT_SYMBOL(fc0011_attach);
 503
 504MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
 505MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
 506MODULE_LICENSE("GPL");
 507