/*****************************************************************************

Copyright (c) 2007 Analog Devices.  All Rights Reserved.

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.  
******************************************************************************

$File: FrameDisplay $
$Revision: 1.1 $
$Date: 2007/06/21 03:47:41 $
******************************************************************************

Modification History:
====================
******************************************************************************
  
Include files

*****************************************************************************/

#include <services/services.h>				// system service includes
#include <drivers/adi_dev.h>				// device manager includes
#include <drivers/encoder/adi_adv717x.h>	// ADV717x driver include
#include <drivers/twi/adi_twi.h>			// twi driver include
#include <adi_itu656.h>
#include <SDK-ezkitutilities.h>
#include <adi_ssl_Init.h>
#include <adi_itu656.h>

#include <stdio.h>

/*****************************************************************************

User configurations:

Callbacks can be either "live" meaning they happen at hardware interrupt
time, or "deferred" meaning that the Deferred Callback Service is used
to make callbacks at a lower priority interrupt level.  Deferred 
callbacks usually allow the system to process data more efficiently with
lower interrupt latencies.  

The macro USE_DEFERRED_CALLBACKS can be used to toggle between 
"live" and "deferred" callbacks.  All drivers and system services that 
make callbacks into an application are passed a handle to a callback service.  
If that handle is NULL, then live callbacks are used.  If that handle is non-NULL, 
meaning the handle is a real handle into a deferred callback service, 
then callbacks are deferred using the given callback service.  

The other user controlled macro is the USE_LOOPBACK macro.  When enabled
the example will setup the buffer chains to use the chained with loopback
mode rather than the chained mode.  When loopback is enabled, buffers
are continually reused and never have to be requeued.  When loopback
is disabled, buffers must be requeued by the application or else the 
driver will starve for data.  

*****************************************************************************/

//#define USE_DEFERRED_CALLBACKS    // enables deferred callbacks

//#define USE_LOOPBACK              // enabled chained with loopback

/****************
Macros
****************/
#define   PAL_FRAME    // Enable this macro to build PAL Frame. Disable for NTSC
/*****************************************************************************

Enumerations and defines

*****************************************************************************/

#if defined(PAL_FRAME)
#define NUM_FRAMES 		25            // color change rate = every 25 frames (for PAL)
#define	NUM_LINES		PAL_FRAME_LINES
#define	FRAME_DATA_LEN	PAL_DATA_PER_LINE
#else   // build for NTSC
#define NUM_FRAMES 		30            // color change rate = every 30 frames (for NTSC)
#define	NUM_LINES		NTSC_FRAME_LINES
#define	FRAME_DATA_LEN	NTSC_DATA_PER_LINE
#endif

/*****************************************************************************

Static data

*****************************************************************************/
// Create two ITU656 frame in SDRAM which will be sent out to the monitor
section("ITU656_Frame0") static char PingFrame[FRAME_DATA_LEN * NUM_LINES];
section("ITU656_Frame1") static char PongFrame[FRAME_DATA_LEN * NUM_LINES];

// list of colors in UYVY format
// total of 8 colors
static char blue[]      = {0xF0,0x29,0x6E,0x29};    // Blue pixel ycbcr format
static char red[]       = {0x5A,0x51,0xF0,0x51};    // Red pixel ycbcr format
static char megenta[]   = {0xCA,0x6A,0xDE,0x6A};    // Megenta pixel ycbcr format
static char green[]     = {0x36,0x91,0x22,0x91};    // Green pixel ycbcr format
static char cyan[]      = {0xA6,0xAA,0x10,0xAA};    // Cyan pixel ycbcr format
static char yellow[]    = {0x10,0xD2,0x92,0xD2};    // Yellow pixel ycbcr format
static char white[]     = {0x80,0xEB,0x80,0xEB};    // White pixel ycbcr format
static char black[]     = {0x80,0x10,0x80,0x10};    // Black pixel ycbcr format
    
// handle to the ADV717x driver
static ADI_DEV_DEVICE_HANDLE DriverHandle;
static ADI_DCB_HANDLE          DCBManagerHandle;
// storage for critical region
static ADI_INT_CRITICAL_REGION_DATA	CriticalRegionData;		

// Create two buffer chains.  One chain will be used for one of the frames,
// the other chain for the other frame.
ADI_DEV_2D_BUFFER PingBuffer[NUM_FRAMES];
ADI_DEV_2D_BUFFER PongBuffer[NUM_FRAMES];

// Globals
volatile u8	PingColor;	    // Ping frame color
volatile u8 PongColor;      // Pong frame color
FRAME_TYPE  Frame;          // ITU Frame type

// BF537 Ez-Kit Configuration Tables
ADI_EBIU_COMMAND_PAIR ebiu_config[] = 
{
	{ ADI_EBIU_CMD_SET_EZKIT,	(void*)ADI_EBIU_EZKIT_BF537 		},
	{ ADI_EBIU_CMD_END, 		0									}
};

ADI_PWR_COMMAND_PAIR pwr_table[] = 
{
	{ ADI_PWR_CMD_SET_EZKIT, 	(void *) ADI_PWR_EZKIT_BF537_600MHZ },
    { ADI_PWR_CMD_END,    		0 									} 
};


// Hardware TWI Configuration table for BF537 to access ADV7179 regs

// Run TWI clock at 100MHz & 50% Duty Cycle
adi_twi_bit_rate rate={100,50};

ADI_DEV_CMD_VALUE_PAIR TWIConfig[]={ 
     	{ ADI_TWI_CMD_SET_HARDWARE,         (void *)ADI_INT_TWI             },
     	{ ADI_DEV_CMD_SET_DATAFLOW_METHOD,  (void *)ADI_DEV_MODE_SEQ_CHAINED},
     	{ ADI_TWI_CMD_SET_FIFO,             (void *)0x0000                  },
     	{ ADI_TWI_CMD_SET_LOSTARB,          (void *)1                       },
	   	{ ADI_TWI_CMD_SET_RATE,				(void *)(&rate)					},     
     	{ ADI_TWI_CMD_SET_ANAK,             (void *)0                       },
     	{ ADI_TWI_CMD_SET_DNAK,             (void *)0                       },
     	{ ADI_DEV_CMD_SET_DATAFLOW,         (void *)TRUE                    },
     	{ ADI_DEV_CMD_END,                 	NULL                            }
};
 	
/*********************************************************************

	Function:		ExceptionHandler
					HWErrorHandler

	Description:	We should never get an exception or hardware error,
					but just in case we'll catch them and simply turn
					on all the LEDS should one ever occur.

*********************************************************************/
static ADI_INT_HANDLER(ExceptionHandler)	// exception handler
{
	// wait here until user fixes the issue
	printf ("Exception Error occured!\n");
	while(1);
}

static ADI_INT_HANDLER(HWErrorHandler)		// hardware error handler
{	
	// wait here until user fixes the issue
	printf ("Hardware Error occured!\n");
	while(1);
}

/*****************************************************************************

    Function:       Callback

    Description:    Each type of callback event has it's own unique ID 
                    so we can use a single callback function for all 
                    callback events.  The switch statement tells us 
                    which event has occurred.
                    
*****************************************************************************/
static void EncoderCallback(
    void 	*AppHandle,
    u32  	Event,
    void 	*pArg
){
    
    ADI_DEV_2D_BUFFER *pBuffer;            // pointer to the buffer that was processed
    u8  Color;    

    // Holds present result status
    u32 Result;
        
    // CASEOF (event type)
    switch (Event) {
        
        // CASE (buffer processed)
        case ADI_DEV_EVENT_BUFFER_PROCESSED:
            
            // when the buffer chain was created, the CallbackParameter value for the buffer
            // that was generating the callback was set to be the address of the first buffer
            // in the chain.  So here in the callback that value is passed in as the pArg
            // parameter.  
            pBuffer = (ADI_DEV_2D_BUFFER *)pArg;
            // if (callback generated by PingBuffer)
            if (pBuffer == &PingBuffer[0])
            {
                Color = PingColor;
                PingColor += 2;
                if (PingColor > 7)
                {
                    PingColor = 0;
                }
            }
            // else (callback is from PongFrame)
            else
            {
                Color = PongColor;
                PongColor += 2;
                if (PongColor > 7)
                {
                    PongColor = 1;                    
                }
            }
            // update databuffer with new color
            // CASEOF (Color)
            switch(Color)
            {
                case 0: // Fill frame with blue color
                    adi_video_FrameFill(pBuffer->Data,Frame,blue);
                    break;
                
                case 1: // Fill frame with red color
                    adi_video_FrameFill(pBuffer->Data,Frame,red);
                    break;
                
                case 2: // Fill frame with megenta color
                    adi_video_FrameFill(pBuffer->Data,Frame,megenta);
                    break;
                
                case 3: // Fill frame with green color
                    adi_video_FrameFill(pBuffer->Data,Frame,green);
                    break;
                
                case 4: // Fill frame with blue color
                    adi_video_FrameFill(pBuffer->Data,Frame,cyan);
                    break;
                
                case 5: // Fill frame with yellow color
                    adi_video_FrameFill(pBuffer->Data,Frame,yellow);
                    break;
                
                case 6: // Fill frame with white color
                    adi_video_FrameFill(pBuffer->Data,Frame,white);
                    break;
                
                default: // Fill frame with black color
                    adi_video_FrameFill(pBuffer->Data,Frame,black);
                    break;
            }
            // if loopback is enabled, the driver will continually reuse the buffers.
            // if loopback is disabled though, we need to requeue the buffer chain that just finished
#if defined(USE_LOOPBACK)
#else
            adi_dev_Write(DriverHandle, ADI_DEV_2D, pArg);

#endif            
				
            break;

        // CASE DMA Error
        case ADI_DEV_EVENT_DMA_ERROR_INTERRUPT:         
            printf("DMA error\n");
            break;
                      
		default:
			printf("Callback event not supported. Event code: 0x%x \n",Event);
			break;
                
    // ENDCASE
    }
    
    // return
}

/*********************************************************************
*
*   Function:   main
*
*********************************************************************/
void main	(void) 
{    
    u32                     ResponseCount;          // response counter
    int i;		
    ADI_DEV_ACCESS_REGISTER ADV717x_Cfg;
    ADI_DEV_ACCESS_REGISTER_FIELD FieldVal;
    u32 Result;


#if defined(PAL_FRAME)
	Frame = PAL_IL;
#else   
	Frame = NTSC_IL;
#endif	

	ezInit(1);
	
	adi_ssl_Init();

	if(adi_int_CECHook(3, ExceptionHandler, NULL, FALSE)!=0)
		printf("adi_int_CECHook 3 returned an error\n");		
	if(adi_int_CECHook(5, HWErrorHandler, NULL, FALSE)!=0)
		printf("adi_int_CECHook 5 returned an error\n");	
    
// if DCB selected, initialize the Deferred Callback Manager and setup a queue
#if defined(USE_DEFERRED_CALLBACKS)
	// init Deffered callback manager
	if(adi_dcb_Open(14, &DCBMgrData[ADI_DCB_QUEUE_SIZE], (ADI_DCB_ENTRY_SIZE)*4, &ResponseCount, &DCBManagerHandle)!=0)
		printf("adi_dcb_Open failed\n");		
#else	// else, live callback
    DCBManagerHandle = NULL;
#endif

    
    // open the AD7179 driver for output
    if(adi_dev_Open(	  adi_dev_ManagerHandle,            // Dev manager Handle
                 		  &ADIADV7179EntryPoint,          // Device Entry point
                          0,                              // Device number
	                      NULL,                           // No client handle
    	                  &DriverHandle,                  // Device manager handle address
        	              ADI_DEV_DIRECTION_OUTBOUND,     // Data Direction
            	          adi_dma_ManagerHandle,               // Handle to DMA Manager
                	      DCBManagerHandle,               // Handle to callback manager
                    	  EncoderCallback)!= ADI_DEV_RESULT_SUCCESS)

     	printf("adi_dev_Open failed\n");

	// Send TWI Configuration table
	 if(adi_dev_Control(DriverHandle,ADI_ADV717x_CMD_SET_TWI_CONFIG_TABLE,(void*)TWIConfig)!= ADI_DEV_RESULT_SUCCESS)	 	
	 		printf("adi_dev_Control failed to set TWI Configuration Table\n");
     
	/**************** PPI Configuration Settings ****************/	
	
	// Set PPI Device number
	if(adi_dev_Control( DriverHandle, ADI_ADV717x_CMD_SET_PPI_DEVICE_NUMBER, (void*)0 )!= ADI_DEV_RESULT_SUCCESS)
		printf("adi_dev_Control failed to set PPI Device Number\n");
		
/**********************************
ADV717x Register Configuration
**********************************/

	//printf ("Configuring ADV717x...\n");

	// ADV717x register address to switch btwn NTSC & PAL    
    ADV717x_Cfg.Address = ADV717x_MR0;
    
#if defined(PAL_FRAME)	// Configure ADV717x for PAL video out

	// MR0 value to set ADV717x in PAL mode
	ADV717x_Cfg.Data = 0x05;
    if((Result = adi_dev_Control(DriverHandle, ADI_ADV717x_CMD_SET_SCF_REG, (void *) ADV717x_SCF_VALUE_PAL_BI))!= ADI_DEV_RESULT_SUCCESS)
    	printf("adi_dev_Control failed to configure ADV717x SCF Regs: 0x%x\n",Result);
#else					// Configure ADV717x for NTSC video out

	// MR0 value to set ADV717x in NTSC mode
	ADV717x_Cfg.Data = 0x00;
    if((Result = adi_dev_Control(DriverHandle, ADI_ADV717x_CMD_SET_SCF_REG, (void *) ADV717x_SCF_VALUE_NTSC))!= ADI_DEV_RESULT_SUCCESS)
		printf("adi_dev_Control failed to configure ADV717x SCF Regs: 0x%x\n",Result);
#endif 

	// configure ADV717x in selected mode
    if((Result = adi_dev_Control(DriverHandle, ADI_DEV_CMD_REGISTER_WRITE, (void *)&ADV717x_Cfg))!= ADI_DEV_RESULT_SUCCESS)  
		printf("adi_dev_Control failed to configure ADV717x operating mode: 0x%x\n",Result);
		
	FieldVal.Address 	= ADV717x_SCPR;
	FieldVal.Field 		= ADV717x_OUT_VIDEO;
	FieldVal.Data 		= 2;
		
	// configure ADV717x in selected mode
    if(adi_dev_Control(DriverHandle, ADI_DEV_CMD_REGISTER_FIELD_WRITE, (void *)&FieldVal)!= ADI_DEV_RESULT_SUCCESS)  
		printf("adi_dev_Control failed to configure ADV717x operating mode\n");

/******************
Prepare Video Frame
*******************/
// initialize the two video frames and make them both different colors
adi_video_FrameFormat(PingFrame,Frame);
adi_video_FrameFormat(PongFrame,Frame);

// Fill the frame with specified color
adi_video_FrameFill(PingFrame,Frame,blue);
adi_video_FrameFill(PongFrame,Frame,red);

PingColor = 2;
PongColor = 3;

/*********************************
Buffer preparation 
*********************************/

/*
create a buffer chain that points to the PingFrame.  Each buffer points to the same PingFrame
so the PingFrame will be displayed NUM_FRAMES times.  NUM_FRAMES is sized to 
keep the display busy for 1 second.  Place a callback on only the last buffer
in the chain.  Make the CallbackParameter (the value that gets passed to the callback
function as the pArg parameter) point to the first buffer in the chain.  This way, when
the callback goes off, the callback function can requeue the whole chain if the loopback
mode is off.
*/

#if defined(PAL_FRAME)

    for (i = 0; i < NUM_FRAMES; i++) {
        PingBuffer[i].Data = PingFrame;
        PingBuffer[i].ElementWidth = 2;
        PingBuffer[i].XCount = (FRAME_DATA_LEN/2);         // for PAL
        PingBuffer[i].XModify = 2;
        PingBuffer[i].YCount = NUM_LINES;         // for PAL
        PingBuffer[i].YModify = 2;
        PingBuffer[i].CallbackParameter = NULL;
        PingBuffer[i].pNext = &PingBuffer[i + 1];
    }
    PingBuffer[NUM_FRAMES - 1].CallbackParameter = &PingBuffer[0];
    PingBuffer[NUM_FRAMES - 1].pNext = NULL;
    
    // now do the same for the Pong buffers
    for (i = 0; i < NUM_FRAMES; i++) {
        PongBuffer[i].Data = PongFrame;
        PongBuffer[i].ElementWidth = 2;
        PongBuffer[i].XCount = (FRAME_DATA_LEN/2);         // for PAL
        PongBuffer[i].XModify = 2;
        PongBuffer[i].YCount = NUM_LINES;         // for PAL
        PongBuffer[i].YModify = 2;
        PongBuffer[i].CallbackParameter = NULL;
        PongBuffer[i].pNext = &PongBuffer[i + 1];
    }
    PongBuffer[NUM_FRAMES - 1].CallbackParameter = &PongBuffer[0];
    PongBuffer[NUM_FRAMES - 1].pNext = NULL;

#else   // select NTSC frame type

    for (i = 0; i < NUM_FRAMES; i++) {
        PingBuffer[i].Data = PingFrame;
        PingBuffer[i].ElementWidth = 2;
        PingBuffer[i].XCount = (FRAME_DATA_LEN/2); // for NTSC
        PingBuffer[i].XModify = 2;
        PingBuffer[i].YCount = NUM_LINES;         // for NTSC
        PingBuffer[i].YModify = 2;
        PingBuffer[i].CallbackParameter = NULL;
        PingBuffer[i].pNext = &PingBuffer[i + 1];
    }
    PingBuffer[NUM_FRAMES - 1].CallbackParameter = &PingBuffer[0];
    PingBuffer[NUM_FRAMES - 1].pNext = NULL;
    
    // now do the same for the Pong buffers
    for (i = 0; i < NUM_FRAMES; i++) {
        PongBuffer[i].Data = PongFrame;
        PongBuffer[i].ElementWidth = 2;
        PongBuffer[i].XCount = (FRAME_DATA_LEN/2);         // for NTSC
        PongBuffer[i].XModify = 2;
        PongBuffer[i].YCount = NUM_LINES;         // for NTSC
        PongBuffer[i].YModify = 2;
        PongBuffer[i].CallbackParameter = NULL;
        PongBuffer[i].pNext = &PongBuffer[i + 1];
    }
    PongBuffer[NUM_FRAMES - 1].CallbackParameter = &PongBuffer[0];
    PongBuffer[NUM_FRAMES - 1].pNext = NULL;
#endif

// Set Dataflow method
#if defined(USE_LOOPBACK)
    if(adi_dev_Control(DriverHandle, ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_CHAINED_LOOPBACK )!= ADI_DEV_RESULT_SUCCESS)
#else
    if(adi_dev_Control(DriverHandle, ADI_DEV_CMD_SET_DATAFLOW_METHOD, (void *)ADI_DEV_MODE_CHAINED )!= ADI_DEV_RESULT_SUCCESS)
#endif    
        printf("adi_dev_Control failed to set dataflow method\n");

    /* Enable streaming */
    if(adi_dev_Control(DriverHandle, ADI_DEV_CMD_SET_STREAMING, (void *)TRUE)!= ADI_DEV_RESULT_SUCCESS)
    {
		printf("adi_dev_Control failed to enable streaming\n");
    }
    
    // give the ping-pong buffers to the device
    if(adi_dev_Write(DriverHandle, ADI_DEV_2D, (ADI_DEV_BUFFER *)PingBuffer)!= ADI_DEV_RESULT_SUCCESS)
    	printf("adi_dev_Control failed to submit PingBuffer\n");

    if(adi_dev_Write(DriverHandle, ADI_DEV_2D, (ADI_DEV_BUFFER *)PongBuffer)!= ADI_DEV_RESULT_SUCCESS)
    	printf("adi_dev_Control failed to submit PongBuffer\n");
    	    	
	//printf ("Enabling Video dataflow...\n");
    
    // enable data flow
    if(adi_dev_Control(DriverHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)TRUE)!= ADI_DEV_RESULT_SUCCESS)
		printf("adi_dev_Control failed to enable dataflow\n");

    // continue displaying frames
    while (1);

}

/*****/
