/*********************************************************************************

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 <drivers/lcd/sharp/adi_lq043t1dg01.h>  /* SHARP LQ043T1DG01 LCD driver include */
#include <drivers/eppi/adi_eppi.h>              /* EPPI driver include                  */
#include "adi_ssl_init.h"                       /* DMA and Device Manager Handles       */
#include <SDK-ezkitutilities.h>
#include "lcd_nl6448.h"
#include "mediaplayer.h" 
#include "devicecontrol.h" 
#include "ada_gfx/gfx.h"
#include <adi_id3_parser.h>

/*****************************************************************************\

  Defines:

\*****************************************************************************/
/* Processor DMA bus size = 4 bytes */
//#define	    DMA_BUS_SIZE			4
/*SHARP LQ043T1DG01 LCD is connected to EPPI 0*/
#define     EPPI_DEV_NUMBER         0

/* Processor Timer ID used to generate LCD DISP signal  */
#define     LCD_DISP_TIMER_ID       ADI_TMR_GP_TIMER_3
/* Processor Flag ID connected to LCD DISP signal       */
#define     LCD_DISP_FLAG_ID        ADI_FLAG_PE3


/*****************************************************************************\

  Declarations:

\*****************************************************************************/
#pragma align 4
section ("mpp_nocache") u8 PingFrame[LCD_WIDTH * LCD_DATA_PER_PIXEL * LCD_HEIGHT];

/* handle to the LCD driver */
ADI_DEV_DEVICE_HANDLE    LcdDriverHandle;

/* Create two buffer chains */
#pragma align 4
section ("mpp_nocache") ADI_DEV_2D_BUFFER PingBuffer[LCD_NUM_FRAMES];
                        
section("sdram0_bank2_stack") static OS_STK stack[THRD_STACKSIZE * sizeof(OS_STK)];


/*****************************************************************************\

  Externs:

\*****************************************************************************/
extern OS_EVENT *lcd_Q;
extern void* lcd_arr[SZ_MSGQ_APP_CMD];
extern OS_EVENT *lcd_event;


/*****************************************************************************\

  Function Prototypes

\*****************************************************************************/

void lauch_thread_lcd(void);
void thread_lcd(void *pInParam);

int init_lcd_LQ043T1DG01(void);
int close_lcd_LQ043T1DG01(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_LQ043T1DG01()
{
    u32 Result= ADI_DEV_RESULT_SUCCESS, ResponseCount;
    int i, k;
    
    ADI_LQ043T1DG01_TIMER_FLAG  Disp;   /* to hold LCD disp flag details */
    
    /************** Open Sharp LQ043T1DG01 LCD device ***************/
    
    /* open the LCD driver for video out */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
       	Result = adi_dev_Open(  adi_dev_ManagerHandle,     		/* Dev manager Handle                   */
                   	            &ADILQ043T1DG01EntryPoint,      /* 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                    */
		            
       

    /**************** Sharp LQ043T1DG01 LCD driver Configuration ****************/
    
    Disp.DispTimerId = LCD_DISP_TIMER_ID;   /* Timer ID to be used to generate DISP signal requirements */
    Disp.DispFlagId  = LCD_DISP_FLAG_ID;    /* Flag port to with LCD DISP pin is connected to           */
    
	/* Set DISP Timer & Flag ID */
	if (Result == ADI_DEV_RESULT_SUCCESS) 
		Result = adi_dev_Control(	LcdDriverHandle, 
									ADI_LQ043T1DG01_CMD_SET_DISP_TIMER_FLAG, 
									(void*)&Disp );
    
	/* Set EPPI Device number to be used for LCD video out */
	if (Result == ADI_DEV_RESULT_SUCCESS) 
		Result = adi_dev_Control(	LcdDriverHandle, 
									ADI_LQ043T1DG01_CMD_SET_EPPI_DEV_NUMBER, 
									(void*)EPPI_DEV_NUMBER );
    
    /* Open EPPI Device for LCD out */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
		Result = adi_dev_Control( 	LcdDriverHandle, 
									ADI_LQ043T1DG01_CMD_SET_OPEN_EPPI_DEVICE, 
									(void*)TRUE );
	
    /* Set DMA streaming mode so that data flow to LCD is not delayed
    during calls to our callback function                            */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
    	Result = adi_dev_Control(	LcdDriverHandle,
                     				ADI_DEV_CMD_SET_STREAMING,
                     				(void*)TRUE );

	/**************** EPPI Configuration ****************/

	/* EPPI Control register configuration value for RGB out
        - EPPI as Output
    	  GP 2 frame sync mode, 
    	  Internal Clock generation disabled, Internal FS generation enabled,
          Receives samples on EPPI_CLK raising edge, Transmits samples on EPPI_CLK falling edge,
      	  FS1 & FS2 are active high, 
      	  DLEN = 6 (24 bits for RGB888 out) or 5 (18 bits for RGB666 out)
          DMA Unpacking disabled when RGB Formating is enabled, otherwise DMA unpacking enabled
      	  Swapping Disabled, 
      	  One (DMA) Channel Mode,
          RGB Formatting Enabled for RGB666 output, disabled for RGB888 output
      	  Regular watermark - when FIFO is 75% full, 
      	  Urgent watermark - when FIFO is 25% full 
	*/

	/* EPPI Configuration table for Sharp LQ043T1DG01 LCD */
	ADI_DEV_CMD_VALUE_PAIR EppiLcdConfig[]=
	{
	/* set EPPI for RGB888 (24-bit) output */
    	{ ADI_EPPI_CMD_SET_DATA_LENGTH,             (void *)6       },  /* 24 bit out                       */
		{ ADI_EPPI_CMD_ENABLE_RGB_FORMATTING,       (void *)FALSE   },  /* Disable RGB formatting           */
        { ADI_EPPI_CMD_SET_PORT_DIRECTION,          (void *)1       },  /* EPPI in Output mode              */
        { ADI_EPPI_CMD_SET_TRANSFER_TYPE,           (void *)3       },  /* GP Transfer mode                 */
    	{ ADI_EPPI_CMD_SET_FRAME_SYNC_CONFIG,       (void *)2       },  /* GP2 mode                         */
    	{ ADI_EPPI_CMD_ENABLE_INTERNAL_CLOCK_GEN,   (void *)TRUE    },  /* Internally generated Clock       */
	    { ADI_EPPI_CMD_ENABLE_INTERNAL_FS_GEN,      (void *)TRUE    },  /* Internally generated Frame sync  */
        { ADI_EPPI_CMD_SET_CLOCK_POLARITY,          (void *)1       },  /* Tx raising edge, Rx falling edge */
    	{ ADI_EPPI_CMD_SET_FRAME_SYNC_POLARITY,     (void *)3       },  /* FS1 & FS2 active low	            */
    	{ ADI_EPPI_CMD_SET_SKIP_ENABLE,             (void *)FALSE   },  /* Disable skipping 	            */
	    { ADI_EPPI_CMD_SET_PACK_UNPACK_ENABLE,      (void *)TRUE    },  /* DMA unpacking enabled            */
    	{ ADI_EPPI_CMD_SET_SWAP_ENABLE,             (void *)FALSE   },  /* Swapping disabled 	            */
    	{ ADI_EPPI_CMD_SET_SPLIT_EVEN_ODD,          (void *)FALSE   },  /* Splitting disabled 	            */
        { ADI_EPPI_CMD_SET_FIFO_REGULAR_WATERMARK,  (void *)1      	},  /* Regular watermark  	            */
        { ADI_EPPI_CMD_SET_FIFO_URGENT_WATERMARK,   (void *)3       },  /* Urgent watermark 	            */
	    { ADI_DEV_CMD_END,                          NULL            }   /* End of command table             */
    }; 
    
    /* Configure EPPI Control register - can use LCD driver handle, as LCD driver passes unrecognised commands to EPPI */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
		Result = adi_dev_Control(	LcdDriverHandle, 
							 		ADI_DEV_CMD_TABLE, 
							 		(void*)EppiLcdConfig );

	/*
       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.
	*/

    /* FOR (Number of output 2D buffers) */
    for (i = 0; i < LCD_NUM_FRAMES; i++) 
    {
        PingBuffer[i].Data              = (void*)PingFrame;
        PingBuffer[i].ElementWidth      = DMA_BUS_SIZE;
        PingBuffer[i].XCount            = ((LCD_WIDTH * LCD_DATA_PER_PIXEL)/DMA_BUS_SIZE);
        PingBuffer[i].XModify           = DMA_BUS_SIZE;
        PingBuffer[i].YCount            = LCD_HEIGHT;
        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 = NULL;
    
	/* Set Dataflow method */
	if (Result == ADI_DEV_RESULT_SUCCESS) 
    	Result = adi_dev_Control(	LcdDriverHandle, 
    								ADI_DEV_CMD_SET_DATAFLOW_METHOD, 
    								(void *)ADI_DEV_MODE_CHAINED_LOOPBACK );
    								
    /* 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);   	    

    /* Enable LCD video data flow */
    if (Result == ADI_DEV_RESULT_SUCCESS) 
    	Result = adi_dev_Control(	LcdDriverHandle, 
    								ADI_DEV_CMD_SET_DATAFLOW, 
    								(void *)TRUE);     
    
    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;
    MPPMESSAGE	MessageToSelf;
    u32 Result;
    /* CASEOF (event type) */
    switch (Event)
    {
        /* CASE (buffer processed) */
        case ADI_DEV_EVENT_BUFFER_PROCESSED:
            /* if a new frame has been prepared then copy its frame data  */
            /* into this DMA buffer chain's data area                     */
			pBuffer = (ADI_DEV_2D_BUFFER *)pArg;

            break;

        /* CASE (EPPI Status Error) */
        case ADI_EPPI_EVENT_LUMA_FIFO_ERROR:
        case ADI_EPPI_EVENT_LINE_TRACK_OVERFLOW_ERROR:
        case ADI_EPPI_EVENT_LINE_TRACK_UNDERFLOW_ERROR:
        case ADI_EPPI_EVENT_FRAME_TRACK_OVERFLOW_ERROR:
        case ADI_EPPI_EVENT_FRAME_TRACK_UNDERFLOW_ERROR:
        case ADI_EPPI_EVENT_PREAMBLE_ERROR_NOT_CORRECTED:
        case ADI_EPPI_EVENT_PREAMBLE_ERROR:
			if(APP_TRACE_LEVEL)
    		{
            	APP_TRACE("EPPI status error. Event code: 0x%08X\n",Event);
    		}
            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;

	
	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);

		}	
		
		switch(pMessage->uMID)
		{
				/* Updates the position of the file selection box */
				case MSGID_LCD_FILESELECT: 	
					/* Refresh File List Frame */										
					display_filelistframe((void*)PingFrame);								
					/* Update file selector    */
					display_fileselector((void*)PingFrame,pMessage->Param2, pMessage->Param1);	
					/* Update file lising      */				
					display_filelist((void*)PingFrame,pMessage->Param2, pMessage->Param1);	
					break;
				
				/* Resets the LCD screen to the default state */				
				case MSGID_LCD_REFRESH_RESET:
					/* Refresh File List Frame */										
					display_filelistframe((void*)PingFrame);					
					/* Refresh File Tag Frame */										
					display_filetagframe((void*)PingFrame);					
					/* Refresh File Status Frame */										
					display_statusframe((void*)PingFrame);					
					/* Update file selector    */
					display_fileselector((void*)PingFrame,pMessage->Param2, pMessage->Param1);	
					/* Update file lising      */				
					display_filelist((void*)PingFrame,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((void*)PingFrame);								
					/* Update file selector    */
					display_fileselector((void*)PingFrame,pMessage->Param2, pMessage->Param1);	
					/* Update file lising      */				
					display_filelist((void*)PingFrame,pMessage->Param2, pMessage->Param1);
					/* Output tag information */
				    display_filetag((void*)PingFrame, NULL, NULL_TYPE);		
				    /* Clear status information */
					display_statusinfo((void*)PingFrame, 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((void*)PingFrame, pMessage->Param2, pMessage->Param1);	
					break;
					
				/* Update the status field with PLAY / STOPPED / PAUSED etc states	*/
				case MSGID_LCD_STATUS:
					/* Output status information */
					display_statusinfo((void*)PingFrame, pMessage->Param1);	
					break;
				
				default:/* ignore all other commands */
					break;
			
		}/* end of switch */
	}	
}

/****************************************************************************
    Function:      close_lcd

    Description:   This function closes the LCD device driver
    			 
    
    
   Return value:   
*****************************************************************************/ 
int close_lcd_LQ043T1DG01(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);
}



