/*******************************************************************************
 *
 *      A L T R O N I C S    Z 7 0 8 0    L C D   &   T O U C H S C R E E N
 *
 *                              D R I V E R
 *
 *      Copyright A Levido 2013 - All Rights Reserved
 *
 *******************************************************************************
 *  This module uses an ILI9340 driver chip (not ST77881 as stated by Altronics)
 *  We set up for 240 x 320 pixels, 16bpp colour UINT16 5:6:5 RGB format.
 *  Dislay is in portrait mode. Origin is top left. Uses PMP, ADC Timer1,
 *  Timer2 and OC3
 */

#include "display.h"
#include "system.h"
#include <plib.h>

#include "graphics.h"
#include <stdio.h>

char buffer[30];
UINT32 battV;

/* Hardware */
#define LCD_RESET_LO                    PORTClearBits(IOPORT_D, BIT_7)
#define LCD_RESET_HI                    PORTSetBits(IOPORT_D, BIT_7)
#define LCD_CS_LO                       PORTClearBits(IOPORT_D, BIT_10)
#define LCD_CS_HI                       PORTSetBits(IOPORT_D, BIT_10)
#define LCD_POWER_ON                    PORTSetBits(IOPORT_D, BIT_8)
#define LCD_POWER_OFF                   PORTClearBits(IOPORT_D, BIT_8)
#define LCD_BACKLIGHT_OFF               PORTClearBits(IOPORT_D, BIT_0)

#define XYPOS_SETUP                     PORTSetBits(IOPORT_B, BIT_0 | BIT_1)
#define XYNEG_SETUP                     PORTClearBits(IOPORT_B, BIT_2 | BIT_3)
#define XPOS_ADC                        PORTSetPinsAnalogIn(IOPORT_B, BIT_0); AD1CHS = 0x00000000
#define XPOS_HIZ                        PORTSetPinsAnalogIn(IOPORT_B, BIT_0)
#define XPOS_3V3                        PORTSetPinsDigitalOut(IOPORT_B, BIT_0)
#define YPOS_ADC                        PORTSetPinsAnalogIn(IOPORT_B, BIT_1); AD1CHS = 0x00010000
#define YPOS_HIZ                        PORTSetPinsAnalogIn(IOPORT_B, BIT_1)
#define YPOS_3V3                        PORTSetPinsDigitalOut(IOPORT_B, BIT_1)
#define XNEG_HIZ                        PORTSetPinsAnalogIn(IOPORT_B, BIT_2)
#define XNEG_GND                        PORTSetPinsDigitalOut(IOPORT_B, BIT_2)
#define YNEG_HIZ                        PORTSetPinsAnalogIn(IOPORT_B, BIT_3)
#define YNEG_GND                        PORTSetPinsDigitalOut(IOPORT_B, BIT_3)
#define BATT_ADC                        AD1CHS = 0x00050000
#define B_HIGH                          616 // 3.7V * 1024 / 2.05 x 3
#define B_LOW                           566 // 3.4V * 1024 / 2.05 x 3
#define STAT1                           PORTBbits.RB8
#define STAT2                           PORTBbits.RB9
#define PGOOD                           PORTBbits.RB10

/* Dimensions */
#define MAX_X                           239
#define MAX_Y                           319

/* Timer for backlight PWM */
#define PWM_FREQ                         10000
#define PWM_PERIOD                       ((FPB / (256 * PWM_FREQ)) - 1)
void tchInitialise(void);

/*** LCD Driver ***************************************************************/

/* 16 to 18 bpp colour mapping */
const UINT8 map[128] = {
     0,  2,  4,  6,  8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
    33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63,
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 
    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
     0,  2,  4,  6,  8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
    33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63            
};

UINT32 bgcol;

/* Helper functions */
void shortdelay(UINT32 i)
{
    i *= 10;
    while(i--)
        asm volatile("nop");
}
void lcdSendCommand(UINT8 i)
{
    PMADDR = 0x0000; 
    PMDIN = (i); 
    while(PMMODEbits.BUSY);
    PMADDR = 0x4000;
    shortdelay(2);
}
void lcdSendDataByte(UINT8 x)
{ 
    PMADDR = 0x4000;
    PMDIN = (x); 
    while(PMMODEbits.BUSY);;
    shortdelay(2);
}
void lcdSendPixel(UINT16 x)
{
    PMADDR = 0x4000;
    PMDIN = (x >> 8);
    while(PMMODEbits.BUSY);
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    PMDIN = (x & 0x00ff);
    while(PMMODEbits.BUSY);
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
}
void delay(UINT32 i)
{
    i *= 8000*3;
    while(i--)
        asm volatile("nop");
}
void lcdSetX(UINT16 start, UINT16 end){
    lcdSendCommand(0x2a);
    lcdSendDataByte(start >> 8);
    lcdSendDataByte(start & 0xff);
    lcdSendDataByte(end >> 8);
    lcdSendDataByte(end & 0xff);
}
void lcdSetY(UINT16 start, UINT16 end){
    lcdSendCommand(0x2b);
    lcdSendDataByte(start >> 8);
    lcdSendDataByte(start & 0xff);
    lcdSendDataByte(end >> 8);
    lcdSendDataByte(end & 0xff);
}

/* API Functions */
void lcdInit(UINT32 bgColour)
{
    UINT32 i;
    LCD_POWER_ON;
    bgcol = bgColour;

    /* Initialise PMP */
    PMCONbits.SIDL = 1;                 // Stop on idle
    PMCONbits.ADRMUX = 0;               // No multiplexing
    PMCONbits.PTWREN = 1;               // Enable WR pin
    PMCONbits.PTRDEN = 1;               // Enable RD pin
    PMCONbits.CSF = 1;                  // CS2 is select sel, CS1 is A14
    PMMODEbits.MODE16 = 0;              // 8 bit
    PMMODEbits.MODE = 2;                // Master mode 2 (PMCS, RD, WR)
    PMMODEbits.WAITB = 0;               // 1 Tpb
    PMMODEbits.WAITM = 1;               // 2 Tpb
    PMMODEbits.WAITE = 1;               // 1 Tpb
    PMAENbits.PTEN15 = 0;               // PMCS2
    PMAENbits.PTEN14 = 1;               // A14
    PMCONbits.ON = 1;                   // Turn on

    /* Backlight PWM */
    OpenTimer2(T2_ON | T2_PS_1_256 | T2_SOURCE_INT, PWM_PERIOD);
    OpenOC1(OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 15, 15);

    /* Reset LCD */
    LCD_RESET_HI;
    delay(1);
    LCD_RESET_LO;
    delay(2);
    LCD_RESET_HI;
    delay(10);

    LCD_CS_LO;
    lcdSendCommand(0x01);               // soft reset
    delay(10);
    lcdSendCommand(0x28);               // off
    lcdSendCommand(0x11);               // exit sleep
    lcdSendCommand(0xc0);               // power control 1
    lcdSendDataByte(0x26);              // - GVDD = 4.75V
    lcdSendDataByte(0x04);              // - VCI1 = 2.5V
    lcdSendCommand(0xc1);               // power control 2
    lcdSendDataByte(0x04);              // - VGH = VCI1 x 5, VGL = -VCI1 x 4
    lcdSendCommand(0xc5);               // VCOM control
    lcdSendDataByte(0x34);              // - VCOMH = 5.2V
    lcdSendDataByte(0x04);              // - VCOML = -2.4V
    lcdSendCommand(0x36);               // Memory Access Control (MADCTL)
    lcdSendDataByte(0x08);              // - portrait, BGR = 1
    lcdSendCommand(0x3a);               // Pixel format
    lcdSendDataByte(0x55);              // - 18bpp
    lcdSendCommand(0xb6);               // Display Function
    lcdSendDataByte(0x08);              // - interval scan & normal src in nda,
    lcdSendDataByte(0xa2);              // - REV = 1, reverse gate & src scan
    lcdSendDataByte(0x27);              // - 320 lines
    lcdSendDataByte(0x00);              // - fast clock
    lcdSendCommand(0x26);               // Gamma Set
    lcdSendDataByte(0x01);              // - 1, 2 4, 8
    delay(10);
    
    lcdSendCommand(0x2d);               // Colour map
    for (i = 0; i < 128; i++){
        lcdSendDataByte(map[i]);
    }

    lcdSetX(0, 239);
    lcdSetY(0, 319);
    lcdSendCommand(0x2c);               // Write background colour
    for (i = 0; i < 240 * 320; i++){
        lcdSendPixel(bgColour);
    }
    lcdSendCommand(0x29);               // On
    LCD_CS_HI;

    tchInitialise();
}
void lcdShutdown()
{
    LCD_CS_LO;
    lcdSendCommand(0x28);               // Off
    LCD_CS_HI;
    CloseTimer2();                      // close Backlight
    CloseOC1();
    LCD_BACKLIGHT_OFF;
    PMCONbits.ON = 0;                   // turn PMP off
    TRISE = 0x0f;                       // tristate portE
    LCD_POWER_OFF;
}

void lcdWakeup(UINT8 level)
{
    lcdInit(bgcol);
    lcdSetBacklight(level);

}
void lcdPutPixel(INT16 x, INT16 y, UINT16 colour)
{
    LCD_CS_LO;
    lcdSetX(x, x);
    lcdSetY(y, y);
    lcdSendCommand(0x2c);
    lcdSendPixel(colour);
    LCD_CS_HI;
}

void lcdBlock(INT16 x1, INT16 y1, INT16 x2, INT16 y2, UINT16 colour)
{
    UINT32 i = (y2 - y1 + 1) * (x2 - x1 + 1);

    LCD_CS_LO;
    lcdSetX(x1, x2);
    lcdSetY(y1, y2);
    lcdSendCommand(0x2c);
    while (i--){ lcdSendPixel(colour); }
    LCD_CS_HI;
}

#define ICON_SZ     24

void lcdIcon(INT16 x1, INT16 y1, UINT16 fg, UINT16 bg, const UINT8 icon[])
{
    UINT16 i, j;
    UINT32 temp, mask;

    x1 = x1 - ICON_SZ / 2;
    y1 = y1 - ICON_SZ / 2;
    
    LCD_CS_LO;
    lcdSetX(x1, x1 + ICON_SZ - 1);
    lcdSetY(y1, y1 + ICON_SZ - 1);
    lcdSendCommand(0x2c);
     /* for each horizontal slice i */
    for(i = 0; i < ICON_SZ; i++){
        temp = icon[ICON_SZ / 8  * i];
        temp |= (icon[ICON_SZ / 8 * i + 1] << 8);
        temp |= (icon[ICON_SZ / 8 * i + 2] << 16);
        mask = 1;
        /* for each bit in the slice j */
        for(j = 0;  j < ICON_SZ; j++){
               if(mask & temp) { lcdSendPixel(fg); }
                else { lcdSendPixel(bg); }
               mask = mask << 1;
        }
    }
    LCD_CS_HI;
}

void lcdRender(INT16 x1, INT16 y1, UINT16 width, UINT16 height, UINT16 fg, UINT16 bg, const UINT16 * pglyph)
{
    UINT16 i, dy;
    UINT16 temp, mask;



    LCD_CS_LO;
    lcdSendCommand(0x36);               // Memory Access Control (MADCTL)
    lcdSendDataByte(0x28);              // - portrait, BGR = 1
    lcdSetY(x1, x1 + width - 1);
    lcdSetX(y1, y1 + height - 1);
    lcdSendCommand(0x2c);
     /* for each vertical slice i */
    for(i = 1; i <= width; i++){
            mask = 1;
            for(dy = 0; dy < height; dy++){
                if(mask & pglyph[i]) { lcdSendPixel(fg); }
                else { lcdSendPixel(bg); }
                mask = mask << 1;
            }
        }
    lcdSendCommand(0x36);               // Memory Access Control (MADCTL)
    lcdSendDataByte(0x08);              // - portrait, BGR = 1
    LCD_CS_HI;
}

void lcdSetBacklight(UINT8 level)
{
    if(level > 31) level = 31;
    OC1RS = level;
}
