18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Montage Technology TS2020 - Silicon Tuner driver 48c2ecf20Sopenharmony_ci Copyright (C) 2009-2012 Konstantin Dimitrov <kosio.dimitrov@gmail.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci Copyright (C) 2009-2012 TurboSight.com 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 118c2ecf20Sopenharmony_ci#include "ts2020.h" 128c2ecf20Sopenharmony_ci#include <linux/regmap.h> 138c2ecf20Sopenharmony_ci#include <linux/math64.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define TS2020_XTAL_FREQ 27000 /* in kHz */ 168c2ecf20Sopenharmony_ci#define FREQ_OFFSET_LOW_SYM_RATE 3000 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistruct ts2020_priv { 198c2ecf20Sopenharmony_ci struct i2c_client *client; 208c2ecf20Sopenharmony_ci struct mutex regmap_mutex; 218c2ecf20Sopenharmony_ci struct regmap_config regmap_config; 228c2ecf20Sopenharmony_ci struct regmap *regmap; 238c2ecf20Sopenharmony_ci struct dvb_frontend *fe; 248c2ecf20Sopenharmony_ci struct delayed_work stat_work; 258c2ecf20Sopenharmony_ci int (*get_agc_pwm)(struct dvb_frontend *fe, u8 *_agc_pwm); 268c2ecf20Sopenharmony_ci /* i2c details */ 278c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 288c2ecf20Sopenharmony_ci int i2c_address; 298c2ecf20Sopenharmony_ci bool loop_through:1; 308c2ecf20Sopenharmony_ci u8 clk_out:2; 318c2ecf20Sopenharmony_ci u8 clk_out_div:5; 328c2ecf20Sopenharmony_ci bool dont_poll:1; 338c2ecf20Sopenharmony_ci u32 frequency_div; /* LO output divider switch frequency */ 348c2ecf20Sopenharmony_ci u32 frequency_khz; /* actual used LO frequency */ 358c2ecf20Sopenharmony_ci#define TS2020_M88TS2020 0 368c2ecf20Sopenharmony_ci#define TS2020_M88TS2022 1 378c2ecf20Sopenharmony_ci u8 tuner; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct ts2020_reg_val { 418c2ecf20Sopenharmony_ci u8 reg; 428c2ecf20Sopenharmony_ci u8 val; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void ts2020_stat_work(struct work_struct *work); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void ts2020_release(struct dvb_frontend *fe) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 508c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci i2c_unregister_device(client); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int ts2020_sleep(struct dvb_frontend *fe) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 608c2ecf20Sopenharmony_ci int ret; 618c2ecf20Sopenharmony_ci u8 u8tmp; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (priv->tuner == TS2020_M88TS2020) 648c2ecf20Sopenharmony_ci u8tmp = 0x0a; /* XXX: probably wrong */ 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci u8tmp = 0x00; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, u8tmp, 0x00); 698c2ecf20Sopenharmony_ci if (ret < 0) 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* stop statistics polling */ 738c2ecf20Sopenharmony_ci if (!priv->dont_poll) 748c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&priv->stat_work); 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int ts2020_init(struct dvb_frontend *fe) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 818c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 828c2ecf20Sopenharmony_ci int i; 838c2ecf20Sopenharmony_ci u8 u8tmp; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (priv->tuner == TS2020_M88TS2020) { 868c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x42, 0x73); 878c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x05, priv->clk_out_div); 888c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x20, 0x27); 898c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x07, 0x02); 908c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x11, 0xff); 918c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x60, 0xf9); 928c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x08, 0x01); 938c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x00, 0x41); 948c2ecf20Sopenharmony_ci } else { 958c2ecf20Sopenharmony_ci static const struct ts2020_reg_val reg_vals[] = { 968c2ecf20Sopenharmony_ci {0x7d, 0x9d}, 978c2ecf20Sopenharmony_ci {0x7c, 0x9a}, 988c2ecf20Sopenharmony_ci {0x7a, 0x76}, 998c2ecf20Sopenharmony_ci {0x3b, 0x01}, 1008c2ecf20Sopenharmony_ci {0x63, 0x88}, 1018c2ecf20Sopenharmony_ci {0x61, 0x85}, 1028c2ecf20Sopenharmony_ci {0x22, 0x30}, 1038c2ecf20Sopenharmony_ci {0x30, 0x40}, 1048c2ecf20Sopenharmony_ci {0x20, 0x23}, 1058c2ecf20Sopenharmony_ci {0x24, 0x02}, 1068c2ecf20Sopenharmony_ci {0x12, 0xa0}, 1078c2ecf20Sopenharmony_ci }; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x00, 0x01); 1108c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x00, 0x03); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci switch (priv->clk_out) { 1138c2ecf20Sopenharmony_ci case TS2020_CLK_OUT_DISABLED: 1148c2ecf20Sopenharmony_ci u8tmp = 0x60; 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci case TS2020_CLK_OUT_ENABLED: 1178c2ecf20Sopenharmony_ci u8tmp = 0x70; 1188c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x05, priv->clk_out_div); 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case TS2020_CLK_OUT_ENABLED_XTALOUT: 1218c2ecf20Sopenharmony_ci u8tmp = 0x6c; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci default: 1248c2ecf20Sopenharmony_ci u8tmp = 0x60; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x42, u8tmp); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (priv->loop_through) 1318c2ecf20Sopenharmony_ci u8tmp = 0xec; 1328c2ecf20Sopenharmony_ci else 1338c2ecf20Sopenharmony_ci u8tmp = 0x6c; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci regmap_write(priv->regmap, 0x62, u8tmp); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(reg_vals); i++) 1388c2ecf20Sopenharmony_ci regmap_write(priv->regmap, reg_vals[i].reg, 1398c2ecf20Sopenharmony_ci reg_vals[i].val); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Initialise v5 stats here */ 1438c2ecf20Sopenharmony_ci c->strength.len = 1; 1448c2ecf20Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_DECIBEL; 1458c2ecf20Sopenharmony_ci c->strength.stat[0].uvalue = 0; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Start statistics polling by invoking the work function */ 1488c2ecf20Sopenharmony_ci ts2020_stat_work(&priv->stat_work.work); 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, 0x51, 0x1f - offset); 1578c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x51, 0x1f); 1588c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x50, offset); 1598c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x50, 0x00); 1608c2ecf20Sopenharmony_ci msleep(20); 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int ts2020_set_tuner_rf(struct dvb_frontend *fe) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct ts2020_priv *dev = fe->tuner_priv; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci unsigned int utmp; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci ret = regmap_read(dev->regmap, 0x3d, &utmp); 1718c2ecf20Sopenharmony_ci if (ret) 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci utmp &= 0x7f; 1758c2ecf20Sopenharmony_ci if (utmp < 0x16) 1768c2ecf20Sopenharmony_ci utmp = 0xa1; 1778c2ecf20Sopenharmony_ci else if (utmp == 0x16) 1788c2ecf20Sopenharmony_ci utmp = 0x99; 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci utmp = 0xf9; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci regmap_write(dev->regmap, 0x60, utmp); 1838c2ecf20Sopenharmony_ci ret = ts2020_tuner_gate_ctrl(fe, 0x08); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int ts2020_set_params(struct dvb_frontend *fe) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1918c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 1928c2ecf20Sopenharmony_ci int ret; 1938c2ecf20Sopenharmony_ci unsigned int utmp; 1948c2ecf20Sopenharmony_ci u32 f3db, gdiv28; 1958c2ecf20Sopenharmony_ci u16 u16tmp, value, lpf_coeff; 1968c2ecf20Sopenharmony_ci u8 buf[3], reg10, lpf_mxdiv, mlpf_max, mlpf_min, nlpf; 1978c2ecf20Sopenharmony_ci unsigned int f_ref_khz, f_vco_khz, div_ref, div_out, pll_n; 1988c2ecf20Sopenharmony_ci unsigned int frequency_khz = c->frequency; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * Integer-N PLL synthesizer 2028c2ecf20Sopenharmony_ci * kHz is used for all calculations to keep calculations within 32-bit 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci f_ref_khz = TS2020_XTAL_FREQ; 2058c2ecf20Sopenharmony_ci div_ref = DIV_ROUND_CLOSEST(f_ref_khz, 2000); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* select LO output divider */ 2088c2ecf20Sopenharmony_ci if (frequency_khz < priv->frequency_div) { 2098c2ecf20Sopenharmony_ci div_out = 4; 2108c2ecf20Sopenharmony_ci reg10 = 0x10; 2118c2ecf20Sopenharmony_ci } else { 2128c2ecf20Sopenharmony_ci div_out = 2; 2138c2ecf20Sopenharmony_ci reg10 = 0x00; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci f_vco_khz = frequency_khz * div_out; 2178c2ecf20Sopenharmony_ci pll_n = f_vco_khz * div_ref / f_ref_khz; 2188c2ecf20Sopenharmony_ci pll_n += pll_n % 2; 2198c2ecf20Sopenharmony_ci priv->frequency_khz = pll_n * f_ref_khz / div_ref / div_out; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci pr_debug("frequency=%u offset=%d f_vco_khz=%u pll_n=%u div_ref=%u div_out=%u\n", 2228c2ecf20Sopenharmony_ci priv->frequency_khz, priv->frequency_khz - c->frequency, 2238c2ecf20Sopenharmony_ci f_vco_khz, pll_n, div_ref, div_out); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (priv->tuner == TS2020_M88TS2020) { 2268c2ecf20Sopenharmony_ci lpf_coeff = 2766; 2278c2ecf20Sopenharmony_ci reg10 |= 0x01; 2288c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, 0x10, reg10); 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci lpf_coeff = 3200; 2318c2ecf20Sopenharmony_ci reg10 |= 0x0b; 2328c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, 0x10, reg10); 2338c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x11, 0x40); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci u16tmp = pll_n - 1024; 2378c2ecf20Sopenharmony_ci buf[0] = (u16tmp >> 8) & 0xff; 2388c2ecf20Sopenharmony_ci buf[1] = (u16tmp >> 0) & 0xff; 2398c2ecf20Sopenharmony_ci buf[2] = div_ref - 8; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x01, buf[0]); 2428c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x02, buf[1]); 2438c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x03, buf[2]); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ret |= ts2020_tuner_gate_ctrl(fe, 0x10); 2468c2ecf20Sopenharmony_ci if (ret < 0) 2478c2ecf20Sopenharmony_ci return -ENODEV; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci ret |= ts2020_tuner_gate_ctrl(fe, 0x08); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Tuner RF */ 2528c2ecf20Sopenharmony_ci if (priv->tuner == TS2020_M88TS2020) 2538c2ecf20Sopenharmony_ci ret |= ts2020_set_tuner_rf(fe); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; 2568c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x04, gdiv28 & 0xff); 2578c2ecf20Sopenharmony_ci ret |= ts2020_tuner_gate_ctrl(fe, 0x04); 2588c2ecf20Sopenharmony_ci if (ret < 0) 2598c2ecf20Sopenharmony_ci return -ENODEV; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (priv->tuner == TS2020_M88TS2022) { 2628c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, 0x25, 0x00); 2638c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x27, 0x70); 2648c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x41, 0x09); 2658c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x08, 0x0b); 2668c2ecf20Sopenharmony_ci if (ret < 0) 2678c2ecf20Sopenharmony_ci return -ENODEV; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci regmap_read(priv->regmap, 0x26, &utmp); 2718c2ecf20Sopenharmony_ci value = utmp; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci f3db = (c->bandwidth_hz / 1000 / 2) + 2000; 2748c2ecf20Sopenharmony_ci f3db += FREQ_OFFSET_LOW_SYM_RATE; /* FIXME: ~always too wide filter */ 2758c2ecf20Sopenharmony_ci f3db = clamp(f3db, 7000U, 40000U); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci gdiv28 = gdiv28 * 207 / (value * 2 + 151); 2788c2ecf20Sopenharmony_ci mlpf_max = gdiv28 * 135 / 100; 2798c2ecf20Sopenharmony_ci mlpf_min = gdiv28 * 78 / 100; 2808c2ecf20Sopenharmony_ci if (mlpf_max > 63) 2818c2ecf20Sopenharmony_ci mlpf_max = 63; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci nlpf = (f3db * gdiv28 * 2 / lpf_coeff / 2848c2ecf20Sopenharmony_ci (TS2020_XTAL_FREQ / 1000) + 1) / 2; 2858c2ecf20Sopenharmony_ci if (nlpf > 23) 2868c2ecf20Sopenharmony_ci nlpf = 23; 2878c2ecf20Sopenharmony_ci if (nlpf < 1) 2888c2ecf20Sopenharmony_ci nlpf = 1; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) 2918c2ecf20Sopenharmony_ci * lpf_coeff * 2 / f3db + 1) / 2; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (lpf_mxdiv < mlpf_min) { 2948c2ecf20Sopenharmony_ci nlpf++; 2958c2ecf20Sopenharmony_ci lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) 2968c2ecf20Sopenharmony_ci * lpf_coeff * 2 / f3db + 1) / 2; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (lpf_mxdiv > mlpf_max) 3008c2ecf20Sopenharmony_ci lpf_mxdiv = mlpf_max; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = regmap_write(priv->regmap, 0x04, lpf_mxdiv); 3038c2ecf20Sopenharmony_ci ret |= regmap_write(priv->regmap, 0x06, nlpf); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret |= ts2020_tuner_gate_ctrl(fe, 0x04); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci ret |= ts2020_tuner_gate_ctrl(fe, 0x01); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci msleep(80); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci return (ret < 0) ? -EINVAL : 0; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci *frequency = priv->frequency_khz; 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int ts2020_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci *frequency = 0; /* Zero-IF */ 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/* 3298c2ecf20Sopenharmony_ci * Get the tuner gain. 3308c2ecf20Sopenharmony_ci * @fe: The front end for which we're determining the gain 3318c2ecf20Sopenharmony_ci * @v_agc: The voltage of the AGC from the demodulator (0-2600mV) 3328c2ecf20Sopenharmony_ci * @_gain: Where to store the gain (in 0.001dB units) 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * Returns 0 or a negative error code. 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cistatic int ts2020_read_tuner_gain(struct dvb_frontend *fe, unsigned v_agc, 3378c2ecf20Sopenharmony_ci __s64 *_gain) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 3408c2ecf20Sopenharmony_ci unsigned long gain1, gain2, gain3; 3418c2ecf20Sopenharmony_ci unsigned utmp; 3428c2ecf20Sopenharmony_ci int ret; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Read the RF gain */ 3458c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, 0x3d, &utmp); 3468c2ecf20Sopenharmony_ci if (ret < 0) 3478c2ecf20Sopenharmony_ci return ret; 3488c2ecf20Sopenharmony_ci gain1 = utmp & 0x1f; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Read the baseband gain */ 3518c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, 0x21, &utmp); 3528c2ecf20Sopenharmony_ci if (ret < 0) 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci gain2 = utmp & 0x1f; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci switch (priv->tuner) { 3578c2ecf20Sopenharmony_ci case TS2020_M88TS2020: 3588c2ecf20Sopenharmony_ci gain1 = clamp_t(long, gain1, 0, 15); 3598c2ecf20Sopenharmony_ci gain2 = clamp_t(long, gain2, 0, 13); 3608c2ecf20Sopenharmony_ci v_agc = clamp_t(long, v_agc, 400, 1100); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci *_gain = -((__s64)gain1 * 2330 + 3638c2ecf20Sopenharmony_ci gain2 * 3500 + 3648c2ecf20Sopenharmony_ci v_agc * 24 / 10 * 10 + 3658c2ecf20Sopenharmony_ci 10000); 3668c2ecf20Sopenharmony_ci /* gain in range -19600 to -116850 in units of 0.001dB */ 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci case TS2020_M88TS2022: 3708c2ecf20Sopenharmony_ci ret = regmap_read(priv->regmap, 0x66, &utmp); 3718c2ecf20Sopenharmony_ci if (ret < 0) 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci gain3 = (utmp >> 3) & 0x07; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci gain1 = clamp_t(long, gain1, 0, 15); 3768c2ecf20Sopenharmony_ci gain2 = clamp_t(long, gain2, 2, 16); 3778c2ecf20Sopenharmony_ci gain3 = clamp_t(long, gain3, 0, 6); 3788c2ecf20Sopenharmony_ci v_agc = clamp_t(long, v_agc, 600, 1600); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci *_gain = -((__s64)gain1 * 2650 + 3818c2ecf20Sopenharmony_ci gain2 * 3380 + 3828c2ecf20Sopenharmony_ci gain3 * 2850 + 3838c2ecf20Sopenharmony_ci v_agc * 176 / 100 * 10 - 3848c2ecf20Sopenharmony_ci 30000); 3858c2ecf20Sopenharmony_ci /* gain in range -47320 to -158950 in units of 0.001dB */ 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/* 3938c2ecf20Sopenharmony_ci * Get the AGC information from the demodulator and use that to calculate the 3948c2ecf20Sopenharmony_ci * tuner gain. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_cistatic int ts2020_get_tuner_gain(struct dvb_frontend *fe, __s64 *_gain) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 3998c2ecf20Sopenharmony_ci int v_agc = 0, ret; 4008c2ecf20Sopenharmony_ci u8 agc_pwm; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Read the AGC PWM rate from the demodulator */ 4038c2ecf20Sopenharmony_ci if (priv->get_agc_pwm) { 4048c2ecf20Sopenharmony_ci ret = priv->get_agc_pwm(fe, &agc_pwm); 4058c2ecf20Sopenharmony_ci if (ret < 0) 4068c2ecf20Sopenharmony_ci return ret; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci switch (priv->tuner) { 4098c2ecf20Sopenharmony_ci case TS2020_M88TS2020: 4108c2ecf20Sopenharmony_ci v_agc = (int)agc_pwm * 20 - 1166; 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case TS2020_M88TS2022: 4138c2ecf20Sopenharmony_ci v_agc = (int)agc_pwm * 16 - 670; 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (v_agc < 0) 4188c2ecf20Sopenharmony_ci v_agc = 0; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return ts2020_read_tuner_gain(fe, v_agc, _gain); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/* 4258c2ecf20Sopenharmony_ci * Gather statistics on a regular basis 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_cistatic void ts2020_stat_work(struct work_struct *work) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct ts2020_priv *priv = container_of(work, struct ts2020_priv, 4308c2ecf20Sopenharmony_ci stat_work.work); 4318c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client; 4328c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &priv->fe->dtv_property_cache; 4338c2ecf20Sopenharmony_ci int ret; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ret = ts2020_get_tuner_gain(priv->fe, &c->strength.stat[0].svalue); 4388c2ecf20Sopenharmony_ci if (ret < 0) 4398c2ecf20Sopenharmony_ci goto err; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_DECIBEL; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (!priv->dont_poll) 4448c2ecf20Sopenharmony_ci schedule_delayed_work(&priv->stat_work, msecs_to_jiffies(2000)); 4458c2ecf20Sopenharmony_ci return; 4468c2ecf20Sopenharmony_cierr: 4478c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* 4518c2ecf20Sopenharmony_ci * Read TS2020 signal strength in v3 format. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_cistatic int ts2020_read_signal_strength(struct dvb_frontend *fe, 4548c2ecf20Sopenharmony_ci u16 *_signal_strength) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 4578c2ecf20Sopenharmony_ci struct ts2020_priv *priv = fe->tuner_priv; 4588c2ecf20Sopenharmony_ci unsigned strength; 4598c2ecf20Sopenharmony_ci __s64 gain; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (priv->dont_poll) 4628c2ecf20Sopenharmony_ci ts2020_stat_work(&priv->stat_work.work); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (c->strength.stat[0].scale == FE_SCALE_NOT_AVAILABLE) { 4658c2ecf20Sopenharmony_ci *_signal_strength = 0; 4668c2ecf20Sopenharmony_ci return 0; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci gain = c->strength.stat[0].svalue; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Calculate the signal strength based on the total gain of the tuner */ 4728c2ecf20Sopenharmony_ci if (gain < -85000) 4738c2ecf20Sopenharmony_ci /* 0%: no signal or weak signal */ 4748c2ecf20Sopenharmony_ci strength = 0; 4758c2ecf20Sopenharmony_ci else if (gain < -65000) 4768c2ecf20Sopenharmony_ci /* 0% - 60%: weak signal */ 4778c2ecf20Sopenharmony_ci strength = 0 + div64_s64((85000 + gain) * 3, 1000); 4788c2ecf20Sopenharmony_ci else if (gain < -45000) 4798c2ecf20Sopenharmony_ci /* 60% - 90%: normal signal */ 4808c2ecf20Sopenharmony_ci strength = 60 + div64_s64((65000 + gain) * 3, 2000); 4818c2ecf20Sopenharmony_ci else 4828c2ecf20Sopenharmony_ci /* 90% - 99%: strong signal */ 4838c2ecf20Sopenharmony_ci strength = 90 + div64_s64((45000 + gain), 5000); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci *_signal_strength = strength * 65535 / 100; 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops ts2020_tuner_ops = { 4908c2ecf20Sopenharmony_ci .info = { 4918c2ecf20Sopenharmony_ci .name = "TS2020", 4928c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 4938c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz 4948c2ecf20Sopenharmony_ci }, 4958c2ecf20Sopenharmony_ci .init = ts2020_init, 4968c2ecf20Sopenharmony_ci .release = ts2020_release, 4978c2ecf20Sopenharmony_ci .sleep = ts2020_sleep, 4988c2ecf20Sopenharmony_ci .set_params = ts2020_set_params, 4998c2ecf20Sopenharmony_ci .get_frequency = ts2020_get_frequency, 5008c2ecf20Sopenharmony_ci .get_if_frequency = ts2020_get_if_frequency, 5018c2ecf20Sopenharmony_ci .get_rf_strength = ts2020_read_signal_strength, 5028c2ecf20Sopenharmony_ci}; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistruct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, 5058c2ecf20Sopenharmony_ci const struct ts2020_config *config, 5068c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct i2c_client *client; 5098c2ecf20Sopenharmony_ci struct i2c_board_info board_info; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* This is only used by ts2020_probe() so can be on the stack */ 5128c2ecf20Sopenharmony_ci struct ts2020_config pdata; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci memcpy(&pdata, config, sizeof(pdata)); 5158c2ecf20Sopenharmony_ci pdata.fe = fe; 5168c2ecf20Sopenharmony_ci pdata.attach_in_use = true; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci memset(&board_info, 0, sizeof(board_info)); 5198c2ecf20Sopenharmony_ci strscpy(board_info.type, "ts2020", I2C_NAME_SIZE); 5208c2ecf20Sopenharmony_ci board_info.addr = config->tuner_address; 5218c2ecf20Sopenharmony_ci board_info.platform_data = &pdata; 5228c2ecf20Sopenharmony_ci client = i2c_new_client_device(i2c, &board_info); 5238c2ecf20Sopenharmony_ci if (!i2c_client_has_driver(client)) 5248c2ecf20Sopenharmony_ci return NULL; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return fe; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ts2020_attach); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci/* 5318c2ecf20Sopenharmony_ci * We implement own regmap locking due to legacy DVB attach which uses frontend 5328c2ecf20Sopenharmony_ci * gate control callback to control I2C bus access. We can open / close gate and 5338c2ecf20Sopenharmony_ci * serialize whole open / I2C-operation / close sequence at the same. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_cistatic void ts2020_regmap_lock(void *__dev) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct ts2020_priv *dev = __dev; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci mutex_lock(&dev->regmap_mutex); 5408c2ecf20Sopenharmony_ci if (dev->fe->ops.i2c_gate_ctrl) 5418c2ecf20Sopenharmony_ci dev->fe->ops.i2c_gate_ctrl(dev->fe, 1); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic void ts2020_regmap_unlock(void *__dev) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct ts2020_priv *dev = __dev; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (dev->fe->ops.i2c_gate_ctrl) 5498c2ecf20Sopenharmony_ci dev->fe->ops.i2c_gate_ctrl(dev->fe, 0); 5508c2ecf20Sopenharmony_ci mutex_unlock(&dev->regmap_mutex); 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int ts2020_probe(struct i2c_client *client, 5548c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct ts2020_config *pdata = client->dev.platform_data; 5578c2ecf20Sopenharmony_ci struct dvb_frontend *fe = pdata->fe; 5588c2ecf20Sopenharmony_ci struct ts2020_priv *dev; 5598c2ecf20Sopenharmony_ci int ret; 5608c2ecf20Sopenharmony_ci u8 u8tmp; 5618c2ecf20Sopenharmony_ci unsigned int utmp; 5628c2ecf20Sopenharmony_ci char *chip_str; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 5658c2ecf20Sopenharmony_ci if (!dev) { 5668c2ecf20Sopenharmony_ci ret = -ENOMEM; 5678c2ecf20Sopenharmony_ci goto err; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* create regmap */ 5718c2ecf20Sopenharmony_ci mutex_init(&dev->regmap_mutex); 5728c2ecf20Sopenharmony_ci dev->regmap_config.reg_bits = 8, 5738c2ecf20Sopenharmony_ci dev->regmap_config.val_bits = 8, 5748c2ecf20Sopenharmony_ci dev->regmap_config.lock = ts2020_regmap_lock, 5758c2ecf20Sopenharmony_ci dev->regmap_config.unlock = ts2020_regmap_unlock, 5768c2ecf20Sopenharmony_ci dev->regmap_config.lock_arg = dev, 5778c2ecf20Sopenharmony_ci dev->regmap = regmap_init_i2c(client, &dev->regmap_config); 5788c2ecf20Sopenharmony_ci if (IS_ERR(dev->regmap)) { 5798c2ecf20Sopenharmony_ci ret = PTR_ERR(dev->regmap); 5808c2ecf20Sopenharmony_ci goto err_kfree; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci dev->i2c = client->adapter; 5848c2ecf20Sopenharmony_ci dev->i2c_address = client->addr; 5858c2ecf20Sopenharmony_ci dev->loop_through = pdata->loop_through; 5868c2ecf20Sopenharmony_ci dev->clk_out = pdata->clk_out; 5878c2ecf20Sopenharmony_ci dev->clk_out_div = pdata->clk_out_div; 5888c2ecf20Sopenharmony_ci dev->dont_poll = pdata->dont_poll; 5898c2ecf20Sopenharmony_ci dev->frequency_div = pdata->frequency_div; 5908c2ecf20Sopenharmony_ci dev->fe = fe; 5918c2ecf20Sopenharmony_ci dev->get_agc_pwm = pdata->get_agc_pwm; 5928c2ecf20Sopenharmony_ci fe->tuner_priv = dev; 5938c2ecf20Sopenharmony_ci dev->client = client; 5948c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&dev->stat_work, ts2020_stat_work); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* check if the tuner is there */ 5978c2ecf20Sopenharmony_ci ret = regmap_read(dev->regmap, 0x00, &utmp); 5988c2ecf20Sopenharmony_ci if (ret) 5998c2ecf20Sopenharmony_ci goto err_regmap_exit; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if ((utmp & 0x03) == 0x00) { 6028c2ecf20Sopenharmony_ci ret = regmap_write(dev->regmap, 0x00, 0x01); 6038c2ecf20Sopenharmony_ci if (ret) 6048c2ecf20Sopenharmony_ci goto err_regmap_exit; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci usleep_range(2000, 50000); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci ret = regmap_write(dev->regmap, 0x00, 0x03); 6108c2ecf20Sopenharmony_ci if (ret) 6118c2ecf20Sopenharmony_ci goto err_regmap_exit; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci usleep_range(2000, 50000); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci ret = regmap_read(dev->regmap, 0x00, &utmp); 6168c2ecf20Sopenharmony_ci if (ret) 6178c2ecf20Sopenharmony_ci goto err_regmap_exit; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "chip_id=%02x\n", utmp); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci switch (utmp) { 6228c2ecf20Sopenharmony_ci case 0x01: 6238c2ecf20Sopenharmony_ci case 0x41: 6248c2ecf20Sopenharmony_ci case 0x81: 6258c2ecf20Sopenharmony_ci dev->tuner = TS2020_M88TS2020; 6268c2ecf20Sopenharmony_ci chip_str = "TS2020"; 6278c2ecf20Sopenharmony_ci if (!dev->frequency_div) 6288c2ecf20Sopenharmony_ci dev->frequency_div = 1060000; 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci case 0xc3: 6318c2ecf20Sopenharmony_ci case 0x83: 6328c2ecf20Sopenharmony_ci dev->tuner = TS2020_M88TS2022; 6338c2ecf20Sopenharmony_ci chip_str = "TS2022"; 6348c2ecf20Sopenharmony_ci if (!dev->frequency_div) 6358c2ecf20Sopenharmony_ci dev->frequency_div = 1103000; 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci default: 6388c2ecf20Sopenharmony_ci ret = -ENODEV; 6398c2ecf20Sopenharmony_ci goto err_regmap_exit; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (dev->tuner == TS2020_M88TS2022) { 6438c2ecf20Sopenharmony_ci switch (dev->clk_out) { 6448c2ecf20Sopenharmony_ci case TS2020_CLK_OUT_DISABLED: 6458c2ecf20Sopenharmony_ci u8tmp = 0x60; 6468c2ecf20Sopenharmony_ci break; 6478c2ecf20Sopenharmony_ci case TS2020_CLK_OUT_ENABLED: 6488c2ecf20Sopenharmony_ci u8tmp = 0x70; 6498c2ecf20Sopenharmony_ci ret = regmap_write(dev->regmap, 0x05, dev->clk_out_div); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci goto err_regmap_exit; 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci case TS2020_CLK_OUT_ENABLED_XTALOUT: 6548c2ecf20Sopenharmony_ci u8tmp = 0x6c; 6558c2ecf20Sopenharmony_ci break; 6568c2ecf20Sopenharmony_ci default: 6578c2ecf20Sopenharmony_ci ret = -EINVAL; 6588c2ecf20Sopenharmony_ci goto err_regmap_exit; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ret = regmap_write(dev->regmap, 0x42, u8tmp); 6628c2ecf20Sopenharmony_ci if (ret) 6638c2ecf20Sopenharmony_ci goto err_regmap_exit; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (dev->loop_through) 6668c2ecf20Sopenharmony_ci u8tmp = 0xec; 6678c2ecf20Sopenharmony_ci else 6688c2ecf20Sopenharmony_ci u8tmp = 0x6c; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci ret = regmap_write(dev->regmap, 0x62, u8tmp); 6718c2ecf20Sopenharmony_ci if (ret) 6728c2ecf20Sopenharmony_ci goto err_regmap_exit; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* sleep */ 6768c2ecf20Sopenharmony_ci ret = regmap_write(dev->regmap, 0x00, 0x00); 6778c2ecf20Sopenharmony_ci if (ret) 6788c2ecf20Sopenharmony_ci goto err_regmap_exit; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dev_info(&client->dev, 6818c2ecf20Sopenharmony_ci "Montage Technology %s successfully identified\n", chip_str); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, 6848c2ecf20Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 6858c2ecf20Sopenharmony_ci if (!pdata->attach_in_use) 6868c2ecf20Sopenharmony_ci fe->ops.tuner_ops.release = NULL; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci i2c_set_clientdata(client, dev); 6898c2ecf20Sopenharmony_ci return 0; 6908c2ecf20Sopenharmony_cierr_regmap_exit: 6918c2ecf20Sopenharmony_ci regmap_exit(dev->regmap); 6928c2ecf20Sopenharmony_cierr_kfree: 6938c2ecf20Sopenharmony_ci kfree(dev); 6948c2ecf20Sopenharmony_cierr: 6958c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 6968c2ecf20Sopenharmony_ci return ret; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int ts2020_remove(struct i2c_client *client) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct ts2020_priv *dev = i2c_get_clientdata(client); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* stop statistics polling */ 7068c2ecf20Sopenharmony_ci if (!dev->dont_poll) 7078c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&dev->stat_work); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci regmap_exit(dev->regmap); 7108c2ecf20Sopenharmony_ci kfree(dev); 7118c2ecf20Sopenharmony_ci return 0; 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic const struct i2c_device_id ts2020_id_table[] = { 7158c2ecf20Sopenharmony_ci {"ts2020", 0}, 7168c2ecf20Sopenharmony_ci {"ts2022", 0}, 7178c2ecf20Sopenharmony_ci {} 7188c2ecf20Sopenharmony_ci}; 7198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ts2020_id_table); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic struct i2c_driver ts2020_driver = { 7228c2ecf20Sopenharmony_ci .driver = { 7238c2ecf20Sopenharmony_ci .name = "ts2020", 7248c2ecf20Sopenharmony_ci }, 7258c2ecf20Sopenharmony_ci .probe = ts2020_probe, 7268c2ecf20Sopenharmony_ci .remove = ts2020_remove, 7278c2ecf20Sopenharmony_ci .id_table = ts2020_id_table, 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cimodule_i2c_driver(ts2020_driver); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Konstantin Dimitrov <kosio.dimitrov@gmail.com>"); 7338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module"); 7348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 735