From 56271b571abdff74672d174345ae3197e170953c Mon Sep 17 00:00:00 2001 From: Jörg Hohensohn Date: Fri, 12 Dec 2003 22:11:08 +0000 Subject: LCD flip (upside down) and a high-performance blit function git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4143 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/lcd-recorder.c | 48 ++++++++++++++--- firmware/drivers/lcd.c | 113 ++++++++++++++++++++++++++++++++++++++++ firmware/export/lcd.h | 4 ++ 3 files changed, 158 insertions(+), 7 deletions(-) diff --git a/firmware/drivers/lcd-recorder.c b/firmware/drivers/lcd-recorder.c index a409c9fc63..0a95e82c3d 100644 --- a/firmware/drivers/lcd-recorder.c +++ b/firmware/drivers/lcd-recorder.c @@ -103,10 +103,8 @@ static struct scrollinfo scroll[SCROLLABLE_LINES]; static int xmargin = 0; static int ymargin = 0; static int curfont = FONT_SYSFIXED; +static int xoffset = 0; /* needed for flip */ -#ifndef SIMULATOR -static -#endif unsigned char lcd_framebuffer[LCD_WIDTH][LCD_HEIGHT/8]; /* All zeros and ones bitmaps for area filling */ @@ -163,6 +161,25 @@ void lcd_init (void) sizeof(scroll_stack), scroll_name); } + +/* Performance function that works with an external buffer + note that y and height are in 8-pixel units! */ +void lcd_blit (unsigned char* p_data, int x, int y, int width, int height, int stride) __attribute__ ((section (".icode"))); +void lcd_blit (unsigned char* p_data, int x, int y, int width, int height, int stride) +{ + /* Copy display bitmap to hardware */ + while (height--) + { + lcd_write (true, LCD_CNTL_PAGE | (y++ & 0xf)); + lcd_write (true, LCD_CNTL_HIGHCOL | (((x+xoffset)>>4) & 0xf)); + lcd_write (true, LCD_CNTL_LOWCOL | ((x+xoffset) & 0xf)); + + lcd_write_data(p_data, width); + p_data += stride; + } +} + + /* * Update the display. * This must be called after all other LCD functions that change the display. @@ -176,8 +193,8 @@ void lcd_update (void) for (y = 0; y < LCD_HEIGHT/8; y++) { lcd_write (true, LCD_CNTL_PAGE | (y & 0xf)); - lcd_write (true, LCD_CNTL_HIGHCOL); - lcd_write (true, LCD_CNTL_LOWCOL); + lcd_write (true, LCD_CNTL_HIGHCOL | ((xoffset>>4) & 0xf)); + lcd_write (true, LCD_CNTL_LOWCOL | (xoffset & 0xf)); for (x = 0; x < LCD_WIDTH; x++) lcd_write (false, lcd_framebuffer[x][y]); @@ -210,8 +227,8 @@ void lcd_update_rect (int x_start, int y, for (; y <= ymax; y++) { lcd_write (true, LCD_CNTL_PAGE | (y & 0xf)); - lcd_write (true, LCD_CNTL_HIGHCOL | ((x_start>>4) & 0xf)); - lcd_write (true, LCD_CNTL_LOWCOL | (x_start & 0xf)); + lcd_write (true, LCD_CNTL_HIGHCOL | (((x_start+xoffset)>>4) & 0xf)); + lcd_write (true, LCD_CNTL_LOWCOL | ((x_start+xoffset) & 0xf)); for (x = x_start; x < xmax; x++) lcd_write (false, lcd_framebuffer[x][y]); @@ -232,6 +249,23 @@ void lcd_set_invert_display(bool yesno) lcd_write(true, LCD_SET_NORMAL_DISPLAY); } +/* turn the display upside down (call lcd_update() afterwards) */ +void lcd_set_flip(bool yesno) +{ + if (yesno) + { + lcd_write(true, LCD_SET_SEGMENT_REMAP); + lcd_write(true, LCD_SET_COM_OUTPUT_SCAN_DIRECTION); + xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 112 we have */ + } + else + { + lcd_write(true, LCD_SET_SEGMENT_REMAP | 0x01); + lcd_write(true, LCD_SET_COM_OUTPUT_SCAN_DIRECTION | 0x08); + xoffset = 0; + } +} + /** * Rolls up the lcd display by the specified amount of lines. * Lines that are rolled out over the top of the screen are diff --git a/firmware/drivers/lcd.c b/firmware/drivers/lcd.c index 3f6acee301..aaa93b3385 100644 --- a/firmware/drivers/lcd.c +++ b/firmware/drivers/lcd.c @@ -173,3 +173,116 @@ void lcd_write(bool command, int byte) : /* %0 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC), /* %1 */ "z"(LCDR)); } + + +/* A high performance function to write data to the display, + one or multiple bytes. + Ultimately, all calls to lcd_write(false, xxx) should be substituted by + this, it will be most efficient if the LCD buffer is tilted to have the + X row as consecutive bytes, so we can write a whole row */ +/* FixMe: somehow the red LED is affected by this, although I don't touch + any other bit. Therefore not used yet, except for lcd_blit() */ +void lcd_write_data(unsigned char* p_bytes, int count) __attribute__ ((section (".icode"))); +void lcd_write_data(unsigned char* p_bytes, int count) +{ + do + { + unsigned byte; + unsigned sda1; /* precalculated SC=low,SD=1 */ + unsigned clk0sda0; /* precalculated SC and SD low */ + + cli(); /* make port modifications atomic */ + + /* precalculate the values for later bit toggling, init data write */ + asm ( + "mov.b @%2,%0\n" /* sda1 = PBDRL */ + "or %4,%0\n" /* sda1 |= LCD_DS | LCD_SD DS and SD high, */ + "and %3,%0\n" /* sda1 &= ~(LCD_CS | LCD_SC) CS and SC low */ + "mov %0,%1\n" /* sda1 -> clk0sda0 */ + "and %5,%1\n" /* clk0sda0 &= ~LCD_SD both low */ + "mov.b %1,@%2\n" /* PBDRL = clk0sda0 */ + : // outputs + /* %0 */ "=r"(sda1), + /* %1 */ "=r"(clk0sda0) + : // inputs + /* %2 */ "r"(LCDR), + /* %3 */ "r"(~(LCD_CS | LCD_SC)), + /* %4 */ "r"(LCD_DS | LCD_SD), + /* %5 */ "r"(~LCD_SD) + ); + + byte = *p_bytes++ << 24; /* fetch to MSB position */ + + /* unrolled loop to serialize the byte */ + asm ( + "mov %4,r0\n" /* we need &PBDRL in r0 for "or.b x,@(r0,gbr)" */ + + "shll %0\n" /* shift the MSB into carry */ + "bf 1f\n" + "mov.b %1,@%4\n" /* if it was a "1": set SD high, SC low still */ + "1: \n" + "or.b %2, @(r0,gbr)\n" /* rise SC (independent of SD level) */ + "shll %0\n" /* shift for next round, now for longer hold time */ + "mov.b %3,@%4\n" /* SC and SD low again */ + + "bf 1f\n" + "mov.b %1,@%4\n" + "1: \n" + "or.b %2, @(r0,gbr)\n" + "shll %0\n" + "mov.b %3,@%4\n" + + "bf 1f\n" + "mov.b %1,@%4\n" + "1: \n" + "or.b %2, @(r0,gbr)\n" + "shll %0\n" + "mov.b %3,@%4\n" + + "bf 1f\n" + "mov.b %1,@%4\n" + "1: \n" + "or.b %2, @(r0,gbr)\n" + "shll %0\n" + "mov.b %3,@%4\n" + + "bf 1f\n" + "mov.b %1,@%4\n" + "1: \n" + "or.b %2, @(r0,gbr)\n" + "shll %0\n" + "mov.b %3,@%4\n" + + "bf 1f\n" + "mov.b %1,@%4\n" + "1: \n" + "or.b %2, @(r0,gbr)\n" + "shll %0\n" + "mov.b %3,@%4\n" + + "bf 1f\n" + "mov.b %1,@%4\n" + "1: \n" + "or.b %2, @(r0,gbr)\n" + "shll %0\n" + "mov.b %3,@%4\n" + + "bf 1f\n" + "mov.b %1,@%4\n" + "1: \n" + "or.b %2, @(r0,gbr)\n" + + "or.b %5, @(r0,gbr)\n" /* restore port */ + : + : /* %0 */ "r"(byte), + /* %1 */ "r"(sda1), + /* %2 */ "I"(LCD_SC), + /* %3 */ "r"(clk0sda0), + /* %4 */ "r"(LCDR), + /* %5 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC) + ); + + sti(); + + } while (--count); /* tail loop is faster */ +} diff --git a/firmware/export/lcd.h b/firmware/export/lcd.h index c4632a0282..c81d2de2b3 100644 --- a/firmware/export/lcd.h +++ b/firmware/export/lcd.h @@ -44,10 +44,13 @@ extern void lcd_scroll_speed( int speed ); extern void lcd_scroll_delay( int ms ); extern void lcd_set_contrast(int val); extern void lcd_write( bool command, int byte ); +extern void lcd_write_data( unsigned char* p_bytes, int count ); extern int lcd_default_contrast(void); #if defined(SIMULATOR) || defined(HAVE_LCD_BITMAP) extern void lcd_update(void); +/* performance function */ +extern void lcd_blit (unsigned char* p_data, int x, int y, int width, int height, int stride); /* update a fraction of the screen */ extern void lcd_update_rect(int x, int y, int width, int height); @@ -134,6 +137,7 @@ extern void lcd_clearpixel(int x, int y); extern void lcd_invertpixel(int x, int y); extern void lcd_roll(int pixels); extern void lcd_set_invert_display(bool yesno); +extern void lcd_set_flip(bool yesno); extern void lcd_bidir_scroll(int threshold); extern void lcd_scroll_step(int pixels); extern void lcd_setfont(int font); -- cgit v1.2.3