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 18ccae7af2SMauro Carvalho Chehab /* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ 19ccae7af2SMauro Carvalho Chehab 20ccae7af2SMauro Carvalho Chehab #include <linux/module.h> 21ccae7af2SMauro Carvalho Chehab #include <linux/delay.h> 22ccae7af2SMauro Carvalho Chehab #include <linux/dvb/frontend.h> 23ccae7af2SMauro Carvalho Chehab #include <linux/i2c.h> 24ccae7af2SMauro Carvalho Chehab #include <linux/slab.h> 25ccae7af2SMauro Carvalho Chehab 26ccae7af2SMauro Carvalho Chehab #include "dvb_frontend.h" 27ccae7af2SMauro Carvalho Chehab 28ccae7af2SMauro Carvalho Chehab #include "mt2060.h" 29ccae7af2SMauro Carvalho Chehab #include "mt2060_priv.h" 30ccae7af2SMauro Carvalho Chehab 31ccae7af2SMauro Carvalho Chehab static int debug; 32ccae7af2SMauro Carvalho Chehab module_param(debug, int, 0644); 33ccae7af2SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); 34ccae7af2SMauro Carvalho Chehab 35ccae7af2SMauro Carvalho Chehab #define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) 36ccae7af2SMauro Carvalho Chehab 37ccae7af2SMauro Carvalho Chehab // Reads a single register 38ccae7af2SMauro Carvalho Chehab static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) 39ccae7af2SMauro Carvalho Chehab { 40ccae7af2SMauro Carvalho Chehab struct i2c_msg msg[2] = { 41ccae7af2SMauro Carvalho Chehab { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, 42ccae7af2SMauro Carvalho Chehab { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, 43ccae7af2SMauro Carvalho Chehab }; 44ccae7af2SMauro Carvalho Chehab 45ccae7af2SMauro Carvalho Chehab if (i2c_transfer(priv->i2c, msg, 2) != 2) { 46ccae7af2SMauro Carvalho Chehab printk(KERN_WARNING "mt2060 I2C read failed\n"); 47ccae7af2SMauro Carvalho Chehab return -EREMOTEIO; 48ccae7af2SMauro Carvalho Chehab } 49ccae7af2SMauro Carvalho Chehab return 0; 50ccae7af2SMauro Carvalho Chehab } 51ccae7af2SMauro Carvalho Chehab 52ccae7af2SMauro Carvalho Chehab // Writes a single register 53ccae7af2SMauro Carvalho Chehab static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) 54ccae7af2SMauro Carvalho Chehab { 55ccae7af2SMauro Carvalho Chehab u8 buf[2] = { reg, val }; 56ccae7af2SMauro Carvalho Chehab struct i2c_msg msg = { 57ccae7af2SMauro Carvalho Chehab .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 58ccae7af2SMauro Carvalho Chehab }; 59ccae7af2SMauro Carvalho Chehab 60ccae7af2SMauro Carvalho Chehab if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 61ccae7af2SMauro Carvalho Chehab printk(KERN_WARNING "mt2060 I2C write failed\n"); 62ccae7af2SMauro Carvalho Chehab return -EREMOTEIO; 63ccae7af2SMauro Carvalho Chehab } 64ccae7af2SMauro Carvalho Chehab return 0; 65ccae7af2SMauro Carvalho Chehab } 66ccae7af2SMauro Carvalho Chehab 67ccae7af2SMauro Carvalho Chehab // Writes a set of consecutive registers 68ccae7af2SMauro Carvalho Chehab static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) 69ccae7af2SMauro Carvalho Chehab { 70ccae7af2SMauro Carvalho Chehab struct i2c_msg msg = { 71ccae7af2SMauro Carvalho Chehab .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len 72ccae7af2SMauro Carvalho Chehab }; 73ccae7af2SMauro Carvalho Chehab if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 74ccae7af2SMauro Carvalho Chehab printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); 75ccae7af2SMauro Carvalho Chehab return -EREMOTEIO; 76ccae7af2SMauro Carvalho Chehab } 77ccae7af2SMauro Carvalho Chehab return 0; 78ccae7af2SMauro Carvalho Chehab } 79ccae7af2SMauro Carvalho Chehab 80ccae7af2SMauro Carvalho Chehab // Initialisation sequences 81ccae7af2SMauro Carvalho Chehab // LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 82ccae7af2SMauro Carvalho Chehab static u8 mt2060_config1[] = { 83ccae7af2SMauro Carvalho Chehab REG_LO1C1, 84ccae7af2SMauro Carvalho Chehab 0x3F, 0x74, 0x00, 0x08, 0x93 85ccae7af2SMauro Carvalho Chehab }; 86ccae7af2SMauro Carvalho Chehab 87ccae7af2SMauro Carvalho Chehab // FMCG=2, GP2=0, GP1=0 88ccae7af2SMauro Carvalho Chehab static u8 mt2060_config2[] = { 89ccae7af2SMauro Carvalho Chehab REG_MISC_CTRL, 90ccae7af2SMauro Carvalho Chehab 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 91ccae7af2SMauro Carvalho Chehab }; 92ccae7af2SMauro Carvalho Chehab 93ccae7af2SMauro Carvalho Chehab // VGAG=3, V1CSE=1 94ccae7af2SMauro Carvalho Chehab 95ccae7af2SMauro Carvalho Chehab #ifdef MT2060_SPURCHECK 96ccae7af2SMauro Carvalho Chehab /* The function below calculates the frequency offset between the output frequency if2 97ccae7af2SMauro Carvalho Chehab and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ 98ccae7af2SMauro Carvalho Chehab static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) 99ccae7af2SMauro Carvalho Chehab { 100ccae7af2SMauro Carvalho Chehab int I,J; 101ccae7af2SMauro Carvalho Chehab int dia,diamin,diff; 102ccae7af2SMauro Carvalho Chehab diamin=1000000; 103ccae7af2SMauro Carvalho Chehab for (I = 1; I < 10; I++) { 104ccae7af2SMauro Carvalho Chehab J = ((2*I*lo1)/lo2+1)/2; 105ccae7af2SMauro Carvalho Chehab diff = I*(int)lo1-J*(int)lo2; 106ccae7af2SMauro Carvalho Chehab if (diff < 0) diff=-diff; 107ccae7af2SMauro Carvalho Chehab dia = (diff-(int)if2); 108ccae7af2SMauro Carvalho Chehab if (dia < 0) dia=-dia; 109ccae7af2SMauro Carvalho Chehab if (diamin > dia) diamin=dia; 110ccae7af2SMauro Carvalho Chehab } 111ccae7af2SMauro Carvalho Chehab return diamin; 112ccae7af2SMauro Carvalho Chehab } 113ccae7af2SMauro Carvalho Chehab 114ccae7af2SMauro Carvalho Chehab #define BANDWIDTH 4000 // kHz 115ccae7af2SMauro Carvalho Chehab 116ccae7af2SMauro Carvalho Chehab /* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ 117ccae7af2SMauro Carvalho Chehab static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) 118ccae7af2SMauro Carvalho Chehab { 119ccae7af2SMauro Carvalho Chehab u32 Spur,Sp1,Sp2; 120ccae7af2SMauro Carvalho Chehab int I,J; 121ccae7af2SMauro Carvalho Chehab I=0; 122ccae7af2SMauro Carvalho Chehab J=1000; 123ccae7af2SMauro Carvalho Chehab 124ccae7af2SMauro Carvalho Chehab Spur=mt2060_spurcalc(lo1,lo2,if2); 125ccae7af2SMauro Carvalho Chehab if (Spur < BANDWIDTH) { 126ccae7af2SMauro Carvalho Chehab /* Potential spurs detected */ 127ccae7af2SMauro Carvalho Chehab dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", 128ccae7af2SMauro Carvalho Chehab (int)lo1,(int)lo2); 129ccae7af2SMauro Carvalho Chehab I=1000; 130ccae7af2SMauro Carvalho Chehab Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); 131ccae7af2SMauro Carvalho Chehab Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); 132ccae7af2SMauro Carvalho Chehab 133ccae7af2SMauro Carvalho Chehab if (Sp1 < Sp2) { 134ccae7af2SMauro Carvalho Chehab J=-J; I=-I; Spur=Sp2; 135ccae7af2SMauro Carvalho Chehab } else 136ccae7af2SMauro Carvalho Chehab Spur=Sp1; 137ccae7af2SMauro Carvalho Chehab 138ccae7af2SMauro Carvalho Chehab while (Spur < BANDWIDTH) { 139ccae7af2SMauro Carvalho Chehab I += J; 140ccae7af2SMauro Carvalho Chehab Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); 141ccae7af2SMauro Carvalho Chehab } 142ccae7af2SMauro Carvalho Chehab dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", 143ccae7af2SMauro Carvalho Chehab (int)(lo1+I),(int)(lo2+I)); 144ccae7af2SMauro Carvalho Chehab } 145ccae7af2SMauro Carvalho Chehab return I; 146ccae7af2SMauro Carvalho Chehab } 147ccae7af2SMauro Carvalho Chehab #endif 148ccae7af2SMauro Carvalho Chehab 149ccae7af2SMauro Carvalho Chehab #define IF2 36150 // IF2 frequency = 36.150 MHz 150ccae7af2SMauro Carvalho Chehab #define FREF 16000 // Quartz oscillator 16 MHz 151ccae7af2SMauro Carvalho Chehab 152ccae7af2SMauro Carvalho Chehab static int mt2060_set_params(struct dvb_frontend *fe) 153ccae7af2SMauro Carvalho Chehab { 154ccae7af2SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 155ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv; 156ccae7af2SMauro Carvalho Chehab int i=0; 157ccae7af2SMauro Carvalho Chehab u32 freq; 158ccae7af2SMauro Carvalho Chehab u8 lnaband; 159ccae7af2SMauro Carvalho Chehab u32 f_lo1,f_lo2; 160ccae7af2SMauro Carvalho Chehab u32 div1,num1,div2,num2; 161ccae7af2SMauro Carvalho Chehab u8 b[8]; 162ccae7af2SMauro Carvalho Chehab u32 if1; 163ccae7af2SMauro Carvalho Chehab 164ccae7af2SMauro Carvalho Chehab priv = fe->tuner_priv; 165ccae7af2SMauro Carvalho Chehab 166ccae7af2SMauro Carvalho Chehab if1 = priv->if1_freq; 167ccae7af2SMauro Carvalho Chehab b[0] = REG_LO1B1; 168ccae7af2SMauro Carvalho Chehab b[1] = 0xFF; 169ccae7af2SMauro Carvalho Chehab 170ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 171ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 172ccae7af2SMauro Carvalho Chehab 173ccae7af2SMauro Carvalho Chehab mt2060_writeregs(priv,b,2); 174ccae7af2SMauro Carvalho Chehab 175ccae7af2SMauro Carvalho Chehab freq = c->frequency / 1000; /* Hz -> kHz */ 176ccae7af2SMauro Carvalho Chehab 177ccae7af2SMauro Carvalho Chehab f_lo1 = freq + if1 * 1000; 178ccae7af2SMauro Carvalho Chehab f_lo1 = (f_lo1 / 250) * 250; 179ccae7af2SMauro Carvalho Chehab f_lo2 = f_lo1 - freq - IF2; 180ccae7af2SMauro Carvalho Chehab // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise 181ccae7af2SMauro Carvalho Chehab f_lo2 = ((f_lo2 + 25) / 50) * 50; 182ccae7af2SMauro Carvalho Chehab priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, 183ccae7af2SMauro Carvalho Chehab 184ccae7af2SMauro Carvalho Chehab #ifdef MT2060_SPURCHECK 185ccae7af2SMauro Carvalho Chehab // LO-related spurs detection and correction 186ccae7af2SMauro Carvalho Chehab num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); 187ccae7af2SMauro Carvalho Chehab f_lo1 += num1; 188ccae7af2SMauro Carvalho Chehab f_lo2 += num1; 189ccae7af2SMauro Carvalho Chehab #endif 190ccae7af2SMauro Carvalho Chehab //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) 191ccae7af2SMauro Carvalho Chehab num1 = f_lo1 / (FREF / 64); 192ccae7af2SMauro Carvalho Chehab div1 = num1 / 64; 193ccae7af2SMauro Carvalho Chehab num1 &= 0x3f; 194ccae7af2SMauro Carvalho Chehab 195ccae7af2SMauro Carvalho Chehab // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) 196ccae7af2SMauro Carvalho Chehab num2 = f_lo2 * 64 / (FREF / 128); 197ccae7af2SMauro Carvalho Chehab div2 = num2 / 8192; 198ccae7af2SMauro Carvalho Chehab num2 &= 0x1fff; 199ccae7af2SMauro Carvalho Chehab 200ccae7af2SMauro Carvalho Chehab if (freq <= 95000) lnaband = 0xB0; else 201ccae7af2SMauro Carvalho Chehab if (freq <= 180000) lnaband = 0xA0; else 202ccae7af2SMauro Carvalho Chehab if (freq <= 260000) lnaband = 0x90; else 203ccae7af2SMauro Carvalho Chehab if (freq <= 335000) lnaband = 0x80; else 204ccae7af2SMauro Carvalho Chehab if (freq <= 425000) lnaband = 0x70; else 205ccae7af2SMauro Carvalho Chehab if (freq <= 480000) lnaband = 0x60; else 206ccae7af2SMauro Carvalho Chehab if (freq <= 570000) lnaband = 0x50; else 207ccae7af2SMauro Carvalho Chehab if (freq <= 645000) lnaband = 0x40; else 208ccae7af2SMauro Carvalho Chehab if (freq <= 730000) lnaband = 0x30; else 209ccae7af2SMauro Carvalho Chehab if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; 210ccae7af2SMauro Carvalho Chehab 211ccae7af2SMauro Carvalho Chehab b[0] = REG_LO1C1; 212ccae7af2SMauro Carvalho Chehab b[1] = lnaband | ((num1 >>2) & 0x0F); 213ccae7af2SMauro Carvalho Chehab b[2] = div1; 214ccae7af2SMauro Carvalho Chehab b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); 215ccae7af2SMauro Carvalho Chehab b[4] = num2 >> 4; 216ccae7af2SMauro Carvalho Chehab b[5] = ((num2 >>12) & 1) | (div2 << 1); 217ccae7af2SMauro Carvalho Chehab 218ccae7af2SMauro Carvalho Chehab dprintk("IF1: %dMHz",(int)if1); 219ccae7af2SMauro Carvalho Chehab dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); 220ccae7af2SMauro Carvalho Chehab dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); 221ccae7af2SMauro 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]); 222ccae7af2SMauro Carvalho Chehab 223ccae7af2SMauro Carvalho Chehab mt2060_writeregs(priv,b,6); 224ccae7af2SMauro Carvalho Chehab 225ccae7af2SMauro Carvalho Chehab //Waits for pll lock or timeout 226ccae7af2SMauro Carvalho Chehab i = 0; 227ccae7af2SMauro Carvalho Chehab do { 228ccae7af2SMauro Carvalho Chehab mt2060_readreg(priv,REG_LO_STATUS,b); 229ccae7af2SMauro Carvalho Chehab if ((b[0] & 0x88)==0x88) 230ccae7af2SMauro Carvalho Chehab break; 231ccae7af2SMauro Carvalho Chehab msleep(4); 232ccae7af2SMauro Carvalho Chehab i++; 233ccae7af2SMauro Carvalho Chehab } while (i<10); 234ccae7af2SMauro Carvalho Chehab 235ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 236ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 237ccae7af2SMauro Carvalho Chehab 2384539fc5cSMauro Carvalho Chehab return 0; 239ccae7af2SMauro Carvalho Chehab } 240ccae7af2SMauro Carvalho Chehab 241ccae7af2SMauro Carvalho Chehab static void mt2060_calibrate(struct mt2060_priv *priv) 242ccae7af2SMauro Carvalho Chehab { 243ccae7af2SMauro Carvalho Chehab u8 b = 0; 244ccae7af2SMauro Carvalho Chehab int i = 0; 245ccae7af2SMauro Carvalho Chehab 246ccae7af2SMauro Carvalho Chehab if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) 247ccae7af2SMauro Carvalho Chehab return; 248ccae7af2SMauro Carvalho Chehab if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) 249ccae7af2SMauro Carvalho Chehab return; 250ccae7af2SMauro Carvalho Chehab 251ccae7af2SMauro Carvalho Chehab /* initialize the clock output */ 252ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); 253ccae7af2SMauro Carvalho Chehab 254ccae7af2SMauro Carvalho Chehab do { 255ccae7af2SMauro Carvalho Chehab b |= (1 << 6); // FM1SS; 256ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_LO2C1,b); 257ccae7af2SMauro Carvalho Chehab msleep(20); 258ccae7af2SMauro Carvalho Chehab 259ccae7af2SMauro Carvalho Chehab if (i == 0) { 260ccae7af2SMauro Carvalho Chehab b |= (1 << 7); // FM1CA; 261ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_LO2C1,b); 262ccae7af2SMauro Carvalho Chehab b &= ~(1 << 7); // FM1CA; 263ccae7af2SMauro Carvalho Chehab msleep(20); 264ccae7af2SMauro Carvalho Chehab } 265ccae7af2SMauro Carvalho Chehab 266ccae7af2SMauro Carvalho Chehab b &= ~(1 << 6); // FM1SS 267ccae7af2SMauro Carvalho Chehab mt2060_writereg(priv, REG_LO2C1,b); 268ccae7af2SMauro Carvalho Chehab 269ccae7af2SMauro Carvalho Chehab msleep(20); 270ccae7af2SMauro Carvalho Chehab i++; 271ccae7af2SMauro Carvalho Chehab } while (i < 9); 272ccae7af2SMauro Carvalho Chehab 273ccae7af2SMauro Carvalho Chehab i = 0; 274ccae7af2SMauro Carvalho Chehab while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) 275ccae7af2SMauro Carvalho Chehab msleep(20); 276ccae7af2SMauro Carvalho Chehab 277ccae7af2SMauro Carvalho Chehab if (i <= 10) { 278ccae7af2SMauro Carvalho Chehab mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) 279ccae7af2SMauro Carvalho Chehab dprintk("calibration was successful: %d", (int)priv->fmfreq); 280ccae7af2SMauro Carvalho Chehab } else 281ccae7af2SMauro Carvalho Chehab dprintk("FMCAL timed out"); 282ccae7af2SMauro Carvalho Chehab } 283ccae7af2SMauro Carvalho Chehab 284ccae7af2SMauro Carvalho Chehab static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) 285ccae7af2SMauro Carvalho Chehab { 286ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = fe->tuner_priv; 287ccae7af2SMauro Carvalho Chehab *frequency = priv->frequency; 288ccae7af2SMauro Carvalho Chehab return 0; 289ccae7af2SMauro Carvalho Chehab } 290ccae7af2SMauro Carvalho Chehab 291ccae7af2SMauro Carvalho Chehab static int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 292ccae7af2SMauro Carvalho Chehab { 293ccae7af2SMauro Carvalho Chehab *frequency = IF2 * 1000; 294ccae7af2SMauro Carvalho Chehab return 0; 295ccae7af2SMauro Carvalho Chehab } 296ccae7af2SMauro Carvalho Chehab 297ccae7af2SMauro Carvalho Chehab static int mt2060_init(struct dvb_frontend *fe) 298ccae7af2SMauro Carvalho Chehab { 299ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = fe->tuner_priv; 300ccae7af2SMauro Carvalho Chehab int ret; 301ccae7af2SMauro Carvalho Chehab 302ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 303ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 304ccae7af2SMauro Carvalho Chehab 305ccae7af2SMauro Carvalho Chehab ret = mt2060_writereg(priv, REG_VGAG, 306ccae7af2SMauro Carvalho Chehab (priv->cfg->clock_out << 6) | 0x33); 307ccae7af2SMauro Carvalho Chehab 308ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 309ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 310ccae7af2SMauro Carvalho Chehab 311ccae7af2SMauro Carvalho Chehab return ret; 312ccae7af2SMauro Carvalho Chehab } 313ccae7af2SMauro Carvalho Chehab 314ccae7af2SMauro Carvalho Chehab static int mt2060_sleep(struct dvb_frontend *fe) 315ccae7af2SMauro Carvalho Chehab { 316ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = fe->tuner_priv; 317ccae7af2SMauro Carvalho Chehab int ret; 318ccae7af2SMauro Carvalho Chehab 319ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 320ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 321ccae7af2SMauro Carvalho Chehab 322ccae7af2SMauro Carvalho Chehab ret = mt2060_writereg(priv, REG_VGAG, 323ccae7af2SMauro Carvalho Chehab (priv->cfg->clock_out << 6) | 0x30); 324ccae7af2SMauro Carvalho Chehab 325ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 326ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 327ccae7af2SMauro Carvalho Chehab 328ccae7af2SMauro Carvalho Chehab return ret; 329ccae7af2SMauro Carvalho Chehab } 330ccae7af2SMauro Carvalho Chehab 331f2709c20SMauro Carvalho Chehab static void mt2060_release(struct dvb_frontend *fe) 332f2709c20SMauro Carvalho Chehab { 333f2709c20SMauro Carvalho Chehab kfree(fe->tuner_priv); 334f2709c20SMauro Carvalho Chehab fe->tuner_priv = NULL; 335f2709c20SMauro Carvalho Chehab } 336f2709c20SMauro Carvalho Chehab 337ccae7af2SMauro Carvalho Chehab static const struct dvb_tuner_ops mt2060_tuner_ops = { 338ccae7af2SMauro Carvalho Chehab .info = { 339ccae7af2SMauro Carvalho Chehab .name = "Microtune MT2060", 340ccae7af2SMauro Carvalho Chehab .frequency_min = 48000000, 341ccae7af2SMauro Carvalho Chehab .frequency_max = 860000000, 342ccae7af2SMauro Carvalho Chehab .frequency_step = 50000, 343ccae7af2SMauro Carvalho Chehab }, 344ccae7af2SMauro Carvalho Chehab 345f2709c20SMauro Carvalho Chehab .release = mt2060_release, 346ccae7af2SMauro Carvalho Chehab 347ccae7af2SMauro Carvalho Chehab .init = mt2060_init, 348ccae7af2SMauro Carvalho Chehab .sleep = mt2060_sleep, 349ccae7af2SMauro Carvalho Chehab 350ccae7af2SMauro Carvalho Chehab .set_params = mt2060_set_params, 351ccae7af2SMauro Carvalho Chehab .get_frequency = mt2060_get_frequency, 352ccae7af2SMauro Carvalho Chehab .get_if_frequency = mt2060_get_if_frequency, 353ccae7af2SMauro Carvalho Chehab }; 354ccae7af2SMauro Carvalho Chehab 355ccae7af2SMauro Carvalho Chehab /* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ 356ccae7af2SMauro Carvalho Chehab struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) 357ccae7af2SMauro Carvalho Chehab { 358ccae7af2SMauro Carvalho Chehab struct mt2060_priv *priv = NULL; 359ccae7af2SMauro Carvalho Chehab u8 id = 0; 360ccae7af2SMauro Carvalho Chehab 361ccae7af2SMauro Carvalho Chehab priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); 362ccae7af2SMauro Carvalho Chehab if (priv == NULL) 363ccae7af2SMauro Carvalho Chehab return NULL; 364ccae7af2SMauro Carvalho Chehab 365ccae7af2SMauro Carvalho Chehab priv->cfg = cfg; 366ccae7af2SMauro Carvalho Chehab priv->i2c = i2c; 367ccae7af2SMauro Carvalho Chehab priv->if1_freq = if1; 368ccae7af2SMauro Carvalho Chehab 369ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 370ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 371ccae7af2SMauro Carvalho Chehab 372ccae7af2SMauro Carvalho Chehab if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { 373ccae7af2SMauro Carvalho Chehab kfree(priv); 374ccae7af2SMauro Carvalho Chehab return NULL; 375ccae7af2SMauro Carvalho Chehab } 376ccae7af2SMauro Carvalho Chehab 377ccae7af2SMauro Carvalho Chehab if (id != PART_REV) { 378ccae7af2SMauro Carvalho Chehab kfree(priv); 379ccae7af2SMauro Carvalho Chehab return NULL; 380ccae7af2SMauro Carvalho Chehab } 381ccae7af2SMauro Carvalho Chehab printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); 382ccae7af2SMauro Carvalho Chehab memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); 383ccae7af2SMauro Carvalho Chehab 384ccae7af2SMauro Carvalho Chehab fe->tuner_priv = priv; 385ccae7af2SMauro Carvalho Chehab 386ccae7af2SMauro Carvalho Chehab mt2060_calibrate(priv); 387ccae7af2SMauro Carvalho Chehab 388ccae7af2SMauro Carvalho Chehab if (fe->ops.i2c_gate_ctrl) 389ccae7af2SMauro Carvalho Chehab fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 390ccae7af2SMauro Carvalho Chehab 391ccae7af2SMauro Carvalho Chehab return fe; 392ccae7af2SMauro Carvalho Chehab } 393ccae7af2SMauro Carvalho Chehab EXPORT_SYMBOL(mt2060_attach); 394ccae7af2SMauro Carvalho Chehab 39559e8b7aaSAntti Palosaari static int mt2060_probe(struct i2c_client *client, 39659e8b7aaSAntti Palosaari const struct i2c_device_id *id) 39759e8b7aaSAntti Palosaari { 39859e8b7aaSAntti Palosaari struct mt2060_platform_data *pdata = client->dev.platform_data; 39959e8b7aaSAntti Palosaari struct dvb_frontend *fe; 40059e8b7aaSAntti Palosaari struct mt2060_priv *dev; 40159e8b7aaSAntti Palosaari int ret; 40259e8b7aaSAntti Palosaari u8 chip_id; 40359e8b7aaSAntti Palosaari 40459e8b7aaSAntti Palosaari dev_dbg(&client->dev, "\n"); 40559e8b7aaSAntti Palosaari 40659e8b7aaSAntti Palosaari if (!pdata) { 40759e8b7aaSAntti Palosaari dev_err(&client->dev, "Cannot proceed without platform data\n"); 40859e8b7aaSAntti Palosaari ret = -EINVAL; 40959e8b7aaSAntti Palosaari goto err; 41059e8b7aaSAntti Palosaari } 41159e8b7aaSAntti Palosaari 41259e8b7aaSAntti Palosaari dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); 41359e8b7aaSAntti Palosaari if (!dev) { 41459e8b7aaSAntti Palosaari ret = -ENOMEM; 41559e8b7aaSAntti Palosaari goto err; 41659e8b7aaSAntti Palosaari } 41759e8b7aaSAntti Palosaari 41859e8b7aaSAntti Palosaari fe = pdata->dvb_frontend; 41959e8b7aaSAntti Palosaari dev->config.i2c_address = client->addr; 42059e8b7aaSAntti Palosaari dev->config.clock_out = pdata->clock_out; 42159e8b7aaSAntti Palosaari dev->cfg = &dev->config; 42259e8b7aaSAntti Palosaari dev->i2c = client->adapter; 42359e8b7aaSAntti Palosaari dev->if1_freq = pdata->if1 ? pdata->if1 : 1220; 42459e8b7aaSAntti Palosaari dev->client = client; 42559e8b7aaSAntti Palosaari 42659e8b7aaSAntti Palosaari ret = mt2060_readreg(dev, REG_PART_REV, &chip_id); 42759e8b7aaSAntti Palosaari if (ret) { 42859e8b7aaSAntti Palosaari ret = -ENODEV; 42959e8b7aaSAntti Palosaari goto err; 43059e8b7aaSAntti Palosaari } 43159e8b7aaSAntti Palosaari 43259e8b7aaSAntti Palosaari dev_dbg(&client->dev, "chip id=%02x\n", chip_id); 43359e8b7aaSAntti Palosaari 43459e8b7aaSAntti Palosaari if (chip_id != PART_REV) { 43559e8b7aaSAntti Palosaari ret = -ENODEV; 43659e8b7aaSAntti Palosaari goto err; 43759e8b7aaSAntti Palosaari } 43859e8b7aaSAntti Palosaari 43959e8b7aaSAntti Palosaari dev_info(&client->dev, "Microtune MT2060 successfully identified\n"); 44059e8b7aaSAntti Palosaari memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(fe->ops.tuner_ops)); 44159e8b7aaSAntti Palosaari fe->ops.tuner_ops.release = NULL; 44259e8b7aaSAntti Palosaari fe->tuner_priv = dev; 44359e8b7aaSAntti Palosaari i2c_set_clientdata(client, dev); 44459e8b7aaSAntti Palosaari 44559e8b7aaSAntti Palosaari mt2060_calibrate(dev); 44659e8b7aaSAntti Palosaari 44759e8b7aaSAntti Palosaari return 0; 44859e8b7aaSAntti Palosaari err: 44959e8b7aaSAntti Palosaari dev_dbg(&client->dev, "failed=%d\n", ret); 45059e8b7aaSAntti Palosaari return ret; 45159e8b7aaSAntti Palosaari } 45259e8b7aaSAntti Palosaari 45359e8b7aaSAntti Palosaari static int mt2060_remove(struct i2c_client *client) 45459e8b7aaSAntti Palosaari { 45559e8b7aaSAntti Palosaari dev_dbg(&client->dev, "\n"); 45659e8b7aaSAntti Palosaari 45759e8b7aaSAntti Palosaari return 0; 45859e8b7aaSAntti Palosaari } 45959e8b7aaSAntti Palosaari 46059e8b7aaSAntti Palosaari static const struct i2c_device_id mt2060_id_table[] = { 46159e8b7aaSAntti Palosaari {"mt2060", 0}, 46259e8b7aaSAntti Palosaari {} 46359e8b7aaSAntti Palosaari }; 46459e8b7aaSAntti Palosaari MODULE_DEVICE_TABLE(i2c, mt2060_id_table); 46559e8b7aaSAntti Palosaari 46659e8b7aaSAntti Palosaari static struct i2c_driver mt2060_driver = { 46759e8b7aaSAntti Palosaari .driver = { 46859e8b7aaSAntti Palosaari .name = "mt2060", 46959e8b7aaSAntti Palosaari .suppress_bind_attrs = true, 47059e8b7aaSAntti Palosaari }, 47159e8b7aaSAntti Palosaari .probe = mt2060_probe, 47259e8b7aaSAntti Palosaari .remove = mt2060_remove, 47359e8b7aaSAntti Palosaari .id_table = mt2060_id_table, 47459e8b7aaSAntti Palosaari }; 47559e8b7aaSAntti Palosaari 47659e8b7aaSAntti Palosaari module_i2c_driver(mt2060_driver); 47759e8b7aaSAntti Palosaari 478ccae7af2SMauro Carvalho Chehab MODULE_AUTHOR("Olivier DANET"); 479ccae7af2SMauro Carvalho Chehab MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); 480ccae7af2SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 481