#include <windows.h>
#include <s2440.h>
#include <nkintr.h>
#include <oalintr.h>
#include <p2.h>
#include "IIC_OV7620.h"

#define ERRORMSG(a,b)	RETAILMSG(1,b)	

//#define U8	unsigned char
//#define U16	unsigned short
//#define U32	unsigned int

#define CAM_ID			(0x42)

// GP9:SCL, GP8:SDA
#define SCL			(1<<14)
#define SDA			(1<<15)

#define SCL0			(s2440IOP->rGPEDAT &= ~SCL)
#define SCL1			(s2440IOP->rGPEDAT |=	SCL)
#define SDA0			(s2440IOP->rGPEDAT &= ~SDA)
#define SDA1			(s2440IOP->rGPEDAT |=	SDA)
//#define SDAOUT 		SDA
#define GetSDA			(s2440IOP->rGPEDAT)

static U8 _iicData[IICBUFSIZE];
static volatile int _iicDataCount;
static volatile int _iicStatus;
static volatile int _iicMode;
static int _iicPt;

const unsigned char Ov7620_YCbCr8bit_TV[][2] = {

	{0x12, 0x80},		// Camera Soft reset. Self cleared after reset.
	{0x00, 0x00},
	{0x01, 0x80},		// set blue gain
	{0x02, 0x80},		// set red gain
	{0x03, 0xb0},
	{0x06, 0x60},		// set brightness
	{0x0c, 0x24},		// set blue background
	{0x0d, 0x24},		// set red background
	{0x10, 0xff},		// set exposure time, brightness control
	{0x11, 0x80},		// set frame rate CLK_input = PCLK
	{0x12, 0x34},		// set 8 Bit YUV mode, enable AGC/AWB, mirror image.
	{0x13, 0x21}, 		 // 8bit Data, CCIR601 Format
	{0x15, 0x01},		// Use PCLK falling edge to latch data, 8 Bit UYVY....
	{0x16, 0x03},		//
	{0x17, 0x2f},		//
	{0x18, 0xcf},	       // (207-47)*4 = 640
	{0x19, 0x06},		//
	{0x1a, 0xf5},	       // ((244-5)+1)*2=480
	{0x1b, 0x00},
	{0x20, 0x00},
	{0x21, 0x80},
	{0x22, 0x80},
	{0x23, 0x00},
	{0x26, 0xa2},
	{0x27, 0xea},
	{0x29, 0x00},
	{0x2a, 0x00},
	{0x2b, 0x00},
	{0x2c, 0x88},
	{0x2e, 0x80},
	{0x2f, 0x44},
	{0x60, 0x27},
	{0x61, 0x82},
	{0x62, 0x5f},
	{0x63, 0xd5},
	{0x64, 0x57},
	{0x65, 0x83},
	{0x66, 0x55},
	{0x68, 0xcf},
	{0x69, 0x76},
	{0x6a, 0x22},
	{0x6b, 0x00},
	{0x6c, 0x08},
	{0x6d, 0x48},	
	{0x6e, 0x80},
	{0x6f, 0x0c},
	{0x70, 0x89},
	{0x71, 0x00},
	{0x72, 0x14},
	{0x73, 0x54},	
	{0x75, 0x0e},
	{0x76, 0x00},
	{0x77, 0xff},
	{0x78, 0x80},
	{0x79, 0x80},
	{0x7a, 0x80},
	{0x7b, 0xe6},
	{0x7c, 0x00},	
	{0x13, 0x21},
	{0x14, 0x94},	
	{0x24, 0x10},
	{0x25, 0x8a},
	{0x28, 0x20},	// Progressive mode.
	{0x2d, 0x95},	//
	{0x67, 0x92},	// 
	{0x74, 0x00},	// -CJH

	{0x12, 0x34}		// set 8 Bit YUV mode, enable AGC/AWB, mirror image.
};

// Change GP8(I2CSDA) as input/output mode.
#define	SET_SDA_INPUT	(s2440IOP->rGPECON &= ~(3<<30))
#define	SET_SDA_OUTPUT	(s2440IOP->rGPECON = (s2440IOP->rGPECON & ~(2<<30)) |(1<<30) )

volatile IOPreg *s2440IOP = (IOPreg *)IOP_BASE;
volatile IICreg *s2440IIC = (IICreg *)IIC_BASE;

extern void Camera_Initialize(void);

void Init_Sccb_Port(void);
void Camera_Initialize(void);
void Init_Camera_Module_YCbCr_VGA(void);
void setCIS(unsigned char bSubAddr, unsigned char bData);
void outSCCB(unsigned char *bpData, unsigned char bSize);
void Sccb_Start(void);
void loopn(U32 wCount);
void Sccb_Write_8bit(U8 Write_Byte);
void Sccb_Ack(void);
void Sccb_Stop(void);
void Delay(int time);
void SetCAMClockDivider(int divn);

void OV7620_WriteBlock(void);
void Wr_OV7620(U32 slvAddr, U32 addr, U8 data);
void Run_IicPoll(void);
void IicPoll(void);

void Init_Sccb_Port(void)
{
	// GP9:SCL, GP8:SDA, set all GPIO output.
	s2440IOP->rGPECON = (s2440IOP->rGPECON & ~(0xA<<28)) | (5<<28); //GPE 14, 15
}

void outSCCB(unsigned char *bpData, unsigned char bSize)
{
	int i;
	int j;
	unsigned char bTemp, bCombineMode;

	SET_SDA_OUTPUT;

	Sccb_Start();

	while(bSize--){
		Sccb_Write_8bit(*bpData);
		bpData++;
		Sccb_Ack();
		//SET_SDA_INPUT;
		//while(GetSDA & SDA); // check ACK
	} //while(bSize--)

	Sccb_Stop();

}

void setCIS(unsigned char bSubAddr, unsigned char bData)
{
	unsigned char bTemp[3];

	bTemp[0] = CAM_ID; // slave Address ( Omni)
	bTemp[1] = bSubAddr;
	bTemp[2] = bData;
//while(1)
	outSCCB(bTemp, 3);
}

void loopn(U32 wCount)
{
	int i, j;

	for (i=0; i<wCount; i++);
		for(j=0; j<1000; j++);		// 451Mhz
//		for(j=0; j<1500; j++);		// 531Mhz
}

void Sccb_Start(void)
{
	SET_SDA_OUTPUT;
// start
	SCL1;
	loopn(4);
	SDA1;
	loopn(8);
	SDA0;	//start point
	loopn(4);
}

void Sccb_Ack(void)
{
	SET_SDA_OUTPUT;
	SCL0;
	loopn(4);
	SDA1;
	loopn(4);
	SCL1;
	loopn(4);
}

void Sccb_Write_8bit(U8 Write_Byte)
{
	int i;

	SET_SDA_OUTPUT;
	for(i=0;i<8;i++){
		SCL0;
		loopn(4);
		if(Write_Byte & 0x80) SDA1;
		else SDA0;
		loopn(4);
		Write_Byte <<= 1;
		SCL1;
		loopn(4);
	} //for(i=0;i<8;i++){
}

void Sccb_Stop(void)
{
	// stop
	SET_SDA_OUTPUT;
	SCL0;
	loopn(4);
	SDA0;
	loopn(4);
	SCL1;
	loopn(4);
	SDA1;	// stop point
}

static int delayLoopCount = 100;

void Delay(int time)
{
    int i;
    
    for(;time>0;time--)
        for(i=0;i<delayLoopCount;i++);
}

void Init_Camera_Module_YCbCr_VGA(void)
{
	
	setCIS(0x12, 0x80);	// Camera Soft reset. Self cleared after reset.
	Delay(10);

	// From Omnivision...
	setCIS(0x00, 0x00);	// 

//	setCIS(0x01, 0x40);	// set blue gain
//	setCIS(0x02, 0x40);	// set red gain

	setCIS(0x01, 0x80);	// set blue gain
	setCIS(0x02, 0x80);	// set red gain
	setCIS(0x03, 0xb0);	// saturation control
	
	//setCIS(0x03, 0xb0);	// saturation control
	//setCIS(0x06, 0x80);	// set brightness
	setCIS(0x06, 0x60);	// set brightness - CJH
	//setCIS(0x07, 0x00);	// Sharpness control : Threshold-0, Magnitude-0
	
	setCIS(0x0c, 0x24);	// set white balance blue background
	setCIS(0x0d, 0x24);	// set white balance red background
	//setCIS(0x10, 0xb0);	// set auto exposure time, brightness control
	setCIS(0x10, 0xff);	// set auto exposure time, brightness control - CJH
	setCIS(0x11, (1<<7)+(0<<6)+(0x0)); // HSYNC positive, CHSYNC negative, VSYNC posigive
					// 16 Bit : PCLK = CLK_in/((1+x)*2)
					//  8 Bit : PCLK = CLK_in/(1+x)

	setCIS(0x12, 0x34);	// mirror image, enable AGC/AWB

				// precise A/D black level compensation
	setCIS(0x13, (0x1<<5)+(0x0<<4)+(0x1));  // 8bit Data, CCIR601 Format
	setCIS(0x15, 0x01);	// Use PCLK rising edge to latch data
				// UV data output: 16 Bit - UVUV..., 8 Bit - UYVY...
	setCIS(0x16, 0x03);	// Field mode : 00-OFF mode, 01-ODD mode, 10-EVEN mode, 11-FRAME mode 

	//setCIS(0x17, CAM_STX);		// 
	//setCIS(0x18, CAM_ENDX);		// (207-47)*4 = 640
	//setCIS(0x19, CAM_STY);		// 
	//setCIS(0x1a, CAM_ENDY);		// ((244-5)+1)*2=480 

	setCIS(0x17, 0x2f);
	setCIS(0x18, 0xcf);	
	setCIS(0x19, 0x06);
	setCIS(0x1a, 0xf5);


	setCIS(0x1b, 0x00);	// Pixel Shift
//	setCIS(0x1b, 0x00);	// Pixel Shift - 0
	setCIS(0x20, 0x00);	// limit vertical size to 480, second stage aperture correction enable
				// AWB smart mode disable, AWB is slow mode
				
	setCIS(0x21, 0x80);	// Y Channel Offset Adjustment - 0 , direction - Subtract
	setCIS(0x22, 0x80);	// U Channel Offset Adjustment - 0
	setCIS(0x23, 0x00);	// Crystal Current control : maximum current
	
	setCIS(0x26, 0xa2);	// Digital sharpness threshold, magnitude.
	setCIS(0x27, 0xea);	// Disable CCIR rang clip
	//setCIS(0x27, 0xe0);	// Disable CCIR rang clip - CJH
	setCIS(0x29, 0x00);	// 
	//setCIS(0x2a, 0x10);	// frame rate high, 60Hz, 50Hz:0x80, UV delay 2 pixel.
	setCIS(0x2a, 0x00);	// frame rate high, 60Hz, 50Hz:0x80, UV delay 2 pixel. - CJH
	setCIS(0x2b, 0x00);	// frame rate low, 60Hz, 50Hz:0xac
	setCIS(0x2c, 0x88);	// 
	setCIS(0x2e, 0x80);	// 
	setCIS(0x2f, 0x44);	// 
	setCIS(0x60, 0x27);	// 
	setCIS(0x61, 0x82);	// 02?x.........................................
	setCIS(0x62, 0x5f);	// 
	//setCIS(0x63, 0xcc);	// 
	setCIS(0x63, 0xd5);	// 
	setCIS(0x64, 0x57);	// Enable Y Gamma
	setCIS(0x65, 0x83);	// 
	setCIS(0x66, 0x55);	// 

	setCIS(0x68, 0xcf);	// 
	//setCIS(0x68, 0xca);	// -CJH
	setCIS(0x69, 0x76);	// 
	setCIS(0x6a, 0x22);	// 
	setCIS(0x6b, 0x00);	// 
	setCIS(0x6c, 0x08);	// 
	//setCIS(0x6d, 0x44);	// 
	setCIS(0x6d, 0x48);	// 
	setCIS(0x6e, 0x80);	// 
	//setCIS(0x6f, 0x0d);	// 
	setCIS(0x6f, 0x0c);	// -CJH
	//setCIS(0x70, 0x8b);	// 
	setCIS(0x70, 0x89);	// -CJH
	setCIS(0x71, 0x00);	//  freerun PCLK
	setCIS(0x72, 0x14);	// 
	setCIS(0x73, 0x54);	// 
	
	//setCIS(0x75, 0x8e);	// 
	setCIS(0x75, 0x0e);	// -CJH
	setCIS(0x76, 0x00);	// 
	setCIS(0x77, 0xff);	// 
	setCIS(0x78, 0x80);	// 
	setCIS(0x79, 0x80);	// 
	setCIS(0x7a, 0x80);	// 
	//setCIS(0x7b, 0xe2);	// 
	setCIS(0x7b, 0xe6);	// -CJH
	setCIS(0x7c, 0x00);	// 

	setCIS(0x13, 0x21);	// YUV 8-bit format, Enable AEC/AGC/AWB 
	//setCIS(0x14, 0x04);	// turn off GAMMA
	setCIS(0x14, 0x94);	// turn off GAMMA - CJH
	//setCIS(0x24, 0x3a);	// 
	setCIS(0x24, 0x10);	// -CJH
	//setCIS(0x25, 0x60);	// 
	setCIS(0x25, 0x8a);	// -CJH
	setCIS(0x28, 0x20);	// Progressive mode.
	setCIS(0x2d, 0x95);	//
	setCIS(0x67, 0x92);	// 
	//setCIS(0x74, 0x00);	// 	
	setCIS(0x74, 0x00);	// -CJH

	setCIS(0x12, 0x34);	// mirror image, enable AGC/AWB
}

void Camera_Initialize(void)
{
	// Use GPIO as IIC
/*	
	RETAILMSG(1,(TEXT("Camera_Initialize\n")));
	SetCAMClockDivider(1);
	Init_Sccb_Port(); //Initializing GPIO for Serial Camera Control Bus
	Init_Camera_Module_YCbCr_VGA(); //Initializing Camera Processor.  Using SCCB interface	
*/	
	SetCAMClockDivider(1);		// for 24M Camera, set to 1 if it's 24M camera
	RETAILMSG(1,(TEXT("Use IIC for initialization\n")));
	// Use IIC for initialization
	OV7620_WriteBlock();
}

void SetCAMClockDivider(int divn) // divn is even number 0~15
{
	volatile CLKPWRreg	*s2440PWR = (CLKPWRreg *)CLKPWR_BASE;
	
//	s2440PWR->rCLKDIVN |= (1<<3); // UCLK 48MHz setting
	s2440PWR->rCAMDIVN = (s2440PWR->rCAMDIVN & ~(0xf))|(0x10)|(divn); // CAMCLK is divided..
//	RETAILMSG(1,(TEXT("s2440PWR->rCLKCON:0x%x\r\n"),s2440PWR->rCLKCON));
}

void OV7620_WriteBlock(void)
{
    unsigned int i, j, save_E, save_PE, RegAddr, RegData;
    static U8 rdata[256]; 
	
      	
    save_E   = s2440IOP->rGPECON;
    save_PE  = s2440IOP->rGPEUP;

    s2440IOP->rGPEUP  &= ~0xc000;                  	//Pull-up disable
    s2440IOP->rGPEUP  |=  0xc000;                  	//Pull-up disable
    s2440IOP->rGPECON &= ~(0xF<<28);                //GPE15:IICSDA , GPE14:IICSCL    
    s2440IOP->rGPECON |=  (0xa<<28);                //GPE15:IICSDA , GPE14:IICSCL    

    //Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt, Transmit clock value Tx clock=IICCLK/16
    s2440IIC->rIICCON  = (1<<7) | (0<<6) | (1<<5) | (0xf);

    s2440IIC->rIICADD  = 0x10;                    	//2440 slave address = [7:1]. this is avialable when 240 is slave.
    s2440IIC->rIICSTAT = 0x10;                    	//IIC bus data output enable(Rx/Tx)
    s2440IIC->rIICLC = (1<<2)|(3);  				// Filter enable, 15 clocks SDA output delay     added into 2440

	
    
    for(i=0; i<(sizeof(Ov7620_YCbCr8bit_TV)/2); i++){
    	RETAILMSG(1,(TEXT("number:%d\r\n"),i));	
		Wr_OV7620(SlaveID, Ov7620_YCbCr8bit_TV[i][0], Ov7620_YCbCr8bit_TV[i][1]);
	}
	
    RETAILMSG(1,(TEXT("Block TX Ended...\r\n")));	

    s2440IOP->rGPEUP  = save_PE;
    s2440IOP->rGPECON = save_E;
}

void Wr_OV7620(U32 slvAddr, U32 addr, U8 data)
{
    _iicMode      = WRDATA;
    _iicPt        = 0;
    _iicData[0]   = (U8)addr;
    _iicData[1]   = data;
    _iicDataCount = 2;
    // write slave address
    s2440IIC->rIICDS	= slvAddr;  //0x42: OV7620 Slave ID 
    // After this time, timing is critical, because IIC start.
    s2440IIC->rIICSTAT	= 0xf0; 	//Start Master TX Condition    
    s2440IIC->rIICCON	= 0xaf;   	//Clearing the pending bit isn't needed because the pending bit has been cleared.

    while(_iicDataCount!=-1)
       Run_IicPoll();
    
}

void Run_IicPoll(void)
{
    if(s2440IIC->rIICCON & 0x10)                  //Tx/Rx Interrupt Enable
       	IicPoll();
}       

void IicPoll(void)
{
    U32 iicSt,i;
/*    
    iicSt = s2440IIC->rIICSTAT; 
    if(iicSt & 0x8){}                   //When bus arbitration is failed.
    if(iicSt & 0x4){}                   //When a slave address is matched with IICADD
    if(iicSt & 0x2){}                   //When a slave address is 0000000b
    if(iicSt & 0x1){}                   //When ACK isn't received
*/
    switch(_iicMode)
    {
        case RDDATA:
            if((_iicDataCount--)==0)
            {
                _iicData[_iicPt++] = s2440IIC->rIICDS;
            
                s2440IIC->rIICSTAT = 0x90;      //Stop MasRx condition 
                s2440IIC->rIICCON  = 0xaf;      //Resumes IIC operation.
                Delay(1);                 //Wait until stop condtion is in effect., Too long time... 
                                                //The pending bit will not be set after issuing stop condition.
                break;    
            }      
            _iicData[_iicPt++] = s2440IIC->rIICDS;     //The last data has to be read with no ack.

            if((_iicDataCount)==0)
                s2440IIC->rIICCON = 0x2f;                 //Resumes IIC operation with NOACK in case of OV7620 Cameara  
            else 
                s2440IIC->rIICCON = 0xaf;                 //Resumes IIC operation with ACK
            break;

        case WRDATA:
            
            if((_iicDataCount--)==0)
            {
                s2440IIC->rIICSTAT = 0xd0;                //stop MasTx condition 
                s2440IIC->rIICCON  = 0xaf;                //resumes IIC operation.
                Delay(10);                       			// we should adjust this time.
                //The pending bit will not be set after issuing stop condition.
                break;    
            }
            
            s2440IIC->rIICDS = _iicData[_iicPt++];        	//_iicData[0] has dummy.
            
//            for(i=0;i<20;i++);                  			//for setup time until rising edge of IICSCL. we have to adjust this time.
			RETAILMSG(1,(TEXT("B")));	
            
            s2440IIC->rIICCON = 0xaf;                    	 //resumes IIC operation.
            
            break;

        case SETRDADDR:
 	        RETAILMSG(1,(TEXT("[S%d]",_iicDataCount)));
            if((_iicDataCount--)==0)
            {
                  s2440IIC->rIICSTAT = 0xd0;                //stop MasTx condition 
    		    s2440IIC->rIICCON  = 0xaf;                //resumes IIC operation.
                  Delay(1);      //wait until stop condtion is in effect.

                  break;                  //IIC operation is stopped because of IICCON[4]    
            }

            s2440IIC->rIICDS = _iicData[_iicPt++];
            for(i=0;i<10;i++);          //for setup time until rising edge of IICSCL
            s2440IIC->rIICCON = 0xaf;             //resumes IIC operation.
            break;

        default:
            break;      
    }
}


