/*
    
File name   : mb86a20s.c

Description : Driver for mb86a20s OFDM Demodulator

COPYRIGHT (C) NXP Semiconductors

Date          Modification                              Name
15 Dec 2008   Created                                   Daniel Patrick - Anastacio Cavalcante
05 Jan 2009	  Adapted I2C communication					Daniel Patrick - Anastacio Cavalcante
07 Jan 2009   Added set_pll_registers function	        Daniel Patrick
09 Jan 2009   Implemented fine tuner					Daniel Patrick - Anastacio Cavalcante
12 Jan 2009   Changed functions name to DTF85500		Daniel Patrick
13 Jan 2009   Added BER measurement after viterbi		Daniel Patrick
16 Fev 2009   Added SNR measurement                     Bruno Farias

*/

#define MB86A20S_DEFAULT_FIRMWARE      "dvb-fe-mb86a20s.fw"
#define MB86A20S_DEFAULT_FIRMWARE_INFO "dvb-fe-mb86a20s.info"

#define FE_ALGO_HW        1

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>

#include "dvb_frontend.h"
#include "mb86a20s.h"

static int mb86a20s_init_on_startup = 1;
module_param_named(init_on_startup, mb86a20s_init_on_startup, int, 0644);
MODULE_PARM_DESC(init_on_startup, "Load firmware on startup");

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

/*********************************** Constants **********************************/

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


#define MAX_NBITS_ERROR	(100000000)
#define TRUE	     (1)
#define FALSE	     (0)
#define OFFSETVAL    (7)
#define SLPMODE		 (0)
#define WKPMODE		 (1)
#define TIMEOUT      (30)
#define MODE_STAT    0x07
#define STANDBY      0x73
#define MACRO_PWDN   0xD0
#define STATE_INIT   0x08


/************************************ Enums *************************************/

enum InitReg { ADDR01=0x01, ADDR03=0x03, ADDR04=0x04, ADDR05=0x05, ADDR06=0x06, ADDR13=0x013,
	       	   ADDR14=0x014,ADDR15=0x015,ADDR16=0x16, ADDR1C=0x1C, ADDR1D=0x1D, ADDR1E=0x1E, ADDR20=0x20, 
	           ADDR22=0x22, ADDR30=0x30, ADDR31=0x31, ADDR32=0x32, ADDR50=0x50, 
	           ADDR51=0x51, ADDR58=0x58, ADDR80=0x80, ADDR81=0x81, ADDRC2=0xC2, 
   		       ADDREF=0xEF, ADDRBC=0xBC, ADDRB0=0xB0, ADDR76=0x76, ADDR77=0x77,
		       ADDR9D=0x9D, ADDR9E=0x9E, ADDR9F=0x9F, ADDRA0=0xA0, ADDRA1=0xA1,
			   ADDRA2=0xA2, ADDRA3=0xA3, ADDRA4=0xA4, ADDRA5=0xA5, ADDRA6=0xA6,
	           ADDRA7=0xA7};

enum TunerState { INIT=0, IDLE, SETUP, STDBY, ACTIVE, TUNERERR };

enum BitMask { BIT0=0x01, BIT1=0x02, BIT2=0x04, BIT3=0x08, BIT4=0x10, BIT5=0x20,
				BIT6=0x40, BIT7=0x80 };

enum LayersSelection { LAYERA=0, LAYERB, LAYERC };

enum ErrorCorrection { VITERBI=0, RS=1, AUTOMATIC=0, MANUAL=1 };

enum GuardInterval { GI1_32=0, GI1_16, GI1_8, GI1_4 };

enum SlotMode { PACKET512=512, PACKET1024=1024, PACKET2048=2048, PACKET4096=4096, 
				PACKET8191=8191, PACKET16383=16383, PACKET32767=32767, PACKET65535=65535};


/*************************** Structures and Variables ***************************/

s32 slot_mode, tState, lastState, transmission_mode, poweroff_counter;


/*********************** Methods for accessing I2C client ***********************/

static int mb86a20s_write_byteI(struct mb86a20s_state* state, int reg, int data)
{
    int ret;
    u8 buf[] = { reg, data };
    struct i2c_msg msg = { .addr=0, .flags=0, .buf=buf, .len=2 };

    msg.addr = state->config.demod_address;
    ret = i2c_transfer(state->i2c, &msg, 1);

    if (ret != 1)
    {
        dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__, reg, data, ret);
    }
    else
    {
        dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__, reg, data, ret);
    }

    return ( (ret != 1) ? -1 : 0 );
}


static int mb86a20s_read_byte(struct mb86a20s_state* state, int reg)
{
    int ret;
    u8 b0[] = { reg };
    u8 b1[] = { 0 };
    struct i2c_msg msg[] = {{ .addr=0, .flags=0,        .buf=b0, .len=1},
                            { .addr=0, .flags=I2C_M_RD, .buf=b1, .len=1}};

    dprintk("%s: READ reg=0x%x\n", __FUNCTION__, reg);

    msg[0].addr = state->config.demod_address;
    msg[1].addr = state->config.demod_address;
    ret = i2c_transfer(state->i2c, msg, 2);

    if (ret != 2)
    {
        dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg, ret);
    }
    else
    {
        dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __FUNCTION__, reg, b1[0], ret);
    }

    return ( (ret != 2) ? -1 : (b1[0]) );
}


/********************************** Auxiliary Functions ******************************/

/**
 * @brief This function gets the mb86a20s error count. The error count
 *		  is used to calculate the the packet error rate (PER) and bit error
 *		  rate (BER) after Viterbi and RS decoding.
 *
 * @param[in] layer		layer (A, B or C)
 *
 * @retval This function returns 0 if the error count is read successfully. 
 * 		   Otherwise, it returns -1 and errno is set appropriately.
 */
s32 get_LayerErrorCount(struct dvb_frontend* fe, s32 layer) {

	s32 err = -1, bera = 0, aux;
	unsigned char bera0, bera1;

	struct mb86a20s_state* state = (struct mb86a20s_state*) fe->demodulator_priv;

	do {
		// complete flag detection
		err = mb86a20s_write_byteI(state, 0x50, 0xb8);
		aux = mb86a20s_read_byte(state, 0x51);
		if (err) dprintk(KERN_WARNING "dtf85500->%s(%d) error(%d)\n", __FUNCTION__, __LINE__, err);
	} while (aux == 0);

	switch(layer) {
		case LAYERA:
			err   = mb86a20s_write_byteI(state, 0x50, 0xb9);
			bera0 = mb86a20s_read_byte(state, 0x51);
			err   = mb86a20s_write_byteI(state, 0x50, 0xba);
			bera1 = mb86a20s_read_byte(state, 0x51);
			break;
		default:
			return err = -1;
	}

	if (err != -1) {
		bera = ((bera0 << 8) & 0x00FF00) + (bera1 & 0xFF);
		dprintk(KERN_WARNING "Layer A: %d  ber0: %d, ber1: %d\n", bera, bera0, bera1);
	} else {
		return err;
	}

	err = mb86a20s_write_byteI(state, 0x50, 0xB1);
	err = mb86a20s_write_byteI(state, 0x51, 0x01);
	err = mb86a20s_write_byteI(state, 0x51, 0x00);

	return bera;
}


/**
 * @brief This function gets the mb86a20s slot mode.
 *
 * @retval This function returns the slot mode (512, 1024, 2048, 4096, 8192,
 *		   16384, 32768).
 */
s32 get_SlotMode(void) {

	return slot_mode;
}

/**
 * @brief This function sets the mb86a20s slot mode to 512, 1024, 2048, 
 *		  4096, 8192, 16384, 32768 packets. The slot mode indicates the
 *		  measurement cycles for the packet mode.
 *
 * @param[in] mode 		Slot mode
 *
 * @retval This IOCTL returns 0 if the slot mode is set successfully. 
 * 		   Otherwise, it returns -1 and errno is set appropriately.
 */
s32 set_SlotMode(struct dvb_frontend* fe, s32 mode) {

	s32 err = 0;
	u8 pn0,pn1;
	struct mb86a20s_state* state = (struct mb86a20s_state*) fe->demodulator_priv;

	switch(mode) {
		case PACKET512:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET512 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET512 & 0x000FF) );
			slot_mode = PACKET512;
			break;
		case PACKET1024:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET1024 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET1024 & 0x000FF) );
			slot_mode = PACKET1024;
			break;
		case PACKET2048:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET2048 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET2048 & 0x000FF) );
			slot_mode = PACKET2048;
			break;
		case PACKET4096:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET4096 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET4096 & 0x000FF) );
			slot_mode = PACKET4096;
			break;
		case PACKET8191:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET8191 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET8191 & 0x000FF) );
			slot_mode = PACKET8191;
			break;
		case PACKET16383:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET16383 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET16383 & 0x000FF) );
			slot_mode = PACKET16383;
			break;
		case PACKET32767:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET32767 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET32767 & 0x000FF) );
			slot_mode = PACKET32767;
			break;
		case PACKET65535:
			err = mb86a20s_write_byteI(state, 0x50, 0xB2);
			err = mb86a20s_write_byteI(state, 0x51, (u8)((PACKET65535 & 0x0FF00)>>8) );
			err = mb86a20s_write_byteI(state, 0x50, 0xB3);
			err = mb86a20s_write_byteI(state, 0x51, (u8)(PACKET65535 & 0x000FF) );
			slot_mode = PACKET65535;
			break;

		default:
			err = -1; /* error while setting slot mode */
	}

    /*Reading Slot Mode*/
    err = mb86a20s_write_byteI(state, 0X50, 0xB2);
    pn0 = mb86a20s_read_byte(state, 0x51);
    err = mb86a20s_write_byteI(state, 0X50, 0xB3);
    pn1 = mb86a20s_read_byte(state, 0x51);

	dprintk("%s(%d), slot mode: %d\n", __FUNCTION__, __LINE__, ((pn0<<8)&0xff00) + (pn1&0x00ff));

	return err;
}


/**
 * @brief This function is responsible for reducing power consumption 
 *		  of the MB86A20S Demodulator when the receiver is in standby mode.
 *		  In order to set the demodulator to sleep mode then 0 must be passed
 *		  to the function call. To set the demodulator to wakeup mode then 1
 *		  must be passed.
 *
 * @param[in] slpwkp 		Sleep(0)/Wakeup(1) mode
 *
 * @retval This function returns 0 if the sleep/wakeup mode is set successfully. 
 * 		   Otherwise, it returns -1 and errno is set appropriately.
 */
s32 set_swmode(struct dvb_frontend* fe, s32 slpwkp) {

	struct mb86a20s_state* state = (struct mb86a20s_state*) fe->demodulator_priv;
	s32 err = 0;

	if (slpwkp == FALSE) {
		//Initializes OFDM state
		err = mb86a20s_write_byteI(state, STATE_INIT, 0x01);
		
		//It becomes standby mode		
		err = mb86a20s_write_byteI(state, STANDBY, 0x01);
		
		//Generates analog macro power down signal
		err = mb86a20s_write_byteI(state, MACRO_PWDN, 0x31);
		
		//Update state		
		lastState = tState;
		tState = STDBY;
		dprintk("%s(%d), Sleep mode...\n", __FUNCTION__, __LINE__);
	
	} else if (slpwkp == TRUE) {
		//Re-estabilish power signal 		
		err = mb86a20s_write_byteI(state, MACRO_PWDN, 0x00);
		
		//Standy off		
		err = mb86a20s_write_byteI(state, STANDBY, 0x00);

		//Clear initialization processing of OFDM state
		err = mb86a20s_write_byteI(state, STATE_INIT, 0x00);

		//Update state
		lastState = tState;
		tState = SETUP;
		dprintk("%s(%d), Wakeup mode...\n", __FUNCTION__, __LINE__);	
	} else {
		err=-1;
	}

	return (err < 0) ? -1 : 0;
}

/**
 * @brief This function sets the mb86a20s transmission mode to
 *		  either automatic decision or manual seeting.
 *
 * @param[in] mode 		Transmission mode
 *
 * @retval This returns 0 if the transmission mode is set successfully. 
 * 		   Otherwise, it returns -1 and errno is set appropriately.
 */
s32 set_TransmissionMode(struct dvb_frontend* fe, s32 mode) {

	struct mb86a20s_state* state = (struct mb86a20s_state*) fe->demodulator_priv;
	s32 err = 0, mddetect, mmode, ffsize;

	mmode = mb86a20s_read_byte(state, ADDRBC);
	ffsize = mb86a20s_read_byte(state, ADDRB0);

	if (mode == TRANSMISSION_MODE_AUTO) {
		//Operates in detected mode
		err = mb86a20s_write_byteI(state, MODE_STAT, 0x00);
                //Checking if mode was auto-detected
		mddetect = mb86a20s_read_byte(state, MODE_STAT);
        	//If mode was auto-detected
		if ((mddetect & 0x20) == 0x20) {
	  	  dprintk("%s(%d), Transmission Mode auto-detected\n", __FUNCTION__, __LINE__);
	  	  if ((mddetect & 0x0C) == 0x04) 
	  	  	dprintk("Transmission Mode = Mode2\n");
	  	  else 
	  	  	dprintk("Transmission Mode = Mode3\n");
		}
		transmission_mode = FALSE;
		
	} else if (mode == TRANSMISSION_MODE_2K) { 
		err = mb86a20s_write_byteI(state, ADDRB0, ffsize&0x3F);
		transmission_mode = TRUE;
		
	} else if (mode == TRANSMISSION_MODE_4K) { 
		/* Auto-detect=OFF */
		err = mb86a20s_write_byteI(state, MODE_STAT, 0x10);
		// Setting to Mode 2 = Transmission Mode 4K		
		err = mb86a20s_write_byteI(state, MODE_STAT, 0x04);

		//Verifing if mode was set correctly
		err = mb86a20s_read_byte(state, MODE_STAT);
		if((err&0x0C)==0x04) 
		  dprintk("\nTransmission Mode = Mode2\n");
		
		transmission_mode = TRUE;
		
	} else if (mode == TRANSMISSION_MODE_8K) {
		/* Auto-detect=OFF */
		err = mb86a20s_write_byteI(state, MODE_STAT, 0x10);
		// Setting to Mode 3 = Transmission Mode 8K		
		err = mb86a20s_write_byteI(state, MODE_STAT, 0x08);

		//Verifing if mode was set correctly
		err = mb86a20s_read_byte(state, MODE_STAT);
		if ((err & 0x0C) == 0x08) 
			dprintk("\nTransmission Mode = Mode3\n");
		transmission_mode = TRUE;
		
	} else {
		err = -1; /* error while setting transmission mode */
	}

	dprintk("%s(%d), mmode register: %d\n", __FUNCTION__, __LINE__, mmode);

	return err;
}

/**
 * @brief This function can set the mb86a20s guard interval to 1/32,
 *		  1/16, 1/8, 1/4. This guard interval is used to avoid linear  
 *		  distortion such as multipath.
 *
 * @param[in] guard_ratio	Guard Interval
 *
 * @retval This returns 0 if the guard interval is set successfully. 
 * 		   Otherwise, it returns -1 and errno is set appropriately.
 */
s32 set_GuardInterval(struct dvb_frontend* fe, s32 guard_ratio) {

	s32 gddetect, err = 0;
	struct mb86a20s_state* state = (struct mb86a20s_state*) fe->demodulator_priv;

	switch (guard_ratio)
	{
    	case GUARD_INTERVAL_1_16:
        	/* Auto-detect=OFF */
			err = mb86a20s_write_byteI(state, MODE_STAT, 0x10);
			// Setting Guard Interval = 1/16		
			err = mb86a20s_write_byteI(state, MODE_STAT, 0x0A);

			//Verifing if guard interval was set correctly
			err = mb86a20s_read_byte(state, MODE_STAT);
			if ((err&0x0A) == 0x0A) 
		  		dprintk("\nSet Guard Interval = %s\n", "1/16");		
			break;
			
		case GUARD_INTERVAL_1_8:
        	/* Auto-detect=OFF */
			err = mb86a20s_write_byteI(state, MODE_STAT, 0x10);
			// Setting Guard Interval = 1/8		
			err = mb86a20s_write_byteI(state, MODE_STAT, 0x09);

			//Verifing if guard interval was set correctly
			err = mb86a20s_read_byte(state, MODE_STAT);
			if ((err&0x09) == 0x09) 
		  		dprintk("\nSet Guard Interval = %s\n", "1/8");		
           	break;
           	
		case GUARD_INTERVAL_1_4:
        	/* Auto-detect=OFF */
			err = mb86a20s_write_byteI(state, MODE_STAT, 0x10);
			// Setting Guard Interval = 1/4		
			err = mb86a20s_write_byteI(state, MODE_STAT, 0x08);

			//Verifing if guard interval was set correctly
			err = mb86a20s_read_byte(state, MODE_STAT);
			if ((err&0x08) == 0x08) 
		  		dprintk("\nSet Guard Interval = %s\n", "1/4");	
			break;	

		case GUARD_INTERVAL_AUTO:
			//Operates in detected guard
			err = mb86a20s_write_byteI(state, MODE_STAT, 0x00);
            //Checking if guard was auto-detected
			gddetect = mb86a20s_read_byte(state, MODE_STAT);
        	//If guard-interval was auto-detected
 			if ((gddetect&0x20) == 0x20) {
	  	  		dprintk("%s(%d), Guard Interval auto-detected\n", __FUNCTION__, __LINE__);
	  	  		if ((gddetect&0x03) == 0x00) 
	  	  			dprintk("\nGuard Interval = %s\n","1/4");
	  	  		else if ((gddetect&0x03) == 0x01) 
	  	  			dprintk("\nGuard Interval = %s\n","1/8");
	  	  		else 
	  	  			dprintk("\nGuard Interval = %s\n","1/16");  
			} else {
  	  	  		err = -1;
			}
          	break;
          	
		default:
			dprintk("\nValue not valid (%d)\n",guard_ratio);
          	err = -1;
            break;
	}

	return err;
}

/**
 * @brief This function return the estimated CN measurement.
 *
 * @param[in] CNval CN measurement
 *
 * @retval This function returns estimated CN measurement value.
 */
s32 CNval_to_CN10(u16 CNval) {
	u16 ret ;
	u16 A, B, C ;
	if(      CNval>=18187 ) { A= 10 ; B=1461 ; C=18187 ; }
	else if( CNval>=16534 ) { A= 20 ; B=1653 ; C=16534 ; }
	else if( CNval>=14823 ) { A= 30 ; B=1711 ; C=14823 ; }
	else if( CNval>=13161 ) { A= 40 ; B=1662 ; C=13161 ; }
	else if( CNval>=11622 ) { A= 50 ; B=1539 ; C=11622 ; }
	else if( CNval>=10279 ) { A= 60 ; B=1343 ; C=10279 ; }
	else if( CNval>= 9089 ) { A= 70 ; B=1190 ; C= 9089 ; }
	else if( CNval>= 8042 ) { A= 80 ; B=1047 ; C= 8042 ; }
	else if( CNval>= 7137 ) { A= 90 ; B= 905 ; C= 7137 ; }
	else if( CNval>= 6342 ) { A=100 ; B= 795 ; C= 6342 ; }
	else if( CNval>= 5641 ) { A=110 ; B= 701 ; C= 5641 ; }
	else if( CNval>= 5030 ) { A=120 ; B= 611 ; C= 5030 ; }
	else if( CNval>= 4474 ) { A=130 ; B= 556 ; C= 4474 ; }
	else if( CNval>= 3988 ) { A=140 ; B= 486 ; C= 3988 ; }
	else if( CNval>= 3556 ) { A=150 ; B= 432 ; C= 3556 ; }
	else if( CNval>= 3180 ) { A=160 ; B= 376 ; C= 3180 ; }
	else if( CNval>= 2841 ) { A=170 ; B= 339 ; C= 2841 ; }
	else if( CNval>= 2541 ) { A=180 ; B= 300 ; C= 2541 ; }
	else if( CNval>= 2276 ) { A=190 ; B= 265 ; C= 2276 ; }
	else if( CNval>= 2038 ) { A=200 ; B= 238 ; C= 2038 ; }
	else if( CNval>= 1800 ) { A=210 ; B= 238 ; C= 1800 ; }
	else if( CNval>= 1625 ) { A=220 ; B= 175 ; C= 1625 ; }
	else if( CNval>= 1462 ) { A=230 ; B= 163 ; C= 1462 ; }
	else if( CNval>= 1324 ) { A=240 ; B= 138 ; C= 1324 ; }
	else if( CNval>= 1175 ) { A=250 ; B= 149 ; C= 1175 ; }
	else if( CNval>= 1063 ) { A=260 ; B= 112 ; C= 1063 ; }
	else if( CNval>=  980 ) { A=270 ; B=  83 ; C=  980 ; }
	else if( CNval>=  907 ) { A=280 ; B=  73 ; C=  907 ; }
	else if( CNval>=  840 ) { A=290 ; B=  67 ; C=  840 ; }
	else if( CNval>=  788 ) { A=300 ; B=  52 ; C=  788 ; }

	if(      CNval>=19648 ) ret = 0 ;
	else if( CNval<   788 ) ret = 300 ;
	else                    ret = (s32)( A-(10*(CNval-C)-B/2)/B) ;
	return ret ;
}

/**
 * @brief This function reset CN measurement.
 *
 * @param[in] fe dvb_frontend
 *
 * @retval This function returns 0 if the CN measurement is resetted successfully.
 */
s32 mb86a20s_conuter_reset(struct dvb_frontend* fe) {

  struct mb86a20s_state* state = fe->demodulator_priv;
  unsigned char value;
  unsigned char reset,set;

  value = mb86a20s_read_byte(state,0x45);  
  dprintk("\nvalue=0x%x\n",value);

  if (value) {  
    dprintk("\nMode = Manual\n");
    reset = 0x10 | value;

    if(mb86a20s_write_byteI(state, 0x45, reset)==1) 
    	return -1;
    set = 0x6f & value;
  
    if(mb86a20s_write_byteI(state, 0x45, set)==1) 
    	return -1;
  }

  return 0;
}


/********************************** ISDB-T Methods ******************************/

static void mb86a20s_release(struct dvb_frontend* fe)
{
    struct mb86a20s_state* state = (struct mb86a20s_state*) fe->demodulator_priv;
    kfree(state);
    fe->demodulator_priv = NULL;

    return;
}

static int mb86a20s_init(struct dvb_frontend* fe) 
{
	s32 err = 0;
	unsigned long start, stop;
	struct mb86a20s_state*  state = fe->demodulator_priv;
	s32 value_i;
	u8 value;

	s32 IQINV      = 1;   // IQ Inversion(0:off, 1:on)
   	s32 MTYPESEL   = 0;   // Reception Segment(0:13sg, 1:1seg(P), 2:1seg, 3:3seg)
   	s32 RS_SW      = 1;

   	/* AGC setting */

	s32 AGCSET_EN = 1;   // Enable AGC
   	s32 AGCSEL    = 1;   // AGCSEL(0:postFIR, 1:preFIR)
   	s32 RPWSIF    = 0;   // IFAGC POWER(0:Add, 1:Ignore)
   	s32 MAIF      = 0;   // IFAGC Control(0:Auto, 1:Manual)
   	s32 POIF      = 1;   // IFAGC Polarity(0:Neg, 1:Pos)
   	s32 RPWSRF    = 1;   // RFAGC POWER(0:Add, 1:Ignore)
   	s32 MARF      = 0;   // RFAGC Control(0:Auto, 1:Manual)=0
   	s32 PORF      = 1;   // RFAGC Polarity(0:Neg, 1:Pos)

   	s32 DTS = 3;

   	s32 VIFREF    = 50;
   	s32 AGAIN     = 120;
   	s32 IFA       = 53;
   	s32 IFB       = 3800;
   	s32 IFAGCO    = 0;
   	s32 MAXIFAGC  = 255;
   	s32 VMAIF     = 125;
   	s32 VMARF     = 0;
   	s32 IFSAMPLE  = 100;
   	s32 OUTSAMPLE = 100;
   	s32 BGAIN     = 2;
   	s32 RFA       = 34;
   	s32 RFB       = 3800;
   	s32 RFAGCO    = 0;
   	s32 MAXRFAGC  = 255;

   	/* Monitor */
   	s32 VBER_A_NUM = 0xFFFF;
   	s32 VBER_B_NUM = 0xFFFF;
   	s32 VBER_C_NUM = 0xFFFF;

   	s32 SBER_A_NUM = 16383;
   	s32 SBER_B_NUM = 16383;
   	s32 SBER_C_NUM = 16383;

   	s32 PBER_A_NUM = 16383;
  	s32 PBER_B_NUM = 16383;
   	s32 PBER_C_NUM = 16383;

   	s32 MER_SYMBOL = 4;
   	s32 CN_SYMBOL  = 4;

   	/* Serial/Parallel */

   	s32 TSOUTSEL  = 0;   // TSOUTSEL(0:Parallel, 1:Serial)
   	s32 TSERRMASK = 0;
   	s32 PPACON    = 1;   // Sync byte enable
   	s32 PENON     = 1;   // Parity Enable
   	s32 PLAYER    = 7;
   	s32 TSCLKSEL  = 0;
	
	if (state->initialised)
    {
    	dprintk("%s(%d) initialised \n", __FUNCTION__, __LINE__);
        return 0;
    }

	dprintk("%s(%d), Initializing tuner... \n", __FUNCTION__, __LINE__);
	
    start = jiffies;

	err = mb86a20s_write_byteI(state, 0x70, 0x0f); // Execute I2C Reg reset
	err = mb86a20s_write_byteI(state, 0x70, 0xff);	// Release I2C Reg reset
	err = mb86a20s_write_byteI(state, 0x08, 0x01);	// Sequencer stop
	err = mb86a20s_write_byteI(state, 0x09, (u8)((AGCSEL<<5)|0x1a|(IQINV<<2)));	//AGC selection and IQ inv

	err = mb86a20s_write_byteI(state,0x50, 0xd1);
	err = mb86a20s_write_byteI(state,0x51, 0x20|(RS_SW<<1));
	err = mb86a20s_write_byteI(state,0x71, MTYPESEL);   //Reception segment setting

	// Clock frequency setting
	value_i = 0xCAC50; // 830544	
	
	err = mb86a20s_write_byteI(state,0x28, 0x2a);
	err = mb86a20s_write_byteI(state,0x29, (u8)((value_i & 0x0ff0000) >> 16));
	err = mb86a20s_write_byteI(state,0x2a, (u8)((value_i & 0x000ff00) >> 8));
	err = mb86a20s_write_byteI(state,0x2b, (u8)(value_i & 0x00000ff));
	err = mb86a20s_write_byteI(state,0x3b, 0x21);
	err = mb86a20s_write_byteI(state,0x3c, 0x38);

	// Carrier frequency setting
	value_i = 0x51EC63;   // carrier = [(fclk*2 - IF)/fclk]*2^24
	err = mb86a20s_write_byteI(state,0x28, 0x20);
	err = mb86a20s_write_byteI(state,0x29, (u8)((value_i & 0x0ff0000) >> 16));
   	err = mb86a20s_write_byteI(state,0x2a, (u8)((value_i & 0x000ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x2b, (u8)(value_i & 0x00000ff));

	// Wide freq detector data conversion
   	//value_i = (int) (8192.0*64.0/63.0*32.0/fclk+0.50) ;
   	value_i = 0x1F35; // Not Documented
   	err = mb86a20s_write_byteI(state,0x28, 0x22);
   	err = mb86a20s_write_byteI(state,0x29, (u8)((value_i & 0x0ff0000) >> 16));
   	err = mb86a20s_write_byteI(state,0x2a, (u8)((value_i & 0x000ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x2b, (u8)(value_i & 0x00000ff));

	if (AGCSET_EN) {
          // AGCCTRL setting
    	value = ( (0x01 & RPWSIF) <<5) +  \
	          ( (0x01 & MAIF  ) <<4) +  \
	          ( (0x01 & POIF  ) <<3) +  \
	          ( (0x01 & RPWSRF) <<2) +  \
	          ( (0x01 & MARF  ) <<1) +  \
	          ( (0x01 & PORF  ) );

		err = mb86a20s_write_byteI(state,0x01, value);

		// DTS setting
        err = mb86a20s_write_byteI(state,0x04, 0x08); //AFC Operation
        value = (u8) (DTS) ;
        err = mb86a20s_write_byteI(state,0x05, value);

	  	// VIFREF setting
        err = mb86a20s_write_byteI(state,0x04, 0x0e);

        value = (u8) (VIFREF >> 8);
        err = mb86a20s_write_byteI(state,0x05, value);

        err = mb86a20s_write_byteI(state,0x04, 0x0f);

        value = (u8) (VIFREF & 0x00ff);
        err = mb86a20s_write_byteI(state,0x05, value);

		// AGAIN setting
        err = mb86a20s_write_byteI(state,0x04, 0x0b);

        value = (u8) (AGAIN);
        err = mb86a20s_write_byteI(state,0x05, value);

		// IFA setting
        err = mb86a20s_write_byteI(state,0x04, 0x00);
        value = (u8) (IFA >> 8);

        err = mb86a20s_write_byteI(state,0x05, value);

        err = mb86a20s_write_byteI(state,0x04, 0x01);

        value = (u8) (IFA & 0x00ff);
        err = mb86a20s_write_byteI(state,0x05, value);

		// IFB setting
		err = mb86a20s_write_byteI(state,0x04, 0x02);

		value = (u8)(IFB >> 8);
		err = mb86a20s_write_byteI(state,0x05, value);

		err = mb86a20s_write_byteI(state,0x04, 0x03);

		value = (u8)(IFB & 0x00ff);
		err = mb86a20s_write_byteI(state,0x05, value);

		// IFAGCO setting
		err = mb86a20s_write_byteI(state,0x04, 0x09);

		value = (u8) (IFAGCO);
		err = mb86a20s_write_byteI(state,0x05, value);

		// MAXIFAGC setting
		err = mb86a20s_write_byteI(state,0x04, 0x0a);

		value = (u8) (MAXIFAGC);
		err = mb86a20s_write_byteI(state,0x05, value);

		// VMAIF setting
		err = mb86a20s_write_byteI(state,0x04, 0x27);

		value = (u8) (VMAIF);
		err = mb86a20s_write_byteI(state,0x05, value);

		err = mb86a20s_write_byteI(state,0x04, 0x28);

		value = 0x00;
		err = mb86a20s_write_byteI(state,0x05, value);

		// VMARF setting
		err = mb86a20s_write_byteI(state,0x04, 0x1e);

		value = (u8) (VMARF);
		err = mb86a20s_write_byteI(state,0x05, value);

		// IFSAMPLE setting
        err = mb86a20s_write_byteI(state,0x04, 0x29);

        value = (u8) (IFSAMPLE);
        err = mb86a20s_write_byteI(state,0x05, value);

        // OUTSAMPLE setting
        err = mb86a20s_write_byteI(state,0x04, 0x32);

        value = (u8) (OUTSAMPLE);
        err = mb86a20s_write_byteI(state,0x05, value);

        // BGAIN setting
        err = mb86a20s_write_byteI(state,0x04, 0x14);

        value = (u8) (BGAIN);
        err = mb86a20s_write_byteI(state,0x05, value);

        // RFA setting
        err = mb86a20s_write_byteI(state,0x04, 0x04);

        value = (u8) (RFA >> 8);
        err = mb86a20s_write_byteI(state,0x05, value);

        err = mb86a20s_write_byteI(state,0x04, 0x05);

        value = (u8) (RFA & 0x00ff);
        err = mb86a20s_write_byteI(state,0x05, value);

        // RFB setting
        err = mb86a20s_write_byteI(state,0x04, 0x06);

        value = (u8) (RFB >> 8);
        err = mb86a20s_write_byteI(state,0x05, value);

        err = mb86a20s_write_byteI(state,0x04, 0x07);

        value = (u8) (RFB & 0x00ff);
        err = mb86a20s_write_byteI(state,0x05, value);

        // RFAGCO setting
        err = mb86a20s_write_byteI(state,0x04, 0x12);

        value = (u8) (RFAGCO);
        err = mb86a20s_write_byteI(state,0x05, value);

        // MAXRFAGC setting
        err = mb86a20s_write_byteI(state,0x04, 0x13);

        value = (u8) (MAXRFAGC);
        err = mb86a20s_write_byteI(state,0x05, value);
	}

	// VBER = ON
	err = mb86a20s_write_byteI(state,0x52, 0x01);

	// VBER Packet Number setting of Layer-A
   	err = mb86a20s_write_byteI(state,0x50, 0xa7);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((VBER_A_NUM & 0x0ff0000) >> 16));
   	err = mb86a20s_write_byteI(state,0x50, 0xa8);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((VBER_A_NUM & 0x000ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xa9);
   	err = mb86a20s_write_byteI(state,0x51,  (u8)(VBER_A_NUM & 0x00000ff));

   	// VBER Packet Number setting of Layer-B
   	err = mb86a20s_write_byteI(state,0x50, 0xaa);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((VBER_B_NUM & 0x0ff0000) >> 16));
   	err = mb86a20s_write_byteI(state,0x50, 0xab);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((VBER_B_NUM & 0x000ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xac);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(VBER_B_NUM & 0x00000ff));

   	// VBER Packet Number setting of Layer-C
   	err = mb86a20s_write_byteI(state,0x50, 0xad);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((VBER_C_NUM & 0x0ff0000) >> 16));
   	err = mb86a20s_write_byteI(state,0x50, 0xae);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((VBER_C_NUM & 0x000ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xaf);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(VBER_C_NUM & 0x00000ff));

   	// SBER = ON
   	err = mb86a20s_write_byteI(state,0x5e, 0x07);

   	// SBER Packet Number setting of Layer-A
   	err = mb86a20s_write_byteI(state,0x50, 0xdc);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((SBER_A_NUM & 0x0ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xdd);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(SBER_A_NUM & 0x000ff));

   	// SBER Packet Number setting of Layer-B
   	err = mb86a20s_write_byteI(state,0x50, 0xde);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((SBER_B_NUM & 0x0ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xdf);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(SBER_B_NUM & 0x000ff));

   	// SBER Packet Number setting of Layer-C
   	err = mb86a20s_write_byteI(state,0x50, 0xe0);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((SBER_C_NUM & 0x0ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xe1);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(SBER_C_NUM & 0x000ff));

   	// PBER = ON
   	err = mb86a20s_write_byteI(state,0x50, 0xb0);
   	err = mb86a20s_write_byteI(state,0x51, 0x07);

   	// PBER Packet Number setting of Layer-A
   	err = mb86a20s_write_byteI(state,0x50, 0xb2);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((PBER_A_NUM & 0x0ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xb3);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(PBER_A_NUM & 0x000ff));

   	// PBER Packet Number setting of Layer-B
   	err = mb86a20s_write_byteI(state,0x50, 0xb4);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((PBER_B_NUM & 0x0ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xb5);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(PBER_B_NUM & 0x000ff));

   	// PBER Packet Number setting of Layer-C
   	err = mb86a20s_write_byteI(state,0x50, 0xb6);
   	err = mb86a20s_write_byteI(state,0x51, (u8)((PBER_C_NUM & 0x0ff00) >> 8));
   	err = mb86a20s_write_byteI(state,0x50, 0xb7);
   	err = mb86a20s_write_byteI(state,0x51, (u8)(PBER_C_NUM & 0x000ff));

   	// Symbol No. setting of MER Monitor
   	value = (u8) (MER_SYMBOL);
   	err = mb86a20s_write_byteI(state,0x50, 0x51);
   	err = mb86a20s_write_byteI(state,0x51, value);

   	// MER Monitor Operation Mode setting
   	err = mb86a20s_write_byteI(state,0x50, 0x50);
   	err = mb86a20s_write_byteI(state,0x51, 0x02);

   	// Symbol No. setting of C/N Monitor
   	value = (u8) (CN_SYMBOL);
   	err = mb86a20s_write_byteI(state,0x45, value);

   	// C/N Monitor Operation Mode setting
   	err = mb86a20s_write_byteI(state,0x48, 0x04);

   	// Serial/Parallel, Error packet mask
  	err = mb86a20s_write_byteI(state,0x50, 0xd5);

   	value = 0x00 | ((u8) (TSERRMASK) << 1) | (u8) (TSOUTSEL);
   	err = mb86a20s_write_byteI(state,0x51, value);

   	// Parallel data setting
   	err = mb86a20s_write_byteI(state,0x50, 0xd6);
   	value = 0x00 | ((u8) (PPACON) << 4) | ((u8) (PENON) << 3) | (u8) (PLAYER) ;

   	err = mb86a20s_write_byteI(state,0x51, value);
   	err = mb86a20s_write_byteI(state,0x50, 0xd2);

   	value = 0x03 | ((u8) (TSCLKSEL) << 2);
   	err = mb86a20s_write_byteI(state,0x51, value);

   	//agalog dustubance
   	err = mb86a20s_write_byteI(state,0x50, 0x1e);
   	err = mb86a20s_write_byteI(state,0x51, 0x5d);
   	err = mb86a20s_write_byteI(state,0x50, 0x22);
   	err = mb86a20s_write_byteI(state,0x51, 0x00);
   	err = mb86a20s_write_byteI(state,0x50, 0x23);
   	err = mb86a20s_write_byteI(state,0x51, 0xc8);
  	err = mb86a20s_write_byteI(state,0x50, 0x24);
   	err = mb86a20s_write_byteI(state,0x51, 0x00);
   	err = mb86a20s_write_byteI(state,0x50, 0x25);
   	err = mb86a20s_write_byteI(state,0x51, 0xf0);
   	err = mb86a20s_write_byteI(state,0x50, 0x26);
   	err = mb86a20s_write_byteI(state,0x51, 0x00);
   	err = mb86a20s_write_byteI(state,0x50, 0x27);
   	err = mb86a20s_write_byteI(state,0x51, 0xc3);

   	err = mb86a20s_write_byteI(state,0x50, 0x39);
   	err = mb86a20s_write_byteI(state,0x51, 0x02);

   	// Register setting chagne(defalut was wrong)
   	err = mb86a20s_write_byteI(state,0x28, 0x74);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0xff);

   	// Register setting chagne(defalut was wrong)
   	err = mb86a20s_write_byteI(state,0x28, 0x46);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x20);
   	err = mb86a20s_write_byteI(state,0x2b, 0x0c);

   	// Register setting chagne(defalut was wrong)
   	err = mb86a20s_write_byteI(state,0x04, 0x40);
   	err = mb86a20s_write_byteI(state,0x05, 0x00);

   	// Register setting chagne(defalut was wrong)
   	err = mb86a20s_write_byteI(state,0x28, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x08);
   	err = mb86a20s_write_byteI(state,0x28, 0x05);
   	err = mb86a20s_write_byteI(state,0x2b, 0x02);

   	// Digital filter selection = Manual
   	err = mb86a20s_write_byteI(state,0x1c, 0x01);

   	// Digital filter Tap coefficient
   	err = mb86a20s_write_byteI(state,0x28, 0x06);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x00);

   	err = mb86a20s_write_byteI(state,0x28, 0x07);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x16);

   	err = mb86a20s_write_byteI(state,0x28, 0x08);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
  	err = mb86a20s_write_byteI(state,0x2b, 0x12);

   	err = mb86a20s_write_byteI(state,0x28, 0x09);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x2c);

   	err = mb86a20s_write_byteI(state,0x28, 0x0a);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x28);

   	err = mb86a20s_write_byteI(state,0x28, 0x0b);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x28);

   	err = mb86a20s_write_byteI(state,0x28, 0x0c);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x6c);

   	err = mb86a20s_write_byteI(state,0x28, 0x0d);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x74);

   	err = mb86a20s_write_byteI(state,0x28, 0x0e);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x00);

   	err = mb86a20s_write_byteI(state,0x28, 0x0f);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x0c);

   	err = mb86a20s_write_byteI(state,0x28, 0x10);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x15);

   	err = mb86a20s_write_byteI(state,0x28, 0x11);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x17);

   	err = mb86a20s_write_byteI(state,0x28, 0x12);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x11);

   	err = mb86a20s_write_byteI(state,0x28, 0x13);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x04);

   	err = mb86a20s_write_byteI(state,0x28, 0x14);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x01);
   	err = mb86a20s_write_byteI(state,0x2b, 0xf3);

   	err = mb86a20s_write_byteI(state,0x28, 0x15);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x01);
   	err = mb86a20s_write_byteI(state,0x2b, 0xe2);

   	err = mb86a20s_write_byteI(state,0x28, 0x16);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x03);
   	err = mb86a20s_write_byteI(state,0x2b, 0xd9);

   	err = mb86a20s_write_byteI(state,0x28, 0x17);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x03);
   	err = mb86a20s_write_byteI(state,0x2b, 0xdb);

   	err = mb86a20s_write_byteI(state,0x28, 0x18);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x07);
   	err = mb86a20s_write_byteI(state,0x2b, 0xed);

   	err = mb86a20s_write_byteI(state,0x28, 0x19);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x0e);

   	err = mb86a20s_write_byteI(state,0x28, 0x1a);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x3a);

   	err = mb86a20s_write_byteI(state,0x28, 0x1b);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x6a);

   	err = mb86a20s_write_byteI(state,0x28, 0x1c);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0x96);

   	err = mb86a20s_write_byteI(state,0x28, 0x1d);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0xb4);

   	err = mb86a20s_write_byteI(state,0x28, 0x1e);
   	err = mb86a20s_write_byteI(state,0x29, 0x00);
   	err = mb86a20s_write_byteI(state,0x2a, 0x00);
   	err = mb86a20s_write_byteI(state,0x2b, 0xbe);

   	err = mb86a20s_write_byteI(state,0x70, 0xf0); // Execute logic reset
   	err = mb86a20s_write_byteI(state,0x70, 0xff); // Release logic reset

   	err = mb86a20s_write_byteI(state,0x08, 0x00); // Sequencer re-start   
		
    state->initialised = 1;
    
    stop = jiffies;
    
    poweroff_counter = 0;
    
    // default slot mode
    set_SlotMode(fe, PACKET1024);

	return (err < 0) ? -1 : 0;
	
}

static int mb86a20s_sleep(struct dvb_frontend* fe)
{
	struct mb86a20s_state*  state = fe->demodulator_priv;
	
	dprintk("%s(%d) \n", __FUNCTION__, __LINE__);

	// set demodulator to sleep (STDBY)		
	set_swmode(fe, SLPMODE);
	state->initialised = 0;

	return 0;
}

static int mb86a20s_write_byte(struct dvb_frontend* fe, u8 *buf, int len)
{

    struct mb86a20s_state* state = fe->demodulator_priv;

    dprintk("%s(%d) \n", __FUNCTION__, __LINE__);
    
    if (len != 2)
        return -EINVAL;

 	return mb86a20s_write_byteI(state, buf[0], buf[1]);
}

static int mb86a20s_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
{
	dprintk("%s(%d) \n", __FUNCTION__, __LINE__);
	// enable tuner i2c - not necessary
	
	return 0;
}

static int mb86a20s_get_frontend_algo(struct dvb_frontend *fe)
{
    struct mb86a20s_state*  state = fe->demodulator_priv;

    if (state->config.tuning_algo == 1)
    {
        return FE_ALGO_HW;
    }
    return 0;	    
	
}

static int mb86a20s_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters* fe_params)
{	

	s32 ginterval;
    struct mb86a20s_state* state = fe->demodulator_priv;
	
 	dprintk("%s(%d) \n", __FUNCTION__, __LINE__);

    /* Inversion status */
    fe_params->inversion = INVERSION_OFF;    

    /* Bandwidth */
    fe_params->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;

    /* FEC */
    //fe_params->u.ofdm.code_rate_HP =
    //fe_params->u.ofdm.code_rate_LP =

    /* Constellation */
    //fe_params->u.ofdm.constellation = 

    /* Guard interval */
    ginterval = mb86a20s_read_byte(state, 0x07); //get guard interval

    if ((ginterval & 0x20) == 0x20 ) {
    	if ((ginterval & 0x03) == 0x00) 
    		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
      	else if ((ginterval & 0x03) == 0x01) 
      		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
      	else 
      		fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;  	
    }

    /* Hierarchy */
    //fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;

    return 0;
}

static int mb86a20s_set_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters* fe_params)
{
	struct mb86a20s_state*  state = fe->demodulator_priv;
    int err = 0;

    dprintk("%s(%d)\n", __FUNCTION__, __LINE__);

    if (!state->initialised) 
    {
        /* Error mb86a20s not initialized */
        err = -1;
    }
    else 
    {
    	if ((tState == STDBY) || (tState == IDLE)) {
    		lastState = tState;
    		tState = SETUP;
    		set_swmode(fe, WKPMODE);
    		poweroff_counter = 0;
    		dprintk("set to WAKEUP \n");
    	}
    
    	/* Transmission mode */
    	set_TransmissionMode(fe, fe_params->u.ofdm.transmission_mode);
    
    	/* Set guard interval */
    	err = set_GuardInterval(fe, fe_params->u.ofdm.guard_interval);
    	if (err < 0)
    		dprintk("%s(%d) - GuardInterval(%d) \n", __FUNCTION__, __LINE__, fe_params->u.ofdm.guard_interval);
    
        /* Set tuner frequency */
        dprintk("%s(%d) set_params\n", __FUNCTION__, __LINE__);
        err = fe->ops.tuner_ops.set_params(fe, fe_params);

    }

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

    return (err);
}


/**
 * @brief This IOCTL function calculates approximately the BER after Viterbi
 *		  and RS decoding for layer A, B, or C.
 *
 * @param[in] layer		layer (A/(0), B/(1) or C/(2))
 *
 * @retval This IOCTL returns the BER. Otherwise, it returns -1 and errno 
 *         is set appropriately.
 */
static int mb86a20s_read_ber(struct dvb_frontend* fe, u32* vber)
{

  s32 err;
  struct mb86a20s_state* state = (struct mb86a20s_state*) fe->demodulator_priv;

  // Reset - PBER ()
  err = mb86a20s_write_byteI(state, 0x50, 0xB1);
  err = mb86a20s_write_byteI(state, 0x51, 0x07);
  err = mb86a20s_write_byteI(state, 0x51, 0x00);
    
  // BER measurement after RS
  err = get_LayerErrorCount(fe, LAYERA);

  //BER value returned to the application
  *vber = err;

  return 0;
}


/**
 * @brief This IOCTL function return the SNR. The value of SNR
 *	      must be divided for 10 at the application.	  
 *
 * @retval This IOCTL returns the SNR.
 */
static int mb86a20s_read_snr(struct dvb_frontend* fe, u16* snr)
{
  struct mb86a20s_state* state = fe->demodulator_priv;
  s32 cn_symbol  = 4;
  s32 reset;
  unsigned char datah, datal;
  u16 cn_value, est_cn=0 ;
  s32 err;

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

  //reset CN measurement
  reset = mb86a20s_conuter_reset(fe); 

  //WAIT (for more period than symbol time set at SYMCOUNT )
  msleep((2^(cn_symbol+1))+5);
  
  //Set LOCK=1. (To prevent register being over write.)   
  err = mb86a20s_write_byteI(state, 0x45, 0x24);
    
  //reading measurement result     
  datah = mb86a20s_read_byte(state, 0x46);
  datal = mb86a20s_read_byte(state,0x47);

  //Set Lock=0
  err = mb86a20s_write_byteI(state, 0x45, 0x04);

  cn_value = ((datah<<8) & 0xff00) + (datal & 0x00ff);
    
  est_cn = CNval_to_CN10(cn_value);
  
  *snr = (u16) est_cn ;

  return 0;
}




static int mb86a20s_read_status(struct dvb_frontend* fe, fe_status_t* fe_status)
{
    int status;
    struct mb86a20s_state* state = fe->demodulator_priv;
	
	status = mb86a20s_read_byte(state, 0x0A);
	
	if ((status == 0x09) || (status == 0x08)) { 
		*fe_status |= FE_HAS_LOCK;
		lastState = tState;
		tState = ACTIVE;
	}

	if (status == 0x06) { 
		*fe_status |= FE_HAS_SYNC; 
	}
	
	return 0;	
}

static int mb86a20s_tune(struct dvb_frontend* fe, struct dvb_frontend_parameters* params,
                        unsigned int mode_flags, unsigned int *delay, fe_status_t *status)
{
    fe_status_t s;
    int err = 0;
    
	dprintk("%s(%d) \n", __FUNCTION__, __LINE__);
	
    if (params == NULL)
    {
        /* dvb-core is not in FESTATE_RETUNE state */
        /* may be we can check lock state here */
        *status = 0;
        return 0;
    }

    if (mb86a20s_set_fe(fe, params) < 0)
        return -EIO;
    
    /* wait for lock or timeout */
    do
    {
        err = mb86a20s_read_status(fe, &s);
        msleep(6); // TODO: Remove msleep
    } while( !( (s & FE_TIMEDOUT) || (s & FE_HAS_LOCK) || (err<0) ) );
    
    if (err < 0)
        return err;

    if (s & FE_HAS_LOCK)
    {
        /* locked on central frequency. Exit now. */
        *status = s;
        return 0;
    }

    /* Set tuner frequency */
    if (fe->ops.tuner_ops.set_params)
    {
        err = fe->ops.tuner_ops.set_params(fe, params);
    }
    if (err < 0)
        return err;
	
    /* wait for lock or timeout */
    do
    {
        err = mb86a20s_read_status(fe, &s);
        msleep(6);
    }while( !( (s & FE_TIMEDOUT) || (s & FE_HAS_LOCK) || (err < 0) ) );

    if (err < 0)
        return err;

    if (s & FE_HAS_LOCK)
    {
        /* locked on frequency + offset. Exit now. */
        *status = s;
        return 0;
    }
    else
    {
        /* has detected an offset but not locked (it can happens on analog carrier) */
        *status = FE_TIMEDOUT;
        return 0;
    }

	return 0;
}

static struct dvb_frontend_ops mb86a20s_ops = {

    .info  = {
        .name = "NXP MB86A20S ISDB-T",
        .type = FE_OFDM_BR,
        .frequency_min = 470000000,
        .frequency_max = 800000000,
        .frequency_stepsize = 6000000,
        .caps =
            FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
            FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
            FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
            FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
    },

    .release = mb86a20s_release,

    .init = mb86a20s_init,
    .sleep = mb86a20s_sleep,
    .write = mb86a20s_write_byte,
    .i2c_gate_ctrl = mb86a20s_i2c_gate_ctrl,

    .tune = mb86a20s_tune,
    .get_frontend_algo = mb86a20s_get_frontend_algo,

    .set_frontend = mb86a20s_set_fe,
    .get_frontend = mb86a20s_get_fe,
    .get_tune_settings = 0,//mb86a20s_get_tune_settings,

    .read_status = mb86a20s_read_status,
    .read_ber = mb86a20s_read_ber,
    .read_signal_strength = 0,//mb86a20s_read_signal_strength,
    .read_snr = mb86a20s_read_snr,
    .read_ucblocks = 0,//mb86a20s_read_ucblocks,
};

struct dvb_frontend* mb86a20s_attach(const struct mb86a20s_config* config, struct i2c_adapter* i2c)
{
    struct mb86a20s_state*  state = NULL;
    struct dvb_frontend* returned_fe;

    /* Allocate memory for the internal state */
    state = (struct mb86a20s_state*) kmalloc(sizeof(struct mb86a20s_state), GFP_KERNEL);

	dprintk("%s(%d) \n", __FUNCTION__, __LINE__);

    if (state != NULL) {
    	/* Setup the state */
        memcpy((void*)&state->config, config, sizeof(struct mb86a20s_config));
        state->i2c = i2c;
        memcpy(&state->ops, &mb86a20s_ops, sizeof(struct dvb_frontend_ops));
        state->initialised = 0;
        
        /* Create dvb_frontend */
        memcpy(&state->frontend.ops, &state->ops, sizeof(struct dvb_frontend_ops));
        state->frontend.demodulator_priv = state;

        returned_fe = &state->frontend;
        
        /* Init + Load Firmware */
        if (mb86a20s_init_on_startup) {
        	dprintk("%s(%d) - call init .... \n", __FUNCTION__, __LINE__);
        	mb86a20s_init(returned_fe);
        }

    }
    else {
    	kfree(state);
    	dprintk(" %s(%d) returned_fe NULL\n", __FUNCTION__, __LINE__);
        returned_fe = NULL;
    }
   	dprintk(" %s(%d) returned_fe OK .... \n", __FUNCTION__, __LINE__);
    return (returned_fe);
}

MODULE_DESCRIPTION("Fujitsu MB86A20S ISDB-T Demodulator");
MODULE_AUTHOR("Anastacio Cavalcante, "
              "Daniel Patrick, "
              "Bruno Farias");
MODULE_LICENSE("GPL");

EXPORT_SYMBOL(mb86a20s_attach);
