18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "mt2060.h" 198c2ecf20Sopenharmony_ci#include "mt2060_priv.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic int debug; 228c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci// Reads a single register 288c2ecf20Sopenharmony_cistatic int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct i2c_msg msg[2] = { 318c2ecf20Sopenharmony_ci { .addr = priv->cfg->i2c_address, .flags = 0, .len = 1 }, 328c2ecf20Sopenharmony_ci { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .len = 1 }, 338c2ecf20Sopenharmony_ci }; 348c2ecf20Sopenharmony_ci int rc = 0; 358c2ecf20Sopenharmony_ci u8 *b; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci b = kmalloc(2, GFP_KERNEL); 388c2ecf20Sopenharmony_ci if (!b) 398c2ecf20Sopenharmony_ci return -ENOMEM; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci b[0] = reg; 428c2ecf20Sopenharmony_ci b[1] = 0; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci msg[0].buf = b; 458c2ecf20Sopenharmony_ci msg[1].buf = b + 1; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, msg, 2) != 2) { 488c2ecf20Sopenharmony_ci printk(KERN_WARNING "mt2060 I2C read failed\n"); 498c2ecf20Sopenharmony_ci rc = -EREMOTEIO; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci *val = b[1]; 528c2ecf20Sopenharmony_ci kfree(b); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return rc; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci// Writes a single register 588c2ecf20Sopenharmony_cistatic int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct i2c_msg msg = { 618c2ecf20Sopenharmony_ci .addr = priv->cfg->i2c_address, .flags = 0, .len = 2 628c2ecf20Sopenharmony_ci }; 638c2ecf20Sopenharmony_ci u8 *buf; 648c2ecf20Sopenharmony_ci int rc = 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci buf = kmalloc(2, GFP_KERNEL); 678c2ecf20Sopenharmony_ci if (!buf) 688c2ecf20Sopenharmony_ci return -ENOMEM; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci buf[0] = reg; 718c2ecf20Sopenharmony_ci buf[1] = val; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci msg.buf = buf; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 768c2ecf20Sopenharmony_ci printk(KERN_WARNING "mt2060 I2C write failed\n"); 778c2ecf20Sopenharmony_ci rc = -EREMOTEIO; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci kfree(buf); 808c2ecf20Sopenharmony_ci return rc; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci// Writes a set of consecutive registers 848c2ecf20Sopenharmony_cistatic int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int rem, val_len; 878c2ecf20Sopenharmony_ci u8 *xfer_buf; 888c2ecf20Sopenharmony_ci int rc = 0; 898c2ecf20Sopenharmony_ci struct i2c_msg msg = { 908c2ecf20Sopenharmony_ci .addr = priv->cfg->i2c_address, .flags = 0 918c2ecf20Sopenharmony_ci }; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci xfer_buf = kmalloc(16, GFP_KERNEL); 948c2ecf20Sopenharmony_ci if (!xfer_buf) 958c2ecf20Sopenharmony_ci return -ENOMEM; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci msg.buf = xfer_buf; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci for (rem = len - 1; rem > 0; rem -= priv->i2c_max_regs) { 1008c2ecf20Sopenharmony_ci val_len = min_t(int, rem, priv->i2c_max_regs); 1018c2ecf20Sopenharmony_ci msg.len = 1 + val_len; 1028c2ecf20Sopenharmony_ci xfer_buf[0] = buf[0] + len - 1 - rem; 1038c2ecf20Sopenharmony_ci memcpy(&xfer_buf[1], &buf[1 + len - 1 - rem], val_len); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 1068c2ecf20Sopenharmony_ci printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n", val_len); 1078c2ecf20Sopenharmony_ci rc = -EREMOTEIO; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci kfree(xfer_buf); 1138c2ecf20Sopenharmony_ci return rc; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci// Initialisation sequences 1178c2ecf20Sopenharmony_ci// LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 1188c2ecf20Sopenharmony_cistatic u8 mt2060_config1[] = { 1198c2ecf20Sopenharmony_ci REG_LO1C1, 1208c2ecf20Sopenharmony_ci 0x3F, 0x74, 0x00, 0x08, 0x93 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci// FMCG=2, GP2=0, GP1=0 1248c2ecf20Sopenharmony_cistatic u8 mt2060_config2[] = { 1258c2ecf20Sopenharmony_ci REG_MISC_CTRL, 1268c2ecf20Sopenharmony_ci 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci// VGAG=3, V1CSE=1 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#ifdef MT2060_SPURCHECK 1328c2ecf20Sopenharmony_ci/* The function below calculates the frequency offset between the output frequency if2 1338c2ecf20Sopenharmony_ci and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ 1348c2ecf20Sopenharmony_cistatic int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int I,J; 1378c2ecf20Sopenharmony_ci int dia,diamin,diff; 1388c2ecf20Sopenharmony_ci diamin=1000000; 1398c2ecf20Sopenharmony_ci for (I = 1; I < 10; I++) { 1408c2ecf20Sopenharmony_ci J = ((2*I*lo1)/lo2+1)/2; 1418c2ecf20Sopenharmony_ci diff = I*(int)lo1-J*(int)lo2; 1428c2ecf20Sopenharmony_ci if (diff < 0) diff=-diff; 1438c2ecf20Sopenharmony_ci dia = (diff-(int)if2); 1448c2ecf20Sopenharmony_ci if (dia < 0) dia=-dia; 1458c2ecf20Sopenharmony_ci if (diamin > dia) diamin=dia; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci return diamin; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#define BANDWIDTH 4000 // kHz 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ 1538c2ecf20Sopenharmony_cistatic int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci u32 Spur,Sp1,Sp2; 1568c2ecf20Sopenharmony_ci int I,J; 1578c2ecf20Sopenharmony_ci I=0; 1588c2ecf20Sopenharmony_ci J=1000; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci Spur=mt2060_spurcalc(lo1,lo2,if2); 1618c2ecf20Sopenharmony_ci if (Spur < BANDWIDTH) { 1628c2ecf20Sopenharmony_ci /* Potential spurs detected */ 1638c2ecf20Sopenharmony_ci dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", 1648c2ecf20Sopenharmony_ci (int)lo1,(int)lo2); 1658c2ecf20Sopenharmony_ci I=1000; 1668c2ecf20Sopenharmony_ci Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); 1678c2ecf20Sopenharmony_ci Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (Sp1 < Sp2) { 1708c2ecf20Sopenharmony_ci J=-J; I=-I; Spur=Sp2; 1718c2ecf20Sopenharmony_ci } else 1728c2ecf20Sopenharmony_ci Spur=Sp1; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci while (Spur < BANDWIDTH) { 1758c2ecf20Sopenharmony_ci I += J; 1768c2ecf20Sopenharmony_ci Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", 1798c2ecf20Sopenharmony_ci (int)(lo1+I),(int)(lo2+I)); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci return I; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci#endif 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#define IF2 36150 // IF2 frequency = 36.150 MHz 1868c2ecf20Sopenharmony_ci#define FREF 16000 // Quartz oscillator 16 MHz 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int mt2060_set_params(struct dvb_frontend *fe) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1918c2ecf20Sopenharmony_ci struct mt2060_priv *priv; 1928c2ecf20Sopenharmony_ci int i=0; 1938c2ecf20Sopenharmony_ci u32 freq; 1948c2ecf20Sopenharmony_ci u8 lnaband; 1958c2ecf20Sopenharmony_ci u32 f_lo1,f_lo2; 1968c2ecf20Sopenharmony_ci u32 div1,num1,div2,num2; 1978c2ecf20Sopenharmony_ci u8 b[8]; 1988c2ecf20Sopenharmony_ci u32 if1; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci priv = fe->tuner_priv; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if1 = priv->if1_freq; 2038c2ecf20Sopenharmony_ci b[0] = REG_LO1B1; 2048c2ecf20Sopenharmony_ci b[1] = 0xFF; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2078c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci mt2060_writeregs(priv,b,2); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci freq = c->frequency / 1000; /* Hz -> kHz */ 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci f_lo1 = freq + if1 * 1000; 2148c2ecf20Sopenharmony_ci f_lo1 = (f_lo1 / 250) * 250; 2158c2ecf20Sopenharmony_ci f_lo2 = f_lo1 - freq - IF2; 2168c2ecf20Sopenharmony_ci // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise 2178c2ecf20Sopenharmony_ci f_lo2 = ((f_lo2 + 25) / 50) * 50; 2188c2ecf20Sopenharmony_ci priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#ifdef MT2060_SPURCHECK 2218c2ecf20Sopenharmony_ci // LO-related spurs detection and correction 2228c2ecf20Sopenharmony_ci num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); 2238c2ecf20Sopenharmony_ci f_lo1 += num1; 2248c2ecf20Sopenharmony_ci f_lo2 += num1; 2258c2ecf20Sopenharmony_ci#endif 2268c2ecf20Sopenharmony_ci //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) 2278c2ecf20Sopenharmony_ci num1 = f_lo1 / (FREF / 64); 2288c2ecf20Sopenharmony_ci div1 = num1 / 64; 2298c2ecf20Sopenharmony_ci num1 &= 0x3f; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) 2328c2ecf20Sopenharmony_ci num2 = f_lo2 * 64 / (FREF / 128); 2338c2ecf20Sopenharmony_ci div2 = num2 / 8192; 2348c2ecf20Sopenharmony_ci num2 &= 0x1fff; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (freq <= 95000) lnaband = 0xB0; else 2378c2ecf20Sopenharmony_ci if (freq <= 180000) lnaband = 0xA0; else 2388c2ecf20Sopenharmony_ci if (freq <= 260000) lnaband = 0x90; else 2398c2ecf20Sopenharmony_ci if (freq <= 335000) lnaband = 0x80; else 2408c2ecf20Sopenharmony_ci if (freq <= 425000) lnaband = 0x70; else 2418c2ecf20Sopenharmony_ci if (freq <= 480000) lnaband = 0x60; else 2428c2ecf20Sopenharmony_ci if (freq <= 570000) lnaband = 0x50; else 2438c2ecf20Sopenharmony_ci if (freq <= 645000) lnaband = 0x40; else 2448c2ecf20Sopenharmony_ci if (freq <= 730000) lnaband = 0x30; else 2458c2ecf20Sopenharmony_ci if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci b[0] = REG_LO1C1; 2488c2ecf20Sopenharmony_ci b[1] = lnaband | ((num1 >>2) & 0x0F); 2498c2ecf20Sopenharmony_ci b[2] = div1; 2508c2ecf20Sopenharmony_ci b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); 2518c2ecf20Sopenharmony_ci b[4] = num2 >> 4; 2528c2ecf20Sopenharmony_ci b[5] = ((num2 >>12) & 1) | (div2 << 1); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci dprintk("IF1: %dMHz",(int)if1); 2558c2ecf20Sopenharmony_ci dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); 2568c2ecf20Sopenharmony_ci dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); 2578c2ecf20Sopenharmony_ci dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci mt2060_writeregs(priv,b,6); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci //Waits for pll lock or timeout 2628c2ecf20Sopenharmony_ci i = 0; 2638c2ecf20Sopenharmony_ci do { 2648c2ecf20Sopenharmony_ci mt2060_readreg(priv,REG_LO_STATUS,b); 2658c2ecf20Sopenharmony_ci if ((b[0] & 0x88)==0x88) 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci msleep(4); 2688c2ecf20Sopenharmony_ci i++; 2698c2ecf20Sopenharmony_ci } while (i<10); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2728c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void mt2060_calibrate(struct mt2060_priv *priv) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci u8 b = 0; 2808c2ecf20Sopenharmony_ci int i = 0; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) 2838c2ecf20Sopenharmony_ci return; 2848c2ecf20Sopenharmony_ci if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* initialize the clock output */ 2888c2ecf20Sopenharmony_ci mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci do { 2918c2ecf20Sopenharmony_ci b |= (1 << 6); // FM1SS; 2928c2ecf20Sopenharmony_ci mt2060_writereg(priv, REG_LO2C1,b); 2938c2ecf20Sopenharmony_ci msleep(20); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (i == 0) { 2968c2ecf20Sopenharmony_ci b |= (1 << 7); // FM1CA; 2978c2ecf20Sopenharmony_ci mt2060_writereg(priv, REG_LO2C1,b); 2988c2ecf20Sopenharmony_ci b &= ~(1 << 7); // FM1CA; 2998c2ecf20Sopenharmony_ci msleep(20); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci b &= ~(1 << 6); // FM1SS 3038c2ecf20Sopenharmony_ci mt2060_writereg(priv, REG_LO2C1,b); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci msleep(20); 3068c2ecf20Sopenharmony_ci i++; 3078c2ecf20Sopenharmony_ci } while (i < 9); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci i = 0; 3108c2ecf20Sopenharmony_ci while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) 3118c2ecf20Sopenharmony_ci msleep(20); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (i <= 10) { 3148c2ecf20Sopenharmony_ci mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) 3158c2ecf20Sopenharmony_ci dprintk("calibration was successful: %d", (int)priv->fmfreq); 3168c2ecf20Sopenharmony_ci } else 3178c2ecf20Sopenharmony_ci dprintk("FMCAL timed out"); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct mt2060_priv *priv = fe->tuner_priv; 3238c2ecf20Sopenharmony_ci *frequency = priv->frequency; 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci *frequency = IF2 * 1000; 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int mt2060_init(struct dvb_frontend *fe) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct mt2060_priv *priv = fe->tuner_priv; 3368c2ecf20Sopenharmony_ci int ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3398c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (priv->sleep) { 3428c2ecf20Sopenharmony_ci ret = mt2060_writereg(priv, REG_MISC_CTRL, 0x20); 3438c2ecf20Sopenharmony_ci if (ret) 3448c2ecf20Sopenharmony_ci goto err_i2c_gate_ctrl; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci ret = mt2060_writereg(priv, REG_VGAG, 3488c2ecf20Sopenharmony_ci (priv->cfg->clock_out << 6) | 0x33); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cierr_i2c_gate_ctrl: 3518c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3528c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int mt2060_sleep(struct dvb_frontend *fe) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct mt2060_priv *priv = fe->tuner_priv; 3608c2ecf20Sopenharmony_ci int ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3638c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = mt2060_writereg(priv, REG_VGAG, 3668c2ecf20Sopenharmony_ci (priv->cfg->clock_out << 6) | 0x30); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci goto err_i2c_gate_ctrl; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (priv->sleep) 3718c2ecf20Sopenharmony_ci ret = mt2060_writereg(priv, REG_MISC_CTRL, 0xe8); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cierr_i2c_gate_ctrl: 3748c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3758c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void mt2060_release(struct dvb_frontend *fe) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 3838c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops mt2060_tuner_ops = { 3878c2ecf20Sopenharmony_ci .info = { 3888c2ecf20Sopenharmony_ci .name = "Microtune MT2060", 3898c2ecf20Sopenharmony_ci .frequency_min_hz = 48 * MHz, 3908c2ecf20Sopenharmony_ci .frequency_max_hz = 860 * MHz, 3918c2ecf20Sopenharmony_ci .frequency_step_hz = 50 * kHz, 3928c2ecf20Sopenharmony_ci }, 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci .release = mt2060_release, 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci .init = mt2060_init, 3978c2ecf20Sopenharmony_ci .sleep = mt2060_sleep, 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci .set_params = mt2060_set_params, 4008c2ecf20Sopenharmony_ci .get_frequency = mt2060_get_frequency, 4018c2ecf20Sopenharmony_ci .get_if_frequency = mt2060_get_if_frequency, 4028c2ecf20Sopenharmony_ci}; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ 4058c2ecf20Sopenharmony_cistruct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct mt2060_priv *priv = NULL; 4088c2ecf20Sopenharmony_ci u8 id = 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); 4118c2ecf20Sopenharmony_ci if (priv == NULL) 4128c2ecf20Sopenharmony_ci return NULL; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci priv->cfg = cfg; 4158c2ecf20Sopenharmony_ci priv->i2c = i2c; 4168c2ecf20Sopenharmony_ci priv->if1_freq = if1; 4178c2ecf20Sopenharmony_ci priv->i2c_max_regs = ~0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4208c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { 4238c2ecf20Sopenharmony_ci kfree(priv); 4248c2ecf20Sopenharmony_ci return NULL; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (id != PART_REV) { 4288c2ecf20Sopenharmony_ci kfree(priv); 4298c2ecf20Sopenharmony_ci return NULL; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); 4328c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci fe->tuner_priv = priv; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci mt2060_calibrate(priv); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4398c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return fe; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt2060_attach); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int mt2060_probe(struct i2c_client *client, 4468c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct mt2060_platform_data *pdata = client->dev.platform_data; 4498c2ecf20Sopenharmony_ci struct dvb_frontend *fe; 4508c2ecf20Sopenharmony_ci struct mt2060_priv *dev; 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci u8 chip_id; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (!pdata) { 4578c2ecf20Sopenharmony_ci dev_err(&client->dev, "Cannot proceed without platform data\n"); 4588c2ecf20Sopenharmony_ci ret = -EINVAL; 4598c2ecf20Sopenharmony_ci goto err; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); 4638c2ecf20Sopenharmony_ci if (!dev) { 4648c2ecf20Sopenharmony_ci ret = -ENOMEM; 4658c2ecf20Sopenharmony_ci goto err; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci fe = pdata->dvb_frontend; 4698c2ecf20Sopenharmony_ci dev->config.i2c_address = client->addr; 4708c2ecf20Sopenharmony_ci dev->config.clock_out = pdata->clock_out; 4718c2ecf20Sopenharmony_ci dev->cfg = &dev->config; 4728c2ecf20Sopenharmony_ci dev->i2c = client->adapter; 4738c2ecf20Sopenharmony_ci dev->if1_freq = pdata->if1 ? pdata->if1 : 1220; 4748c2ecf20Sopenharmony_ci dev->client = client; 4758c2ecf20Sopenharmony_ci dev->i2c_max_regs = pdata->i2c_write_max ? pdata->i2c_write_max - 1 : ~0; 4768c2ecf20Sopenharmony_ci dev->sleep = true; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci ret = mt2060_readreg(dev, REG_PART_REV, &chip_id); 4798c2ecf20Sopenharmony_ci if (ret) { 4808c2ecf20Sopenharmony_ci ret = -ENODEV; 4818c2ecf20Sopenharmony_ci goto err; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "chip id=%02x\n", chip_id); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (chip_id != PART_REV) { 4878c2ecf20Sopenharmony_ci ret = -ENODEV; 4888c2ecf20Sopenharmony_ci goto err; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Power on, calibrate, sleep */ 4928c2ecf20Sopenharmony_ci ret = mt2060_writereg(dev, REG_MISC_CTRL, 0x20); 4938c2ecf20Sopenharmony_ci if (ret) 4948c2ecf20Sopenharmony_ci goto err; 4958c2ecf20Sopenharmony_ci mt2060_calibrate(dev); 4968c2ecf20Sopenharmony_ci ret = mt2060_writereg(dev, REG_MISC_CTRL, 0xe8); 4978c2ecf20Sopenharmony_ci if (ret) 4988c2ecf20Sopenharmony_ci goto err; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci dev_info(&client->dev, "Microtune MT2060 successfully identified\n"); 5018c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops)); 5028c2ecf20Sopenharmony_ci fe->ops.tuner_ops.release = NULL; 5038c2ecf20Sopenharmony_ci fe->tuner_priv = dev; 5048c2ecf20Sopenharmony_ci i2c_set_clientdata(client, dev); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_cierr: 5088c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "failed=%d\n", ret); 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int mt2060_remove(struct i2c_client *client) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "\n"); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct i2c_device_id mt2060_id_table[] = { 5208c2ecf20Sopenharmony_ci {"mt2060", 0}, 5218c2ecf20Sopenharmony_ci {} 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mt2060_id_table); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic struct i2c_driver mt2060_driver = { 5268c2ecf20Sopenharmony_ci .driver = { 5278c2ecf20Sopenharmony_ci .name = "mt2060", 5288c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 5298c2ecf20Sopenharmony_ci }, 5308c2ecf20Sopenharmony_ci .probe = mt2060_probe, 5318c2ecf20Sopenharmony_ci .remove = mt2060_remove, 5328c2ecf20Sopenharmony_ci .id_table = mt2060_id_table, 5338c2ecf20Sopenharmony_ci}; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cimodule_i2c_driver(mt2060_driver); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olivier DANET"); 5388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); 5398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 540