18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Fitipower FC0012 tuner driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "fc0012.h" 98c2ecf20Sopenharmony_ci#include "fc0012-priv.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci u8 buf[2] = {reg, val}; 148c2ecf20Sopenharmony_ci struct i2c_msg msg = { 158c2ecf20Sopenharmony_ci .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 168c2ecf20Sopenharmony_ci }; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 198c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, 208c2ecf20Sopenharmony_ci "%s: I2C write reg failed, reg: %02x, val: %02x\n", 218c2ecf20Sopenharmony_ci KBUILD_MODNAME, reg, val); 228c2ecf20Sopenharmony_ci return -EREMOTEIO; 238c2ecf20Sopenharmony_ci } 248c2ecf20Sopenharmony_ci return 0; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct i2c_msg msg[2] = { 308c2ecf20Sopenharmony_ci { .addr = priv->cfg->i2c_address, .flags = 0, 318c2ecf20Sopenharmony_ci .buf = ®, .len = 1 }, 328c2ecf20Sopenharmony_ci { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, 338c2ecf20Sopenharmony_ci .buf = val, .len = 1 }, 348c2ecf20Sopenharmony_ci }; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, msg, 2) != 2) { 378c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, 388c2ecf20Sopenharmony_ci "%s: I2C read reg failed, reg: %02x\n", 398c2ecf20Sopenharmony_ci KBUILD_MODNAME, reg); 408c2ecf20Sopenharmony_ci return -EREMOTEIO; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci return 0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void fc0012_release(struct dvb_frontend *fe) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 488c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int fc0012_init(struct dvb_frontend *fe) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct fc0012_priv *priv = fe->tuner_priv; 548c2ecf20Sopenharmony_ci int i, ret = 0; 558c2ecf20Sopenharmony_ci unsigned char reg[] = { 568c2ecf20Sopenharmony_ci 0x00, /* dummy reg. 0 */ 578c2ecf20Sopenharmony_ci 0x05, /* reg. 0x01 */ 588c2ecf20Sopenharmony_ci 0x10, /* reg. 0x02 */ 598c2ecf20Sopenharmony_ci 0x00, /* reg. 0x03 */ 608c2ecf20Sopenharmony_ci 0x00, /* reg. 0x04 */ 618c2ecf20Sopenharmony_ci 0x0f, /* reg. 0x05: may also be 0x0a */ 628c2ecf20Sopenharmony_ci 0x00, /* reg. 0x06: divider 2, VCO slow */ 638c2ecf20Sopenharmony_ci 0x00, /* reg. 0x07: may also be 0x0f */ 648c2ecf20Sopenharmony_ci 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, 658c2ecf20Sopenharmony_ci Loop Bw 1/8 */ 668c2ecf20Sopenharmony_ci 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ 678c2ecf20Sopenharmony_ci 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ 688c2ecf20Sopenharmony_ci 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, 698c2ecf20Sopenharmony_ci may also be 0x83 */ 708c2ecf20Sopenharmony_ci 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ 718c2ecf20Sopenharmony_ci 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ 728c2ecf20Sopenharmony_ci 0x00, /* reg. 0x0e */ 738c2ecf20Sopenharmony_ci 0x00, /* reg. 0x0f */ 748c2ecf20Sopenharmony_ci 0x00, /* reg. 0x10: may also be 0x0d */ 758c2ecf20Sopenharmony_ci 0x00, /* reg. 0x11 */ 768c2ecf20Sopenharmony_ci 0x1f, /* reg. 0x12: Set to maximum gain */ 778c2ecf20Sopenharmony_ci 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, 788c2ecf20Sopenharmony_ci Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ 798c2ecf20Sopenharmony_ci 0x00, /* reg. 0x14 */ 808c2ecf20Sopenharmony_ci 0x04, /* reg. 0x15: Enable LNA COMPS */ 818c2ecf20Sopenharmony_ci }; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci switch (priv->cfg->xtal_freq) { 848c2ecf20Sopenharmony_ci case FC_XTAL_27_MHZ: 858c2ecf20Sopenharmony_ci case FC_XTAL_28_8_MHZ: 868c2ecf20Sopenharmony_ci reg[0x07] |= 0x20; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci case FC_XTAL_36_MHZ: 898c2ecf20Sopenharmony_ci default: 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (priv->cfg->dual_master) 948c2ecf20Sopenharmony_ci reg[0x0c] |= 0x02; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (priv->cfg->loop_through) 978c2ecf20Sopenharmony_ci reg[0x09] |= 0x01; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1008c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for (i = 1; i < sizeof(reg); i++) { 1038c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, i, reg[i]); 1048c2ecf20Sopenharmony_ci if (ret) 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1098c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (ret) 1128c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, "%s: fc0012_writereg failed: %d\n", 1138c2ecf20Sopenharmony_ci KBUILD_MODNAME, ret); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int fc0012_set_params(struct dvb_frontend *fe) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct fc0012_priv *priv = fe->tuner_priv; 1218c2ecf20Sopenharmony_ci int i, ret = 0; 1228c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 1238c2ecf20Sopenharmony_ci u32 freq = p->frequency / 1000; 1248c2ecf20Sopenharmony_ci u32 delsys = p->delivery_system; 1258c2ecf20Sopenharmony_ci unsigned char reg[7], am, pm, multi, tmp; 1268c2ecf20Sopenharmony_ci unsigned long f_vco; 1278c2ecf20Sopenharmony_ci unsigned short xtal_freq_khz_2, xin, xdiv; 1288c2ecf20Sopenharmony_ci bool vco_select = false; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (fe->callback) { 1318c2ecf20Sopenharmony_ci ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 1328c2ecf20Sopenharmony_ci FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); 1338c2ecf20Sopenharmony_ci if (ret) 1348c2ecf20Sopenharmony_ci goto exit; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci switch (priv->cfg->xtal_freq) { 1388c2ecf20Sopenharmony_ci case FC_XTAL_27_MHZ: 1398c2ecf20Sopenharmony_ci xtal_freq_khz_2 = 27000 / 2; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci case FC_XTAL_36_MHZ: 1428c2ecf20Sopenharmony_ci xtal_freq_khz_2 = 36000 / 2; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case FC_XTAL_28_8_MHZ: 1458c2ecf20Sopenharmony_ci default: 1468c2ecf20Sopenharmony_ci xtal_freq_khz_2 = 28800 / 2; 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* select frequency divider and the frequency of VCO */ 1518c2ecf20Sopenharmony_ci if (freq < 37084) { /* freq * 96 < 3560000 */ 1528c2ecf20Sopenharmony_ci multi = 96; 1538c2ecf20Sopenharmony_ci reg[5] = 0x82; 1548c2ecf20Sopenharmony_ci reg[6] = 0x00; 1558c2ecf20Sopenharmony_ci } else if (freq < 55625) { /* freq * 64 < 3560000 */ 1568c2ecf20Sopenharmony_ci multi = 64; 1578c2ecf20Sopenharmony_ci reg[5] = 0x82; 1588c2ecf20Sopenharmony_ci reg[6] = 0x02; 1598c2ecf20Sopenharmony_ci } else if (freq < 74167) { /* freq * 48 < 3560000 */ 1608c2ecf20Sopenharmony_ci multi = 48; 1618c2ecf20Sopenharmony_ci reg[5] = 0x42; 1628c2ecf20Sopenharmony_ci reg[6] = 0x00; 1638c2ecf20Sopenharmony_ci } else if (freq < 111250) { /* freq * 32 < 3560000 */ 1648c2ecf20Sopenharmony_ci multi = 32; 1658c2ecf20Sopenharmony_ci reg[5] = 0x42; 1668c2ecf20Sopenharmony_ci reg[6] = 0x02; 1678c2ecf20Sopenharmony_ci } else if (freq < 148334) { /* freq * 24 < 3560000 */ 1688c2ecf20Sopenharmony_ci multi = 24; 1698c2ecf20Sopenharmony_ci reg[5] = 0x22; 1708c2ecf20Sopenharmony_ci reg[6] = 0x00; 1718c2ecf20Sopenharmony_ci } else if (freq < 222500) { /* freq * 16 < 3560000 */ 1728c2ecf20Sopenharmony_ci multi = 16; 1738c2ecf20Sopenharmony_ci reg[5] = 0x22; 1748c2ecf20Sopenharmony_ci reg[6] = 0x02; 1758c2ecf20Sopenharmony_ci } else if (freq < 296667) { /* freq * 12 < 3560000 */ 1768c2ecf20Sopenharmony_ci multi = 12; 1778c2ecf20Sopenharmony_ci reg[5] = 0x12; 1788c2ecf20Sopenharmony_ci reg[6] = 0x00; 1798c2ecf20Sopenharmony_ci } else if (freq < 445000) { /* freq * 8 < 3560000 */ 1808c2ecf20Sopenharmony_ci multi = 8; 1818c2ecf20Sopenharmony_ci reg[5] = 0x12; 1828c2ecf20Sopenharmony_ci reg[6] = 0x02; 1838c2ecf20Sopenharmony_ci } else if (freq < 593334) { /* freq * 6 < 3560000 */ 1848c2ecf20Sopenharmony_ci multi = 6; 1858c2ecf20Sopenharmony_ci reg[5] = 0x0a; 1868c2ecf20Sopenharmony_ci reg[6] = 0x00; 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci multi = 4; 1898c2ecf20Sopenharmony_ci reg[5] = 0x0a; 1908c2ecf20Sopenharmony_ci reg[6] = 0x02; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci f_vco = freq * multi; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (f_vco >= 3060000) { 1968c2ecf20Sopenharmony_ci reg[6] |= 0x08; 1978c2ecf20Sopenharmony_ci vco_select = true; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (freq >= 45000) { 2018c2ecf20Sopenharmony_ci /* From divided value (XDIV) determined the FA and FP value */ 2028c2ecf20Sopenharmony_ci xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); 2038c2ecf20Sopenharmony_ci if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) 2048c2ecf20Sopenharmony_ci xdiv++; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci pm = (unsigned char)(xdiv / 8); 2078c2ecf20Sopenharmony_ci am = (unsigned char)(xdiv - (8 * pm)); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (am < 2) { 2108c2ecf20Sopenharmony_ci reg[1] = am + 8; 2118c2ecf20Sopenharmony_ci reg[2] = pm - 1; 2128c2ecf20Sopenharmony_ci } else { 2138c2ecf20Sopenharmony_ci reg[1] = am; 2148c2ecf20Sopenharmony_ci reg[2] = pm; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci /* fix for frequency less than 45 MHz */ 2188c2ecf20Sopenharmony_ci reg[1] = 0x06; 2198c2ecf20Sopenharmony_ci reg[2] = 0x11; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* fix clock out */ 2238c2ecf20Sopenharmony_ci reg[6] |= 0x20; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* From VCO frequency determines the XIN ( fractional part of Delta 2268c2ecf20Sopenharmony_ci Sigma PLL) and divided value (XDIV) */ 2278c2ecf20Sopenharmony_ci xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); 2288c2ecf20Sopenharmony_ci xin = (xin << 15) / xtal_freq_khz_2; 2298c2ecf20Sopenharmony_ci if (xin >= 16384) 2308c2ecf20Sopenharmony_ci xin += 32768; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci reg[3] = xin >> 8; /* xin with 9 bit resolution */ 2338c2ecf20Sopenharmony_ci reg[4] = xin & 0xff; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (delsys == SYS_DVBT) { 2368c2ecf20Sopenharmony_ci reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ 2378c2ecf20Sopenharmony_ci switch (p->bandwidth_hz) { 2388c2ecf20Sopenharmony_ci case 6000000: 2398c2ecf20Sopenharmony_ci reg[6] |= 0x80; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case 7000000: 2428c2ecf20Sopenharmony_ci reg[6] |= 0x40; 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci case 8000000: 2458c2ecf20Sopenharmony_ci default: 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } else { 2498c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, "%s: modulation type not supported!\n", 2508c2ecf20Sopenharmony_ci KBUILD_MODNAME); 2518c2ecf20Sopenharmony_ci return -EINVAL; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* modified for Realtek demod */ 2558c2ecf20Sopenharmony_ci reg[5] |= 0x07; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2588c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci for (i = 1; i <= 6; i++) { 2618c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, i, reg[i]); 2628c2ecf20Sopenharmony_ci if (ret) 2638c2ecf20Sopenharmony_ci goto exit; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* VCO Calibration */ 2678c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0e, 0x80); 2688c2ecf20Sopenharmony_ci if (!ret) 2698c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0e, 0x00); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* VCO Re-Calibration if needed */ 2728c2ecf20Sopenharmony_ci if (!ret) 2738c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0e, 0x00); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!ret) { 2768c2ecf20Sopenharmony_ci msleep(10); 2778c2ecf20Sopenharmony_ci ret = fc0012_readreg(priv, 0x0e, &tmp); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci if (ret) 2808c2ecf20Sopenharmony_ci goto exit; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* vco selection */ 2838c2ecf20Sopenharmony_ci tmp &= 0x3f; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (vco_select) { 2868c2ecf20Sopenharmony_ci if (tmp > 0x3c) { 2878c2ecf20Sopenharmony_ci reg[6] &= ~0x08; 2888c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x06, reg[6]); 2898c2ecf20Sopenharmony_ci if (!ret) 2908c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0e, 0x80); 2918c2ecf20Sopenharmony_ci if (!ret) 2928c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0e, 0x00); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci } else { 2958c2ecf20Sopenharmony_ci if (tmp < 0x02) { 2968c2ecf20Sopenharmony_ci reg[6] |= 0x08; 2978c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x06, reg[6]); 2988c2ecf20Sopenharmony_ci if (!ret) 2998c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0e, 0x80); 3008c2ecf20Sopenharmony_ci if (!ret) 3018c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0e, 0x00); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci priv->frequency = p->frequency; 3068c2ecf20Sopenharmony_ci priv->bandwidth = p->bandwidth_hz; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ciexit: 3098c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3108c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 3118c2ecf20Sopenharmony_ci if (ret) 3128c2ecf20Sopenharmony_ci dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", 3138c2ecf20Sopenharmony_ci KBUILD_MODNAME, __func__, ret); 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct fc0012_priv *priv = fe->tuner_priv; 3208c2ecf20Sopenharmony_ci *frequency = priv->frequency; 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci *frequency = 0; /* Zero-IF */ 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct fc0012_priv *priv = fe->tuner_priv; 3338c2ecf20Sopenharmony_ci *bandwidth = priv->bandwidth; 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci#define INPUT_ADC_LEVEL -8 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct fc0012_priv *priv = fe->tuner_priv; 3428c2ecf20Sopenharmony_ci int ret; 3438c2ecf20Sopenharmony_ci unsigned char tmp; 3448c2ecf20Sopenharmony_ci int int_temp, lna_gain, int_lna, tot_agc_gain, power; 3458c2ecf20Sopenharmony_ci static const int fc0012_lna_gain_table[] = { 3468c2ecf20Sopenharmony_ci /* low gain */ 3478c2ecf20Sopenharmony_ci -63, -58, -99, -73, 3488c2ecf20Sopenharmony_ci -63, -65, -54, -60, 3498c2ecf20Sopenharmony_ci /* middle gain */ 3508c2ecf20Sopenharmony_ci 71, 70, 68, 67, 3518c2ecf20Sopenharmony_ci 65, 63, 61, 58, 3528c2ecf20Sopenharmony_ci /* high gain */ 3538c2ecf20Sopenharmony_ci 197, 191, 188, 186, 3548c2ecf20Sopenharmony_ci 184, 182, 181, 179, 3558c2ecf20Sopenharmony_ci }; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3588c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x12, 0x00); 3618c2ecf20Sopenharmony_ci if (ret) 3628c2ecf20Sopenharmony_ci goto err; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = fc0012_readreg(priv, 0x12, &tmp); 3658c2ecf20Sopenharmony_ci if (ret) 3668c2ecf20Sopenharmony_ci goto err; 3678c2ecf20Sopenharmony_ci int_temp = tmp; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = fc0012_readreg(priv, 0x13, &tmp); 3708c2ecf20Sopenharmony_ci if (ret) 3718c2ecf20Sopenharmony_ci goto err; 3728c2ecf20Sopenharmony_ci lna_gain = tmp & 0x1f; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3758c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) { 3788c2ecf20Sopenharmony_ci int_lna = fc0012_lna_gain_table[lna_gain]; 3798c2ecf20Sopenharmony_ci tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + 3808c2ecf20Sopenharmony_ci (int_temp & 0x1f)) * 2; 3818c2ecf20Sopenharmony_ci power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (power >= 45) 3848c2ecf20Sopenharmony_ci *strength = 255; /* 100% */ 3858c2ecf20Sopenharmony_ci else if (power < -95) 3868c2ecf20Sopenharmony_ci *strength = 0; 3878c2ecf20Sopenharmony_ci else 3888c2ecf20Sopenharmony_ci *strength = (power + 95) * 255 / 140; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci *strength |= *strength << 8; 3918c2ecf20Sopenharmony_ci } else { 3928c2ecf20Sopenharmony_ci ret = -1; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci goto exit; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cierr: 3988c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3998c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 4008c2ecf20Sopenharmony_ciexit: 4018c2ecf20Sopenharmony_ci if (ret) 4028c2ecf20Sopenharmony_ci dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", 4038c2ecf20Sopenharmony_ci KBUILD_MODNAME, __func__, ret); 4048c2ecf20Sopenharmony_ci return ret; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops fc0012_tuner_ops = { 4088c2ecf20Sopenharmony_ci .info = { 4098c2ecf20Sopenharmony_ci .name = "Fitipower FC0012", 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci .frequency_min_hz = 37 * MHz, /* estimate */ 4128c2ecf20Sopenharmony_ci .frequency_max_hz = 862 * MHz, /* estimate */ 4138c2ecf20Sopenharmony_ci }, 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci .release = fc0012_release, 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci .init = fc0012_init, 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci .set_params = fc0012_set_params, 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci .get_frequency = fc0012_get_frequency, 4228c2ecf20Sopenharmony_ci .get_if_frequency = fc0012_get_if_frequency, 4238c2ecf20Sopenharmony_ci .get_bandwidth = fc0012_get_bandwidth, 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci .get_rf_strength = fc0012_get_rf_strength, 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistruct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, 4298c2ecf20Sopenharmony_ci struct i2c_adapter *i2c, const struct fc0012_config *cfg) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct fc0012_priv *priv; 4328c2ecf20Sopenharmony_ci int ret; 4338c2ecf20Sopenharmony_ci u8 chip_id; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4368c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); 4398c2ecf20Sopenharmony_ci if (!priv) { 4408c2ecf20Sopenharmony_ci ret = -ENOMEM; 4418c2ecf20Sopenharmony_ci dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); 4428c2ecf20Sopenharmony_ci goto err; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci priv->cfg = cfg; 4468c2ecf20Sopenharmony_ci priv->i2c = i2c; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* check if the tuner is there */ 4498c2ecf20Sopenharmony_ci ret = fc0012_readreg(priv, 0x00, &chip_id); 4508c2ecf20Sopenharmony_ci if (ret < 0) 4518c2ecf20Sopenharmony_ci goto err; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (chip_id) { 4568c2ecf20Sopenharmony_ci case 0xa1: 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci default: 4598c2ecf20Sopenharmony_ci ret = -ENODEV; 4608c2ecf20Sopenharmony_ci goto err; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci dev_info(&i2c->dev, "%s: Fitipower FC0012 successfully identified\n", 4648c2ecf20Sopenharmony_ci KBUILD_MODNAME); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (priv->cfg->loop_through) { 4678c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x09, 0x6f); 4688c2ecf20Sopenharmony_ci if (ret < 0) 4698c2ecf20Sopenharmony_ci goto err; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* 4738c2ecf20Sopenharmony_ci * TODO: Clock out en or div? 4748c2ecf20Sopenharmony_ci * For dual tuner configuration clearing bit [0] is required. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci if (priv->cfg->clock_out) { 4778c2ecf20Sopenharmony_ci ret = fc0012_writereg(priv, 0x0b, 0x82); 4788c2ecf20Sopenharmony_ci if (ret < 0) 4798c2ecf20Sopenharmony_ci goto err; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci fe->tuner_priv = priv; 4838c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, 4848c2ecf20Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cierr: 4878c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4888c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (ret) { 4918c2ecf20Sopenharmony_ci dev_dbg(&i2c->dev, "%s: failed: %d\n", __func__, ret); 4928c2ecf20Sopenharmony_ci kfree(priv); 4938c2ecf20Sopenharmony_ci return NULL; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return fe; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fc0012_attach); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); 5018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>"); 5028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5038c2ecf20Sopenharmony_ciMODULE_VERSION("0.6"); 504