1ccae7af2SMauro Carvalho Chehab /* 2ccae7af2SMauro Carvalho Chehab * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" 3ccae7af2SMauro Carvalho Chehab * 4ccae7af2SMauro Carvalho Chehab * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> 5ccae7af2SMauro Carvalho Chehab * 6ccae7af2SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 7ccae7af2SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 8ccae7af2SMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 9ccae7af2SMauro Carvalho Chehab * (at your option) any later version. 10ccae7af2SMauro Carvalho Chehab * 11ccae7af2SMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 12ccae7af2SMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 13ccae7af2SMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14ccae7af2SMauro Carvalho Chehab * 15ccae7af2SMauro Carvalho Chehab * GNU General Public License for more details. 16ccae7af2SMauro Carvalho Chehab * 17ccae7af2SMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 18ccae7af2SMauro Carvalho Chehab * along with this program; if not, write to the Free Software 19ccae7af2SMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= 20ccae7af2SMauro Carvalho Chehab */ 21ccae7af2SMauro Carvalho Chehab 22ccae7af2SMauro Carvalho Chehab /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ 23ccae7af2SMauro Carvalho Chehab 24ccae7af2SMauro Carvalho Chehab #include <linux/module.h> 25ccae7af2SMauro Carvalho Chehab #include <linux/delay.h> 26ccae7af2SMauro Carvalho Chehab #include <linux/dvb/frontend.h> 27ccae7af2SMauro Carvalho Chehab #include <linux/i2c.h> 28ccae7af2SMauro Carvalho Chehab #include <linux/slab.h> 29ccae7af2SMauro Carvalho Chehab 30ccae7af2SMauro Carvalho Chehab #include "dvb_frontend.h" 31ccae7af2SMauro Carvalho Chehab 32ccae7af2SMauro Carvalho Chehab #include "mt2060.h" 33ccae7af2SMauro Carvalho Chehab #include "mt2060_priv.h" 34ccae7af2SMauro Carvalho Chehab 35ccae7af2SMauro Carvalho Chehab static int debug; 36ccae7af2SMauro Carvalho Chehab module_param(debug, int, 0644); 37ccae7af2SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); 38ccae7af2SMauro Carvalho Chehab 39ccae7af2SMauro Carvalho Chehab #define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) 40ccae7af2SMauro Carvalho Chehab 41ccae7af2SMauro Carvalho Chehab // Reads a single register 42ccae7af2SMauro Carvalho Chehab static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) 43ccae7af2SMauro Carvalho Chehab { 44ccae7af2SMauro Carvalho Chehab struct i2c_msg msg[2] = { 45ccae7af2SMauro Carvalho Chehab { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, 46ccae7af2SMauro Carvalho Chehab { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, 47ccae7af2SMauro Carvalho Chehab }; 48ccae7af2SMauro Carvalho Chehab 49ccae7af2SMauro Carvalho Chehab if (i2c_transfer(priv->i2c, msg, 2) != 2) { 50ccae7af2SMauro Carvalho Chehab printk(KERN_WARNING "mt2060 I2C read failed\n"); 51ccae7af2SMauro Carvalho Chehab return -EREMOTEIO; 52ccae7af2SMauro Carvalho Chehab } 53ccae7af2SMauro Carvalho Chehab return 0; 54ccae7af2SMauro Carvalho Chehab } 55ccae7af2SMauro Carvalho Chehab 56ccae7af2SMauro Carvalho Chehab // Writes a single register 57ccae7af2SMauro Carvalho Chehab static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) 58ccae7af2SMauro Carvalho Chehab { 59ccae7af2SMauro Carvalho Chehab u8 buf[2] = { reg, val }; 60ccae7af2SMauro Carvalho Chehab struct i2c_msg msg = { 61ccae7af2SMauro Carvalho Chehab .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 62ccae7af2SMauro Carvalho Chehab }; 63ccae7af2SMauro Carvalho Chehab 64ccae7af2SMauro Carvalho Chehab if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 65ccae7af2SMauro Carvalho Chehab printk(KERN_WARNING "mt2060 I2C write failed\n"); 66ccae7af2SMauro Carvalho Chehab return -EREMOTEIO; 67ccae7af2SMauro Carvalho Chehab } 68ccae7af2SMauro Carvalho Chehab return 0; 69ccae7af2SMauro Carvalho Chehab } 70ccae7af2SMauro Carvalho Chehab 71ccae7af2SMauro Carvalho Chehab // Writes a set of consecutive registers 72ccae7af2SMauro Carvalho Chehab static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) 73ccae7af2SMauro Carvalho Chehab { 74ccae7af2SMauro Carvalho Chehab struct i2c_msg msg = { 75ccae7af2SMauro Carvalho Chehab .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len 76ccae7af2SMauro Carvalho Chehab }; 77ccae7af2SMauro Carvalho Chehab if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 78ccae7af2SMauro Carvalho Chehab printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); 79ccae7af2SMauro Carvalho Chehab return -EREMOTEIO; 80ccae7af2SMauro Carvalho Chehab } 81ccae7af2SMauro Carvalho Chehab return 0; 82ccae7af2SMauro Carvalho Chehab } 83ccae7af2SMauro Carvalho Chehab 84ccae7af2SMauro Carvalho Chehab // Initialisation sequences 85ccae7af2SMauro Carvalho Chehab // LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 86ccae7af2SMauro Carvalho Chehab static u8 mt2060_config1[] = { 87ccae7af2SMauro Carvalho Chehab REG_LO1C1, 88ccae7af2SMauro Carvalho Chehab 0x3F, 0x74, 0x00, 0x08, 0x93 89ccae7af2SMauro Carvalho Chehab }; 90ccae7af2SMauro Carvalho Chehab 91ccae7af2SMauro Carvalho Chehab // FMCG=2, GP2=0, GP1=0 92ccae7af2SMauro Carvalho Chehab static u8 mt2060_config2[] = { 93ccae7af2SMauro Carvalho Chehab REG_MISC_CTRL, 94ccae7af2SMauro Carvalho Chehab 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 95ccae7af2SMauro Carvalho Chehab }; 96ccae7af2SMauro Carvalho Chehab 97ccae7af2SMauro Carvalho Chehab // VGAG=3, V1CSE=1 98ccae7af2SMauro Carvalho Chehab 99ccae7af2SMauro Carvalho Chehab #ifdef MT2060_SPURCHECK 100ccae7af2SMauro Carvalho Chehab /* The function below calculates the frequency offset between the output frequency if2 101ccae7af2SMauro Carvalho Chehab and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ 102ccae7af2SMauro Carvalho Chehab static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) 103ccae7af2SMauro Carvalho Chehab { 104ccae7af2SMauro Carvalho Chehab int I,J; 105ccae7af2SMauro Carvalho Chehab int dia,diamin,diff; 106ccae7af2SMauro Carvalho Chehab diamin=1000000; 107ccae7af2SMauro Carvalho Chehab for (I = 1; I < 10; I++) { 108ccae7af2SMauro Carvalho Chehab J = ((2*I*lo1)/lo2+1)/2; 109ccae7af2SMauro Carvalho Chehab diff = I*(int)lo1-J*(int)lo2; 110ccae7af2SMauro Carvalho Chehab if (diff < 0) diff=-diff; 111ccae7af2SMauro Carvalho Chehab dia = (diff-(int)if2); 112ccae7af2SMauro Carvalho Chehab if (dia < 0) dia=-dia; 113ccae7af2SMauro Carvalho Chehab if (diamin > dia) diamin=dia; 114ccae7af2SMauro Carvalho Chehab } 115ccae7af2SMauro Carvalho Chehab return diamin; 116ccae7af2SMauro Carvalho Chehab } 117ccae7af2SMauro Carvalho Chehab 118ccae7af2SMauro Carvalho Chehab #define BANDWIDTH 4000 // kHz 119ccae7af2SMauro Carvalho Chehab 120ccae7af2SMauro Carvalho Chehab /* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ 121ccae7af2SMauro Carvalho Chehab static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) 122ccae7af2SMauro Carvalho Chehab { 123ccae7af2SMauro Carvalho Chehab u32 Spur,Sp1,Sp2; 124ccae7af2SMauro Carvalho Chehab int I,J; 125ccae7af2SMauro Carvalho Chehab I=0; 126ccae7af2SMauro Carvalho Chehab J=1000; 127ccae7af2SMauro Carvalho Chehab 128ccae7af2SMauro Carvalho Chehab Spur=mt2060_spurcalc(lo1,lo2,if2); 129ccae7af2SMauro Carvalho Chehab if (Spur < BANDWIDTH) { 130ccae7af2SMauro Carvalho Chehab /* Potential spurs detected */ 131ccae7af2SMauro Carvalho Chehab dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", 132ccae7af2SMauro Carvalho Chehab (int)lo1,(int)lo2); 133ccae7af2SMauro Carvalho Chehab I=1000; 134ccae7af2SMauro Carvalho Chehab Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); 135ccae7af2SMauro Carvalho Chehab Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); 136ccae7af2SMauro Carvalho Chehab 137ccae7af2SMauro Carvalho Chehab if (Sp1 < Sp2) { 138ccae7af2SMauro Carvalho Chehab J=-J; I=-I; Spur=Sp2; 139ccae7af2SMauro Carvalho Chehab } else 140ccae7af2SMauro Carvalho Chehab Spur=Sp1; 141ccae7af2SMauro Carvalho Chehab 142ccae7af2SMauro Carvalho Chehab while (Spur < BANDWIDTH) { 143ccae7af2SMauro Carvalho Chehab I += J; 144ccae7af2SMauro Carvalho Chehab Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); 145ccae7af2SMauro Carvalho Chehab } 146ccae7af2SMauro Carvalho Chehab dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", 147ccae7af2SMauro Carvalho Chehab (int)(lo1+I),(int)(lo2+I)); 148ccae7af2SMauro Carvalho Chehab } 149ccae7af2SMauro Carvalho Chehab return I; 150ccae7af2SMauro Carvalho Chehab } 151ccae7af2SMauro Carvalho Chehab #endif 152ccae7af2SMauro Carvalho Chehab 153ccae7af2SMauro Carvalho Chehab #define IF2 36150 // IF2 frequency = 36.150 MHz 154ccae7af2SMauro Carvalho Chehab #define FREF 16000 // Quartz oscillator 16 MHz 155ccae7af2SMauro Carvalho Chehab 156ccae7af2SMauro Carvalho Chehab static int mt2060_set_params(struct dvb_frontend *fe) 157ccae7af2SMauro Carvalho Chehab { 158ccae7af2SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 159ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv; 160ccae7af2SMauro Carvalho Chehab int i=0; 161ccae7af2SMauro Carvalho Chehab u32 freq; 162ccae7af2SMauro Carvalho Chehab u8 lnaband; 163ccae7af2SMauro Carvalho Chehab u32 f_lo1,f_lo2; 164ccae7af2SMauro Carvalho Chehab u32 div1,num1,div2,num2; 165ccae7af2SMauro Carvalho Chehab u8 b[8]; 166ccae7af2SMauro Carvalho Chehab u32 if1; 167ccae7af2SMauro Carvalho Chehab 168ccae7af2SMauro Carvalho Chehab priv = fe->tuner_priv; 169ccae7af2SMauro Carvalho Chehab 170ccae7af2SMauro Carvalho Chehab if1 = priv->if1_freq; 171ccae7af2SMauro Carvalho Chehab b[0] = REG_LO1B1; 172ccae7af2SMauro Carvalho Chehab b[1] = 0xFF; 173ccae7af2SMauro Carvalho Chehab 174ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 175ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 176ccae7af2SMauro Carvalho Chehab 177ccae7af2SMauro Carvalho Chehab mt2060_writeregs(priv,b,2); 178ccae7af2SMauro Carvalho Chehab 179ccae7af2SMauro Carvalho Chehab freq = c->frequency / 1000; /* Hz -> kHz */ 180ccae7af2SMauro Carvalho Chehab 181ccae7af2SMauro Carvalho Chehab f_lo1 = freq + if1 * 1000; 182ccae7af2SMauro Carvalho Chehab f_lo1 = (f_lo1 / 250) * 250; 183ccae7af2SMauro Carvalho Chehab f_lo2 = f_lo1 - freq - IF2; 184ccae7af2SMauro Carvalho Chehab // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise 185ccae7af2SMauro Carvalho Chehab f_lo2 = ((f_lo2 + 25) / 50) * 50; 186ccae7af2SMauro Carvalho Chehab priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, 187ccae7af2SMauro Carvalho Chehab 188ccae7af2SMauro Carvalho Chehab #ifdef MT2060_SPURCHECK 189ccae7af2SMauro Carvalho Chehab // LO-related spurs detection and correction 190ccae7af2SMauro Carvalho Chehab num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); 191ccae7af2SMauro Carvalho Chehab f_lo1 += num1; 192ccae7af2SMauro Carvalho Chehab f_lo2 += num1; 193ccae7af2SMauro Carvalho Chehab #endif 194ccae7af2SMauro Carvalho Chehab //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) 195ccae7af2SMauro Carvalho Chehab num1 = f_lo1 / (FREF / 64); 196ccae7af2SMauro Carvalho Chehab div1 = num1 / 64; 197ccae7af2SMauro Carvalho Chehab num1 &= 0x3f; 198ccae7af2SMauro Carvalho Chehab 199ccae7af2SMauro Carvalho Chehab // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) 200ccae7af2SMauro Carvalho Chehab num2 = f_lo2 * 64 / (FREF / 128); 201ccae7af2SMauro Carvalho Chehab div2 = num2 / 8192; 202ccae7af2SMauro Carvalho Chehab num2 &= 0x1fff; 203ccae7af2SMauro Carvalho Chehab 204ccae7af2SMauro Carvalho Chehab if (freq <= 95000) lnaband = 0xB0; else 205ccae7af2SMauro Carvalho Chehab if (freq <= 180000) lnaband = 0xA0; else 206ccae7af2SMauro Carvalho Chehab if (freq <= 260000) lnaband = 0x90; else 207ccae7af2SMauro Carvalho Chehab if (freq <= 335000) lnaband = 0x80; else 208ccae7af2SMauro Carvalho Chehab if (freq <= 425000) lnaband = 0x70; else 209ccae7af2SMauro Carvalho Chehab if (freq <= 480000) lnaband = 0x60; else 210ccae7af2SMauro Carvalho Chehab if (freq <= 570000) lnaband = 0x50; else 211ccae7af2SMauro Carvalho Chehab if (freq <= 645000) lnaband = 0x40; else 212ccae7af2SMauro Carvalho Chehab if (freq <= 730000) lnaband = 0x30; else 213ccae7af2SMauro Carvalho Chehab if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; 214ccae7af2SMauro Carvalho Chehab 215ccae7af2SMauro Carvalho Chehab b[0] = REG_LO1C1; 216ccae7af2SMauro Carvalho Chehab b[1] = lnaband | ((num1 >>2) & 0x0F); 217ccae7af2SMauro Carvalho Chehab b[2] = div1; 218ccae7af2SMauro Carvalho Chehab b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); 219ccae7af2SMauro Carvalho Chehab b[4] = num2 >> 4; 220ccae7af2SMauro Carvalho Chehab b[5] = ((num2 >>12) & 1) | (div2 << 1); 221ccae7af2SMauro Carvalho Chehab 222ccae7af2SMauro Carvalho Chehab dprintk("IF1: %dMHz",(int)if1); 223ccae7af2SMauro Carvalho Chehab dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); 224ccae7af2SMauro Carvalho Chehab dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); 225ccae7af2SMauro Carvalho Chehab dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); 226ccae7af2SMauro Carvalho Chehab 227ccae7af2SMauro Carvalho Chehab mt2060_writeregs(priv,b,6); 228ccae7af2SMauro Carvalho Chehab 229ccae7af2SMauro Carvalho Chehab //Waits for pll lock or timeout 230ccae7af2SMauro Carvalho Chehab i = 0; 231ccae7af2SMauro Carvalho Chehab do { 232ccae7af2SMauro Carvalho Chehab mt2060_readreg(priv,REG_LO_STATUS,b); 233ccae7af2SMauro Carvalho Chehab if ((b[0] & 0x88)==0x88) 234ccae7af2SMauro Carvalho Chehab break; 235ccae7af2SMauro Carvalho Chehab msleep(4); 236ccae7af2SMauro Carvalho Chehab i++; 237ccae7af2SMauro Carvalho Chehab } while (i<10); 238ccae7af2SMauro Carvalho Chehab 239ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 240ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 241ccae7af2SMauro Carvalho Chehab 2424539fc5cSMauro Carvalho Chehab return 0; 243ccae7af2SMauro Carvalho Chehab } 244ccae7af2SMauro Carvalho Chehab 245ccae7af2SMauro Carvalho Chehab static void mt2060_calibrate(struct mt2060_priv *priv) 246ccae7af2SMauro Carvalho Chehab { 247ccae7af2SMauro Carvalho Chehab u8 b = 0; 248ccae7af2SMauro Carvalho Chehab int i = 0; 249ccae7af2SMauro Carvalho Chehab 250ccae7af2SMauro Carvalho Chehab if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) 251ccae7af2SMauro Carvalho Chehab return; 252ccae7af2SMauro Carvalho Chehab if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) 253ccae7af2SMauro Carvalho Chehab return; 254ccae7af2SMauro Carvalho Chehab 255ccae7af2SMauro Carvalho Chehab /* initialize the clock output */ 256ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); 257ccae7af2SMauro Carvalho Chehab 258ccae7af2SMauro Carvalho Chehab do { 259ccae7af2SMauro Carvalho Chehab b |= (1 << 6); // FM1SS; 260ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_LO2C1,b); 261ccae7af2SMauro Carvalho Chehab msleep(20); 262ccae7af2SMauro Carvalho Chehab 263ccae7af2SMauro Carvalho Chehab if (i == 0) { 264ccae7af2SMauro Carvalho Chehab b |= (1 << 7); // FM1CA; 265ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_LO2C1,b); 266ccae7af2SMauro Carvalho Chehab b &= ~(1 << 7); // FM1CA; 267ccae7af2SMauro Carvalho Chehab msleep(20); 268ccae7af2SMauro Carvalho Chehab } 269ccae7af2SMauro Carvalho Chehab 270ccae7af2SMauro Carvalho Chehab b &= ~(1 << 6); // FM1SS 271ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_LO2C1,b); 272ccae7af2SMauro Carvalho Chehab 273ccae7af2SMauro Carvalho Chehab msleep(20); 274ccae7af2SMauro Carvalho Chehab i++; 275ccae7af2SMauro Carvalho Chehab } while (i < 9); 276ccae7af2SMauro Carvalho Chehab 277ccae7af2SMauro Carvalho Chehab i = 0; 278ccae7af2SMauro Carvalho Chehab while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) 279ccae7af2SMauro Carvalho Chehab msleep(20); 280ccae7af2SMauro Carvalho Chehab 281ccae7af2SMauro Carvalho Chehab if (i <= 10) { 282ccae7af2SMauro Carvalho Chehab mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) 283ccae7af2SMauro Carvalho Chehab dprintk("calibration was successful: %d", (int)priv->fmfreq); 284ccae7af2SMauro Carvalho Chehab } else 285ccae7af2SMauro Carvalho Chehab dprintk("FMCAL timed out"); 286ccae7af2SMauro Carvalho Chehab } 287ccae7af2SMauro Carvalho Chehab 288ccae7af2SMauro Carvalho Chehab static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) 289ccae7af2SMauro Carvalho Chehab { 290ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = fe->tuner_priv; 291ccae7af2SMauro Carvalho Chehab *frequency = priv->frequency; 292ccae7af2SMauro Carvalho Chehab return 0; 293ccae7af2SMauro Carvalho Chehab } 294ccae7af2SMauro Carvalho Chehab 295ccae7af2SMauro Carvalho Chehab static int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 296ccae7af2SMauro Carvalho Chehab { 297ccae7af2SMauro Carvalho Chehab *frequency = IF2 * 1000; 298ccae7af2SMauro Carvalho Chehab return 0; 299ccae7af2SMauro Carvalho Chehab } 300ccae7af2SMauro Carvalho Chehab 301ccae7af2SMauro Carvalho Chehab static int mt2060_init(struct dvb_frontend *fe) 302ccae7af2SMauro Carvalho Chehab { 303ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = fe->tuner_priv; 304ccae7af2SMauro Carvalho Chehab int ret; 305ccae7af2SMauro Carvalho Chehab 306ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 307ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 308ccae7af2SMauro Carvalho Chehab 309ccae7af2SMauro Carvalho Chehab ret = mt2060_writereg(priv, REG_VGAG, 310ccae7af2SMauro Carvalho Chehab (priv->cfg->clock_out << 6) | 0x33); 311ccae7af2SMauro Carvalho Chehab 312ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 313ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 314ccae7af2SMauro Carvalho Chehab 315ccae7af2SMauro Carvalho Chehab return ret; 316ccae7af2SMauro Carvalho Chehab } 317ccae7af2SMauro Carvalho Chehab 318ccae7af2SMauro Carvalho Chehab static int mt2060_sleep(struct dvb_frontend *fe) 319ccae7af2SMauro Carvalho Chehab { 320ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = fe->tuner_priv; 321ccae7af2SMauro Carvalho Chehab int ret; 322ccae7af2SMauro Carvalho Chehab 323ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 324ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 325ccae7af2SMauro Carvalho Chehab 326ccae7af2SMauro Carvalho Chehab ret = mt2060_writereg(priv, REG_VGAG, 327ccae7af2SMauro Carvalho Chehab (priv->cfg->clock_out << 6) | 0x30); 328ccae7af2SMauro Carvalho Chehab 329ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 330ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 331ccae7af2SMauro Carvalho Chehab 332ccae7af2SMauro Carvalho Chehab return ret; 333ccae7af2SMauro Carvalho Chehab } 334ccae7af2SMauro Carvalho Chehab 335f2709c20SMauro Carvalho Chehab static void mt2060_release(struct dvb_frontend *fe) 336f2709c20SMauro Carvalho Chehab { 337f2709c20SMauro Carvalho Chehab kfree(fe->tuner_priv); 338f2709c20SMauro Carvalho Chehab fe->tuner_priv = NULL; 339f2709c20SMauro Carvalho Chehab } 340f2709c20SMauro Carvalho Chehab 341ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops mt2060_tuner_ops = { 342ccae7af2SMauro Carvalho Chehab .info = { 343ccae7af2SMauro Carvalho Chehab .name = "Microtune MT2060", 344ccae7af2SMauro Carvalho Chehab .frequency_min = 48000000, 345ccae7af2SMauro Carvalho Chehab .frequency_max = 860000000, 346ccae7af2SMauro Carvalho Chehab .frequency_step = 50000, 347ccae7af2SMauro Carvalho Chehab }, 348ccae7af2SMauro Carvalho Chehab 349f2709c20SMauro Carvalho Chehab .release = mt2060_release, 350ccae7af2SMauro Carvalho Chehab 351ccae7af2SMauro Carvalho Chehab .init = mt2060_init, 352ccae7af2SMauro Carvalho Chehab .sleep = mt2060_sleep, 353ccae7af2SMauro Carvalho Chehab 354ccae7af2SMauro Carvalho Chehab .set_params = mt2060_set_params, 355ccae7af2SMauro Carvalho Chehab .get_frequency = mt2060_get_frequency, 356ccae7af2SMauro Carvalho Chehab .get_if_frequency = mt2060_get_if_frequency, 357ccae7af2SMauro Carvalho Chehab }; 358ccae7af2SMauro Carvalho Chehab 359ccae7af2SMauro Carvalho Chehab /* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ 360ccae7af2SMauro Carvalho Chehab struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) 361ccae7af2SMauro Carvalho Chehab { 362ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = NULL; 363ccae7af2SMauro Carvalho Chehab u8 id = 0; 364ccae7af2SMauro Carvalho Chehab 365ccae7af2SMauro Carvalho Chehab priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); 366ccae7af2SMauro Carvalho Chehab if (priv == NULL) 367ccae7af2SMauro Carvalho Chehab return NULL; 368ccae7af2SMauro Carvalho Chehab 369ccae7af2SMauro Carvalho Chehab priv->cfg = cfg; 370ccae7af2SMauro Carvalho Chehab priv->i2c = i2c; 371ccae7af2SMauro Carvalho Chehab priv->if1_freq = if1; 372ccae7af2SMauro Carvalho Chehab 373ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 374ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 375ccae7af2SMauro Carvalho Chehab 376ccae7af2SMauro Carvalho Chehab if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { 377ccae7af2SMauro Carvalho Chehab kfree(priv); 378ccae7af2SMauro Carvalho Chehab return NULL; 379ccae7af2SMauro Carvalho Chehab } 380ccae7af2SMauro Carvalho Chehab 381ccae7af2SMauro Carvalho Chehab if (id != PART_REV) { 382ccae7af2SMauro Carvalho Chehab kfree(priv); 383ccae7af2SMauro Carvalho Chehab return NULL; 384ccae7af2SMauro Carvalho Chehab } 385ccae7af2SMauro Carvalho Chehab printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); 386ccae7af2SMauro Carvalho Chehab memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); 387ccae7af2SMauro Carvalho Chehab 388ccae7af2SMauro Carvalho Chehab fe->tuner_priv = priv; 389ccae7af2SMauro Carvalho Chehab 390ccae7af2SMauro Carvalho Chehab mt2060_calibrate(priv); 391ccae7af2SMauro Carvalho Chehab 392ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 393ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 394ccae7af2SMauro Carvalho Chehab 395ccae7af2SMauro Carvalho Chehab return fe; 396ccae7af2SMauro Carvalho Chehab } 397ccae7af2SMauro Carvalho Chehab EXPORT_SYMBOL(mt2060_attach); 398ccae7af2SMauro Carvalho Chehab 399ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Olivier DANET"); 400ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); 401ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 402