mt2060.c revision 4de2730a1d2742aea67f24d1041bdc5e0bad37e3
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/* See mt2060_priv.h for details */
234de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
244de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
254de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
264de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include <linux/module.h>
274de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include <linux/moduleparam.h>
284de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include <linux/delay.h>
294de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include <linux/dvb/frontend.h>
304de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include "mt2060.h"
314de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#include "mt2060_priv.h"
324de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
334de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int debug=0;
344de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettchermodule_param(debug, int, 0644);
354de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
364de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
374de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#define dprintk(args...) do { if (debug) printk(KERN_DEBUG "MT2060: " args); printk("\n"); } while (0)
384de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
394de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// Reads a single register
404de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int mt2060_readreg(struct mt2060_state *state, u8 reg, u8 *val)
414de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
424de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	struct i2c_msg msg[2] = {
434de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		{ .addr = state->config->i2c_address, .flags = 0,        .buf = &reg, .len = 1 },
444de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		{ .addr = state->config->i2c_address, .flags = I2C_M_RD, .buf = val,  .len = 1 },
454de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	};
464de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
474de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (i2c_transfer(state->i2c, msg, 2) != 2) {
484de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		printk(KERN_WARNING "mt2060 I2C read failed\n");
494de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return -EREMOTEIO;
504de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	}
514de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	return 0;
524de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
534de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
544de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher// Writes a single register
554de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int mt2060_writereg(struct mt2060_state *state, u8 reg, u8 val)
564de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
574de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8 buf[2];
584de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	struct i2c_msg msg = {
594de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		.addr = state->config->i2c_address, .flags = 0, .buf = buf, .len = 2
604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	};
614de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	buf[0]=reg;
624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	buf[1]=val;
634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (i2c_transfer(state->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
724de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int mt2060_writeregs(struct mt2060_state *state,u8 *buf, u8 len)
734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	struct i2c_msg msg = {
754de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		.addr = state->config->i2c_address, .flags = 0, .buf = buf, .len = len
764de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	};
774de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (i2c_transfer(state->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 Boettcherstatic u8 mt2060_config3[] = {
994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	REG_VGAG,
1004de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	0x33
1014de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher};
1024de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1034de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherint mt2060_init(struct mt2060_state *state)
1044de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
1054de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (mt2060_writeregs(state,mt2060_config1,sizeof(mt2060_config1)))
1064de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return -EREMOTEIO;
1074de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (mt2060_writeregs(state,mt2060_config3,sizeof(mt2060_config3)))
1084de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return -EREMOTEIO;
1094de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	return 0;
1104de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
1114de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherEXPORT_SYMBOL(mt2060_init);
1124de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1134de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#ifdef  MT2060_SPURCHECK
1144de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* The function below calculates the frequency offset between the output frequency if2
1154de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */
1164de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2)
1174de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
1184de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int I,J;
1194de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int dia,diamin,diff;
1204de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	diamin=1000000;
1214de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	for (I = 1; I < 10; I++) {
1224de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		J = ((2*I*lo1)/lo2+1)/2;
1234de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		diff = I*(int)lo1-J*(int)lo2;
1244de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		if (diff < 0) diff=-diff;
1254de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		dia = (diff-(int)if2);
1264de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		if (dia < 0) dia=-dia;
1274de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		if (diamin > dia) diamin=dia;
1284de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	}
1294de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	return diamin;
1304de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
1314de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1324de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#define BANDWIDTH 4000 // kHz
1334de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1344de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */
1354de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2)
1364de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
1374de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 Spur,Sp1,Sp2;
1384de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int I,J;
1394de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	I=0;
1404de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	J=1000;
1414de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1424de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	Spur=mt2060_spurcalc(lo1,lo2,if2);
1434de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (Spur < BANDWIDTH) {
1444de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		/* Potential spurs detected */
1454de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		dprintk("Spurs before : f_lo1: %d  f_lo2: %d  (kHz)",
1464de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			(int)lo1,(int)lo2);
1474de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		I=1000;
1484de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2);
1494de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2);
1504de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1514de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		if (Sp1 < Sp2) {
1524de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			J=-J; I=-I; Spur=Sp2;
1534de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		} else
1544de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			Spur=Sp1;
1554de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1564de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		while (Spur < BANDWIDTH) {
1574de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			I += J;
1584de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			Spur = mt2060_spurcalc(lo1+I,lo2+I,if2);
1594de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		}
1604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		dprintk("Spurs after  : f_lo1: %d  f_lo2: %d  (kHz)",
1614de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			(int)(lo1+I),(int)(lo2+I));
1624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	}
1634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	return I;
1644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
1654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#endif
1664de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1674de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#define IF2  36150       // IF2 frequency = 36.150 MHz
1684de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#define FREF 16000       // Quartz oscillator 16 MHz
1694de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1704de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherint mt2060_set(struct mt2060_state *state, struct dvb_frontend_parameters *fep)
1714de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
1724de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int ret=0;
1734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int i=0;
1744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 freq;
1754de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8  lnaband;
1764de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 f_lo1,f_lo2;
1774de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 div1,num1,div2,num2;
1784de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8  b[8];
1794de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u32 if1;
1804de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1814de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if1 = state->if1_freq;
1824de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[0] = REG_LO1B1;
1834de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[1] = 0xFF;
1844de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	mt2060_writeregs(state,b,2);
1854de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1864de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	freq = fep->frequency / 1000; // Hz -> kHz
1874de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1884de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo1 =  freq + if1 * 1000;
1894de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo1 = (f_lo1/250)*250;
1904de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo2 =  f_lo1 - freq - IF2;
1914de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo2 = (f_lo2/50)*50;
1924de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
1934de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#ifdef MT2060_SPURCHECK
1944de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	// LO-related spurs detection and correction
1954de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	num1   = mt2060_spurcheck(f_lo1,f_lo2,IF2);
1964de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo1 += num1;
1974de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	f_lo2 += num1;
1984de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher#endif
1994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	//Frequency LO1 = 16MHz * (DIV1 + NUM1/64 )
2004de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	div1 = f_lo1 / FREF;
2014de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	num1 = (64 * (f_lo1 % FREF)  )/FREF;
2024de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2034de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	// Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 )
2044de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	div2 = f_lo2 / FREF;
2054de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	num2 = (16384 * (f_lo2 % FREF) /FREF +1)/2;
2064de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2074de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <=  95000) lnaband = 0xB0; else
2084de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 180000) lnaband = 0xA0; else
2094de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 260000) lnaband = 0x90; else
2104de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 335000) lnaband = 0x80; else
2114de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 425000) lnaband = 0x70; else
2124de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 480000) lnaband = 0x60; else
2134de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 570000) lnaband = 0x50; else
2144de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 645000) lnaband = 0x40; else
2154de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 730000) lnaband = 0x30; else
2164de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10;
2174de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2184de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[0] = REG_LO1C1;
2194de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[1] = lnaband | ((num1 >>2) & 0x0F);
2204de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[2] = div1;
2214de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[3] = (num2 & 0x0F)  | ((num1 & 3) << 4);
2224de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[4] = num2 >> 4;
2234de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	b[5] = ((num2 >>12) & 1) | (div2 << 1);
2244de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2254de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	dprintk("IF1: %dMHz",(int)if1);
2264de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	dprintk("PLL freq: %d  f_lo1: %d  f_lo2: %d  (kHz)",(int)freq,(int)f_lo1,(int)f_lo2);
2274de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	dprintk("PLL div1: %d  num1: %d  div2: %d  num2: %d",(int)div1,(int)num1,(int)div2,(int)num2);
2284de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick 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]);
2294de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2304de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	mt2060_writeregs(state,b,6);
2314de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2324de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	//Waits for pll lock or timeout
2334de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	i=0;
2344de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	do {
2354de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		mt2060_readreg(state,REG_LO_STATUS,b);
2364de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		if ((b[0] & 0x88)==0x88) break;
2374de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(4);
2384de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		i++;
2394de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	} while (i<10);
2404de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2414de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	return ret;
2424de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
2434de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherEXPORT_SYMBOL(mt2060_set);
2444de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2454de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* from usbsnoop.log */
2464de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherstatic void mt2060_calibrate(struct mt2060_state *state)
2474de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
2484de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8 b = 0;
2494de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	int i = 0;
2504de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2514de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (mt2060_writeregs(state,mt2060_config1,sizeof(mt2060_config1)))
2524de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return;
2534de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (mt2060_writeregs(state,mt2060_config2,sizeof(mt2060_config2)))
2544de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return;
2554de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2564de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	do {
2574de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		b |= (1 << 6); // FM1SS;
2584de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		mt2060_writereg(state, REG_LO2C1,b);
2594de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(20);
2604de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2614de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		if (i == 0) {
2624de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			b |= (1 << 7); // FM1CA;
2634de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			mt2060_writereg(state, REG_LO2C1,b);
2644de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			b &= ~(1 << 7); // FM1CA;
2654de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher			msleep(20);
2664de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		}
2674de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2684de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		b &= ~(1 << 6); // FM1SS
2694de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		mt2060_writereg(state, REG_LO2C1,b);
2704de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2714de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(20);
2724de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		i++;
2734de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	} while (i < 9);
2744de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2754de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	i = 0;
2764de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	while (i++ < 10 && mt2060_readreg(state, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0)
2774de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		msleep(20);
2784de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2794de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (i < 10) {
2804de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		mt2060_readreg(state, REG_FM_FREQ, &state->fmfreq); // now find out, what is fmreq used for :)
2814de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		dprintk("calibration was successful: %d",state->fmfreq);
2824de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	} else
2834de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		dprintk("FMCAL timed out");
2844de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
2854de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2864de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */
2874de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcherint mt2060_attach(struct mt2060_state *state, struct mt2060_config *config, struct i2c_adapter *i2c,u16 if1)
2884de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher{
2894de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	u8 id = 0;
2904de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	memset(state,0,sizeof(struct mt2060_state));
2914de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2924de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	state->config = config;
2934de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	state->i2c = i2c;
2944de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	state->if1_freq = if1;
2954de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2964de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (mt2060_readreg(state,REG_PART_REV,&id) != 0)
2974de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return -ENODEV;
2984de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
2994de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	if (id != PART_REV)
3004de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher		return -ENODEV;
3014de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
3024de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	printk(KERN_INFO "MT2060: successfully identified\n");
3034de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
3044de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	mt2060_calibrate(state);
3054de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
3064de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher	return 0;
3074de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher}
3084de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherEXPORT_SYMBOL(mt2060_attach);
3094de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick Boettcher
3104de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_AUTHOR("Olivier DANET");
3114de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
3124de2730a1d2742aea67f24d1041bdc5e0bad37e3Patrick BoettcherMODULE_LICENSE("GPL");
313