#include "my_includes.h"
#include "I2C.h"

// Note. Remember to reset device befroe acceess I2C interface

#ifdef DEBUG_I2C
    #define I2C_DEBUG(x)    DEBUG(x)  
#else
    #define I2C_DEBUG(x)
#endif


// internal funciton prototype
#define SDA_DIR_IN   IOWR_ALTERA_AVALON_PIO_DIRECTION(I2C_SDAT_BASE,0)
#define SDA_DIR_OUT  IOWR_ALTERA_AVALON_PIO_DIRECTION(I2C_SDAT_BASE,1) 
#define SDA_HIGH     IOWR_ALTERA_AVALON_PIO_DATA(I2C_SDAT_BASE, 1)
#define SDA_LOW      IOWR_ALTERA_AVALON_PIO_DATA(I2C_SDAT_BASE, 0)
#define SDA_READ     IORD_ALTERA_AVALON_PIO_DATA(I2C_SDAT_BASE)
#define SCL_HIGH     IOWR_ALTERA_AVALON_PIO_DATA(I2C_SCLK_BASE, 1)
#define SCL_LOW      IOWR_ALTERA_AVALON_PIO_DATA(I2C_SCLK_BASE, 0)
#define SCL_DELAY    usleep(1)

void i2c_start(void);
void i2c_stop(void);
bool i2c_write(alt_u8 Data);
void i2c_read(alt_u8 *pData, bool bAck);

#define TV_I2C_ID       0x40
#define AUDIO_I2C_ID    0x34



/////////////////////////////////////////////////////////////////////////////////////////////
///////////// export function ///////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
bool I2C_TV_Read(alt_u8 ControlAddr, alt_u8 *pControlData){
    bool bSuccess = TRUE;
    alt_u8 DeviceAddr;
   
    // device id
    DeviceAddr = TV_I2C_ID;

    i2c_start();
    if (!i2c_write(DeviceAddr)){  // send ID
        bSuccess = FALSE;
        I2C_DEBUG(("I2C TV Fail: Address NACK!\n"));
    }
    if (bSuccess && !i2c_write(ControlAddr)){ // send sub-address
        bSuccess = FALSE;
        I2C_DEBUG(("I2C TV Fail: SubAddress NACK!\n"));
    }            
    i2c_start();  // restart
    DeviceAddr |= 1; // Read
    if (bSuccess && !i2c_write(DeviceAddr)){  // send id
        bSuccess = FALSE;
        I2C_DEBUG(("I2C TV Fail: Address+1 NACK!\n"));
    }
    
    if (bSuccess){
        i2c_read(pControlData, FALSE);  // read
    }        
    i2c_stop();
    
    return bSuccess;
}

bool I2C_TV_Write(alt_u8 ControlAddr, alt_u8 ControlData){
    bool bSuccess = TRUE;
    alt_u8 DeviceAddr;
    
    // device id
    DeviceAddr = TV_I2C_ID;

    i2c_start();
    if (!i2c_write(DeviceAddr)){  // send ID
        bSuccess = FALSE;
        I2C_DEBUG(("I2C TV Fail: Address NACK!\n"));
    }
    if (bSuccess && !i2c_write(ControlAddr)){ // send sub-address
        bSuccess = FALSE;
        I2C_DEBUG(("I2C TV Fail: SubAddress NACK!\n"));
    }            
    if (bSuccess && !i2c_write(ControlData)){  
        bSuccess = FALSE;
        I2C_DEBUG(("I2C TV Fail: write NACK!\n"));
    }
    i2c_stop();
    
    return bSuccess;
}


// Note. The interface of Audio chip is not fully I2C
// the control addd contain one data bits.
bool I2C_AUDIO_Write(alt_u8 ControlAddr, alt_u8 ControlData){
    bool bSuccess = TRUE;
    alt_u8 DeviceAddr;
    
    // device id
    DeviceAddr = AUDIO_I2C_ID;
   // WM8731/L is a write only device
    i2c_start();
    if (!i2c_write(DeviceAddr)){  // send ID
        bSuccess = FALSE;
        I2C_DEBUG(("I2C AUDIO Fail: Address NACK!\n"));
    }
    
    if (bSuccess && !i2c_write(ControlAddr)){ // send sub-address
        bSuccess = FALSE;
        I2C_DEBUG(("I2C AUDIO Fail: SubAddress NACK!\n"));
    }      
          
    if (bSuccess && !i2c_write(ControlData)){  
        bSuccess = FALSE;
        I2C_DEBUG(("I2C AUDIO Fail: Address+1 NACK!\n"));
    }
    
    i2c_stop();
    return bSuccess;
}


/////////////////////////////////////////////////////////////////////////////////////////////
///////////// Interncal function (i2cXXX) body //////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////



void i2c_start(void){
    // make sure it is in normal state
    SDA_DIR_OUT;  // data output enabled
    
    
    
    // start condition
    SDA_HIGH; // data high
    SCL_HIGH;
    SCL_DELAY;
     
    SDA_LOW; // data low
    SCL_DELAY; 
    SCL_LOW; // clock low
    SCL_DELAY;
}


void i2c_stop(void){
    // assume SCL = 0
    
    SDA_DIR_OUT;  // data output enabled
    SDA_LOW; // Data Low
    //SCL_DELAY; 
    SCL_HIGH;  // clock high
    SCL_DELAY; // clock high long delay
    SDA_HIGH; // data high
    SCL_DELAY; // data high delay
}

bool i2c_write(alt_u8 Data){ // return true if device response ack
    alt_u8 Mask = 0x80;
    bool bAck;
    int i;
    
    // assume, SCL = 0
    
    SDA_DIR_OUT;  // data write mode
    
    for(i=0;i<8;i++){
        // output data on bus
        if (Data & Mask){ // there is a delay in this command
            SDA_HIGH;
        }else{    
            SDA_LOW;
        }
        Mask >>= 1; // there is a delay in this command
        // clock high
        SCL_HIGH;
        SCL_DELAY;
        SCL_LOW;
        SCL_DELAY;
    }
    
    //===== get ack
    SDA_DIR_IN;  // data read mode
    //SCL_DELAY;
    // clock high
    SCL_HIGH;  // clock high
    SCL_DELAY;  // clock high delay
    bAck = SDA_READ?FALSE:TRUE;  // get ack
    //SCL_DELAY;
    //SDA_DIR_OUT;
    SCL_LOW; // clock low         
    SCL_DELAY; // clock low delay
    return bAck;
}    

void i2c_read(alt_u8 *pData, bool bAck){ // return true if device response ack
    alt_u8 Data=0;
    int i;
    
    // assume SCL = low
    
    SDA_DIR_IN;  // set data read mode
    SCL_LOW; // clock low
    SCL_DELAY; // clock low delay
    
    for(i=0;i<8;i++){
        Data <<= 1;
        SCL_HIGH;  // clock high
        SCL_DELAY;
        if (SDA_READ)  // read data 
            Data |= 0x01;
        SCL_LOW;  // clock log  
        SCL_DELAY;
    }
    
    // send ACK
    SDA_DIR_OUT;  // set data write mode
    if (bAck)
        SDA_LOW;
    else
        SDA_HIGH;
    SCL_HIGH; // clock high
    SCL_DELAY; // clock high  delay
    SCL_LOW; // clock low
    SCL_DELAY; // clock low delay
    SDA_LOW;  // data low
    SCL_DELAY; // data low delay
//    SDA_DIR_IN;  // set data read mode
    
    *pData = Data;
}

