/*********************************************************************************

Copyright(c) 2007 Analog Devices, Inc. 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.  

Description:
        Contains functions to initialize and terminate the LCD attached to the BF548
        EZ-KIT Lite.
				
*********************************************************************************/

/*****************************************************************************\

  Includes:

\*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include <services/services.h>                  /* system service includes              */
#include <drivers/adi_dev.h>                    /* device manager includes              */

#include "adi_ssl_init.h"                       /* DMA and Device Manager Handles       */
#include <SDK-ezkitutilities.h>


#include <drivers/lcd/varitronix/adi_t350mcqb01.h>	/* Varitronix LCD driver includes	*/

#include "mediaplayer.h" 
#include "ada_gfx/gfx.h"
#include <adi_id3_parser.h>

/*****************************************************************************\

  Defines:

\*****************************************************************************/

/*****************************************************************************\

  Declarations:

\*****************************************************************************/
#pragma align 4
section ("mpp_nocache") u8 PingFrame[LCD_WIDTH * LCD_DATA_PER_PIXEL * LCD_HEIGHT];
//#pragma align 4
//section ("mpp_nocache") u8 PongFrame[LCD_WIDTH * LCD_DATA_PER_PIXEL * LCD_HEIGHT];

/* pointer to the currently displaying frame */
u8	*current_frame;

/* where new frames get built before being displayed */
u8	*build_frame;

/* 'semaphore' to switch current_frame to build_frame */
volatile int switch_frames;


/* Create two buffer chains */

ADI_DEV_2D_BUFFER PingBuffer[LCD_NUM_FRAMES];
//ADI_DEV_2D_BUFFER PongBuffer[LCD_NUM_FRAMES];

/* handle to the LCD driver */
ADI_DEV_DEVICE_HANDLE    LcdDriverHandle;

/*table of PPI driver configuration values */
ADI_DEV_CMD_VALUE_PAIR ConfigurationTable [] = {		
	{ ADI_T350MCQB01_CMD_SET_PPI_DEV_NUMBER, 	(void *)0								},
	{ ADI_T350MCQB01_CMD_SET_OPEN_PPI_DEVICE, 	(void *)TRUE							},
	{ ADI_DEV_CMD_SET_DATAFLOW_METHOD, 			(void *)ADI_DEV_MODE_CHAINED_LOOPBACK	},
	//{ ADI_DEV_CMD_SET_DATAFLOW_METHOD, 			(void *)ADI_DEV_MODE_CHAINED			},
	{ ADI_DEV_CMD_END,							NULL									},
};

section("sdram0_bank2_stack") static OS_STK stack[THRD_STACKSIZE * sizeof(OS_STK)];


/*****************************************************************************\

  Externs:

\*****************************************************************************/

extern OS_EVENT *lcd_Q;
// RKA extern void* lcd_arr[SZ_MSGQ_APP_CMD];
extern void* lcd_arr[100];
extern OS_EVENT *lcd_event;


/*****************************************************************************\

  Function Prototypes

\*****************************************************************************/

void lauch_thread_lcd(void);
void thread_lcd(void *pInParam);

int init_lcd_T350MCQB01(void);
int close_lcd_T350MCQB01(void);

static void switchLcdFrame(void);

static void callback_lcd(void *AppHandle, u32 Event, void *pArg);

/****************************************************************************
    Function:      init_lcd_LQ043T1DG01

    Description:   Opens a DCB queue for the LCD driver to use and then opens
                   the LCD device and underlying EPPI device. Configures both
                   devices appropriately, sets data buffers to 'black' and
                   starts the data flow to the LCD.

   Return value:   0 if successful, 1 otherwise.
*****************************************************************************/
int init_lcd_T350MCQB01()
{
	u32 Result= ADI_DEV_RESULT_SUCCESS, ResponseCount, i;
	
    /* open the LCD driver for video out */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
       	Result = adi_dev_Open(  	adi_dev_ManagerHandle,     		/* Dev manager Handle                   */
                   	            	&ADIT350MCQB01EntryPoint,       /* Device Entry point                   */
                                	0,                              /* Device number                        */
	                            	NULL,                           /* No client handle                     */
                                	&LcdDriverHandle,          		/* Location to store LCD driver handle  */
       	                        	ADI_DEV_DIRECTION_OUTBOUND,     /* Data Direction                       */
           	                    	adi_dma_ManagerHandle,         	/* Handle to DMA Manager                */
               	                	adi_dcb_QueueHandle,            /* Handle to callback manager           */
                   	            	callback_lcd);			        /* Callback Function                    */
    
                   	            	
		/* initialize pointers to current frame and build frame */
        current_frame = &PingFrame[0];
        //build_frame   = &PongFrame[0];

        switch_frames = 0;
    	
                   	            	
                   	            	

    /* FOR (Number of output 2D buffers) */
    for (i = 0; i < LCD_NUM_FRAMES; i++) 
    {
        PingBuffer[i].Data              = current_frame;
        PingBuffer[i].ElementWidth      = DMA_BUS_SIZE;
        /* 320 lines  * 3 bytes per pixel (RGB) */
        /* Divide by two because PPI is using 16-bit DMA*/
        PingBuffer[i].XCount            = ((LCD_WIDTH * LCD_DATA_PER_PIXEL)/DMA_BUS_SIZE);
        PingBuffer[i].XModify           = DMA_BUS_SIZE;
        PingBuffer[i].YCount            = LCD_HEIGHT +1; 		/* height + one blanking line		*/
        PingBuffer[i].YModify           = DMA_BUS_SIZE;
        PingBuffer[i].CallbackParameter = NULL;
        PingBuffer[i].pNext             = &PingBuffer[i + 1];   /* chain to next buffer in the list */
    }

    /* DMA generates Callback when last buffer in the chain is processed
       address of first buffer in the chain is passed as callback parameter */
    PingBuffer[LCD_NUM_FRAMES - 1].CallbackParameter = &PingBuffer[0];
    /* Terminate this buffer chain */
    //PingBuffer[LCD_NUM_FRAMES - 1].pNext = &PongBuffer[0];
    PingBuffer[LCD_NUM_FRAMES - 1].pNext = NULL;
 
    /* FOR (Number of output 2D buffers) */
   /* for (i = 0; i < LCD_NUM_FRAMES; i++) 
    {
        PongBuffer[i].Data              = current_frame;
        PongBuffer[i].ElementWidth      = DMA_BUS_SIZE;
        PongBuffer[i].XCount            = ((LCD_WIDTH * LCD_DATA_PER_PIXEL)/DMA_BUS_SIZE);
        PongBuffer[i].XModify           = DMA_BUS_SIZE;
        PongBuffer[i].YCount            = LCD_HEIGHT +1;
        PongBuffer[i].YModify           = DMA_BUS_SIZE;
        PongBuffer[i].CallbackParameter = NULL;
        PongBuffer[i].pNext             = &PongBuffer[i + 1];
    }

    PongBuffer[LCD_NUM_FRAMES - 1].CallbackParameter = &PongBuffer[0];
    PongBuffer[LCD_NUM_FRAMES - 1].pNext = NULL;
    */
 
     
	if (Result == ADI_DEV_RESULT_SUCCESS)               	            
		Result = adi_dev_Control(	LcdDriverHandle, 				/* Handle to callback manager           */
									ADI_DEV_CMD_TABLE, 				/* Command Table			            */
									ConfigurationTable);			/* configuration Table                  */
																		       								
    /* give the ping-buffers to the device */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
    	Result = adi_dev_Write(		LcdDriverHandle, 
    								ADI_DEV_2D, 
    								(ADI_DEV_BUFFER *)PingBuffer);
    								
    								
    /* give the pong-buffers to the device */
    /*if (Result == ADI_DEV_RESULT_SUCCESS) 
    	Result = adi_dev_Write(		LcdDriverHandle, 
    								ADI_DEV_2D, 
    								(ADI_DEV_BUFFER *)PongBuffer);*/
								
    								
    								   	 
  
    display_start_message(current_frame);								
    								
      								  								   
    /* Enable LCD video data flow */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
    	Result = adi_dev_Control(	LcdDriverHandle, 
    								ADI_DEV_CMD_SET_DATAFLOW, 
    								(void *)TRUE);	

    															

	/* launch LCD thread */
	launch_thread_lcd();
	
    return Result;
}



/*****************************************************************************

    Function:       callback_lcd

    Description:    Called at the end of each DMA buffer chain or if there
                    is an error detected by the device driver or DMA service.
                    Each type of callback event has its own unique ID
                    so we can use a single callback function for all
                    callback events.  The switch statement tells us
                    which event has occurred.

*****************************************************************************/
section ("callback_code")
static void callback_lcd(
    void    *AppHandle,
    u32     Event,
    void    *pArg
){

	
    ADI_DEV_2D_BUFFER* pBuffer;
    u32 Result,i;
    /* CASEOF (event type) */
    switch (Event)
    {
        /* CASE (buffer processed) */
        case ADI_DEV_EVENT_BUFFER_PROCESSED:
		/* if a new frame has been prepared then update its frame data  */
        /* into this DMA buffer chain's data area                     */
		pBuffer = (ADI_DEV_2D_BUFFER *)pArg;
		if (switch_frames > 0)
        {
		
    		for (i = 0; i < LCD_NUM_FRAMES; i += 1)
        	{
           		pBuffer[i].Data = current_frame;
           		((ADI_DMA_DESCRIPTOR_LARGE *)&pBuffer[i])->StartAddress = current_frame;

        	}
                /* update the 'semaphore'                                        */
                switch_frames -= 1;
        }

        break;

        /* CASE (DMA Error) */
        case ADI_DEV_EVENT_DMA_ERROR_INTERRUPT:
			if(APP_TRACE_LEVEL)
    		{
            	APP_TRACE("EPPI DMA error\n");
    		}
            break;

        default:
			if(APP_TRACE_LEVEL)
    		{
            	APP_TRACE("EPPI callback event not supported. Event code: 0x%08X\n",Event);
    		}
            break;
    }
	

}

 
 /*********************************************************************

	Function:	launch_thread_lcd																			
	
 	Parameters:	None 
																		
 	Return:		None														
																			
 	Description:	Launches the LCD thread
*********************************************************************/
section ("sdram0_bank2_thread")
void launch_thread_lcd(void) 
{
	/*OS_STK *stack;*/
	INT8U OS_result;
	INT32U i;

	
	/* Create LCD message Queue */
	lcd_Q = OSQCreate(lcd_arr, SZ_MSGQ_BUF);
	lcd_event = lcd_Q;
	
	
	
	if ((OS_result = OSTaskCreateExt(	thread_lcd, 
										(void *)lcd_event, 
										&stack[THRD_STACKSIZE -1], 
										THRD_PRIORITY_LCD, 
										THRD_PRIORITY_LCD, 
										&stack[0], 
										THRD_STACKSIZE , 
										(void *)0, 
										OS_TASK_OPT_STK_CHK )))
										
    	ezErrorCheck(1); /* wait for help */	

}// end of lauch_thread_lcds

/****************************************************************************
    Function:      thread_lcd

    Description:   This function contains the thread for the LCD device.
    			   The thread receives messages from the Display manager
    			   and updates the LCD accordingly	

   Return value:   
*****************************************************************************/
section ("sdram0_bank2_thread")

void thread_lcd(void *pInParam)
{
	
	int i;
	INT8U err;
	int Result;
	MPPMESSAGE	*pMessage;
	bool newdisplay;
	
	OS_EVENT *queue = lcd_event;

	
   while(1)
	{
		/* pend on message queue */
	    pMessage = (MPPMESSAGE *) OSQPend (queue, 0, &err);
		if (err && APP_TRACE_LEVEL)
		{					
    		APP_TRACE("thread_lcd OSQPend error %d\n",err); /* wait for help */
    		ezErrorCheck(1);

		}	
		
		newdisplay = true;
		
		switch(pMessage->uMID)
		{
			
				/* Updates the position of the file selection box */
				case MSGID_LCD_FILESELECT: 	
					/* Refresh File List Frame */										
					display_filelistframe(current_frame);								
					/* Update file selector    */
					display_fileselector(current_frame,pMessage->Param2, pMessage->Param1);	
					/* Update file listing      */				
					display_filelist(current_frame,pMessage->Param2, pMessage->Param1);
					

					break;
				
				/* Resets the LCD screen to the default state */				
				case MSGID_LCD_REFRESH_RESET:
					/* Refresh File List Frame */										
					display_filelistframe(current_frame);					
					/* Refresh File Tag Frame */										
					display_filetagframe(current_frame);					
					/* Refresh File Status Frame */										
					display_statusframe(current_frame);					
					/* Update file selector    */
					display_fileselector(current_frame,pMessage->Param2, pMessage->Param1);	
					/* Update file listing      */				
					display_filelist(current_frame,pMessage->Param2, pMessage->Param1);	

					
					break;
						
					
				/* Updates the LCD state to the previously displayed state 			*/
				/* Mainly used after a video JPEG/MPEG4 frame is finished playing 	*/
				/* and we want to re-display the file selection menu				*/
				case MSGID_LCD_REFRESH_RESUME:
					/* Refresh File List Frame */										
					display_filelistframe(current_frame);								
					/* Update file selector    */
					display_fileselector(current_frame,pMessage->Param2, pMessage->Param1);	
					/* Update file listing      */				
					display_filelist(current_frame,pMessage->Param2, pMessage->Param1);
					/* Output tag information */
				    display_filetag(current_frame, NULL, NULL_TYPE);		
				    /* Clear status information */
					display_statusinfo(current_frame, STATE_NONE);

					break;
				
				/* Used to update the file tagging information field if / when it 	*/
				/* available.  Not all audio files have tagging information			*/	
				case MSGID_LCD_TAG: 
					/* Output tag information */
				    display_filetag(current_frame, pMessage->Param2, pMessage->Param1);	
					break;
					
				/* Update the status field with PLAY / STOPPED / PAUSED etc states	*/
				case MSGID_LCD_STATUS:
					/* Output status information */
					display_statusinfo(current_frame, pMessage->Param1);	
					break;
				
				default:/* ignore all other commands */
					newdisplay = false;

					break;
			
		}/* end of switch */
		
		//if(newdisplay)
		//{
			/* update the LCD data frame */
		//	switchLcdFrame();
		//}
	}			
	
}

/****************************************************************************
    Function:      close_lcd

    Description:   This function closes the LCD device driver
    			 
    
    
   Return value:   
*****************************************************************************/ 
int close_lcd_T350MCQB01(void)
{
	
	u32 Result= ADI_DEV_RESULT_SUCCESS;
	
    if (Result == ADI_DEV_RESULT_SUCCESS)	
    	/* close the video out driver */
    	Result = adi_dev_Close(LcdDriverHandle);
    	
    return (Result);
}


/****************************************************************************
    Function:      switchLcdFrame

    Description:   switch over the build and currrent frame pointers 
    
   Return value:   
*****************************************************************************/ 

static void switchLcdFrame(void)
{
	
    u8 *p = current_frame;
                    

    u32 mask = cli();             /* no interrupts, please       */
    /* Protect this section of code - entering a critical region    */
    //pExitCriticalArg = adi_int_EnterCriticalRegion(pManager->pEnterCriticalArg);
    /* switch over the build and currrent frame pointers so that */
    /* the callback function will start display of new frame     */
    current_frame = build_frame;
    build_frame = p;
    switch_frames = 2;            /* prime the 'semaphore'       */
    /* Exit the critical region */
    //adi_int_ExitCriticalRegion(pExitCriticalArg);

    sti(mask);                    /* allow interrupts again      */
}

