18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Fitipower FC0011 tuner driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Michael Buesch <m@bues.ch> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Derived from FC0012 tuner driver: 88c2ecf20Sopenharmony_ci * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "fc0011.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* Tuner registers */ 158c2ecf20Sopenharmony_cienum { 168c2ecf20Sopenharmony_ci FC11_REG_0, 178c2ecf20Sopenharmony_ci FC11_REG_FA, /* FA */ 188c2ecf20Sopenharmony_ci FC11_REG_FP, /* FP */ 198c2ecf20Sopenharmony_ci FC11_REG_XINHI, /* XIN high 8 bit */ 208c2ecf20Sopenharmony_ci FC11_REG_XINLO, /* XIN low 8 bit */ 218c2ecf20Sopenharmony_ci FC11_REG_VCO, /* VCO */ 228c2ecf20Sopenharmony_ci FC11_REG_VCOSEL, /* VCO select */ 238c2ecf20Sopenharmony_ci FC11_REG_7, /* Unknown tuner reg 7 */ 248c2ecf20Sopenharmony_ci FC11_REG_8, /* Unknown tuner reg 8 */ 258c2ecf20Sopenharmony_ci FC11_REG_9, 268c2ecf20Sopenharmony_ci FC11_REG_10, /* Unknown tuner reg 10 */ 278c2ecf20Sopenharmony_ci FC11_REG_11, /* Unknown tuner reg 11 */ 288c2ecf20Sopenharmony_ci FC11_REG_12, 298c2ecf20Sopenharmony_ci FC11_REG_RCCAL, /* RC calibrate */ 308c2ecf20Sopenharmony_ci FC11_REG_VCOCAL, /* VCO calibrate */ 318c2ecf20Sopenharmony_ci FC11_REG_15, 328c2ecf20Sopenharmony_ci FC11_REG_16, /* Unknown tuner reg 16 */ 338c2ecf20Sopenharmony_ci FC11_REG_17, 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci FC11_NR_REGS, /* Number of registers */ 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cienum FC11_REG_VCOSEL_bits { 398c2ecf20Sopenharmony_ci FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ 408c2ecf20Sopenharmony_ci FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ 418c2ecf20Sopenharmony_ci FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ 428c2ecf20Sopenharmony_ci FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ 438c2ecf20Sopenharmony_ci FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cienum FC11_REG_RCCAL_bits { 478c2ecf20Sopenharmony_ci FC11_RCCAL_FORCE = 0x10, /* force */ 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cienum FC11_REG_VCOCAL_bits { 518c2ecf20Sopenharmony_ci FC11_VCOCAL_RUN = 0, /* VCO calibration run */ 528c2ecf20Sopenharmony_ci FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ 538c2ecf20Sopenharmony_ci FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ 548c2ecf20Sopenharmony_ci FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct fc0011_priv { 598c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 608c2ecf20Sopenharmony_ci u8 addr; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci u32 frequency; 638c2ecf20Sopenharmony_ci u32 bandwidth; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci u8 buf[2] = { reg, val }; 708c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = priv->addr, 718c2ecf20Sopenharmony_ci .flags = 0, .buf = buf, .len = 2 }; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 748c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, 758c2ecf20Sopenharmony_ci "I2C write reg failed, reg: %02x, val: %02x\n", 768c2ecf20Sopenharmony_ci reg, val); 778c2ecf20Sopenharmony_ci return -EIO; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci u8 dummy; 868c2ecf20Sopenharmony_ci struct i2c_msg msg[2] = { 878c2ecf20Sopenharmony_ci { .addr = priv->addr, 888c2ecf20Sopenharmony_ci .flags = 0, .buf = ®, .len = 1 }, 898c2ecf20Sopenharmony_ci { .addr = priv->addr, 908c2ecf20Sopenharmony_ci .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, 918c2ecf20Sopenharmony_ci }; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, msg, 2) != 2) { 948c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, 958c2ecf20Sopenharmony_ci "I2C read failed, reg: %02x\n", reg); 968c2ecf20Sopenharmony_ci return -EIO; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void fc0011_release(struct dvb_frontend *fe) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 1058c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int fc0011_init(struct dvb_frontend *fe) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct fc0011_priv *priv = fe->tuner_priv; 1118c2ecf20Sopenharmony_ci int err; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (WARN_ON(!fe->callback)) 1148c2ecf20Sopenharmony_ci return -EINVAL; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 1178c2ecf20Sopenharmony_ci FC0011_FE_CALLBACK_POWER, priv->addr); 1188c2ecf20Sopenharmony_ci if (err) { 1198c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, "Power-on callback failed\n"); 1208c2ecf20Sopenharmony_ci return err; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 1238c2ecf20Sopenharmony_ci FC0011_FE_CALLBACK_RESET, priv->addr); 1248c2ecf20Sopenharmony_ci if (err) { 1258c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, "Reset callback failed\n"); 1268c2ecf20Sopenharmony_ci return err; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* Initiate VCO calibration */ 1338c2ecf20Sopenharmony_cistatic int fc0011_vcocal_trigger(struct fc0011_priv *priv) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci int err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); 1388c2ecf20Sopenharmony_ci if (err) 1398c2ecf20Sopenharmony_ci return err; 1408c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); 1418c2ecf20Sopenharmony_ci if (err) 1428c2ecf20Sopenharmony_ci return err; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* Read VCO calibration value */ 1488c2ecf20Sopenharmony_cistatic int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int err; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); 1538c2ecf20Sopenharmony_ci if (err) 1548c2ecf20Sopenharmony_ci return err; 1558c2ecf20Sopenharmony_ci usleep_range(10000, 20000); 1568c2ecf20Sopenharmony_ci err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); 1578c2ecf20Sopenharmony_ci if (err) 1588c2ecf20Sopenharmony_ci return err; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int fc0011_set_params(struct dvb_frontend *fe) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p = &fe->dtv_property_cache; 1668c2ecf20Sopenharmony_ci struct fc0011_priv *priv = fe->tuner_priv; 1678c2ecf20Sopenharmony_ci int err; 1688c2ecf20Sopenharmony_ci unsigned int i, vco_retries; 1698c2ecf20Sopenharmony_ci u32 freq = p->frequency / 1000; 1708c2ecf20Sopenharmony_ci u32 bandwidth = p->bandwidth_hz / 1000; 1718c2ecf20Sopenharmony_ci u32 fvco, xin, frac, xdiv, xdivr; 1728c2ecf20Sopenharmony_ci u8 fa, fp, vco_sel, vco_cal; 1738c2ecf20Sopenharmony_ci u8 regs[FC11_NR_REGS] = { }; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci regs[FC11_REG_7] = 0x0F; 1768c2ecf20Sopenharmony_ci regs[FC11_REG_8] = 0x3E; 1778c2ecf20Sopenharmony_ci regs[FC11_REG_10] = 0xB8; 1788c2ecf20Sopenharmony_ci regs[FC11_REG_11] = 0x80; 1798c2ecf20Sopenharmony_ci regs[FC11_REG_RCCAL] = 0x04; 1808c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); 1818c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); 1828c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); 1838c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); 1848c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); 1858c2ecf20Sopenharmony_ci if (err) 1868c2ecf20Sopenharmony_ci return -EIO; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Set VCO freq and VCO div */ 1898c2ecf20Sopenharmony_ci if (freq < 54000) { 1908c2ecf20Sopenharmony_ci fvco = freq * 64; 1918c2ecf20Sopenharmony_ci regs[FC11_REG_VCO] = 0x82; 1928c2ecf20Sopenharmony_ci } else if (freq < 108000) { 1938c2ecf20Sopenharmony_ci fvco = freq * 32; 1948c2ecf20Sopenharmony_ci regs[FC11_REG_VCO] = 0x42; 1958c2ecf20Sopenharmony_ci } else if (freq < 216000) { 1968c2ecf20Sopenharmony_ci fvco = freq * 16; 1978c2ecf20Sopenharmony_ci regs[FC11_REG_VCO] = 0x22; 1988c2ecf20Sopenharmony_ci } else if (freq < 432000) { 1998c2ecf20Sopenharmony_ci fvco = freq * 8; 2008c2ecf20Sopenharmony_ci regs[FC11_REG_VCO] = 0x12; 2018c2ecf20Sopenharmony_ci } else { 2028c2ecf20Sopenharmony_ci fvco = freq * 4; 2038c2ecf20Sopenharmony_ci regs[FC11_REG_VCO] = 0x0A; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Calc XIN. The PLL reference frequency is 18 MHz. */ 2078c2ecf20Sopenharmony_ci xdiv = fvco / 18000; 2088c2ecf20Sopenharmony_ci WARN_ON(xdiv > 0xFF); 2098c2ecf20Sopenharmony_ci frac = fvco - xdiv * 18000; 2108c2ecf20Sopenharmony_ci frac = (frac << 15) / 18000; 2118c2ecf20Sopenharmony_ci if (frac >= 16384) 2128c2ecf20Sopenharmony_ci frac += 32786; 2138c2ecf20Sopenharmony_ci if (!frac) 2148c2ecf20Sopenharmony_ci xin = 0; 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci xin = clamp_t(u32, frac, 512, 65024); 2178c2ecf20Sopenharmony_ci regs[FC11_REG_XINHI] = xin >> 8; 2188c2ecf20Sopenharmony_ci regs[FC11_REG_XINLO] = xin; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* Calc FP and FA */ 2218c2ecf20Sopenharmony_ci xdivr = xdiv; 2228c2ecf20Sopenharmony_ci if (fvco - xdiv * 18000 >= 9000) 2238c2ecf20Sopenharmony_ci xdivr += 1; /* round */ 2248c2ecf20Sopenharmony_ci fp = xdivr / 8; 2258c2ecf20Sopenharmony_ci fa = xdivr - fp * 8; 2268c2ecf20Sopenharmony_ci if (fa < 2) { 2278c2ecf20Sopenharmony_ci fp -= 1; 2288c2ecf20Sopenharmony_ci fa += 8; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci if (fp > 0x1F) { 2318c2ecf20Sopenharmony_ci fp = 0x1F; 2328c2ecf20Sopenharmony_ci fa = 0xF; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci if (fa >= fp) { 2358c2ecf20Sopenharmony_ci dev_warn(&priv->i2c->dev, 2368c2ecf20Sopenharmony_ci "fa %02X >= fp %02X, but trying to continue\n", 2378c2ecf20Sopenharmony_ci (unsigned int)(u8)fa, (unsigned int)(u8)fp); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci regs[FC11_REG_FA] = fa; 2408c2ecf20Sopenharmony_ci regs[FC11_REG_FP] = fp; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Select bandwidth */ 2438c2ecf20Sopenharmony_ci switch (bandwidth) { 2448c2ecf20Sopenharmony_ci case 8000: 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case 7000: 2478c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci default: 2508c2ecf20Sopenharmony_ci dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. Using 6000 kHz.\n", 2518c2ecf20Sopenharmony_ci bandwidth); 2528c2ecf20Sopenharmony_ci bandwidth = 6000; 2538c2ecf20Sopenharmony_ci fallthrough; 2548c2ecf20Sopenharmony_ci case 6000: 2558c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Pre VCO select */ 2608c2ecf20Sopenharmony_ci if (fvco < 2320000) { 2618c2ecf20Sopenharmony_ci vco_sel = 0; 2628c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 2638c2ecf20Sopenharmony_ci } else if (fvco < 3080000) { 2648c2ecf20Sopenharmony_ci vco_sel = 1; 2658c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 2668c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; 2678c2ecf20Sopenharmony_ci } else { 2688c2ecf20Sopenharmony_ci vco_sel = 2; 2698c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 2708c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Fix for low freqs */ 2748c2ecf20Sopenharmony_ci if (freq < 45000) { 2758c2ecf20Sopenharmony_ci regs[FC11_REG_FA] = 0x6; 2768c2ecf20Sopenharmony_ci regs[FC11_REG_FP] = 0x11; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Clock out fix */ 2808c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Write the cached registers */ 2838c2ecf20Sopenharmony_ci for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { 2848c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, i, regs[i]); 2858c2ecf20Sopenharmony_ci if (err) 2868c2ecf20Sopenharmony_ci return err; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* VCO calibration */ 2908c2ecf20Sopenharmony_ci err = fc0011_vcocal_trigger(priv); 2918c2ecf20Sopenharmony_ci if (err) 2928c2ecf20Sopenharmony_ci return err; 2938c2ecf20Sopenharmony_ci err = fc0011_vcocal_read(priv, &vco_cal); 2948c2ecf20Sopenharmony_ci if (err) 2958c2ecf20Sopenharmony_ci return err; 2968c2ecf20Sopenharmony_ci vco_retries = 0; 2978c2ecf20Sopenharmony_ci while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { 2988c2ecf20Sopenharmony_ci /* Reset the tuner and try again */ 2998c2ecf20Sopenharmony_ci err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, 3008c2ecf20Sopenharmony_ci FC0011_FE_CALLBACK_RESET, priv->addr); 3018c2ecf20Sopenharmony_ci if (err) { 3028c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); 3038c2ecf20Sopenharmony_ci return err; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci /* Reinit tuner config */ 3068c2ecf20Sopenharmony_ci err = 0; 3078c2ecf20Sopenharmony_ci for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) 3088c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, i, regs[i]); 3098c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); 3108c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); 3118c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); 3128c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); 3138c2ecf20Sopenharmony_ci err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); 3148c2ecf20Sopenharmony_ci if (err) 3158c2ecf20Sopenharmony_ci return -EIO; 3168c2ecf20Sopenharmony_ci /* VCO calibration */ 3178c2ecf20Sopenharmony_ci err = fc0011_vcocal_trigger(priv); 3188c2ecf20Sopenharmony_ci if (err) 3198c2ecf20Sopenharmony_ci return err; 3208c2ecf20Sopenharmony_ci err = fc0011_vcocal_read(priv, &vco_cal); 3218c2ecf20Sopenharmony_ci if (err) 3228c2ecf20Sopenharmony_ci return err; 3238c2ecf20Sopenharmony_ci vco_retries++; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci if (!(vco_cal & FC11_VCOCAL_OK)) { 3268c2ecf20Sopenharmony_ci dev_err(&priv->i2c->dev, 3278c2ecf20Sopenharmony_ci "Failed to read VCO calibration value (got %02X)\n", 3288c2ecf20Sopenharmony_ci (unsigned int)vco_cal); 3298c2ecf20Sopenharmony_ci return -EIO; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci vco_cal &= FC11_VCOCAL_VALUEMASK; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci switch (vco_sel) { 3348c2ecf20Sopenharmony_ci default: 3358c2ecf20Sopenharmony_ci WARN_ON(1); 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci case 0: 3388c2ecf20Sopenharmony_ci if (vco_cal < 8) { 3398c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 3408c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; 3418c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOSEL, 3428c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL]); 3438c2ecf20Sopenharmony_ci if (err) 3448c2ecf20Sopenharmony_ci return err; 3458c2ecf20Sopenharmony_ci err = fc0011_vcocal_trigger(priv); 3468c2ecf20Sopenharmony_ci if (err) 3478c2ecf20Sopenharmony_ci return err; 3488c2ecf20Sopenharmony_ci } else { 3498c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 3508c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOSEL, 3518c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL]); 3528c2ecf20Sopenharmony_ci if (err) 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case 1: 3578c2ecf20Sopenharmony_ci if (vco_cal < 5) { 3588c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 3598c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; 3608c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOSEL, 3618c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL]); 3628c2ecf20Sopenharmony_ci if (err) 3638c2ecf20Sopenharmony_ci return err; 3648c2ecf20Sopenharmony_ci err = fc0011_vcocal_trigger(priv); 3658c2ecf20Sopenharmony_ci if (err) 3668c2ecf20Sopenharmony_ci return err; 3678c2ecf20Sopenharmony_ci } else if (vco_cal <= 48) { 3688c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 3698c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; 3708c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOSEL, 3718c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL]); 3728c2ecf20Sopenharmony_ci if (err) 3738c2ecf20Sopenharmony_ci return err; 3748c2ecf20Sopenharmony_ci } else { 3758c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 3768c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOSEL, 3778c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL]); 3788c2ecf20Sopenharmony_ci if (err) 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci err = fc0011_vcocal_trigger(priv); 3818c2ecf20Sopenharmony_ci if (err) 3828c2ecf20Sopenharmony_ci return err; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci case 2: 3868c2ecf20Sopenharmony_ci if (vco_cal > 53) { 3878c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 3888c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; 3898c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOSEL, 3908c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL]); 3918c2ecf20Sopenharmony_ci if (err) 3928c2ecf20Sopenharmony_ci return err; 3938c2ecf20Sopenharmony_ci err = fc0011_vcocal_trigger(priv); 3948c2ecf20Sopenharmony_ci if (err) 3958c2ecf20Sopenharmony_ci return err; 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); 3988c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; 3998c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_VCOSEL, 4008c2ecf20Sopenharmony_ci regs[FC11_REG_VCOSEL]); 4018c2ecf20Sopenharmony_ci if (err) 4028c2ecf20Sopenharmony_ci return err; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci err = fc0011_vcocal_read(priv, NULL); 4078c2ecf20Sopenharmony_ci if (err) 4088c2ecf20Sopenharmony_ci return err; 4098c2ecf20Sopenharmony_ci usleep_range(10000, 50000); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); 4128c2ecf20Sopenharmony_ci if (err) 4138c2ecf20Sopenharmony_ci return err; 4148c2ecf20Sopenharmony_ci regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; 4158c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); 4168c2ecf20Sopenharmony_ci if (err) 4178c2ecf20Sopenharmony_ci return err; 4188c2ecf20Sopenharmony_ci regs[FC11_REG_16] = 0xB; 4198c2ecf20Sopenharmony_ci err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]); 4208c2ecf20Sopenharmony_ci if (err) 4218c2ecf20Sopenharmony_ci return err; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, "Tuned to fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X vcocal=%02X(%u) bw=%u\n", 4248c2ecf20Sopenharmony_ci (unsigned int)regs[FC11_REG_FA], 4258c2ecf20Sopenharmony_ci (unsigned int)regs[FC11_REG_FP], 4268c2ecf20Sopenharmony_ci (unsigned int)regs[FC11_REG_XINHI], 4278c2ecf20Sopenharmony_ci (unsigned int)regs[FC11_REG_XINLO], 4288c2ecf20Sopenharmony_ci (unsigned int)regs[FC11_REG_VCO], 4298c2ecf20Sopenharmony_ci (unsigned int)regs[FC11_REG_VCOSEL], 4308c2ecf20Sopenharmony_ci (unsigned int)vco_cal, vco_retries, 4318c2ecf20Sopenharmony_ci (unsigned int)bandwidth); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci priv->frequency = p->frequency; 4348c2ecf20Sopenharmony_ci priv->bandwidth = p->bandwidth_hz; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci return 0; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct fc0011_priv *priv = fe->tuner_priv; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci *frequency = priv->frequency; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci return 0; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci *frequency = 0; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct fc0011_priv *priv = fe->tuner_priv; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci *bandwidth = priv->bandwidth; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops fc0011_tuner_ops = { 4658c2ecf20Sopenharmony_ci .info = { 4668c2ecf20Sopenharmony_ci .name = "Fitipower FC0011", 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci .frequency_min_hz = 45 * MHz, 4698c2ecf20Sopenharmony_ci .frequency_max_hz = 1000 * MHz, 4708c2ecf20Sopenharmony_ci }, 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci .release = fc0011_release, 4738c2ecf20Sopenharmony_ci .init = fc0011_init, 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci .set_params = fc0011_set_params, 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci .get_frequency = fc0011_get_frequency, 4788c2ecf20Sopenharmony_ci .get_if_frequency = fc0011_get_if_frequency, 4798c2ecf20Sopenharmony_ci .get_bandwidth = fc0011_get_bandwidth, 4808c2ecf20Sopenharmony_ci}; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistruct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, 4838c2ecf20Sopenharmony_ci struct i2c_adapter *i2c, 4848c2ecf20Sopenharmony_ci const struct fc0011_config *config) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci struct fc0011_priv *priv; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); 4898c2ecf20Sopenharmony_ci if (!priv) 4908c2ecf20Sopenharmony_ci return NULL; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci priv->i2c = i2c; 4938c2ecf20Sopenharmony_ci priv->addr = config->i2c_address; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci fe->tuner_priv = priv; 4968c2ecf20Sopenharmony_ci fe->ops.tuner_ops = fc0011_tuner_ops; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return fe; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fc0011_attach); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); 5058c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Buesch <m@bues.ch>"); 5068c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 507