linux/drivers/media/dvb-frontends/cx24113.c
<<
>>
Prefs
   1/*
   2 *  Driver for Conexant CX24113/CX24128 Tuner (Satellite)
   3 *
   4 *  Copyright (C) 2007-8 Patrick Boettcher <pb@linuxtv.org>
   5 *
   6 *  Developed for BBTI / Technisat
   7 *
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License as published by
  10 *  the Free Software Foundation; either version 2 of the License, or
  11 *  (at your option) any later version.
  12 *
  13 *  This program is distributed in the hope that it will be useful,
  14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 *
  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 <linux/slab.h>
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/init.h>
  28
  29#include "dvb_frontend.h"
  30#include "cx24113.h"
  31
  32static int debug;
  33
  34#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0)
  35#define cx_err(args...)  do { printk(KERN_ERR  "CX24113: " args); } while (0)
  36
  37#define dprintk(args...) \
  38        do { \
  39                if (debug) { \
  40                        printk(KERN_DEBUG "CX24113: %s: ", __func__); \
  41                        printk(args); \
  42                } \
  43        } while (0)
  44
  45struct cx24113_state {
  46        struct i2c_adapter *i2c;
  47        const struct cx24113_config *config;
  48
  49#define REV_CX24113 0x23
  50        u8 rev;
  51        u8 ver;
  52
  53        u8 icp_mode:1;
  54
  55#define ICP_LEVEL1 0
  56#define ICP_LEVEL2 1
  57#define ICP_LEVEL3 2
  58#define ICP_LEVEL4 3
  59        u8 icp_man:2;
  60        u8 icp_auto_low:2;
  61        u8 icp_auto_mlow:2;
  62        u8 icp_auto_mhi:2;
  63        u8 icp_auto_hi:2;
  64        u8 icp_dig;
  65
  66#define LNA_MIN_GAIN 0
  67#define LNA_MID_GAIN 1
  68#define LNA_MAX_GAIN 2
  69        u8 lna_gain:2;
  70
  71        u8 acp_on:1;
  72
  73        u8 vco_mode:2;
  74        u8 vco_shift:1;
  75#define VCOBANDSEL_6 0x80
  76#define VCOBANDSEL_5 0x01
  77#define VCOBANDSEL_4 0x02
  78#define VCOBANDSEL_3 0x04
  79#define VCOBANDSEL_2 0x08
  80#define VCOBANDSEL_1 0x10
  81        u8 vco_band;
  82
  83#define VCODIV4 4
  84#define VCODIV2 2
  85        u8 vcodiv;
  86
  87        u8 bs_delay:4;
  88        u16 bs_freqcnt:13;
  89        u16 bs_rdiv;
  90        u8 prescaler_mode:1;
  91
  92        u8 rfvga_bias_ctrl;
  93
  94        s16 tuner_gain_thres;
  95        u8  gain_level;
  96
  97        u32 frequency;
  98
  99        u8 refdiv;
 100
 101        u8 Fwindow_enabled;
 102};
 103
 104static int cx24113_writereg(struct cx24113_state *state, int reg, int data)
 105{
 106        u8 buf[] = { reg, data };
 107        struct i2c_msg msg = { .addr = state->config->i2c_addr,
 108                .flags = 0, .buf = buf, .len = 2 };
 109        int err = i2c_transfer(state->i2c, &msg, 1);
 110        if (err != 1) {
 111                printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x,"
 112                         " data == 0x%02x)\n", __func__, err, reg, data);
 113                return err;
 114        }
 115
 116        return 0;
 117}
 118
 119static int cx24113_readreg(struct cx24113_state *state, u8 reg)
 120{
 121        int ret;
 122        u8 b;
 123        struct i2c_msg msg[] = {
 124                { .addr = state->config->i2c_addr,
 125                        .flags = 0, .buf = &reg, .len = 1 },
 126                { .addr = state->config->i2c_addr,
 127                        .flags = I2C_M_RD, .buf = &b, .len = 1 }
 128        };
 129
 130        ret = i2c_transfer(state->i2c, msg, 2);
 131
 132        if (ret != 2) {
 133                printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n",
 134                        __func__, reg, ret);
 135                return ret;
 136        }
 137
 138        return b;
 139}
 140
 141static void cx24113_set_parameters(struct cx24113_state *state)
 142{
 143        u8 r;
 144
 145        r = cx24113_readreg(state, 0x10) & 0x82;
 146        r |= state->icp_mode;
 147        r |= state->icp_man << 4;
 148        r |= state->icp_dig << 2;
 149        r |= state->prescaler_mode << 5;
 150        cx24113_writereg(state, 0x10, r);
 151
 152        r = (state->icp_auto_low  << 0) | (state->icp_auto_mlow << 2)
 153                | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6);
 154        cx24113_writereg(state, 0x11, r);
 155
 156        if (state->rev == REV_CX24113) {
 157                r = cx24113_readreg(state, 0x20) & 0xec;
 158                r |= state->lna_gain;
 159                r |= state->rfvga_bias_ctrl << 4;
 160                cx24113_writereg(state, 0x20, r);
 161        }
 162
 163        r = cx24113_readreg(state, 0x12) & 0x03;
 164        r |= state->acp_on << 2;
 165        r |= state->bs_delay << 4;
 166        cx24113_writereg(state, 0x12, r);
 167
 168        r = cx24113_readreg(state, 0x18) & 0x40;
 169        r |= state->vco_shift;
 170        if (state->vco_band == VCOBANDSEL_6)
 171                r |= (1 << 7);
 172        else
 173                r |= (state->vco_band << 1);
 174        cx24113_writereg(state, 0x18, r);
 175
 176        r  = cx24113_readreg(state, 0x14) & 0x20;
 177        r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f);
 178        cx24113_writereg(state, 0x14, r);
 179        cx24113_writereg(state, 0x15, (state->bs_freqcnt        & 0xff));
 180
 181        cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff);
 182        r = (cx24113_readreg(state, 0x17) & 0x0f) |
 183                ((state->bs_rdiv & 0x0f) << 4);
 184        cx24113_writereg(state, 0x17, r);
 185}
 186
 187#define VGA_0 0x00
 188#define VGA_1 0x04
 189#define VGA_2 0x02
 190#define VGA_3 0x06
 191#define VGA_4 0x01
 192#define VGA_5 0x05
 193#define VGA_6 0x03
 194#define VGA_7 0x07
 195
 196#define RFVGA_0 0x00
 197#define RFVGA_1 0x01
 198#define RFVGA_2 0x02
 199#define RFVGA_3 0x03
 200
 201static int cx24113_set_gain_settings(struct cx24113_state *state,
 202                s16 power_estimation)
 203{
 204        u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0,
 205           vga    = cx24113_readreg(state, 0x1f) & 0x3f,
 206           rfvga  = cx24113_readreg(state, 0x20) & 0xf3;
 207        u8 gain_level = power_estimation >= state->tuner_gain_thres;
 208
 209        dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n",
 210                        power_estimation, state->tuner_gain_thres,
 211                        state->gain_level, gain_level);
 212
 213        if (gain_level == state->gain_level)
 214                return 0; /* nothing to be done */
 215
 216        ampout |= 0xf;
 217
 218        if (gain_level) {
 219                rfvga |= RFVGA_0 << 2;
 220                vga   |= (VGA_7 << 3) | VGA_7;
 221        } else {
 222                rfvga |= RFVGA_2 << 2;
 223                vga  |= (VGA_6 << 3) | VGA_2;
 224        }
 225        state->gain_level = gain_level;
 226
 227        cx24113_writereg(state, 0x1d, ampout);
 228        cx24113_writereg(state, 0x1f, vga);
 229        cx24113_writereg(state, 0x20, rfvga);
 230
 231        return 1; /* did something */
 232}
 233
 234static int cx24113_set_Fref(struct cx24113_state *state, u8 high)
 235{
 236        u8 xtal = cx24113_readreg(state, 0x02);
 237        if (state->rev == 0x43 && state->vcodiv == VCODIV4)
 238                high = 1;
 239
 240        xtal &= ~0x2;
 241        if (high)
 242                xtal |= high << 1;
 243        return cx24113_writereg(state, 0x02, xtal);
 244}
 245
 246static int cx24113_enable(struct cx24113_state *state, u8 enable)
 247{
 248        u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable;
 249        if (state->rev == REV_CX24113)
 250                r21 |= (1 << 1);
 251        return cx24113_writereg(state, 0x21, r21);
 252}
 253
 254static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz)
 255{
 256        u8 r;
 257
 258        if (bandwidth_khz <= 19000)
 259                r = 0x03 << 6;
 260        else if (bandwidth_khz <= 25000)
 261                r = 0x02 << 6;
 262        else
 263                r = 0x01 << 6;
 264
 265        dprintk("bandwidth to be set: %d\n", bandwidth_khz);
 266        bandwidth_khz *= 10;
 267        bandwidth_khz -= 10000;
 268        bandwidth_khz /= 1000;
 269        bandwidth_khz += 5;
 270        bandwidth_khz /= 10;
 271
 272        dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz);
 273
 274        r |= bandwidth_khz & 0x3f;
 275
 276        return cx24113_writereg(state, 0x1e, r);
 277}
 278
 279static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on)
 280{
 281        u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7);
 282        return cx24113_writereg(state, 0x10, r);
 283}
 284
 285static int cx24113_get_status(struct dvb_frontend *fe, u32 *status)
 286{
 287        struct cx24113_state *state = fe->tuner_priv;
 288        u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1;
 289        if (r)
 290                *status |= TUNER_STATUS_LOCKED;
 291        dprintk("PLL locked: %d\n", r);
 292        return 0;
 293}
 294
 295static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv)
 296{
 297        if (state->rev == 0x43 && state->vcodiv == VCODIV4)
 298                refdiv = 2;
 299        return state->refdiv = refdiv;
 300}
 301
 302static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
 303{
 304        s32 N;
 305        s64 F;
 306        u64 dividend;
 307        u8 R, r;
 308        u8 vcodiv;
 309        u8 factor;
 310        s32 freq_hz = state->frequency * 1000;
 311
 312        if (state->config->xtal_khz < 20000)
 313                factor = 1;
 314        else
 315                factor = 2;
 316
 317        if (state->rev == REV_CX24113) {
 318                if (state->frequency >= 1100000)
 319                        vcodiv = VCODIV2;
 320                else
 321                        vcodiv = VCODIV4;
 322        } else {
 323                if (state->frequency >= 1165000)
 324                        vcodiv = VCODIV2;
 325                else
 326                        vcodiv = VCODIV4;
 327        }
 328        state->vcodiv = vcodiv;
 329
 330        dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv);
 331        R = 0;
 332        do {
 333                R = cx24113_set_ref_div(state, R + 1);
 334
 335                /* calculate tuner PLL settings: */
 336                N =  (freq_hz / 100 * vcodiv) * R;
 337                N /= (state->config->xtal_khz) * factor * 2;
 338                N += 5;     /* For round up. */
 339                N /= 10;
 340                N -= 32;
 341        } while (N < 6 && R < 3);
 342
 343        if (N < 6) {
 344                cx_err("strange frequency: N < 6\n");
 345                return;
 346        }
 347        F = freq_hz;
 348        F *= (u64) (R * vcodiv * 262144);
 349        dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
 350        /* do_div needs an u64 as first argument */
 351        dividend = F;
 352        do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
 353        F = dividend;
 354        dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
 355        F -= (N + 32) * 262144;
 356
 357        dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
 358
 359        if (state->Fwindow_enabled) {
 360                if (F > (262144 / 2 - 1638))
 361                        F = 262144 / 2 - 1638;
 362                if (F < (-262144 / 2 + 1638))
 363                        F = -262144 / 2 + 1638;
 364                if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) {
 365                        F = 0;
 366                        r = cx24113_readreg(state, 0x10);
 367                        cx24113_writereg(state, 0x10, r | (1 << 6));
 368                }
 369        }
 370        dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
 371
 372        *n = (u16) N;
 373        *f = (s32) F;
 374}
 375
 376
 377static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r)
 378{
 379        u8 reg;
 380        cx24113_writereg(state, 0x19, (n >> 1) & 0xff);
 381
 382        reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f);
 383        cx24113_writereg(state, 0x1a, reg);
 384
 385        cx24113_writereg(state, 0x1b, (f >> 3) & 0xff);
 386
 387        reg = cx24113_readreg(state, 0x1c) & 0x1f;
 388        cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5));
 389
 390        cx24113_set_Fref(state, r - 1);
 391}
 392
 393static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency)
 394{
 395        u8 r = 1; /* or 2 */
 396        u16 n = 6;
 397        s32 f = 0;
 398
 399        r = cx24113_readreg(state, 0x14);
 400        cx24113_writereg(state, 0x14, r & 0x3f);
 401
 402        r = cx24113_readreg(state, 0x10);
 403        cx24113_writereg(state, 0x10, r & 0xbf);
 404
 405        state->frequency = frequency;
 406
 407        dprintk("tuning to frequency: %d\n", frequency);
 408
 409        cx24113_calc_pll_nf(state, &n, &f);
 410        cx24113_set_nfr(state, n, f, state->refdiv);
 411
 412        r = cx24113_readreg(state, 0x18) & 0xbf;
 413        if (state->vcodiv != VCODIV2)
 414                r |= 1 << 6;
 415        cx24113_writereg(state, 0x18, r);
 416
 417        /* The need for this sleep is not clear. But helps in some cases */
 418        msleep(5);
 419
 420        r = cx24113_readreg(state, 0x1c) & 0xef;
 421        cx24113_writereg(state, 0x1c, r | (1 << 4));
 422        return 0;
 423}
 424
 425static int cx24113_init(struct dvb_frontend *fe)
 426{
 427        struct cx24113_state *state = fe->tuner_priv;
 428        int ret;
 429
 430        state->tuner_gain_thres = -50;
 431        state->gain_level = 255; /* to force a gain-setting initialization */
 432        state->icp_mode = 0;
 433
 434        if (state->config->xtal_khz < 11000) {
 435                state->icp_auto_hi  = ICP_LEVEL4;
 436                state->icp_auto_mhi  = ICP_LEVEL4;
 437                state->icp_auto_mlow = ICP_LEVEL3;
 438                state->icp_auto_low = ICP_LEVEL3;
 439        } else {
 440                state->icp_auto_hi  = ICP_LEVEL4;
 441                state->icp_auto_mhi  = ICP_LEVEL4;
 442                state->icp_auto_mlow = ICP_LEVEL3;
 443                state->icp_auto_low = ICP_LEVEL2;
 444        }
 445
 446        state->icp_dig = ICP_LEVEL3;
 447        state->icp_man = ICP_LEVEL1;
 448        state->acp_on  = 1;
 449        state->vco_mode = 0;
 450        state->vco_shift = 0;
 451        state->vco_band = VCOBANDSEL_1;
 452        state->bs_delay = 8;
 453        state->bs_freqcnt = 0x0fff;
 454        state->bs_rdiv = 0x0fff;
 455        state->prescaler_mode = 0;
 456        state->lna_gain = LNA_MAX_GAIN;
 457        state->rfvga_bias_ctrl = 1;
 458        state->Fwindow_enabled = 1;
 459
 460        cx24113_set_Fref(state, 0);
 461        cx24113_enable(state, 0x3d);
 462        cx24113_set_parameters(state);
 463
 464        cx24113_set_gain_settings(state, -30);
 465
 466        cx24113_set_bandwidth(state, 18025);
 467        cx24113_set_clk_inversion(state, 1);
 468
 469        if (state->config->xtal_khz >= 40000)
 470                ret = cx24113_writereg(state, 0x02,
 471                        (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2));
 472        else
 473                ret = cx24113_writereg(state, 0x02,
 474                        (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2));
 475
 476        return ret;
 477}
 478
 479static int cx24113_set_params(struct dvb_frontend *fe)
 480{
 481        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 482        struct cx24113_state *state = fe->tuner_priv;
 483        /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */
 484        u32 roll_off = 675;
 485        u32 bw;
 486
 487        bw  = ((c->symbol_rate/100) * roll_off) / 1000;
 488        bw += (10000000/100) + 5;
 489        bw /= 10;
 490        bw += 1000;
 491        cx24113_set_bandwidth(state, bw);
 492
 493        cx24113_set_frequency(state, c->frequency);
 494        msleep(5);
 495        return cx24113_get_status(fe, &bw);
 496}
 497
 498static s8 cx24113_agc_table[2][10] = {
 499        {-54, -41, -35, -30, -25, -21, -16, -10,  -6,  -2},
 500        {-39, -35, -30, -25, -19, -15, -11,  -5,   1,   9},
 501};
 502
 503void cx24113_agc_callback(struct dvb_frontend *fe)
 504{
 505        struct cx24113_state *state = fe->tuner_priv;
 506        s16 s, i;
 507        if (!fe->ops.read_signal_strength)
 508                return;
 509
 510        do {
 511                /* this only works with the current CX24123 implementation */
 512                fe->ops.read_signal_strength(fe, (u16 *) &s);
 513                s >>= 8;
 514                dprintk("signal strength: %d\n", s);
 515                for (i = 0; i < sizeof(cx24113_agc_table[0]); i++)
 516                        if (cx24113_agc_table[state->gain_level][i] > s)
 517                                break;
 518                s = -25 - i*5;
 519        } while (cx24113_set_gain_settings(state, s));
 520}
 521EXPORT_SYMBOL(cx24113_agc_callback);
 522
 523static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency)
 524{
 525        struct cx24113_state *state = fe->tuner_priv;
 526        *frequency = state->frequency;
 527        return 0;
 528}
 529
 530static int cx24113_release(struct dvb_frontend *fe)
 531{
 532        struct cx24113_state *state = fe->tuner_priv;
 533        dprintk("\n");
 534        fe->tuner_priv = NULL;
 535        kfree(state);
 536        return 0;
 537}
 538
 539static const struct dvb_tuner_ops cx24113_tuner_ops = {
 540        .info = {
 541                .name           = "Conexant CX24113",
 542                .frequency_min  = 950000,
 543                .frequency_max  = 2150000,
 544                .frequency_step = 125,
 545        },
 546
 547        .release       = cx24113_release,
 548
 549        .init          = cx24113_init,
 550
 551        .set_params    = cx24113_set_params,
 552        .get_frequency = cx24113_get_frequency,
 553        .get_status    = cx24113_get_status,
 554};
 555
 556struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
 557                const struct cx24113_config *config, struct i2c_adapter *i2c)
 558{
 559        /* allocate memory for the internal state */
 560        struct cx24113_state *state =
 561                kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
 562        int rc;
 563        if (state == NULL) {
 564                cx_err("Unable to kzalloc\n");
 565                goto error;
 566        }
 567
 568        /* setup the state */
 569        state->config = config;
 570        state->i2c = i2c;
 571
 572        cx_info("trying to detect myself\n");
 573
 574        /* making a dummy read, because of some expected troubles
 575         * after power on */
 576        cx24113_readreg(state, 0x00);
 577
 578        rc = cx24113_readreg(state, 0x00);
 579        if (rc < 0) {
 580                cx_info("CX24113 not found.\n");
 581                goto error;
 582        }
 583        state->rev = rc;
 584
 585        switch (rc) {
 586        case 0x43:
 587                cx_info("detected CX24113 variant\n");
 588                break;
 589        case REV_CX24113:
 590                cx_info("successfully detected\n");
 591                break;
 592        default:
 593                cx_err("unsupported device id: %x\n", state->rev);
 594                goto error;
 595        }
 596        state->ver = cx24113_readreg(state, 0x01);
 597        cx_info("version: %x\n", state->ver);
 598
 599        /* create dvb_frontend */
 600        memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops,
 601                        sizeof(struct dvb_tuner_ops));
 602        fe->tuner_priv = state;
 603        return fe;
 604
 605error:
 606        kfree(state);
 607
 608        return NULL;
 609}
 610EXPORT_SYMBOL(cx24113_attach);
 611
 612module_param(debug, int, 0644);
 613MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
 614
 615MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>");
 616MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware");
 617MODULE_LICENSE("GPL");
 618
 619