cx24113.c (14042B)
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 18static 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 31struct 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 90static 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 105static 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 = ®, .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 127static 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 187static 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 220static 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 232static 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 240static 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 265static 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 271static 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 281static 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 288static 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 363static 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 379static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) 380{ 381 u8 r; 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 411static 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 465static 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 484static 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 489void 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} 507EXPORT_SYMBOL(cx24113_agc_callback); 508 509static 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 516static 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 524static 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 541struct 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 588error: 589 kfree(state); 590 591 return NULL; 592} 593EXPORT_SYMBOL(cx24113_attach); 594 595module_param(debug, int, 0644); 596MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); 597 598MODULE_AUTHOR("Patrick Boettcher <pb@linuxtv.org>"); 599MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); 600MODULE_LICENSE("GPL"); 601