18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * stv6110.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for ST STV6110 satellite tuner IC. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 NetUP Inc. 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "stv6110.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Max transfer size done by I2C transfer functions */ 208c2ecf20Sopenharmony_ci#define MAX_XFER_SIZE 64 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic int debug; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct stv6110_priv { 258c2ecf20Sopenharmony_ci int i2c_address; 268c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci u32 mclk; 298c2ecf20Sopenharmony_ci u8 clk_div; 308c2ecf20Sopenharmony_ci u8 gain; 318c2ecf20Sopenharmony_ci u8 regs[8]; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define dprintk(args...) \ 358c2ecf20Sopenharmony_ci do { \ 368c2ecf20Sopenharmony_ci if (debug) \ 378c2ecf20Sopenharmony_ci printk(KERN_DEBUG args); \ 388c2ecf20Sopenharmony_ci } while (0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic s32 abssub(s32 a, s32 b) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci if (a > b) 438c2ecf20Sopenharmony_ci return a - b; 448c2ecf20Sopenharmony_ci else 458c2ecf20Sopenharmony_ci return b - a; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void stv6110_release(struct dvb_frontend *fe) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 518c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int stv6110_write_regs(struct dvb_frontend *fe, u8 buf[], 558c2ecf20Sopenharmony_ci int start, int len) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct stv6110_priv *priv = fe->tuner_priv; 588c2ecf20Sopenharmony_ci int rc; 598c2ecf20Sopenharmony_ci u8 cmdbuf[MAX_XFER_SIZE]; 608c2ecf20Sopenharmony_ci struct i2c_msg msg = { 618c2ecf20Sopenharmony_ci .addr = priv->i2c_address, 628c2ecf20Sopenharmony_ci .flags = 0, 638c2ecf20Sopenharmony_ci .buf = cmdbuf, 648c2ecf20Sopenharmony_ci .len = len + 1 658c2ecf20Sopenharmony_ci }; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci dprintk("%s\n", __func__); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (1 + len > sizeof(cmdbuf)) { 708c2ecf20Sopenharmony_ci printk(KERN_WARNING 718c2ecf20Sopenharmony_ci "%s: i2c wr: len=%d is too big!\n", 728c2ecf20Sopenharmony_ci KBUILD_MODNAME, len); 738c2ecf20Sopenharmony_ci return -EINVAL; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (start + len > 8) 778c2ecf20Sopenharmony_ci return -EINVAL; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci memcpy(&cmdbuf[1], buf, len); 808c2ecf20Sopenharmony_ci cmdbuf[0] = start; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 838c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci rc = i2c_transfer(priv->i2c, &msg, 1); 868c2ecf20Sopenharmony_ci if (rc != 1) 878c2ecf20Sopenharmony_ci dprintk("%s: i2c error\n", __func__); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 908c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[], 968c2ecf20Sopenharmony_ci int start, int len) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct stv6110_priv *priv = fe->tuner_priv; 998c2ecf20Sopenharmony_ci int rc; 1008c2ecf20Sopenharmony_ci u8 reg[] = { start }; 1018c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 1028c2ecf20Sopenharmony_ci { 1038c2ecf20Sopenharmony_ci .addr = priv->i2c_address, 1048c2ecf20Sopenharmony_ci .flags = 0, 1058c2ecf20Sopenharmony_ci .buf = reg, 1068c2ecf20Sopenharmony_ci .len = 1, 1078c2ecf20Sopenharmony_ci }, { 1088c2ecf20Sopenharmony_ci .addr = priv->i2c_address, 1098c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 1108c2ecf20Sopenharmony_ci .buf = regs, 1118c2ecf20Sopenharmony_ci .len = len, 1128c2ecf20Sopenharmony_ci }, 1138c2ecf20Sopenharmony_ci }; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1168c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci rc = i2c_transfer(priv->i2c, msg, 2); 1198c2ecf20Sopenharmony_ci if (rc != 2) 1208c2ecf20Sopenharmony_ci dprintk("%s: i2c error\n", __func__); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1238c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci memcpy(&priv->regs[start], regs, len); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int stv6110_read_reg(struct dvb_frontend *fe, int start) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci u8 buf[] = { 0 }; 1338c2ecf20Sopenharmony_ci stv6110_read_regs(fe, buf, start, 1); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return buf[0]; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int stv6110_sleep(struct dvb_frontend *fe) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci u8 reg[] = { 0 }; 1418c2ecf20Sopenharmony_ci stv6110_write_regs(fe, reg, 0, 1); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic u32 carrier_width(u32 symbol_rate, enum fe_rolloff rolloff) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci u32 rlf; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci switch (rolloff) { 1518c2ecf20Sopenharmony_ci case ROLLOFF_20: 1528c2ecf20Sopenharmony_ci rlf = 20; 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci case ROLLOFF_25: 1558c2ecf20Sopenharmony_ci rlf = 25; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci default: 1588c2ecf20Sopenharmony_ci rlf = 35; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return symbol_rate + ((symbol_rate * rlf) / 100); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int stv6110_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct stv6110_priv *priv = fe->tuner_priv; 1688c2ecf20Sopenharmony_ci u8 r8, ret = 0x04; 1698c2ecf20Sopenharmony_ci int i; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if ((bandwidth / 2) > 36000000) /*BW/2 max=31+5=36 mhz for r8=31*/ 1728c2ecf20Sopenharmony_ci r8 = 31; 1738c2ecf20Sopenharmony_ci else if ((bandwidth / 2) < 5000000) /* BW/2 min=5Mhz for F=0 */ 1748c2ecf20Sopenharmony_ci r8 = 0; 1758c2ecf20Sopenharmony_ci else /*if 5 < BW/2 < 36*/ 1768c2ecf20Sopenharmony_ci r8 = (bandwidth / 2) / 1000000 - 5; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* ctrl3, RCCLKOFF = 0 Activate the calibration Clock */ 1798c2ecf20Sopenharmony_ci /* ctrl3, CF = r8 Set the LPF value */ 1808c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL3] &= ~((1 << 6) | 0x1f); 1818c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL3] |= (r8 & 0x1f); 1828c2ecf20Sopenharmony_ci stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); 1838c2ecf20Sopenharmony_ci /* stat1, CALRCSTRT = 1 Start LPF auto calibration*/ 1848c2ecf20Sopenharmony_ci priv->regs[RSTV6110_STAT1] |= 0x02; 1858c2ecf20Sopenharmony_ci stv6110_write_regs(fe, &priv->regs[RSTV6110_STAT1], RSTV6110_STAT1, 1); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci i = 0; 1888c2ecf20Sopenharmony_ci /* Wait for CALRCSTRT == 0 */ 1898c2ecf20Sopenharmony_ci while ((i < 10) && (ret != 0)) { 1908c2ecf20Sopenharmony_ci ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x02); 1918c2ecf20Sopenharmony_ci mdelay(1); /* wait for LPF auto calibration */ 1928c2ecf20Sopenharmony_ci i++; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* RCCLKOFF = 1 calibration done, deactivate the calibration Clock */ 1968c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL3] |= (1 << 6); 1978c2ecf20Sopenharmony_ci stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int stv6110_init(struct dvb_frontend *fe) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct stv6110_priv *priv = fe->tuner_priv; 2048c2ecf20Sopenharmony_ci u8 buf0[] = { 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci memcpy(priv->regs, buf0, 8); 2078c2ecf20Sopenharmony_ci /* K = (Reference / 1000000) - 16 */ 2088c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); 2098c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL1] |= 2108c2ecf20Sopenharmony_ci ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* divisor value for the output clock */ 2138c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL2] &= ~0xc0; 2148c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL2] |= (priv->clk_div << 6); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8); 2178c2ecf20Sopenharmony_ci msleep(1); 2188c2ecf20Sopenharmony_ci stv6110_set_bandwidth(fe, 72000000); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic int stv6110_get_frequency(struct dvb_frontend *fe, u32 *frequency) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct stv6110_priv *priv = fe->tuner_priv; 2268c2ecf20Sopenharmony_ci u32 nbsteps, divider, psd2, freq; 2278c2ecf20Sopenharmony_ci u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci stv6110_read_regs(fe, regs, 0, 8); 2308c2ecf20Sopenharmony_ci /*N*/ 2318c2ecf20Sopenharmony_ci divider = (priv->regs[RSTV6110_TUNING2] & 0x0f) << 8; 2328c2ecf20Sopenharmony_ci divider += priv->regs[RSTV6110_TUNING1]; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /*R*/ 2358c2ecf20Sopenharmony_ci nbsteps = (priv->regs[RSTV6110_TUNING2] >> 6) & 3; 2368c2ecf20Sopenharmony_ci /*p*/ 2378c2ecf20Sopenharmony_ci psd2 = (priv->regs[RSTV6110_TUNING2] >> 4) & 1; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci freq = divider * (priv->mclk / 1000); 2408c2ecf20Sopenharmony_ci freq /= (1 << (nbsteps + psd2)); 2418c2ecf20Sopenharmony_ci freq /= 4; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci *frequency = freq; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct stv6110_priv *priv = fe->tuner_priv; 2518c2ecf20Sopenharmony_ci u8 ret = 0x04; 2528c2ecf20Sopenharmony_ci u32 divider, ref, p, presc, i, result_freq, vco_freq; 2538c2ecf20Sopenharmony_ci s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__, 2568c2ecf20Sopenharmony_ci frequency, priv->mclk); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* K = (Reference / 1000000) - 16 */ 2598c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); 2608c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL1] |= 2618c2ecf20Sopenharmony_ci ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* BB_GAIN = db/2 */ 2648c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL2] &= ~0x0f; 2658c2ecf20Sopenharmony_ci priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (frequency <= 1023000) { 2688c2ecf20Sopenharmony_ci p = 1; 2698c2ecf20Sopenharmony_ci presc = 0; 2708c2ecf20Sopenharmony_ci } else if (frequency <= 1300000) { 2718c2ecf20Sopenharmony_ci p = 1; 2728c2ecf20Sopenharmony_ci presc = 1; 2738c2ecf20Sopenharmony_ci } else if (frequency <= 2046000) { 2748c2ecf20Sopenharmony_ci p = 0; 2758c2ecf20Sopenharmony_ci presc = 0; 2768c2ecf20Sopenharmony_ci } else { 2778c2ecf20Sopenharmony_ci p = 0; 2788c2ecf20Sopenharmony_ci presc = 1; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci /* DIV4SEL = p*/ 2818c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] &= ~(1 << 4); 2828c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] |= (p << 4); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* PRESC32ON = presc */ 2858c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] &= ~(1 << 5); 2868c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] |= (presc << 5); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci p_val = (int)(1 << (p + 1)) * 10;/* P = 2 or P = 4 */ 2898c2ecf20Sopenharmony_ci for (r_div = 0; r_div <= 3; r_div++) { 2908c2ecf20Sopenharmony_ci p_calc = (priv->mclk / 100000); 2918c2ecf20Sopenharmony_ci p_calc /= (1 << (r_div + 1)); 2928c2ecf20Sopenharmony_ci if ((abssub(p_calc, p_val)) < (abssub(p_calc_opt, p_val))) 2938c2ecf20Sopenharmony_ci r_div_opt = r_div; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci p_calc_opt = (priv->mclk / 100000); 2968c2ecf20Sopenharmony_ci p_calc_opt /= (1 << (r_div_opt + 1)); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci ref = priv->mclk / ((1 << (r_div_opt + 1)) * (1 << (p + 1))); 3008c2ecf20Sopenharmony_ci divider = (((frequency * 1000) + (ref >> 1)) / ref); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* RDIV = r_div_opt */ 3038c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] &= ~(3 << 6); 3048c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] |= (((r_div_opt) & 3) << 6); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* NDIV_MSB = MSB(divider) */ 3078c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] &= ~0x0f; 3088c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING2] |= (((divider) >> 8) & 0x0f); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* NDIV_LSB, LSB(divider) */ 3118c2ecf20Sopenharmony_ci priv->regs[RSTV6110_TUNING1] = (divider & 0xff); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* CALVCOSTRT = 1 VCO Auto Calibration */ 3148c2ecf20Sopenharmony_ci priv->regs[RSTV6110_STAT1] |= 0x04; 3158c2ecf20Sopenharmony_ci stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], 3168c2ecf20Sopenharmony_ci RSTV6110_CTRL1, 8); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci i = 0; 3198c2ecf20Sopenharmony_ci /* Wait for CALVCOSTRT == 0 */ 3208c2ecf20Sopenharmony_ci while ((i < 10) && (ret != 0)) { 3218c2ecf20Sopenharmony_ci ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x04); 3228c2ecf20Sopenharmony_ci msleep(1); /* wait for VCO auto calibration */ 3238c2ecf20Sopenharmony_ci i++; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ret = stv6110_read_reg(fe, RSTV6110_STAT1); 3278c2ecf20Sopenharmony_ci stv6110_get_frequency(fe, &result_freq); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci vco_freq = divider * ((priv->mclk / 1000) / ((1 << (r_div_opt + 1)))); 3308c2ecf20Sopenharmony_ci dprintk("%s, stat1=%x, lo_freq=%d kHz, vco_frec=%d kHz\n", __func__, 3318c2ecf20Sopenharmony_ci ret, result_freq, vco_freq); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return 0; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic int stv6110_set_params(struct dvb_frontend *fe) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 3398c2ecf20Sopenharmony_ci u32 bandwidth = carrier_width(c->symbol_rate, c->rolloff); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci stv6110_set_frequency(fe, c->frequency); 3428c2ecf20Sopenharmony_ci stv6110_set_bandwidth(fe, bandwidth); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct stv6110_priv *priv = fe->tuner_priv; 3508c2ecf20Sopenharmony_ci u8 r8 = 0; 3518c2ecf20Sopenharmony_ci u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 3528c2ecf20Sopenharmony_ci stv6110_read_regs(fe, regs, 0, 8); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* CF */ 3558c2ecf20Sopenharmony_ci r8 = priv->regs[RSTV6110_CTRL3] & 0x1f; 3568c2ecf20Sopenharmony_ci *bandwidth = (r8 + 5) * 2000000;/* x2 for ZIF tuner BW/2 = F+5 Mhz */ 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops stv6110_tuner_ops = { 3628c2ecf20Sopenharmony_ci .info = { 3638c2ecf20Sopenharmony_ci .name = "ST STV6110", 3648c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 3658c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 3668c2ecf20Sopenharmony_ci .frequency_step_hz = 1 * MHz, 3678c2ecf20Sopenharmony_ci }, 3688c2ecf20Sopenharmony_ci .init = stv6110_init, 3698c2ecf20Sopenharmony_ci .release = stv6110_release, 3708c2ecf20Sopenharmony_ci .sleep = stv6110_sleep, 3718c2ecf20Sopenharmony_ci .set_params = stv6110_set_params, 3728c2ecf20Sopenharmony_ci .get_frequency = stv6110_get_frequency, 3738c2ecf20Sopenharmony_ci .set_frequency = stv6110_set_frequency, 3748c2ecf20Sopenharmony_ci .get_bandwidth = stv6110_get_bandwidth, 3758c2ecf20Sopenharmony_ci .set_bandwidth = stv6110_set_bandwidth, 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci}; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistruct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, 3808c2ecf20Sopenharmony_ci const struct stv6110_config *config, 3818c2ecf20Sopenharmony_ci struct i2c_adapter *i2c) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct stv6110_priv *priv = NULL; 3848c2ecf20Sopenharmony_ci u8 reg0[] = { 0x00, 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 3878c2ecf20Sopenharmony_ci { 3888c2ecf20Sopenharmony_ci .addr = config->i2c_address, 3898c2ecf20Sopenharmony_ci .flags = 0, 3908c2ecf20Sopenharmony_ci .buf = reg0, 3918c2ecf20Sopenharmony_ci .len = 9 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci }; 3948c2ecf20Sopenharmony_ci int ret; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* divisor value for the output clock */ 3978c2ecf20Sopenharmony_ci reg0[2] &= ~0xc0; 3988c2ecf20Sopenharmony_ci reg0[2] |= (config->clk_div << 6); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4018c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ret = i2c_transfer(i2c, msg, 1); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4068c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (ret != 1) 4098c2ecf20Sopenharmony_ci return NULL; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct stv6110_priv), GFP_KERNEL); 4128c2ecf20Sopenharmony_ci if (priv == NULL) 4138c2ecf20Sopenharmony_ci return NULL; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci priv->i2c_address = config->i2c_address; 4168c2ecf20Sopenharmony_ci priv->i2c = i2c; 4178c2ecf20Sopenharmony_ci priv->mclk = config->mclk; 4188c2ecf20Sopenharmony_ci priv->clk_div = config->clk_div; 4198c2ecf20Sopenharmony_ci priv->gain = config->gain; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci memcpy(&priv->regs, ®0[1], 8); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &stv6110_tuner_ops, 4248c2ecf20Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 4258c2ecf20Sopenharmony_ci fe->tuner_priv = priv; 4268c2ecf20Sopenharmony_ci printk(KERN_INFO "STV6110 attached on addr=%x!\n", priv->i2c_address); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return fe; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(stv6110_attach); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 4338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ST STV6110 driver"); 4368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Igor M. Liplianin"); 4378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 438