root/drivers/media/dvb-frontends/cx24113.c

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

DEFINITIONS

This source file includes following definitions.
  1. cx24113_writereg
  2. cx24113_readreg
  3. cx24113_set_parameters
  4. cx24113_set_gain_settings
  5. cx24113_set_Fref
  6. cx24113_enable
  7. cx24113_set_bandwidth
  8. cx24113_set_clk_inversion
  9. cx24113_get_status
  10. cx24113_set_ref_div
  11. cx24113_calc_pll_nf
  12. cx24113_set_nfr
  13. cx24113_set_frequency
  14. cx24113_init
  15. cx24113_set_params
  16. cx24113_agc_callback
  17. cx24113_get_frequency
  18. cx24113_release
  19. cx24113_attach

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

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