/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2008, Atmel Corporation
 *
 * 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 disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 * ----------------------------------------------------------------------------
 */

/**
 * \file
 *
 * \section Purpose
 *
 *  HX8347 driver
 *
 * \section Usage
 *
 *  Explanation on the usage of the code made available through the header file.
 */
//------------------------------------------------------------------------------
//         Headers
//------------------------------------------------------------------------------
#include "board.h"
#include "libsam_gui.h"

#include <stdio.h>

//------------------------------------------------------------------------------
//         Types
//------------------------------------------------------------------------------
typedef volatile unsigned short REG16;

//------------------------------------------------------------------------------
//         Definitions
//------------------------------------------------------------------------------
/** LCD index register address */
#define HX8347_IR(baseAddr) (*((REG16 *)(baseAddr)))
/** LCD status register address */
#define HX8347_SR(baseAddr) (*((REG16 *)(baseAddr)))
/** LCD data address */
#define HX8347_D(baseAddr)  (*((REG16 *)((unsigned int)(baseAddr) + BOARD_LCD_RS)))

/** HX8347 ID code */
#define HX8347_HIMAXID_CODE    0x47

/** HX8347 LCD Registers */
#define HX8347_R00H        0x00
#define HX8347_R01H        0x01
#define HX8347_R02H        0x02
#define HX8347_R03H        0x03
#define HX8347_R04H        0x04
#define HX8347_R05H        0x05
#define HX8347_R06H        0x06
#define HX8347_R07H        0x07
#define HX8347_R08H        0x08
#define HX8347_R09H        0x09
#define HX8347_R0AH        0x0A
#define HX8347_R0CH        0x0C
#define HX8347_R0DH        0x0D
#define HX8347_R0EH        0x0E
#define HX8347_R0FH        0x0F
#define HX8347_R10H        0x10
#define HX8347_R11H        0x11
#define HX8347_R12H        0x12
#define HX8347_R13H        0x13
#define HX8347_R14H        0x14
#define HX8347_R15H        0x15
#define HX8347_R16H        0x16
#define HX8347_R18H        0x18
#define HX8347_R19H        0x19
#define HX8347_R1AH        0x1A
#define HX8347_R1BH        0x1B
#define HX8347_R1CH        0x1C
#define HX8347_R1DH        0x1D
#define HX8347_R1EH        0x1E
#define HX8347_R1FH        0x1F
#define HX8347_R20H        0x20
#define HX8347_R21H        0x21
#define HX8347_R22H        0x22
#define HX8347_R23H        0x23
#define HX8347_R24H        0x24
#define HX8347_R25H        0x25
#define HX8347_R26H        0x26
#define HX8347_R27H        0x27
#define HX8347_R28H        0x28
#define HX8347_R29H        0x29
#define HX8347_R2AH        0x2A
#define HX8347_R2BH        0x2B
#define HX8347_R2CH        0x2C
#define HX8347_R2DH        0x2D
#define HX8347_R35H        0x35
#define HX8347_R36H        0x36
#define HX8347_R37H        0x37
#define HX8347_R38H        0x38
#define HX8347_R39H        0x39
#define HX8347_R3AH        0x3A
#define HX8347_R3BH        0x3B
#define HX8347_R3CH        0x3C
#define HX8347_R3DH        0x3D
#define HX8347_R3EH        0x3E
#define HX8347_R40H        0x40
#define HX8347_R41H        0x41
#define HX8347_R42H        0x42
#define HX8347_R43H        0x43
#define HX8347_R44H        0x44
#define HX8347_R45H        0x45
#define HX8347_R46H        0x46
#define HX8347_R47H        0x47
#define HX8347_R48H        0x48
#define HX8347_R49H        0x49
#define HX8347_R4AH        0x4A
#define HX8347_R4BH        0x4B
#define HX8347_R4CH        0x4C
#define HX8347_R4DH        0x4D
#define HX8347_R4EH        0x4E
#define HX8347_R4FH        0x4F
#define HX8347_R50H        0x50
#define HX8347_R51H        0x51
#define HX8347_R64H        0x64
#define HX8347_R65H        0x65
#define HX8347_R66H        0x66
#define HX8347_R67H        0x67
#define HX8347_R70H        0x70
#define HX8347_R72H        0x72
#define HX8347_R90H        0x90
#define HX8347_R91H        0x91
#define HX8347_R93H        0x93
#define HX8347_R94H        0x94
#define HX8347_R95H        0x95

//------------------------------------------------------------------------------
//         External functions
//------------------------------------------------------------------------------
// External delay 1 ms function
extern void DelayMS(unsigned int ms);
#define Delay(ms) DelayMS(ms)

//------------------------------------------------------------------------------
//         Global functions
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
/// Write data to LCD Register.
/// \param pLcdBase   LCD base address.
/// \param reg        Register address.
/// \param data       Data to be written.
//------------------------------------------------------------------------------
static void _HX8347_WriteReg(void *pLcdBase, unsigned char reg, unsigned short data)
{
    HX8347_IR(pLcdBase) = reg;
    HX8347_D(pLcdBase)  = data;
}

//------------------------------------------------------------------------------
/// Read data from LCD Register.
/// \param pLcdBase   LCD base address.
/// \param reg        Register address.
/// \return data      Data to be read.
//------------------------------------------------------------------------------
static unsigned short _HX8347_ReadReg(void *pLcdBase, unsigned char reg)
{
    HX8347_IR(pLcdBase) = reg;
    return HX8347_D(pLcdBase);
}

//------------------------------------------------------------------------------
/// Read LCD status Register.
/// \param pLcdBase   LCD base address.
/// \param reg        Register address.
/// \return data      Status Data.
//------------------------------------------------------------------------------
static unsigned short _HX8347_ReadStatus(void *pLcdBase)
{
    return HX8347_SR(pLcdBase);
}

//------------------------------------------------------------------------------
/// Prepare to write GRAM data.
/// \param pLcdBase   LCD base address.
//------------------------------------------------------------------------------
static void _HX8347_WriteRAM_Prepare(void *pLcdBase)
{
    HX8347_IR(pLcdBase) = HX8347_R22H;
}

//------------------------------------------------------------------------------
/// Write data to LCD GRAM.
/// \param pLcdBase   LCD base address.
/// \param color      16-bits RGB color.
//------------------------------------------------------------------------------
static void _HX8347_WriteRAM(void *pLcdBase, unsigned short color)
{
    // Write 16-bit GRAM Reg
    HX8347_D(pLcdBase) = color;
}

//------------------------------------------------------------------------------
/// Prepare to read GRAM data.
/// \param pLcdBase   LCD base address.
//------------------------------------------------------------------------------
static void _HX8347_ReadRAM_Prepare(void *pLcdBase)
{
    HX8347_IR(pLcdBase) = HX8347_R22H;
}

//------------------------------------------------------------------------------
/// Read GRAM data.
/// \param pLcdBase   LCD base address.
/// \return           16-bits RGB color.
//------------------------------------------------------------------------------
static unsigned short _HX8347_ReadRAM(void *pLcdBase)
{
    // Read 16-bit GRAM Reg
    return HX8347_D(pLcdBase);
}

//------------------------------------------------------------------------------
/// Initialize the LCD controller.
/// \param pLcdBase   LCD base address.
//------------------------------------------------------------------------------
static void _HX8347_Initialize(void *pLcdBase)
{
    unsigned short chipid;

    // Check HX8347 chipid
    chipid = _HX8347_ReadReg(pLcdBase, HX8347_R67H);
    if(chipid != HX8347_HIMAXID_CODE) {

        printf("Read HX8347 chip ID error, skip initialization.\r\n");
        return ;
    }

    // Start internal OSC
    _HX8347_WriteReg(pLcdBase, HX8347_R19H, 0x49); // OSCADJ=10 0000, OSD_EN=1 //60Hz
    _HX8347_WriteReg(pLcdBase, HX8347_R93H, 0x0C); // RADJ=1100

    // Power on flow
    _HX8347_WriteReg(pLcdBase, HX8347_R44H, 0x4D); // VCM=100 1101
    _HX8347_WriteReg(pLcdBase, HX8347_R45H, 0x11); // VDV=1 0001
    _HX8347_WriteReg(pLcdBase, HX8347_R20H, 0x40); // BT=0100
    _HX8347_WriteReg(pLcdBase, HX8347_R1DH, 0x07); // VC1=111
    _HX8347_WriteReg(pLcdBase, HX8347_R1EH, 0x00); // VC3=000
    _HX8347_WriteReg(pLcdBase, HX8347_R1FH, 0x04); // VRH=0100

    _HX8347_WriteReg(pLcdBase, HX8347_R1CH, 0x04); // AP=100
    _HX8347_WriteReg(pLcdBase, HX8347_R1BH, 0x10); // GASENB=0, PON=1, DK=0, XDK=0, DDVDH_TRI=0, STB=0
    Delay(50);

    _HX8347_WriteReg(pLcdBase, HX8347_R43H, 0x80); // Set VCOMG=1
    Delay(50);

    // Gamma for CMO 2.8
    _HX8347_WriteReg(pLcdBase, HX8347_R46H, 0x95);
    _HX8347_WriteReg(pLcdBase, HX8347_R47H, 0x51);
    _HX8347_WriteReg(pLcdBase, HX8347_R48H, 0x00);
    _HX8347_WriteReg(pLcdBase, HX8347_R49H, 0x36);
    _HX8347_WriteReg(pLcdBase, HX8347_R4AH, 0x11);
    _HX8347_WriteReg(pLcdBase, HX8347_R4BH, 0x66);
    _HX8347_WriteReg(pLcdBase, HX8347_R4CH, 0x14);
    _HX8347_WriteReg(pLcdBase, HX8347_R4DH, 0x77);
    _HX8347_WriteReg(pLcdBase, HX8347_R4EH, 0x13);
    _HX8347_WriteReg(pLcdBase, HX8347_R4FH, 0x4C);
    _HX8347_WriteReg(pLcdBase, HX8347_R50H, 0x46);
    _HX8347_WriteReg(pLcdBase, HX8347_R51H, 0x46);

    //240x320 window setting
    _HX8347_WriteReg(pLcdBase, HX8347_R02H, 0x00); // Column address start2
    _HX8347_WriteReg(pLcdBase, HX8347_R03H, 0x00); // Column address start1
    _HX8347_WriteReg(pLcdBase, HX8347_R04H, 0x00); // Column address end2
    _HX8347_WriteReg(pLcdBase, HX8347_R05H, 0xEF); // Column address end1
    _HX8347_WriteReg(pLcdBase, HX8347_R06H, 0x00); // Row address start2
    _HX8347_WriteReg(pLcdBase, HX8347_R07H, 0x00); // Row address start1
    _HX8347_WriteReg(pLcdBase, HX8347_R08H, 0x01); // Row address end2
    _HX8347_WriteReg(pLcdBase, HX8347_R09H, 0x3F); // Row address end1

    // Display Setting
    _HX8347_WriteReg(pLcdBase, HX8347_R01H, 0x06); // IDMON=0, INVON=1, NORON=1, PTLON=0
    _HX8347_WriteReg(pLcdBase, HX8347_R16H, 0xC8); // MY=1, MX=1, MV=0, BGR=1
    _HX8347_WriteReg(pLcdBase, HX8347_R23H, 0x95); // N_DC=1001 0101
    _HX8347_WriteReg(pLcdBase, HX8347_R24H, 0x95); // P_DC=1001 0101
    _HX8347_WriteReg(pLcdBase, HX8347_R25H, 0xFF); // I_DC=1111 1111
    _HX8347_WriteReg(pLcdBase, HX8347_R27H, 0x06); // N_BP=0000 0110
    _HX8347_WriteReg(pLcdBase, HX8347_R28H, 0x06); // N_FP=0000 0110
    _HX8347_WriteReg(pLcdBase, HX8347_R29H, 0x06); // P_BP=0000 0110
    _HX8347_WriteReg(pLcdBase, HX8347_R2AH, 0x06); // P_FP=0000 0110
    _HX8347_WriteReg(pLcdBase, HX8347_R2CH, 0x06); // I_BP=0000 0110
    _HX8347_WriteReg(pLcdBase, HX8347_R2DH, 0x06); // I_FP=0000 0110
    _HX8347_WriteReg(pLcdBase, HX8347_R3AH, 0x01); // N_RTN=0000, N_NW=001
    _HX8347_WriteReg(pLcdBase, HX8347_R3BH, 0x01); // P_RTN=0000, P_NW=001
    _HX8347_WriteReg(pLcdBase, HX8347_R3CH, 0xF0); // I_RTN=1111, I_NW=000
    _HX8347_WriteReg(pLcdBase, HX8347_R3DH, 0x00); // DIV=00
    _HX8347_WriteReg(pLcdBase, HX8347_R3EH, 0x38); // SON=38h
    _HX8347_WriteReg(pLcdBase, HX8347_R40H, 0x0F); // GDON=0Fh
    _HX8347_WriteReg(pLcdBase, HX8347_R41H, 0xF0); // GDOF=F0h
}

//------------------------------------------------------------------------------
/// Turn on the LCD.
/// \param pLcdBase   LCD base address.
//------------------------------------------------------------------------------
static void _HX8347_On( void *pLcdBase )
{
    // Display ON Setting
    _HX8347_WriteReg(pLcdBase, HX8347_R90H, 0x7F); // SAP=0111 1111
    _HX8347_WriteReg(pLcdBase, HX8347_R26H, 0x04); // GON=0, DTE=0, D=01
    Delay(100);
    _HX8347_WriteReg(pLcdBase, HX8347_R26H, 0x24); // GON=1, DTE=0, D=01
    _HX8347_WriteReg(pLcdBase, HX8347_R26H, 0x2C); // GON=1, DTE=0, D=11
    Delay(100);
    _HX8347_WriteReg(pLcdBase, HX8347_R26H, 0x3C); // GON=1, DTE=1, D=11
}

//------------------------------------------------------------------------------
/// Turn off the LCD.
/// \param pLcdBase   LCD base address.
//------------------------------------------------------------------------------
static void _HX8347_Off( void *pLcdBase )
{
    _HX8347_WriteReg(pLcdBase, HX8347_R90H, 0x00); // SAP=0000 0000
    _HX8347_WriteReg(pLcdBase, HX8347_R26H, 0x00); // GON=0, DTE=0, D=00
}

//------------------------------------------------------------------------------
/// Set cursor of LCD srceen.
/// \param pLcdBase   LCD base address.
/// \param x          X-coordinate of upper-left corner on LCD.
/// \param y          Y-coordinate of upper-left corner on LCD.
//------------------------------------------------------------------------------
static void _HX8347_SetCursor(void *pLcdBase, unsigned short x, unsigned short y)
{
    unsigned char x1, x2, y1, y2;

    x1 = x & 0xff;
    x2 = (x & 0xff00) >>8;
    y1 = y & 0xff;
    y2 = (y & 0xff00) >>8;
    _HX8347_WriteReg(pLcdBase, HX8347_R02H, x2); // column high
    _HX8347_WriteReg(pLcdBase, HX8347_R03H, x1); // column low
    _HX8347_WriteReg(pLcdBase, HX8347_R06H, y2); // row high
    _HX8347_WriteReg(pLcdBase, HX8347_R07H, y1); // row low
}

//------------------------------------------------------------------------------
/// Set the window size user can access.
/// \param pLcdBase   LCD base address.
/// \param x_start    X-coordinate of upper-left corner on LCD.
/// \param y_start    Y-coordinate of upper-left corner on LCD.
/// \param x_end      X-coordinate of bottom-right corner on LCD.
/// \param y_end      Y-coordinate of bottom-right corner on LCD.
//------------------------------------------------------------------------------
static void _HX8347_SetWindow(void *pLcdBase,unsigned short x_start, unsigned short y_start,unsigned short x_end, unsigned short y_end)
{
    unsigned char x1, x2, y1, y2;

        x1 = x_start & 0xff;
        x2 = (x_start & 0xff00) >>8;
        y1 = y_start & 0xff;
        y2 = (y_start & 0xff00) >>8;
        _HX8347_WriteReg(pLcdBase, 0x02, x2); // column high
        _HX8347_WriteReg(pLcdBase, 0x03, x1); // column low
        _HX8347_WriteReg(pLcdBase, 0x06, y2); // row high
        _HX8347_WriteReg(pLcdBase, 0x07, y1); // row low

        x1 = x_end & 0xff;
        x2 = (x_end & 0xff00) >>8;
        y1 = y_end & 0xff;
        y2 = (y_end & 0xff00) >>8;
        _HX8347_WriteReg(pLcdBase, 0x04, x2); // column high
        _HX8347_WriteReg(pLcdBase, 0x05, x1); // column low
        _HX8347_WriteReg(pLcdBase, 0x08, y2); // row high
        _HX8347_WriteReg(pLcdBase, 0x09, y1); // row low
}

SDISPBackend sDISP_Backend_HX8347=
{
    .sData={ 0 },

//    .Reset=_DBE_HX8347_Reset,
//    .Initialize=_DBE_HX8347_Initialize,
//    .GetPixel=_DBE_HX8347_GetPixel,
//    .SetPixel=_DBE_HX8347_SetPixel,
//    .DrawLine=_DBE_HX8347_DrawLine,
//    .DrawCircle=_DBE_HX8347_DrawCircle,
//    .DrawFilledCircle=_DBE_HX8347_DrawFilledCircle,
//    .DrawRectangle=_DBE_HX8347_DrawRectangle,
//    .DrawFilledRectangle=_DBE_HX8347_DrawFilledRectangle,
//    .DrawBitmap=_DBE_HX8347_DrawBitmap,
//    .IOCtl=_DBE_HX8347_IOCtl
} ;
