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