Sansa Fuze: LCD

- add lcd_set_invert()
- add lcd_set_flip() commented out since not working fine yet:
  mpegplayer isn't flipped, and statusbar display is buggy
- use the registers list from e200v2
- cosmetics (indentation fix, comments changes, function names, casts,
  function moves, ...) to make the diff with lcd-e200v2.c shorter

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24181 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Rafaël Carré 2010-01-04 21:35:26 +00:00
parent 2895b659c8
commit 98fa13cb29
2 changed files with 197 additions and 113 deletions

View file

@ -62,7 +62,7 @@
//#define HAVE_LCD_FLIP //#define HAVE_LCD_FLIP
/* define this if you can invert the colours on your LCD */ /* define this if you can invert the colours on your LCD */
//#define HAVE_LCD_INVERT #define HAVE_LCD_INVERT
/* define this if you have a real-time clock */ /* define this if you have a real-time clock */

View file

@ -31,22 +31,68 @@
/* The controller is unknown, but some registers appear to be the same as the /* The controller is unknown, but some registers appear to be the same as the
HD66789R */ HD66789R */
#define R_ENTRY_MODE 0x03
#define R_RAM_ADDR_SET 0x21
#define R_WRITE_DATA_2_GRAM 0x22
#define R_ENTRY_MODE_HORZ 0x7030
#define R_ENTRY_MODE_VERT 0x7038
static unsigned lcd_yuv_options = 0;
static bool display_on = false; /* is the display turned on? */ static bool display_on = false; /* is the display turned on? */
static bool display_flipped = false;
static int xoffset = 20; /* needed for flip */ /* register defines */
/* we need to write a red pixel for correct button reads #define R_START_OSC 0x00
* (see lcd_button_support()),but that must not happen while the lcd is updating #define R_DRV_OUTPUT_CONTROL 0x01
* so block lcd_button_support the during updates */ #define R_DRV_WAVEFORM_CONTROL 0x02
static volatile int lcd_busy = false; #define R_ENTRY_MODE 0x03
#define R_COMPARE_REG1 0x04
#define R_COMPARE_REG2 0x05
#define R_DISP_CONTROL1 0x07
#define R_DISP_CONTROL2 0x08
#define R_DISP_CONTROL3 0x09
#define R_FRAME_CYCLE_CONTROL 0x0b
#define R_EXT_DISP_IF_CONTROL 0x0c
#define R_POWER_CONTROL1 0x10
#define R_POWER_CONTROL2 0x11
#define R_POWER_CONTROL3 0x12
#define R_POWER_CONTROL4 0x13
#define R_RAM_ADDR_SET 0x21
#define R_WRITE_DATA_2_GRAM 0x22
#define R_GAMMA_FINE_ADJ_POS1 0x30
#define R_GAMMA_FINE_ADJ_POS2 0x31
#define R_GAMMA_FINE_ADJ_POS3 0x32
#define R_GAMMA_GRAD_ADJ_POS 0x33
#define R_GAMMA_FINE_ADJ_NEG1 0x34
#define R_GAMMA_FINE_ADJ_NEG2 0x35
#define R_GAMMA_FINE_ADJ_NEG3 0x36
#define R_GAMMA_GRAD_ADJ_NEG 0x37
#define R_GAMMA_AMP_ADJ_RES_POS 0x38
#define R_GAMMA_AMP_AVG_ADJ_RES_NEG 0x39
#define R_GATE_SCAN_POS 0x40
#define R_VERT_SCROLL_CONTROL 0x41
#define R_1ST_SCR_DRV_POS 0x42
#define R_2ND_SCR_DRV_POS 0x43
#define R_HORIZ_RAM_ADDR_POS 0x44
#define R_VERT_RAM_ADDR_POS 0x45
/* Flip Flag */
#define R_ENTRY_MODE_HORZ_NORMAL 0x7030
#define R_ENTRY_MODE_HORZ_FLIPPED 0x7000
static unsigned short r_entry_mode = R_ENTRY_MODE_HORZ_NORMAL;
#define R_ENTRY_MODE_VERT 0x7038
#define R_ENTRY_MODE_SOLID_VERT 0x1038
/* FIXME */
#define R_ENTRY_MODE_VIDEO_NORMAL 0x7038
#define R_ENTRY_MODE_VIDEO_FLIPPED 0x7018
/* Reverse Flag */
#define R_DISP_CONTROL_NORMAL 0x0004
#define R_DISP_CONTROL_REV 0x0000
static unsigned short r_disp_control_rev = R_DISP_CONTROL_NORMAL;
static const int xoffset = 20;
static volatile bool lcd_busy = false;
static inline void lcd_delay(int x) static inline void lcd_delay(int x)
{ {
@ -54,7 +100,7 @@ static inline void lcd_delay(int x)
asm volatile ("nop\n"); asm volatile ("nop\n");
} while (x--); } while (x--);
} }
static void as3525_dbop_init(void) static void as3525_dbop_init(void)
{ {
CGU_DBOP = (1<<3) | AS3525_DBOP_DIV; CGU_DBOP = (1<<3) | AS3525_DBOP_DIV;
@ -69,6 +115,7 @@ static void as3525_dbop_init(void)
GPIOC_AFSEL = 0xff; GPIOC_AFSEL = 0xff;
DBOP_TIMPOL_23 = 0x6000e; DBOP_TIMPOL_23 = 0x6000e;
/* short count: 16|enable write|output data width: 16|read strobe line */ /* short count: 16|enable write|output data width: 16|read strobe line */
DBOP_CTRL = (1<<18|1<<16|1<<12|1<<3); DBOP_CTRL = (1<<18|1<<16|1<<12|1<<3);
DBOP_TIMPOL_01 = 0x6e167; DBOP_TIMPOL_01 = 0x6e167;
@ -94,7 +141,6 @@ static void lcd_write_cmd(int cmd)
/* Wait for fifo to empty */ /* Wait for fifo to empty */
while ((DBOP_STAT & (1<<10)) == 0); while ((DBOP_STAT & (1<<10)) == 0);
/* This loop is unique to the Fuze */
lcd_delay(4); lcd_delay(4);
DBOP_TIMPOL_23 = 0xa167e06f; DBOP_TIMPOL_23 = 0xa167e06f;
@ -105,12 +151,12 @@ void lcd_write_data(const fb_data* p_bytes, int count)
const long *data; const long *data;
if ((int)p_bytes & 0x3) if ((int)p_bytes & 0x3)
{ /* need to do a single 16bit write beforehand if the address is { /* need to do a single 16bit write beforehand if the address is
* not word aligned*/ * not word aligned */
lcd_write_value16(*p_bytes); lcd_write_value16(*p_bytes);
count--;p_bytes++; count--;p_bytes++;
} }
/* from here, 32bit transfers are save */ /* from here, 32bit transfers are save
/* set it to transfer 4*(outputwidth) units at a time, * set it to transfer 4*(outputwidth) units at a time,
* if bit 12 is set it only does 2 halfwords though */ * if bit 12 is set it only does 2 halfwords though */
DBOP_CTRL |= (1<<13|1<<14); DBOP_CTRL |= (1<<13|1<<14);
lcd_delay(10); lcd_delay(10);
@ -129,92 +175,128 @@ void lcd_write_data(const fb_data* p_bytes, int count)
/* due to the 32bit alignment requirement or uneven count, /* due to the 32bit alignment requirement or uneven count,
* we possibly need to do a 16bit transfer at the end also */ * we possibly need to do a 16bit transfer at the end also */
if (count > 0) if (count > 0)
lcd_write_value16(*(unsigned short*)data); lcd_write_value16(*(fb_data*)data);
} }
static void lcd_write_reg(int reg, int value) static void lcd_write_reg(int reg, int value)
{ {
unsigned short data = value; fb_data data = value;
lcd_write_cmd(reg); lcd_write_cmd(reg);
lcd_write_value16(data); lcd_write_value16(data);
} }
/* turn the display upside down (call lcd_update() afterwards) */ /*** hardware configuration ***/
void lcd_set_contrast(int val)
{
(void)val;
}
void lcd_set_invert_display(bool yesno)
{
r_disp_control_rev = yesno ? R_DISP_CONTROL_REV :
R_DISP_CONTROL_NORMAL;
if (display_on)
{
lcd_write_reg(R_DISP_CONTROL1, 0x0013 | r_disp_control_rev);
}
}
#ifdef HAVE_LCD_FLIP
static bool display_flipped = false;
/* turn the display upside down */
void lcd_set_flip(bool yesno) void lcd_set_flip(bool yesno)
{ {
display_flipped = yesno; display_flipped = yesno;
xoffset = yesno ? 0 : 20; /* TODO: Implement flipped mode */
/* TODO */ r_entry_mode = yesno ? R_ENTRY_MODE_HORZ_FLIPPED :
R_ENTRY_MODE_HORZ_NORMAL;
} }
#endif
static void _display_on(void) static void _display_on(void)
{ {
/* Initialise in the same way as the original firmare */ /* Initialise in the same way as the original firmare */
lcd_write_reg(0x07, 0); lcd_write_reg(R_DISP_CONTROL1, 0);
lcd_write_reg(0x13, 0); lcd_write_reg(R_POWER_CONTROL4, 0);
lcd_write_reg(0x11, 0x3704); lcd_write_reg(R_POWER_CONTROL2, 0x3704);
lcd_write_reg(0x14, 0x1a1b); lcd_write_reg(0x14, 0x1a1b);
lcd_write_reg(0x10, 0x3860); lcd_write_reg(R_POWER_CONTROL1, 0x3860);
lcd_write_reg(0x13, 0x40); lcd_write_reg(R_POWER_CONTROL4, 0x40);
lcd_write_reg(0x13, 0x60); lcd_write_reg(R_POWER_CONTROL4, 0x60);
lcd_write_reg(0x13, 0x70); lcd_write_reg(R_POWER_CONTROL4, 0x70);
lcd_write_reg(0x01, 277); lcd_write_reg(R_DRV_OUTPUT_CONTROL, 277);
lcd_write_reg(0x02, (7<<8)); lcd_write_reg(R_DRV_WAVEFORM_CONTROL, (7<<8));
lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ); lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
lcd_write_reg(0x08, 0x01); lcd_write_reg(R_DISP_CONTROL2, 0x01);
lcd_write_reg(0x0b, (1<<10)); lcd_write_reg(R_FRAME_CYCLE_CONTROL, (1<<10));
lcd_write_reg(0x0c, 0); lcd_write_reg(R_EXT_DISP_IF_CONTROL, 0);
lcd_write_reg(0x30, 0x40); lcd_write_reg(R_GAMMA_FINE_ADJ_POS1, 0x40);
lcd_write_reg(0x31, 0x0687); lcd_write_reg(R_GAMMA_FINE_ADJ_POS2, 0x0687);
lcd_write_reg(0x32, 0x0306); lcd_write_reg(R_GAMMA_FINE_ADJ_POS3, 0x0306);
lcd_write_reg(0x33, 0x104); lcd_write_reg(R_GAMMA_GRAD_ADJ_POS, 0x104);
lcd_write_reg(0x34, 0x0585); lcd_write_reg(R_GAMMA_FINE_ADJ_NEG1, 0x0585);
lcd_write_reg(0x35, 255+66); lcd_write_reg(R_GAMMA_FINE_ADJ_NEG2, 255+66);
lcd_write_reg(0x36, 0x0687+128); lcd_write_reg(R_GAMMA_FINE_ADJ_NEG3, 0x0687+128);
lcd_write_reg(0x37, 259); lcd_write_reg(R_GAMMA_GRAD_ADJ_NEG, 259);
lcd_write_reg(0x38, 0); lcd_write_reg(R_GAMMA_AMP_ADJ_RES_POS, 0);
lcd_write_reg(0x39, 0); lcd_write_reg(R_GAMMA_AMP_AVG_ADJ_RES_NEG, 0);
lcd_write_reg(0x42, (LCD_WIDTH - 1)); lcd_write_reg(R_1ST_SCR_DRV_POS, (LCD_WIDTH - 1));
lcd_write_reg(0x43, 0); lcd_write_reg(R_2ND_SCR_DRV_POS, 0);
lcd_write_reg(0x44, (LCD_WIDTH - 1)); lcd_write_reg(R_HORIZ_RAM_ADDR_POS, (LCD_WIDTH - 1));
lcd_write_reg(0x45, 0); lcd_write_reg(R_VERT_RAM_ADDR_POS, 0);
lcd_write_reg(0x46, (((LCD_WIDTH - 1) + xoffset) << 8) | xoffset); lcd_write_reg(0x46, (((LCD_WIDTH - 1) + xoffset) << 8) | xoffset);
lcd_write_reg(0x47, (LCD_HEIGHT - 1)); lcd_write_reg(0x47, (LCD_HEIGHT - 1));
lcd_write_reg(0x48, 0x0); lcd_write_reg(0x48, 0x0);
lcd_write_reg(0x07, 0x11); lcd_write_reg(R_DISP_CONTROL1, 0x11);
lcd_write_reg(0x07, 0x17); lcd_write_reg(R_DISP_CONTROL1, 0x13 | r_disp_control_rev);
display_on = true; /* must be done before calling lcd_update() */ display_on = true; /* must be done before calling lcd_update() */
lcd_update(); lcd_update();
} }
void lcd_init_device(void)
{
as3525_dbop_init();
GPIOA_DIR |= (1<<5|1<<4|1<<3);
GPIOA_PIN(5) = 0;
GPIOA_PIN(3) = (1<<3);
GPIOA_PIN(4) = 0;
GPIOA_PIN(5) = (1<<5);
_display_on();
}
#if defined(HAVE_LCD_ENABLE) #if defined(HAVE_LCD_ENABLE)
void lcd_enable(bool on) void lcd_enable(bool on)
{ {
if (display_on == on) if (display_on == on)
return; /* nothing to do */ return;
if(on) if(on)
{ {
lcd_write_reg(0, 1); lcd_write_reg(R_START_OSC, 1);
lcd_write_reg(0x10, 0); lcd_write_reg(R_POWER_CONTROL1, 0);
lcd_write_reg(0x11, 0x3704); lcd_write_reg(R_POWER_CONTROL2, 0x3704);
lcd_write_reg(0x14, 0x1a1b); lcd_write_reg(0x14, 0x1a1b);
lcd_write_reg(0x10, 0x3860); lcd_write_reg(R_POWER_CONTROL1, 0x3860);
lcd_write_reg(0x13, 0x40); lcd_write_reg(R_POWER_CONTROL4, 0x40);
lcd_write_reg(0x13, 0x60); lcd_write_reg(R_POWER_CONTROL4, 0x60);
lcd_write_reg(0x13, 112); lcd_write_reg(R_POWER_CONTROL4, 112);
lcd_write_reg(0x07, 0x11); lcd_write_reg(R_DISP_CONTROL1, 0x11);
lcd_write_reg(0x07, 0x17); lcd_write_reg(R_DISP_CONTROL1, 0x13 | r_disp_control_rev);
display_on = true; display_on = true;
lcd_update(); /* Resync display */ lcd_update(); /* Resync display */
send_event(LCD_EVENT_ACTIVATION, NULL); send_event(LCD_EVENT_ACTIVATION, NULL);
@ -223,9 +305,9 @@ void lcd_enable(bool on)
} }
else else
{ {
lcd_write_reg(0x07, 0x22); lcd_write_reg(R_DISP_CONTROL1, 0x22);
lcd_write_reg(0x07, 0); lcd_write_reg(R_DISP_CONTROL1, 0);
lcd_write_reg(0x10, 1); lcd_write_reg(R_POWER_CONTROL1, 1);
display_on = false; display_on = false;
} }
} }
@ -240,23 +322,28 @@ bool lcd_active(void)
/*** update functions ***/ /*** update functions ***/
/* FIXME : find the datasheet for this RENESAS controller so we identify the
* registers used in windowing code (not present in HD66789R) */
/* Set horizontal window addresses */ /* Set horizontal window addresses */
static void lcd_window_x(int xmin, int xmax) static void lcd_window_x(int xmin, int xmax)
{ {
xmin += xoffset; xmin += xoffset;
xmax += xoffset; xmax += xoffset;
lcd_write_reg(0x46, (xmax << 8) | xmin); lcd_write_reg(R_HORIZ_RAM_ADDR_POS + 2, (xmax << 8) | xmin);
lcd_write_reg(0x20, xmin); lcd_write_reg(R_RAM_ADDR_SET - 1, xmin);
} }
/* Set vertical window addresses */ /* Set vertical window addresses */
static void lcd_window_y(int ymin, int ymax) static void lcd_window_y(int ymin, int ymax)
{ {
lcd_write_reg(0x47, ymax); lcd_write_reg(R_VERT_RAM_ADDR_POS + 2, ymax);
lcd_write_reg(0x48, ymin); lcd_write_reg(R_VERT_RAM_ADDR_POS + 3, ymin);
lcd_write_reg(0x21, ymin); lcd_write_reg(R_RAM_ADDR_SET, ymin);
} }
static unsigned lcd_yuv_options = 0;
void lcd_yuv_set_options(unsigned options) void lcd_yuv_set_options(unsigned options)
{ {
lcd_yuv_options = options; lcd_yuv_options = options;
@ -267,11 +354,15 @@ extern void lcd_write_yuv420_lines(unsigned char const * const src[3],
int width, int width,
int stride); int stride);
extern void lcd_write_yuv420_lines_odither(unsigned char const * const src[3], extern void lcd_write_yuv420_lines_odither(unsigned char const * const src[3],
int width, int width,
int stride, int stride,
int x_screen, /* To align dither */ int x_screen, /* To align dither pattern */
int y_screen); /* pattern */ int y_screen);
/* Performance function to blit a YUV bitmap directly to the LCD */
/* Performance function to blit a YUV bitmap directly to the LCD
* src_x, src_y, width and height should be even
* x, y, width and height have to be within LCD bounds
*/
void lcd_blit_yuv(unsigned char * const src[3], void lcd_blit_yuv(unsigned char * const src[3],
int src_x, int src_y, int stride, int src_x, int src_y, int stride,
int x, int y, int width, int height) int x, int y, int width, int height)
@ -279,10 +370,6 @@ void lcd_blit_yuv(unsigned char * const src[3],
unsigned char const * yuv_src[3]; unsigned char const * yuv_src[3];
off_t z; off_t z;
lcd_busy = true;
lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_VERT);
/* Sorry, but width and height must be >= 2 or else */ /* Sorry, but width and height must be >= 2 or else */
width &= ~1; width &= ~1;
height >>= 1; height >>= 1;
@ -292,6 +379,16 @@ void lcd_blit_yuv(unsigned char * const src[3],
yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1); yuv_src[1] = src[1] + (z >> 2) + (src_x >> 1);
yuv_src[2] = src[2] + (yuv_src[1] - src[1]); yuv_src[2] = src[2] + (yuv_src[1] - src[1]);
lcd_busy = true;
#ifdef HAVE_LCD_FLIP
lcd_write_reg(R_ENTRY_MODE,
display_flipped ? R_ENTRY_MODE_VIDEO_FLIPPED : R_ENTRY_MODE_VIDEO_NORMAL
);
#else
lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_VIDEO_NORMAL);
#endif
lcd_window_x(x, x + width - 1); lcd_window_x(x, x + width - 1);
if (lcd_yuv_options & LCD_YUV_DITHER) if (lcd_yuv_options & LCD_YUV_DITHER)
@ -299,7 +396,7 @@ void lcd_blit_yuv(unsigned char * const src[3],
do do
{ {
lcd_window_y(y, y + 1); lcd_window_y(y, y + 1);
/* Start write to GRAM */
lcd_write_cmd(R_WRITE_DATA_2_GRAM); lcd_write_cmd(R_WRITE_DATA_2_GRAM);
lcd_write_yuv420_lines_odither(yuv_src, width, stride, x, y); lcd_write_yuv420_lines_odither(yuv_src, width, stride, x, y);
@ -308,14 +405,14 @@ void lcd_blit_yuv(unsigned char * const src[3],
yuv_src[2] += stride >> 1; yuv_src[2] += stride >> 1;
y += 2; y += 2;
} }
while (--height > 0); while (--height > 0);
} }
else else
{ {
do do
{ {
lcd_window_y(y, y + 1); lcd_window_y(y, y + 1);
/* Start write to GRAM */
lcd_write_cmd(R_WRITE_DATA_2_GRAM); lcd_write_cmd(R_WRITE_DATA_2_GRAM);
lcd_write_yuv420_lines(yuv_src, width, stride); lcd_write_yuv420_lines(yuv_src, width, stride);
@ -324,25 +421,12 @@ void lcd_blit_yuv(unsigned char * const src[3],
yuv_src[2] += stride >> 1; yuv_src[2] += stride >> 1;
y += 2; y += 2;
} }
while (--height > 0); while (--height > 0);
} }
lcd_busy = false; lcd_busy = false;
} }
void lcd_init_device()
{
as3525_dbop_init();
GPIOA_DIR |= (1<<5|1<<4|1<<3);
GPIOA_PIN(5) = 0;
GPIOA_PIN(3) = (1<<3);
GPIOA_PIN(4) = 0;
GPIOA_PIN(5) = (1<<5);
_display_on();
}
/* Update the display. /* Update the display.
This must be called after all other LCD functions that change the display. */ This must be called after all other LCD functions that change the display. */
void lcd_update(void) void lcd_update(void)
@ -352,29 +436,27 @@ void lcd_update(void)
lcd_busy = true; lcd_busy = true;
lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ); lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
lcd_window_x(0, LCD_WIDTH - 1); lcd_window_x(0, LCD_WIDTH - 1);
lcd_window_y(0, LCD_HEIGHT - 1); lcd_window_y(0, LCD_HEIGHT - 1);
/* Start write to GRAM */
lcd_write_cmd(R_WRITE_DATA_2_GRAM); lcd_write_cmd(R_WRITE_DATA_2_GRAM);
/* Write data */ lcd_write_data((fb_data*)lcd_framebuffer, LCD_WIDTH*LCD_HEIGHT);
lcd_write_data((unsigned short *)lcd_framebuffer, LCD_WIDTH*LCD_HEIGHT);
lcd_busy = false; lcd_busy = false;
} }
/* Update a fraction of the display. */ /* Update a fraction of the display. */
void lcd_update_rect(int x, int y, int width, int height) void lcd_update_rect(int x, int y, int width, int height)
{ {
int xmax, ymax;
const fb_data *ptr; const fb_data *ptr;
int xmax, ymax;
if (!display_on) if (!display_on)
return; return;
xmax = x + width; xmax = x + width;
if (xmax >= LCD_WIDTH) if (xmax >= LCD_WIDTH)
xmax = LCD_WIDTH - 1; /* Clip right */ xmax = LCD_WIDTH - 1; /* Clip right */
@ -395,12 +477,11 @@ void lcd_update_rect(int x, int y, int width, int height)
lcd_busy = true; lcd_busy = true;
lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ); lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
lcd_window_x(x, xmax); lcd_window_x(x, xmax);
lcd_window_y(y, ymax); lcd_window_y(y, ymax);
/* Start write to GRAM */
lcd_write_cmd(R_WRITE_DATA_2_GRAM); lcd_write_cmd(R_WRITE_DATA_2_GRAM);
ptr = &lcd_framebuffer[y][x]; ptr = &lcd_framebuffer[y][x];
@ -412,23 +493,26 @@ void lcd_update_rect(int x, int y, int width, int height)
ptr += LCD_WIDTH; ptr += LCD_WIDTH;
} }
while (--height >= 0); while (--height >= 0);
lcd_busy = false; lcd_busy = false;
} }
/* writes one read pixel outside the visible area, needed for correct dbop reads */ /* writes one red pixel outside the visible area, needed for correct
* dbop reads */
bool lcd_button_support(void) bool lcd_button_support(void)
{ {
fb_data data = 0xf<<12;
if (lcd_busy) if (lcd_busy)
return false; return false;
lcd_write_reg(R_ENTRY_MODE, R_ENTRY_MODE_HORZ);
/* Set start position and window */
lcd_write_reg(R_ENTRY_MODE, r_entry_mode);
/* Set start position and window */
lcd_window_x(-1, 0); lcd_window_x(-1, 0);
lcd_window_y(-1, 0); lcd_window_y(-1, 0);
lcd_write_cmd(R_WRITE_DATA_2_GRAM); lcd_write_cmd(R_WRITE_DATA_2_GRAM);
lcd_write_value16(data); lcd_write_value16(0xf<<12);
return true; return true;
} }