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