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 = &reg, .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
15614d24d148c7521b2b88b396652e36f55d061e195Mauro Carvalho Chehabstatic int mt2060_set_params(struct dvb_frontend *fe)
1574de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
1582676c258eb836caed508099e58030398217a5ef0Mauro Carvalho Chehab	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
15946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	struct mt2060_priv *priv;
1604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int i=0;
1614de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 freq;
1624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8  lnaband;
1634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 f_lo1,f_lo2;
1644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 div1,num1,div2,num2;
1654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8  b[8];
1664de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 if1;
1674de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
16846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	priv = fe->tuner_priv;
16946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
17046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	if1 = priv->if1_freq;
1714de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[0] = REG_LO1B1;
1724de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[1] = 0xFF;
1734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1746e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
1756e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
1766e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
17746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	mt2060_writeregs(priv,b,2);
1784de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1792676c258eb836caed508099e58030398217a5ef0Mauro Carvalho Chehab	freq = c->frequency / 1000; /* Hz -> kHz */
18046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
18146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	f_lo1 = freq + if1 * 1000;
18246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	f_lo1 = (f_lo1 / 250) * 250;
18346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	f_lo2 = f_lo1 - freq - IF2;
18446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	// From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise
18546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	f_lo2 = ((f_lo2 + 25) / 50) * 50;
18646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	priv->frequency =  (f_lo1 - f_lo2 - IF2) * 1000,
1874de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1884de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#ifdef MT2060_SPURCHECK
1894de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	// LO-related spurs detection and correction
1904de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	num1   = mt2060_spurcheck(f_lo1,f_lo2,IF2);
1914de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo1 += num1;
1924de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo2 += num1;
1934de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#endif
1944de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	//Frequency LO1 = 16MHz * (DIV1 + NUM1/64 )
19546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	num1 = f_lo1 / (FREF / 64);
19646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	div1 = num1 / 64;
19746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	num1 &= 0x3f;
1984de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	// Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 )
20046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	num2 = f_lo2 * 64 / (FREF / 128);
20146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	div2 = num2 / 8192;
20246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	num2 &= 0x1fff;
2034de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2044de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <=  95000) lnaband = 0xB0; else
2054de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 180000) lnaband = 0xA0; else
2064de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 260000) lnaband = 0x90; else
2074de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 335000) lnaband = 0x80; else
2084de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 425000) lnaband = 0x70; else
2094de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 480000) lnaband = 0x60; else
2104de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 570000) lnaband = 0x50; else
2114de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 645000) lnaband = 0x40; else
2124de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 730000) lnaband = 0x30; else
2134de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10;
2144de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2154de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[0] = REG_LO1C1;
2164de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[1] = lnaband | ((num1 >>2) & 0x0F);
2174de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[2] = div1;
2184de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[3] = (num2 & 0x0F)  | ((num1 & 3) << 4);
2194de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[4] = num2 >> 4;
2204de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[5] = ((num2 >>12) & 1) | (div2 << 1);
2214de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2224de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	dprintk("IF1: %dMHz",(int)if1);
22346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	dprintk("PLL freq=%dkHz  f_lo1=%dkHz  f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2);
22446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	dprintk("PLL div1=%d  num1=%d  div2=%d  num2=%d",(int)div1,(int)num1,(int)div2,(int)num2);
2254de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick 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]);
2264de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
22746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	mt2060_writeregs(priv,b,6);
2284de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2294de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	//Waits for pll lock or timeout
23046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	i = 0;
2314de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	do {
23246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		mt2060_readreg(priv,REG_LO_STATUS,b);
23346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		if ((b[0] & 0x88)==0x88)
23446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET			break;
2354de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(4);
2364de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		i++;
2374de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	} while (i<10);
2384de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2396e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
2406e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
2416e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
2424539fc5c68d78f2df815f426f957528011b50a08Mauro Carvalho Chehab	return 0;
2434de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
2444de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
24546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic void mt2060_calibrate(struct mt2060_priv *priv)
2464de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
2474de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8 b = 0;
2484de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int i = 0;
2494de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
25046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1)))
2514de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return;
25246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2)))
2534de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return;
2544de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
255136cafbf4a024b52ba0a10627217f03cea9ff9f8Patrick Boettcher	/* initialize the clock output */
256136cafbf4a024b52ba0a10627217f03cea9ff9f8Patrick Boettcher	mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
257136cafbf4a024b52ba0a10627217f03cea9ff9f8Patrick Boettcher
2584de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	do {
2594de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		b |= (1 << 6); // FM1SS;
26046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		mt2060_writereg(priv, REG_LO2C1,b);
2614de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(20);
2624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		if (i == 0) {
2644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			b |= (1 << 7); // FM1CA;
26546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET			mt2060_writereg(priv, REG_LO2C1,b);
2664de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			b &= ~(1 << 7); // FM1CA;
2674de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			msleep(20);
2684de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		}
2694de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2704de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		b &= ~(1 << 6); // FM1SS
27146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		mt2060_writereg(priv, REG_LO2C1,b);
2724de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(20);
2744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		i++;
2754de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	} while (i < 9);
2764de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2774de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	i = 0;
27846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0)
2794de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(20);
2804de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2817d979a86a95036a0c765aaa90f8bcefb4096a406Roel Kluin	if (i <= 10) {
28246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :)
28346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		dprintk("calibration was successful: %d", (int)priv->fmfreq);
2844de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	} else
2854de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		dprintk("FMCAL timed out");
2864de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
2874de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
28846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency)
28946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET{
29046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	struct mt2060_priv *priv = fe->tuner_priv;
29146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	*frequency = priv->frequency;
29246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	return 0;
29346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET}
29446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
295055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaaristatic int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
296055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari{
297055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari	*frequency = IF2 * 1000;
298055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari	return 0;
299055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari}
300055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari
301294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTETstatic int mt2060_init(struct dvb_frontend *fe)
302294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET{
303294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET	struct mt2060_priv *priv = fe->tuner_priv;
3046e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	int ret;
3056e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3066e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
3076e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
3086e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3096e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	ret = mt2060_writereg(priv, REG_VGAG,
3106e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari			      (priv->cfg->clock_out << 6) | 0x33);
3116e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3126e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
3136e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
3146e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3156e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	return ret;
316294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET}
317294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET
31846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_sleep(struct dvb_frontend *fe)
31946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET{
32046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	struct mt2060_priv *priv = fe->tuner_priv;
3216e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	int ret;
3226e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3236e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
3246e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
3256e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3266e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	ret = mt2060_writereg(priv, REG_VGAG,
3276e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari			      (priv->cfg->clock_out << 6) | 0x30);
3286e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3296e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
3306e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
3316e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3326e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	return ret;
33346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET}
33446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
33546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic int mt2060_release(struct dvb_frontend *fe)
33646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET{
33746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	kfree(fe->tuner_priv);
33846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	fe->tuner_priv = NULL;
33946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	return 0;
34046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET}
34146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
34246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANETstatic const struct dvb_tuner_ops mt2060_tuner_ops = {
34346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	.info = {
34446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		.name           = "Microtune MT2060",
34546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		.frequency_min  =  48000000,
34646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		.frequency_max  = 860000000,
34746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		.frequency_step =     50000,
34846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	},
34946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
35046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	.release       = mt2060_release,
35146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
352294d83d7fed1ba5a15b625500b96b3fbf60138adMatthieu CASTET	.init          = mt2060_init,
35346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	.sleep         = mt2060_sleep,
35446f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
35546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	.set_params    = mt2060_set_params,
35646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	.get_frequency = mt2060_get_frequency,
357055327c55a7b74b25bf3e178da353982e6eef487Antti Palosaari	.get_if_frequency = mt2060_get_if_frequency,
35846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET};
35946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
3604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */
3616958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcherstruct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
3624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
36346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	struct mt2060_priv *priv = NULL;
3644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8 id = 0;
3654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
36646f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL);
36746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	if (priv == NULL)
3686958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher		return NULL;
3694de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
37046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	priv->cfg      = cfg;
37146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	priv->i2c      = i2c;
37246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	priv->if1_freq = if1;
3734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
3746e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
3756e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
3766e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
37746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
37846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		kfree(priv);
3796958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher		return NULL;
38046f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	}
3814de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
38246f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	if (id != PART_REV) {
38346f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET		kfree(priv);
3846958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher		return NULL;
38546f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	}
386b7571f8d7e12cd70048331e6a0199a42dc995d99Patrick Boettcher	printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1);
38746f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops));
38846f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET
38946f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	fe->tuner_priv = priv;
3904de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
39146f73f936665ab26c8501634e6aa34464fcc1521Olivier DANET	mt2060_calibrate(priv);
3924de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
3936e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari	if (fe->ops.i2c_gate_ctrl)
3946e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
3956e623433f7f566d8aca64c519a19c2f7bbb686beAntti Palosaari
3966958effedb0dc709966c22e7fd0e8210b5401b84Patrick Boettcher	return fe;
3974de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
3984de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherEXPORT_SYMBOL(mt2060_attach);
3994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
4004de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_AUTHOR("Olivier DANET");
4014de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
4024de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_LICENSE("GPL");
403