/*
    Conexant cx24117/cx24128 - DVB QPSK Satellite demod/tuner driver

    Copyright (C) 2005 Steven Toth <stoth@hauppauge.com>

    Support for KWorld DVB-S 100 by Vadim Catana <skystar@moldova.cc>

    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/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

#include "dvb_frontend.h"
#include "cx24117.h"

#define XTAL 10111000

static int force_band;
static int debug = 2;
#define dprintk(args...) \
	do { \
		if (debug) printk (KERN_DEBUG "cx24117: " args); \
	} while (0)


static CX24117_CONFIG     p_config;
static CX24117_MPEG_OUT p_mpeg;

#if CX24117_INCLUDE_MPEG_EXTRA_CLOCK
    static CX24117_EXTRA_CLOCK_DATA cx24117_extra_clocks;
#endif /* CX24117_INCLUDE_MPEG_EXTRA_CLOCK */

/* Various tuner defaults need to be established for a given symbol rate Sps */
static struct
{
	u32 symbolrate_low;
	u32 symbolrate_high;
	u32 VCAprogdata;
	u32 VGAprogdata;
	u32 FILTune;
} cx24117_AGC_vals[] =
{
	{
		.symbolrate_low		= 1000000,
		.symbolrate_high	= 4999999,
		/* the specs recommend other values for VGA offsets,
		   but tests show they are wrong */
		.VGAprogdata		= (1 << 19) | (0x180 << 9) | 0x1e0,
		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x07,
		.FILTune		= 0x27f /* 0.41 V */
	},
	{
		.symbolrate_low		=  5000000,
		.symbolrate_high	= 14999999,
		.VGAprogdata		= (1 << 19) | (0x180 << 9) | 0x1e0,
		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x1f,
		.FILTune		= 0x317 /* 0.90 V */
	},
	{
		.symbolrate_low		= 15000000,
		.symbolrate_high	= 45000000,
		.VGAprogdata		= (1 << 19) | (0x100 << 9) | 0x180,
		.VCAprogdata		= (2 << 19) | (0x07 << 9) | 0x3f,
		.FILTune		= 0x145 /* 2.70 V */
	},
};

/*
 * Various tuner defaults need to be established for a given frequency kHz.
 * fixme: The bounds on the bands do not match the doc in real life.
 * fixme: Some of them have been moved, other might need adjustment.
 */
static struct
{
	u32 freq_low;
	u32 freq_high;
	u32 VCOdivider;
	u32 progdata;
} cx24117_bandselect_vals[] =
{
	/* band 1 */
	{
		.freq_low	= 950000,
		.freq_high	= 1074999,
		.VCOdivider	= 4,
		.progdata	= (0 << 19) | (0 << 9) | 0x40,
	},

	/* band 2 */
	{
		.freq_low	= 1075000,
		.freq_high	= 1177999,
		.VCOdivider	= 4,
		.progdata	= (0 << 19) | (0 << 9) | 0x80,
	},

	/* band 3 */
	{
		.freq_low	= 1178000,
		.freq_high	= 1295999,
		.VCOdivider	= 2,
		.progdata	= (0 << 19) | (1 << 9) | 0x01,
	},

	/* band 4 */
	{
		.freq_low	= 1296000,
		.freq_high	= 1431999,
		.VCOdivider	= 2,
		.progdata	= (0 << 19) | (1 << 9) | 0x02,
	},

	/* band 5 */
	{
		.freq_low	= 1432000,
		.freq_high	= 1575999,
		.VCOdivider	= 2,
		.progdata	= (0 << 19) | (1 << 9) | 0x04,
	},

	/* band 6 */
	{
		.freq_low	= 1576000,
		.freq_high	= 1717999,
		.VCOdivider	= 2,
		.progdata	= (0 << 19) | (1 << 9) | 0x08,
	},

	/* band 7 */
	{
		.freq_low	= 1718000,
		.freq_high	= 1855999,
		.VCOdivider	= 2,
		.progdata	= (0 << 19) | (1 << 9) | 0x10,
	},

	/* band 8 */
	{
		.freq_low	= 1856000,
		.freq_high	= 2035999,
		.VCOdivider	= 2,
		.progdata	= (0 << 19) | (1 << 9) | 0x20,
	},

	/* band 9 */
	{
		.freq_low	= 2036000,
		.freq_high	= 2150000,
		.VCOdivider	= 2,
		.progdata	= (0 << 19) | (1 << 9) | 0x40,
	},
};

static struct {
	u8 reg;
	u8 data;
} cx24117_regdata[] =
{
	{0x00, 0x03}, /* Reset system */
	{0x00, 0x00}, /* Clear reset */
	{0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */
	{0x04, 0x10}, /* MPEG */
	{0x05, 0x04}, /* MPEG */
	{0x06, 0x31}, /* MPEG (default) */
	{0x0b, 0x00}, /* Freq search start point (default) */
	{0x0c, 0x00}, /* Demodulator sample gain (default) */
	{0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */
	{0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */
	{0x0f, 0xfe}, /* FEC search mask (all supported codes) */
	{0x10, 0x01}, /* Default search inversion, no repeat (default) */
	{0x16, 0x00}, /* Enable reading of frequency */
	{0x17, 0x01}, /* Enable EsNO Ready Counter */
	{0x1c, 0x80}, /* Enable error counter */
	{0x20, 0x00}, /* Tuner burst clock rate = 500KHz */
	{0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */
	{0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */
	{0x29, 0x00}, /* DiSEqC LNB_DC off */
	{0x2a, 0xb0}, /* DiSEqC Parameters (default) */
	{0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */
	{0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */
	{0x2d, 0x00},
	{0x2e, 0x00},
	{0x2f, 0x00},
	{0x30, 0x00},
	{0x31, 0x00},
	{0x32, 0x8c}, /* DiSEqC Parameters (default) */
	{0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */
	{0x34, 0x00},
	{0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */
	{0x36, 0x02}, /* DiSEqC Parameters (default) */
	{0x37, 0x3a}, /* DiSEqC Parameters (default) */
	{0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */
	{0x44, 0x00}, /* Constellation (default) */
	{0x45, 0x00}, /* Symbol count (default) */
	{0x46, 0x0d}, /* Symbol rate estimator on (default) */
	{0x56, 0xc1}, /* Error Counter = Viterbi BER */
	{0x57, 0xff}, /* Error Counter Window (default) */
	{0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */
	{0x67, 0x83}, /* Non-DCII symbol clock */
};


static int cx24117_writereg(struct cx24117_state* state, int reg, int data)
{
	u8 buf[] = { reg, data };
	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
	int err;

	if (debug>1)
		printk("cx24117: %s:  write reg 0x%02x, value 0x%02x\n",
						__FUNCTION__,reg, data);

	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
		printk("%s: writereg error(err == %i, reg == 0x%02x,"
			 " data == 0x%02x)\n", __FUNCTION__, err, reg, data);
		return -EREMOTEIO;
	}

	return 0;
}

static int cx24117_readreg(struct cx24117_state* state, u8 reg)
{
	int ret;
	u8 b0[] = { reg };
	u8 b1[] = { 0 };
	struct i2c_msg msg[] = {
		{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 }
	};

	ret = i2c_transfer(state->i2c, msg, 2);

	if (ret != 2) {
		printk("%s: reg=0x%x (error=%d)\n", __FUNCTION__, reg, ret);
		return ret;
	}

	if (debug>1)
		printk("cx24117: read reg 0x%02x, value 0x%02x, result 0x%02x\n",reg, ret, b1[0]);

	return b1[0];
}

static int cx24117_write_mask(struct cx24117_state* state, int reg, int mask, int data)
{
    int val;

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

    /* Read a byte and check */
    val = cx24117_readreg(state, reg);
    if (val < 0)
        return val;

    /* Mask if off */
    val = val & ~mask;
    val |= data & 0xff;

    /* Write it out again */
    return cx24117_writereg(state, reg, val);
}


static int cx24117_set_inversion(struct cx24117_state* state, fe_spectral_inversion_t inversion)
{

	return 0;
}

static int cx24117_get_inversion(struct cx24117_state* state, fe_spectral_inversion_t *inversion)
{

	return 0;
}

static int cx24117_set_fec(struct cx24117_state* state, fe_code_rate_t fec)
{


	return 0;
}

static int cx24117_get_fec(struct cx24117_state* state, fe_code_rate_t *fec)
{

	return 0;
}

/* Approximation of closest integer of log2(a/b). It actually gives the
   lowest integer i such that 2^i >= round(a/b) */
static u32 cx24117_int_log2(u32 a, u32 b)
{
	u32 exp, nearest = 0;
	u32 div = a / b;
	if(a % b >= b / 2) ++div;
	if(div < (1 << 31))
	{
		for(exp = 1; div > exp; nearest++)
			exp += exp;
	}
	return nearest;
}

static int cx24117_set_symbolrate(struct cx24117_state* state, u32 srate)
{
	u32 tmp, sample_rate, ratio, sample_gain;
	u8 pll_mult;

	return 0;
}

/*
 * Based on the required frequency and symbolrate, the tuner AGC has to be configured
 * and the correct band selected. Calculate those values
 */
static int cx24117_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
{
	struct cx24117_state *state = fe->demodulator_priv;


	return 0;
}

/*
 * Tuner data is 21 bits long, must be left-aligned in data.
 * Tuner cx24109 is written through a dedicated 3wire interface on the demod chip.
 */
static int cx24117_pll_writereg(struct dvb_frontend* fe, struct dvb_frontend_parameters *p, u32 data)
{
	struct cx24117_state *state = fe->demodulator_priv;
	unsigned long timeout;

	dprintk("%s:  pll writereg called, data=0x%08x\n",__FUNCTION__,data);


	return 0;
}

static int cx24117_pll_tune(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
{
	struct cx24117_state *state = fe->demodulator_priv;
	u8 val;

	dprintk("frequency=%i\n", p->frequency);



	return 0;
}


static int cx24117_do_upload(struct cx24117_state* state, unsigned char* mem, unsigned int len, u8 dspCodeInReg)
{
    u8 buf[0x200+3];
    struct i2c_msg fw_msg = {.addr = 0,.flags = 0,.buf = buf,.len = 0 };
    int tx_size;
    int pos = 0;

    printk ("cx24117_do_upload(): Enter!\n");
    //printk ("cx24117_do_upload(): state->config->demod_address=0x%x\n", state->config->demod_address);
    //printk ("cx24117_do_upload(): mem=0x%x\n", mem);
    //printk ("cx24117_do_upload(): len=%d\n", len);
    //printk ("cx24117_do_upload(): dspCodeInReg=0x%x\n", dspCodeInReg);
	
    fw_msg.addr = state->config->demod_address;

    buf[0] = dspCodeInReg;
    while (pos != len) {

        /* Work out how much to send this time */
        tx_size = len - pos;
        if (tx_size > 0x200) {
            tx_size = 0x200;
        }

        /* Send the chunk */
        memcpy(buf + 1, mem + pos, tx_size);
        fw_msg.len = tx_size + 1;
        if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
            printk(KERN_ERR "cx24117: Error during firmware upload\n");
            return -EIO;
        }
        pos += tx_size;
        dprintk("%s: fw_pos=0x%x\n", __FUNCTION__, pos);
        //printk("%s: fw_pos=0x%x, buf[1]=0x%x\n", __FUNCTION__, pos, buf[1]);
    }

    return 0;
}


bool cx24117_download_firmware(struct cx24117_state* state, CX24117_SW_DOWNLOAD* p_download)
{
    unsigned long length = 0;
    unsigned long reg_value, crc8_value;
    unsigned long iostatus;
    unsigned long code_length;
    int result;

    if (p_download->code_length)
    {
        code_length = p_download->code_length-1; // skip crc8
    } 	else
    {
        return False;
    }

    /* Disable test ROM */
    result = cx24117_readreg(state, CX24117_PLL_LOCK_BOARDVER);
    printk ("CX24117_PLL_LOCK_BOARDVER=0x%x\n", result);
    if ((result & 0x04)) {
            //printk ("CX24117_PLL_LOCK_BOARDVER=0x%x\n", result);
        //if (cx24117_write_mask(state, CX24117_PLL_LOCK_BOARDVER, , 0x01) == False)
        //{
        //    CX24117_DBG_SET_ERROR(CX24117_IO_READ_ERR);
	 //       return(False);
        //}
        //reg_value = 1 - reg_value; /* toggle the value */
        //if (CX24117_WRITE (p_communication, CX24117_IIC_TEST_ROM, reg_value) == False)
        //{
        //    CX24117_DBG_SET_ERROR(CX24117_IO_WRITE_ERR);
	 //       return(False);
        //}
    }
 


    /* 1. Write 0 to address 251(0xFB) ( FTM enable = 0 ) */
    if (cx24117_write_mask(state,  CX24117_UC8051_FTM_ENABLE, 0x01, 0x00) <0)
    {
         return (False);
    }
	
    /* 2. Reset the UC8051 and other blocks by writing 0x76 to address 224(0xE0). */
    if (cx24117_writereg(state,  CX24117_RESET_CONTROL, 0x30) <0)
    {
         return (False);
    }	

    /* 3. Enable load (code memory) from external memory when I2C auto-increment is disabled by writing 0x81 to address 247(0xF7). */
    if (cx24117_writereg(state,  CX24117_DOWNLOAD_CONTROL, 0x81) <0)
    {
         return (False);
    }	
	
    /* 4. Load the initial address (16 bits) of the program location in the internal program memory to address {249(0xF9) & 248(0xF8)}. */
    if (cx24117_writereg(state,  CX24117_CODELOAD_START_ADDR_7_0, 0x00) <0)
    {
         return (False);
    }	
	
    if (cx24117_writereg(state,  CX24117_CODELOAD_START_ADDR_15_8,  0x00) <0)
    {
         return (False);
    }	
	
    /* 5. Load the program to the memory by writing to address 250(0xFA). (Using I2C multi-write protocol). */
    /* download data. */
    if (cx24117_do_upload (state, p_download->p_code, p_download->code_length, CX24117_CODELOAD_WDATA)<0)
    {
         printk ("cx24117_do_upload (): Error!\n");
         return (False);
		 
    }

    /* 6. Return to auto-increment mode and protect the program memory by writing 0x0C to address 247(0xF7).*/
    if (cx24117_writereg(state,  CX24117_DOWNLOAD_CONTROL,  0x0C) <0)
    {
         return (False);
    }	
		
    /* 7. Release reset of the UC8051 module by writing 0x00 to address 224(0xE0). */
    if (cx24117_writereg(state,  CX24117_RESET_CONTROL, 0x00) <0)
    {
         return (False);
    }	
	
    /* Now the UC8051 will start executing the program from address 0 in the program memory. */
	
    /* 8. Verify checksum. */
    code_length = p_download->code_length - 2;

    if (cx24117_writereg(state,  CX24117_CODELOAD_START_ADDR_7_0, code_length & 0xFF) <0)
    {
         return (False);
    }	
	
    if (cx24117_writereg(state,  CX24117_CODELOAD_START_ADDR_15_8,  code_length >> 8) <0)
    {
         return (False);
    }		

    if (cx24117_writereg(state,  CX24117_DOWNLOAD_CONTROL, 0x00) <0)
    {
         return (False);
    }	

    if (cx24117_writereg(state,  CX24117_MBIST_DC,  0x00) <0)
    {
         return (False);
    }	
	
    if (cx24117_writereg(state,  CX24117_MBIST_DC,  0x07) <0)
    {
         return (False);
    }	


    printk ("Sleep 500ms...\n");    
    msleep (500);
  	
    /* Get the CRC8 byte */
    crc8_value=cx24117_readreg(state, CX24117_XPRGRM_CRC8);
    printk ("Firmware CRC is 0x%x\n", crc8_value);
	
    if (cx24117_write_mask(state,  CX24117_MBIST_SUB_SEL, 0x07, 0x00) <0)
    {
         return (False);
    }	
	
    if (p_download->p_code[code_length+1] != crc8_value)
    {
        printk ("Firmware CRC is mismatch!p_code[code_length+1] is 0x%x\n", p_download->p_code[code_length+1]);
        return (False);
    }

    return (True);
}

static bool cx24117_set_dividers(struct cx24117_state* state, CX24117_DIVIDER_SETTINGS* p_divider_setting)
{
    unsigned long reg_value;
    unsigned char offset = 0;

    if (p_divider_setting == 0)
    {
        return (False);
    }

    printk ("cx24117_set_dividers() Enter!\n");
	
    if (p_divider_setting->toggle_pll_en == True)
    {
        if (cx24117_write_mask(state, CX24117_AFE_PLL_ENABLE, 0x01, 0x00) <0 )
        {
 	     printk ("write CX24117_AFE_PLL_ENABLE return Error!\n");         
            return (False);
        }
    }

    if (p_divider_setting->update == True)
    {
        /* Commonn */
        reg_value = p_divider_setting->pll_out_div_2;
        if (cx24117_write_mask(state, CX24117_AFE_PLL_DIV_BY_2, 0x80, reg_value) <0)
        {
 	     printk ("write CX24117_AFE_PLL_DIV_BY_2 return Error!\n");       
            return (False);
        }  

		// Address 236
        reg_value = p_divider_setting->pll_lclk_div;
        if (cx24117_write_mask(state, CX24117_PLL_LCLK_DIV, 0x0F, reg_value) <0)
        {
            return (False);
        }        
		// Address 237
        reg_value = p_divider_setting->pll_mt_clk_div;
        if (cx24117_write_mask(state, CX24117_PLL_MT_CLK_DIV, 0x0F, reg_value) <0)
        {

            return (False);
        }        
		// Address 238
        reg_value = p_divider_setting->pll_smooth_clk_div;
        if (cx24117_write_mask(state, CX24117_PLL_SMTH_DIV, 0x0F, reg_value) <0)
        {

            return (False);
        }  
	       // Address 239
        reg_value = p_divider_setting->pll_uc8051_clk_div; 
	 if (cx24117_write_mask(state, CX24117_PLL_UC8051_DIV, 0x0F, reg_value) <0)
	 {

            return (False);
        }

        reg_value = p_divider_setting->pll_ftx_clk_div; 
        if (cx24117_write_mask(state, CX24117_PLL_FTX_CLK_DIV, 0x0F, reg_value) <0)
        {

             return (False);
         }
		
         reg_value = p_divider_setting->pll_ob_mux_clk_div_and_tclk_div; 
         if (cx24117_writereg(state, CX24117_PLL_OB_MUX_CLK_DIV_TCLK_DIV,  reg_value) <0)
         {

              return (False);
         }
    }

    if (p_divider_setting->demod[0].update == True)
    {
            reg_value = p_divider_setting->demod[0].demod_clk_div;
            if (cx24117_write_mask(state, CX24117_PLL_CH0_DIV, 0x0F, reg_value) <0)
            {
                return (False);
            }         
           
            reg_value = p_divider_setting->demod[0].adc_clk_div;   
            if (cx24117_write_mask(state, CX24117_AFE_ADC0_CLK_DIV, 0x07, reg_value) <0)
            {
                return (False);
            } 
    }

    if (p_divider_setting->demod[1].update == True)
    {
            reg_value = p_divider_setting->demod[1].demod_clk_div;
            if (cx24117_write_mask(state, CX24117_PLL_CH1_DIV, 0x0F, reg_value) <0)
            {
                return (False);
            }         
           
            reg_value = p_divider_setting->demod[1].adc_clk_div;   
            if (cx24117_write_mask(state, CX24117_AFE_ADC1_CLK_DIV, 0x07, reg_value) <0)
            {
                return (False);
            } 
    }

    if (p_divider_setting->toggle_pll_en == True)
    {
        if (cx24117_write_mask(state, CX24117_AFE_PLL_ENABLE, 0x01, 0x01) <0)
        {
            return (False);
        }
    }
    return (True);
}


static bool cx24117_set_AFE(struct cx24117_state* state, CX24117_AFE_SETTINGS* p_adc_setting)
{
    unsigned long reg_value;
    unsigned char offset;

    printk ("cx24117_set_AFE() Enter!\n");

    if (p_adc_setting->demod[0].update == True)
    {
        reg_value = p_adc_setting->demod[0].afe_actl_7_0;
        if (cx24117_writereg(state,  CX24117_AFE_ACNTL0_7_0, reg_value) <0)
        {
            return (False);
        }         
           
        reg_value = p_adc_setting->demod[0].afe_actl_12_8;   
        if (cx24117_write_mask(state,  CX24117_AFE_ACNTL0_12_8, 0x1F, reg_value) <0)
        {
             return (False);
        }              
    }

    if (p_adc_setting->demod[1].update == True)
    {
        reg_value = p_adc_setting->demod[1].afe_actl_7_0;
        if (cx24117_writereg(state,  CX24117_AFE_ACNTL1_7_0,  reg_value) <0)
        {
            return (False);
        }         
           
        reg_value = p_adc_setting->demod[1].afe_actl_12_8;   
        if (cx24117_write_mask(state,  CX24117_AFE_ACNTL1_12_8, 0x1F, reg_value) <0)
        {
             return (False);
        }              
    }
	
    if (p_adc_setting->update == True)
    {
        reg_value = p_adc_setting->afe_band_gap_control;   
        if (cx24117_write_mask(state,  CX24117_AFE_BG, 0x07, reg_value) <0)
        {
            return (False);
        }
    }

    return (True);
}


static bool cx24117_get_dividers(struct cx24117_state* state, CX24117_DIVIDER_SETTINGS* p_divider_setting)
{
    unsigned long reg_value;
    unsigned char offset = 0;


    printk ("cx24117_get_dividers(): Enter!\n");
    if (p_divider_setting == 0)
    {
        return (False);
    }

    /* Commonn */
    reg_value=cx24117_readreg(state, CX24117_AFE_PLL_DIV_BY_2);
    p_divider_setting->pll_out_div_2 = reg_value & 0x80;     //0x40?

    reg_value=cx24117_readreg(state, CX24117_AFE_PLL_LOOP_DIV);
    p_divider_setting->pll_main_div = reg_value & 0x3F;
	
    reg_value=cx24117_readreg(state, CX24117_PLL_LCLK_DIV);
    p_divider_setting->pll_lclk_div = reg_value & 0x0F;                              //0x6

    reg_value=cx24117_readreg(state, CX24117_PLL_MT_CLK_DIV);
    p_divider_setting->pll_mt_clk_div = reg_value & 0x0F;                         //0x5

    reg_value=cx24117_readreg(state, CX24117_PLL_SMTH_DIV);
    p_divider_setting->pll_smooth_clk_div = reg_value & 0x0F;                 //0x3
	
    reg_value=cx24117_readreg(state, CX24117_PLL_UC8051_DIV);
    p_divider_setting->pll_uc8051_clk_div = reg_value & 0x0F;                 //0x5

    reg_value=cx24117_readreg(state, CX24117_PLL_CH0_DIV);
    p_divider_setting->demod[0].demod_clk_div = reg_value & 0x0F;       //0x4
        
    reg_value=cx24117_readreg(state, CX24117_AFE_ADC0_CLK_DIV);
    p_divider_setting->demod[0].adc_clk_div = reg_value & 0x07;           //0x2

    p_divider_setting->demod[0].update = True;
    
    reg_value=cx24117_readreg(state, CX24117_PLL_CH1_DIV);
    p_divider_setting->demod[1].demod_clk_div = reg_value & 0x0F;
        
    reg_value=cx24117_readreg(state, CX24117_AFE_ADC1_CLK_DIV);
    p_divider_setting->demod[1].adc_clk_div = reg_value & 0x07;   

    p_divider_setting->demod[1].update = True;
	
    return (True);
}

bool cx24117_execute_LLF(struct cx24117_state* state, CX24117_LLF* p_llf)
{      
    unsigned char  index;
    unsigned char  token_byte;
    unsigned short poll_count;       
    unsigned char  llf_opcode_addr;
    unsigned char  llf_args_start_addr;
    unsigned char  llf_write_control_addr;
    unsigned char  max_reply_offset;
    unsigned char  llf_reply_offset;

    printk ("cx24117_execute_LLF(): Enter!\n");
    printk ("cx24117_execute_LLF(): LLFOpCode=0x%x!\n", p_llf->op_code);	

    if (p_llf == 0) 
    {
        return (False);
    }
   
    token_byte  = CX24117_LLF_POLL_READY_TOKEN;

    llf_opcode_addr         = CX24117_LLF_OPCODE_ADDR;
    llf_args_start_addr     = CX24117_LLF_ARG_BASE_ADDR;
    llf_write_control_addr = CX24117_LLF_CONTROL_BYTE_ADDR;

     /* -- Process the LLF packet -- */
    /* Write the OpCode first */
    if (cx24117_writereg(state, llf_opcode_addr, p_llf->op_code) <0)
    {
         return (False);
    }		 
    
    /* Write the required number of LLF arguments */
    if (p_llf->argument_length > CX24117_LLF_MAX_WRITE_ARGS)
    {
        p_llf->argument_length = CX24117_LLF_MAX_WRITE_ARGS;
    }

    for (index = 0; index < p_llf->argument_length; index++)
    {
        if (cx24117_writereg(state, (llf_args_start_addr + index), p_llf->data[index]) <0)
        {
             return (False);
        }	
    }

    /* Write the control byte */
    if (cx24117_writereg(state, llf_write_control_addr, token_byte) <0)
    {
         return (False);
    }	

    /* Wait for LLF execution */
    for (poll_count = 0; poll_count < CX24117_LLF_MAX_READ_CONTROL_BYTE_POLL; poll_count++)
    {
        token_byte = cx24117_readreg(state, llf_write_control_addr);

        /* Verify it is reset to zero by the microcode */
        if (token_byte == CX24117_LLF_DONE_TOKEN)
        {
            break;
        }
    }

    /* Read the return data if present */
    if (token_byte == CX24117_LLF_DONE_TOKEN)
    {
        if (p_llf->reply_length != 0x00)
        {
            /* Maximum allowed return bytes for this session */
            max_reply_offset = (unsigned char)(CX24117_LLF_MAX_ARG_SIZE - p_llf->argument_length);

            if (p_llf->reply_length > max_reply_offset)
            {
                p_llf->reply_length = max_reply_offset;
            }

            llf_reply_offset = (unsigned char)(llf_args_start_addr + p_llf->argument_length);

            for (index = 0; index < p_llf->reply_length; index++)
            {
                p_llf->data[index] = cx24117_readreg(state, (llf_reply_offset + index)); 

            }
        }
    }
    else /* Token mismatch */
    {
        return(False);
    }

    printk ("cx24117_execute_LLF(): execute successfully.\n");
    return (True);
} /* cx24117_ExecuteLLF */


bool cx24117_set_VCO_frequency(struct cx24117_state* state, 
                        unsigned short                 crystal_freq_khz, 
                        unsigned short                 nw_crystal_freq_khz,
                        unsigned char                  pll_sdiv,
			   CX24117_TUNER_REF_CLOCKOUT_DIV tuner_ref_clkout_div)
{
    CX24117_LLF                          llf;
    CX24117_DIVIDER_SETTINGS dividers;
    CX24117_CHIP_TYPE               chip_type_val = CX24117_CHIP_TYPE_CX24117;
	
    unsigned short                         inverse_vco_frequency_khz = 0, ref_clkout_div = 1;
    unsigned long                          vco_frequency_khz = 0;
    unsigned long                          result;

    unsigned char                          demod_ref_clock_div;
    unsigned long                          chip_type;


    printk ("cx24117_set_VCO_frequency(): Enter!\n");
                                
    if (state == 0)
    {
        return (False);
    }
    
    if (cx24117_get_dividers(state, &dividers) == False)
    {
         return (False);
    }
		
    chip_type = cx24117_readreg (state, CX24117_CHIP_TYPE_REG);
    printk ("chip type is 0x%x\n", chip_type);

    /* Determine the chip-type */
    if (chip_type == 0x02)
    {
        chip_type_val = CX24117_CHIP_TYPE_CX24117;
    }

    demod_ref_clock_div = (unsigned char)(dividers.pll_out_div_2 + 1); /* HW values : 0 or 1 */

    /* Get the VCO_frequency_in_kHz = crystal_freq_kHz. x PLL_Multiplier */
    if (tuner_ref_clkout_div == CX24117_TUNER_REF_CLOCKOUT_DIV2)
    {
        ref_clkout_div = 2;
    }
    vco_frequency_khz = (unsigned long)((unsigned long)pll_sdiv * (unsigned long)((crystal_freq_khz/ref_clkout_div) / demod_ref_clock_div));	

    /* Inverse VCO frequency = 2^34 / (vco_frequency_in_kHz) */
    inverse_vco_frequency_khz = 2^34 / (vco_frequency_khz);

    /* Improve precision */
    // result = 2^34 - (inverse_vco_frequency * vco_frequency)
    result = 2^34 - (inverse_vco_frequency_khz * vco_frequency_khz);

    if (result > (vco_frequency_khz / 2))
    {
        inverse_vco_frequency_khz++;
    }

    llf.op_code = CX24117_OPCODE_SET_VCO_FREQUENCY;

    llf.data[0] = (unsigned char)((vco_frequency_khz >> 16) & 0xFF); /* [23:16] */
    llf.data[1] = (unsigned char)((vco_frequency_khz >> 8)  & 0xFF); /* [15:8]  */
    llf.data[2] = (unsigned char)(vco_frequency_khz & 0xFF);         /* [ 7:0]  */

    llf.data[3] = (unsigned char)((inverse_vco_frequency_khz >> 8) & 0xFF); /* MSB */
    llf.data[4] = (unsigned char)(inverse_vco_frequency_khz & 0xFF);        /* LSB */
    
    llf.data[5] = dividers.pll_smooth_clk_div;
    llf.data[6] = (unsigned char)((crystal_freq_khz >> 8) & 0xFF); /* MSB */
    llf.data[7] = (unsigned char)(crystal_freq_khz & 0xFF);        /* LSB */
    llf.data[8] = dividers.pll_lclk_div;
    llf.data[9] = (unsigned char)chip_type_val;
    /* Following parameters are for network demod (optional when network demod is not used).*/
    llf.data[10] = (unsigned char)((nw_crystal_freq_khz >> 8) & 0xFF); 
    llf.data[11] = (unsigned char)(nw_crystal_freq_khz & 0xFF);        

    llf.argument_length = 12;      /* Number of LLF arguments    */
    llf.reply_length = 0;          /* Number of return arguments */

    if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
    {
        return (False);
    }

    return (True);
}



static int cx24117_initfe(struct dvb_frontend* fe)
{
    CX24117_SW_DOWNLOAD  download;
    CX24117_DIVIDER_SETTINGS dividers;
    CX24117_AFE_SETTINGS     afe_setting;
    CX24117_LLF llf;
	
    struct cx24117_state *state = fe->demodulator_priv;
    int i,result;
    int err=0, reg_value;
    unsigned long           start, stop;
    int ChipID, ChipVer;

    unsigned char basic_settings;
    unsigned char mpeg_control_signal;
    unsigned char data_output;
    unsigned char clock_configuration;
    unsigned char data_hold_and_setup;

    struct dvb_frontend_parameters myparams;
    fe_status_t mystatus;

    CX24117_ACQ_STATE	acqState;
    CX24117_LOCKIND	lockInd;


    dprintk("%s:  init frontend\n",__FUNCTION__);
    printk ("cx24117_initfe(): Enter. \n");
	 
    if (state->initialised)
    {
        return 0;
    }
	
    p_config.chip_type = CX24117_CHIP_TYPE_CX24117;
    p_config.crystal_frequency_hz = 40444000;
    p_config.nw_crystal_frequency_hz = 10111000;
    p_config.pll_multiplier = 12;
    p_config.association[0].demod_sel=CX24117_DEMOD_CHANNEL_SEL0;
    p_config.association[0].input_spec_inverted=0;
    p_config.association[0].lnb_sel=CX24117_LNB_SEL_A;
    p_config.association[0].tuner_info.tuner_type=CX24117_TUNER_TYPE_DUAL;
    p_config.association[0].tuner_info.tuner_channel=CX24117_TUNER_CHANNEL_A;
    p_config.association[0].tuner_info.tuner_ref_clkout_div=CX24117_TUNER_REF_CLOCKOUT_DIV1;

    p_config.association[1].demod_sel=CX24117_DEMOD_CHANNEL_SEL1;
    p_config.association[1].input_spec_inverted=0;
    p_config.association[1].lnb_sel=CX24117_LNB_SEL_B;
    p_config.association[1].tuner_info.tuner_type=CX24117_TUNER_TYPE_DUAL;
    p_config.association[1].tuner_info.tuner_channel=CX24117_TUNER_CHANNEL_B;
    p_config.association[1].tuner_info.tuner_ref_clkout_div=CX24117_TUNER_REF_CLOCKOUT_DIV2;

   p_mpeg.global_setting.output_mode=CX24117_OUTPUT_MODE_PARALLEL;
   p_mpeg.global_setting.channel_priority=CX24117_CHANNEL_PRIORITY_CH0_CH1_CH2;
   p_mpeg.global_setting.tristate_config=CX24117_TSTATE_MPEG_OFF;
   p_mpeg.global_setting.setup_hold_scale=0x01;

    p_mpeg.demod0.clk_parity_mode=CX24117_OUTPUT_CLK_CONTINUOUS;
    p_mpeg.demod0.insert_sync_byte=CX24117_SYNC_BYTE_INSERT;
    p_mpeg.demod0.tei_bit=CX24117_TEI_BIT_NOT_SET;
    p_mpeg.demod0.invert_clk=CX24117_INVERT_CLK_CHANGING_ON_FALLING;
    p_mpeg.demod0.null_data_mode=CX24117_NULL_DATA_MODE_DISABLED;
    p_mpeg.demod0.null_data_value=CX24117_NULL_DATA_VALUE_LOW;
    p_mpeg.demod0.dstart_signal_polarity=CX24117_CNTL_SIGNAL_POLARITY_ACTIVE_HIGH;
    p_mpeg.demod0.dfail_signal_polarity=CX24117_CNTL_SIGNAL_POLARITY_ACTIVE_HIGH;
    p_mpeg.demod0.dvalid_signal_polarity=CX24117_CNTL_SIGNAL_POLARITY_ACTIVE_HIGH;
    p_mpeg.demod0.dstart_signal_width=CX24117_START_SIGNAL_WIDTH_BIT_WIDE;
    p_mpeg.demod0.dfail_signal_width=CX24117_CNTL_SIGNAL_MODE_FIRST_BYTE;
    p_mpeg.demod0.dvalid_signal_width=CX24117_CNTL_SIGNAL_MODE_ENTIRE_PACKET;
    p_mpeg.demod0.data_hold=5;
    p_mpeg.demod0.data_setup=5;
    p_mpeg.demod0.extra_clocks_between_packets=16;

    start = jiffies;

    /* Reset the PLL */
    err=cx24117_write_mask(state, CX24117_AFE_PLL_ENABLE, 0x01,0x00);    
    err=cx24117_write_mask(state, CX24117_AFE_PLL_ENABLE, 0x01,0x01);  

    /* Disable the PLL */
    err=cx24117_write_mask(state, CX24117_AFE_PLL_ENABLE, 0x01,0x00);

    /* Init demod */
    if (!err) 
    {	
        /* Disable test ROM */
        result = cx24117_readreg(state, CX24117_PLL_LOCK_BOARDVER);
        printk ("CX24117_PLL_LOCK_BOARDVER=0x%x\n", result);
        if ((result & 0x04)) {
            //printk ("CX24117_PLL_LOCK_BOARDVER=0x%x\n", result);
        }
    }

    /* Init demod */
    if (!err) 
    {
        /* Disable FTM usage by I2C */
	 err=cx24117_write_mask(state, CX24117_UC8051_FTM_ENABLE, 0x01,0x00);
        /* Enable/Disable FTM Tx */		
        err=cx24117_write_mask(state, CX24117_FTM_BIT_ENABLE, 0x01,0x00);
    }

    if (!err) 
    {
        /* DiSEqC AFE settings */
	 err=cx24117_write_mask(state, CX24117_DISEQC0_AFE, 0x07,0x04);
        err=cx24117_write_mask(state, CX24117_DISEQC1_AFE, 0x07,0x04);
    }

    if (!err) 
    {
        /* Enable all clocks */
	 err=cx24117_writereg(state, CX24117_SHUT_CLK_CNTL_7_0, 0x00);
        err=cx24117_write_mask(state, CX24117_SHUT_CLK_CNTL_13_8, 0x3f,0x00);
    }

    if (!err) 
    {
        dividers.toggle_pll_en           = True;
	 dividers.update                     = True;
	 dividers.pll_out_div_2            = CX24117_REF_CLOCK_DIV_1;                   /* 235[7]   */
	 dividers.pll_main_div             = p_config.pll_multiplier;                  /* 235[5:0] */
	 dividers.pll_lclk_div               = CX24117_PLL_LDPC_CLOCK_DIVIDER_DEFAULT;    /* 236[3:0] */
	 dividers.pll_mt_clk_div          = CX24117_PLL_MPEG_TX_CLOCK_DIVIDER_DEFAULT; /* 237[3:0] */
	 dividers.pll_smooth_clk_div    = CX24117_PLL_CLOCK_SMOOTH_DIVIDER_DEFAULT;  /* 238[3:0] */
	 dividers.pll_uc8051_clk_div    = CX24117_PLL_UC8051_DIV_DEFAULT;			 /* 239[4:0] */
	 dividers.pll_ftx_clk_div           = CX24117_PLL_FTX_CLK_DIV_DEFAULT;			 /* 243[4:0] */

	 dividers.pll_ob_mux_clk_div_and_tclk_div = CX24117_PLL_OB_MUX_CLK_DIV_TCLK_DIV_DEFAULT; /* a244[7:0] */

	 dividers.demod[0].update        = True;
	 dividers.demod[0].demod_clk_div = CX24117_PLL_DEMOD_CLOCK_DIV; /* 240[3:0] */
	 dividers.demod[0].adc_clk_div   = CX24117_ADC_MOD_SEL_DIV;     /* 230[2:0] */

	 dividers.demod[1].update        = True;
	 dividers.demod[1].demod_clk_div = CX24117_PLL_DEMOD_CLOCK_DIV; /* 241[3:0] */
	 dividers.demod[1].adc_clk_div   = CX24117_ADC_MOD_SEL_DIV;     /* 231[2:0] */

	 dividers.demod[2].update        = True;
	 dividers.demod[2].demod_clk_div = CX24117_PLL_DEMOD_CLOCK_DIV; /* 242[3:0] */
	 dividers.demod[2].adc_clk_div   = CX24117_ADC_MOD_SEL_DIV;     /* 232[2:0] */

	 if (cx24117_set_dividers(state, &dividers) == False)
	 {
	     printk ("cx24117_set_dividers() return Error!\n");
	     err=1;
            return (False);			
	 }
    }


    if (!err) 
    {
        /* Set the AFE */
        afe_setting.demod[0].update        = True;
        afe_setting.demod[0].afe_actl_7_0  = 0x00; 
        afe_setting.demod[0].afe_actl_12_8 = 0x00;

        afe_setting.demod[1].update        = True;
        afe_setting.demod[1].afe_actl_7_0  = 0x00; 
        afe_setting.demod[1].afe_actl_12_8 = 0x00;

        afe_setting.demod[2].update        = True;
        afe_setting.demod[2].afe_actl_7_0  = 0x00; 
        afe_setting.demod[2].afe_actl_12_8 = 0x00;

        afe_setting.update = True;
        afe_setting.afe_band_gap_control = 0x03;

        if (cx24117_set_AFE(state, &afe_setting) == False)
        {
            return (False);
        }
    }


    if (!err) 
    {
        err=cx24117_write_mask(state, CX24117_AFE_PLL_LOOP_DIV, 0x3F, CX24117_DEFAULT_PLL_MULT);
    }

    if (!err) 
    {
        /* Firmware download */
        download.p_code      = (unsigned char*)cx24117_firmware;
        download.code_length = cx24117_firmware_length;

	 if (cx24117_download_firmware(state, &download)== False)
        {
	     printk ("cx24117_download_firmware() return Error!\n");
	     err=1;        
            return (False);
        } 
	 else
        {
	     printk ("cx24117_download_firmware() succeed!\n");        
        }
	 	
    }


    if (!err) 
    {
         /* Pass the additional spectral inversion */
         llf.op_code  = CX24117_OPCODE_DEMOD_INIT;
	
         llf.data[0]  = (unsigned char)p_config.association[0].input_spec_inverted;
         llf.data[1]  = (unsigned char)p_config.association[1].input_spec_inverted;

         llf.argument_length = 3;
         llf.reply_length = 0;

         if (cx24117_execute_LLF(state, &llf) == False) 
         {
             return (False);
         }
    }

    /* Set VCO Frequency */ /* Call this before TunerInit to pass the XTAL frequency. */
    if (!err) 
    {
        if (cx24117_set_VCO_frequency(state, 
                                (unsigned short)(p_config.crystal_frequency_hz/1000), 
                                (unsigned short)(p_config.nw_crystal_frequency_hz/1000),
                                p_config.pll_multiplier, 
				    p_config.association[0].tuner_info.tuner_ref_clkout_div) == False)
        {
            return (False);
        }

    }

    /* Tuner init */
    if (!err) 
    {	
        llf.op_code = CX24117_OPCODE_TUNER_INIT;

        llf.data[0] = (unsigned char)CX24117_DEMOD_CHANNEL_SEL0; 
        llf.data[1] = (unsigned char)p_config.association[0].tuner_info.tuner_type; 
        llf.data[2] = (unsigned char)p_config.association[0].tuner_info.tuner_channel;
        llf.data[3] = (unsigned char)p_config.association[0].tuner_info.tuner_ref_clkout_div; 



        llf.data[4] = (unsigned char)CX24117_DEMOD_CHANNEL_SEL_NONE; 
        llf.data[5] = (unsigned char)CX24117_TUNER_TYPE_NONE; 
        llf.data[6] = (unsigned char)CX24117_TUNER_CHANNEL_NONE;
        llf.data[7] = (unsigned char)0; 


        llf.data[8] = (unsigned char)CX24117_DEMOD_CHANNEL_SEL_NONE; 
        llf.data[9] = (unsigned char)CX24117_TUNER_TYPE_NONE; 
        llf.data[10]= (unsigned char)CX24117_TUNER_CHANNEL_NONE;
        llf.data[11]= (unsigned char)0; 


        llf.argument_length = 12;       
        llf.reply_length = 0;           

        if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
        {
            err=1;
            return (False);
        }

        /* check for error code */
        reg_value=cx24117_readreg (state, CX24117_LAST_TUNER_DRIVER_ERROR);
        printk ("Tuner init return 0x%x\n", reg_value);
		
        switch (reg_value)
        {
        case 0UL:
            printk ("Tuner init OK!\n");
	     break;		
        case 1UL:
            printk ("Invalid Tuner I2C address!\n");
	     break;
        case 2UL:
            printk ("Invalid Tuner detected!\n");
	     break;
        case 3UL:
            printk ("PLL lock detect time out!\n");
	     break;
        case 4UL:
            printk ("I2C transaction failed!\n");
	     break;		
        default:
            break;
        }
    }

    /* Apply MPEG settings */
    if (!err) 
    {

        llf.op_code = CX24117_OPCODE_MPEG_CONFIG_GLOBAL;

        llf.data[0] = (unsigned char)p_mpeg.global_setting.output_mode;
        llf.data[1] = (unsigned char)p_mpeg.global_setting.channel_priority;
        llf.data[2] = (unsigned char)p_mpeg.global_setting.tristate_config; 
        llf.data[3] = (unsigned char)p_mpeg.global_setting.setup_hold_scale; 

#if CX24117_INCLUDE_MPEG_EXTRA_CLOCK
	cx24117_extra_clocks.output_mode = p_mpeg.global_setting.output_mode;
#endif /* CX24117_INCLUDE_MPEG_EXTRA_CLOCK */

        llf.argument_length = 4;         
        llf.reply_length = 0;     

        if (cx24117_execute_LLF(state, &llf) == False) 
        {
            err=1;
            return (False);
        }
    }

    if (!err) 
    {	
        basic_settings  = (unsigned char)((int)p_mpeg.demod0.clk_parity_mode & 0x01);
        basic_settings |= ((int)p_mpeg.demod0.tei_bit << 1)  & 0x02;

        mpeg_control_signal  = (unsigned char)((int)p_mpeg.demod0.dstart_signal_width & 0x01);
        mpeg_control_signal |= ((int)p_mpeg.demod0.dvalid_signal_width << 1) & 0x02;
        mpeg_control_signal |= ((int)p_mpeg.demod0.dfail_signal_width  << 2) & 0x04;

        mpeg_control_signal |= ((int)p_mpeg.demod0.dstart_signal_polarity << 4) & 0x10;
        mpeg_control_signal |= ((int)p_mpeg.demod0.dvalid_signal_polarity << 5) & 0x20;
        mpeg_control_signal |= ((int)p_mpeg.demod0.dfail_signal_polarity  << 6) & 0x40;

        data_output  = (unsigned char)((int)p_mpeg.demod0.null_data_value & 0x01);
        data_output |= ((int)p_mpeg.demod0.null_data_mode << 1) & 0x02;
        data_output |= ((int)p_mpeg.demod0.insert_sync_byte << 2) & 0x04;

        clock_configuration = (unsigned char)p_mpeg.demod0.invert_clk;

        data_hold_and_setup  = p_mpeg.demod0.data_setup;
        data_hold_and_setup |= (p_mpeg.demod0.data_hold << 4) & 0xF0;

        llf.op_code = CX24117_OPCODE_MPEG_CONFIG;

        llf.data[0] = (unsigned char)0;
        llf.data[1] = (unsigned char)basic_settings;
        llf.data[2] = (unsigned char)mpeg_control_signal; 
        llf.data[3] = (unsigned char)data_output; 
        llf.data[4] = (unsigned char)clock_configuration; 
        llf.data[5] = (unsigned char)data_hold_and_setup; 
	
#if CX24117_INCLUDE_MPEG_EXTRA_CLOCK
    llf.data[6] = (unsigned char)p_mpeg.demod0.extra_clocks_between_packets;

    cx24117_extra_clocks.demod[0].extra_clocks_between_packets = p_mpeg.demod0.extra_clocks_between_packets;
    cx24117_extra_clocks.demod[0].insert_sync_byte = p_mpeg.demod0.insert_sync_byte;

    if (p_mpeg.demod0.extra_clocks_between_packets != 0)
    {
         cx24117_extra_clocks.demod[0].mpeg_clock_rate_adjust = True;
    }
    else
    {
        cx24117_extra_clocks.demod[0].mpeg_clock_rate_adjust = False;
    }
#else
	llf.data[6] = 0;
#endif

        llf.argument_length = 7;         
        llf.reply_length = 0;     

        if (cx24117_execute_LLF(state, &llf) == False) 
        {
            return (False);
        }
    }
	
    state->initialised = 1;
	   
    stop = jiffies;
    printk(KERN_INFO "%s: init done in %ld ms\n", __FUNCTION__, (stop-start)*1000/HZ);	


    cx24117_tune(fe, &myparams, i, &result, &mystatus);

    mdelay (5000);
	
    if ((cx24117_monitor(fe, 0, &acqState, &lockInd)) == FALSE)
    {
	/* probably an invalid parameter */
	printk("cx24117_monitor() failed\n");	
     }
     else if (acqState != CX24117_ACQ_LOCKED_AND_TRACKING)
     {
	/* system is locked to signal and tracking it. */
	printk("Fail Lock\n");	
     }
	
    return 0;
}



static bool cx24117_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
{
	struct cx24117_state *state = fe->demodulator_priv;
       CX24117_LNB_SEL                  lnb_sel;
       CX24117_LNBDC_LEVEL          lnb_dc_level;
	CX24117_LLF                          llf;
	u8 val;

       lnb_sel =	CX24117_LNB_SEL_A;
	switch (voltage) {
	case SEC_VOLTAGE_13:
		dprintk("%s: setting voltage 13V\n", __FUNCTION__);
		lnb_dc_level = CX24117_LNBDC_LOW;
		break;
	case SEC_VOLTAGE_18:
		dprintk("%s: setting voltage 18V\n", __FUNCTION__);
		lnb_dc_level = CX24117_LNBDC_HIGH;		
		break;
	case SEC_VOLTAGE_OFF:
		/* already handled in cx88-dvb */
		return 0;
	default:
		return -EINVAL;
	};



    llf.op_code = CX24117_OPCODE_LNB_SET_DC_LEVEL;

    llf.data[0] = (unsigned char)lnb_sel;
    llf.data[1] = (unsigned char)lnb_dc_level;
    llf.argument_length = 2; 
    llf.reply_length = 0;    

    if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
    {
        return (False);
    }

    return(True);
}

static int cx24117_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
{
	struct cx24117_state *state = fe->demodulator_priv;
       CX24117_LNB_SEL        lnb_sel;
       CX24117_LNBTONE       lnb_tone;
       CX24117_LLF                llf;	   
	u8 val;

       lnb_sel = CX24117_LNB_SEL_A;
	   
	switch (tone) {
	case SEC_TONE_ON:
		dprintk("%s: setting tone on\n", __FUNCTION__);
              lnb_tone=CX24117_LNBTONE_ON;		
		break;
	case SEC_TONE_OFF:
		dprintk("%s: setting tone off\n",__FUNCTION__);
		lnb_tone=CX24117_LNBTONE_OFF;
		break;
	default:
		printk("%s: CASE reached default with tone=%d\n", __FUNCTION__, tone);
		return -EINVAL;
	}

    llf.op_code = CX24117_OPCODE_LNB_PCB_CONFIG;

    llf.data[0] = (unsigned char)lnb_sel;
    llf.data[1] = (unsigned char)CX24117_DEFAULT_LNB_DC_POLARITY; 
    llf.data[2] = (unsigned char)CX24117_DEFAULT_LNB_OPEN_DRAIN; 
    llf.data[3] = (unsigned char)lnb_tone; 

    llf.argument_length = 4;  /* Number of LLF arguments */
    llf.reply_length = 0;     /* Number of return arguments */

    if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
    {
        return (False);
    }

    return (True);
}




#if CX24117_INCLUDE_FTM
 
#endif

#if CX24117_INCLUDE_DISEQC2
/* wait for diseqc queue to become ready (or timeout) */
static void cx24117_wait_for_diseqc(struct cx24117_state *state)
{
	dprintk("%s:\n",__FUNCTION__);

}

static int cx24117_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd)
{
	struct cx24117_state *state = fe->demodulator_priv;
	int i, val, tone;

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


	return 0;
}

static int cx24117_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
{
	struct cx24117_state *state = fe->demodulator_priv;
	int val, tone;

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


	return 0;
}

#endif

/*
 * Configured to return the measurement of errors in blocks, because no UCBLOCKS value
 * is available, so this value doubles up to satisfy both measurements
 */

#if CX24117_INCLUDE_BER
bool cx24117_get_BER (struct cx24117_state *state,
                const CX24117_DEMOD_CHANNEL_SEL     demod_sel,
                unsigned long*                                        p_corr_bit_err_rate,      
                unsigned short*                                       p_uncorr_frame_err_rate,  
                unsigned short*                                       p_ber_window_adjustment,
                CX24117_MSTATUS*                                p_mstatus)
{
    static unsigned long last_measurement_count[CX24117_MAX_DEMODS] = {0,0};    /* initial value */
    unsigned long          reg_value = 0;
    unsigned long          frame_error_value;
    unsigned char	      offset = 0;

    *p_mstatus = CX24117_MSTATUS_NOT_READY;

    reg_value=cx24117_readreg(state, CX24117_MEASUREMENT_COUNTER);
    if ((reg_value == last_measurement_count[demod_sel])) /* measurements are not ready */
    {
        return (True);
    }

     last_measurement_count[demod_sel] = reg_value;
	
    /* Get the BER counters */
    /* corrected bit errors */
    *p_corr_bit_err_rate = ((cx24117_readreg(state, CX24117_CORR_BIT_ERR_COUNT_31_24)) << 24) | \
                                            (cx24117_readreg(state, CX24117_CORR_BIT_ERR_COUNT_23_16) << 16) | \
                                            (cx24117_readreg(state, CX24117_CORR_BIT_ERR_COUNT_15_08) << 8 )  | \
                                            (cx24117_readreg(state, CX24117_CORR_BIT_ERR_COUNT_07_00));	



    /* uncorected frame errors */
    frame_error_value=((cx24117_readreg(state, CX24117_UNCORR_FRAME_ERR_COUNT_15_08))<<8) |\
                                  (cx24117_readreg(state, CX24117_UNCORR_FRAME_ERR_COUNT_07_00));
	
    *p_uncorr_frame_err_rate = (unsigned short)frame_error_value;

    if (p_ber_window_adjustment != 0)
    {

	 reg_value=((cx24117_readreg(state, CX24117_BER_WINDOW_ADJUSTMENT_15_08))<<8) |
                          (cx24117_readreg(state, CX24117_BER_WINDOW_ADJUSTMENT_15_08));	

        *p_ber_window_adjustment = (unsigned short) reg_value;
    }

    *p_mstatus = CX24117_MSTATUS_READY;

    return (True);
}

static int cx24117_read_ber(struct dvb_frontend* fe, u32* ber)
{
	struct cx24117_state *state = fe->demodulator_priv;
       unsigned long                                        corr_bit_err_rate;      
       unsigned short                                       uncorr_frame_err_rate;  
       unsigned short                                       ber_window_adjustment;
       CX24117_MSTATUS                                mstatus;

      /* The true bit error rate is this value divided by the window size (set as 256 * 255) */
      cx24117_get_BER (state, CX24117_DEMOD_CHANNEL_SEL0, &corr_bit_err_rate, &uncorr_frame_err_rate, &ber_window_adjustment, &mstatus);

	*ber = corr_bit_err_rate;

	dprintk("%s:  BER = %d\n",__FUNCTION__,*ber);

	return 0;
}

#endif



static int cx24117_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
{
	struct cx24117_state *state = fe->demodulator_priv;

	//dprintk("%s:  Signal strength = %d\n",__FUNCTION__,*signal_strength);

	return 0;
}

static int cx24117_read_snr(struct dvb_frontend* fe, u16* snr)
{
	struct cx24117_state *state = fe->demodulator_priv;

	/* Inverted raw Es/N0 count, totally bogus but better than the
	   BER threshold. */


	//dprintk("%s:  read S/N index = %d\n",__FUNCTION__,*snr);

	return 0;
}

static int cx24117_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
{
	struct cx24117_state *state = fe->demodulator_priv;

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

	return 0;
}

static int cx24117_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
{
	struct cx24117_state *state = fe->demodulator_priv;

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

	return 0;
}

bool cx24117_change_channel(struct dvb_frontend* fe,  
                      const CX24117_DEMOD_CHANNEL_SEL demod_sel,
                      const CX24117_CHANOBJ*          p_channel_object)
{
       struct cx24117_state *state = fe->demodulator_priv;
	CX24117_LLF                          llf;
	
	unsigned char demod_clk_div, adc_clk_div;
	unsigned long reg_value, demod_lclk_div;

       printk ("cx24117_change_channel(): Enter. \n");
	   
    /**************************************************************************
     * Part1: Input validation
     **************************************************************************/
    if ( p_channel_object == 0)
    {
        return(False);
    }  

    /**************************************************************************
     * Part2: Set LNB parameters                                              *
     **************************************************************************/
    if (p_channel_object->lnb_sel != CX24117_LNB_SEL_NONE)
    {
        /* Set LNB DC Polarity 13v/18v */
	    llf.op_code = CX24117_OPCODE_LNB_SET_DC_LEVEL;

	    llf.data[0] = (unsigned char)p_channel_object->lnb_sel;
	    llf.data[1] = (unsigned char)p_channel_object->lnb_dc;
	    llf.argument_length = 2; 
	    llf.reply_length = 0;    

	    if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
	    {
	        return (False);
	    }
		

	    llf.op_code = CX24117_OPCODE_LNB_PCB_CONFIG;

	    llf.data[0] = (unsigned char)p_channel_object->lnb_sel;
	    llf.data[1] = (unsigned char)CX24117_DEFAULT_LNB_DC_POLARITY; 
	    llf.data[2] = (unsigned char)CX24117_DEFAULT_LNB_OPEN_DRAIN; 
	    llf.data[3] = (unsigned char)p_channel_object->lnb_tone; 

	    llf.argument_length = 4;  /* Number of LLF arguments */
	    llf.reply_length = 0;     /* Number of return arguments */

	    if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
	    {
	        return (False);
	    }
	
    }

   /**************************************************************************
    * Part3: Set demod parameters.                                           *
    **************************************************************************/
	/* NBC and Legacy (sr > 10Msps && sr <= 31Msps) */
	if (p_channel_object->symbol_rate_ksps > 10000UL &&  p_channel_object->symbol_rate_ksps <= 31000UL)
	{
		adc_clk_div = CX24117_ADC_MOD_SEL_DIV_MDR;
		demod_clk_div = CX24117_PLL_DEMOD_CLOCK_DIV_MDR;
	}
	/* Legacy (SR <= 10Msps) and NBC at 10Msps only*/
	else if(p_channel_object->symbol_rate_ksps <= 10000UL)	
	{
		adc_clk_div = CX24117_ADC_MOD_SEL_DIV_LDR;
		demod_clk_div = CX24117_PLL_DEMOD_CLOCK_DIV_LDR;
	}
	/* NBC and Legacy (SR > 31Msps) */
	else
	{
		adc_clk_div = CX24117_ADC_MOD_SEL_DIV;
		demod_clk_div = CX24117_PLL_DEMOD_CLOCK_DIV;
	}		

	if (cx24117_write_mask(state, CX24117_AFE_ADC0_CLK_DIV, 0x07,  (unsigned long)adc_clk_div)<0)
	{
		return (False);
	}

	if (cx24117_write_mask(state, CX24117_PLL_CH0_DIV, 0x0F,  (unsigned long)adc_clk_div)<0)
	{
		return (False);
	}
	
#if CX24117_INCLUDE_MPEG_EXTRA_CLOCK
	 cx24117_extra_clocks.demod[demod_sel].rx_operation_mode = p_channel_object->rx_operation_mode;
	 cx24117_extra_clocks.demod[demod_sel].last_pilot = p_channel_object->pilot;	
	
       if (cx24117_extra_clocks.demod[demod_sel].extra_clocks_between_packets != 0)
       {
           cx24117_extra_clocks.demod[demod_sel].mpeg_clock_rate_adjust = True;
       }
       else
       {
           cx24117_extra_clocks.demod[demod_sel].mpeg_clock_rate_adjust = False;
       }
#endif /* CX24117_INCLUDE_MPEG_EXTRA_CLOCK */

   /**************************************************************************
    * Part4: Re-start acquisition.                                           *
    **************************************************************************/ 
	/* Change channel */
    llf.op_code  = CX24117_OPCODE_CHANGE_CHANNEL;
	
    llf.data[0]  = (unsigned char)demod_sel;
    llf.data[1]  = (unsigned char)((p_channel_object->frequency_khz >> 16) & 0xFF); /* 23:16 */
    llf.data[2]  = (unsigned char)((p_channel_object->frequency_khz >> 8) & 0xFF);  /* 15:8  */
    llf.data[3]  = (unsigned char)(p_channel_object->frequency_khz & 0xFF);         /*  7:0  */   

    llf.data[4]  = (unsigned char)((p_channel_object->symbol_rate_ksps >> 8) & 0xFF);  /* MSB */ 
    llf.data[5]  = (unsigned char)(p_channel_object->symbol_rate_ksps & 0xFF);         /* LSB */

    llf.data[6]  = (unsigned char)((p_channel_object->acq_mode & 0x03) | ((p_channel_object->spec_inv << 0x02) & 0x0C)); /* SI[3:2], mode[1:0] */

    llf.data[7]  = (unsigned char)((p_channel_object->rx_operation_mode & 0x3F) | (p_channel_object->pilot<< 0x06)); /* pilot[7:6], modcode[5:0] */ 
    
    llf.data[8]  = (unsigned char)((p_channel_object->requested_search_range_khz >> 8) & 0xFF);    /* MSB */
    llf.data[9]  = (unsigned char)(p_channel_object->requested_search_range_khz & 0xFF);           /* LSB */

    llf.data[10] = (unsigned char)(p_channel_object->roll_off & 0x07);

    llf.data[11] = p_channel_object->viterbi_coderates;
    llf.data[12] = demod_clk_div;
    llf.data[13] = adc_clk_div;
    llf.data[14] = p_channel_object->flags;

    llf.argument_length = 15;

    llf.reply_length = 0;

    if (cx24117_execute_LLF(state, &llf) == False) 
    {
        return (False);
    }	
    return(True);
}


static int cx24117_tune(struct dvb_frontend* fe,
			struct dvb_frontend_parameters* params,
			unsigned int mode_flags,
			unsigned int *delay,
			fe_status_t *status)
{
       CX24117_CHANOBJ channel_object;
	int retval = 0;
	

       printk ("cx24117_tune(): Enter. \n");
	   
	if (params== NULL)
	{
	    return 0;
       }

	//channel_object.frequency_khz = params->frequency;   /* intermediate frequency in kHz for QPSK */
	//channel_object.spec_inv = params->inversion;
	//channel_object.symbol_rate_ksps = params->u.qpsk.symbol_rate;

       channel_object.frequency_khz=155000;                                                    /* tuner frequency in kHz                */
       channel_object.symbol_rate_ksps=20000;                                                 /* symbol rate (kSps)                    */
       channel_object.acq_mode=CX24117_ACQ_MODE_BLIND;                           /* blind/quick acquisition mode          */
       channel_object.spec_inv=CX24117_SPEC_INV_OFF_BOTH;                          /* spectral inversion                    */
       channel_object.rx_operation_mode=CX24117_OPERATION_MODE_15;          /* receiver operation mode               */
	channel_object.pilot=CX24117_PILOT_ON;                                                  /* pilot on/off (NBC modes only)         */
       channel_object.lnb_dc=CX24117_LNBDC_LOW;                                           /* lnb polarity                          */
       channel_object.lnb_tone=CX24117_LNBTONE_OFF;                                      /* lnb tone                              */
       channel_object.lnb_sel=CX24117_LNB_SEL_NONE;                                            /* lnb A/B selection                     */
       channel_object.roll_off=CX24117_ROLLOFF_02;                                           /* matched filter roll-off factor        */
       channel_object.lnb_offset_khz=146;                                                            /* lnb offset                            */
       channel_object.requested_search_range_khz=5000;                                     /* requested freq search range in kHz    */
       channel_object.viterbi_coderates= CX24117_CODERATE_2DIV3;                   /* additional code rates for DTV and DVB */
       channel_object.flags=0x00;                                                                        /* Refer CX24117_CHANNEL_FLAGS           */

      switch (params->u.qpsk.fec_inner)
	{
	case FEC_AUTO:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_AMC_AUTO;
		break;
	case FEC_1_2:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_3;
		break;
	case FEC_2_3:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_5;
		break;
	case FEC_3_4:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_6;
		break;
	case FEC_4_5:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_7;
		break;
	case FEC_5_6:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_8;
		break;
	case FEC_6_7:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_AMC_AUTO;
		break;
	case FEC_7_8:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_AMC_AUTO;
		break;
	case FEC_8_9:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_8;
		break;
	default:
		channel_object.rx_operation_mode=CX24117_OPERATION_MODE_AMC_AUTO;
		break;
	}

       channel_object.rx_operation_mode=CX24117_OPERATION_MODE_15;

	retval = cx24117_change_channel(fe, CX24117_DEMOD_CHANNEL_SEL0, &channel_object);
	
	*delay = HZ/10;

	return retval;
}


bool  cx24117_get_operation_mode(struct cx24117_state* state,
                         const CX24117_DEMOD_CHANNEL_SEL                   demod_sel,
                         CX24117_OPERATION_MODE*                                p_oper_mode)
{
	unsigned long reg_value = 0;
	unsigned char offset = 0;

       /* Get the actual modcode */
    reg_value=cx24117_readreg(state, CX24117_LOCKED_TO_MODCODE);

    *p_oper_mode = (CX24117_OPERATION_MODE)(reg_value & 0x3F);

    return(True);
}

bool cx24117_get_viterbi_rate(struct cx24117_state* state,
                       const CX24117_DEMOD_CHANNEL_SEL          demod_sel,
                       CX24117_CODERATE*                                  p_coderate) 
{
	unsigned long reg_value = 0;
	unsigned char offset = 0;


    /* Get the actual coderate */
    reg_value=cx24117_readreg (state, CX24117_LOCKED_TO_PARAM);
    reg_value=reg_value & 0x3F;
    
    switch (reg_value)
    {
        case 1: *p_coderate = CX24117_CODERATE_1DIV2; break;
        case 2: *p_coderate = CX24117_CODERATE_2DIV3; break;
        case 3: *p_coderate = CX24117_CODERATE_3DIV4; break;
        case 4: *p_coderate = CX24117_CODERATE_4DIV5; break;
        case 5: *p_coderate = CX24117_CODERATE_5DIV6; break;
        case 6: *p_coderate = CX24117_CODERATE_6DIV7; break;
        case 7: *p_coderate = CX24117_CODERATE_7DIV8; break;
        default:*p_coderate = CX24117_CODERATE_UNDEF; break;
    }
    return(True);
}



bool cx24117_get_legacy_opermodefromCR(struct cx24117_state* state,
								const CX24117_DEMOD_CHANNEL_SEL     demod_sel,
								const bool						      dvb,         /* dvb = 1, dtv = 0 */
								CX24117_OPERATION_MODE*                  p_oper_mode)
{
	unsigned char offset = 0;
	CX24117_CODERATE code_rate;

	if (p_oper_mode == 0)
	{
		return (False);
	}

    /* Get the actual CR */
	if (cx24117_get_viterbi_rate(state, demod_sel, &code_rate) == False)
	{
		return (False);
	}

	if (dvb)
	{
		switch (code_rate)
		{
			case CX24117_CODERATE_1DIV2: *p_oper_mode = CX24117_OPERATION_MODE_DVB_1_2; break;
			case CX24117_CODERATE_2DIV3: *p_oper_mode = CX24117_OPERATION_MODE_DVB_2_3; break;
			case CX24117_CODERATE_3DIV4: *p_oper_mode = CX24117_OPERATION_MODE_DVB_3_4; break;
			case CX24117_CODERATE_5DIV6: *p_oper_mode = CX24117_OPERATION_MODE_DVB_5_6; break;
			case CX24117_CODERATE_7DIV8: *p_oper_mode = CX24117_OPERATION_MODE_DVB_7_8; break;
			default: return (False); /* error */
		}
	}
	else
	{
		switch (code_rate)
		{
			case CX24117_CODERATE_1DIV2: *p_oper_mode = CX24117_OPERATION_MODE_DTV_1_2; break;
			case CX24117_CODERATE_2DIV3: *p_oper_mode = CX24117_OPERATION_MODE_1; break;
			case CX24117_CODERATE_6DIV7: *p_oper_mode = CX24117_OPERATION_MODE_2; break;
			default: return (False); /* error */
		}
	}

	return(True);
}

bool cx24117_get_pilot(struct cx24117_state* state,
                 const CX24117_DEMOD_CHANNEL_SEL    demod_sel,
                 CX24117_PILOT*				       p_pilot)
{
	unsigned long reg_value = 0;
	unsigned char offset = 0;


    /* Get the pilot status */
    reg_value = cx24117_readreg(state, CX24117_LOCKED_TO_PARAM);

    *p_pilot = (CX24117_PILOT)((reg_value & 0x80)>>8);

    return(True);
}


#if CX24117_INCLUDE_MPEG_EXTRA_CLOCK
static CX24117_TRANSPEC CX24117_GetTranportSpec(CX24117_OPERATION_MODE oper_mode)
{
    switch (oper_mode)
    {
    case CX24117_OPERATION_MODE_DTV_1_2:
    case CX24117_OPERATION_MODE_1:
    case CX24117_OPERATION_MODE_2:
        return CX24117_SPEC_DTV;

    case CX24117_OPERATION_MODE_3:
    case CX24117_OPERATION_MODE_4:
    case CX24117_OPERATION_MODE_5:
    case CX24117_OPERATION_MODE_6:
    case CX24117_OPERATION_MODE_7:
    case CX24117_OPERATION_MODE_8:
    case CX24117_OPERATION_MODE_9:
    case CX24117_OPERATION_MODE_10:
    case CX24117_OPERATION_MODE_11:
    case CX24117_OPERATION_MODE_12:
    case CX24117_OPERATION_MODE_13:
    case CX24117_OPERATION_MODE_14:
    case CX24117_OPERATION_MODE_15:
    case CX24117_OPERATION_MODE_16:
        return CX24117_SPEC_NBC;

    case CX24117_OPERATION_MODE_DVB_1_2:
    case CX24117_OPERATION_MODE_DVB_2_3:
    case CX24117_OPERATION_MODE_DVB_3_4:
    case CX24117_OPERATION_MODE_DVB_5_6:
    case CX24117_OPERATION_MODE_DVB_7_8:
        return CX24117_SPEC_DVB;

    default: 
        return CX24117_SPEC_UNDEF;
    }
}



/*******************************************************************************************************
* Name: CX24117_DRV_GetNewMN
* Description:
* 	This function will get the new m and n values (bits output per symbol ratio - numerator and denominator). 
* new_M = M * (((187+S)*B+D) / ((187+S) * B))
* 
* Where,
* S is 0 if no sync byte is transmitted, 1 if there is a sync byte.
* B is 1 for parallel mode, 8 for serial mode.
* D is the required extra clocks.
* Return Value:
* 	TRUE - operation successful; FALSE - otherwise
* 
* I/O	Parameters	        Descriptions
*                                                   
*******************************************************************************************************/
bool CX24117_DRV_GetNewMN(CX24117_M_N*	     p_old_mn, 
					 CX24117_M_N*				     p_new_mn, 
					 unsigned char				     extra_clocks, 
					 bool						     sync_byte, 
					 bool						     parallel_mode, 
					 CX24117_OPERATION_MODE	     oper_mode, 
					 CX24117_PILOT				     pilot)
{
    CX24117_TRANSPEC tspec = CX24117_GetTranportSpec(oper_mode);

    p_new_mn->m = p_old_mn->m;
    p_new_mn->n = p_old_mn->n;

    if (extra_clocks == 0)
    {
        return True;
    }

    if (tspec == CX24117_SPEC_NBC && extra_clocks == CX24117_MPEG_SER_EXTRA_CLOCKS && parallel_mode == False) /* NBC: Serial mode */
    {
		if (sync_byte == False)
		{ /* Without Sync Byte */
			if ((oper_mode <= CX24117_OPERATION_MODE_16) && (pilot == CX24117_PILOT_ON))  
			{
				p_new_mn->m = CX24117_m_n_ser30_clocks_w_pilots_wo_sync[oper_mode-4].m;
				p_new_mn->n = CX24117_m_n_ser30_clocks_w_pilots_wo_sync[oper_mode-4].n;
				p_new_mn->m_n_rate = CX24117_m_n_ser30_clocks_w_pilots_wo_sync[oper_mode-4].m_n_rate; 
			}
			else if (oper_mode <= CX24117_OPERATION_MODE_16)    
			{
				p_new_mn->m = CX24117_m_n_ser30_clocks_wo_pilots_wo_sync[oper_mode-4].m;
				p_new_mn->n = CX24117_m_n_ser30_clocks_wo_pilots_wo_sync[oper_mode-4].n;
				p_new_mn->m_n_rate = CX24117_m_n_ser30_clocks_wo_pilots_wo_sync[oper_mode-4].m_n_rate;
			}
		}
		else 
		{
			if ((oper_mode <= CX24117_OPERATION_MODE_16) && (pilot == CX24117_PILOT_ON))  
			{ 
				p_new_mn->m = CX24117_m_n_ser30_clocks_w_pilots_w_sync[oper_mode-4].m;   
				p_new_mn->n = CX24117_m_n_ser30_clocks_w_pilots_w_sync[oper_mode-4].n; 
				p_new_mn->m_n_rate = CX24117_m_n_ser30_clocks_w_pilots_w_sync[oper_mode-4].m_n_rate;
			}
			else if (oper_mode <= CX24117_OPERATION_MODE_16)    
			{
				p_new_mn->m = CX24117_m_n_ser30_clocks_wo_pilots_w_sync[oper_mode-4].m;
				p_new_mn->n = CX24117_m_n_ser30_clocks_wo_pilots_w_sync[oper_mode-4].n; 
				p_new_mn->m_n_rate = CX24117_m_n_ser30_clocks_wo_pilots_w_sync[oper_mode-4].m_n_rate;
			}      
		}
	}
	else if (tspec == CX24117_SPEC_NBC && extra_clocks == CX24117_MPEG_PAR_EXTRA_CLOCKS && parallel_mode == True) /* NBC: Parallel mode */
	{
		if (sync_byte == False)
		{ /* Without Sync Byte */
			if ((oper_mode <= CX24117_OPERATION_MODE_16) && (pilot == CX24117_PILOT_ON))  
			{
				p_new_mn->m = CX24117_m_n_par16_clocks_w_pilots_wo_sync[oper_mode-4].m;
				p_new_mn->n = CX24117_m_n_par16_clocks_w_pilots_wo_sync[oper_mode-4].n;
				p_new_mn->m_n_rate = CX24117_m_n_par16_clocks_w_pilots_wo_sync[oper_mode-4].m_n_rate;
			}
			else if (oper_mode <= CX24117_OPERATION_MODE_16)    
			{
				p_new_mn->m = CX24117_m_n_par16_clocks_wo_pilots_wo_sync[oper_mode-4].m;
				p_new_mn->n = CX24117_m_n_par16_clocks_wo_pilots_wo_sync[oper_mode-4].n;
				p_new_mn->m_n_rate = CX24117_m_n_par16_clocks_wo_pilots_wo_sync[oper_mode-4].m_n_rate;
			}
		}
		else 
		{
			if ((oper_mode <= CX24117_OPERATION_MODE_16) && (pilot == CX24117_PILOT_ON))  
			{ 
				p_new_mn->m = CX24117_m_n_par16_clocks_w_pilots_w_sync[oper_mode-4].m;   
				p_new_mn->n = CX24117_m_n_par16_clocks_w_pilots_w_sync[oper_mode-4].n; 
				p_new_mn->m_n_rate = CX24117_m_n_par16_clocks_w_pilots_w_sync[oper_mode-4].m_n_rate;
			}
			else if (oper_mode <= CX24117_OPERATION_MODE_16)    
			{
				p_new_mn->m = CX24117_m_n_par16_clocks_wo_pilots_w_sync[oper_mode-4].m;
				p_new_mn->n = CX24117_m_n_par16_clocks_wo_pilots_w_sync[oper_mode-4].n; 
				p_new_mn->m_n_rate = CX24117_m_n_par16_clocks_wo_pilots_w_sync[oper_mode-4].m_n_rate;
			}      
		}
	}
	else if ((tspec == CX24117_SPEC_DVB || tspec == CX24117_SPEC_DTV) && extra_clocks == CX24117_MPEG_SER_EXTRA_CLOCKS && parallel_mode == False) /* DVB: Serial mode */
	{
		if (sync_byte == False)
		{ /* Without Sync Byte */
			p_new_mn->m = CX24117_m_n_ser30_clocks_wo_sync[oper_mode-40].m;
			p_new_mn->n = CX24117_m_n_ser30_clocks_wo_sync[oper_mode-40].n;
			p_new_mn->m_n_rate = CX24117_m_n_ser30_clocks_wo_sync[oper_mode-40].m_n_rate;
		}
		else 
		{
			p_new_mn->m = CX24117_m_n_ser30_clocks_w_sync[oper_mode-40].m;
			p_new_mn->n = CX24117_m_n_ser30_clocks_w_sync[oper_mode-40].n; 
			p_new_mn->m_n_rate = CX24117_m_n_ser30_clocks_w_sync[oper_mode-40].m_n_rate; 
		}
	}
	else if ((tspec == CX24117_SPEC_DVB || tspec == CX24117_SPEC_DTV) && extra_clocks == CX24117_MPEG_PAR_EXTRA_CLOCKS && parallel_mode == True)  /* DVB: Parallel mode */
	{
		if (sync_byte == False)
		{ /* Without Sync Byte */
			p_new_mn->m = CX24117_m_n_par16_clocks_wo_sync[oper_mode-40].m;
			p_new_mn->n = CX24117_m_n_par16_clocks_wo_sync[oper_mode-40].n;
			p_new_mn->m_n_rate = CX24117_m_n_par16_clocks_wo_sync[oper_mode-40].m_n_rate;
		}
		else 
		{
			p_new_mn->m = CX24117_m_n_par16_clocks_w_sync[oper_mode-40].m;
			p_new_mn->n = CX24117_m_n_par16_clocks_w_sync[oper_mode-40].n; 
			p_new_mn->m_n_rate = CX24117_m_n_par16_clocks_w_sync[oper_mode-40].m_n_rate;   
		}
	}
	return (True);
} 

/*******************************************************************************************************
* Name: CX24117_LLF_GetMN
* Description:
* 	This function will get the m and n values (bits output per symbol ratio - numerator and denominator). 
*
* Return Value:
* 	TRUE - operation successful; FALSE - otherwise
* 
* I/O	Parameters	        Descriptions
* IN	p_communication	    Pointer to stinger communication object.
* IN    demod_sel			Demod selection.  
*                                                   
*******************************************************************************************************/
bool CX24117_LLF_GetMN(struct cx24117_state* state,  
                  const CX24117_DEMOD_CHANNEL_SEL         demod_sel,
		    unsigned long*				                    p_m, 
		    unsigned long*				                    p_n)
{
    CX24117_LLF llf;

    llf.op_code = CX24117_OPCODE_GET_MN; 

    llf.data[0] = (unsigned char)demod_sel;

    llf.argument_length = 1;              
    llf.reply_length    = 6;

    if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
    {
        return (False);
    }
    
    *p_m  = 0;
    *p_m  = ((unsigned long)llf.data[0]) << 16;
    *p_m |= ((unsigned long)llf.data[1]) << 8;
    *p_m |= ((unsigned long)llf.data[2]);

    *p_n  = 0;
    *p_n  = ((unsigned long)llf.data[3]) << 16;
    *p_n |= ((unsigned long)llf.data[4]) << 8;
    *p_n |= ((unsigned long)llf.data[5]);

    return (True);
} /* CX24117_LLF_GetMN */

/*******************************************************************************************************
* Name: CX24117_LLF_SetMN
* Description:
* 	This function will send the calculated M, N, (M/N)*256 and requested extra clocks to firmware.
*
* Return Value:
* 	TRUE - operation successful; FALSE - otherwise
* 
* I/O	Parameters	        Descriptions
* IN	p_communication	    Pointer to stinger communication object.
* IN    demod_sel			Demod selection.  
*                                                   
*******************************************************************************************************/
bool CX24117_LLF_SetMN(struct cx24117_state* state,  
                  const CX24117_DEMOD_CHANNEL_SEL demod_sel,
		    unsigned char                                     extra_clocks, 
		    unsigned long					     pll_m, 
		    unsigned long                                     pll_n, 
		    unsigned short                                    pll_mn_ratio)
{
    CX24117_LLF llf;

    llf.op_code = CX24117_OPCODE_SET_MN; 
	
    llf.data[0] = (unsigned char)demod_sel;
    llf.data[1] = extra_clocks;  /*  7:0  */       

    llf.data[2] = (unsigned char)((pll_m >> 16) & 0x000000FF); /* 23:16 */
    llf.data[3] = (unsigned char)((pll_m >> 8)  & 0x000000FF); /* 15:8  */
    llf.data[4] = (unsigned char)(pll_m & 0x000000FF);         /*  7:0  */

    llf.data[5] = (unsigned char)((pll_n >> 16) & 0x000000FF); /* 23:16 */
    llf.data[6] = (unsigned char)((pll_n >> 8)  & 0x000000FF); /* 15:8  */
    llf.data[7] = (unsigned char)(pll_n & 0x000000FF);         /*  7:0  */

    llf.data[8] = (unsigned char)((pll_mn_ratio >> 8) & 0x00FF); /* MSB */ 
    llf.data[9] = (unsigned char)(pll_mn_ratio & 0x00FF);        /* LSB */    
    
    llf.argument_length = 10;   
    llf.reply_length    = 0;

    if (cx24117_execute_LLF(state, &llf) == False) /* Send it to DPR */
    {
        return (False);
    }
    
    return (True);
} /* CX24117_LLF_SetMN */

bool cx24117_program_output_rate(struct cx24117_state* state,  const CX24117_DEMOD_CHANNEL_SEL demod_sel) 
{
    CX24117_M_N   bit_rate_constant, new_bit_rate_constant; 

    if (cx24117_extra_clocks.demod[demod_sel].extra_clocks_between_packets != 0) 
    {
	        if (CX24117_LLF_GetMN(state, demod_sel, &bit_rate_constant.m, &bit_rate_constant.n) == False)
	        {
	            return (False);
	        }

		if (cx24117_extra_clocks.demod[demod_sel].rx_operation_mode == CX24117_OPERATION_MODE_AMC_AUTO)
		{
			if (cx24117_get_operation_mode(state, demod_sel, &cx24117_extra_clocks.demod[demod_sel].rx_operation_mode) == False)
			{
				return (False);
			}
			if (cx24117_get_pilot(state, demod_sel, &cx24117_extra_clocks.demod[demod_sel].last_pilot) == False)
			{
				return (False);
			}
		}
		else if (cx24117_extra_clocks.demod[demod_sel].rx_operation_mode >= CX24117_OPERATION_MODE_DVB_1_2 && cx24117_extra_clocks.demod[demod_sel].rx_operation_mode <= CX24117_OPERATION_MODE_DVB_7_8) /* DVB auto-CR cases */
		{
			if (cx24117_get_legacy_opermodefromCR (state, demod_sel, True, &cx24117_extra_clocks.demod[demod_sel].rx_operation_mode) == False)
			{
				return (False);
			}
		}
		else if (cx24117_extra_clocks.demod[demod_sel].rx_operation_mode == CX24117_OPERATION_MODE_DTV_1_2 || /* DTV auto-CR cases */
				 cx24117_extra_clocks.demod[demod_sel].rx_operation_mode == CX24117_OPERATION_MODE_1	   || 
				 cx24117_extra_clocks.demod[demod_sel].rx_operation_mode == CX24117_OPERATION_MODE_2)
		{
			if (cx24117_get_legacy_opermodefromCR(state, demod_sel, False, &cx24117_extra_clocks.demod[demod_sel].rx_operation_mode) == False)
			{
				return (False);
			}
		}
		else /* DVB-S2 Auto-Pilot only case */
		{
			if (cx24117_get_pilot(state, demod_sel, &cx24117_extra_clocks.demod[demod_sel].last_pilot) == False)
			{
				return (False);
			}
		}

	        bit_rate_constant.m  = CX24117_DRV_GetNewMN(&bit_rate_constant,
				                               &new_bit_rate_constant,
	                                                    cx24117_extra_clocks.demod[demod_sel].extra_clocks_between_packets,
	                                                   (cx24117_extra_clocks.demod[demod_sel].insert_sync_byte == CX24117_SYNC_BYTE_INSERT) ? True : False,
	                                                   (cx24117_extra_clocks.output_mode == CX24117_OUTPUT_MODE_PARALLEL) ? True : False,
	                                                    cx24117_extra_clocks.demod[demod_sel].rx_operation_mode,
								   cx24117_extra_clocks.demod[demod_sel].last_pilot);
			
	        if (CX24117_LLF_SetMN(state, demod_sel,
	                              cx24117_extra_clocks.demod[demod_sel].extra_clocks_between_packets, 
	                              new_bit_rate_constant.m, 
	                              new_bit_rate_constant.n, 
	                              new_bit_rate_constant.m_n_rate) == False)
	        {
	            return (False);
	        }
    }
    return (True);
}
#endif /* CX24117_INCLUDE_MPEG_EXTRA_CLOCK */

bool cx24117_monitor(struct dvb_frontend* fe,  
                const CX24117_DEMOD_CHANNEL_SEL demod_sel,
		  CX24117_ACQ_STATE*                       p_acq_state,
                CX24117_LOCKIND*                           p_lockind)
{
    struct cx24117_state *state = fe->demodulator_priv;
    unsigned long reg_value;
    unsigned char offset = 0;


    printk ("cx24117_monitor(): Enter!\n");
	
    if ( p_acq_state == 0 || p_lockind == 0)
    {
        return (False);
    }

    reg_value=cx24117_readreg (state, CX24117_LOCK_BYTE);

    memset (p_lockind, 0, sizeof(CX24117_LOCKIND));

    p_lockind->pll                    = (bool)(reg_value & 0x01UL);
    p_lockind->demod_sync     = (bool)((reg_value >> 1) & 0x01UL);
    p_lockind->ldpc                 = (bool)((reg_value >> 2) & 0x01UL);
    p_lockind->viterbi              = (bool)((reg_value >> 2) & 0x01UL);
    p_lockind->bch                  = (bool)((reg_value >> 3) & 0x01UL);
    p_lockind->reed_solomon   = (bool)((reg_value >> 3) & 0x01UL);

    *p_acq_state = CX24117_ACQ_UNDEF;

    if (p_lockind->bch == True || p_lockind->reed_solomon == True)
    {
        *p_acq_state = CX24117_ACQ_LOCKED_AND_TRACKING;
    }
    else
    {
        *p_acq_state = CX24117_ACQ_SEARCHING;
    }

#if CX24117_INCLUDE_MPEG_EXTRA_CLOCK
    if (cx24117_extra_clocks.demod[demod_sel].extra_clocks_between_packets != 0 && (reg_value == 0x01))
    {
        cx24117_extra_clocks.demod[demod_sel].mpeg_clock_rate_adjust = True;
    }

    if (*p_acq_state == CX24117_ACQ_LOCKED_AND_TRACKING)
    {
        if (cx24117_extra_clocks.demod[demod_sel].mpeg_clock_rate_adjust == True)
        {
            cx24117_extra_clocks.demod[demod_sel].mpeg_clock_rate_adjust = False;
            if (cx24117_program_output_rate(state, demod_sel) == False) 
            {
                return (False);
            }
        }
    }
#endif /* CX24117_INCLUDE_MPEG_EXTRA_CLOCK */

    return (True);
}

static int cx24117_read_status(struct dvb_frontend* fe, fe_status_t* status)
{
	struct cx24117_state *state = fe->demodulator_priv;
	CX24117_ACQ_STATE                               acq_state;
       CX24117_LOCKIND                                       lockind;


       if (cx24117_monitor(fe, CX24117_DEMOD_CHANNEL_SEL0,  &acq_state, &lockind)==False)
	{
	    return False;
	}

	*status = 0;
	if (lockind.demod_sync)
		*status |= FE_HAS_SIGNAL;
	if (lockind.pll)
		*status |= FE_HAS_CARRIER;	/* Phase locked */
	if (lockind.viterbi)
		*status |= FE_HAS_VITERBI;

	/* Reed-Solomon Status */
	if (lockind.reed_solomon)
		*status |= FE_HAS_SYNC;
	if (acq_state==CX24117_ACQ_LOCKED_AND_TRACKING)
		*status |= FE_HAS_LOCK;		/*Full Sync */

	return 0;
}


static int cx24117_get_algo(struct dvb_frontend *fe)
{
	return 1; //FE_ALGO_HW
}

static void cx24117_release(struct dvb_frontend* fe)
{
	struct cx24117_state* state = fe->demodulator_priv;
	dprintk("%s\n",__FUNCTION__);
	kfree(state);
}

static struct dvb_frontend_ops cx24117_ops = {

	.info = {
		.name = "Conexant CX24117/CX24128",
		.type = FE_QPSK,
		.frequency_min = 950000,
		.frequency_max = 2150000,
		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
		.frequency_tolerance = 5000,
		.symbol_rate_min = 1000000,
		.symbol_rate_max = 45000000,
		.caps = FE_CAN_INVERSION_AUTO |
			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
			FE_CAN_QPSK | FE_CAN_RECOVER
	},

	.release = cx24117_release,

	.init = cx24117_initfe,
	.set_frontend = cx24117_set_frontend,
	.get_frontend = cx24117_get_frontend,
	.read_status = cx24117_read_status,
	.read_ber = cx24117_read_ber,
	.read_signal_strength = cx24117_read_signal_strength,
	.read_snr = cx24117_read_snr,
#if CX24117_INCLUDE_DISEQC2	
	.diseqc_send_master_cmd = cx24117_send_diseqc_msg,
	.diseqc_send_burst = cx24117_diseqc_send_burst,
	.set_tone = cx24117_set_tone,
#endif	
	.set_voltage = cx24117_set_voltage,
	.tune = cx24117_tune,
	.get_frontend_algo = cx24117_get_algo,
};

struct dvb_frontend* cx24117_attach(const struct cx24117_config* config,
				    struct i2c_adapter* i2c)
{
    struct cx24117_state* state = NULL;
    struct dvb_frontend*    returned_fe;	
    int ret;
    int ChipID, ChipVer;

    printk ("cx24117_attach(): Enter. \n");
    
    dprintk("%s\n",__FUNCTION__);

    /* allocate memory for the internal state */
    state = kmalloc(sizeof(struct cx24117_state), GFP_KERNEL);
    if (state == NULL) {
        printk("Unable to kmalloc\n");
        goto error;
    }

    /* setup the state */
    state->config=config;
    state->i2c = i2c;
    state->initialised = 0;		

	state->VCAarg = 0;
	state->VGAarg = 0;
	state->bandselectarg = 0;
	state->pllarg = 0;
	state->currentfreq = 0;
	state->currentsymbolrate = 0;

	/* check if the demod is there */
	ChipVer = cx24117_readreg(state, 0xFE);
	printk("ChipVer is 0x%x\n", ChipVer);

	ChipID = cx24117_readreg(state, 0xFF);
	printk("ChipID is 0x%x\n", ChipID);


    if ((ChipID != CX24117_CHIP_ID) && (ChipVer != CX24117_REV_CHIP_VERSION)) {
        printk("ChipID or Version Error! \n");
        goto error;
    }

    /* create dvb_frontend */
    memcpy(&state->frontend.ops, &cx24117_ops, sizeof(struct dvb_frontend_ops));
    state->frontend.demodulator_priv = state;

    returned_fe = &state->frontend;	

    cx24117_initfe(returned_fe);

    return (returned_fe);

error:
	kfree(state);

	return NULL;
}


module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");

module_param(force_band, int, 0644);
MODULE_PARM_DESC(force_band, "Force a specific band select (1-9, default:off).");

MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24117/cx24128 hardware");
MODULE_AUTHOR("Steven Toth");
MODULE_LICENSE("GPL");

EXPORT_SYMBOL(cx24117_attach);
