/* --COPYRIGHT--,BSD
 * Copyright (c) 2013, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * --/COPYRIGHT--*/
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************

//*****************************************************************************
//
// IMPORTANT NOTE: The following driver file supports using either RAM or
// non-volatile memory(Flash or FRAM) to store the LCD DisplayBuffer.
//
//*****************************************************************************

//*****************************************************************************
//
// Sharp96x96.c
//
//*****************************************************************************
//
//! \addtogroup display_api
//! @{
//
//*****************************************************************************

#include "grlib.h"
#include "Sharp96x96.h"
#include <msp430.h>
#ifdef __MSP430FR5969
#include "HAL_MSP-EXP430FR5969_Sharp96x96.h"
#include "../driverlibHeaders.h"
#include "inc/hw_memmap.h"
#elif defined(__MSP430G2553)
#include "HAL_MSP-EXP430G2_Sharp96x96.h"
#endif
#include <stdint.h>


static void Sharp96x96_InitializeDisplayBuffer(void *pvDisplayData, uint8_t ucValue);

//*****************************************************************************
//
// If flash is used as non-volatile memory, the DisplayBuffer will have 32 extra
// vertical lines to create a buffer size which is a multiple of 512 Byte
// (this is the default size of flash memory segments in MSP430 device). It is
// required that the display buffer is a multiple of 512 Bytes to be able to
// initialize the buffer using the FLASH segment erase feature of the FLASH
// controller.
//
//*****************************************************************************

#ifdef NON_VOLATILE_MEMORY_BUFFER
#pragma location=NON_VOLATILE_MEMORY_ADDRESS
#endif

#ifndef NON_VOLATILE_MEMORY_BUFFER
uint8_t DisplayBuffer[LCD_VERTICAL_MAX][LCD_HORIZONTAL_MAX/8];
#else
#ifdef __ICC430__
__no_init uint8_t DisplayBuffer[LCD_VERTICAL_MAX +32][LCD_HORIZONTAL_MAX/8];
#else
uint8_t DisplayBuffer[LCD_VERTICAL_MAX +32][LCD_HORIZONTAL_MAX/8];
#endif //__ICC430__
#endif //NON_VOLATILE_MEMORY_BUFFER

uint8_t VCOMbit= 0x40;
uint8_t flagSendToggleVCOMCommand = 0;

//*******************************************************************************
//
//! Reverses the bit order.- Since the bit reversal function is called
//! frequently by the several driver function this function is implemented
//! to maximize code execution
//
//*******************************************************************************
const uint8_t referse_data[] = {0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF};
uint8_t reverse(uint8_t x)
{
  uint8_t b = 0;

  b  = referse_data[x & 0xF]<<4;
  b |= referse_data[(x & 0xF0)>>4];
  return b;
}

//*****************************************************************************
//
//! Draws a pixel on the screen.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param lX is the X coordinate of the pixel.
//! \param lY is the Y coordinate of the pixel.
//! \param ulValue is the color of the pixel.
//!
//! This function sets the given pixel to a particular color.  The coordinates
//! of the pixel are assumed to be within the extents of the display.
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_PixelDraw(void *pvDisplayData, int16_t lX, int16_t lY,
                                   uint16_t ulValue)
{

#ifdef NON_VOLATILE_MEMORY_BUFFER
	PrepareMemoryWrite();
#endif

	if( ClrBlack == ulValue){
		DisplayBuffer[lY][lX>>3] &= ~(0x80 >> (lX & 0x7));
	}else{
		DisplayBuffer[lY][lX>>3] |= (0x80 >> (lX & 0x7));
	}

#ifdef NON_VOLATILE_MEMORY_BUFFER
	FinishMemoryWrite();
#endif

}
//*****************************************************************************
//
//! Draws a horizontal sequence of pixels on the screen.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param lX is the X coordinate of the first pixel.
//! \param lY is the Y coordinate of the first pixel.
//! \param lX0 is sub-pixel offset within the pixel data, which is valid for 1
//! or 4 bit per pixel formats.
//! \param lCount is the number of pixels to draw.
//! \param lBPP is the number of bits per pixel; must be 1, 4, or 8.
//! \param pucData is a pointer to the pixel data.  For 1 and 4 bit per pixel
//! formats, the most significant bit(s) represent the left-most pixel.
//! \param pucPalette is a pointer to the palette used to draw the pixels.
//!
//! This function draws a horizontal sequence of pixels on the screen, using
//! the supplied palette.  For 1 bit per pixel format, the palette contains
//! pre-translated colors; for 4 and 8 bit per pixel formats, the palette
//! contains 24-bit RGB values that must be translated before being written to
//! the display.
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_DrawMultiple(void *pvDisplayData, int16_t lX,
                                           int16_t lY, int16_t lX0, int16_t lCount,
                                           int16_t lBPP,
                                           const uint8_t *pucData,
                                           const uint16_t *pucPalette)
{  

	uint8_t *pData = &DisplayBuffer[lY][lX>>3];
	uint16_t xj = 0;

#ifdef NON_VOLATILE_MEMORY_BUFFER
	PrepareMemoryWrite();
#endif

	//Write bytes of data to the display buffer
	for(xj=0;xj<lCount>>3;xj++){
		*pData++ = *pucData++;
	}

	//Write last data byte to the display buffer
	*pData = (*pData & (0xFF >> (lCount & 0x7))) | *pucData;

#ifdef NON_VOLATILE_MEMORY_BUFFER
	FinishMemoryWrite();
#endif
}
//*****************************************************************************
//
//! Draws a horizontal line.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param lX1 is the X coordinate of the start of the line.
//! \param lX2 is the X coordinate of the end of the line.
//! \param lY is the Y coordinate of the line.
//! \param ulValue is the color of the line.
//!
//! This function draws a horizontal line on the display.  The coordinates of
//! the line are assumed to be within the extents of the display.
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_LineDrawH(void *pvDisplayData, int16_t lX1, int16_t lX2,
                                   int16_t lY, uint16_t ulValue)
{
	volatile uint16_t xi = 0;
	volatile uint16_t x_index_min = lX1>>3;
	volatile uint16_t x_index_max = lX2>>3;
	volatile uint8_t *pucData, ucfirst_x_byte, uclast_x_byte;

#ifdef NON_VOLATILE_MEMORY_BUFFER
	PrepareMemoryWrite();
#endif

	//calculate first byte
	//mod by 8 and shift this # bits
	ucfirst_x_byte = (0xFF >> (lX1 & 0x7));    
	//calculate last byte
	//mod by 8 and shift this # bits
	uclast_x_byte = (0xFF << (7-(lX2 & 0x7))); 

	//check if more than one data byte
	if(x_index_min != x_index_max){

		//set buffer to correct location
		pucData = &DisplayBuffer[lY][x_index_min];

		//black pixels (clear bits)
		if(ClrBlack == ulValue)
		{
			//write first byte
			*pucData++ &= ~ucfirst_x_byte;

			//write middle bytes
			for(xi = x_index_min; xi < x_index_max-1; xi++)
			{
				*pucData++ = 0x00;
			}

			//write last byte
			*pucData &= ~uclast_x_byte;
		}
		//white pixels (set bits)
		else
		{
			//write first byte
			*pucData++ |= ucfirst_x_byte;

			//write middle bytes
			for(xi = x_index_min; xi < x_index_max-1; xi++)
			{
				*pucData++ = 0xFF;
			}

			//write last byte
			*pucData |= uclast_x_byte;
		}
	}
	//only one data byte
	else
	{
		//calculate value of single byte
		ucfirst_x_byte &= uclast_x_byte;

		//set buffer to correct location
		pucData = &DisplayBuffer[lY][x_index_min];

		//draw black pixels (clear bits)
		if(ClrBlack == ulValue)
		{
			*pucData++ &= ~ucfirst_x_byte;
		}
		//white pixels (set bits)
		else
		{
			*pucData++ |= ucfirst_x_byte;
		}
	}

#ifdef NON_VOLATILE_MEMORY_BUFFER
	FinishMemoryWrite();
#endif
}


//*****************************************************************************
//
//! Draws a vertical line.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param lX is the X coordinate of the line.
//! \param lY1 is the Y coordinate of the start of the line.
//! \param lY2 is the Y coordinate of the end of the line.
//! \param ulValue is the color of the line.
//!
//! This function draws a vertical line on the display.  The coordinates of the
//! line are assumed to be within the extents of the display.
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_LineDrawV(void *pvDisplayData, int16_t lX, int16_t lY1,
                                   int16_t lY2, uint16_t ulValue)
{

	volatile uint16_t yi = 0;
	volatile uint16_t x_index = lX>>3;
	volatile uint8_t data_byte;

#ifdef NON_VOLATILE_MEMORY_BUFFER
	PrepareMemoryWrite();
#endif

	//calculate data byte
	//mod by 8 and shift this # bits
	data_byte = (0x80 >> (lX & 0x7));     

	//write data to the display buffer
	for(yi = lY1; yi <= lY2; yi++){

		//black pixels (clear bits)
		if(ClrBlack == ulValue)
		{
			DisplayBuffer[yi][x_index] &= ~data_byte;
		}
		//white pixels (set bits)
		else
		{
			DisplayBuffer[yi][x_index] |= data_byte;
		}
	}

#ifdef NON_VOLATILE_MEMORY_BUFFER
	FinishMemoryWrite();
#endif
}

//*****************************************************************************
//
//! Fills a rectangle.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param pRect is a pointer to the structure describing the rectangle.
//! \param ulValue is the color of the rectangle.
//!
//! This function fills a rectangle on the display.  The coordinates of the
//! rectangle are assumed to be within the extents of the display, and the
//! rectangle specification is fully inclusive (in other words, both sXMin and
//! sXMax are drawn, along with sYMin and sYMax).
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_RectFill(void *pvDisplayData, const tRectangle *pRect,
                                  uint16_t ulValue)
{
	volatile uint16_t xi = 0;
	volatile uint16_t yi = 0;
	volatile uint16_t x_index_min = pRect->sXMin>>3;
	volatile uint16_t x_index_max = pRect->sXMax>>3;
	volatile uint8_t *pucData, ucfirst_x_byte, uclast_x_byte;

#ifdef NON_VOLATILE_MEMORY_BUFFER
	PrepareMemoryWrite();
#endif

	//calculate first byte
	//mod by 8 and shift this # bits
	ucfirst_x_byte = (0xFF >> (pRect->sXMin & 0x7));  

	//calculate last byte
	//mod by 8 and shift this # bits
	uclast_x_byte = (0xFF << (7-(pRect->sXMax & 0x7)));   

	//check if more than one data byte
	if(x_index_min != x_index_max){

		//write bytes
		for (yi = pRect->sYMin; yi<= pRect->sYMax; yi++)
		{
			//set buffer to correct location
			pucData = &DisplayBuffer[yi][x_index_min];

			//black pixels (clear bits)
			if(ClrBlack == ulValue)
			{
				//write first byte
				*pucData++ &= ~ucfirst_x_byte;

				//write middle bytes
				for(xi = x_index_min; xi < x_index_max-1; xi++)
				{
					*pucData++ = 0x00;
				}

				//write last byte
				*pucData &= ~uclast_x_byte;
			}
			//white pixels (set bits)
			else
			{
				//write first byte
				*pucData++ |= ucfirst_x_byte;

				//write middle bytes
				for(xi = x_index_min; xi < x_index_max-1; xi++)
				{
					*pucData++ = 0xFF;
				}

				//write last byte
				*pucData |= uclast_x_byte;
			}
		}
	}
	//only one data byte
	else
	{
		//calculate value of single byte
		ucfirst_x_byte &= uclast_x_byte;

		//set buffer to correct location
		pucData = &DisplayBuffer[pRect->sYMin][x_index_min];

		//black pixels (clear bits)
		if(ClrBlack == ulValue)
		{
			*pucData++ &= ~ucfirst_x_byte;
		}
		//white pixels (set bits)
		else
		{
			*pucData++ |= ucfirst_x_byte;
		}
	}

#ifdef NON_VOLATILE_MEMORY_BUFFER
	FinishMemoryWrite();
#endif
}

//*****************************************************************************
//
//! Translates a 24-bit RGB color to a display driver-specific color.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param ulValue is the 24-bit RGB color.  The least-significant byte is the
//! blue channel, the next byte is the green channel, and the third byte is the
//! red channel.
//!
//! This function translates a 24-bit RGB color into a value that can be
//! written into the display's frame buffer in order to reproduce that color,
//! or the closest possible approximation of that color.
//!
//! \return Returns the display-driver specific color.
//
//*****************************************************************************
static uint16_t Sharp96x96_ColorTranslate(void *pvDisplayData,
                                        uint32_t ulValue)
{
    //
    // Translate from a 24-bit RGB color to mono color.
    //
    return(DPYCOLORTRANSLATE(ulValue));
}


//*****************************************************************************
//
//! Flushes any cached drawing operations.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//!
//!
//! This functions flushes any cached drawing operations to the display.  This
//! is useful when a local frame buffer is used for drawing operations, and the
//! flush would copy the local frame buffer to the display.
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_Flush (void *pvDisplayData)
{
	uint8_t *pucData = &DisplayBuffer[0][0];
	int32_t xi =0;
	int32_t xj = 0;
	//image update mode(1X000000b)
	uint8_t command = SHARP_LCD_CMD_WRITE_LINE;

	//COM inversion bit
	command = command^VCOMbit;

	SetCS();

	WriteCmdData(command);
	flagSendToggleVCOMCommand = SHARP_SKIP_TOGGLE_VCOM_COMMAND;
#ifdef LANDSCAPE
	for(xj=0; xj<LCD_VERTICAL_MAX; xj++)
		{
		  WriteCmdData(reverse(xj + 1));

		  for(xi=0; xi<(LCD_HORIZONTAL_MAX>>3); xi++)
		  {
			WriteCmdData(*(pucData++));
		  }
		  WriteCmdData(SHARP_LCD_TRAILER_BYTE);
		}
#endif
#ifdef LANDSCAPE_FLIP
	pucData = &DisplayBuffer[LCD_VERTICAL_MAX-1][(LCD_HORIZONTAL_MAX>>3)-1];

	for(xj=1; xj<=LCD_VERTICAL_MAX; xj++)
	{
		WriteCmdData(reverse(xj));

	  for(xi=0; xi < (LCD_HORIZONTAL_MAX>>3); xi++)
	  {
		WriteCmdData(reverse(*pucData--));
	  }
	  WriteCmdData(SHARP_LCD_TRAILER_BYTE);
	}
#endif

	WriteCmdData(SHARP_LCD_TRAILER_BYTE);

	// Wait for last byte to be sent, then drop SCS
	WaitUntilLcdWriteFinished();

    // Ensure a 2us min delay to meet the LCD's thSCS
    __delay_cycles(SYSTEM_CLOCK_SPEED * 0.000002);

	ClearCS();
}

//*****************************************************************************
//
//! Send command to clear screen.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//! \param ucValue is the background color of the buffered data.
//!
//! This function sets every pixel to the background color.
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_ClearScreen (void *pvDisplayData, uint16_t ulValue)
{
	//clear screen mode(0X100000b)
	uint8_t command = SHARP_LCD_CMD_CLEAR_SCREEN;
	//COM inversion bit
	command = command^VCOMbit;

	SetCS();

	WriteCmdData(command);
	flagSendToggleVCOMCommand = SHARP_SKIP_TOGGLE_VCOM_COMMAND;
	WriteCmdData(SHARP_LCD_TRAILER_BYTE);

	// Wait for last byte to be sent, then drop SCS
	WaitUntilLcdWriteFinished();

	// Ensure a 2us min delay to meet the LCD's thSCS
	__delay_cycles(SYSTEM_CLOCK_SPEED * 0.000002);

	ClearCS();
	if(ClrBlack == ulValue)
	Sharp96x96_InitializeDisplayBuffer(pvDisplayData, SHARP_BLACK);
	else
	Sharp96x96_InitializeDisplayBuffer(pvDisplayData, SHARP_WHITE);

}

//*****************************************************************************
//
//! Send toggle VCOM command.
//!
//! This function toggles the state of VCOM which prevents a DC bias from being 
//! built up within the panel.
//!
//! \return None.
//
//*****************************************************************************
void Sharp96x96_SendToggleVCOMCommand()
{
	VCOMbit ^= SHARP_VCOM_TOGGLE_BIT;

	if(SHARP_SEND_TOGGLE_VCOM_COMMAND == flagSendToggleVCOMCommand)
	{
		//clear screen mode(0X100000b)
		uint8_t command = SHARP_LCD_CMD_CHANGE_VCOM;
		//COM inversion bit
		command = command^VCOMbit;

	    SetCS();

		WriteCmdData(command);
		WriteCmdData(SHARP_LCD_TRAILER_BYTE);

		// Wait for last byte to be sent, then drop SCS
		WaitUntilLcdWriteFinished();

		// Ensure a 2us min delay to meet the LCD's thSCS
		__delay_cycles(SYSTEM_CLOCK_SPEED * 0.000002);

		ClearCS();
	}

	flagSendToggleVCOMCommand = SHARP_SEND_TOGGLE_VCOM_COMMAND;
}


//*****************************************************************************
//
//! Initialize DisplayBuffer.
//!
//! \param pvDisplayData is a pointer to the driver-specific data for this
//! display driver.
//!
//!	\param ucValue is the foreground color of the buffered data.
//!
//! This function initializes the display buffer and discards any cached data.
//!
//! \return None.
//
//*****************************************************************************
static void Sharp96x96_InitializeDisplayBuffer(void *pvDisplayData, uint8_t ucValue)
{
	uint16_t i=0,j=0;
	uint8_t *pucData = pvDisplayData;


#ifdef USE_FLASH_BUFFER
	// This is a callback function to HAL file since it implements device specific
	// functionality
	InitializeDisplayBuffer(pvDisplayData, ucValue);

#else
	for(i =0; i< LCD_VERTICAL_MAX; i++)
	for(j =0; j< (LCD_HORIZONTAL_MAX>>3); j++)
		*pucData++ = ucValue;

#endif //USE_FLASH_BUFFER
}

//*****************************************************************************
//
//! The display structure that describes the driver for the 
//! sharpLCD panel 
//
//*****************************************************************************
const tDisplay g_sharp96x96LCD =
{
    sizeof(tDisplay),
    DisplayBuffer,
    LCD_HORIZONTAL_MAX,
    LCD_VERTICAL_MAX,
    Sharp96x96_PixelDraw, //PixelDraw,
    Sharp96x96_DrawMultiple,
    Sharp96x96_LineDrawH,
    Sharp96x96_LineDrawV, //LineDrawV,
    Sharp96x96_RectFill, //RectFill,
    Sharp96x96_ColorTranslate,
    Sharp96x96_Flush, //Flush
    Sharp96x96_ClearScreen //Clear screen. Contents of display buffer unmodified
};



//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************

