//ys_drv_gfx_int018.c
//drv_gfx_generic.c
//INT018 Display Driver
// 2018.3.27 by YSDK Design
///////////////////////////////////////////////////////////////////


#include "framework/gfx/driver/controller/generic/drv_gfx_generic.h"
#include "gfx/hal/inc/gfx_driver_interface.h"
#include "gfx/hal/inc/gfx_default_impl.h"
#include "framework/driver/pmp/drv_pmp_static.h"
#include "system_definitions.h"

#define MAX_LAYER_COUNT 1
#define MAX_BUFFER_COUNT 1

static uint32_t supportedColorModes = GFX_COLOR_MASK_RGB_565;
const char* DRIVER_NAME = "Generic";

const unsigned short ys_DISP_HOR_RESOLUTION = 160;
const unsigned short ys_DISP_VER_RESOLUTION = 128;

int ys_DISP_ORIENTATION;

int ys_maxX; //
int ys_maxY; //

int INT018_delay_Clock = 200000000; //200MHz


void INT018_delay_us(volatile unsigned int usec) //1usec delay
{
volatile int count;

count = (int)(INT018_delay_Clock/20000000)*usec;
do //actual survey: at 200MH (Clock=200000000)
{ //delay_us(1000):1000.4usec delay_us(100):100.6usec delay_us(10):10.5usec delay_us(1):1.5usec
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
count--;
}while(count != 0);
}

void INT018_delay_ms(volatile unsigned int msec) //1msec delay
{
volatile unsigned int i; //actual survey: at200MH (Clock=200000000)//delay_ms(1): 1.0006msec delay_ms(100):100.04msec

for(i=0; i<msec; i++)
INT018_delay_us(1000);
}




void ClearDevice(void) //全画面黒色にセット
{
int color = 0x0000; //黒
// int BackColor = 0x001f; //青
// int FrontColor = 0xffe0; //黄

unsigned short tempX = ys_DRV_GFX_INT018_MaxXGet(); //★★★ 直接の代入回避
unsigned short tempY = ys_DRV_GFX_INT018_MaxYGet();

// ys_fillRect(0, 0, 100, 100, BackColor); //青色 矩形 W100 x H100
//
// ys_pixelSet(50, 50, FrontColor); //黄色 ピクセル (50,50)
// ys_pixelSet(51, 51, FrontColor);
// ys_pixelSet(50, 51, FrontColor);
// ys_pixelSet(51, 50, FrontColor);

ys_fillRect(0, 0, tempX, tempY, color);
}


unsigned short ys_DRV_GFX_INT018_IsDeviceBusy(void) //ビジーチェック
{
//Check the wait pin
return (DisplayWaitPin() == 0);
}




//補助関数

//PMP書き込み関数
void DRV_GFX_INT018_DeviceWrite(unsigned char data)
{
PMDIN = data; //PMPへデータ書き込み
while(PMMODEbits.BUSY); //PMMODEレジスタのBUSYビットが0になるまで待つ
}



//PMP読み込み関数
unsigned char ys_DRV_GFX_INT018_DeviceRead(void)
{
unsigned char value;

value = PMDIN;
while(PMMODEbits.BUSY); //PMMODEレジスタのBUSYビットが0になるまで待つ
PMCONbits.PMPEN = 0; // disable PMP
value = PMDIN;
PMCONbits.PMPEN = 1; // enable PMP
return value;
}

//PMP書き込み関数
void ys_DRV_GFX_INT018_WriteData(unsigned char cmd)
{
DisplayEnable(); //Set the CS Low
DisplaySetData(); //Set the RS low
DRV_GFX_INT018_DeviceWrite(cmd); //Dump data on the PMP
DisplayDisable(); //Set the CS High
}

//コマンド書き込み関数
void ys_DRV_GFX_INT018_WriteCommand(unsigned char cmd)
{
DisplayEnable(); //Set the CS Low
DisplaySetCommand(); //Set the RS high
DRV_GFX_INT018_DeviceWrite(cmd); //Send PMP data
DisplayDisable(); //Set the CS High
}

//データ書き込み関数
unsigned char ys_DRV_GFX_INT018_ReadData(void)
{
unsigned char data;

DisplayEnable(); //Set the CS low
DisplaySetData(); //Set the RS low
data = ys_DRV_GFX_INT018_DeviceRead(); //Read one unsigned char of data
DisplayDisable(); //Set the CS high

return data;
}

//状態読み込み関数
unsigned char ys_DRV_GFX_INT018_ReadStatus(void)
{
unsigned char status;

DisplayEnable(); //Set the CS low
DisplaySetCommand(); //Set the RS high
status = ys_DRV_GFX_INT018_DeviceRead(); //Read one unsigned char of data
DisplayDisable(); //Set the CS high

return status;
}

//レジスタ読み込み関数
unsigned char ys_DRV_GFX_INT018_ReadRegister(unsigned char reg)
{
unsigned char dat;
ys_DRV_GFX_INT018_WriteCommand(reg);
dat = ys_DRV_GFX_INT018_ReadData();
return dat; //ys added
}

//レジスタ書き込み関数
void ys_DRV_GFX_INT018_WriteRegister(unsigned char reg, unsigned char dat)
{
ys_DRV_GFX_INT018_WriteCommand(reg);
ys_DRV_GFX_INT018_WriteData(dat);
}

//アクティブウィンドウセット関数
void ys_DRV_GFX_INT018_ActiveWindow(unsigned short XL, unsigned short XR, unsigned short YT, unsigned short YB)
{
unsigned short swap;

//Some error checking
if(XL > XR)
{
swap = XL;
XL = XR;
XR = swap;
}
else if (YT > YB)
{
swap = YT;
YT = YB;
YB = swap;
}

//setting active window X
ys_DRV_GFX_INT018_WriteRegister(HSAW0,(unsigned char)(XL));
ys_DRV_GFX_INT018_WriteRegister(HSAW1,(unsigned char)(XL >> 8) & 0x03);
ys_DRV_GFX_INT018_WriteRegister(HEAW0,(unsigned char)(XR));
ys_DRV_GFX_INT018_WriteRegister(HEAW1,(unsigned char)(XR >> 8) & 0x03);

//setting active window Y
ys_DRV_GFX_INT018_WriteRegister(VSAW0,(unsigned char)(YT));
ys_DRV_GFX_INT018_WriteRegister(VSAW1,(unsigned char)(YT >> 8) & 0x01);
ys_DRV_GFX_INT018_WriteRegister(VEAW0,(unsigned char)(YB));
ys_DRV_GFX_INT018_WriteRegister(VEAW1,(unsigned char)(YB >> 8) & 0x01);
}

//ピクセルセット関数
void ys_DRV_GFX_INT018_XYCoordinate(unsigned short X, unsigned short Y)
{
//Set the X coordinate of the memory cursor
ys_DRV_GFX_INT018_WriteRegister(CURH0, (unsigned char)X);
ys_DRV_GFX_INT018_WriteRegister(CURH1, (unsigned char)(X>>8));

//Set the Y coordinate of the memory cursor
ys_DRV_GFX_INT018_WriteRegister(CURV0, (unsigned char)Y);
ys_DRV_GFX_INT018_WriteRegister(CURV1, (unsigned char)(Y>>8));
}


//液晶のx幅を取得

uint16_t ys_DRV_GFX_INT018_MaxXGet()
{
return ys_maxX;
}


//液晶のY幅を取得

uint16_t ys_DRV_GFX_INT018_MaxYGet()
{
return ys_maxY;
}



//-----------------------------------------------------------------------------
// 初期化関数 //Initialize function
static GFX_Result initialize(GFX_Context* context)
{
DisplayResetDisable(); // disable the reset by default
DisplayResetConfig(); // enable RESET line
DisplayCmdDataConfig(); // enable RS line
DisplayDisable(); // not selected by default
DisplayConfig(); // enable chip select line
DisplayWaitConfig(); //Set up the wait pin
DisplayWaitConfig2(); //デジタルピンに設定
// DisplayIntConfig(); //Set up the int pin

/////////////////////////////////////////////////////////////////////
// Initialize the device
/////////////////////////////////////////////////////////////////////

INT018_delay_ms(500);

//Set the device for 16-bit data width (5-6-5)
ys_DRV_GFX_INT018_WriteRegister(SYSR,0x08); //16ビットカラーにセット

//液晶解像度設定
if( (ys_DISP_ORIENTATION == 90) || (ys_DISP_ORIENTATION == 270))
{
ys_maxX = ys_DISP_VER_RESOLUTION - 1;
ys_maxY = ys_DISP_HOR_RESOLUTION - 1;
}
else //通常(ys_DISP_ORIENTATION = 0)
{
ys_maxX = ys_DISP_HOR_RESOLUTION - 1;
ys_maxY = ys_DISP_VER_RESOLUTION - 1;
}


//Set the horizontal and vertical resolutions
ys_DRV_GFX_INT018_WriteRegister(HDWR,(unsigned char)(ys_DISP_HOR_RESOLUTION/8-1));
ys_DRV_GFX_INT018_WriteRegister(VDHR0,(unsigned char)(ys_DISP_VER_RESOLUTION-1));
ys_DRV_GFX_INT018_WriteRegister(VDHR1,(unsigned char)((ys_DISP_VER_RESOLUTION-1)>>8));

//Set rotations and horizontal write directions for
//different displays

if (ys_DISP_ORIENTATION == 0)
{
if(ys_DISP_HOR_RESOLUTION == 160)
ys_DRV_GFX_INT018_WriteRegister(DPCR,ROT_270_MASK|HDIR_MASK);
else ys_DRV_GFX_INT018_WriteRegister(DPCR,HDIR_MASK|ROT_90_MASK);
}
else if(ys_DISP_ORIENTATION == 90)
{
if (ys_DISP_HOR_RESOLUTION == 160)
{
//#define DPCR 0x20 //Display Configuration Register

}
else ys_DRV_GFX_INT018_WriteRegister(DPCR,ROT_90_MASK|HDIR_MASK);

}


//Turn on the display
ys_DRV_GFX_INT018_WriteRegister(PWRR,0x80);

/////////////////////////////////////////////////////////////////////
// Clear the display buffer with all zeros so the display will not
// show garbage data when initially powered up
/////////////////////////////////////////////////////////////////////
//ys_DRV_GFX_INT018_SetColor(BLACK);

ClearDevice();

return GFX_SUCCESS;

}


//------------------------------------------------------------------------------
//ピクセル描画 //Pixel set function
void ys_pixelSet(int x, int y, int color)
{
while (ys_DRV_GFX_INT018_IsDeviceBusy() != 0); //ビジーが終了するまで待つ

//Select the pixel
if(ys_DISP_ORIENTATION == 0)
ys_DRV_GFX_INT018_XYCoordinate(x,y);
else if(ys_DISP_ORIENTATION == 90)
ys_DRV_GFX_INT018_XYCoordinate(y,x);

ys_DRV_GFX_INT018_WriteCommand(MRWC);
ys_DRV_GFX_INT018_WriteData((unsigned char)(color>>8));
ys_DRV_GFX_INT018_WriteData((unsigned char)color);
}


static GFX_Result pixelSet(const GFX_PixelBuffer* buf,
const GFX_Point* pnt,
GFX_Color color)
{
int x = pnt->x;
int y = pnt->y;

ys_pixelSet(x, y, color);

return GFX_SUCCESS;
}


//------------------------------------------------------------------------------
//直線描画 //Line draw function
//Bresenham's line algorithm

void ys_drawLine(int x0, int y0, int x1, int y1, int color)
{
// short steep, t;
// short deltax, deltay, error;
// short x, y;
// short ystep;
//
//
// y0=DISP_VER_RESOLUTION-y0 -1;
// y1=DISP_VER_RESOLUTION-y1 -1;
// steep = (abs(y1 - y0) > abs(x1 - x0));
// if(steep){
// t = x0; x0 = y0; y0 = t;
// t = x1; x1 = y1; y1 = t;
// }
// if(x0 > x1) {
// t = x0; x0 = x1; x1 = t;
// t = y0; y0 = y1; y1 = t;
// }
// deltax = x1 - x0;
// deltay = abs(y1 - y0);
// error = 0;
// y = y0;
// if(y0 < y1) ystep = 1; else ystep = -1;
// for(x=x0; x<=x1; x++)
// {
// if(steep) ys_pixelSet(y,x,color); else ys_pixelSet(x,y,color);
// error += deltay;
// if((error << 1) >= deltax) {
// y += ystep;
// error -= deltax;
// }
// }

}

GFX_Result drawLine(const GFX_Point* p0,
const GFX_Point* p1,
const GFX_DrawState* state)
{
int color = state->color;
int x0 = p0->x;
int y0 = p0->y;
int x1 = p1->x;
int y1 = p1->y;

ys_drawLine(x0, y0, x1, y1, color);

return(GFX_SUCCESS);
}

//------------------------------------------------------------------------------
//矩形描画 //Rectangle fill functin
void ys_fillRect(int left, int top, int right, int bottom, int color)
{

while (ys_DRV_GFX_INT018_IsDeviceBusy() != 0); //ビジーが終了するまでまつ


if(ys_DISP_ORIENTATION == 90)
{
ys_DRV_GFX_INT018_WriteRegister(DPCR,HDIR_MASK|ROT_90_MASK); //必須
}
else
ys_DRV_GFX_INT018_WriteRegister(DPCR,ROT_270_MASK|HDIR_MASK);

//If this is only one pixel, call the PutPixel() function
if(left == right && top == bottom)
ys_pixelSet(left, right, color);
else
{
//Set the active window

if(ys_DISP_ORIENTATION == 0)
ys_DRV_GFX_INT018_ActiveWindow(left,right,top,bottom);
else ys_DRV_GFX_INT018_ActiveWindow(top,bottom,left,right);

//Set the BTE background
ys_DRV_GFX_INT018_WriteRegister(BGCR0,(unsigned char)(color>>11));//Red
ys_DRV_GFX_INT018_WriteRegister(BGCR1,(unsigned char)(color>>5)); //Green
ys_DRV_GFX_INT018_WriteRegister(BGCR2,(unsigned char)(color)); //Blue

//Clear to the BTE background
ys_DRV_GFX_INT018_WriteRegister(MCLR,0xC0);
}

//Wait for the BTE to complete
while(ys_DRV_GFX_INT018_IsDeviceBusy());

//Reset the active window
if(ys_DISP_ORIENTATION == 0)
ys_DRV_GFX_INT018_ActiveWindow(0,ys_DRV_GFX_INT018_MaxXGet(),0,ys_DRV_GFX_INT018_MaxYGet());
else if(ys_DISP_ORIENTATION == 90)
ys_DRV_GFX_INT018_ActiveWindow(0,ys_DRV_GFX_INT018_MaxYGet(),0,ys_DRV_GFX_INT018_MaxXGet());

}


GFX_Result fillRect(const GFX_Rect* rect,
const GFX_DrawState* state)
{
int color = state->color;

int left = rect->x;
int top = rect->y;
int right = left + rect->width;
int bottom = top + rect->height;


ys_fillRect(left, top, right, bottom, color);

return(GFX_SUCCESS);
}

//----------------------------------------------------------------------------------
//円描画 //Circle draw functin
void ys_drawCircle(int x0, int y0, int r, int color) //(x_center, y_center, radius, color)
{
int x = r;
int y = 0;
int F = -2 * r + 3;

while(x >= y){
ys_pixelSet(x0+x, y0+y, color);
ys_pixelSet(x0-x, y0+y, color);
ys_pixelSet(x0+x, y0-y, color);
ys_pixelSet(x0-x, y0-y, color);
ys_pixelSet(x0+y, y0+x, color);
ys_pixelSet(x0-y, y0+x, color);
ys_pixelSet(x0+y, y0-x, color);
ys_pixelSet(x0-y, y0-x, color);
if(F >= 0){
x--;
F -= 4 * x;
}
y++;
F += 4 * y + 2;
}
}


static void destroy(GFX_Context* context)
{
// driver specific shutdown tasks
if(context->driver_data != GFX_NULL)
{
context->memory.free(context->driver_data);
context->driver_data = GFX_NULL;
}

// general default shutdown
defDestroy(context);
}


static GFX_Result layerActiveSet(uint32_t idx)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerEnabledSet(GFX_Bool val)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerPositionSet(int32_t x, int32_t y)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerSizeSet(int32_t width, int32_t height)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferCountSet(uint32_t count)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferAddressSet(uint32_t idx, GFX_Buffer address)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferCoherentSet(uint32_t idx, GFX_Bool coherent)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferAllocate(uint32_t idx)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferFree(uint32_t idx)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerVisibleSet(GFX_Bool val)
{
return GFX_UNSUPPORTED;
}

static GFX_Result layerAlphaEnableSet(GFX_Bool enable)
{
return GFX_UNSUPPORTED;
}

static GFX_Color pixelGet(const GFX_PixelBuffer* buf,
const GFX_Point* pnt)
{
return 0;
}

// function that returns the information for this driver
GFX_Result driverGenericInfoGet(GFX_DriverInfo* info)
{
if(info == GFX_NULL)
return GFX_FAILURE;

// populate info struct
strcpy(info->name, DRIVER_NAME);
info->color_formats = supportedColorModes;
info->layer_count = MAX_LAYER_COUNT;

return GFX_SUCCESS;
}

// function that initialized the driver context
GFX_Result driverGenericContextInitialize(GFX_Context* context)
{
// set driver-specific function implementations
context->hal.initialize = &initialize;
context->hal.destroy = &destroy;
// context->hal.brightnessRangeGet = &brightnessRangeGet;
// context->hal.brightnessSet = &brightnessSet;
// context->hal.layerVsyncSet = &vsyncSet;
// context->hal.vsyncCallbackSet = &vsyncCallbackSet;
// context->hal.hsyncCallbackSet = &hsyncCallbackSet;
context->hal.layerActiveSet = &layerActiveSet;
context->hal.layerEnabledSet = &layerEnabledSet;
context->hal.layerPositionSet = &layerPositionSet;
context->hal.layerSizeSet = &layerSizeSet;
context->hal.layerBufferCountSet = &layerBufferCountSet;
context->hal.layerBufferAddressSet = &layerBufferAddressSet;
context->hal.layerBufferCoherentSet = &layerBufferCoherentSet;
context->hal.layerBufferAllocate = &layerBufferAllocate;
context->hal.layerBufferFree = &layerBufferFree;
context->hal.layerVisibleSet = &layerVisibleSet;
context->hal.layerAlphaEnableSet = &layerAlphaEnableSet;

context->hal.drawPipeline[GFX_PIPELINE_GCU].pixelSet = &pixelSet;
context->hal.drawPipeline[GFX_PIPELINE_GCU].pixelGet = &pixelGet;

context->hal.drawPipeline[GFX_PIPELINE_GCUGPU].pixelSet = &pixelSet;
context->hal.drawPipeline[GFX_PIPELINE_GCUGPU].pixelGet = &pixelGet;


//Mchip commentout
// accelerated draw line disabled for now, something keeps cutting off pixels
// towards the bottom of the screen in some cases
context->hal.drawPipeline[GFX_PIPELINE_GCU].drawLine[GFX_DRAW_LINE][GFX_ANTIALIAS_OFF] = &drawLine;

context->hal.drawPipeline[GFX_PIPELINE_GCU].drawRect[GFX_DRAW_FILL][GFX_ANTIALIAS_OFF] = &fillRect;
context->hal.drawPipeline[GFX_PIPELINE_GCUGPU].drawRect[GFX_DRAW_FILL][GFX_ANTIALIAS_OFF] = &fillRect;

return GFX_SUCCESS;
}