/*******************************************************************
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.

Project Name:  	Power_On_Self_Test

Hardware:		ADSP-BF526 EZ-KIT Lite

Description:	This file tests the LCD and touchscreen on the EZ-KIT
*******************************************************************/

/*******************************************************************
*  include files
*******************************************************************/
#if defined(__ADSPBF537__)		/* __ADSPBF537__ */
#include <cdefBF537.h>
#elif defined(__ADSPBF526__)	/* __ADSPBF526__ */
#include <cdefBF526.h>
#elif defined(__ADSPBF518__)	/* __ADSPBF526__ */
#include <cdefBF518.h>
#else
#error *** Processor not supported ***		/* NOT DEFINED */
#endif

#include <stdio.h>
#include <sys\exception.h>
#include <ccblkfn.h>
#include <sysreg.h>
#include <signal.h>
#include "..\lcd_buffers.h"
#include "..\LCD\lq035q1dh02.h"
#include "ad7879.h"


/*******************************************************************
*  global variables and defines
*******************************************************************/

/* define used for placement and size of buttons */
#define 	FIRST_LINE_LOC			1
#define 	FIRST_PIXEL_LOC			1
#define		HORIZ_INNER_BORDER		32
#define 	VERT_INNER_BORDER		46
#define		VERT_PERIM_BORDER		24
#define 	HORIZ_PERIM_BORDER		18
#define		NUM_BLOCKS_PER_ROW		3

/* buffer for the image to be displayed */
extern u16 Frame0[];

/* This is part of the front porch and will not
   be displayed									*/
extern u16 TempFrame[];

/* keeps track of the buttons pushed */
volatile u16	ButtonsPushed = 0;

/* Dummy buffer to account for the front porch  */
extern DESCRIPTOR_LARGE DummyBuffer[];

/* array of descriptors for DMA */
extern DESCRIPTOR_LARGE Buffer1[];

#define FILTER_SIZE		7

int DataCount = 0;

u16 x[FILTER_SIZE], y[FILTER_SIZE];

/*******************************************************************
*  function prototypes
*******************************************************************/
static bool Is_Boundary( u32 PixelLoc, u32 LineLoc );
static int Get_Button_Pressed(u32 xPos, u32 yPos);
static bool Highlight_Button( u32 ButtonNum, u16  *pDataBuffer );
static void adi_lcd_TestPattern( u16  *pDataBuffer );
static void Dummy_Read(void);
EX_INTERRUPT_HANDLER(TOUCHIRQ_ISR1);


/*******************************************************************
*   Function:    TOUCHIRQ_ISR1
*   Description: ISR for PENIRQ
*******************************************************************/
EX_INTERRUPT_HANDLER(TOUCHIRQ_ISR1)
{

	u16 x_avg = 0, y_avg = 0;
	u16 x_coord, y_coord;
	u16 z = 0;
	int i = 0, j = 0;
	int SmallestValue;
	int temp1 = 1;
	int ButtonPressed = 0;

	/* turn off PENIRQ ints */
	Disable_PENIRQ();

	/* acknowledge the interrupt */
	Clear_PENIRQ();

	/* read position and pressure registers */
	Read_AD7879( X_POSITION_REG, &x[DataCount] );
	Read_AD7879( Y_POSITION_REG, &y[DataCount] );
	Read_AD7879( Z1_TOUCH_PRESSURE_REG, &z );

	/* only keep the coordinates if the pressure
	   was in a valid range */
	if( z > 30 )
	{
		if( DataCount == (FILTER_SIZE-1) )
		{
			/* we do a software version of a filter here
			   because early AD7879 silicon had some issues
			   with the filtering */

			/* sort the x buffer */
			for (j=0; j<FILTER_SIZE-1; j++)
			{
				for (i=0; i<(FILTER_SIZE-1-j); i++)
				{
					if (x[i+1] < x[i])
					{
						SmallestValue = x[i+1];
						x[i+1] = x[i];
						x[i] = SmallestValue;
					}
				}
			}

			/* sort the y buffer */
			for (j=0; j<FILTER_SIZE-1; j++)
			{
				for (i=0; i<(FILTER_SIZE-1-j); i++)
				{
					if (y[i+1] < y[i])
					{
						SmallestValue = y[i+1];
						y[i+1] = y[i];
						y[i] = SmallestValue;
					}
				}
			}

			/* add up the middle coordinates */
			for( i = 2; i < FILTER_SIZE - 2; i++ )
			{
				x_avg += x[i];
				y_avg += y[i];
			}

			/* get the average of the middle coordinates */
			x_avg /= (FILTER_SIZE - 4);
			y_avg /= (FILTER_SIZE - 4);

			/* x and y go from 0 - 4096 so to figure out
			   the equivalent on a 320 x 240 display we
			   need to take the x coordinate and divide by
			   12.8(4096/320).  To get the y coordinate we
			   divide by 17(4096/240)						*/
			x_coord = (abs(320-(u32)((x_avg)/12.8)));
			y_coord = (abs(240-(u32)((y_avg)/17)));

			//printf("(%d, %d)\n", x_coord, y_coord);

			/* find out what button was pressed and highlight it */
			if( !(Is_Boundary( (u32)x_coord, (u32)y_coord )) )
			{
				ButtonPressed = Get_Button_Pressed( (u32)x_coord, (u32)y_coord );

				if( ButtonPressed != 1 )
				{
					asm("nop;");
				}

				if( ButtonPressed != 0 )
				{
					Highlight_Button( ButtonPressed, Frame0 );

					/* keep track of all buttons that were pressed */
					temp1 <<= (ButtonPressed-1);
					ButtonsPushed |= temp1;
				}
			}
		}

		DataCount++;

		if( DataCount == FILTER_SIZE )
		{
			/* reset our DataCount so we can do this
			   for the next FILTER_SIZE values. */
			DataCount = 0;
		}
	}

	/* allow PENIRQ ints */
	Enable_PENIRQ();
}

/*******************************************************************
*   Function:    Get_Button_Pressed
*   Description: Get the button that was pressed by the user
*******************************************************************/
int Get_Button_Pressed(u32 xPos, u32 yPos)
{
	unsigned char ucButton = 1;
	u32 RowPos = 0;
	u32 ColumnPos = 0;
	bool bButtonFound = false;
	unsigned char ucCurrentButton = 1;

	for( RowPos = (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW); (RowPos <= LCD_LINES_PER_FRAME) && !bButtonFound; RowPos+= (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW) )
	{
		for( ColumnPos = (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW); ColumnPos <= LCD_PIXELS_PER_LINE; ColumnPos +=  (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW) )
		{
			if( (xPos < ColumnPos) && (yPos < RowPos) )
			{
				ucButton = ucCurrentButton;
				bButtonFound = true;
				break;
			}

			ucCurrentButton++;
		}
	}

	if( bButtonFound )
		return (int)ucButton;
	else
		return 0;
}


/*******************************************************************
*   Function:    Highlight_Button
*   Description: Change the color of the button
*******************************************************************/
bool Highlight_Button( u32 ButtonNum, u16  *pDataBuffer )
{
	unsigned char ucButton = 1;
	u16 RowPos = 0;
	u16 ColumnPos = 0;
	bool bButtonFound = false;
	u32 CurrentButton = 1;
	u32 ButtonStartAddress = 0;
	u32 ButtonLineStartAddress = 0;
	u32     Line,Pixel;
	u16	*pTemp = pDataBuffer;
	u16 CurrentRowPos = 1;
	u16 CurrentColumnPos = 1;
	u32 x,y;
	u32 NumLinesInBlock = 0;

	for( RowPos = (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW); (RowPos <= LCD_LINES_PER_FRAME); RowPos+= (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW) )
	{
		for( ColumnPos = (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW); ColumnPos <= LCD_PIXELS_PER_LINE; ColumnPos +=  (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW) )
		{
			if( ButtonNum == CurrentButton )
			{
				bButtonFound = true;
				break;
			}

			CurrentButton++;
			CurrentColumnPos += (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW);
		}

		if( bButtonFound )
			break;

		CurrentRowPos += (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW);
		CurrentColumnPos = 1;
	}

	if( CurrentColumnPos <= (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW) )
	{
		CurrentColumnPos += (VERT_PERIM_BORDER);
	}
	else
	{
		CurrentColumnPos += (VERT_INNER_BORDER/2);
	}

	if( CurrentRowPos <= (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW) )
	{
		CurrentRowPos += (HORIZ_PERIM_BORDER);
	}
	else
	{
		CurrentRowPos += (HORIZ_INNER_BORDER/2);
	}

	if( CurrentRowPos <= (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW) )
	{
		NumLinesInBlock = ((LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW)-(HORIZ_INNER_BORDER/2)-HORIZ_PERIM_BORDER);
	}
	else if( CurrentRowPos >= ((LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW)*2) && CurrentRowPos <= LCD_PIXELS_PER_LINE)
	{
		NumLinesInBlock = ((LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW)-(HORIZ_INNER_BORDER/2)-HORIZ_PERIM_BORDER);
	}
	else
	{
		NumLinesInBlock = ((LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW)-HORIZ_INNER_BORDER);
	}

	/* figure out the start address in memory for our Button */
	ButtonStartAddress += ( (CurrentRowPos-1) * LCD_PIXELS_PER_LINE );
	ButtonStartAddress += (CurrentColumnPos-1);

	ButtonLineStartAddress = ButtonStartAddress;

	pDataBuffer += ButtonStartAddress;
	pTemp = pDataBuffer;

	for( x = 1, Line = CurrentRowPos; x <= NumLinesInBlock; Line++, x++ )
	{
	    for( Pixel = CurrentColumnPos;  ; Pixel++ )
	    {
	    	if( !(Is_Boundary( Pixel, Line )) )
	    	{
	    		*pTemp++ = (u16) (RGB565_WHITE);
	    	}
	    	else
	    	{
	    		pDataBuffer += (LCD_PIXELS_PER_LINE);
	    		pTemp = pDataBuffer;

	    		/* if we hit a boundary then break out
	    		   because we have highlighted that row */
	    		break;
	    	}

	    }
	}

	return true;
}


/*******************************************************************
*   Function:    Is_Boundary
*   Description: Figures out if we are within a button or in the
*				 outside boundary area
*******************************************************************/
bool Is_Boundary( u32 PixelLoc, u32 LineLoc )
{
	bool bPixelMatch = false;
	bool bLineMatch = false;

	/* are we in the left hand vertical perimeter border */
	if( (PixelLoc >= FIRST_PIXEL_LOC) &&
		(PixelLoc < (FIRST_PIXEL_LOC + VERT_PERIM_BORDER)) )
	{
		bPixelMatch = true;
	}
	/* are we in between the first and second column */
	else if((PixelLoc >= (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW) - VERT_INNER_BORDER/2) &&
			(PixelLoc <= (LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW) + VERT_INNER_BORDER/2) )
	{
		bPixelMatch = true;
	}
	/* are we in between the second and third column */
	else if((PixelLoc >= ((LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW)*2) - VERT_INNER_BORDER/2) &&
			(PixelLoc <= ((LCD_PIXELS_PER_LINE/NUM_BLOCKS_PER_ROW)*2) + VERT_INNER_BORDER/2) )
	{
		bPixelMatch = true;
	}
	/* are we in the right hand vertical border */
	else if((PixelLoc >= LCD_PIXELS_PER_LINE-VERT_PERIM_BORDER) &&
			(PixelLoc <= LCD_PIXELS_PER_LINE) )
	{
		bPixelMatch = true;
	}

	/* are we in the top horizontal border */
	else if((LineLoc >= FIRST_LINE_LOC) &&
			(LineLoc < (FIRST_LINE_LOC + HORIZ_PERIM_BORDER)) )
	{
		bLineMatch = true;
	}
	/* are we between the first and second row */
	else if((LineLoc >= (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW) - HORIZ_INNER_BORDER/2) &&
			(LineLoc <= (LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW) + HORIZ_INNER_BORDER/2) )
	{
		bLineMatch = true;
	}
	/* are we between the second and third row */
	else if((LineLoc >= ((LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW)*2) - HORIZ_INNER_BORDER/2) &&
			(LineLoc <= ((LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW)*2) + HORIZ_INNER_BORDER/2) )
	{
		bLineMatch = true;
	}
	/* are we in the bottom horizontal border */
	else if((LineLoc >= LCD_LINES_PER_FRAME-HORIZ_PERIM_BORDER) &&
			(LineLoc <= LCD_LINES_PER_FRAME) )
	{
		bLineMatch = true;
	}

	return( bPixelMatch | bLineMatch );
}

/*******************************************************************
*   Function:    adi_lcd_TestPattern
*   Description: Fills RGB888 frame with the LCD Test pattern
*******************************************************************/
void adi_lcd_TestPattern( u16  *pDataBuffer )
{
    u32     Line,Pixel;
    u8      r,g,b;
    u16		*pTemp;

    pTemp = pDataBuffer;


	/* fill in our first row of buttons */
	for( Line = 1; Line <= LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW; Line++ )
	{
	    for( Pixel = 1; Pixel <= LCD_PIXELS_PER_LINE; Pixel++ )
	    {
	    	*pDataBuffer++ = (u16)(RGB565_RED);
	    }
	}

	/* fill in our second row of buttons */
	for( Line = 1; Line <= LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW; Line++ )
	{
	    for( Pixel = 1; Pixel <= LCD_PIXELS_PER_LINE; Pixel++ )
	    {
	    	*pDataBuffer++ = (u16)(RGB565_GREEN);
	    }
	}

	/* fill in our third row of buttons */
	for( Line = 1; Line <= LCD_LINES_PER_FRAME/NUM_BLOCKS_PER_ROW; Line++ )
	{
	    for( Pixel = 1; Pixel <= LCD_PIXELS_PER_LINE; Pixel++ )
	    {
		    *pDataBuffer++ = (u16)(RGB565_BLUE);
	    }
	}

	/* fill in the boundary with the boundary color */
	for(Line = 1; Line <= LCD_LINES_PER_FRAME; Line++)
	{
		/* FOR (each pixel) */
		for (Pixel = 1; Pixel <= LCD_PIXELS_PER_LINE; Pixel++ )
		{
		    /* IF (this pixel falls under LCD boundary (B)) */
		    if ( Is_Boundary( Pixel, Line ) )
		    {
	            *pTemp++ = (u16)(LCD_BOUNDARY_COLOR);
            }
            else
            {
                *pTemp++;
            }
        }
    }

}

/*******************************************************************
*   Function:    TEST_TOUCHLCD
*   Description: Main test routine will get launched from the POST
*				 framework.
*******************************************************************/
int TEST_LCD_TOUCH(void)
{
	volatile int i;
	u16 RevDevId;
	u16 x,y,z;

	ButtonsPushed = 0;

	/* turn on the LCD backlight */
	Write_AD7879(CONTROL_REG_2, 0x2000);

	/* REV = 1, TB = 1, BGR = 0  */
	Select_LQ035Q1DH02_Register(0x01);
	Write_LQ035Q1DH02_Data(0x22EF);

	/* Set SHUT(Bit 0) to 0 which takes the LCD out of sleep mode
	   because it is by default in sleep mode via a resistor
	   Set CM(bit 8) to 0 for 262k color mode						*/
	Select_LQ035Q1DH02_Register(0x11);
	Write_LQ035Q1DH02_Data(0x0000);

	/* initialize flags used on the AD7879 */
   	Init_AD7879_Interrupt();

   	/* assign ISRs to interrupt vectors		*/
	Assign_AD7879_IntVector(TOUCHIRQ_ISR1);

	/* do Software Reset */
   	Write_AD7879(CONTROL_REG_2, 0x10);

   	/*  Read the Revision and Device Id Reg  */
   	Read_AD7879(0xE, &RevDevId);

   	/* clear results registers */
   	Read_AD7879( X_POSITION_REG, &x );
	Read_AD7879( Y_POSITION_REG, &y );
	Read_AD7879( Z1_TOUCH_PRESSURE_REG, &z );

   	/* 	powered up continuously
   		GPIO enable
   		GPIO data bit low to turn on backlight
   		GPIO direction as output
   		SER/DFR = differential
   		ADC averaging = 4 middle values averaged
   		Median filter size = 8 measurements
   		ADC first converstion Delay = 1.024ms
   	*/
   	Write_AD7879(CONTROL_REG_2, 0xA0C7);

   	/* 	Include Y position, X position, and Z1 touch measure   */
   	Write_AD7879(CONTROL_REG_3, 0x00F0);

   	/* 	INT interrupt enabled
   		ADC mode  = Master mode
   		ADC Acquisition time = 16 clock periods
   		Conversion interval timer = 8.32ms
   	*/
   	if( RevDevId == 0x7A )
   		/* In this revision we had to enable
   		   PENIRQ to get the same functionality
   		   as enabling INT.  PENIRQ did not work
   		   correctly in the very first rev of
   		   the AD7879. */
   		Write_AD7879(CONTROL_REG_1, 0x0EF9);
   	else
		Write_AD7879(CONTROL_REG_1, 0x8EF9);

	/* initialize video buffer  */
	adi_lcd_TestPattern(Frame0);

	/* configure our buffers to pad lines for the front porch */
	DummyBuffer[0].pNext             = &DummyBuffer[1];   /* chain to next buffer in the list */
    DummyBuffer[0].StartAddress      = TempFrame;
    DummyBuffer[0].XCount            = H_ACTPIX;
    DummyBuffer[0].XModify           = 2;
    DummyBuffer[0].YCount            = 2;
    DummyBuffer[0].YModify           = 2;
    DummyBuffer[0].Config           	=	DMAEN 		|	// enable DMA
											WDSIZE_16	|	// WDSIZE=01, 16 bit
											DMA2D 		|	// DMA2D=1. 2D
											NDSIZE_9	|	// NDSIZE=9
									  		FLOW_LARGE;		// large list descriptor mode

	DummyBuffer[1].pNext             = &Buffer1[0];   	/* chain to next buffer in the list */
    DummyBuffer[1].StartAddress      = TempFrame;
    DummyBuffer[1].XCount            = H_ACTPIX;
    DummyBuffer[1].XModify           = 2;
    DummyBuffer[1].YCount            = 2;
    DummyBuffer[1].YModify           = 2;
    DummyBuffer[1].Config           	=	DMAEN 		|	// enable DMA
											WDSIZE_16	|	// WDSIZE=01, 16 bit
											DMA2D 		|	// DMA2D=1. 2D
											NDSIZE_9	|	// NDSIZE=9
									  		FLOW_LARGE;		// large list descriptor mode

	/* FOR (Number of output 2D buffers) */
    for (i = 0; i < LCD_NUM_FRAMES_DISPLAY; i++)
    {
    	Buffer1[i].pNext             = &Buffer1[i + 1];   /* chain to next buffer in the list */
    	Buffer1[i].StartAddress      = Frame0;
        Buffer1[i].XCount            = H_ACTPIX;
        Buffer1[i].XModify           = 2;
        Buffer1[i].YCount            = V_LINES;
        Buffer1[i].YModify           = 2;
        Buffer1[i].Config           	=	DMAEN 		|	// enable DMA
											WDSIZE_16	|	// WDSIZE=01, 16 bit
											DMA2D 		|	// DMA2D=1. 2D
											NDSIZE_9	|	// NDSIZE=9
									  		FLOW_LARGE;		// large list descriptor mode
    }

	// make the buffer loop back around
	Buffer1[LCD_NUM_FRAMES_DISPLAY - 1].pNext = &Buffer1[0];

	LQ035Q1DH02_Init_PPI_Ints();	/* initialize PPI interrupts */

	LQ035Q1DH02_Init_TIMER0();			/* init timer 0( horizontal FS ) */
	LQ035Q1DH02_Init_TIMER1();			/* init timer 1( vertical FS ) */
	LQ035Q1DH02_Init_PPI();				/* initialize PPI */

	LQ035Q1DH02_Init_DMA(DummyBuffer);	/* initialize the DMA */

	/* start PPI */
    LQ035Q1DH02_Enable_PPI();

	/* Frame sync 2 (VS) needs to start at least one PPI clk earlier */
	LQ035Q1DH02_Enable_TIMER1();

	/* now start frame sync 1 */
	LQ035Q1DH02_Enable_TIMER0();

	/* unmask PENIRQ interrupt  */
	Enable_PENIRQ();

    /* continue displaying frames */
   	while(ButtonsPushed != 0x1FF)
   	{

   	}

   	/* give a little time to hightlight the last box */
   	for( i = 0; i < 0xFFFFFF; i++ )
   		asm("nop;");

   	/* mask SIC level interrupt for PENIRQ */
   	Disable_PENIRQ();

	/* disable DMA */
    LQ035Q1DH02_Disable_DMA();

	/* disable PPI */
    LQ035Q1DH02_Disable_PPI();

	/* disable Timers */
    LQ035Q1DH02_Disable_Timers();

    return 1;
}/* end main */



#ifdef _STANDALONE_

#include <bfrom.h>

int main(void)
{
	int bPassed = 1;
	int count = 0;

	/* use Blackfin ROM SysControl() to change the PLL */
    ADI_SYSCTRL_VALUES sys_cntrl_struct;

	sys_cntrl_struct.uwPllCtl = 0x2000;		/* (25MHz CLKIN x (MSEL=16))::CCLK = 400MHz */
	sys_cntrl_struct.uwPllDiv = 0x0005;		/* (400MHz/(SSEL=5))::SCLK = 80MHz */

	bfrom_SysControl( SYSCTRL_WRITE | SYSCTRL_PLLCTL | SYSCTRL_PLLDIV,
					  &sys_cntrl_struct, NULL);
	bPassed = TEST_LCD_TOUCH();

	return bPassed;
}
#endif /* #ifdef _STANDALONE_ */

