/*
================================ COPYRIGHT ================================
The contents of this file are subject to the VTech Informations Ltd. License
of VT-OS Ver. 1.1 operating system (the "License"); you may not use this 
file except in compliance with the License.  

Software distributed under the License is distributed on an "AS IS" basis, 
WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the License 
for the specific language governing rights and limitations under the License.
  
The Original Code is VT-OS Ver. 1.1 Operating System, released 
on October 1st, 1999
	
The Initial Developer of the Original Code is VTech Informations Ltd.  All 
codes are Copyright (C) VTech Informations Ltd. 1999.  All Rights Reserved.
===========================================================================
*/

/*
===========================================================================
File        :   lcddrv.c
Author(s)   :   Brian Lee, Thomas Cheng
Company     :   VTech Informations Ltd.
Project     :   Helio 
Date:	    :   October 1st, 1999
Purpose:	:   LCD driver
Revision    :   1.1
Note        :   None
===========================================================================
*/              


/*==========The following is modified by Andrew Hui ==990812=========*/

#define LCD_CLK_36M     0
#define LCD_CLK_18M     1
#define LCD_CLK_9M      2
#define LCD_CLK_4M      3

#define VIDEO_RF        LCD_CLK_9M

#define A_LCD_CLK       ( 36864000/(1<<VIDEO_RF) )
#define A_LCD_BAUD      (0x6)               /* for 4.5MHz Lcd Clock only */
#define A_LCD_CP        (A_LCD_CLK/(A_LCD_BAUD*2+2))


/*==========The above is modified by Andrew Hui ==990812=========*/



/*	File		: LcdDrv.c
*	Company 	: VTech Info-Tech Ltd.
*	Project 	: PDA
*	Programmer	: Thomas Cheng
*	Date		: August 10, 1998
*	Revision	: 1.0
*				  1.1 (BL) speed optimize
*	Comment 	: LCD driver, which functions are hardware dependent
*/


#include "stdafx.h"
#include "LcdDrv.h"
#ifdef PR31700
#include "pr31700s.h"
#include "pr31700c.h"
#include "lcdapi.h"
#endif

extern BOOLEAN insert_pt_enable, insert_pt_on;
extern BitmapTemplate insert_pt_off_bmp;

/* local variables */
UWORD lcd_mem[LCD_SIZE];

WORD wordmask[8] = {0xffffff0f,0xfffffff0,0xffff0fff,0xfffff0ff,
0xff0fffff,0xfff0ffff,0x0fffffff,0xf0ffffff};
WORD wordshift[8] = {24,28,16,20,8,12,0,4};

/*	Function	: LcdInit
*	Purpose 	: Initialize LCD display
*	Scope		: OS
*	Input		: None
*	Output		: None
*	Comment 	:
*/
#define A_LCD_FR		  70
#define A_LCD_LINERATE	  (A_LCD_FR * (LCD_WIDTH +1 ))
#define A_LCD_VIDRATE	  ( (A_LCD_CP/A_LCD_LINERATE) -1 )

void LcdInit(void)
{
#ifdef PC_SIM
	// clear display memory
	memset(lcd_mem, (UBYTE)(0x0), LCD_WIDTH*LCD_HEIGHT/SIZE_OF_BYTE*BITS_OF_GREY);
#endif
	
#ifdef PR31700
	UWORD lcd_hi = (UWORD)lcd_mem & 0x0FFFFFFF, lcd_lo = lcd_hi + LCD_WIDTH * LCD_HEIGHT * 4 / 8;
	SHORT i, j;
	//	LcdTurnOff();
	
	
	/*==========The following is modified by Andrew Hui ==990812=========*/
    CPU->REG_POWER_CTRL &= ~VIDRF(3);
    CPU->REG_POWER_CTRL |= VIDRF(VIDEO_RF);
	/*==========The above is modified by Andrew Hui ==990812=========*/
	
	//    CPU->REG_VIDEO_CTRL_1 = BAUDVAL(A_LCD_BAUD) | VIDDONEVAL(0x10) | BITSEL(2);
    CPU->REG_VIDEO_CTRL_1 = BAUDVAL(A_LCD_BAUD) | VIDDONEVAL(0x4) | BITSEL(2);
	
	CPU->REG_VIDEO_CTRL_2 = VIDRATE(A_LCD_VIDRATE) | HORZVAL(LCD_WIDTH / 4 - 1) | LINEVAL(LCD_HEIGHT - 1);
	CPU->REG_VIDEO_CTRL_3 = VIDBANK(0x000) | VIDBASEHI(lcd_hi>>4);
	CPU->REG_VIDEO_CTRL_4 = FRAMEMASKVAL(0) | VIDBASELO(lcd_lo>>4);
	
    CPU->REG_VIDEO_CTRL_7 = 0xFA50;
	
    color_mode = BLACK_AND_WHITE_MODE;
	
    color_level[COLOR_BLACK_COLOR]  =   COLOR_GREY16_16;
    color_level[COLOR_GREY2_COLOR]  =   COLOR_GREY16_16;
    color_level[COLOR_GREY1_COLOR]  =   COLOR_GREY1_16;
    color_level[COLOR_WHITE_COLOR]  =   COLOR_GREY1_16;
	
    CPU->REG_VIDEO_CTRL_8 = 0xFFF;
    CPU->REG_VIDEO_CTRL_9 = 0xFFFF0000;
    CPU->REG_VIDEO_CTRL_10 = 0xFFFFF;
    CPU->REG_VIDEO_CTRL_11 = 0xFFFFF;
    CPU->REG_VIDEO_CTRL_12 = 0xFFFFFFF;
    CPU->REG_VIDEO_CTRL_13 = 0xFFFFFFF;
    CPU->REG_VIDEO_CTRL_14 = 0xFFFFFFF;
    
    CPU->REG_VIDEO_CTRL_8 = 0xFFF;
    CPU->REG_VIDEO_CTRL_9 = 0xFFFF0000;
    CPU->REG_VIDEO_CTRL_10 = 0xFFFFF;
    CPU->REG_VIDEO_CTRL_11 = 0xFFFFF;
    CPU->REG_VIDEO_CTRL_12 = 0xFFFFFFF;
    CPU->REG_VIDEO_CTRL_13 = 0xFFFFFFF;
    CPU->REG_VIDEO_CTRL_14 = 0xFFFFFFF;    
	
	LcdClr();
	LcdTurnOn();
#endif
	insert_pt_enable = FALSE;
	insert_pt_on = FALSE;
	insert_pt_off_bmp.bitmap_data = 0;
	rotation = 0;
}

/*	Function	: void LcdClr(void)
*	Purpose 	: Clear the display memory
*	Scope		: OS
*	Input		:
*	Output		:
*	Comment 	:
*/
void LcdClr(void)
{
	UWORD i;
	i=LCD_SIZE;
	while(i--) lcd_mem[i]=0;
}


#ifdef PR31700
/******************************************************************************
Prototype		  : 	void LcdTurnOn(void)
Purpose 		  : 	Turn on the LCD
Scope			  : 	OS
Input parameters  : 	none
Output parameters : 	none
Return value	  : 	none
******************************************************************************/

void LcdTurnOn(void)
{
	CPU->REG_VIDEO_CTRL_2 = VIDRATE(A_LCD_VIDRATE) | HORZVAL(LCD_WIDTH / 4 - 1) | LINEVAL(LCD_HEIGHT - 1);
	CPU->REG_CLK_CTRL |= ENVIDCLK;
	CPU->REG_VIDEO_CTRL_1 |= ENVID;
	CPU->REG_VIDEO_CTRL_1 |= DISPON;
	
	//AH990719	  CPU->REG_MFIO_SEL 	|= 0x00020000 | (1<<3); 	  //AH990719 should set
	//AH990719	  CPU->REG_MFIO_DIRECTN |= 0x00020000 | (1<<3);
	
	CPU->REG_MFIO_SEL	  |=  (1<<3);		//AH990719 should set
	CPU->REG_MFIO_DIRECTN |=  (1<<3);		//AH990719 should set
	
	//AH990919	  CPU->REG_MFIO_DATA_OUT &= ~0x00020000;
	CPU->REG_MFIO_DATA_OUT |= (1<<3);
}

/******************************************************************************
Prototype		  : 	void LcdTurnOff(void)
Purpose 		  : 	Turn off the LCD
Scope			  : 	OS
Input parameters  : 	none
Output parameters : 	none
Return value	  : 	none
******************************************************************************/

void LcdTurnOff(void)
{
	//AH990719	  CPU->REG_MFIO_SEL 	|= 0x00020000 | (1<<3);  //AH990719 should set
	//AH990719	  CPU->REG_MFIO_DIRECTN |= 0x00020000 | (1<<3);  //AH990719 should set
	
	CPU->REG_MFIO_SEL	  |= (1<<3);  //AH990719 should set
	CPU->REG_MFIO_DIRECTN |= (1<<3);  //AH990719 should set
	
	//AH990719	  CPU->REG_MFIO_DATA_OUT |= 0x00020000;
	
	CPU->REG_MFIO_DATA_OUT &= ~(1<<3);
	
	CPU->REG_VIDEO_CTRL_2 = VIDRATE(A_LCD_VIDRATE) | HORZVAL(LCD_WIDTH / 4 - 1) | LINEVAL(LCD_HEIGHT+1);
	
	//	  TmrWaitTime(50);
	while ( ((CPU->REG_VIDEO_CTRL_1 >> 22) & 0x3FF ) != 160 ) ;
	while ( ((CPU->REG_VIDEO_CTRL_1 >> 22) & 0x3FF ) != 0 ) ;
	
	CPU->REG_VIDEO_CTRL_1 &= ~ENVID;
	CPU->REG_CLK_CTRL &= ~ENVIDCLK;
	CPU->REG_VIDEO_CTRL_1 &= ~DISPON;
	/*
	while (1)
	{
	if ((CPU->REG_VIDEO_CTRL_1 >> 22) & 0x3FF == 161)
	{
	CPU->REG_VIDEO_CTRL_1 &= ~ENVID;
	CPU->REG_CLK_CTRL &= ~ENVIDCLK;
	CPU->REG_VIDEO_CTRL_1 &= ~DISPON;
	break;
	}
	}
	*/
}
#endif	/* PR31700 */

/*	Function	: WordPack
*	Purpose 	: Pack two words into one refering to individual shifts and
*				  the merge point
*	Scope		: Internal
*	Input		: Two words with individual shifts (0-8), merge point (0-8)
*	Output		: Packed word
*	Comment 	: p(0:31) = w1(s1*bg:s1*bg+mp*bg-1) || w2(mp*bg-s2*bg:31-s2*bg)
*/
UWORD WordPack(UWORD word1, BYTE word1_shift, UWORD word2, BYTE word2_shift,
			   BYTE merge_pt)
{
	UWORD word_mask;
	if (merge_pt == SIZE_OF_WORD / BITS_OF_GREY)
		word_mask = 0;
	else
		word_mask = WORD_MASK >> (merge_pt*BITS_OF_GREY);
	return ((word1 << (word1_shift*BITS_OF_GREY)) & ~word_mask)
		| ((word2 >> (word2_shift*BITS_OF_GREY)) & word_mask);
}

/*	Function	: WordHead1Pack
*	Purpose 	: Pack two words into one refering to the merge point assuming
*				  latter word always starts packing from its head
*	Scope		: Internal
*	Input		: Two words and merge point
*	Output		: Packed word
*	Comment 	: p(0:31) = w1(0:mp*bg-1) || w2(0:31-mp*bg)
*/
UWORD WordHead1Pack(UWORD word1, UWORD word2, BYTE merge_pt)
{
	return WordPack(word1, 0, word2, merge_pt, merge_pt);
}

/*	Function	: WordHead2Pack
*	Purpose 	: Pack two words into one refering to two merge points assuming
*				  latter word always starts packing from its head and possibly
*				  ends packing in the middle
*	Scope		: Internal
*	Input		: Two words and two merge points
*	Output		: Packed word
*	Comment 	: p(0:31) = w1(0:mp1*bg-1) || w2(0:(mp2-mp1)*bg-1) || w1(mp2*bg:31)
*/
UWORD WordHead2Pack(UWORD word1, UWORD word2, BYTE merge_pt1, BYTE merge_pt2)
{
	word2 = WordHead1Pack(word1, word2, merge_pt1);
	return WordPack(word2, 0, word1, 0, merge_pt2);
}

/*	Function	: WordBodyPack
*	Purpose 	: Pack two words into one refering to the merge point assuming
*				  former word always ends packing to its tail and latter word
*				  always starts packing from its head
*	Scope		: Internal
*	Input		: Two words and merge point
*	Output		: Packed word
*	Comment 	: p(0:31) = w1(31-mp*bg:31) || w2(0:31-mp*bg)
*/
UWORD WordBodyPack(UWORD word1, UWORD word2, BYTE merge_pt)
{
	return WordPack(word1, SIZE_OF_WORD-merge_pt, word2, merge_pt, merge_pt);
}

/*	Function	: WordTail1Pack
*	Purpose 	: Pack two words into one refering to former word's shift and
*				  the merge point
*	Scope		: Internal
*	Input		: Two words with former word's shift and merge point
*	Output		: Packed word
*	Comment 	: p(0:31) = w1(s1*bg:(s1+mp)*bg-1) || w2(mp*bg:31)
*/
UWORD WordTail1Pack(UWORD word1, BYTE word1_shift, UWORD word2, BYTE merge_pt)
{
	return WordPack(word1, word1_shift, word2, 0, merge_pt);
}

/*	Function	: WordTail2Pack
*	Purpose 	: Pack three words into one refering to two merge points assuming
*				  former word always ends packing to its tail and middle word
*				  always starts packing from its head and possibly ends packing
*				  in the middle
*	Scope		: Internal
*	Input		: Three words and two merge points
*	Output		: Packed word
*	Comment 	: p(0:31) = w1(mp1*bg:31-mp1*bg) || w2(0:(mp2-mp1)*bg-1) || w3(mp2*bg:31)
*/
UWORD WordTail2Pack(UWORD word1, UWORD word2, UWORD word3, BYTE merge_pt1,
					BYTE merge_pt2)
{
	word2 = WordBodyPack(word1, word2, merge_pt1);
	return WordPack(word2, 0, word3, 0, merge_pt2);
}

/*	Function	: WordSwap
*	Purpose 	: Reorder the word from big-endian to little-endian or vice
*				  versa since they are mathematically equivalent
*	Scope		: Internal
*	Input		: One word
*	Output		: Swaped word
*	Comment 	: p(0:31) = w(24:31) || w(16:23) || w(8:15) || w(0:7)
*/
/*UWORD WordSwap(UWORD word1)
{
return (word1 << 24) | ((word1 & 0x0000FF00) << 8)
| ((word1 & 0x00FF0000) >> 8) | (word1 >> 24);
}
*/
/*	Function	: PixelRead
*	Purpose 	: Read the color of pixel at the specified location
*	Scope		: Internal
*	Input		: Cartesian coordinate
*	Output		: Color
*	Comment 	: If the location is outside the screen region, return COLOR_NIL
*/
BYTE PixelRead(SHORT xcoord, SHORT ycoord)
{
	WORD *pixel_addr;
	WORD xr, yr;
	
	if (ChkWithinScreen()) {
		if (rotation == 0) {
			xr = xcoord;
			yr = ycoord;
		}
		else {
			xr = 159-ycoord;
			yr = xcoord;
		}
		return((BYTE)(lcd_mem[CoordToWordPos(xr, yr)] >> wordshift[7-xr&7]) & 0xf);
	}
	else
		return 0;
}

/*	Function	: PixelWrite
*	Purpose 	: Write the color of pixel at the specified location
*	Scope		: Internal
*	Input		: Cartesian coordinate and color
*	Output		: None
*	Comment 	: If the location is outside the screen region, do nothing
*/
void PixelWrite(SHORT xcoord, SHORT ycoord, BYTE color)
{
	WORD *pixel_addr;
	WORD xr, yr;
	
	if (ChkWithinScreen()) {
		if (rotation == 0) {
			xr = xcoord;
			yr = ycoord;
		}
		else {
			xr = 159-ycoord;
			yr = xcoord;
		}
		pixel_addr = &lcd_mem[CoordToWordPos(xr, yr)];
		*pixel_addr = (*pixel_addr & wordmask[xr&7]) | ((WORD)color << wordshift[7-xr&7]);
	}
}

/*	Function	: WordRead
*	Purpose 	: Read the color of pixels in word form at the specified location
*	Scope		: Internal
*	Input		: Cartesian coordinate
*	Output		: Color in word form
*	Comment 	: If the location is outside the screen region, return COLOR_WHITE
*/
UWORD WordRead(SHORT xcoord, SHORT ycoord)
{
	WORD xr, yr, shift, mask;
	UWORD data = 0;
	WORD *pixel_addr;
	
	if (ChkWithinScreen()) {
		if (rotation == 0) {
			return EL32(lcd_mem[CoordToWordPos(xcoord, ycoord)]);
		}
		else {
			yr = (WORD)xcoord & 0xFFFFFFF8;
			xr = 159-ycoord;
			pixel_addr = &lcd_mem[CoordToWordPos(xr, yr)];
			shift = wordshift[7-xr&7];
			
			xr = 8;
			do {
				data <<= 4;
				data += ((*pixel_addr >> shift) & 0xf);
				pixel_addr += 20;
			} while (--xr);
			return data;
		}
	}
	else
		return 0;
}

/*	Function	: WordWrite
*	Purpose 	: Write the color of pixels in word form at the specified location
*	Scope		: Internal
*	Input		: Cartesian coordinate, color in word form
*	Output		: None
*	Comment 	: If the location is outside the screen region, do nothing
*/
void WordWrite(SHORT xcoord, SHORT ycoord, UWORD data)
{
	WORD xr, yr, shift, mask;
	WORD *pixel_addr;
	
	if (ChkWithinScreen()) {
		if (rotation == 0) {
			pixel_addr = &lcd_mem[CoordToWordPos(xcoord, ycoord)];
			*pixel_addr = EL32(data);
		}
		else {
			yr = (WORD)xcoord & 0xFFFFFFF8;
			xr = 159-ycoord;
			pixel_addr = &lcd_mem[CoordToWordPos(xr, yr)];
			mask = wordmask[xr&7];
			shift = wordshift[xr&7];
			
			xr = 8;
			do {
				*pixel_addr = (*pixel_addr & mask) | (data & 0xf0000000) >> shift;
				data <<= 4;
				pixel_addr += 20;
			} while (--xr);
		}
	}
}

/*===========================================================================
Author(s)   : Henry Fok
Function	: LcdGetLcdMemAddress                                    
Purpose 	: To get the address of the memory array of variable lcd_mem
Scope		: Application
Input		: None
Output		: None
Return		: address of lcd_mem                                                            
Comment 	: None
===========================================================================*/
UWORD *LcdGetLcdMemAddress()
{
	return (UWORD*)lcd_mem;
}	