/*
    
File name   : tc90507xbg.c

Description : Driver for tdc90507xbg OFDM Demodulator

COPYRIGHT (C) NXP Semiconductors

Date          Modification                              Name
----          ------------                              ----
01 Jun 2007   Created                                   Anastacio Cavalcante - Lucas Cordeiro
23 Jul 2007   Changed driver name to TDHL5				Lucas Cordeiro
24 Jul 2007	  Added Doxygen comments					Lucas Cordeiro
17 Aug 2007	  Added power management functionality	    Lucas Cordeiro
29 Aug 2007	  Added fft size, guard interval functions	Lucas Cordeiro
10 Sep 2007   Added error correction decoder functions  Lucas Cordeiro
20 Jan 2009   Port to Linux DVB API						Daniel Patrick

*/

#define TC90507XBG_DEFAULT_FIRMWARE      "dvb-fe-tc90507xbg.fw"
#define TC90507XBG_DEFAULT_FIRMWARE_INFO "dvb-fe-tc90507xbg.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 "tc90507xbg.h"

static int tc90507xbg_init_on_startup = 1; /* dpatrick - =1? */
module_param_named(init_on_startup, tc90507xbg_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 "tc90507xbg: " 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)


/************************************ 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, 
				PACKET8192=8192, PACKET16384=16384, PACKET32768=32768 };

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

s32 slot_mode, tState, lastState, transmission_mode, poweroff_counter;


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

static int tc90507xbg_write_byteI(struct tc90507xbg_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 tc90507xbg_read_byte(struct tc90507xbg_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 IOCTL function gets the tc90507xbg 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 IOCTL 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=0, perr1, perr2, perr3, error_count = 0;
	struct tc90507xbg_state* state = (struct tc90507xbg_state*) fe->demodulator_priv;

	switch(layer) {

		case LAYERA:
			perr1 = tc90507xbg_read_byte(state, ADDR9D);
			perr2 = tc90507xbg_read_byte(state, ADDR9E);
			perr3 = tc90507xbg_read_byte(state, ADDR9F);
			break;
		case LAYERB:
			perr1 = tc90507xbg_read_byte(state, ADDRA0);
			perr2 = tc90507xbg_read_byte(state, ADDRA1);
			perr3 = tc90507xbg_read_byte(state, ADDRA2);
			break;
		case LAYERC:
			perr1 = tc90507xbg_read_byte(state, ADDRA3);
			perr2 = tc90507xbg_read_byte(state, ADDRA4);
			perr3 = tc90507xbg_read_byte(state, ADDRA5);
			break;
		default:
			return err = -1;
	}

	dprintk("%s(%d), perr1: %d\n", __FUNCTION__, __LINE__, perr1);
	dprintk("%s(%d), perr2: %d\n", __FUNCTION__, __LINE__, perr2);
	dprintk("%s(%d), perr3: %d\n", __FUNCTION__, __LINE__, perr3);

	if (err != -1) {
		error_count = ((perr1<<16)|(perr2<<8)|(perr3))*MAX_NBITS_ERROR;
	} else {
		return err;
	}

	dprintk("%s(%d), Error count: %d\n", __FUNCTION__, __LINE__, error_count);

	return error_count;
}


/**
 * @brief This IOCTL function gets the tc90507xbg 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 IOCTL function sets the tc90507xbg 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, pecya_high, pecya_low;
	struct tc90507xbg_state* state = (struct tc90507xbg_state*) fe->demodulator_priv;

	switch(mode) {
		case PACKET512:
			err = tc90507xbg_write_byteI(state, ADDR76, 0x8F);
			slot_mode = PACKET512;
			break;
		case PACKET1024:
			err = tc90507xbg_write_byteI(state, ADDR76, 0x9F);
			slot_mode = PACKET1024;
			break;
		case PACKET2048:
			err = tc90507xbg_write_byteI(state, ADDR76, 0xAF);
			slot_mode = PACKET2048;
			break;
		case PACKET4096:
			err = tc90507xbg_write_byteI(state, ADDR76, 0xBF);
			slot_mode = PACKET4096;
			break;
		case PACKET8192:
			err = tc90507xbg_write_byteI(state, ADDR76, 0xCF);
			slot_mode = PACKET8192;
			break;
		case PACKET16384:
			err = tc90507xbg_write_byteI(state, ADDR76, 0xDF);
			slot_mode = PACKET16384;
			break;
		case PACKET32768:
			err = tc90507xbg_write_byteI(state, ADDR76, 0xEF);
			slot_mode = PACKET32768;
			break;
		default:
			err = -1; /* error while setting slot mode */
	}

	pecya_high = tc90507xbg_read_byte(state, ADDRA6);
	pecya_low = tc90507xbg_read_byte(state, ADDRA7);

	dprintk("%s(%d), slot mode: %d\n", __FUNCTION__, __LINE__, (pecya_high<<8)|pecya_low);

	return err;
}


/**
 * @brief This function is responsible for reducing power consumption 
 *		  of the TC90507XBG 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 IOCTL 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 tc90507xbg_state* state = (struct tc90507xbg_state*) fe->demodulator_priv;
	s32 err = 0;

	if (slpwkp == FALSE) {
		err = tc90507xbg_write_byteI(state, ADDR03, BIT5);
		lastState = tState;
		tState = STDBY;
		printk("%s(%d), Sleep mode...\n", __FUNCTION__, __LINE__);	
	} else if (slpwkp == TRUE) {
		err = tc90507xbg_write_byteI(state, ADDR03, BIT3);
		lastState = tState;
		tState = SETUP;
		printk("%s(%d), Wakeup mode...\n", __FUNCTION__, __LINE__);	
	} else {
		err=-1;
	}

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

/**
 * @brief This IOCTL function sets the tc90507xbg transmission mode to
 *		  either automatic decision or manual seeting.
 *
 * @param[in] mode 		Transmission mode
 *
 * @retval This IOCTL 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 tc90507xbg_state* state = (struct tc90507xbg_state*) fe->demodulator_priv;
	s32 err = 0, mmode, ffsize;

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

	if (mode == TRANSMISSION_MODE_AUTO) {
		err = tc90507xbg_write_byteI(state, ADDRBC, (~BIT3 & mmode));
		transmission_mode = FALSE;
		
	} else if (mode == TRANSMISSION_MODE_2K) { // 0x00 [7:6]
		err = tc90507xbg_write_byteI(state, ADDRB0, ffsize&0x3F);
		transmission_mode = TRUE;
		
	} else if (mode == TRANSMISSION_MODE_4K) { // 0x01 [7:6]
		err = tc90507xbg_write_byteI(state, ADDRB0, ffsize&0x7F);
		transmission_mode = TRUE;
		
	} else if (mode == TRANSMISSION_MODE_8K) { // 0x02 [7:6]
		err = tc90507xbg_write_byteI(state, ADDRB0, ffsize&0xBF);
		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 IOCTL function can set thetc90507xbg 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 IOCTL 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) {

	struct tc90507xbg_state* state = (struct tc90507xbg_state*) fe->demodulator_priv;
	s32 gdleng, err = 0;

	gdleng = tc90507xbg_read_byte(state, ADDRB0);

	dprintk("%s(%d), Guard ratio gdleng: %d\n", __FUNCTION__, __LINE__, gdleng);

	if (guard_ratio == GI1_32) {
		err = tc90507xbg_write_byteI(state, ADDRB0, gdleng&0xE7);
		
	} else if (guard_ratio == GI1_16) {
		err = tc90507xbg_write_byteI(state, ADDRB0, gdleng&0xEF);
		
	} else if (guard_ratio == GI1_8) {
		err = tc90507xbg_write_byteI(state, ADDRB0, gdleng&0xF7);
		
	} else if (guard_ratio == GI1_4) {
		err = tc90507xbg_write_byteI(state, ADDRB0, gdleng&0xF7);
		
	} else {
		err = -1;
	}

	dprintk("%s(%d), Guard ratio: %d\n", __FUNCTION__, __LINE__, guard_ratio);

	return err;
}



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

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

    return;
}

static int tc90507xbg_init(struct dvb_frontend* fe) 
{
	s32 err = 0;
	unsigned long start, stop;
	struct tc90507xbg_state*  state = fe->demodulator_priv;
	
	if (state->initialised)
    {
    	dprintk("%s(%d) initialised \n", __FUNCTION__, __LINE__);
        return 0;
    }

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

	err = tc90507xbg_write_byteI(state, ADDR01, 0x40);
	
	err = tc90507xbg_write_byteI(state, ADDR13, 0x2C);
	err = tc90507xbg_write_byteI(state, ADDR14, 0x22);
	err = tc90507xbg_write_byteI(state, ADDR15, 0x00);
	
	err = tc90507xbg_write_byteI(state, ADDR16, 0x0A);
	err = tc90507xbg_write_byteI(state, ADDR1C, 0x20);
	err = tc90507xbg_write_byteI(state, ADDR1D, 0xA2);
	err = tc90507xbg_write_byteI(state, ADDR1E, 0xA0);
	err = tc90507xbg_write_byteI(state, ADDR20, 0x00);
	err = tc90507xbg_write_byteI(state, ADDR22, 0x80);
	err = tc90507xbg_write_byteI(state, ADDR30, 0x20);
	err = tc90507xbg_write_byteI(state, ADDR31, 0x11);
	err = tc90507xbg_write_byteI(state, ADDR32, 0x2B);
	err = tc90507xbg_write_byteI(state, ADDR50, 0x04);
	err = tc90507xbg_write_byteI(state, ADDR51, 0x58);
	err = tc90507xbg_write_byteI(state, ADDR58, 0x60);
	err = tc90507xbg_write_byteI(state, ADDRC2, 0x10);
	err = tc90507xbg_write_byteI(state, ADDREF, 0x01);
	
    state->initialised = 1;
    
    stop = jiffies;
    
    poweroff_counter = 0;
    
    // default slot mode
    set_SlotMode(fe, PACKET512);

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

static int tc90507xbg_sleep(struct dvb_frontend* fe)
{
	struct tc90507xbg_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 tc90507xbg_write_byte(struct dvb_frontend* fe, u8 *buf, int len)
{

    struct tc90507xbg_state* state = fe->demodulator_priv;

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

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

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

static int tc90507xbg_get_frontend_algo(struct dvb_frontend *fe)
{
    struct tc90507xbg_state*  state = fe->demodulator_priv;

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

static int tc90507xbg_get_fe(struct dvb_frontend* fe, struct dvb_frontend_parameters* fe_params)
{	
    struct tc90507xbg_state* state = fe->demodulator_priv;
	s32 ginterval;
	
 	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 = 

    /* Transmission mode */
    switch (tc90507xbg_read_byte(state, ADDRB0) & 0xC0) 
    {
        case 0:
            fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
            break;
        case 1:
            fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K;
            break;
        case 2:
            fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
            break;
    }

    /* Guard interval */
    ginterval = (tc90507xbg_read_byte(state, ADDRB0) & 0x18);
    switch (ginterval)
    {
        case 0:
            fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
            break;
        case 8:
            fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
            break;
        case 16:
            fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
            break;
        case 24:
            fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
            break;
    }

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

    return 0;
	
}

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

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

    if (!state->initialised) 
    {
        /* Error tc90507xbg not initialized */
        err = -1;
    }
    else 
    {
    	if ((tState == STDBY) || (tState == IDLE)) {
    		lastState = tState;
    		tState = SETUP;
    		set_swmode(fe, WKPMODE);
    		poweroff_counter = 0;
    		printk(KERN_WARNING " set to WAKEUP \n");
    	}
    
    	// TODO: check set tramission mode
    	/* 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(KERN_WARNING "%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);
}

static int tc90507xbg_read_ber(struct dvb_frontend* fe, u32* vber)
{
	s32 error_count = 0;
	
	error_count = get_LayerErrorCount(fe, LAYERA);
	if (error_count < 0) {
		error_count = get_LayerErrorCount(fe, LAYERB);
	}
	if (error_count < 0) {
		error_count = get_LayerErrorCount(fe, LAYERC);
	}
	
	if (error_count > 0)
		error_count = ((error_count*36) / (slot_mode*1632));
	
	*vber = error_count;
	
	dprintk("%s(%d), VBER: %d\n", __FUNCTION__, __LINE__, *vber);

	return 0;
}

static int tc90507xbg_read_status(struct dvb_frontend* fe, fe_status_t* fe_status)
{
	int status;
    struct tc90507xbg_state* state = fe->demodulator_priv;

	status = tc90507xbg_read_byte(state, ADDR80);
	dprintk(KERN_WARNING "Reg: 0x80, Value: 0x%X\n", status);
	
	if ((~status) & BIT3) { // fulock
		*fe_status |= FE_HAS_LOCK;
		lastState = tState;
		tState = ACTIVE;
	}
	if ((~status) & BIT2) { // vulock
		*fe_status |= FE_HAS_VITERBI;
	}
	if ((~status) & BIT7) { // retryov
		*fe_status |= FE_HAS_SYNC; 
	}
	// FE_HAS_SIGNAL, FE_HAS_CARRIER
	
	return 0;	
}

static int tc90507xbg_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 (tc90507xbg_set_fe(fe, params) < 0)
        return -EIO;
    
    /* wait for lock or timeout */
    do
    {
        err = tc90507xbg_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 = tc90507xbg_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 tc90507xbg_ops = {

    .info  = {
        .name = "NXP TC90507XBG 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 = tc90507xbg_release,

    .init = tc90507xbg_init,
    .sleep = tc90507xbg_sleep,
    .write = tc90507xbg_write_byte,
    .i2c_gate_ctrl = tc90507xbg_i2c_gate_ctrl,

    .tune = tc90507xbg_tune,
    .get_frontend_algo = tc90507xbg_get_frontend_algo,

    .set_frontend = tc90507xbg_set_fe,
    .get_frontend = tc90507xbg_get_fe,
    .get_tune_settings = 0,//tc90507xbg_get_tune_settings,

    .read_status = tc90507xbg_read_status,
    .read_ber = tc90507xbg_read_ber,
    .read_signal_strength = 0,//tc90507xbg_read_signal_strength,
    .read_snr = 0,//tc90507xbg_read_snr,
    .read_ucblocks = 0,//tc90507xbg_read_ucblocks,
};

struct dvb_frontend* tc90507xbg_attach(const struct tc90507xbg_config* config, struct i2c_adapter* i2c)
{
    struct tc90507xbg_state*  state = NULL;
    struct dvb_frontend* returned_fe;

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

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

    if (state != NULL) {
    	/* Setup the state */
        memcpy((void*)&state->config, config, sizeof(struct tc90507xbg_config));
        state->i2c = i2c;
        memcpy(&state->ops, &tc90507xbg_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 (tc90507xbg_init_on_startup) {
        	dprintk("%s(%d) - call init .... \n", __FUNCTION__, __LINE__);
        	tc90507xbg_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("Toshiba TC90507XBG ISDB-T Demodulator");
MODULE_AUTHOR("Anastacio Cavalcante, "
              "Lucas Cordeiro");
MODULE_LICENSE("GPL");

EXPORT_SYMBOL(tc90507xbg_attach);
