/*****************************************************************************/
/**
*
* @file idt_8t49n24x.c
* @addtogroup IDT_8T49N24x
* @{
* <pre>
* Copyright (c) 2016-2022 Xilinx, Inc.
* Copyright 2023-2024 Advanced Micro Devices, Inc. All Rights Reserved.
* Copyright (c) 2016 Adeas B.V. All rights reserved.
*
* MODIFICATION HISTORY:
*
* Ver   Who    Date     Changes
* ----- ------ -------- --------------------------------------------------
* X.XX  XX     YY/MM/DD
* 1.00  RHe    16/06/20 Initial release. A ported version from the
*                       8T49N28X-FrequencyProgrammingGuide-
*                       	register-calculations.py script
* 2.00  MG     16/08/15 Major update
* 2.10  MG     16/09/05 Added LOS variable
* 2.20  GM     18/02/08 Converted math.h functions (e.g. ceil) to
* 							standard functions
* </pre>
*
******************************************************************************/


/***************************** Include Files *********************************/
#include "idt_8t49n24x.h"
#if defined (XPS_BOARD_ZCU102) || \
	defined (XPS_BOARD_ZCU104) || \
	defined (XPS_BOARD_ZCU106) || \
	defined (XPS_BOARD_VCK190)
#include "xiicps.h"
#else
#include "xiic.h"
#endif
#include "xil_types.h"
#include "xil_assert.h"
#include "xstatus.h"
#include <stdlib.h>

/************************** Constant Definitions *****************************/
#define IDT_8T49N24X_ADV_FUNC_EN 0 /* Enable unused APIs */
#if defined (XPS_BOARD_ZCU102) || \
	defined (XPS_BOARD_ZCU104) || \
	defined (XPS_BOARD_ZCU106) || \
	defined (XPS_BOARD_VCK190)
#define I2C_REPEATED_START 0x01
#define I2C_STOP 0x00
#else
#define I2C_REPEATED_START XIIC_REPEATED_START
#define I2C_STOP XIIC_STOP
#endif
/***************** Macros (Inline Functions) Definitions *********************/

/**************************** Type Definitions *******************************/

/************************** Function Prototypes ******************************/

/************************** Variable Definitions *****************************/

/************************** Function Definitions *****************************/
static void IDT_8T49N24x_I2cReset(void *IicPtr);
static unsigned IDT_8T49N24x_I2cSend(void *IicPtr, u16 SlaveAddr, u8 *MsgPtr,
							unsigned ByteCount, u8 Option);
static unsigned IDT_8T49N24x_I2cRecv(void *IicPtr, u16 SlaveAddr, u8 *BufPtr,
							unsigned ByteCount, u8 Option);
static u8 IDT_8T49N24x_GetRegister(void *IicPtr, u8 I2CSlaveAddress,
							u16 RegisterAddress);
static int IDT_8T49N24x_SetRegister(void *IicPtr, u8 I2CSlaveAddress,
							u16 RegisterAddress, u8 Value);
static int IDT_8T49N24x_ModifyRegister(void *IicPtr, u8 I2CSlaveAddress,
							u16 RegisterAddress, u8 Value, u8 Mask);
static int IDT_8T49N24x_CheckDeviceID(void *IicPtr, u8 I2CSlaveAddress);
static int IDT_8T49N24x_InputMonitorControl(void *IicPtr,
							u8 I2CSlaveAddress, u32 Value, u8 Input);
static int IDT_8T49N24x_PreDivider(void *IicPtr, u8 I2CSlaveAddress,
							u32 Value, u8 Input);
static int IDT_8T49N24x_M1Feedback(void *IicPtr, u8 I2CSlaveAddress,
							u32 Value, u8 Input);
static int IDT_8T49N24x_DSMInteger(void *IicPtr, u8 I2CSlaveAddress,
							u16 Value);
static int IDT_8T49N24x_DSMFractional(void *IicPtr, u8 I2CSlaveAddress,
							u32 Value);
static int IDT_8T49N24x_OutputDividerInteger(void *IicPtr,
							u8 I2CSlaveAddress, u32 Value, u8 Output);
static int IDT_8T49N24x_OutputDividerFractional(void *IicPtr,
							u8 I2CSlaveAddress, u32 Value, u8 Output);
static int IDT_8T49N24x_Mode(void *IicPtr, u8 I2CSlaveAddress,
							u8 Synthesizer);
static int IDT_8T49N24x_GetIntDivTable(int FOut, int *DivTable, u8 Bypass);
static int IDT_8T49N24x_CalculateSettings(int FIn, int FOut,
							IDT_8T49N24x_Settings* RegSettings);
static int IDT_8T49N24x_Enable(void *IicPtr, u8 I2CSlaveAddress,
							u8 Enable);
static int IDT_8T49N24x_ReferenceInput(void *IicPtr, u8 I2CSlaveAddress,
							u8 Input, u8 Enable);
static int IDT_8T49N24x_Configure_JA(void *IicPtr, u8 I2CSlaveAddress);
#if (IDT_8T49N24X_ADV_FUNC_EN == 1)
static int IDT_8T49N24x_GPIO(void *IicPtr, u8 I2CSlaveAddress);
static int IDT_8T49N24x_OutputDriver(void *IicPtr, u8 I2CSlaveAddress,
							u8 Output, u8 Enable);
static int IDT_8T49N24x_LockMode(void *IicPtr, u8 I2CSlaveAddress);
static int IDT_8T49N24x_SelectInputReference(void *IicPtr,
							u8 I2CSlaveAddress, u8 Input);
static int IDT_8T49N24x_Configure(void *IicPtr, u8 I2CSlaveAddress);
#endif
/*****************************************************************************/
/**
*
* This function resets the IIC instance for IDT_8T49N24x
*
* @param  IicPtr IIC instance pointer.

*
* @return None.
*
* @note   None.
*
******************************************************************************/
static void IDT_8T49N24x_I2cReset(void *IicPtr)
{
#if defined (XPS_BOARD_ZCU102) || \
	defined (XPS_BOARD_ZCU104) || \
	defined (XPS_BOARD_ZCU106) || \
	defined (XPS_BOARD_VCK190)
	/* Do nothing
	XIicPs *Iic_Ptr = IicPtr;
	XIicPs_Reset(Iic_Ptr);*/
#else
	XIic *Iic_Ptr = IicPtr;
	XIic_WriteReg(Iic_Ptr->BaseAddress, XIIC_RESETR_OFFSET,
				  XIIC_RESET_MASK);
#endif
}

/*****************************************************************************/
/**
*
* This function send the IIC data to IDT_8T49N24x
*
* @param  IicPtr IIC instance pointer.
* @param  SlaveAddr contains the 7 bit IIC address of the device to send the
*		   specified data to.
* @param MsgPtr points to the data to be sent.
* @param ByteCount is the number of bytes to be sent.
* @param Option indicates whether to hold or free the bus after
* 		  transmitting the data.
*
* @return	The number of bytes sent.
*
* @return None.
*
* @note   None.
*
******************************************************************************/
static unsigned IDT_8T49N24x_I2cSend(void *IicPtr, u16 SlaveAddr, u8 *MsgPtr,
							unsigned ByteCount, u8 Option)
{
#if defined (XPS_BOARD_ZCU102) || \
	defined (XPS_BOARD_ZCU104) || \
	defined (XPS_BOARD_ZCU106) || \
	defined (XPS_BOARD_VCK190)
	XIicPs *Iic_Ptr = IicPtr;
	u32 Status;

	/* Set operation to 7-bit mode */
	XIicPs_SetOptions(Iic_Ptr, XIICPS_7_BIT_ADDR_OPTION);
	XIicPs_ClearOptions(Iic_Ptr, XIICPS_10_BIT_ADDR_OPTION);

	/* Set Repeated Start option */
	if (Option == I2C_REPEATED_START) {
		XIicPs_SetOptions(Iic_Ptr, XIICPS_REP_START_OPTION);
	} else {
		XIicPs_ClearOptions(Iic_Ptr, XIICPS_REP_START_OPTION);
	}

	Status = XIicPs_MasterSendPolled(Iic_Ptr, MsgPtr, ByteCount, SlaveAddr);

	/*
	 * Wait until bus is idle to start another transfer.
	 */
	if (!(Iic_Ptr->IsRepeatedStart)) {
		while (XIicPs_BusIsBusy(Iic_Ptr));
	}

	if (Status == XST_SUCCESS) {
		return ByteCount;
	} else {
		return 0;
	}
#else
	XIic *Iic_Ptr = IicPtr;
	return XIic_Send(Iic_Ptr->BaseAddress, SlaveAddr, MsgPtr,
					ByteCount, Option);
#endif
}

/*****************************************************************************/
/**
*
* This function send the IIC data to IDT_8T49N24x
*
* @param  IicPtr IIC instance pointer.
* @param  SlaveAddr contains the 7 bit IIC address of the device to send the
*		   specified data to.
* @param BufPtr points to the memory to write the data.
* @param ByteCount is the number of bytes to be sent.
* @param Option indicates whether to hold or free the bus after
* 		  transmitting the data.
*
* @return	The number of bytes sent.
*
* @return None.
*
* @note   None.
*
******************************************************************************/
static unsigned IDT_8T49N24x_I2cRecv(void *IicPtr, u16 SlaveAddr, u8 *BufPtr,
							unsigned ByteCount, u8 Option)
{
#if defined (XPS_BOARD_ZCU102) || \
	defined (XPS_BOARD_ZCU104) || \
	defined (XPS_BOARD_ZCU106) || \
	defined (XPS_BOARD_VCK190)
	XIicPs *Iic_Ptr = IicPtr;
	u32 Status;

	XIicPs_SetOptions(Iic_Ptr, XIICPS_7_BIT_ADDR_OPTION);
	XIicPs_ClearOptions(Iic_Ptr, XIICPS_10_BIT_ADDR_OPTION);
	if (Option == I2C_REPEATED_START) {
		XIicPs_SetOptions(Iic_Ptr, XIICPS_REP_START_OPTION);
	} else {
		XIicPs_ClearOptions(Iic_Ptr, XIICPS_REP_START_OPTION);
	}

	Status = XIicPs_MasterRecvPolled(Iic_Ptr, BufPtr, ByteCount, SlaveAddr);

	/*
	 * Wait until bus is idle to start another transfer.
	 */
	if (!(Iic_Ptr->IsRepeatedStart)) {
		while (XIicPs_BusIsBusy(Iic_Ptr));
	}

	if (Status == XST_SUCCESS) {
		return ByteCount;
	} else {
		return 0;
	}
#else
	XIic *Iic_Ptr = IicPtr;
	return XIic_Recv(Iic_Ptr->BaseAddress, SlaveAddr, BufPtr,
					ByteCount, Option);
#endif
}

/*****************************************************************************/
/**
*
* This function reads a single byte to the IDT 8T49N24x  
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS Initialization was successful.
*    - XST_FAILURE I2C write error.
*
* @note None.
*
******************************************************************************/
static u8 IDT_8T49N24x_GetRegister(void *IicPtr, u8 I2CSlaveAddress,
							u16 RegisterAddress)
{
	u32 ByteCount = 0;
	u8 Buffer[2];
	u8 Data;
	u8 Retry = 0;
	u8 Exit;


	Exit = FALSE;
	Data = 0;

	do {
		/* Set Address */
		Buffer[0] = (RegisterAddress >> 8); 
		Buffer[1] = RegisterAddress & 0xff; 
		ByteCount = IDT_8T49N24x_I2cSend(IicPtr, I2CSlaveAddress, (u8*)Buffer,
									2, I2C_REPEATED_START);

		if (ByteCount != 2) {
			Retry++;
			
			/* Maximum retries */
			if (Retry == 255) {
				Exit = TRUE;
			}
		}

		/* Read data */
		else {
			/* Read data */
			ByteCount = IDT_8T49N24x_I2cRecv(IicPtr, I2CSlaveAddress,
							(u8*)Buffer, 1, I2C_STOP);
			if (ByteCount != 1) {
				Exit = FALSE;
			}

			else {
				Data = Buffer[0];
				Exit = TRUE;
			}
		}
	} while (!Exit);

	return Data;
}

/*****************************************************************************/
/**
*
* This function send a single byte to the IDT 8T49N24x  
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS Initialization was successful.
*    - XST_FAILURE I2C write error.
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_SetRegister(void *IicPtr, u8 I2CSlaveAddress,
							u16 RegisterAddress, u8 Value)
{
	u32 ByteCount = 0;
	u8 Buffer[3];
	u8 Retry = 0;

	/* Write data */
	Buffer[0] = (RegisterAddress >> 8); 
	Buffer[1] = RegisterAddress & 0xff; 
	Buffer[2] = Value;
	
	while (1) {
		ByteCount = IDT_8T49N24x_I2cSend(IicPtr, I2CSlaveAddress, (u8*)Buffer,
							3, I2C_STOP);
		if (ByteCount != 3) {
			Retry++;

			/* Maximum retries */
			if (Retry == 255) {
				return XST_FAILURE;
			}
		}
	
		else {
			return XST_SUCCESS;
		}
	}  
}

/*****************************************************************************/
/**
*
* This function modifies a single byte to the IDT 8T49N24x  
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS Initialization was successful.
*    - XST_FAILURE I2C write error.
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_ModifyRegister(void *IicPtr, u8 I2CSlaveAddress,
							u16 RegisterAddress, u8 Value, u8 Mask)
{
	u8 Data;
	int Result;

	/* Read data */
	Data = IDT_8T49N24x_GetRegister(IicPtr, I2CSlaveAddress,
							RegisterAddress);

	/* Clear masked bits */
	Data &= ~Mask; 

	/* Update */
	Data |= (Value & Mask);

	/* Write data */
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							RegisterAddress, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function checks the IDT 8T49N24x device ID  
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS Initialization was successful.
*    - XST_FAILURE I2C write error.
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_CheckDeviceID(void *IicPtr, u8 I2CSlaveAddress)
{
	u16 DeviceId;
	u8 Data;

	/* Get DEV_ID[15:12] */
	Data = IDT_8T49N24x_GetRegister(IicPtr, I2CSlaveAddress, 0x0002);

	/* Mask */
	Data &= 0x0f;

	/* Copy */
	DeviceId = Data;

	/* Shift */
	DeviceId <<= 8;

	/* Get DEV_ID[11:4] */
	Data = IDT_8T49N24x_GetRegister(IicPtr, I2CSlaveAddress, 0x0003);

	/* Copy */
	DeviceId |= Data;

	/* Shift */
	DeviceId <<= 4;

	/* Get DEV_ID[3:0] */
	Data = IDT_8T49N24x_GetRegister(IicPtr, I2CSlaveAddress, 0x0004);

	/* Copy */
	DeviceId |= (Data >> 4);

	/* xil_printf("IDT_8T49N241 device ID 0x%x\r\n", DeviceId); */

	/* Check */
	if (DeviceId == 0x0606)
		return XST_SUCCESS;

	else
		return XST_FAILURE;
}

/* Get valid Integer output divider values */
static int IDT_8T49N24x_GetIntDivTable(int FOut, int *DivTable, u8 Bypass)
{
	int NS1_Options[4] = {1,4,5,6};
	int index;
	int NS2Min = 1;
	int NS2Max = 1;
	
	int NS2Temp;
	int OutDivTemp;
	u32 VCOTemp;
	int OutDivMin;
	int OutDivMax;

	/* ceil(IDT_8T49N24X_FVCO_MIN/FOut) */
	OutDivMin = (IDT_8T49N24X_FVCO_MIN/FOut) +
					(((IDT_8T49N24X_FVCO_MIN % FOut)==0) ? 0 : 1);
	/* (int)floor(IDT_8T49N24X_FVCO_MAX/FOut) */
	OutDivMax = (IDT_8T49N24X_FVCO_MAX/FOut);

	int Count = 0;
	int *DivTablePtr = DivTable;
	
	if (Bypass == TRUE) {
		index = 0;
	}
	else {
		index = 1;
	}
	
	for (int i = index; i < (sizeof(NS1_Options)/sizeof(int)); i++) {
		/* This is for the case where we want to bypass NS2 */
		if ((NS1_Options[i] == OutDivMin) || (NS1_Options[i] == OutDivMax)) {
			NS2Min = 0;
			NS2Max = 0;
		}
	}
	
	/* if this test passes, then we know we're not in the bypass case */
	if (NS2Min == 1) {
		/* The last element in the list */
		/* (int)ceil(OutDivMin / NS1_Options[3] / 2) */
		NS2Min = (OutDivMin / NS1_Options[3] / 2) +
				((((OutDivMin / NS1_Options[3]) % 2)==0) ? 0 : 1);
		/* floor(OutDivMax / NS1_Options[index] / 2) */
		NS2Max = (OutDivMax / NS1_Options[index] / 2);
		if (NS2Max == 0)
			/* because we're rounding-down for the max, we may end-up with
			   it being 0, in which case we need to make it 1 */
			NS2Max = 1; 
	}
	 
	NS2Temp = NS2Min;
	
	while (NS2Temp <= NS2Max) {
		for (int i = index; i < (sizeof(NS1_Options)/sizeof(int)); i++) {
			if (NS2Temp == 0) {
				OutDivTemp = NS1_Options[i];
			}
			else {
				OutDivTemp = NS1_Options[i] * NS2Temp * 2;
			}
			
			VCOTemp = FOut * OutDivTemp;
			
			if ((VCOTemp <= IDT_8T49N24X_FVCO_MAX) &&
			    (VCOTemp >= IDT_8T49N24X_FVCO_MIN)) {
				*DivTablePtr = OutDivTemp;
				Count++;
				DivTablePtr++;
			}
		}
		NS2Temp++;
	}
	
	return Count;
}

/*****************************************************************************/
/**
*
* This function calculates the settings  
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS Initialization was successful.
*    - XST_FAILURE I2C write error.
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_CalculateSettings(int FIn, int FOut,
							IDT_8T49N24x_Settings* RegSettings)
{
	int DivTable[20];
	int DivTableCount;
	
	int MaxDiv = 0;
	double FVCO;
	int NS1_RegSettings;
	int NS2_RegSettings;
	
	int NS1Ratio;
	int NS2Ratio;
	
	double FracDiv;
	
	double UpperFBDiv;
	int DSMInt_RegSettings;
	int DSMFrac_RegSettings;
	double Ratio;
	
	int LOS;

	/* Get the valid integer dividers */
	DivTableCount = IDT_8T49N24x_GetIntDivTable(FOut, DivTable, FALSE);
	
	/* Find the highest divider */
	for (int i = 0; i < DivTableCount; i++) {
		if (MaxDiv < DivTable[i]) {
			MaxDiv = DivTable[i];
		}
	}
	
	FVCO = (double)FOut*MaxDiv;
	
	/***************************************************/
	/* INTEGER DIVIDER: Determine NS1 register setting */
	/***************************************************/
	
	/* Only use the divide-by-1 option for really small divide ratios
	 * note that this option will never be on the list for the
	 * Q0 - Q3 dividers
	 */
	if (MaxDiv < 4) { 
		
	}
	
	/* Make sure we can divide the ratio by 4 in NS1 and by 1 or an
	 * even number in NS2
	 */
	if ((MaxDiv == 4) ||
	    (MaxDiv % 8 == 0)) { 
		/* Divide by 4 register selection */
		NS1_RegSettings = 2;
	}
	
	/* Make sure we can divide the ratio by 5 in NS1 and by 1 or
	 * an even number in NS2
	 */
	if ((MaxDiv == 5) ||
	    (MaxDiv % 10 == 0)) {
		/* Divide by 5 register selection */
		NS1_RegSettings = 0;
	}
	
	/* Make sure we can divide the ratio by 6 in NS1 and by 1 or
	 * an even number in NS2
	 */
	if ((MaxDiv == 6) ||
	    (MaxDiv % 12 == 0)) {
		/* Divide by 6 register setting */
		NS1_RegSettings = 1;
	}
	
	/***************************************************/
	/* INTEGER DIVIDER: Determine NS2 register setting */
	/***************************************************/
	
	switch (NS1_RegSettings) {
		case (0) :
			NS1Ratio = 5;
		break;
		
		case (1) :
			NS1Ratio = 6;
		break;
		
		case (2) :
			NS1Ratio = 4;
		break;
		
		case (3) :
			/* This is the bypass (divide-by-1) option */
			NS1Ratio = 1;
		break;
		
		default :
			NS1Ratio = 6;
		break;
	}
	
	/* floor(MaxDiv / NS1Ratio) */
	NS2Ratio = (MaxDiv / NS1Ratio);
	
	/* floor(NS2Ratio/2) */
	NS2_RegSettings = (NS2Ratio/2);
	
	/***************************************************/
	/* FRACTIONAL DIVIDER:                             */
	/***************************************************/
	FracDiv = FVCO/FOut;
	
	u32 N_Q2 = 0;
	u32 NFRAC_Q2 = 0;

	/* double frac_numerator =
	 * 		round(((FracDiv / 2.0) - (int)(FracDiv / 2.0)) * pow(2,28));
	 */
	double frac_numerator = (((FracDiv / 2.0) - (int)(FracDiv / 2.0))
								* 268435456);
	frac_numerator = (int)(frac_numerator + 0.5);
	/* This is the case where the fractional portion is 0.
	 * Due to precision limitations, sometimes fractional portion of the
	 * Effective divider gets rounded to 1.  This checks for that condition
	 */
	if ((frac_numerator >= 268435456) ||
		((FracDiv/2.0) == (int)(FracDiv/2.0))) {
		/* N_Q2 = (int)round(FracDiv / 2.0); */
		N_Q2 = (FracDiv / 2.0);
		N_Q2 = (int)(N_Q2 + 0.5);
		NFRAC_Q2 = 0;
	}
	/* This is the case where the fractional portion is not 0. */
	else {
		/* N_Q2 = (int)floor(FracDiv / 2.0); */
		N_Q2 = (FracDiv / 2.0);
		N_Q2 = (int)(N_Q2 + 0.5);
		NFRAC_Q2 = (int)frac_numerator;
	}

	/*****************************************************/
	/* Calculate the Upper Loop Feedback divider setting */
	/*****************************************************/
	
	UpperFBDiv = (double)(FVCO) / (2*IDT_8T49N24X_XTAL_FREQ);

	/* DSMInt_RegSettings = (int)floor(UpperFBDiv); */
	DSMInt_RegSettings = (int)(UpperFBDiv);
	
	/*DSMFrac_RegSettings =
	 * 			(int)round((UpperFBDiv - floor(UpperFBDiv))*pow(2,21));
	 */
	DSMFrac_RegSettings = (int)(((UpperFBDiv - (int)UpperFBDiv)*2097152)
								+ 0.5);

	/*****************************************************/
	/* Calculate the Lower Loop Feedback divider and
	 * Input Divider
	 *****************************************************/
	
	Ratio = FVCO/FIn;
	
	int M1 = 0;
	int PMin = (int)FIn/IDT_8T49N24X_FPD_MAX;
	
	/* This M1 divider sets the input PFD frequency at 128KHz, the set max */
	/* int M1Min = (int)(FVCO/IDT_8T49N24X_FPD_MAX); */

	int M1_default;
	int P_default;
	int error_tmp = 999999;
	int error = 99999999;

	int count = 0;

	/* Start from lowest divider and iterate until 0 error is found
	 * or the divider limit is exhausted.
	 */
	/* Keep the setting with the lowest error */
	for (int i = PMin; i <= IDT_8T49N24X_P_MAX; i++) {
		/* M1 = (int)round(i*Ratio); */
		M1 = (int)(i*Ratio + 0.5);
		count++;
		if (M1 < IDT_8T49N24X_M_MAX) {
			error_tmp = (int)(Ratio*1e9 - (M1*1e9 / i));

			if (abs(error_tmp) < error || error_tmp == 0) {
				error = abs(error_tmp);
				M1_default = M1;
				P_default = i;

				if (error_tmp == 0)
					break;
			}
		}
		else {
			break;
		}
	}
	
	/* Calculate LOS */
	LOS = FVCO / 8 / FIn; 
	LOS = LOS + 3;
	if (LOS < 6)
		LOS = 6;

	/* Copy registers */
	RegSettings->NS1_Qx = NS1_RegSettings;
	RegSettings->NS2_Qx = NS2_RegSettings;
	
	RegSettings->N_Qx = N_Q2;
	RegSettings->NFRAC_Qx = NFRAC_Q2;

	RegSettings->DSM_INT = DSMInt_RegSettings;
	RegSettings->DSM_FRAC = DSMFrac_RegSettings;
	RegSettings->M1_x = M1_default;
	RegSettings->PRE_x = P_default;
	RegSettings->LOS_x = LOS;

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function calculates and sets the IDT 8TN49N24x device with the 
* given clock configuration.
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
* @param FIn specifies the input frequency.
* @param FOut specifies the output frequency.
* @param FreeRun specifies if the operation mode is locked/synthesizer mode.
*    - TRUE Synthesizer mode (Fout only)
*    - FALSE Locked mode (Fout locked to Fin)
*
* @return
*    - XST_SUCCESS Initialization was successful.
*    - XST_FAILURE I2C write error or incorrect parameters detected.
*
* @note 
*
******************************************************************************/
int IDT_8T49N24x_SetClock(void *IicPtr, u8 I2CSlaveAddress, int FIn,
							int FOut, u8 FreeRun)
{
	int Result;

	if ((FIn < IDT_8T49N24X_FIN_MIN) &&
	   (FIn > IDT_8T49N24X_FIN_MAX)) {
		return XST_FAILURE;
	}
	
	if ((FOut < IDT_8T49N24X_FOUT_MIN) &&
		(FOut > IDT_8T49N24X_FOUT_MAX)) {
		return XST_FAILURE;
	}
	
	IDT_8T49N24x_Settings RegSettings;

	/* Calculate settings */
	IDT_8T49N24x_CalculateSettings(FIn, FOut, &RegSettings);

	/* Disable DPLL and APLL calibration */
	Result = IDT_8T49N24x_Enable(IicPtr, I2CSlaveAddress, FALSE);

	/* Mode */
	if (FreeRun == TRUE) {
		/* Disable reference clock input 0 */
		Result = IDT_8T49N24x_ReferenceInput(IicPtr, I2CSlaveAddress,
							0, FALSE);

		/* Disable reference clock input 1 */
		Result = IDT_8T49N24x_ReferenceInput(IicPtr, I2CSlaveAddress,
							1, FALSE);

		/* Set synthesizer mode */
		Result = IDT_8T49N24x_Mode(IicPtr, I2CSlaveAddress, TRUE);
	}

	else {
		/* Enable reference clock input 0 */
		Result = IDT_8T49N24x_ReferenceInput(IicPtr, I2CSlaveAddress,
							0, TRUE);

		/* Disable reference clock input 1 */
		Result = IDT_8T49N24x_ReferenceInput(IicPtr, I2CSlaveAddress,
							1, FALSE);

		/* Set jitter attentuator mode */
		Result = IDT_8T49N24x_Mode(IicPtr, I2CSlaveAddress, FALSE);
	}

	/* Pre-divider Input 0 */
	Result = IDT_8T49N24x_PreDivider(IicPtr, I2CSlaveAddress,
							RegSettings.PRE_x, 0);

	/* Pre-divider Input 1 */
	Result = IDT_8T49N24x_PreDivider(IicPtr, I2CSlaveAddress,
							RegSettings.PRE_x, 1);

	/* M1 feedback Input 0 */
	Result = IDT_8T49N24x_M1Feedback(IicPtr, I2CSlaveAddress,
							RegSettings.M1_x, 0);

	/* M1 feedback Input 1 */
	Result = IDT_8T49N24x_M1Feedback(IicPtr, I2CSlaveAddress,
							RegSettings.M1_x, 1);

	/* DSM integer */
	Result = IDT_8T49N24x_DSMInteger(IicPtr, I2CSlaveAddress,
							RegSettings.DSM_INT);

	/* DSM fractional */
	Result = IDT_8T49N24x_DSMFractional(IicPtr, I2CSlaveAddress,
							RegSettings.DSM_FRAC);

	/* Output divider integer output 2 */
	Result = IDT_8T49N24x_OutputDividerInteger(IicPtr, I2CSlaveAddress,
							RegSettings.N_Qx, 2);

	/* Output divider integer output 3 */
	Result = IDT_8T49N24x_OutputDividerInteger(IicPtr, I2CSlaveAddress,
							RegSettings.N_Qx, 3);

	/* Output divider fractional output 2 */
	Result = IDT_8T49N24x_OutputDividerFractional(IicPtr,
							I2CSlaveAddress, RegSettings.NFRAC_Qx, 2);

	/* Output divider fractional output 3 */
	Result = IDT_8T49N24x_OutputDividerFractional(IicPtr,
							I2CSlaveAddress, RegSettings.NFRAC_Qx, 3);

	/* Input monitor control 0 */
	Result = IDT_8T49N24x_InputMonitorControl(IicPtr, I2CSlaveAddress,
							RegSettings.LOS_x, 0);

	/* Input monitor control 1 */
	Result = IDT_8T49N24x_InputMonitorControl(IicPtr, I2CSlaveAddress,
							RegSettings.LOS_x, 1);

	/* Enable DPLL and APLL calibration */
	Result = IDT_8T49N24x_Enable(IicPtr, I2CSlaveAddress, TRUE);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the input monitor control
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_InputMonitorControl(void *IicPtr,
							u8 I2CSlaveAddress, u32 Value, u8 Input)
{
	int Result;
	u8 Data;
	u16 Address;

	if (Input == 1)
		Address = 0x0074;
	else
		Address = 0x0071;

	/* LOSx[16] */
	Data = (Value >> 16) & 0x1; 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address, Data);

	/* LOSx[15:8] */
	Data = (Value >> 8); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+1, Data);

	/* LOSx[7:0] */
	Data = (Value & 0xff); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+2, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the pre-divider
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_PreDivider(void *IicPtr, u8 I2CSlaveAddress,
							u32 Value, u8 Input)
{
	int Result;
	u8 Data;
	u16 Address;

	if (Input == 1)
		Address = 0x000e;
	else
		Address = 0x000b;

	/* PREx[20:16] */
	Data = (Value >> 16) & 0x1f; 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address, Data);

	/* PREx[15:8] */
	Data = (Value >> 8); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+1, Data);

	/* PREx[7:0] */
	Data = (Value & 0xff); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+2, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the M1 feedback
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_M1Feedback(void *IicPtr, u8 I2CSlaveAddress,
							u32 Value, u8 Input)
{
	int Result;
	u8 Data;
	u16 Address;

	if (Input == 1)
		Address = 0x0011;
	else
		Address = 0x0014;

	/* M1x[23:16] */
	Data = (Value >> 16); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address, Data);

	/* M1x[15:8] */
	Data = (Value >> 8); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+1, Data);

	/* M1x[7:0] */
	Data = (Value & 0xff); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+2, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the DSM integer
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_DSMInteger(void *IicPtr, u8 I2CSlaveAddress,
							u16 Value)
{
	int Result;
	u8 Data;

	/* DSM_INT[8] */
	Data = (Value >> 8) & 0x01; 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0025, Data);

	/* DSM_INT[7:0] */
	Data = (Value & 0xff); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0026, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the DSM fractional
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_DSMFractional(void *IicPtr, u8 I2CSlaveAddress,
							u32 Value)
{
	int Result;
	u8 Data;

	/* DSM_FRAC[20:16] */
	Data = (Value >> 16) & 0x1f; 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0028, Data);

	/* DSM_FRAC[15:8] */
	Data = (Value >> 8); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0029, Data);

	/* DSM_FRAC[7:0] */
	Data = (Value & 0xff); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x002a, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the Output divider integer
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_OutputDividerInteger(void *IicPtr,
							u8 I2CSlaveAddress, u32 Value, u8 Output)
{
	int Result;
	u8 Data;
	u16 Address;

	switch (Output) {
		case 0 : Address = 0x003f;
			break;
		case 1 : Address = 0x0042;
			break;
		case 2 : Address = 0x0045;
			break;
		case 3 : Address = 0x0048;
			break;
	}
	
	/* N_Qm[17:16] */
	Data = (Value >> 16) & 0x03; 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address, Data);

	/* N_Qm[15:8] */
	Data = (Value >> 8); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+1, Data);

	/* N_Qm[7:0] */
	Data = (Value & 0xff); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+2, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the Output divider fractional
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_OutputDividerFractional(void *IicPtr,
							u8 I2CSlaveAddress, u32 Value, u8 Output)
{
	int Result;
	u8 Data;
	u16 Address;

	switch (Output) {
		case 0 : Address = 0x0000;
			break;
		case 1 : Address = 0x0057;
			break;
		case 2 : Address = 0x005b;
			break;
		case 3 : Address = 0x005f;
			break;
	}
	
	/* NFRAC_Qm[27:24] */
	Data = (Value >> 24) & 0x0f; 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address, Data);

	/* NFRAC_Qm[23:16] */
	Data = (Value >> 16); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+1, Data);

	/* NFRAC_Qm[15:8] */
	Data = (Value >> 8); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+2, Data);

	/* NFRAC_Qm[7:0] */
	Data = (Value & 0xff); 
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Address+3, Data);

	return Result;
}

/*****************************************************************************/
/**
*
* This function sets the mode (jitter attenuator / synthesizer)
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_Mode(void *IicPtr, u8 I2CSlaveAddress,
							u8 Synthesizer)
{
	int Result;
	u8 Value;
	u8 Mask;

	/* Digital PLL */
	/* State[1:0] */
	if (Synthesizer) {
		Value = 0x01;		/* Force FREERUN */
		Value |= (1<<4);	/* Disable reference input 0 */
		Value |= (1<<5);	/* Disable reference input 1 */
	}

	else {
		Value = 0x00;		/* Run automatically */
		Value |= (1<<5);	/* Disable reference input 1 */
	}
	Mask = 0x33;
	Result = IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x000a, Value, Mask);

	/* Analog PLL */
	/* SYN_MODE */
	if (Synthesizer) {
		Value = (1<<3);		/* Synthesizer mode */
	}

	else {
		Value = 0x00;		/* Jitter attenuator mode */
	}
	Mask = (1<<3);
	Result = IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x0069, Value, Mask);

	return Result;
}
/*****************************************************************************/
/**
*
* This function sets disables / enables the reference inputs
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_ReferenceInput(void *IicPtr, u8 I2CSlaveAddress,
							u8 Input, u8 Enable)
{
	int Result;
	u8 Value;
	u8 Mask;
	u8 Shift;

	if (Input == 1) {
		Shift = 5;
	}

	else {
		Shift = 4;
	}

	/* Enable */
	if (Enable) {
		Value = 0x00;		/* Enable */
	}

	/* Disable */
	else {
		Value = (1<<Shift);	/* Disable reference input  */
	}
	Mask = (1<<Shift);
	Result = IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x000a, Value, Mask);

	return Result;
}

/*****************************************************************************/
/**
*
* This function enables/disables the DPLL and APLL calibration
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_Enable(void *IicPtr, u8 I2CSlaveAddress,
							u8 Enable)
{
	int Result;
	u8 Value;
	u8 Mask;

	if (Enable) {
		/* Digital PLL enabled - Calibration logic for Analog PLL enabled */
		Value = 0x00;
	}

	else {
		/* Digital PLL disabled - Calibration logic for Analog PLL disabled */
		Value = 0x05;
	}
	Mask = 0x05;
	Result = IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x0070, Value, Mask);

	return Result;
}

/*****************************************************************************/
/**
*
* This function initializes the IDT 8TN49N24x with default values
* for use with the Video FMC.
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS Initialization was successful.
*    - XST_FAILURE I2C write error.
*
* @note None.
*
******************************************************************************/
int IDT_8T49N24x_Init(void *IicPtr, u8 I2CSlaveAddress)
{
	int Result;

	/* Check device ID */
	Result = IDT_8T49N24x_CheckDeviceID(IicPtr, I2CSlaveAddress);

	if (Result == XST_SUCCESS) {

		/* Disable DPLL and APLL calibration
		 * The i2c interface is clocked by the APLL.
		 * During the PLL parameters update, the i2c might become unresponsive.
		 * To prevent this, the DPLL and APLL calibration are disabled during
		 * the i2c transactions
		 */
		Result = IDT_8T49N24x_Enable(IicPtr, I2CSlaveAddress, FALSE);

		/* Configure device */
		Result = IDT_8T49N24x_Configure_JA(IicPtr, I2CSlaveAddress);

		/* Enable DPLL and APLL calibration */
		Result = IDT_8T49N24x_Enable(IicPtr, I2CSlaveAddress, TRUE);

		return Result;
	}

	return XST_FAILURE;
}

static int IDT_8T49N24x_Configure_JA(void *IicPtr, u8 I2CSlaveAddress)
{
	int Result;
	u32 Index;

	/* The configuration is started from address 0x08 */
	for (Index=8; Index<sizeof(IDT_8T49N24x_Config_JA); Index++) {
		/* Skip address 0x70 */
		/* Address 0x70 enables the DPLL and APLL calibration */
		if (Index != 0x070) {
			Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Index, IDT_8T49N24x_Config_JA[Index]);
		}
	}
	return Result;
}

/*****************************************************************************/
/**
*
* This function displays a registerdump of the IDT 8TN49N24x device.
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return None
*
* @note None.
*
******************************************************************************/
void IDT_8T49N24x_RegisterDump(void *IicPtr, u8 I2CSlaveAddress)
{
	u8 Data;
	u32 i;
	int Result;

	xil_printf("Register dump\r\n");

	Result = IDT_8T49N24x_CheckDeviceID(IicPtr, I2CSlaveAddress);

	if (Result == XST_SUCCESS) {
		xil_printf("\r\n");
		xil_printf("---------------------\r\n");
		xil_printf("- IDT8T49N241 I2C dump:\r\n");
		xil_printf("---------------------\r\n");

		xil_printf("     ");
		for (i=0; i<16; i++)
			xil_printf("+%01x ", i);

		xil_printf("\r\n     ");
		for (i=0; i<16; i++)
			xil_printf("---");

		for (i = 0; i < 256; i++) {
			if ((i % 16) == 0) {
				xil_printf("\r\n%02x : ", i);
			}
			Data = IDT_8T49N24x_GetRegister(IicPtr,
							I2CSlaveAddress, i);
			xil_printf("%02x ", Data);
		}

		xil_printf("\r\n");
	}

	else {
		xil_printf("IDT 8T49N241 not found!\r\n");
	}
}

#if (IDT_8T49N24X_ADV_FUNC_EN == 1)
/*****************************************************************************/
/**
*
* This function set the GPIO outputs
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_GPIO(void *IicPtr, u8 I2CSlaveAddress)
{
	int Result;

	/* GPIO_DIR */
	/* GPIO3 is output */
	/* Rest of the pins are input */
	Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0030, 0x08);

	/* GPIxSEL[2] */
	Result |= IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0031, 0x00);

	/* GPIxSEL[1] */
	Result |= IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0032, 0x00);

	/* GPIxSEL[0] */
	Result |= IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0033, 0x00);

	/* GPOxSEL[2] */
	Result |= IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0034, 0x00);

	/* GPOxSEL[1] */
	Result |= IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0035, 0x00);

	/* GPOxSEL[0] */
	Result |= IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							0x0036, 0x08);

	if (Result != XST_SUCCESS)
		return XST_FAILURE;
	else
		return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function set the input reference
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_SelectInputReference(void *IicPtr,
							u8 I2CSlaveAddress, u8 Input)
{
	int Result;
	u8 Value;
	u8 Mask;
	u8 Shift;

	Shift = 5;

	/* Clock 1 */
	if (Input == 1) {
		Value = (0x05 << Shift); 
	}

	/* Clock 0 */
	else {
		Value = (0x04 << Shift); 	
	}
	Mask = 0x07 << Shift;

	Result = IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x0008, Value, Mask);

	return Result;
}

/*****************************************************************************/
/**
*
* This function set the output drivers
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_OutputDriver(void *IicPtr, u8 I2CSlaveAddress,
							u8 Output, u8 Enable)
{
	int Result;
	u8 Value;
	u8 Mask;
	u16 Address;
	u8 Shift;

	/* OUTEN */
	Mask = (1 << Output);
	if (Enable)
		Value = Mask;
	else
		Value = 0x00;

	Result = IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x0039, Value, Mask);

	/* OUTMODE */
	switch (Output) {
		case 0 : 
			Address = 0x003e;
			Shift = 1;
			break;

		case 1 : 
			Address = 0x003e;
			Shift = 5;
			break;

		case 2 : 
			Address = 0x003d;
			Shift = 1;
			break;

		case 3 : 
			Address = 0x003d;
			Shift = 5;
			break;
	}

	if (Enable)
		Value = 0x02;	/* LVDS */
	else
		Value = 0x00;	/* High-impedance */

	Value <<= Shift;
	Mask = (0x07 << Shift);

	Result |= IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							Address, Value, Mask);

	/* Qm_DIS */
	if (!Enable)
		Value = (1 << Output);
	else
		Value = 0;

	Mask = (1 << Output);

	Result |= IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x006f, Value, Mask);

	if (Result != XST_SUCCESS)
		return XST_FAILURE;
	else
		return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function sets the lock mode
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return
*    - XST_SUCCESS 
*    - XST_FAILURE 
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_LockMode(void *IicPtr, u8 I2CSlaveAddress)
{
	int Result;
	u8 Value;
	u8 Mask;

	Value = 0x02;
	Mask = 0x02;
	Result = IDT_8T49N24x_ModifyRegister(IicPtr, I2CSlaveAddress,
							0x006c, Value, Mask);

	return Result;
}

int IDT_8T49N24x_SetGPOut(u32 IicPtr, u8 I2CSlaveAddress, u8 PortID,
							u8 Set)
{
	if (PortID > 3) {
		xil_printf("Invalid port ID\r\n");
		return XST_FAILURE;
	}
	
	u32 ByteCount = 0;
	u8 Data = 0;
	u8 Buffer[3];
	
	Buffer[0] = 0x00; /* MSB RegAddr */
	Buffer[1] = 0x38; /* LSB RegAddr */
	ByteCount = XIic_Send(IicPtr, I2CSlaveAddress, (u8*)Buffer, 2,
							I2C_REPEATED_START);
	if (ByteCount != 2) {
		return XST_FAILURE;
	}
	ByteCount = XIic_Recv(IicPtr, I2CSlaveAddress, (u8*)Buffer, 1,
							I2C_STOP);
	if (ByteCount != 1) {
		return XST_FAILURE;
	}
	Data = Buffer[0];
	
	if (Set == TRUE) {
		Data |= (1<<PortID);
	}
	else {
		Data &= ~(1<<PortID);
	}
	
	Buffer[0] = 0x00; /* MSB RegAddr */
	Buffer[1] = 0x38; /* LSB RegAddr */
	Buffer[2] = Data;
	ByteCount = XIic_Send(IicPtr, I2CSlaveAddress, (u8*)Buffer, 3,
							I2C_STOP);
	if (ByteCount != 3) {
		return XST_FAILURE;
	}
	
	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function the IDT 8TN49N24x device with the data from the
* configuration table.
*
* @param I2CBaseAddress is the baseaddress of the I2C core.
* @param I2CSlaveAddress is the 7-bit I2C slave address.
*
* @return None
*
* @note None.
*
******************************************************************************/
static int IDT_8T49N24x_Configure(void *IicPtr, u8 I2CSlaveAddress)
{
	int Result;
	u32 Index;
	xil_printf("freerun\r\n");

	/* The configuration is started from address 0x08 */
	for (Index=8; Index<sizeof(IDT_8T49N24x_Config_Syn); Index++) {
		/* Skip address 0x70 */
		/* Address 0x70 enables the DPLL and APLL calibration */
		if (Index != 0x070) {
			Result = IDT_8T49N24x_SetRegister(IicPtr, I2CSlaveAddress,
							Index, IDT_8T49N24x_Config_Syn[Index]);
		}
	}
	return Result;
}
#endif

/*****************************************************************************/
/**
*
* This function setup IDT 8T49N24x clock generator either in free or
* locked mode.
*
* @param  IicPtr IIC instance pointer.
* @param  IICAddress device's IIC bus address.
* @param  InFreq input frequency 0-free running mode.
* @param  OutFreq requested output frequency.
*
* @return
*   - 0 XST_SUCCESS.
*   - 1 XST_FAILURE.
*
* @note   None.
*
******************************************************************************/
int IDT_8T49N24x_I2cClk(void *IicPtr, u8 IICAddress,
		u32 InFreq, u32 OutFreq)
{
	u32 Status;

	/* Reset I2C controller before issuing new transaction. This is
	 * required to recover the IIC controller in case a previous transaction
	 * is pending.
	 */
	IDT_8T49N24x_I2cReset(IicPtr);

	/* Free running mode */
	if (InFreq == 0) {
		Status = IDT_8T49N24x_SetClock((IicPtr), (IICAddress),
								(IDT_8T49N24X_XTAL_FREQ), OutFreq, TRUE);

		if (Status != (XST_SUCCESS)) {
			xil_printf("Error programming IDT_8T49N241\r\n");
			return XST_FAILURE;
		}
	}

	/* Locked mode */
	else {
		Status = IDT_8T49N24x_SetClock((IicPtr), (IICAddress),
								InFreq, OutFreq, FALSE);

		if (Status != (XST_SUCCESS)) {
			xil_printf("Error programming IDT_8T49N241\r\n");
			return XST_FAILURE;
		}
	}
	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* This function setup IDT 8T49N24x clock generator to enable GPIO[3] to be
* used as LoL indicator
*
* @param  IicBaseAddr IIC controller base address.
* @param  IICAddress device's IIC bus address.
*
* @return
*   - 0 XST_SUCCESS.
*   - 1 XST_FAILURE.
*
* @note   None.
*
******************************************************************************/
int IDT_8T49N24x_GpioLolEnable(void *IicPtr, u8 IICAddress)
{
	int Status;

	/* Reset I2C controller before issuing new transaction. This is
	 * required to recover the IIC controller in case a previous transaction
	 * is pending.
	 */
	IDT_8T49N24x_I2cReset(IicPtr);

	/* Set GPIO[3:0] to output mode */
	Status = IDT_8T49N24x_SetRegister(IicPtr, IICAddress,
							0x30, 0x0F);

	/* Set GPOxSEL[2] to 0000b */
	Status |= IDT_8T49N24x_SetRegister(IicPtr, IICAddress,
							0x34, 0x00);

	/* Set GPOxSEL[1] to 0000b */
	Status |= IDT_8T49N24x_SetRegister(IicPtr, IICAddress,
							0x35, 0x00);

	/* Set GPOxSEL[0] to 1111b */
	Status |= IDT_8T49N24x_SetRegister(IicPtr, IICAddress,
							0x36, 0x0F);

	if (Status != (XST_SUCCESS)) {
		xil_printf("Error programming IDT_8T49N241 GPIO=Lol\r\n");
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/** @} */
