/**
 * File Name : i2c_hdmi_phy.c
 *
 * File Description :
 * This file implements the I2C function for communication between the hdmi link layer and phy layer
 *
 * Author : Hee Myung Noh
 * Dept : AP Development
 * Created Date : 2009/01/22
 * Version : 0.1
 * History 
 *  - Initiate version (Hee Myung Noh 090122)
 */

#include <windows.h>
#include <bsp_cfg.h>
#include <register_map.h>

#include "i2c_hdmi.h"
#include "iic_reg.h"


//#define	DISP_I2C_HDMI_PHY_REG


static struct {
	signed long    state;
	UINT8 	*buffer;
	signed long    bytes;
} i2c_hdmi_context;


static volatile CMU_CLK_REG     *g_pSysConReg     = NULL;
static volatile IIC_REG     *g_pIICReg        = NULL;


static BOOL I2CHDMI_Release(void);

/**
 * Polling interrupt 
 *
 * @return  On success, return OK;Otherwise return, ERROR.
 */
static BOOL I2CHDMI_InterruptWait(void)
{
	UINT32 ucStatus;
	BOOL retval = TRUE;

	do
	{
		ucStatus = g_pIICReg->IICCON;


		if (ucStatus & I2C_PEND)
		{

#if 0
			// check status flags
			ucReg = g_pIICReg->IICSTAT;

			if (ucReg & (I2C_STAT_ARBITRERR | I2C_STAT_ADDRASSLAVE |I2C_STAT_ADDRZERO /*| I2C_STAT_ACKERROR*/))
			{
				if (ucReg & I2C_STAT_ARBITRERR)
				{
                    RETAILMSG(1,(_T("I2C_STAT_ARBITRERR\n\r")));
				}
				else if (ucReg & I2C_STAT_ADDRASSLAVE)
				{
                    RETAILMSG(1,(_T("I2C_STAT_ADDRASSLAVE\n\r")));
				}
				else if (ucReg & I2C_STAT_ADDRZERO)
				{
                    RETAILMSG(1,(_T("I2C_STAT_ADDRZERO\n\r")));
				}
				else if (ucReg & I2C_STAT_ACKERROR)
				{
                    RETAILMSG(1,(_T("I2C_STAT_ACKERROR\n\r")));
				}
				I2CHDMI_Release();
				retval = FALSE;
			}
#endif            
			break;
		}

        Sleep(1);
	}while (1);

	return retval;
}

/**
 *  Initialize I2C library
 */
BOOL I2CHDMI_Init( void* pSysConReg, void * pIICReg)
{

    if (pSysConReg == NULL || pIICReg == NULL)
        return FALSE;

    g_pSysConReg = (PCMU_CLK_REG)pSysConReg;
    g_pIICReg    = (PIIC_REG)pIICReg;

    g_pIICReg->IICLC = 0x5;
	
	return TRUE;
}

/**
 * Read data through I2C
 *
 * @return  On success, return OK;Otherwise return ERROR.
 */
BOOL I2CHDMI_Read(UINT8 ucAddr, UINT8 ucBytes, UINT8 *pBuffer)
{
	BOOL iRetval = TRUE;
	UINT32 i, uProcess = TRUE;

	// set state
	i2c_hdmi_context.state = STATE_RX_ADDR;

	// set parameters
	i2c_hdmi_context.buffer = pBuffer;
	i2c_hdmi_context.bytes = ucBytes;

	/* initiate transfer */

	// set clock, enable interrupts and ack generation
	g_pIICReg->IICCON = (I2C_CLK | I2C_INT | I2C_ACK);
    
	// to make IDSR write-enabled
    g_pIICReg->IICSTAT = (I2C_ENABLE | I2C_MODE_MRX);

	// put address to shift register
    g_pIICReg->IICDS =  (UINT8)(ucAddr & 0xFE);
    
	// master rx mode + start + enable - "START" sequence generation
    g_pIICReg->IICSTAT =  (I2C_ENABLE | I2C_START | I2C_MODE_MRX); // 0xB0
        
	while (uProcess)
	{
		//In case of Master RX Mode, Last data is not ack. but set the "ACK not received" bit
		if(i2c_hdmi_context.state != STATE_RX_STOP)
		{
			if (I2CHDMI_InterruptWait() != TRUE)
			{
                RETAILMSG(1,(_T("InterruptWait() fail \n\r")));

				iRetval = FALSE;
				break;
			}
		}

		switch (i2c_hdmi_context.state)
		{
			case STATE_RX_DATA:
				// read byte
				(*(i2c_hdmi_context.buffer)) = g_pIICReg->IICDS;
				i2c_hdmi_context.buffer++;
				--(i2c_hdmi_context.bytes);
				            
				if (i2c_hdmi_context.bytes == 1)
				{
					i2c_hdmi_context.state = STATE_RX_STOP;

                    g_pIICReg->IICCON = (I2C_CLK | I2C_INT | I2C_INT_CLEAR);
				}
				else
				{
                    g_pIICReg->IICCON = (I2C_CLK | I2C_INT | I2C_INT_CLEAR | I2C_ACK);
				}

				break;

			case STATE_RX_ADDR:
				i2c_hdmi_context.state = STATE_RX_DATA;

				if (i2c_hdmi_context.bytes == 1)
				{
					i2c_hdmi_context.state = STATE_RX_STOP;

					g_pIICReg->IICCON = (I2C_CLK | I2C_INT | I2C_INT_CLEAR);  
				}
				else
				{
					g_pIICReg->IICCON = (I2C_CLK | I2C_INT | I2C_INT_CLEAR | I2C_ACK);
				}
				break;

			case STATE_RX_STOP:
				i2c_hdmi_context.state = STATE_IDLE;

				for(i=0 ; i<10000 ; i++);
				
				(*(i2c_hdmi_context.buffer)) = (UINT8)g_pIICReg->IICDS;

				//"STOP" sequence generation
                g_pIICReg->IICSTAT = (I2C_MODE_MRX|I2C_ENABLE);
                
				// clear interrupt pending flag and interrupt clear bit
                g_pIICReg->IICCON = (I2C_CLK | I2C_INT | I2C_INT_CLEAR);

                g_pIICReg->IICSTAT = (I2C_MODE_MRX);
                                
				while( g_pIICReg->IICSTAT & (1<<5) );

				uProcess = FALSE;

				break;

			case STATE_IDLE:
			default:

				iRetval = FALSE;
				uProcess = FALSE;
				break;
		}
	}

	return iRetval;
}

/**
 * Write data through I2C
 *
 * @return  On success, return OK;Otherwise return ERROR.
 */
 BOOL I2CHDMI_Write(UINT8 ucAddr, UINT8 ucBytes, UINT8 *pBuffer)
{
	BOOL iRetval = TRUE;
	UINT32 uProcess = TRUE;
    UINT8 Ptr;

	// set state
	i2c_hdmi_context.state = STATE_TX_ADDR;

	// set parameters
	i2c_hdmi_context.buffer = pBuffer;
	i2c_hdmi_context.bytes = ucBytes;
    Ptr = 0;

	/* initiate transfer */

	// set clock, enable interrupts and ack generation
    g_pIICReg->IICCON =  (I2C_CLK | I2C_INT | I2C_ACK); // 0xE1
    
	// to make IDSR write-enabled
    g_pIICReg->IICSTAT = (I2C_ENABLE | I2C_MODE_MTX); // 0xE1
        
	// put address to shift register
    g_pIICReg->IICDS = (UINT8)(ucAddr & 0xFE);
    
	// master tx mode + start + enable - "START" sequence generation
    g_pIICReg->IICSTAT = (I2C_ENABLE | I2C_START | I2C_MODE_MTX); // 0xF0

       
	while (uProcess)
	{
		if (I2CHDMI_InterruptWait() != TRUE)
		{
            RETAILMSG(1,(_T("InterruptWait() fail \n\r")));
			iRetval = FALSE;
			break;
		}

		switch (i2c_hdmi_context.state)
		{
			case STATE_TX_ADDR:
			case STATE_TX_DATA:

				i2c_hdmi_context.state = STATE_TX_DATA;

                if(Ptr < i2c_hdmi_context.bytes) // if last byte
				{
                    g_pIICReg->IICDS = (UINT8)i2c_hdmi_context.buffer[Ptr];
				}
				else
				{
					i2c_hdmi_context.state = STATE_TX_STOP;
                    g_pIICReg->IICSTAT = 0xD0; //MTX_STOP
				}

                Ptr++;
                g_pIICReg->IICCON &= ~(1<<4);
                                
				break;

			case STATE_TX_STOP:

				i2c_hdmi_context.state = STATE_IDLE;

				uProcess = FALSE;
				break;

			case STATE_IDLE:
			default:
				iRetval = FALSE;
                
				uProcess = FALSE;
				break;
		}
	}

	return iRetval;
}

/**
 * Release I2C line
 */
BOOL I2CHDMI_Release(void)
{
	// clear interrupt pending flag and interrupt clear bit
    g_pIICReg->IICCON = (I2C_CLK | I2C_INT | I2C_INT_CLEAR);

	// disable 
    g_pIICReg->IICSTAT = (I2C_IDLE);
        
	return TRUE;
}
