mt2060.c revision 2676c258eb836caed508099e58030398217a5ef0
14de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* 24de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" 34de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * 44de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * Copyright (c) 2006 Olivier DANET <odanet@caramail.com> 54de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * 64de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * This program is free software; you can redistribute it and/or modify 74de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * it under the terms of the GNU General Public License as published by 84de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * the Free Software Foundation; either version 2 of the License, or 94de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * (at your option) any later version. 104de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * 114de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * This program is distributed in the hope that it will be useful, 124de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * but WITHOUT ANY WARRANTY; without even the implied warranty of 134de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 144de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * 154de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * GNU General Public License for more details. 164de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * 174de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * You should have received a copy of the GNU General Public License 184de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * along with this program; if not, write to the Free Software 194de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= 204de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher */ 214de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 224de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ 234de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 244de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include <linux/module.h> 254de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include <linux/delay.h> 264de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include <linux/dvb/frontend.h> 2746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET#include <linux/i2c.h> 285a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 2946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 3046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET#include "dvb_frontend.h" 3146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 324de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include "mt2060.h" 334de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include "mt2060_priv.h" 344de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 35b7571f8d7e12cd70048331e6a0199a42dc995d99Patrick Boettcherstatic int debug; 364de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettchermodule_param(debug, int, 0644); 374de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); 384de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 3946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) 404de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 414de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// Reads a single register 4246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) 434de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 444de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher struct i2c_msg msg[2] = { 4546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, 4646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, 474de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher }; 484de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 4946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (i2c_transfer(priv->i2c, msg, 2) != 2) { 504de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher printk(KERN_WARNING "mt2060 I2C read failed\n"); 514de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return -EREMOTEIO; 524de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } 534de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return 0; 544de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 554de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 564de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// Writes a single register 5746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) 584de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 5946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET u8 buf[2] = { reg, val }; 604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher struct i2c_msg msg = { 6146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher }; 634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 6446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher printk(KERN_WARNING "mt2060 I2C write failed\n"); 664de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return -EREMOTEIO; 674de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } 684de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return 0; 694de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 704de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 714de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// Writes a set of consecutive registers 7246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) 734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher struct i2c_msg msg = { 7546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len 764de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher }; 7746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 784de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); 794de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return -EREMOTEIO; 804de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } 814de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return 0; 824de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 834de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 844de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// Initialisation sequences 854de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 864de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic u8 mt2060_config1[] = { 874de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher REG_LO1C1, 884de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 0x3F, 0x74, 0x00, 0x08, 0x93 894de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}; 904de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 914de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// FMCG=2, GP2=0, GP1=0 924de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic u8 mt2060_config2[] = { 934de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher REG_MISC_CTRL, 944de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 954de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}; 964de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 974de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// VGAG=3, V1CSE=1 984de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#ifdef MT2060_SPURCHECK 1004de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* The function below calculates the frequency offset between the output frequency if2 1014de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ 1024de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) 1034de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 1044de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher int I,J; 1054de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher int dia,diamin,diff; 1064de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher diamin=1000000; 1074de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher for (I = 1; I < 10; I++) { 1084de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher J = ((2*I*lo1)/lo2+1)/2; 1094de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher diff = I*(int)lo1-J*(int)lo2; 1104de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (diff < 0) diff=-diff; 1114de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher dia = (diff-(int)if2); 1124de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (dia < 0) dia=-dia; 1134de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (diamin > dia) diamin=dia; 1144de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } 1154de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return diamin; 1164de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 1174de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1184de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#define BANDWIDTH 4000 // kHz 1194de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1204de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ 1214de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) 1224de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 1234de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u32 Spur,Sp1,Sp2; 1244de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher int I,J; 1254de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher I=0; 1264de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher J=1000; 1274de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1284de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher Spur=mt2060_spurcalc(lo1,lo2,if2); 1294de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (Spur < BANDWIDTH) { 1304de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher /* Potential spurs detected */ 1314de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", 1324de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher (int)lo1,(int)lo2); 1334de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher I=1000; 1344de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); 1354de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); 1364de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1374de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (Sp1 < Sp2) { 1384de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher J=-J; I=-I; Spur=Sp2; 1394de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } else 1404de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher Spur=Sp1; 1414de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1424de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher while (Spur < BANDWIDTH) { 1434de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher I += J; 1444de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); 1454de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } 1464de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", 1474de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher (int)(lo1+I),(int)(lo2+I)); 1484de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } 1494de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return I; 1504de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 1514de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#endif 1524de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1534de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#define IF2 36150 // IF2 frequency = 36.150 MHz 1544de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#define FREF 16000 // Quartz oscillator 16 MHz 1554de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 15646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) 1574de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 1582676c258eb836caed508099e58030398217a5ef0Mauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 15946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET struct mt2060_priv *priv; 1604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher int ret=0; 1614de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher int i=0; 1624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u32 freq; 1634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u8 lnaband; 1644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u32 f_lo1,f_lo2; 1654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u32 div1,num1,div2,num2; 1664de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u8 b[8]; 1674de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u32 if1; 1684de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 16946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET priv = fe->tuner_priv; 17046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 17146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if1 = priv->if1_freq; 1724de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[0] = REG_LO1B1; 1734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[1] = 0xFF; 1744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1756e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 1766e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 1776e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 17846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_writeregs(priv,b,2); 1794de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1802676c258eb836caed508099e58030398217a5ef0Mauro Carvalho Chehab freq = c->frequency / 1000; /* Hz -> kHz */ 18146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 18246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET f_lo1 = freq + if1 * 1000; 18346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET f_lo1 = (f_lo1 / 250) * 250; 18446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET f_lo2 = f_lo1 - freq - IF2; 18546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise 18646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET f_lo2 = ((f_lo2 + 25) / 50) * 50; 18746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, 1884de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 1894de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#ifdef MT2060_SPURCHECK 1904de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher // LO-related spurs detection and correction 1914de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); 1924de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher f_lo1 += num1; 1934de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher f_lo2 += num1; 1944de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#endif 1954de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) 19646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET num1 = f_lo1 / (FREF / 64); 19746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET div1 = num1 / 64; 19846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET num1 &= 0x3f; 1994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2004de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) 20146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET num2 = f_lo2 * 64 / (FREF / 128); 20246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET div2 = num2 / 8192; 20346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET num2 &= 0x1fff; 2044de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2054de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 95000) lnaband = 0xB0; else 2064de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 180000) lnaband = 0xA0; else 2074de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 260000) lnaband = 0x90; else 2084de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 335000) lnaband = 0x80; else 2094de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 425000) lnaband = 0x70; else 2104de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 480000) lnaband = 0x60; else 2114de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 570000) lnaband = 0x50; else 2124de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 645000) lnaband = 0x40; else 2134de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 730000) lnaband = 0x30; else 2144de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; 2154de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2164de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[0] = REG_LO1C1; 2174de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[1] = lnaband | ((num1 >>2) & 0x0F); 2184de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[2] = div1; 2194de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); 2204de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[4] = num2 >> 4; 2214de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b[5] = ((num2 >>12) & 1) | (div2 << 1); 2224de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2234de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher dprintk("IF1: %dMHz",(int)if1); 22446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); 22546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); 2264de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); 2274de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 22846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_writeregs(priv,b,6); 2294de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2304de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher //Waits for pll lock or timeout 23146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET i = 0; 2324de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher do { 23346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_readreg(priv,REG_LO_STATUS,b); 23446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if ((b[0] & 0x88)==0x88) 23546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET break; 2364de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher msleep(4); 2374de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher i++; 2384de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } while (i<10); 2394de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2406e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 2416e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 2426e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 2434de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return ret; 2444de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 2454de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 24646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic void mt2060_calibrate(struct mt2060_priv *priv) 2474de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 2484de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u8 b = 0; 2494de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher int i = 0; 2504de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 25146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) 2524de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return; 25346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) 2544de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher return; 2554de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 256136cafbf4a024b52ba0a10627217f03cea9ff9f8Patrick Boettcher /* initialize the clock output */ 257136cafbf4a024b52ba0a10627217f03cea9ff9f8Patrick Boettcher mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); 258136cafbf4a024b52ba0a10627217f03cea9ff9f8Patrick Boettcher 2594de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher do { 2604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b |= (1 << 6); // FM1SS; 26146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_writereg(priv, REG_LO2C1,b); 2624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher msleep(20); 2634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher if (i == 0) { 2654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b |= (1 << 7); // FM1CA; 26646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_writereg(priv, REG_LO2C1,b); 2674de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b &= ~(1 << 7); // FM1CA; 2684de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher msleep(20); 2694de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } 2704de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2714de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher b &= ~(1 << 6); // FM1SS 27246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_writereg(priv, REG_LO2C1,b); 2734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher msleep(20); 2754de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher i++; 2764de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } while (i < 9); 2774de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2784de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher i = 0; 27946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) 2804de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher msleep(20); 2814de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 2827d979a86a95036a0c765aaa90f8bcefb4096a406Roel Kluin if (i <= 10) { 28346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) 28446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET dprintk("calibration was successful: %d", (int)priv->fmfreq); 2854de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher } else 2864de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher dprintk("FMCAL timed out"); 2874de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 2884de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 28946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) 29046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET{ 29146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET struct mt2060_priv *priv = fe->tuner_priv; 29246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET *frequency = priv->frequency; 29346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET return 0; 29446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET} 29546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 296055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaaristatic int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 297055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari{ 298055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari *frequency = IF2 * 1000; 299055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari return 0; 300055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari} 301055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari 302294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTETstatic int mt2060_init(struct dvb_frontend *fe) 303294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET{ 304294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET struct mt2060_priv *priv = fe->tuner_priv; 3056e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari int ret; 3066e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3076e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 3086e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 3096e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3106e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari ret = mt2060_writereg(priv, REG_VGAG, 3116e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari (priv->cfg->clock_out << 6) | 0x33); 3126e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3136e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 3146e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 3156e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3166e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari return ret; 317294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET} 318294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET 31946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_sleep(struct dvb_frontend *fe) 32046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET{ 32146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET struct mt2060_priv *priv = fe->tuner_priv; 3226e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari int ret; 3236e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3246e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 3256e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 3266e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3276e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari ret = mt2060_writereg(priv, REG_VGAG, 3286e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari (priv->cfg->clock_out << 6) | 0x30); 3296e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3306e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 3316e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 3326e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3336e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari return ret; 33446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET} 33546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 33646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_release(struct dvb_frontend *fe) 33746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET{ 33846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET kfree(fe->tuner_priv); 33946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET fe->tuner_priv = NULL; 34046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET return 0; 34146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET} 34246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 34346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic const struct dvb_tuner_ops mt2060_tuner_ops = { 34446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .info = { 34546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .name = "Microtune MT2060", 34646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .frequency_min = 48000000, 34746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .frequency_max = 860000000, 34846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .frequency_step = 50000, 34946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET }, 35046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 35146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .release = mt2060_release, 35246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 353294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET .init = mt2060_init, 35446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .sleep = mt2060_sleep, 35546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 35646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .set_params = mt2060_set_params, 35746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET .get_frequency = mt2060_get_frequency, 358055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari .get_if_frequency = mt2060_get_if_frequency, 35946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET}; 36046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 3614de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ 3626958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcherstruct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) 3634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{ 36446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET struct mt2060_priv *priv = NULL; 3654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher u8 id = 0; 3664de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 36746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); 36846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (priv == NULL) 3696958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher return NULL; 3704de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 37146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET priv->cfg = cfg; 37246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET priv->i2c = i2c; 37346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET priv->if1_freq = if1; 3744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 3756e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 3766e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 3776e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 37846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { 37946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET kfree(priv); 3806958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher return NULL; 38146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET } 3824de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 38346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET if (id != PART_REV) { 38446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET kfree(priv); 3856958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher return NULL; 38646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET } 387b7571f8d7e12cd70048331e6a0199a42dc995d99Patrick Boettcher printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); 38846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); 38946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET 39046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET fe->tuner_priv = priv; 3914de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 39246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET mt2060_calibrate(priv); 3934de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 3946e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari if (fe->ops.i2c_gate_ctrl) 3956e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 3966e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari 3976958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher return fe; 3984de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher} 3994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherEXPORT_SYMBOL(mt2060_attach); 4004de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher 4014de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_AUTHOR("Olivier DANET"); 4024de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); 4034de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_LICENSE("GPL"); 404