617 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Fitipower FC0013 tuner driver4 *5 * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>6 * partially based on driver code from Fitipower7 * Copyright (C) 2010 Fitipower Integrated Technology Inc8 */9 10#include "fc0013.h"11#include "fc0013-priv.h"12 13static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val)14{15 u8 buf[2] = {reg, val};16 struct i2c_msg msg = {17 .addr = priv->addr, .flags = 0, .buf = buf, .len = 218 };19 20 if (i2c_transfer(priv->i2c, &msg, 1) != 1) {21 err("I2C write reg failed, reg: %02x, val: %02x", reg, val);22 return -EREMOTEIO;23 }24 return 0;25}26 27static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val)28{29 struct i2c_msg msg[2] = {30 { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 },31 { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 },32 };33 34 if (i2c_transfer(priv->i2c, msg, 2) != 2) {35 err("I2C read reg failed, reg: %02x", reg);36 return -EREMOTEIO;37 }38 return 0;39}40 41static void fc0013_release(struct dvb_frontend *fe)42{43 kfree(fe->tuner_priv);44 fe->tuner_priv = NULL;45}46 47static int fc0013_init(struct dvb_frontend *fe)48{49 struct fc0013_priv *priv = fe->tuner_priv;50 int i, ret = 0;51 unsigned char reg[] = {52 0x00, /* reg. 0x00: dummy */53 0x09, /* reg. 0x01 */54 0x16, /* reg. 0x02 */55 0x00, /* reg. 0x03 */56 0x00, /* reg. 0x04 */57 0x17, /* reg. 0x05 */58 0x02, /* reg. 0x06 */59 0x0a, /* reg. 0x07: CHECK */60 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,61 Loop Bw 1/8 */62 0x6f, /* reg. 0x09: enable LoopThrough */63 0xb8, /* reg. 0x0a: Disable LO Test Buffer */64 0x82, /* reg. 0x0b: CHECK */65 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */66 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */67 0x00, /* reg. 0x0e */68 0x00, /* reg. 0x0f */69 0x00, /* reg. 0x10 */70 0x00, /* reg. 0x11 */71 0x00, /* reg. 0x12 */72 0x00, /* reg. 0x13 */73 0x50, /* reg. 0x14: DVB-t High Gain, UHF.74 Middle Gain: 0x48, Low Gain: 0x40 */75 0x01, /* reg. 0x15 */76 };77 78 switch (priv->xtal_freq) {79 case FC_XTAL_27_MHZ:80 case FC_XTAL_28_8_MHZ:81 reg[0x07] |= 0x20;82 break;83 case FC_XTAL_36_MHZ:84 default:85 break;86 }87 88 if (priv->dual_master)89 reg[0x0c] |= 0x02;90 91 if (fe->ops.i2c_gate_ctrl)92 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */93 94 for (i = 1; i < sizeof(reg); i++) {95 ret = fc0013_writereg(priv, i, reg[i]);96 if (ret)97 break;98 }99 100 if (fe->ops.i2c_gate_ctrl)101 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */102 103 if (ret)104 err("fc0013_writereg failed: %d", ret);105 106 return ret;107}108 109static int fc0013_sleep(struct dvb_frontend *fe)110{111 /* nothing to do here */112 return 0;113}114 115int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val)116{117 struct fc0013_priv *priv = fe->tuner_priv;118 int ret;119 u8 rc_cal;120 int val;121 122 if (fe->ops.i2c_gate_ctrl)123 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */124 125 /* push rc_cal value, get rc_cal value */126 ret = fc0013_writereg(priv, 0x10, 0x00);127 if (ret)128 goto error_out;129 130 /* get rc_cal value */131 ret = fc0013_readreg(priv, 0x10, &rc_cal);132 if (ret)133 goto error_out;134 135 rc_cal &= 0x0f;136 137 val = (int)rc_cal + rc_val;138 139 /* forcing rc_cal */140 ret = fc0013_writereg(priv, 0x0d, 0x11);141 if (ret)142 goto error_out;143 144 /* modify rc_cal value */145 if (val > 15)146 ret = fc0013_writereg(priv, 0x10, 0x0f);147 else if (val < 0)148 ret = fc0013_writereg(priv, 0x10, 0x00);149 else150 ret = fc0013_writereg(priv, 0x10, (u8)val);151 152error_out:153 if (fe->ops.i2c_gate_ctrl)154 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */155 156 return ret;157}158EXPORT_SYMBOL(fc0013_rc_cal_add);159 160int fc0013_rc_cal_reset(struct dvb_frontend *fe)161{162 struct fc0013_priv *priv = fe->tuner_priv;163 int ret;164 165 if (fe->ops.i2c_gate_ctrl)166 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */167 168 ret = fc0013_writereg(priv, 0x0d, 0x01);169 if (!ret)170 ret = fc0013_writereg(priv, 0x10, 0x00);171 172 if (fe->ops.i2c_gate_ctrl)173 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */174 175 return ret;176}177EXPORT_SYMBOL(fc0013_rc_cal_reset);178 179static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq)180{181 int ret;182 u8 tmp;183 184 ret = fc0013_readreg(priv, 0x1d, &tmp);185 if (ret)186 goto error_out;187 tmp &= 0xe3;188 if (freq <= 177500) { /* VHF Track: 7 */189 ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);190 } else if (freq <= 184500) { /* VHF Track: 6 */191 ret = fc0013_writereg(priv, 0x1d, tmp | 0x18);192 } else if (freq <= 191500) { /* VHF Track: 5 */193 ret = fc0013_writereg(priv, 0x1d, tmp | 0x14);194 } else if (freq <= 198500) { /* VHF Track: 4 */195 ret = fc0013_writereg(priv, 0x1d, tmp | 0x10);196 } else if (freq <= 205500) { /* VHF Track: 3 */197 ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c);198 } else if (freq <= 219500) { /* VHF Track: 2 */199 ret = fc0013_writereg(priv, 0x1d, tmp | 0x08);200 } else if (freq < 300000) { /* VHF Track: 1 */201 ret = fc0013_writereg(priv, 0x1d, tmp | 0x04);202 } else { /* UHF and GPS */203 ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);204 }205error_out:206 return ret;207}208 209static int fc0013_set_params(struct dvb_frontend *fe)210{211 struct fc0013_priv *priv = fe->tuner_priv;212 int i, ret = 0;213 struct dtv_frontend_properties *p = &fe->dtv_property_cache;214 u32 freq = p->frequency / 1000;215 u32 delsys = p->delivery_system;216 unsigned char reg[7], am, pm, multi, tmp;217 unsigned long f_vco;218 unsigned short xtal_freq_khz_2, xin, xdiv;219 bool vco_select = false;220 221 if (fe->callback) {222 ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,223 FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1));224 if (ret)225 goto exit;226 }227 228 switch (priv->xtal_freq) {229 case FC_XTAL_27_MHZ:230 xtal_freq_khz_2 = 27000 / 2;231 break;232 case FC_XTAL_36_MHZ:233 xtal_freq_khz_2 = 36000 / 2;234 break;235 case FC_XTAL_28_8_MHZ:236 default:237 xtal_freq_khz_2 = 28800 / 2;238 break;239 }240 241 if (fe->ops.i2c_gate_ctrl)242 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */243 244 /* set VHF track */245 ret = fc0013_set_vhf_track(priv, freq);246 if (ret)247 goto exit;248 249 if (freq < 300000) {250 /* enable VHF filter */251 ret = fc0013_readreg(priv, 0x07, &tmp);252 if (ret)253 goto exit;254 ret = fc0013_writereg(priv, 0x07, tmp | 0x10);255 if (ret)256 goto exit;257 258 /* disable UHF & disable GPS */259 ret = fc0013_readreg(priv, 0x14, &tmp);260 if (ret)261 goto exit;262 ret = fc0013_writereg(priv, 0x14, tmp & 0x1f);263 if (ret)264 goto exit;265 } else if (freq <= 862000) {266 /* disable VHF filter */267 ret = fc0013_readreg(priv, 0x07, &tmp);268 if (ret)269 goto exit;270 ret = fc0013_writereg(priv, 0x07, tmp & 0xef);271 if (ret)272 goto exit;273 274 /* enable UHF & disable GPS */275 ret = fc0013_readreg(priv, 0x14, &tmp);276 if (ret)277 goto exit;278 ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40);279 if (ret)280 goto exit;281 } else {282 /* disable VHF filter */283 ret = fc0013_readreg(priv, 0x07, &tmp);284 if (ret)285 goto exit;286 ret = fc0013_writereg(priv, 0x07, tmp & 0xef);287 if (ret)288 goto exit;289 290 /* disable UHF & enable GPS */291 ret = fc0013_readreg(priv, 0x14, &tmp);292 if (ret)293 goto exit;294 ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20);295 if (ret)296 goto exit;297 }298 299 /* select frequency divider and the frequency of VCO */300 if (freq < 37084) { /* freq * 96 < 3560000 */301 multi = 96;302 reg[5] = 0x82;303 reg[6] = 0x00;304 } else if (freq < 55625) { /* freq * 64 < 3560000 */305 multi = 64;306 reg[5] = 0x02;307 reg[6] = 0x02;308 } else if (freq < 74167) { /* freq * 48 < 3560000 */309 multi = 48;310 reg[5] = 0x42;311 reg[6] = 0x00;312 } else if (freq < 111250) { /* freq * 32 < 3560000 */313 multi = 32;314 reg[5] = 0x82;315 reg[6] = 0x02;316 } else if (freq < 148334) { /* freq * 24 < 3560000 */317 multi = 24;318 reg[5] = 0x22;319 reg[6] = 0x00;320 } else if (freq < 222500) { /* freq * 16 < 3560000 */321 multi = 16;322 reg[5] = 0x42;323 reg[6] = 0x02;324 } else if (freq < 296667) { /* freq * 12 < 3560000 */325 multi = 12;326 reg[5] = 0x12;327 reg[6] = 0x00;328 } else if (freq < 445000) { /* freq * 8 < 3560000 */329 multi = 8;330 reg[5] = 0x22;331 reg[6] = 0x02;332 } else if (freq < 593334) { /* freq * 6 < 3560000 */333 multi = 6;334 reg[5] = 0x0a;335 reg[6] = 0x00;336 } else if (freq < 950000) { /* freq * 4 < 3800000 */337 multi = 4;338 reg[5] = 0x12;339 reg[6] = 0x02;340 } else {341 multi = 2;342 reg[5] = 0x0a;343 reg[6] = 0x02;344 }345 346 f_vco = freq * multi;347 348 if (f_vco >= 3060000) {349 reg[6] |= 0x08;350 vco_select = true;351 }352 353 if (freq >= 45000) {354 /* From divided value (XDIV) determined the FA and FP value */355 xdiv = (unsigned short)(f_vco / xtal_freq_khz_2);356 if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2))357 xdiv++;358 359 pm = (unsigned char)(xdiv / 8);360 am = (unsigned char)(xdiv - (8 * pm));361 362 if (am < 2) {363 reg[1] = am + 8;364 reg[2] = pm - 1;365 } else {366 reg[1] = am;367 reg[2] = pm;368 }369 } else {370 /* fix for frequency less than 45 MHz */371 reg[1] = 0x06;372 reg[2] = 0x11;373 }374 375 /* fix clock out */376 reg[6] |= 0x20;377 378 /* From VCO frequency determines the XIN ( fractional part of Delta379 Sigma PLL) and divided value (XDIV) */380 xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2);381 xin = (xin << 15) / xtal_freq_khz_2;382 if (xin >= 16384)383 xin += 32768;384 385 reg[3] = xin >> 8;386 reg[4] = xin & 0xff;387 388 if (delsys == SYS_DVBT) {389 reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */390 switch (p->bandwidth_hz) {391 case 6000000:392 reg[6] |= 0x80;393 break;394 case 7000000:395 reg[6] |= 0x40;396 break;397 case 8000000:398 default:399 break;400 }401 } else {402 err("%s: modulation type not supported!", __func__);403 return -EINVAL;404 }405 406 /* modified for Realtek demod */407 reg[5] |= 0x07;408 409 for (i = 1; i <= 6; i++) {410 ret = fc0013_writereg(priv, i, reg[i]);411 if (ret)412 goto exit;413 }414 415 ret = fc0013_readreg(priv, 0x11, &tmp);416 if (ret)417 goto exit;418 if (multi == 64)419 ret = fc0013_writereg(priv, 0x11, tmp | 0x04);420 else421 ret = fc0013_writereg(priv, 0x11, tmp & 0xfb);422 if (ret)423 goto exit;424 425 /* VCO Calibration */426 ret = fc0013_writereg(priv, 0x0e, 0x80);427 if (!ret)428 ret = fc0013_writereg(priv, 0x0e, 0x00);429 430 /* VCO Re-Calibration if needed */431 if (!ret)432 ret = fc0013_writereg(priv, 0x0e, 0x00);433 434 if (!ret) {435 msleep(10);436 ret = fc0013_readreg(priv, 0x0e, &tmp);437 }438 if (ret)439 goto exit;440 441 /* vco selection */442 tmp &= 0x3f;443 444 if (vco_select) {445 if (tmp > 0x3c) {446 reg[6] &= ~0x08;447 ret = fc0013_writereg(priv, 0x06, reg[6]);448 if (!ret)449 ret = fc0013_writereg(priv, 0x0e, 0x80);450 if (!ret)451 ret = fc0013_writereg(priv, 0x0e, 0x00);452 }453 } else {454 if (tmp < 0x02) {455 reg[6] |= 0x08;456 ret = fc0013_writereg(priv, 0x06, reg[6]);457 if (!ret)458 ret = fc0013_writereg(priv, 0x0e, 0x80);459 if (!ret)460 ret = fc0013_writereg(priv, 0x0e, 0x00);461 }462 }463 464 priv->frequency = p->frequency;465 priv->bandwidth = p->bandwidth_hz;466 467exit:468 if (fe->ops.i2c_gate_ctrl)469 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */470 if (ret)471 warn("%s: failed: %d", __func__, ret);472 return ret;473}474 475static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency)476{477 struct fc0013_priv *priv = fe->tuner_priv;478 *frequency = priv->frequency;479 return 0;480}481 482static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)483{484 /* always ? */485 *frequency = 0;486 return 0;487}488 489static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)490{491 struct fc0013_priv *priv = fe->tuner_priv;492 *bandwidth = priv->bandwidth;493 return 0;494}495 496#define INPUT_ADC_LEVEL -8497 498static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength)499{500 struct fc0013_priv *priv = fe->tuner_priv;501 int ret;502 unsigned char tmp;503 int int_temp, lna_gain, int_lna, tot_agc_gain, power;504 static const int fc0013_lna_gain_table[] = {505 /* low gain */506 -63, -58, -99, -73,507 -63, -65, -54, -60,508 /* middle gain */509 71, 70, 68, 67,510 65, 63, 61, 58,511 /* high gain */512 197, 191, 188, 186,513 184, 182, 181, 179,514 };515 516 if (fe->ops.i2c_gate_ctrl)517 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */518 519 ret = fc0013_writereg(priv, 0x13, 0x00);520 if (ret)521 goto err;522 523 ret = fc0013_readreg(priv, 0x13, &tmp);524 if (ret)525 goto err;526 int_temp = tmp;527 528 ret = fc0013_readreg(priv, 0x14, &tmp);529 if (ret)530 goto err;531 lna_gain = tmp & 0x1f;532 533 if (fe->ops.i2c_gate_ctrl)534 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */535 536 if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) {537 int_lna = fc0013_lna_gain_table[lna_gain];538 tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 +539 (int_temp & 0x1f)) * 2;540 power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10;541 542 if (power >= 45)543 *strength = 255; /* 100% */544 else if (power < -95)545 *strength = 0;546 else547 *strength = (power + 95) * 255 / 140;548 549 *strength |= *strength << 8;550 } else {551 ret = -1;552 }553 554 goto exit;555 556err:557 if (fe->ops.i2c_gate_ctrl)558 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */559exit:560 if (ret)561 warn("%s: failed: %d", __func__, ret);562 return ret;563}564 565static const struct dvb_tuner_ops fc0013_tuner_ops = {566 .info = {567 .name = "Fitipower FC0013",568 569 .frequency_min_hz = 37 * MHz, /* estimate */570 .frequency_max_hz = 1680 * MHz, /* CHECK */571 },572 573 .release = fc0013_release,574 575 .init = fc0013_init,576 .sleep = fc0013_sleep,577 578 .set_params = fc0013_set_params,579 580 .get_frequency = fc0013_get_frequency,581 .get_if_frequency = fc0013_get_if_frequency,582 .get_bandwidth = fc0013_get_bandwidth,583 584 .get_rf_strength = fc0013_get_rf_strength,585};586 587struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,588 struct i2c_adapter *i2c, u8 i2c_address, int dual_master,589 enum fc001x_xtal_freq xtal_freq)590{591 struct fc0013_priv *priv = NULL;592 593 priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL);594 if (priv == NULL)595 return NULL;596 597 priv->i2c = i2c;598 priv->dual_master = dual_master;599 priv->addr = i2c_address;600 priv->xtal_freq = xtal_freq;601 602 info("Fitipower FC0013 successfully attached.");603 604 fe->tuner_priv = priv;605 606 memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops,607 sizeof(struct dvb_tuner_ops));608 609 return fe;610}611EXPORT_SYMBOL_GPL(fc0013_attach);612 613MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver");614MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>");615MODULE_LICENSE("GPL");616MODULE_VERSION("0.2");617