  /*
     Driver for Philips tda8262/tda8263 DVBS Silicon tuners

     (c) 2006 Andrew de Quincey

     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.

  */

#include <linux/module.h>
#include <linux/dvb/frontend.h>
#include <asm/types.h>

#include "tda18252.h"

static int debug = 0;

unsigned char red_buf[59];
u8 reg_buf[59];

u8 initbuf [] =
  {
  	/*0x05,*//*subaddr*/
  	0x00,0x00,0xd0,0x00,0x40,0x00,0x00,0x07,0xff,0x84,0x09,
  	0x00,0x13,0x00,0x00,0x01,0x84,0x09,0xf0,0x59,0x0a,0x8e,0x6b,0x98,0x01,0x00,0x50,
  	0x10,0x40,0x80,0x00,0x0c,0x48,0x80,0xc9,0xa7,0x00,0x08,0x00,0x30,0x81,0x80,0x00,
  	0x39,0x00,0x8a,0x00,0x00,0x00,0x00,0x00,0x00,0xf6,0xf6,
  };

   u32 lo_tab[][3] =
      {                     // MAIN_PLL_Map uLO_Max, uPost_Div, uDiv
	      {33000, 0x57, 0xF0},
				{36000, 0x56, 0xE0},
				{38000, 0x55, 0xD0},
				{42000, 0x54, 0xC0},
				{45000, 0x53, 0xB0},
				{50000, 0x52, 0xA0},
				{56000, 0x51, 0x90},
				{63000, 0x50, 0x80},
				{67000, 0x47, 0x78},
				{72000, 0x46, 0x70},
				{77000, 0x45, 0x68},
				{84000, 0x44, 0x60},
				{91000, 0x43, 0x58},
				{100000, 0x42, 0x50},
				{112000, 0x41, 0x48},
				{126000, 0x40, 0x40},
				{134000, 0x37, 0x3C},
				{144000, 0x36, 0x38},
				{155000, 0x35, 0x34},
				{168000, 0x34, 0x30},
				{183000, 0x33, 0x2C},
				{201000, 0x32, 0x28},
				{224000, 0x31, 0x24},
				{252000, 0x30, 0x20},
				{268000, 0x27, 0x1E},
				{288000, 0x26, 0x1C},
				{310000, 0x25, 0x1A},
				{336000, 0x24, 0x18},
				{366000, 0x23, 0x16},
				{403000, 0x22, 0x14},
				{448000, 0x21, 0x12},
				{504000, 0x20, 0x10},
				{537000, 0x17, 0x0F},
				{576000, 0x16, 0x0E},
				{620000, 0x15, 0x0D},
				{672000, 0x14, 0x0C},
				{733000, 0x13, 0x0B},
				{806000, 0x12, 0x0A},
				{896000, 0x11, 0x09},
				{1008000, 0x10, 0x08},
      };

  // AGC1_TOP_Map uGu, uGd, uAd15, uAd12, uAd9, uAd6, uAu3, uAu6, uAu9, uAu12
	u8 agc1_top_tab[10] =	{0x09, 0x03, 0x05, 0x05, 0x05, 0x03, 0x01, 0x03, 0x06, 0x04};// hybrid

  u8 agc2_top_tab[8][6]=
      {							// AGC2_TOP_Map uGu2, uGd2, uGu3, uGd3, uGu4, uGd4
				{0x09, 0x08, 0x17, 0x16, 0x0C, 0x0B}, // Filter 0
				{0x09, 0x08, 0x17, 0x16, 0x0C, 0x0B}, // Filter 1
				{0x09, 0x08, 0x17, 0x16, 0x0C, 0x0B}, // Filter 2
				{0x09, 0x08, 0x15, 0x14, 0x0C, 0x0B}, // Filter 3
				{0x09, 0x08, 0x15, 0x14, 0x0C, 0x0B}, // Filter 4
				{0x09, 0x08, 0x13, 0x12, 0x0C, 0x0B}, // Filter 5
				{0x09, 0x08, 0x09, 0x08, 0x0C, 0x0B}, // Filter 6
				{0x09, 0x08, 0x09, 0x08, 0x0C, 0x0B}, // Filter 7
			};

  u32 IR_Map_uRF[4] = {123000,380000,780000,1100000,};           //IR map uRF_max
  u8 IRCoefHighF[4] = {0, 0, 0, 0};					// IRCoefHighF[4]
							// IR_Rx_Map uR2I, uR2Q, uR3IQ, uR3InQ
	//u8 IR_Rx_Map[4] = {0x00, 0x00, 0x00, 0x00};


#define dprintk(args...) \
	do { \
		if (debug) printk(KERN_DEBUG "tda18252: " args); \
	} while (0)

bool m_tmTDA18252AGC1WorkAround = false;
bool m_tmTDA18251IRCalDebug   =   false;

struct tda18252_priv {
	/* i2c details */
	int i2c_address;
	struct i2c_adapter *i2c;
	u8 has_loopthrough:1;
	u32 frequency;
};

static int TDA18252Write(struct dvb_frontend *fe,u8 subaddr,u8 *buf,int lenth)
{
	struct tda18252_priv *priv = fe->tuner_priv;
	int ret;
	u8 write_buf[lenth+1];
	struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = write_buf, .len = (lenth+1) };
	write_buf[0] = subaddr;
	memcpy((write_buf+1),buf,lenth);

	if (fe->ops.i2c_gate_ctrl)
		      fe->ops.i2c_gate_ctrl(fe, 1);
	if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
		      dprintk("%s: i2c write error\n", __FUNCTION__);
		      return -1;
	       }
	if (fe->ops.i2c_gate_ctrl)
		      fe->ops.i2c_gate_ctrl(fe, 0);

  return 1;
}

static int TDA18252Read(struct dvb_frontend *fe,u8 *buf,int lenth)
{
	struct tda18252_priv *priv = fe->tuner_priv;
	struct i2c_msg msg = { .addr = priv->i2c_address, .flags = I2C_M_RD, .buf = buf, .len = lenth };
	int ret;

	if((lenth!=23)&&(lenth!=40)&&(lenth!=59))
		{
			printk("Read data length error");
			return -1;
		}
	if (fe->ops.i2c_gate_ctrl)
		      fe->ops.i2c_gate_ctrl(fe, 1);
	if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
		      dprintk("%s: i2c write error\n", __FUNCTION__);
		      return -1;
	       }
	if (fe->ops.i2c_gate_ctrl)
		      fe->ops.i2c_gate_ctrl(fe, 0);

  return 1;
}

static int TDA18252IRCalibration(struct dvb_frontend *fe)
{
	u32 uRF;
	u8 index;
	u8 RSSI = 0;
	u8 AGC_ON=0;
	u8 CAL_INT=0;
	u8 POW_DOWN1=0;
	u8 POW_DOWN2=0;
	u8 AGC2b=0;
	u8 pdAGC2a=0;
	u8 Freq_pro_en=0;
	u8 RFcal_mode_off=0;

	AGC_ON = red_buf[0x1c];
	pdAGC2a = red_buf[0x1b];
	RSSI = red_buf[0x27];
	AGC_ON = ((AGC_ON>>4)&0x01);
	CAL_INT = red_buf[0x11];
	POW_DOWN1 = red_buf[0x17];
	POW_DOWN2 = red_buf[0x18];
	AGC2b = red_buf[0x1c];
	pdAGC2a = ((pdAGC2a>>5)&0x01);
	Freq_pro_en = red_buf[0x07];
	Freq_pro_en = ((Freq_pro_en>>6)&0x01);
	//RFcal_mode_off = (red_buf[0x2d]&0x01);
	RFcal_mode_off = red_buf[0x2d];
	RFcal_mode_off = (RFcal_mode_off&0x01);

	reg_buf[0x07] = (red_buf[0x07]|0x40);
	while(TDA18252Write(fe,0x07,(reg_buf+0x07),1)<=0);
	reg_buf[0x1b] = (red_buf[0x1b]|0x20);
	while(TDA18252Write(fe,0x1b,(reg_buf+0x1b),1)<=0);
	reg_buf[0x2d] = (red_buf[0x2d]|0x01);
	while(TDA18252Write(fe,0x2d,(reg_buf+0x2d),1)<=0);

	reg_buf[0x27] = 0xde;
	while(TDA18252Write(fe,0x27,(reg_buf+0x27),1)<=0);
	reg_buf[0x17] = 0xf8;
	while(TDA18252Write(fe,0x17,(reg_buf+0x17),1)<=0);
	reg_buf[0x18] = 0x0f;
	while(TDA18252Write(fe,0x18,(reg_buf+0x18),1)<=0);
	reg_buf[0x1c] = 0x8b;
	while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);

	if(!m_tmTDA18251IRCalDebug)
		{
		  reg_buf[0x28] = ((red_buf[0x28]&0x88)|0x27);
		  reg_buf[0x29] = (red_buf[0x29]&0x8f);
		  reg_buf[0x2a] = (red_buf[0x2a]&0xcf);
		  while(TDA18252Write(fe,0x28,(reg_buf+0x28),3)<=0);
		}
	//first frequency Reference
	reg_buf[0x29] = ((reg_buf[0x29]&0xfc)|0x01);
	while(TDA18252Write(fe,0x29,(reg_buf+0x29),1)<=0);
	reg_buf[0x19]	= ((red_buf[0x19]&0x0f)|0x10);
	while(TDA18252Write(fe,0x19,(reg_buf+0x19),1)<=0);
	reg_buf[0x11] = 0x13;
	while(TDA18252Write(fe,0x11,(reg_buf+0x11),1)<=0);

	uRF = IR_Map_uRF[0];
	reg_buf[0x0a] = (u8)(uRF >> 12);
	reg_buf[0x0b] = (u8)(uRF >> 4);
	reg_buf[0x0c] = (((u8)(uRF<<4))|0x07);
	while(TDA18252Write(fe,0x0a,(reg_buf+0x0a),3)<=0);
	reg_buf[0x0f] = (red_buf[0x0f]|0x40);
	while(TDA18252Write(fe,0x0f,(reg_buf+0x0f),1)<=0);

	msleep(2);
	reg_buf[0x05] = (reg_buf[0x05]|0x40);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xbf);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]|0x20);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xdf);
	//while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	msleep(1);
	while(TDA18252Read(fe,red_buf,59)<=0);
	if(!(red_buf[0x05]&0x10))
		{
			printk("Reset MSM error\n");
			return -1;
		}

	//image
	reg_buf[0x11] = 0xed;
	while(TDA18252Write(fe,0x11,(reg_buf+0x11),1)<=0);
	reg_buf[0x0f] = (red_buf[0x0f]|0x40);
	while(TDA18252Write(fe,0x0f,(reg_buf+0x0f),1)<=0)
	msleep(5);
	reg_buf[0x19]	= ((red_buf[0x19]&0x0f)|0x20);
	while(TDA18252Write(fe,0x19,(reg_buf+0x19),1)<=0);

	reg_buf[0x05] = (red_buf[0x05]|0x40);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xbf);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]|0x20);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xdf);
	//while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	msleep(40);

	while(TDA18252Read(fe,red_buf,59)<=0);
	if(!(red_buf[0x05]&0x10))
		{
			printk("Reset MSM error1\n");
			return -1;
		}


	//second frequency Reference
	reg_buf[0x29] = ((reg_buf[0x29]&0xfc)|0x02);
	while(TDA18252Write(fe,0x29,(reg_buf+0x29),1)<=0);
	reg_buf[0x19]	= ((red_buf[0x19]&0x0f)|0x10);
	while(TDA18252Write(fe,0x19,(reg_buf+0x19),1)<=0);
	reg_buf[0x11] = 0x13;
	while(TDA18252Write(fe,0x11,(reg_buf+0x11),1)<=0);

	uRF = IR_Map_uRF[1];
	reg_buf[0x0a] = (u8)(uRF >> 12);
	reg_buf[0x0b] = (u8)(uRF >> 4);
	reg_buf[0x0c] = (((u8)(uRF<<4))|0x07);
	while(TDA18252Write(fe,0x0a,(reg_buf+0x0a),3)<=0);
	reg_buf[0x0f] = (red_buf[0x0f]|0x40);
	while(TDA18252Write(fe,0x0f,(reg_buf+0x0f),1)<=0);

	msleep(2);
	reg_buf[0x05] = (reg_buf[0x05]|0x40);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xbf);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]|0x20);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xdf);
	//while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	msleep(2);
	while(TDA18252Read(fe,red_buf,59)<=0);
	if(!(red_buf[0x05]&0x10))
		{
			printk("Reset MSM error2\n");
			return -1;
		}

	//image
	reg_buf[0x11] = 0xed;
	while(TDA18252Write(fe,0x11,(reg_buf+0x11),1)<=0);
	reg_buf[0x0f] = (red_buf[0x0f]|0x40);
	while(TDA18252Write(fe,0x0f,(reg_buf+0x0f),1)<=0)
	msleep(5);
	reg_buf[0x19]	= ((red_buf[0x19]&0x0f)|0x20);
	while(TDA18252Write(fe,0x19,(reg_buf+0x19),1)<=0);

	reg_buf[0x05] = (red_buf[0x05]|0x40);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xbf);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]|0x20);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xdf);
	//while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);

	msleep(40);

	while(TDA18252Read(fe,red_buf,59)<=0);
	if(!(red_buf[0x05]&0x10))
		{
			printk("Reset MSM error3\n");
			return -1;
		}

	//third frequency Reference
	reg_buf[0x29] = ((reg_buf[0x29]&0xfc)|0x03);
	while(TDA18252Write(fe,0x29,(reg_buf+0x29),1)<=0);
	reg_buf[0x19]	= ((red_buf[0x19]&0x0f)|0x10);
	while(TDA18252Write(fe,0x19,(reg_buf+0x19),1)<=0);
	reg_buf[0x11] = 0x13;
	while(TDA18252Write(fe,0x11,(reg_buf+0x11),1)<=0);

	uRF = IR_Map_uRF[2];
	reg_buf[0x0a] = (u8)(uRF >> 12);
	reg_buf[0x0b] = (u8)(uRF >> 4);
	reg_buf[0x0c] = (((u8)(uRF<<4))|0x07);
	while(TDA18252Write(fe,0x0a,(reg_buf+0x0a),3)<=0);
	reg_buf[0x0f] = (red_buf[0x0f]|0x40);
	while(TDA18252Write(fe,0x0f,(reg_buf+0x0f),1)<=0);

	msleep(2);
	reg_buf[0x05] = (reg_buf[0x05]|0x40);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xbf);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]|0x20);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xdf);
	//while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	msleep(2);
	while(TDA18252Read(fe,red_buf,59)<=0);
	if(!(red_buf[0x05]&0x10))
		{
			printk("Reset MSM error4\n");
			return -1;
		}

	//image
	reg_buf[0x11] = 0xed;
	while(TDA18252Write(fe,0x11,(reg_buf+0x11),1)<=0);
	reg_buf[0x0f] = (red_buf[0x0f]|0x40);
	while(TDA18252Write(fe,0x0f,(reg_buf+0x0f),1)<=0)
	msleep(5);
	reg_buf[0x19]	= ((red_buf[0x19]&0x0f)|0x20);
	while(TDA18252Write(fe,0x19,(reg_buf+0x19),1)<=0);

	reg_buf[0x05] = (red_buf[0x05]|0x40);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xbf);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]|0x20);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	reg_buf[0x05] = (reg_buf[0x05]&0xdf);
	//while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);

	msleep(40);

	while(TDA18252Read(fe,red_buf,59)<=0);
	if(!(red_buf[0x05]&0x10))
		{
			printk("Reset MSM error5\n");
			return -1;
		}
	reg_buf[0x19]	= (red_buf[0x19]&0x0f);
	while(TDA18252Write(fe,0x19,(reg_buf+0x19),1)<=0);

	reg_buf[0x27] = RSSI;
	while(TDA18252Write(fe,0x27,(reg_buf+0x27),1)<=0);
	reg_buf[0x1c] = ((red_buf[0x1c]&0xef)|(AGC_ON<<4));
	while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);
	reg_buf[0x11] = CAL_INT;
	while(TDA18252Write(fe,0x11,(reg_buf+0x11),1)<=0);
	reg_buf[0x17] = POW_DOWN1;
	while(TDA18252Write(fe,0x17,(reg_buf+0x17),1)<=0);
	reg_buf[0x18] = POW_DOWN2;
	while(TDA18252Write(fe,0x18,(reg_buf+0x18),1)<=0);
	reg_buf[0x1c] = AGC2b;
	while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);
	reg_buf[0x1b] = ((red_buf[0x1b]&0xdf)|(pdAGC2a<<5));
	while(TDA18252Write(fe,0x1b,(reg_buf+0x1b),1)<=0);
	reg_buf[0x07] = ((red_buf[0x07]&0xbf)|(Freq_pro_en<<6));
	while(TDA18252Write(fe,0x08,(reg_buf+0x08),1)<=0);
  reg_buf[0x2d] = ((red_buf[0x2d]&0xfe)|RFcal_mode_off);
  while(TDA18252Write(fe,0x2d,(reg_buf+0x2d),1)<=0);

  reg_buf[0x29] = ((reg_buf[0x29]&0xfc)|0x03);
	while(TDA18252Write(fe,0x29,(reg_buf+0x29),1)<=0);
	for(index=0;index<4;index++)
	  {
	  	reg_buf[0x2a] = ((red_buf[0x2a]&0xfc)|((u8)(index)));
	  	//printk("********************** reg_buf[0x2a]=%02x\n",reg_buf[0x2a]);
	  	while(TDA18252Write(fe,0x2a,(reg_buf+0x2a),1)<=0);
	  	while(TDA18252Read(fe,red_buf,59)<=0);
	  	IRCoefHighF[index] = (red_buf[0x2b]&0x3f);
	  }
	return 1;
}

static int TDA18252WriteAGC1_TOP(struct dvb_frontend *fe)
{
	while(TDA18252Read(fe,red_buf,59)<=0);
	msleep(1);
	reg_buf[0x20] = (red_buf[0x20]|0x10);                                //uGu
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);
	reg_buf[0x21] = ((red_buf[0x21]&0xe0)|agc1_top_tab[0]);
	while(TDA18252Write(fe,0x21,(reg_buf+0x21),1)<=0);

	reg_buf[0x20] = (reg_buf[0x20]&0xef);                                //uGd
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);
	reg_buf[0x21] = ((reg_buf[0x21]&0xe0)|agc1_top_tab[1]);
	while(TDA18252Write(fe,0x21,(reg_buf+0x21),1)<=0);

	reg_buf[0x1f] = ((red_buf[0x1f]&0xf8)|0x03);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[9]);               //uAu12
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	reg_buf[0x1f] = (reg_buf[0x1f]&0xf8);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[6]);               //uAu3
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	reg_buf[0x1f] = ((reg_buf[0x1f]&0xf8)|0x01);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[7]);               //uAu6
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	reg_buf[0x1f] = ((reg_buf[0x1f]&0xf8)|0x02);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[8]);               //uAu9
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	reg_buf[0x1f] = ((reg_buf[0x1f]&0xf8)|0x07);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[3]);               //uAd12
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	reg_buf[0x1f] = ((reg_buf[0x1f]&0xf8)|0x04);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[5]);               //uAd6
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	reg_buf[0x1f] = ((reg_buf[0x1f]&0xf8)|0x05);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[4]);               //uAd9
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	reg_buf[0x1f] = ((reg_buf[0x1f]&0xf8)|0x06);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x20] = ((reg_buf[0x20]&0xf8)|agc1_top_tab[2]);               //uAd15
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);

	return 1;
}

static int TDA18252WriteAGC2_TOP(struct dvb_frontend *fe,u8 uBPIndex)
{
	//struct tda18252_priv *priv = fe->tuner_priv;
	u8 w_buf[2];

	while(TDA18252Read(fe,red_buf,59)<=0);
	msleep(1);
	reg_buf[0x20] = ((red_buf[0x20]&0x7f)|0x60);                          //Gu2
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);
	w_buf[0] = agc2_top_tab[uBPIndex][0];
	while(TDA18252Write(fe,0x23,w_buf,1)<=0);

  reg_buf[0x20] = ((reg_buf[0x20]&0xbf)|0xa0);                          //Gu3
	while(TDA18252Write(fe,0x20,reg_buf,1)<=0);
	w_buf[0] = agc2_top_tab[uBPIndex][2];
	while(TDA18252Write(fe,0x23,w_buf,1)<=0);

	reg_buf[0x20] = (reg_buf[0x20]|0xe0);                                 //Gu4
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);
	w_buf[0] = agc2_top_tab[uBPIndex][4];
	while(TDA18252Write(fe,0x23,w_buf,1)<=0);

	reg_buf[0x20] = ((reg_buf[0x20]&0x7f)|0x60);                          //Gd2
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);
	w_buf[0] = agc2_top_tab[uBPIndex][1];
	while(TDA18252Write(fe,0x23,w_buf,1)<=0);

	reg_buf[0x20] = ((reg_buf[0x20]&0xbf)|0xa0);                             //Gd3
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);
	w_buf[0] = agc2_top_tab[uBPIndex][3];
	while(TDA18252Write(fe,0x23,w_buf,1)<=0);

	reg_buf[0x20] = (reg_buf[0x20]|0xe0);                                    //Gd4
	while(TDA18252Write(fe,0x20,(reg_buf+0x20),1)<=0);
	w_buf[0] = agc2_top_tab[uBPIndex][5];
	while(TDA18252Write(fe,0x23,w_buf,1)<=0);

	return 1;
}


static int TDA18252ResetAGC(struct dvb_frontend *fe)
{
	u8 tmp_AGC_On = 0;
	u8 tmp_pdAGC2_DETECT2 = 0;
	u8 tmp_pdAGC2_DETECT3 = 0;
	u8 tmp_pdAGC2_DETECT4 = 0;
	u8 tmp_pdAGC2_DETECT = 0;
	u8 tmp_red_buf;

	tmp_red_buf = red_buf[0x13];
	tmp_AGC_On = ((tmp_red_buf>>4)&0x01);
	tmp_red_buf = red_buf[0x13];
	tmp_pdAGC2_DETECT2 = (tmp_red_buf&0x01);
	tmp_red_buf = red_buf[0x13];
	tmp_pdAGC2_DETECT3 = ((tmp_red_buf>>1)&0x01);
	tmp_red_buf = red_buf[0x13];
	tmp_pdAGC2_DETECT4 = ((tmp_red_buf>>2)&0x01);

	reg_buf[0x1c] = (red_buf[0x1c]|0x10);                      //AGC_ON set "1"
	while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);

	reg_buf[0x18] = (red_buf[0x18]&0xfb);
	while(TDA18252Write(fe,0x18,(reg_buf+0x18),1)<=0);
	reg_buf[0x22] = ((red_buf[0x22]&0xc3)|0x1c);
	while(TDA18252Write(fe,0x22,(reg_buf+0x22),1)<=0);
	reg_buf[0x1f] = ((red_buf[0x1f]&0x3f)|0x40);
	while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	reg_buf[0x24] = ((red_buf[0x24]&0xdf)|0x1c);
	while(TDA18252Write(fe,0x24,(reg_buf+0x24),1)<=0);

	reg_buf[0x1c] = (reg_buf[0x1c]&0xef);                      //AGC_ON set "0"
	while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);

	TDA18252WriteAGC1_TOP(fe);
	TDA18252WriteAGC2_TOP(fe,6);

	reg_buf[0x1c] = (red_buf[0x1c]|0x10);                      //AGC_ON set "1"
	while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);

	reg_buf[0x22] = (red_buf[0x22]&0xef);
	while(TDA18252Write(fe,0x22,(reg_buf+0x22),1)<=0);
	msleep(20);

	if (m_tmTDA18252AGC1WorkAround)
	{
		printk("error\n");
	}
	else
	{
    while(TDA18252Read(fe,red_buf,59)<=0);
	}

	reg_buf[0x22] = (red_buf[0x22]|0x20);
	while(TDA18252Write(fe,0x22,(reg_buf+0x22),1)<=0);

	reg_buf[0x25] = ((red_buf[0x25]&0x80)|0x28);
	while(TDA18252Write(fe,0x25,(reg_buf+0x25),1)<=0);

	reg_buf[0x24] = (red_buf[0x24]&0xdf);
	while(TDA18252Write(fe,0x24,(reg_buf+0x24),1)<=0);

	reg_buf[0x18] = (red_buf[0x18]|0x04);
	while(TDA18252Write(fe,0x18,(reg_buf+0x18),1)<=0);

	if (m_tmTDA18252AGC1WorkAround)
	{
		reg_buf[0x1f] = (red_buf[0x1f]&0x3f);
		while(TDA18252Write(fe,0x1f,(reg_buf+0x1f),1)<=0);
	}

	tmp_pdAGC2_DETECT = ((tmp_pdAGC2_DETECT4<<2)|(tmp_pdAGC2_DETECT3<<1)|tmp_pdAGC2_DETECT2);
	reg_buf[0x1c] = ((red_buf[0x1c]&0xe8)|(tmp_AGC_On<<4)|tmp_pdAGC2_DETECT);
	while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);

	return 1;
}

static int TDA18252CalcMAINPLL(struct dvb_frontend *fe,int uLO,int pro_en)              //uLO KHz
{
	//struct tda18252_priv *priv = fe->tuner_priv;
	u8	uCounter = 0;
	u32	uDiv;
	u8  lo_buf[6] = {0,0,0,0,0,0};

	if (pro_en == 1) // auto
	{

		// update & write LO_Frac_Auto [31..0]
		lo_buf[1] = (u8)(uLO >> 12);
		lo_buf[2] = (u8)(uLO >> 4);
		lo_buf[3] = (((u8)(uLO<<4))|(red_buf[0x0c]&0x0f));
		lo_buf[0] = red_buf[0x09];
		lo_buf[4] = red_buf[0x0d];
		while(TDA18252Write(fe,0x09,lo_buf,5)<=0);
	}
	else
	{
		//manual
		// search for MAIN_Post_Div corresponding to uLO
		uCounter = 0;
		do uCounter ++;
		while (uLO > lo_tab[uCounter - 1][0] && uCounter < 40);
		lo_buf[0] = ((u8)lo_tab[uCounter - 1][1]);

    while(TDA18252Write(fe,0x08,lo_buf,1)<=0);

		//// force LO_Sn05
		//pObj->I2CMap.uBx0F.bF..LO_Sn05 = 0;

		// calculate MAIN_Div
		uDiv = (((u32)(lo_tab[uCounter - 1][2]) * (uLO / 1000)) << 7) / 125;
		lo_buf[0] = (u8)(uDiv >> 16) & 0x7F;
		lo_buf[1] = (u8)(uDiv >> 13) & 0x07;
		lo_buf[2] = (u8)(uDiv >> 5);
		lo_buf[3] = ((u8)(uDiv) & 0xF8);
		lo_buf[4] = 0x01;

    while(TDA18252Write(fe,0x09,lo_buf,5)<=0);
	}

	return 1;
}

static int TDA18252WriteMAINPLL(struct dvb_frontend *fe,u32 uLO)           //uLO Hz
{
	//struct tda18252_priv *priv = fe->tuner_priv;
	u8 lo_buf[5] = {0,0,0,0,0};
	u8 pro_buf[2] = {0,0};
	u8 freq_en;
	u32 LO;
	//int ret;

	//struct i2c_msg msg_r1 = { .addr = priv->i2c_address, .flags = I2C_M_RD, .buf = red_buf, .len = 23 };

	memset(red_buf,0,59);
	/*
	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 1);
	ret = i2c_transfer (i2c, msg_r1, 1);
	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 0);
	*/
	while(TDA18252Read(fe,red_buf,59)<=0);
	LO=(u32)(uLO/1000);
	freq_en = ((red_buf[0x07]>>6)&0x01);
	//printk("*****************nhy|freq_en is %d\n",freq_en);
	if(freq_en)
		{
			lo_buf[0]=(u8)(LO>>12);
			lo_buf[1]=(u8)(LO>>4);
			lo_buf[2]=(((u8)LO<<4)|(red_buf[0x0c]&0x0f));
			lo_buf[3]=0xff;

			while(TDA18252Write(fe,0x0a,lo_buf,4)<=0);

		  pro_buf[0] = 0x49;
      while(TDA18252Write(fe,0x0f,pro_buf,1)<=0);
		  pro_buf[0] = 0x09;
		  while(TDA18252Write(fe,0x0f,pro_buf,1)<=0);
		}
	else
		{
			TDA18252CalcMAINPLL(fe,LO,freq_en);
		}
	return 1;
}

static int tda18252_release(struct dvb_frontend *fe)
{
	kfree(fe->tuner_priv);
	fe->tuner_priv = NULL;
	return 0;
}

static int tda18252_sleep(struct dvb_frontend *fe)
{
	//struct tda18252_priv *priv = fe->tuner_priv;

	int ret;
/*
	u8 buf [] = { 0x17, 0xbf };
	struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 };

	dprintk("%s:\n", __FUNCTION__);

	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 1);
	if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
		dprintk("%s: i2c error\n", __FUNCTION__);
	}
	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 0);
	msleep(1);
  buf[0]=0x18;
  buf[1]=0x5f;
  if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 1);
	if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
		dprintk("%s: i2c error\n", __FUNCTION__);
	}
	if (fe->ops.i2c_gate_ctrl)
		fe->ops.i2c_gate_ctrl(fe, 0);
	printk("tda18252 sleep \n"); */
  ret = 1;
	return (ret == 1) ? 0 : ret;
}

static int tda18252_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
	//struct tda18252_priv *priv = fe->tuner_priv;
	u32 uLO;
	u8 loc_freq;
  int i;

	//struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 };
	//struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = w_buf, .len = 2 };
	//struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 };
	//struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = I2C_M_RD, .buf = buf, .len = 2 };

	dprintk("%s:\n", __FUNCTION__);
	//printk("********************** set param\n");
  memset(reg_buf,0,59);
  memset(red_buf,0,59);
  while(TDA18252Read(fe,red_buf,59)<=0);

  if((params->frequency<47000000)||(params->frequency>1002000000))
		    {
        	return -1;
        }
  if (params->frequency <= 200000000)
	{
		reg_buf[0x29] = ((red_buf[0x29]&0xfc)|0x01);
	}
	else if ((params->frequency > 200000000) && (params->frequency <= 600000000))
	{
		reg_buf[0x29] = ((red_buf[0x29]&0xfc)|0x02);
	}
	else if (params->frequency > 600000000)
	{
		reg_buf[0x29] = ((red_buf[0x29]&0xfc)|0x03);
	}
	while(TDA18252Write(fe,0x29,(reg_buf+0x29),1)<=0);

  uLO = params->frequency + 4000000;
  TDA18252WriteMAINPLL(fe,uLO);
  //div = (params->frequency/1000);
  reg_buf[0x2a] = (red_buf[0x2a]&0x3f);
	if ((uLO <=71000000))  {loc_freq = 0;}
	else if ((71000000< uLO) && (uLO <=97000000))    {loc_freq = 1;}
	else if ((97000000< uLO) && (uLO <=138000000))   {loc_freq = 2;}
	else if ((138000000< uLO) && (uLO <=188000000))  {loc_freq = 3;}
	else if ((188000000< uLO) && (uLO <=253000000))  {loc_freq = 4;}
	else if ((253000000< uLO) && (uLO <=343000000))  {loc_freq = 5;}
	else if ((343000000< uLO) && (uLO <=650000000))  {loc_freq = 6;}
	else if (650000000< uLO)
	{
		// Manage high pass filter cut off frequencies
		loc_freq = 7;
		reg_buf[0x2a] = ((reg_buf[0x2a]&0x3f)|0x40);
	}
  while(TDA18252Write(fe,0x2a,(reg_buf+0x2a),1)<=0);
  //reg_buf[0x1a] = (loc_freq|0x88);
  reg_buf[0x1a] = (loc_freq|(red_buf[0x1a]&0xf8));
  while(TDA18252Write(fe,0x1a,(reg_buf+0x1a),1)<=0);
	//priv->frequency = ((uLO/1000)-4000);
	reg_buf[0x29] = 0x02;
	reg_buf[0x30] = 0xb0;
	reg_buf[0x31] = 0x00;
	while(TDA18252Write(fe,0x29,(reg_buf+0x29),3)<=0);

	TDA18252WriteAGC2_TOP(fe,((u8)(red_buf[26]&0x07)));

  reg_buf[0x2d] = ((red_buf[0x2d]&0xdf)|0x01);
  while(TDA18252Write(fe,0x2d,(reg_buf+0x2d),1)<=0);
  reg_buf[0x18] = ((red_buf[0x18]&0xfd)|0x08);
  while(TDA18252Write(fe,0x18,(reg_buf+0x18),1)<=0);
  reg_buf[0x1b] = (red_buf[0x1b]|0x20);
  reg_buf[0x1c] = (red_buf[0x1c]&0xf8);
  while(TDA18252Write(fe,0x1b,(reg_buf+0x1b),2)<=0);
  reg_buf[0x2d] = (red_buf[0x2d]&0xdf);
  while(TDA18252Write(fe,0x2d,(reg_buf+0x2d),1)<=0);
  reg_buf[0x1e] = (red_buf[0x1e]&0xcf);
  while(TDA18252Write(fe,0x1e,(reg_buf+0x1e),1)<=0);
  reg_buf[0x25] = ((red_buf[0x25]&0xaf)|0x20);
  while(TDA18252Write(fe,0x25,(reg_buf+0x25),1)<=0);

  reg_buf[0x24] = ((red_buf[0x24]&0xdf)|0x1c);
  reg_buf[0x25] = ((reg_buf[0x25]&0xbf)|0x30);
  while(TDA18252Write(fe,0x24,(reg_buf+0x24),2)<=0);

  reg_buf[0x1c] = (reg_buf[0x1c]|0x10);
  while(TDA18252Write(fe,0x1c,(reg_buf+0x1c),1)<=0);

  if (m_tmTDA18252AGC1WorkAround)
		{
			// Work around: restore AGC1 value before any new write
			reg_buf[0x04] = (red_buf[0x04]&0x7c);
			while(TDA18252Write(fe,0x04,(reg_buf+0x04),1)<=0);
		}
	msleep(7);
	reg_buf[0x24] = (reg_buf[0x24]&0xef);
  while(TDA18252Write(fe,0x24,(reg_buf+0x24),1)<=0);

  reg_buf[0x33] = (red_buf[0x33]&0x80);
  reg_buf[0x34] = (red_buf[0x34]&0x80);
  reg_buf[0x35] = (red_buf[0x35]&0x80);
  while(TDA18252Write(fe,0x33,(reg_buf+0x33),3)<=0);
  reg_buf[0x1e] = (reg_buf[0x1e]&0xcf);
  while(TDA18252Write(fe,0x1e,(reg_buf+0x1e),1)<=0);

  while(TDA18252Read(fe,red_buf,59)<=0)
    {
    	printk("*************** read error\n");
    	msleep(1);
    }
 // printk("************** nhy| red_buf[0x01] is %02x\n",red_buf[1]);
  //for(i=0;i<59;i++)
  //  {
  //  	printk("****************** addr=%02x,value=%02x\n",i,red_buf[i]);
  //  }

	return 1;
}

static int tda18252_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
	struct tda18252_priv *priv = fe->tuner_priv;
	*frequency = priv->frequency;
	return 0;
}

static struct dvb_tuner_ops tda18252_tuner_ops = {
	.info = {
		.name = "Philips TDA18252",
		.frequency_min = 54000000,
		.frequency_max = 1002000000,
	},
	.release = tda18252_release,
	.sleep = tda18252_sleep,
	.set_params = tda18252_set_params,
	.get_frequency = tda18252_get_frequency,
};

struct dvb_frontend *tda18252_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough)
{
	struct tda18252_priv *priv = NULL;
	//struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = initbuf, .len = 54 };
	//struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = wrt_buf, .len = 2 };
  //int ret;
  int i;
  dprintk("--->tda18252 attach\n");


	priv = kzalloc(sizeof(struct tda18252_priv), GFP_KERNEL);
	if (priv == NULL)
		return NULL;

	priv->i2c_address = addr;
	priv->i2c = i2c;
	priv->has_loopthrough = has_loopthrough;

	memcpy(&fe->ops.tuner_ops, &tda18252_tuner_ops, sizeof(struct dvb_tuner_ops));

	fe->tuner_priv = priv;

	//initbuf[19] = initbuf[19]&0xbf;
	//initbuf[20] = initbuf[20]&0xbf;

  //while(TDA18252Write(fe,0x05,initbuf,54)<=0);
  while(TDA18252Write(fe,0x05,initbuf,27)<=0);
  while(TDA18252Write(fe,0x22,(initbuf+29),1)<=0);
  while(TDA18252Write(fe,0x24,(initbuf+31),23)<=0);

  memset(red_buf,0,59);
  while(TDA18252Read(fe,red_buf,59)<=0);
  //for(i=0;i<59;i++)
  //  {
  //  	printk("****************** addr=%02x,value=%02x\n",i,red_buf[i]);
  //  }

	memset(reg_buf,0,59);
	memset(red_buf,0,59);
  while(TDA18252Read(fe,red_buf,59)<=0);
	msleep(1);
  /*reset MSM*/
	reg_buf[0x05]=(red_buf[0x05]|0x80);
  while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
  msleep(1);
  reg_buf[0x05]=(reg_buf[0x05]&0x7f);
  while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
  msleep(1);
	reg_buf[0x05]=(reg_buf[0x05]|0x20);
	while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
	msleep(1);
	reg_buf[0x05]=(reg_buf[0x05]&0xdf);
  while(TDA18252Write(fe,0x05,(reg_buf+0x05),1)<=0);
  msleep(1);

	while(TDA18252Read(fe,red_buf,59)<=0);
	msleep(1);

	TDA18252IRCalibration(fe);

	TDA18252ResetAGC(fe);

	TDA18252WriteMAINPLL(fe,TDA18251_LO_INIT);

	//while(TDA18252Read(fe,red_buf,59)<=0);
	reg_buf[0x2d] = (red_buf[0x2d]|0x10);
	while(TDA18252Write(fe,0x2d,(reg_buf+0x2d),1)<=0);

	//reg_buf[0x32] = ((red_buf[0x32]&0x18)|0x82);
	reg_buf[0x32] = 0x0c;
	while(TDA18252Write(fe,0x32,(reg_buf+0x32),1)<=0);

  memset(red_buf,0,59);
  while(TDA18252Read(fe,red_buf,59)<=0);
  //for(i=0;i<59;i++)
  //  {
  //  	printk("****************** addr=%02x,value=%02x\n",i,red_buf[i]);
  //  }
	return fe;
}
EXPORT_SYMBOL(tda18252_attach);

module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");

MODULE_DESCRIPTION("DVB TDA18252 driver");
MODULE_AUTHOR("Andrew de Quincey");
MODULE_LICENSE("GPL");
