[RFC] Analog Device ADMTV 102 silicon tuner support patch

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all,

  This ADMTV102 tuner code is grab from open sourced driver for
MyCinema U3100 Mini DMB-TH from ASUS.
I made some clean up to separate the tuner code from demod code.

  The original driver author cannot be reached, so I don't know should
I declare the copyright.
Please comment.

Regards,
David T.L. Wong
diff -r 5ec629d784ad linux/drivers/media/common/tuners/Kconfig
--- a/linux/drivers/media/common/tuners/Kconfig	Mon Jun 01 22:34:20 2009 -0300
+++ b/linux/drivers/media/common/tuners/Kconfig	Wed Jun 03 10:01:24 2009 +0800
@@ -172,4 +172,11 @@
 	help
 	  Say Y here to support the Freescale MC44S803 based tuners
 
+config MEDIA_TUNER_ADMTV102
+	tristate "Analog Device MTV 102 silicon tuner"
+	depends on VIDEO_MEDIA && I2C
+	default m if MEDIA_TUNER_CUSTOMIZE
+	help
+	  Say Y here to support the Analog Device MTV 102 silicon tuner
+
 endif # MEDIA_TUNER_CUSTOMISE
diff -r 5ec629d784ad linux/drivers/media/common/tuners/Makefile
--- a/linux/drivers/media/common/tuners/Makefile	Mon Jun 01 22:34:20 2009 -0300
+++ b/linux/drivers/media/common/tuners/Makefile	Wed Jun 03 10:01:24 2009 +0800
@@ -23,6 +23,7 @@
 obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
 obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
 obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
+obj-$(CONFIG_MEDIA_TUNER_ADMTV102) += admtv102.o
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff -r 5ec629d784ad linux/drivers/media/common/tuners/admtv102.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/common/tuners/admtv102.c	Wed Jun 03 10:01:24 2009 +0800
@@ -0,0 +1,728 @@
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+#include "admtv102.h"
+#include "admtv102_priv.h"
+
+/* define the register address and data of Tuner that
+ need to be initialized when power on */
+static const u8 init_data_uhf[100] = {
+/*Addr Data */
+ 0x10, 0x6A,      /* LNA current, 0x4C will degrade 1 dB performance but get
+		better power consumption. */
+ 0x11, 0xD8,      /* Mixer current */
+ 0x12, 0xC0,      /* Mixer gain */
+ 0x15, 0x3D,      /* TOP and ADJ to optimize SNR and ACI */
+ 0x17, 0x97,      /* TOP and ADJ to optimize SNR and ACI 9A->97,Changed TOP
+		and ADJ to optimize SNR and ACI */
+ 0x18, 0x03,      /* Changed power detector saturation voltage point and
+		warning voltage point */ /* new insert */
+ 0x1F, 0x17,      /* VCOSEL,PLLF RFPGA amp current */
+ 0x20, 0xFF,      /* RFPGA amp current */
+ 0x21, 0xA4,      /* RFPGA amp current */
+ 0x22, 0xA4,      /* RFPGA amp current */
+ 0x23, 0xDF,      /* Mixer Bias control */
+ 0x26, 0xFA,      /* PLL BUFFER CURRENT */
+ 0x27, 0x00,      /* CONVCOL/H for VCO current control */
+ 0x28, 0xFF,      /* CONVCOBUFL/H for VCO buffer amplifier current control */
+ 0x29, 0xFF,      /* CONDIV1/2 for first and second divider current control */
+ 0x2A, 0xFF,      /* CONDIV3/4 for third and last divider current control */
+ 0x2B, 0xE7,      /* CONDIV5 for third and last divider current control */
+ 0X2C, 0xFF,      /* CONBUF0/1 for L-Band Buffer amp and first Buffer amp
+		current control */
+ 0x2E, 0xFB,      /* CONBUF4 for forth Buffer amp current control */
+ 0x30, 0x80,      /* LFSW(Internal Loop Filter) to improve phase noise F8->80 */
+ 0x32, 0xc2,      /* LOOP filter boundary */
+ 0x33, 0x80,      /* DC offset control */
+ 0x34, 0xEC,      /* DC offset control */
+ 0x39, 0x96,      /* AGCHM AGC compensation value when LNA changes */
+ 0x3A, 0xA0,      /* AGCHM AGC compensation value when LNA changes */
+ 0x3B, 0x05,      /* AGCHM AGC compensation value when LNA changes */
+ 0x3C, 0xD0,      /* AGCHM AGC compensation value when LNA changes */
+ 0x44, 0xDF,      /* BBPGA needs to stay on for current silicon revision */
+ 0x48, 0x23,      /* current for output buffer amp 23->21 */
+ 0x49, 0x08,      /* gain mode for output buffer amp */
+ 0x4A, 0xA0,      /* trip point for BBVGA */
+ 0x4B, 0x9D,      /* trip point for RFPGA */
+ 0x4C, 0x9D,      /* ADJRSSI warning point */
+ 0x4D, 0xC3,      /* PLL current for stability PLL lock */
+ 0xff, 0xff
+};
+
+static const u8 init_data_vhf[100] = {
+/* Addr Data */
+ 0x10, 0x08,      /* LNA current */
+ 0x11, 0xc2,      /* Mixer current */
+ 0x12, 0xC0,      /* Mixer gain */
+ 0x17, 0x98,      /* TOP and ADJ to optimize SNR and ACI 9A->98 */
+ 0x1F, 0x17,      /* VCOSEL,PLLF RFPGA amp current */
+ 0x20, 0x9b,      /* RFPGA amp current */
+ 0x21, 0xA4,      /* RFPGA amp current */
+ 0x22, 0xA4,      /* RFPGA amp current */
+ 0x23, 0x9F,      /* Mixer Bias control */
+ 0x26, 0xF9,      /* PLL BUFFER CURRENT */
+ 0x27, 0x11,      /* CONVCOL/H for VCO current control */
+ 0x28, 0x92,      /* CONVCOBUFL/H for VCO buffer amplifier current control */
+ 0x29, 0xBC,      /* CONDIV1/2 for first and second divider current control */
+ 0x2B, 0xE7,      /* CONDIV5 for third and last divider current control */
+ 0x2D, 0x9C,
+ 0x2E, 0xCE,      /* CONBUF4 for forth Buffer amp current control */
+ 0x2F, 0x1F,
+ 0x30, 0x80,      /* LFSW(Internal Loop Filter) to improve phase noise */
+ 0x32, 0xc2,      /* LOOP filter boundary */
+ 0x33, 0x80,      /* DC offset control */
+ 0x34, 0xEC,      /* DC offset control */
+ 0x48, 0x29,      /* current for output buffer amp */
+ 0x49, 0x08,      /* gain mode for output buffer amp */
+ 0x4A, 0xA0,      /* trip point for BBVGA */
+ 0x4B, 0x9D,      /* trip point for RFPGA */
+ 0x4C, 0x9D,      /* ADJRSSI warning point */
+ 0x4D, 0xC3,      /* PLL current for stability PLL lock */
+ 0xff, 0xff
+};
+
+static const u8 pll_reg_tab[10][3] = {
+  /* 0x24, 0x31, 0x38 */
+    {0x0F, 0x04, 0x50}, /* 13MHz //0x24: 0xnB-> 0xnF */
+    {0x1F, 0x04, 0x50}, /* 16.384MHz */
+    {0x2F, 0x04, 0x50}, /* 19.2MHz */
+    {0x3F, 0x04, 0x50}, /* 20.48MHz */
+    {0x4F, 0x15, 0x51}, /* 24.576MHz */
+    {0x5F, 0x15, 0x51}, /* 26MHz */
+    {0x5A, 0x04, 0x51}, /* 30.4MHz  *//* special set for 30.4MHz case */
+    {0x6F, 0x15, 0x51}, /* 36MHz */
+    {0x7F, 0x15, 0x51}, /* 38.4MHz */
+    {0x3F, 0x04, 0x50}, /* 20MHz */
+};
+
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(args...) do { \
+	if (debug) { \
+		printk(KERN_DEBUG "admtv102: " args); \
+		printk("\n"); } \
+	} while (0)
+
+/* Reads a single register */
+static int admtv102_readreg(struct admtv102_priv *priv, u8 reg, u8 *val)
+{
+	struct i2c_msg msg[2] = {
+		{ .addr = priv->cfg->i2c_address, .flags = 0,
+			.buf = &reg, .len = 1 },
+		{ .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
+			.buf = val, .len = 1 },
+	};
+
+	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+		printk(KERN_WARNING "admtv102 I2C read failed\n");
+		return -EREMOTEIO;
+	}
+	if (debug >= 2)
+		printk(KERN_DEBUG "%s(reg=0x%02X) : 0x%02X\n",
+			__func__, reg, *val);
+	return 0;
+}
+
+/* Writes a single register */
+static int admtv102_writereg(struct admtv102_priv *priv, u8 reg, u8 val)
+{
+	u8 buf[2] = { reg, val };
+	struct i2c_msg msg = {
+		.addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
+	};
+
+	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+		printk(KERN_WARNING "admtv102 I2C write failed\n");
+		return -EREMOTEIO;
+	}
+	if (debug >= 2)
+		printk(KERN_DEBUG "%s(reg=0x%02X, val=0x%02X)\n",
+			__func__, reg, val);
+	return 0;
+}
+
+/* Writes a set of consecutive registers */
+static int admtv102_writeregs(struct admtv102_priv *priv, u8 *buf, u8 len)
+{
+	struct i2c_msg msg = {
+		.addr = priv->cfg->i2c_address, .flags = 0,
+		.buf = buf, .len = len
+	};
+	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+		printk(KERN_WARNING "admtv102 I2C write failed (len=%i)\n",
+			(int)len);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+void config_tuner(struct admtv102_priv *state, const u8 *addrdata)
+{
+	int i = 0;
+	u8 addr, data;
+
+	while (addrdata[i] != 0xFF) {
+		addr = addrdata[i++];
+		data = addrdata[i++];
+		admtv102_writereg(state, addr, data);
+	}
+}
+
+/*
+ Function:  Set Tuner LPF configuration
+ Input:
+	ref_clk_type -- Tuner PLL Type
+	lpfBW -- Band width , in MHz unit
+*/
+void set_lpf(struct admtv102_priv *state, u32 ref_clk_type, u8 lpfBW)
+{
+	u8 tuneval;
+	u8 t;
+	int ret;
+
+	admtv102_writereg(state, 0x15, (u8)(0x38  | ((lpfBW-3)&0x07)));
+	admtv102_writereg(state, 0x25 , (_EXTUNEOFF << 2) | (_TUNEEN<<1));
+	msleep(10);
+
+	tuneval = 0x10; /* default value */
+	ret = admtv102_readreg(state, 0x0F, &t);
+	if (ret == 0)
+		tuneval = t;
+
+	/* change Tuning mode : auto-tune => manual tune(hold mode). */
+	admtv102_writereg(state, 0x25 , (_EXTUNEON << 2) | (_TUNEEN << 1));
+
+	if (ref_clk_type == ADMTV102_REFCLK30400) {
+		/* Write CTUNE val. in order to store tuned value. */
+		admtv102_writereg(state, 0x25,
+			(u8)(((tuneval+state->ctune_clkofs)<<3) |
+			(_EXTUNEON << 2) | (_TUNEEN << 1))
+		);
+	} else {
+		/* Write CTUNE val. in order to store tuned value. */
+		admtv102_writereg(state, 0x25,
+			(u8)((tuneval<<3) | (_EXTUNEON << 2) |
+			(_TUNEEN << 1))
+		);
+	}
+
+    return;
+}
+
+/*
+ Tuner PLL Register Setting.
+ reg_dat -- reg set value table
+*/
+void pll_reg_set(struct admtv102_priv *state, const u8 *reg_dat, int pll_type)
+{
+	int i = 0;
+	u8 data, splitid;
+
+	if (ADMTV102_REFCLK30400 == pll_type) {
+		admtv102_readreg(state, 0x00, &splitid);
+
+		if (0x0E == splitid) {
+			/* 0x5A */
+			data = REFCLK30400_CLKSEL_REG_SPLITID0E;
+		} else if (0x0F == splitid) {
+			/* 0x6A, for mass product */
+			data = REFCLK30400_CLKSEL_REG_SPLITID0F;
+		} else
+			data = reg_dat[i];
+
+		admtv102_writereg(state, 0x24, data);
+	    i++;
+	} else {
+		data = reg_dat[i++];
+		admtv102_writereg(state, 0x24, data);
+	}
+
+	data = reg_dat[i++];
+	admtv102_writereg(state, 0x31, data);
+
+	data = reg_dat[i++];
+	admtv102_writereg(state, 0x38, data);
+}
+
+/* Distinguish Tuner Chip type by reading SplidID */
+int get_tuner_type(struct admtv102_priv *state)
+{
+	u8 splitid, type;
+
+	admtv102_readreg(state, 0x00, &splitid);
+
+	state->ctune_clkofs = CTUNE_CLKOFS_SPLIT0E;
+	switch (splitid) {
+	case 0x0E:
+		state->ctune_clkofs = CTUNE_CLKOFS_SPLIT0E;
+		type = TUNER_ADMTV102;
+		break;
+	case 0x0F:  /* for mass product version */
+		state->ctune_clkofs = CTUNE_CLKOFS_SPLIT0F;
+		type = TUNER_ADMTV102;
+		break;
+	case 0x08:
+	case 0x0A:
+		type = TUNER_MTV102;
+		break;
+	default:
+		type = TUNER_NEWMTV102;
+	}
+
+	return type;
+}
+
+void tuner_init(struct admtv102_priv *state)
+{
+	int pll_type = state->cfg->ref_clk_type;
+
+	get_tuner_type(state);
+	state->icp = 0;
+	state->convco = 0;
+	state->curTempState = HIGH_TEMP;
+	if (pll_type < 0 || pll_type > 9)
+		pll_type = 1;
+	if (state->vhf_set == VHF_SUPPORT)
+		config_tuner(state, &init_data_vhf[0]);
+	else
+		config_tuner(state, &init_data_uhf[0]);
+
+	pll_reg_set(state, &pll_reg_tab[pll_type][0], pll_type);
+	set_lpf(state, pll_type, 8);
+}
+
+/*
+ Function:  frequency setting for ADMTV102
+ Input:
+    frequency : Tuner center frequency in MHz
+    lpf_bw     : Channel Bandwidth in MHz (default: 8- 8MHZ)
+    ref_clk_type: Tuner PLL reference clock type
+ frequency setting formula
+	LOfrequency=(Clockfrequency/PLLR*(PLLN+PLLF/2^20))/PLLS;
+*/
+void set_rf_freq(struct admtv102_priv *state, u32 frequency, u32 lpf_bw)
+{
+	u32 mtv10x_refclk;
+	u32 pll_freq, Freq;
+	u32 div_sel = 0, vco_sel = 0;
+	u8 pc4 = 0, pc8_16 = 0, data47, temper;
+	u32 seg_num;
+	u8 pll_r;
+	u32 lofreq;
+	u32 pll_n, pll_f, tmp;
+	u16 multi_factor, sub_exp, div_1, div_2;
+
+	dprintk("%s(freq=%d, bw=%d)\n", __func__, frequency, lpf_bw);
+
+	state->frequency = frequency;
+	/* judge if the vhf_set has conflict with frequency value or not */
+	/* VHF is 174MHz ~ 245MHz */
+	if (frequency > 400 && VHF_SUPPORT == state->vhf_set) {
+		state->vhf_set = UHF_SUPPORT;
+		tuner_init(state);
+	} else if (frequency < 400 &&  UHF_SUPPORT == state->vhf_set) {
+		state->vhf_set = VHF_SUPPORT;
+		tuner_init(state);
+	}
+
+	pll_r = 1;
+	lofreq  = frequency * 1000;
+	div_sel  = lo2pll_freq(lofreq);
+	seg_num = (0x01 << div_sel);
+
+	if (seg_num >= 1 && seg_num <= 16) {
+		pll_freq = lofreq * (16/seg_num);
+		Freq = frequency * (16/seg_num);
+	} else {
+		pll_freq = lofreq * 2;
+		Freq = frequency * 2;
+	}
+
+	switch (state->cfg->ref_clk_type) {
+	case ADMTV102_REFCLK13000:
+		mtv10x_refclk = 130;  /* 13*multi_factor */
+		multi_factor = 10;
+		sub_exp = 1;
+		div_1 = 5;
+		div_2 = 13; /* 130=13*5*(2^1) */
+		break;
+	case ADMTV102_REFCLK16384:
+		mtv10x_refclk = 16384;
+		multi_factor = 1000;
+		sub_exp = 14;
+		div_1 = 1;
+		div_2 = 1; /* 16384== 2^14 */
+		break;
+	case ADMTV102_REFCLK19200:
+		mtv10x_refclk = 192;
+		multi_factor = 10;
+		sub_exp = 6;
+		div_1 = 1;
+		div_2 = 3; /* 192 = 2^6 *3 */
+		break;
+	case ADMTV102_REFCLK20480:
+		mtv10x_refclk = 2048;
+		multi_factor = 100;
+		sub_exp = 11;
+		div_1 = 1;
+		div_2 = 1; /*  2048 = 2^11 */
+		break;
+	case ADMTV102_REFCLK24576:
+		mtv10x_refclk = 24576;
+		multi_factor = 1000;
+		sub_exp = 13;
+		div_1 = 1;
+		div_2 = 3; /* 24576 = 2^13*3 */
+		break;
+	case ADMTV102_REFCLK26000:
+		mtv10x_refclk = 260;
+		multi_factor = 10;
+		sub_exp = 2;
+		div_1 = 5;
+		div_2 = 13; /* 260=13*5*(2^2) */
+		break;
+	case ADMTV102_REFCLK30400:
+		mtv10x_refclk = 304;
+		multi_factor = 10;
+		pll_r = 2 ;
+		set_lpf(state, ADMTV102_REFCLK30400, (u8)lpf_bw);
+		admtv102_writereg(state, 0x19, pll_r); /* Ref. Clock Divider
+		PLL0 register , bit[7:4] is reserved, bit[3:0] is PLLR */
+		sub_exp = 4;
+		div_1 = 1;
+		div_2 = 19; /* 304 = 16*19 */
+		break;
+	case ADMTV102_REFCLK36000:
+		mtv10x_refclk = 360;
+		multi_factor = 10;
+		sub_exp = 3;
+		div_1 = 5;
+		div_2 = 9; /* 360=2^3*9*5 */
+		break;
+	case ADMTV102_REFCLK38400:
+		mtv10x_refclk = 384;
+		multi_factor = 10;
+		sub_exp = 7;
+		div_1 = 1;
+		div_2 = 3; /* 384 = 2^7 *3 */
+		break;
+	case ADMTV102_REFCLK20000:
+		mtv10x_refclk = 200;
+		multi_factor = 10;
+		sub_exp = 3;
+		div_1 = 5;
+		div_2 = 5; /* 200=8*5*5 */
+		break;
+	default:
+		mtv10x_refclk = 16384;
+		multi_factor = 1000;
+		sub_exp = 14;
+		div_1 = 1;
+		div_2 = 1; /* 16384== 2^14 */
+	}
+
+	pll_n = Freq * multi_factor * pll_r / mtv10x_refclk;
+	tmp = ((pll_r * Freq * multi_factor / div_1) << (20-sub_exp)) / div_2;
+	pll_f = tmp - (pll_n << 20);
+
+	data47 = 0x10; /* default Value */
+	admtv102_readreg(state, 0x2f, &data47);
+
+	if (pll_n <= 66) {
+		/* 4-prescaler */
+		pc4    = 1;
+	} else {
+		/* 8-prescaler */
+		pc4    = 0;
+	}
+	data47 = (u8)(data47 | (pc4<<6) | (pc8_16<<5));
+
+	/* do a reset operation */
+	admtv102_writereg(state, 0x27, 0x00);
+	/* PLL Reset Enable */
+	admtv102_writereg(state, 0x2f, data47);      /* Reset Seq. 0 => 1 */
+	admtv102_writereg(state, 0x2f, (u8) (data47 | 0x80));
+
+	if ((pll_freq*2) < 2592000)  /* 648*1000*2*2 according to data sheet */
+		vco_sel = 0;
+	else
+		vco_sel = 1;
+
+	/* read 0x09 register bit [5:0] */
+	/* To get temperature sensor value */
+	temper = 0x0C;
+	admtv102_readreg(state, 0x09, &temper) ;
+	temper &= 0x3f; /* get low 6 bits */
+
+	dp_phase_tuning(state, lofreq, temper);
+	/* these value may changes: g_icp=0,
+	   state->convco=0, state->curTempState=HIGH_TEMP; */
+
+	if (lofreq > 400000) {
+		/*if iRF=UHF */
+		admtv102_writereg(state, 0x1a,
+			(u8)((state->icp << 2) | ((pll_n&0x300)>>8)));
+	} else {
+		admtv102_writereg(state, 0x1a,
+			(u8)(0xFC | ((pll_n&0x300) >> 8)));
+	}
+
+	/* PLL Setting */
+	admtv102_writereg(state, 0x1b, (u8)(pll_n & 0xFF));
+	admtv102_writereg(state, 0x1c,
+		(u8)((div_sel<<4) | (vco_sel<<7) | ((pll_f&0xF0000)>>16)));
+	admtv102_writereg(state, 0x1d, (u8)((pll_f&0x0FF00)>>8));
+	admtv102_writereg(state, 0x1e, (u8)(pll_f&0xFF));
+	admtv102_writereg(state, 0x15, (u8)(0x38 | ((lpf_bw-3)&0x07)));
+
+	/* PLL Reset */
+	admtv102_writereg(state, 0x2f, data47); /* Reset Seq. 0 => 1 */
+	admtv102_writereg(state, 0x2f, (u8)(data47 | 0x80));
+	admtv102_writereg(state, 0x2f, data47);
+
+	admtv102_writereg(state, 0x27, state->convco);
+	if (lofreq > 400000)
+		admtv102_writereg(state, 0x29, 0xBF);
+}
+
+/*
+ Function:  calculate DIVSEL according to lofreq
+ Input:   lofreq  -- Frequency point value in kHz unit
+ Return:  divsel -- DIVSEL in register 0x1C
+*/
+int  lo2pll_freq(u32 lofreq)
+{
+	u32 fdef_lo_bound_freq = 940000;
+	int seg_num, divsel = 0;
+
+	seg_num = (int)(lofreq / (fdef_lo_bound_freq / 16));
+
+	while (seg_num > 0x01) {
+		seg_num >>= 1;
+		divsel++;
+	}
+
+	return divsel;
+}
+
+/*
+ Function: Change CONVCO value according to Temperature Sensor(0x27[0:4])
+ Input: lofreq -- frequency in kHz unit
+	temper -- Tuner 0x09 register content
+*/
+void dp_phase_tuning(struct admtv102_priv *state, u32 lofreq, u8 temper)
+{
+	if (state->vhf_set == VHF_SUPPORT) {
+		if (temper <= VLOW_DEG_BOUNDARY) {
+			state->convco = RGLOW_DEG_CONVCO_VHF;
+			state->curTempState = LOW_TEMP;
+		} else  if (temper >= VHIGH_DEG_BOUNDARY) {
+			state->convco = RGHIGH_DEG_CONVCO;
+			state->curTempState = HIGH_TEMP;
+		}
+	} else {
+		if (temper <= VLOW_DEG_BOUNDARY) {
+			state->convco = RGLOW_DEG_CONDIV;
+			state->icp = 0x3F;
+			state->curTempState = LOW_TEMP;
+		} else if (temper >= VHIGH_DEG_BOUNDARY) {
+			state->convco = RGHIGH_DEG_CONDIV;
+			if ((lofreq > 610000) && (lofreq < 648000)) {
+				/*610MHz ~ 648MHz */
+				state->icp = 0x1F;
+			} else
+				state->icp = 0x3F;
+			state->curTempState = HIGH_TEMP;
+		} else  {
+			if (state->curTempState) {
+				state->convco = RGLOW_DEG_CONDIV;
+				state->icp = 0x3F;
+			}  else {
+				state->convco = RGHIGH_DEG_CONDIV;
+				if ((lofreq > 610000) &&
+					(lofreq < 648000)) {
+					/* 610MHz ~ 648MHz */
+					state->icp = 0x1F;
+				} else
+					state->icp = 0x3F;
+			}
+		}
+	}
+}
+
+/*
+ Function: Do tuner temperature compensate, it can be called by processor
+	for every 5~10 seconds. This may improve the tuner performance.
+ Input: lofreq -- frequency in kHz unit
+*/
+void temperature_compensate(struct admtv102_priv *state, long lofreq)
+{
+	u8  old_val, new_val, temper;
+
+	old_val = 0xFC;
+	temper = 0x0C;
+
+	admtv102_readreg(state, 0x09, &temper);
+	temper &= 0x3F;
+
+	dp_phase_tuning(state, lofreq, temper);
+
+	admtv102_readreg(state, 0x1A, &old_val);
+
+	/* reserve bit 7, bit1 and bit 0 */
+	new_val = (u8)((state->icp<<2) | (old_val & 0x83));
+	/* write state->icp into 0x1A register bit[6:2] */
+	admtv102_writereg(state, 0x1A, new_val);
+	admtv102_writereg(state, 0x27, state->convco);
+}
+
+int  pll_lock_check(struct admtv102_priv *state)
+{
+	int pll_lock = 0;
+	int lock, ad_out;
+	u8 t;
+
+	/* check if the tuner PLL is locked or not */
+	admtv102_readreg(state, 0x06, &t);
+	lock = ((t & 0x02) == 0x02) ? 1 : 0;
+
+	admtv102_readreg(state, 0x04, &t);
+	ad_out = t & 0x0f;
+
+	/* PLL lock cross check */
+	if (lock &&
+		((ad_out > AD_OUT_MIN) && (ad_out < AD_OUT_MAX)))
+		pll_lock = 1;
+
+	return pll_lock;
+}
+
+/* ========================================================================= */
+
+static int admtv102_set_params(struct dvb_frontend *fe,
+	struct dvb_frontend_parameters *params)
+{
+	struct admtv102_priv *priv;
+	u32 freq;
+	int i;
+	int ret = 0;
+	priv = fe->tuner_priv;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+	freq = params->frequency / 1000 / 1000; /* Hz -> MHz */
+	priv->bandwidth = (fe->ops.info.type == FE_OFDM) ?
+		params->u.ofdm.bandwidth : 0;
+
+	set_rf_freq(priv, freq, priv->bandwidth / 1000 / 1000);
+
+	/* Waits for PLL lock or timeout */
+	i = 0;
+	do {
+		if (pll_lock_check(priv))
+			break;
+		msleep(4);
+		i++;
+	} while (i < 10);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+	return ret;
+}
+
+static int admtv102_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct admtv102_priv *priv = fe->tuner_priv;
+	*frequency = priv->frequency;
+	return 0;
+}
+
+static int admtv102_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+	struct admtv102_priv *priv = fe->tuner_priv;
+	*bandwidth = priv->bandwidth;
+	return 0;
+}
+
+static int admtv102_init(struct dvb_frontend *fe)
+{
+	struct admtv102_priv *state = fe->tuner_priv;
+
+	tuner_init(state);
+	set_rf_freq(state, 746, 8);
+	return 0;
+}
+
+static int admtv102_sleep(struct dvb_frontend *fe)
+{
+	return 0;
+}
+
+static int admtv102_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static const struct dvb_tuner_ops admtv102_tuner_ops = {
+	.info = {
+		.name           = "Analog Device ADMTV102",
+		.frequency_min  =  48000000,
+		.frequency_max  = 860000000,
+		.frequency_step =     50000,
+	},
+
+	.release       = admtv102_release,
+
+	.init          = admtv102_init,
+	.sleep         = admtv102_sleep,
+
+	.set_params    = admtv102_set_params,
+	.get_frequency = admtv102_get_frequency,
+	.get_bandwidth = admtv102_get_bandwidth
+};
+
+struct dvb_frontend *admtv102_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, struct admtv102_config *cfg)
+{
+	struct admtv102_priv *priv = NULL;
+	u8 id = 0;
+
+	priv = kzalloc(sizeof(struct admtv102_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->cfg      = cfg;
+	priv->i2c      = i2c;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+	if (admtv102_readreg(priv, 0, &id) != 0) {
+		kfree(priv);
+		return NULL;
+	}
+
+	memcpy(&fe->ops.tuner_ops, &admtv102_tuner_ops,
+		sizeof(struct dvb_tuner_ops));
+
+	fe->tuner_priv = priv;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+	return fe;
+}
+EXPORT_SYMBOL(admtv102_attach);
+
+MODULE_AUTHOR("David T.L. Wong");
+MODULE_DESCRIPTION("Analog Device ADMTV102 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff -r 5ec629d784ad linux/drivers/media/common/tuners/admtv102.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/common/tuners/admtv102.h	Wed Jun 03 10:01:24 2009 +0800
@@ -0,0 +1,58 @@
+/*
+ *  Driver for Analog Device ADMTV102 silicon tuner
+ *
+ *  Copyright (c) 2006 Olivier DANET <odanet@xxxxxxxxxxxx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef ADMTV102_H
+#define ADMTV102_H
+
+#define ADMTV102_REFCLK13000  0
+#define ADMTV102_REFCLK16384  1
+#define ADMTV102_REFCLK19200  2
+#define ADMTV102_REFCLK20480  3
+#define ADMTV102_REFCLK24576  4
+#define ADMTV102_REFCLK26000  5
+#define ADMTV102_REFCLK30400  6
+#define ADMTV102_REFCLK36000  7
+#define ADMTV102_REFCLK38400  8
+#define ADMTV102_REFCLK20000  9
+
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct admtv102_config {
+	u8 i2c_address;
+	u8 ref_clk_type;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_ADMTV102) || \
+ (defined(CONFIG_MEDIA_TUNER_ADMTV102_MODULE) && defined(MODULE))
+extern struct dvb_frontend *admtv102_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, struct admtv102_config *cfg);
+#else
+static inline struct dvb_frontend *admtv102_attach(struct dvb_frontend *fe,
+	struct i2c_adapter *i2c, struct admtv102_config *cfg)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_MEDIA_TUNER_ADMTV102 */
+
+#endif
diff -r 5ec629d784ad linux/drivers/media/common/tuners/admtv102_priv.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linux/drivers/media/common/tuners/admtv102_priv.h	Wed Jun 03 10:01:24 2009 +0800
@@ -0,0 +1,54 @@
+struct admtv102_priv {
+	struct admtv102_config *cfg;
+	struct i2c_adapter   *i2c;
+
+	u32 frequency;
+	u32 bandwidth;
+
+	int vhf_set;
+	u8 icp;
+	u8 convco;
+	u8 curTempState;
+	u8 ctune_clkofs;
+};
+
+#define TUNER_MTV102    0x00
+#define TUNER_ADMTV102  0x01
+#define TUNER_NEWMTV102 0x02
+
+#define  VHF_SUPPORT               1
+#define  UHF_SUPPORT               0
+
+#define VLOW_DEG_BOUNDARY    7
+#define RGLOW_DEG_CONDIV     0xEC
+#define VHIGH_DEG_BOUNDARY   9
+#define RGHIGH_DEG_CONDIV    0x00
+#define HIGH_TEMP 0
+#define LOW_TEMP  1
+#define RGLOW_DEG_CONVCO_VHF     0x9D
+#define RGHIGH_DEG_CONVCO        0x00
+
+#define _EXTUNEOFF   0
+#define _EXTUNEON    1
+#define _TUNEEN      1
+#define _TUNEDIS     0
+#define CTUNEOFS     0x01  /* default = 0 */
+
+#define CTUNE_CLKOFS_SPLIT0E   0x09
+#define CTUNE_CLKOFS_SPLIT0F   0x00 /* for mass product */
+#define REFCLK30400_CLKSEL_REG_SPLITID0E   0x5A   /* for split ID 0x0e */
+#define REFCLK30400_CLKSEL_REG_SPLITID0F   0x6A   /* for split ID is 0x0f */
+
+#define  AD_OUT_MIN  2
+#define  AD_OUT_MAX  12
+
+void config_tuner(struct admtv102_priv *state, const u8 *addrdata);
+void tuner_init(struct admtv102_priv *state);
+int get_tuner_type(struct admtv102_priv *state);
+void set_rf_freq(struct admtv102_priv *state, u32 frequency, u32 lpfBW);
+void set_lpf(struct admtv102_priv *state, u32 ref_clk_type, u8 lpfBW);
+int lo2pll_freq(u32 lofreq);
+void pll_reg_set(struct admtv102_priv *state, const u8 *reg_dat, int pll_type);
+void dp_phase_tuning(struct admtv102_priv *state, u32 lofreq, u8 temper);
+void temperature_compensate(struct admtv102_priv *state, long lofreq);
+int pll_lock_check(struct admtv102_priv *state);

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux