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