162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Fitipower FC0013 tuner driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> 662306a36Sopenharmony_ci * partially based on driver code from Fitipower 762306a36Sopenharmony_ci * Copyright (C) 2010 Fitipower Integrated Technology Inc 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "fc0013.h" 1162306a36Sopenharmony_ci#include "fc0013-priv.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci u8 buf[2] = {reg, val}; 1662306a36Sopenharmony_ci struct i2c_msg msg = { 1762306a36Sopenharmony_ci .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 1862306a36Sopenharmony_ci }; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 2162306a36Sopenharmony_ci err("I2C write reg failed, reg: %02x, val: %02x", reg, val); 2262306a36Sopenharmony_ci return -EREMOTEIO; 2362306a36Sopenharmony_ci } 2462306a36Sopenharmony_ci return 0; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct i2c_msg msg[2] = { 3062306a36Sopenharmony_ci { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, 3162306a36Sopenharmony_ci { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, 3262306a36Sopenharmony_ci }; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (i2c_transfer(priv->i2c, msg, 2) != 2) { 3562306a36Sopenharmony_ci err("I2C read reg failed, reg: %02x", reg); 3662306a36Sopenharmony_ci return -EREMOTEIO; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void fc0013_release(struct dvb_frontend *fe) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci kfree(fe->tuner_priv); 4462306a36Sopenharmony_ci fe->tuner_priv = NULL; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int fc0013_init(struct dvb_frontend *fe) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct fc0013_priv *priv = fe->tuner_priv; 5062306a36Sopenharmony_ci int i, ret = 0; 5162306a36Sopenharmony_ci unsigned char reg[] = { 5262306a36Sopenharmony_ci 0x00, /* reg. 0x00: dummy */ 5362306a36Sopenharmony_ci 0x09, /* reg. 0x01 */ 5462306a36Sopenharmony_ci 0x16, /* reg. 0x02 */ 5562306a36Sopenharmony_ci 0x00, /* reg. 0x03 */ 5662306a36Sopenharmony_ci 0x00, /* reg. 0x04 */ 5762306a36Sopenharmony_ci 0x17, /* reg. 0x05 */ 5862306a36Sopenharmony_ci 0x02, /* reg. 0x06 */ 5962306a36Sopenharmony_ci 0x0a, /* reg. 0x07: CHECK */ 6062306a36Sopenharmony_ci 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, 6162306a36Sopenharmony_ci Loop Bw 1/8 */ 6262306a36Sopenharmony_ci 0x6f, /* reg. 0x09: enable LoopThrough */ 6362306a36Sopenharmony_ci 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ 6462306a36Sopenharmony_ci 0x82, /* reg. 0x0b: CHECK */ 6562306a36Sopenharmony_ci 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ 6662306a36Sopenharmony_ci 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ 6762306a36Sopenharmony_ci 0x00, /* reg. 0x0e */ 6862306a36Sopenharmony_ci 0x00, /* reg. 0x0f */ 6962306a36Sopenharmony_ci 0x00, /* reg. 0x10 */ 7062306a36Sopenharmony_ci 0x00, /* reg. 0x11 */ 7162306a36Sopenharmony_ci 0x00, /* reg. 0x12 */ 7262306a36Sopenharmony_ci 0x00, /* reg. 0x13 */ 7362306a36Sopenharmony_ci 0x50, /* reg. 0x14: DVB-t High Gain, UHF. 7462306a36Sopenharmony_ci Middle Gain: 0x48, Low Gain: 0x40 */ 7562306a36Sopenharmony_ci 0x01, /* reg. 0x15 */ 7662306a36Sopenharmony_ci }; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci switch (priv->xtal_freq) { 7962306a36Sopenharmony_ci case FC_XTAL_27_MHZ: 8062306a36Sopenharmony_ci case FC_XTAL_28_8_MHZ: 8162306a36Sopenharmony_ci reg[0x07] |= 0x20; 8262306a36Sopenharmony_ci break; 8362306a36Sopenharmony_ci case FC_XTAL_36_MHZ: 8462306a36Sopenharmony_ci default: 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (priv->dual_master) 8962306a36Sopenharmony_ci reg[0x0c] |= 0x02; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 9262306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci for (i = 1; i < sizeof(reg); i++) { 9562306a36Sopenharmony_ci ret = fc0013_writereg(priv, i, reg[i]); 9662306a36Sopenharmony_ci if (ret) 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 10162306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (ret) 10462306a36Sopenharmony_ci err("fc0013_writereg failed: %d", ret); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int fc0013_sleep(struct dvb_frontend *fe) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci /* nothing to do here */ 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct fc0013_priv *priv = fe->tuner_priv; 11862306a36Sopenharmony_ci int ret; 11962306a36Sopenharmony_ci u8 rc_cal; 12062306a36Sopenharmony_ci int val; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 12362306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* push rc_cal value, get rc_cal value */ 12662306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x10, 0x00); 12762306a36Sopenharmony_ci if (ret) 12862306a36Sopenharmony_ci goto error_out; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* get rc_cal value */ 13162306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x10, &rc_cal); 13262306a36Sopenharmony_ci if (ret) 13362306a36Sopenharmony_ci goto error_out; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci rc_cal &= 0x0f; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci val = (int)rc_cal + rc_val; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* forcing rc_cal */ 14062306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0d, 0x11); 14162306a36Sopenharmony_ci if (ret) 14262306a36Sopenharmony_ci goto error_out; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* modify rc_cal value */ 14562306a36Sopenharmony_ci if (val > 15) 14662306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x10, 0x0f); 14762306a36Sopenharmony_ci else if (val < 0) 14862306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x10, 0x00); 14962306a36Sopenharmony_ci else 15062306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x10, (u8)val); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cierror_out: 15362306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 15462306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return ret; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ciEXPORT_SYMBOL(fc0013_rc_cal_add); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciint fc0013_rc_cal_reset(struct dvb_frontend *fe) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct fc0013_priv *priv = fe->tuner_priv; 16362306a36Sopenharmony_ci int ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 16662306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0d, 0x01); 16962306a36Sopenharmony_ci if (!ret) 17062306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x10, 0x00); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 17362306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ciEXPORT_SYMBOL(fc0013_rc_cal_reset); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int ret; 18262306a36Sopenharmony_ci u8 tmp; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x1d, &tmp); 18562306a36Sopenharmony_ci if (ret) 18662306a36Sopenharmony_ci goto error_out; 18762306a36Sopenharmony_ci tmp &= 0xe3; 18862306a36Sopenharmony_ci if (freq <= 177500) { /* VHF Track: 7 */ 18962306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); 19062306a36Sopenharmony_ci } else if (freq <= 184500) { /* VHF Track: 6 */ 19162306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x18); 19262306a36Sopenharmony_ci } else if (freq <= 191500) { /* VHF Track: 5 */ 19362306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x14); 19462306a36Sopenharmony_ci } else if (freq <= 198500) { /* VHF Track: 4 */ 19562306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x10); 19662306a36Sopenharmony_ci } else if (freq <= 205500) { /* VHF Track: 3 */ 19762306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c); 19862306a36Sopenharmony_ci } else if (freq <= 219500) { /* VHF Track: 2 */ 19962306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x08); 20062306a36Sopenharmony_ci } else if (freq < 300000) { /* VHF Track: 1 */ 20162306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x04); 20262306a36Sopenharmony_ci } else { /* UHF and GPS */ 20362306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_cierror_out: 20662306a36Sopenharmony_ci return ret; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int fc0013_set_params(struct dvb_frontend *fe) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct fc0013_priv *priv = fe->tuner_priv; 21262306a36Sopenharmony_ci int i, ret = 0; 21362306a36Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 21462306a36Sopenharmony_ci u32 freq = p->frequency / 1000; 21562306a36Sopenharmony_ci u32 delsys = p->delivery_system; 21662306a36Sopenharmony_ci unsigned char reg[7], am, pm, multi, tmp; 21762306a36Sopenharmony_ci unsigned long f_vco; 21862306a36Sopenharmony_ci unsigned short xtal_freq_khz_2, xin, xdiv; 21962306a36Sopenharmony_ci bool vco_select = false; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (fe->callback) { 22262306a36Sopenharmony_ci ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 22362306a36Sopenharmony_ci FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); 22462306a36Sopenharmony_ci if (ret) 22562306a36Sopenharmony_ci goto exit; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci switch (priv->xtal_freq) { 22962306a36Sopenharmony_ci case FC_XTAL_27_MHZ: 23062306a36Sopenharmony_ci xtal_freq_khz_2 = 27000 / 2; 23162306a36Sopenharmony_ci break; 23262306a36Sopenharmony_ci case FC_XTAL_36_MHZ: 23362306a36Sopenharmony_ci xtal_freq_khz_2 = 36000 / 2; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case FC_XTAL_28_8_MHZ: 23662306a36Sopenharmony_ci default: 23762306a36Sopenharmony_ci xtal_freq_khz_2 = 28800 / 2; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 24262306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* set VHF track */ 24562306a36Sopenharmony_ci ret = fc0013_set_vhf_track(priv, freq); 24662306a36Sopenharmony_ci if (ret) 24762306a36Sopenharmony_ci goto exit; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (freq < 300000) { 25062306a36Sopenharmony_ci /* enable VHF filter */ 25162306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x07, &tmp); 25262306a36Sopenharmony_ci if (ret) 25362306a36Sopenharmony_ci goto exit; 25462306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x07, tmp | 0x10); 25562306a36Sopenharmony_ci if (ret) 25662306a36Sopenharmony_ci goto exit; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* disable UHF & disable GPS */ 25962306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x14, &tmp); 26062306a36Sopenharmony_ci if (ret) 26162306a36Sopenharmony_ci goto exit; 26262306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x14, tmp & 0x1f); 26362306a36Sopenharmony_ci if (ret) 26462306a36Sopenharmony_ci goto exit; 26562306a36Sopenharmony_ci } else if (freq <= 862000) { 26662306a36Sopenharmony_ci /* disable VHF filter */ 26762306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x07, &tmp); 26862306a36Sopenharmony_ci if (ret) 26962306a36Sopenharmony_ci goto exit; 27062306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x07, tmp & 0xef); 27162306a36Sopenharmony_ci if (ret) 27262306a36Sopenharmony_ci goto exit; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* enable UHF & disable GPS */ 27562306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x14, &tmp); 27662306a36Sopenharmony_ci if (ret) 27762306a36Sopenharmony_ci goto exit; 27862306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40); 27962306a36Sopenharmony_ci if (ret) 28062306a36Sopenharmony_ci goto exit; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci /* disable VHF filter */ 28362306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x07, &tmp); 28462306a36Sopenharmony_ci if (ret) 28562306a36Sopenharmony_ci goto exit; 28662306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x07, tmp & 0xef); 28762306a36Sopenharmony_ci if (ret) 28862306a36Sopenharmony_ci goto exit; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* disable UHF & enable GPS */ 29162306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x14, &tmp); 29262306a36Sopenharmony_ci if (ret) 29362306a36Sopenharmony_ci goto exit; 29462306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20); 29562306a36Sopenharmony_ci if (ret) 29662306a36Sopenharmony_ci goto exit; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* select frequency divider and the frequency of VCO */ 30062306a36Sopenharmony_ci if (freq < 37084) { /* freq * 96 < 3560000 */ 30162306a36Sopenharmony_ci multi = 96; 30262306a36Sopenharmony_ci reg[5] = 0x82; 30362306a36Sopenharmony_ci reg[6] = 0x00; 30462306a36Sopenharmony_ci } else if (freq < 55625) { /* freq * 64 < 3560000 */ 30562306a36Sopenharmony_ci multi = 64; 30662306a36Sopenharmony_ci reg[5] = 0x02; 30762306a36Sopenharmony_ci reg[6] = 0x02; 30862306a36Sopenharmony_ci } else if (freq < 74167) { /* freq * 48 < 3560000 */ 30962306a36Sopenharmony_ci multi = 48; 31062306a36Sopenharmony_ci reg[5] = 0x42; 31162306a36Sopenharmony_ci reg[6] = 0x00; 31262306a36Sopenharmony_ci } else if (freq < 111250) { /* freq * 32 < 3560000 */ 31362306a36Sopenharmony_ci multi = 32; 31462306a36Sopenharmony_ci reg[5] = 0x82; 31562306a36Sopenharmony_ci reg[6] = 0x02; 31662306a36Sopenharmony_ci } else if (freq < 148334) { /* freq * 24 < 3560000 */ 31762306a36Sopenharmony_ci multi = 24; 31862306a36Sopenharmony_ci reg[5] = 0x22; 31962306a36Sopenharmony_ci reg[6] = 0x00; 32062306a36Sopenharmony_ci } else if (freq < 222500) { /* freq * 16 < 3560000 */ 32162306a36Sopenharmony_ci multi = 16; 32262306a36Sopenharmony_ci reg[5] = 0x42; 32362306a36Sopenharmony_ci reg[6] = 0x02; 32462306a36Sopenharmony_ci } else if (freq < 296667) { /* freq * 12 < 3560000 */ 32562306a36Sopenharmony_ci multi = 12; 32662306a36Sopenharmony_ci reg[5] = 0x12; 32762306a36Sopenharmony_ci reg[6] = 0x00; 32862306a36Sopenharmony_ci } else if (freq < 445000) { /* freq * 8 < 3560000 */ 32962306a36Sopenharmony_ci multi = 8; 33062306a36Sopenharmony_ci reg[5] = 0x22; 33162306a36Sopenharmony_ci reg[6] = 0x02; 33262306a36Sopenharmony_ci } else if (freq < 593334) { /* freq * 6 < 3560000 */ 33362306a36Sopenharmony_ci multi = 6; 33462306a36Sopenharmony_ci reg[5] = 0x0a; 33562306a36Sopenharmony_ci reg[6] = 0x00; 33662306a36Sopenharmony_ci } else if (freq < 950000) { /* freq * 4 < 3800000 */ 33762306a36Sopenharmony_ci multi = 4; 33862306a36Sopenharmony_ci reg[5] = 0x12; 33962306a36Sopenharmony_ci reg[6] = 0x02; 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci multi = 2; 34262306a36Sopenharmony_ci reg[5] = 0x0a; 34362306a36Sopenharmony_ci reg[6] = 0x02; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci f_vco = freq * multi; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (f_vco >= 3060000) { 34962306a36Sopenharmony_ci reg[6] |= 0x08; 35062306a36Sopenharmony_ci vco_select = true; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (freq >= 45000) { 35462306a36Sopenharmony_ci /* From divided value (XDIV) determined the FA and FP value */ 35562306a36Sopenharmony_ci xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); 35662306a36Sopenharmony_ci if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) 35762306a36Sopenharmony_ci xdiv++; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci pm = (unsigned char)(xdiv / 8); 36062306a36Sopenharmony_ci am = (unsigned char)(xdiv - (8 * pm)); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (am < 2) { 36362306a36Sopenharmony_ci reg[1] = am + 8; 36462306a36Sopenharmony_ci reg[2] = pm - 1; 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci reg[1] = am; 36762306a36Sopenharmony_ci reg[2] = pm; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci /* fix for frequency less than 45 MHz */ 37162306a36Sopenharmony_ci reg[1] = 0x06; 37262306a36Sopenharmony_ci reg[2] = 0x11; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* fix clock out */ 37662306a36Sopenharmony_ci reg[6] |= 0x20; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* From VCO frequency determines the XIN ( fractional part of Delta 37962306a36Sopenharmony_ci Sigma PLL) and divided value (XDIV) */ 38062306a36Sopenharmony_ci xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); 38162306a36Sopenharmony_ci xin = (xin << 15) / xtal_freq_khz_2; 38262306a36Sopenharmony_ci if (xin >= 16384) 38362306a36Sopenharmony_ci xin += 32768; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci reg[3] = xin >> 8; 38662306a36Sopenharmony_ci reg[4] = xin & 0xff; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (delsys == SYS_DVBT) { 38962306a36Sopenharmony_ci reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ 39062306a36Sopenharmony_ci switch (p->bandwidth_hz) { 39162306a36Sopenharmony_ci case 6000000: 39262306a36Sopenharmony_ci reg[6] |= 0x80; 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci case 7000000: 39562306a36Sopenharmony_ci reg[6] |= 0x40; 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci case 8000000: 39862306a36Sopenharmony_ci default: 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } else { 40262306a36Sopenharmony_ci err("%s: modulation type not supported!", __func__); 40362306a36Sopenharmony_ci return -EINVAL; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* modified for Realtek demod */ 40762306a36Sopenharmony_ci reg[5] |= 0x07; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci for (i = 1; i <= 6; i++) { 41062306a36Sopenharmony_ci ret = fc0013_writereg(priv, i, reg[i]); 41162306a36Sopenharmony_ci if (ret) 41262306a36Sopenharmony_ci goto exit; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x11, &tmp); 41662306a36Sopenharmony_ci if (ret) 41762306a36Sopenharmony_ci goto exit; 41862306a36Sopenharmony_ci if (multi == 64) 41962306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x11, tmp | 0x04); 42062306a36Sopenharmony_ci else 42162306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x11, tmp & 0xfb); 42262306a36Sopenharmony_ci if (ret) 42362306a36Sopenharmony_ci goto exit; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* VCO Calibration */ 42662306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0e, 0x80); 42762306a36Sopenharmony_ci if (!ret) 42862306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0e, 0x00); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* VCO Re-Calibration if needed */ 43162306a36Sopenharmony_ci if (!ret) 43262306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0e, 0x00); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!ret) { 43562306a36Sopenharmony_ci msleep(10); 43662306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x0e, &tmp); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci if (ret) 43962306a36Sopenharmony_ci goto exit; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* vco selection */ 44262306a36Sopenharmony_ci tmp &= 0x3f; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (vco_select) { 44562306a36Sopenharmony_ci if (tmp > 0x3c) { 44662306a36Sopenharmony_ci reg[6] &= ~0x08; 44762306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x06, reg[6]); 44862306a36Sopenharmony_ci if (!ret) 44962306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0e, 0x80); 45062306a36Sopenharmony_ci if (!ret) 45162306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0e, 0x00); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci if (tmp < 0x02) { 45562306a36Sopenharmony_ci reg[6] |= 0x08; 45662306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x06, reg[6]); 45762306a36Sopenharmony_ci if (!ret) 45862306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0e, 0x80); 45962306a36Sopenharmony_ci if (!ret) 46062306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x0e, 0x00); 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci priv->frequency = p->frequency; 46562306a36Sopenharmony_ci priv->bandwidth = p->bandwidth_hz; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ciexit: 46862306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 46962306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 47062306a36Sopenharmony_ci if (ret) 47162306a36Sopenharmony_ci warn("%s: failed: %d", __func__, ret); 47262306a36Sopenharmony_ci return ret; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct fc0013_priv *priv = fe->tuner_priv; 47862306a36Sopenharmony_ci *frequency = priv->frequency; 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci /* always ? */ 48562306a36Sopenharmony_ci *frequency = 0; 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct fc0013_priv *priv = fe->tuner_priv; 49262306a36Sopenharmony_ci *bandwidth = priv->bandwidth; 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci#define INPUT_ADC_LEVEL -8 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct fc0013_priv *priv = fe->tuner_priv; 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci unsigned char tmp; 50362306a36Sopenharmony_ci int int_temp, lna_gain, int_lna, tot_agc_gain, power; 50462306a36Sopenharmony_ci static const int fc0013_lna_gain_table[] = { 50562306a36Sopenharmony_ci /* low gain */ 50662306a36Sopenharmony_ci -63, -58, -99, -73, 50762306a36Sopenharmony_ci -63, -65, -54, -60, 50862306a36Sopenharmony_ci /* middle gain */ 50962306a36Sopenharmony_ci 71, 70, 68, 67, 51062306a36Sopenharmony_ci 65, 63, 61, 58, 51162306a36Sopenharmony_ci /* high gain */ 51262306a36Sopenharmony_ci 197, 191, 188, 186, 51362306a36Sopenharmony_ci 184, 182, 181, 179, 51462306a36Sopenharmony_ci }; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 51762306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ret = fc0013_writereg(priv, 0x13, 0x00); 52062306a36Sopenharmony_ci if (ret) 52162306a36Sopenharmony_ci goto err; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x13, &tmp); 52462306a36Sopenharmony_ci if (ret) 52562306a36Sopenharmony_ci goto err; 52662306a36Sopenharmony_ci int_temp = tmp; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci ret = fc0013_readreg(priv, 0x14, &tmp); 52962306a36Sopenharmony_ci if (ret) 53062306a36Sopenharmony_ci goto err; 53162306a36Sopenharmony_ci lna_gain = tmp & 0x1f; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 53462306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) { 53762306a36Sopenharmony_ci int_lna = fc0013_lna_gain_table[lna_gain]; 53862306a36Sopenharmony_ci tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + 53962306a36Sopenharmony_ci (int_temp & 0x1f)) * 2; 54062306a36Sopenharmony_ci power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (power >= 45) 54362306a36Sopenharmony_ci *strength = 255; /* 100% */ 54462306a36Sopenharmony_ci else if (power < -95) 54562306a36Sopenharmony_ci *strength = 0; 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci *strength = (power + 95) * 255 / 140; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci *strength |= *strength << 8; 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci ret = -1; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci goto exit; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cierr: 55762306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 55862306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 55962306a36Sopenharmony_ciexit: 56062306a36Sopenharmony_ci if (ret) 56162306a36Sopenharmony_ci warn("%s: failed: %d", __func__, ret); 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic const struct dvb_tuner_ops fc0013_tuner_ops = { 56662306a36Sopenharmony_ci .info = { 56762306a36Sopenharmony_ci .name = "Fitipower FC0013", 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci .frequency_min_hz = 37 * MHz, /* estimate */ 57062306a36Sopenharmony_ci .frequency_max_hz = 1680 * MHz, /* CHECK */ 57162306a36Sopenharmony_ci }, 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci .release = fc0013_release, 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci .init = fc0013_init, 57662306a36Sopenharmony_ci .sleep = fc0013_sleep, 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci .set_params = fc0013_set_params, 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci .get_frequency = fc0013_get_frequency, 58162306a36Sopenharmony_ci .get_if_frequency = fc0013_get_if_frequency, 58262306a36Sopenharmony_ci .get_bandwidth = fc0013_get_bandwidth, 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci .get_rf_strength = fc0013_get_rf_strength, 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistruct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, 58862306a36Sopenharmony_ci struct i2c_adapter *i2c, u8 i2c_address, int dual_master, 58962306a36Sopenharmony_ci enum fc001x_xtal_freq xtal_freq) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct fc0013_priv *priv = NULL; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL); 59462306a36Sopenharmony_ci if (priv == NULL) 59562306a36Sopenharmony_ci return NULL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci priv->i2c = i2c; 59862306a36Sopenharmony_ci priv->dual_master = dual_master; 59962306a36Sopenharmony_ci priv->addr = i2c_address; 60062306a36Sopenharmony_ci priv->xtal_freq = xtal_freq; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci info("Fitipower FC0013 successfully attached."); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci fe->tuner_priv = priv; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops, 60762306a36Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return fe; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fc0013_attach); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciMODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); 61462306a36Sopenharmony_ciMODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>"); 61562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 61662306a36Sopenharmony_ciMODULE_VERSION("0.2"); 617