1 //==========================================================================
5 // Agilent AAED2000 - LCD support routines
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: gthomas
46 // Description: Simple LCD support
47 //####DESCRIPTIONEND####
49 #include <pkgconf/hal.h>
51 #include <cyg/infra/diag.h>
52 #include <cyg/hal/hal_io.h> // IO macros
53 #include <cyg/hal/hal_if.h> // Virtual vector support
54 #include <cyg/hal/hal_arch.h> // Register state info
55 #include <cyg/hal/hal_intr.h> // HAL interrupt macros
57 #include <cyg/hal/aaed2000.h> // Board definitions
58 #include <cyg/hal/lcd_support.h>
59 #include <cyg/hal/hal_cache.h>
63 #ifdef CYGPKG_ISOINFRA
64 # include <pkgconf/isoinfra.h>
65 # ifdef CYGINT_ISO_STDIO_FORMATTED_IO
66 # include <stdio.h> // sscanf
70 #include CYGHWR_MEMORY_LAYOUT_H
71 #define LCD_FRAMEBUFFER CYGMEM_REGION_lcd
72 static cyg_uint32 lcd_framebuffer;
79 // Physical dimensions of LCD display
80 #define DISPLAY_WIDTH 640
81 #define DISPLAY_HEIGHT 480
84 #ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
85 #define LCD_WIDTH DISPLAY_HEIGHT
86 #define LCD_HEIGHT DISPLAY_WIDTH
88 #define LCD_WIDTH DISPLAY_WIDTH
89 #define LCD_HEIGHT DISPLAY_HEIGHT
93 #define RGB_RED(x) (((x)&0x1F)<<0)
94 #define RGB_GREEN(x) (((x)&0x1F)<<5)
95 #define RGB_BLUE(x) (((x)&0x1F)<<10)
97 // Physical screen info
98 //static int lcd_depth = LCD_DEPTH; // Should be 1, 2, or 4
100 #ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
101 static int lcd_width = LCD_WIDTH;
103 static int lcd_height = LCD_HEIGHT;
105 // Black on light blue
106 static int bg = RGB_RED(0x17) | RGB_GREEN(0x17) | RGB_BLUE(0x1F);
107 #ifdef CYGSEM_AAED2000_LCD_COMM
108 static int fg = RGB_RED(0) | RGB_GREEN(0) | RGB_BLUE(0);
111 // Compute the location for a pixel within the framebuffer
113 lcd_fb(int row, int col)
115 return (cyg_uint32 *)(lcd_framebuffer+(((row*DISPLAY_WIDTH)+col)*2));
123 if (!enable) return; // FIXME - need a way to [safely] blank screen
125 // Turn on PWM [pulse wave modulated] voltage pump
126 HAL_WRITE_UINT32(AAEC_PUMP_CONTROL, 0x800); // Magic FIXME
127 HAL_WRITE_UINT32(AAEC_PUMP_FREQUENCY, 0x3733); // Magic FIXME
129 HAL_READ_UINT32(AAED_EXT_GPIO, ctl);
130 ctl |= AAED_EXT_GPIO_LCD_PWR_EN;
131 HAL_WRITE_UINT32(AAED_EXT_GPIO, ctl);
132 // Power must be on for 0..20ms before enabling signals
133 CYGACC_CALL_IF_DELAY_US(500); // 500us should be long enough
134 HAL_READ_UINT32(AAEC_LCD_CONTROL, ctl);
135 ctl |= AAEC_LCD_CONTROL_ENAB | AAEC_LCD_CONTROL_PWR_ENAB;
136 HAL_WRITE_UINT32(AAEC_LCD_CONTROL, ctl);
139 // Initialize LCD hardware
146 HAL_VIRT_TO_PHYS_ADDRESS(LCD_FRAMEBUFFER, lcd_framebuffer);
148 HAL_READ_UINT32(AAEC_LCD_CONTROL, ctl);
149 if (ctl & (AAEC_LCD_CONTROL_ENAB | AAEC_LCD_CONTROL_PWR_ENAB)) {
150 // LCD currently on - turn it off carefully
151 ctl &= ~(AAEC_LCD_CONTROL_ENAB | AAEC_LCD_CONTROL_PWR_ENAB);
152 HAL_WRITE_UINT32(AAEC_LCD_CONTROL, ctl);
154 CYGACC_CALL_IF_DELAY_US(500);
156 HAL_READ_UINT32(AAED_EXT_GPIO, ctl);
157 ctl &= ~AAED_EXT_GPIO_LCD_PWR_EN;
158 HAL_WRITE_UINT32(AAED_EXT_GPIO, ctl);
159 // Now wait for 1.0 second
160 CYGACC_CALL_IF_DELAY_US(1000*1000);
162 HAL_WRITE_UINT32(AAEC_LCD_CONTROL, 0x00010028); // Magic FIXME
163 HAL_WRITE_UINT32(AAEC_LCD_TIMING0, 0x2B135F9C); // Magic FIXME
164 HAL_WRITE_UINT32(AAEC_LCD_TIMING1, 0x211401DF); // Magic FIXME
165 HAL_WRITE_UINT32(AAEC_LCD_TIMING2, 0x067F1820); // Magic FIXME
166 HAL_WRITE_UINT32(AAEC_LCD_TIMING3, 0x00000000); // Magic FIXME
167 HAL_WRITE_UINT32(AAEC_LCD_UPBASE, lcd_framebuffer);
168 HAL_WRITE_UINT32(AAEC_LCD_LPBASE, lcd_framebuffer);
169 #if 0 // For some reason, this crashes miserably
170 lcd_framebuffer = LCD_FRAMEBUFFER; // Logical operations use cached space
176 // Get information about the frame buffer
178 lcd_getinfo(struct lcd_info *info)
181 return 0; // LCD not initialized
183 info->width = DISPLAY_WIDTH;
184 info->height = DISPLAY_HEIGHT;
186 info->fb = (void*)LCD_FRAMEBUFFER;
187 info->rlen = DISPLAY_WIDTH * 2;
188 info->type = FB_TRUE_RGB555;
189 return 1; // Information valid
196 #ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
197 cyg_uint32 *fb_row0, *fb_rown;
198 cyg_uint32 _bg = (bg<<16)|bg;
200 fb_row0 = lcd_fb(0, 0);
201 fb_rown = lcd_fb(lcd_height, 0);
202 while (fb_row0 != fb_rown) {
207 for (row = 0; row < lcd_height; row++) {
208 for (col = 0; col < lcd_width; col++) {
209 set_pixel(row, col, bg);
215 #ifdef CYGSEM_AAED2000_LCD_COMM
218 // Additional support for LCD/Keyboard as 'console' device
221 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
222 #include "banner.xpm"
226 // Virtual screen info
227 static int curX = 0; // Last used position
229 //static int width = LCD_WIDTH / (FONT_WIDTH*NIBBLES_PER_PIXEL);
230 //static int height = LCD_HEIGHT / (FONT_HEIGHT*SCREEN_SCALE);
232 #define SCREEN_PAN 20
233 #define SCREEN_WIDTH 80
234 #define SCREEN_HEIGHT (LCD_HEIGHT/FONT_HEIGHT)
235 #define VISIBLE_SCREEN_WIDTH (LCD_WIDTH/FONT_WIDTH)
236 #define VISIBLE_SCREEN_HEIGHT (LCD_HEIGHT/FONT_HEIGHT)
237 static char screen[SCREEN_HEIGHT][SCREEN_WIDTH];
238 static int screen_height = SCREEN_HEIGHT;
239 static int screen_width = SCREEN_WIDTH;
240 static int screen_pan = 0;
242 // Usable area on screen [logical pixel rows]
243 static int screen_start = 0;
244 static int screen_end = LCD_HEIGHT/FONT_HEIGHT;
246 static bool cursor_enable = true;
249 static void lcd_drawc(cyg_int8 c, int x, int y);
251 // Note: val is a 16 bit, RGB555 value which must be mapped
252 // onto a 12 bit value.
253 #define RED(v) ((v>>12) & 0x0F)
254 #define GREEN(v) ((v>>7) & 0x0F)
255 #define BLUE(v) ((v>>1) & 0x0F)
256 #ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
257 #error PORTRAIT MODE NOT IMPLEMENTED
258 // Translate coordinates, rotating clockwise 90 degrees
260 set_pixel(int row, int col, unsigned short val)
262 // fp->pixels[col][(DISPLAY_WIDTH-1)-row] = val;
263 int _row = (240-1) - col;
265 unsigned char *pxptr = (unsigned char *)(0xC0000000 + (_row * 480) + ((_col * 3) / 2));
267 diag_printf("%s\n", __FUNCTION__); return;
268 if ((row >= LCD_HEIGHT) || (col >= LCD_WIDTH)) return;
271 int old = start_console();
272 diag_printf("row=%d/%d, col=%d/%d, pxptr = %p\n", row, _row, col, _col, pxptr);
275 if ((row % 2) == 0) {
277 *pxptr++ = RED(val) | (GREEN(val) << 4);
278 *pxptr = (*pxptr & 0xF0) | BLUE(val);
281 *pxptr = (*pxptr & 0x0F) | (RED(val) << 4);
282 *++pxptr = GREEN(val) | (BLUE(val) << 4);
288 set_pixel(int row, int col, unsigned short val)
290 unsigned short *pix = (unsigned short *)lcd_fb(row, col);
298 if ((c >= '0') && (c <= '9')) {
301 if ((c >= 'A') && (c <= 'F')) {
302 return (c - 'A') + 0x0A;
304 if ((c >= 'a') && (c <= 'f')) {
305 return (c - 'a') + 0x0a;
314 return (_hexdigit(*cp)<<4) | _hexdigit(*(cp+1));
317 static unsigned short
318 parse_color(char *cp)
320 int red, green, blue;
322 while (*cp && (*cp != 'c')) cp++;
330 return RGB_RED(red>>3) | RGB_GREEN(green>>2) | RGB_BLUE(blue>>3);
332 return RGB_RED(red>>3) | RGB_GREEN(green>>3) | RGB_BLUE(blue>>3);
343 #ifndef CYGINT_ISO_STDIO_FORMATTED_IO
351 while ((c = *cp++) && (c != ' ')) {
352 if ((c >= '0') && (c <= '9')) {
353 val = val * 10 + (c - '0');
363 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
365 show_xpm(char *xpm[], int screen_pos)
367 int i, row, col, offset;
369 int nrows, ncols, nclrs;
370 unsigned short colors[256]; // Mapped by character index
373 #ifdef CYGINT_ISO_STDIO_FORMATTED_IO
374 if (sscanf(cp, "%d %d %d", &ncols, &nrows, &nclrs) != 3) {
376 if (((ncols = get_int(&cp)) < 0) ||
377 ((nrows = get_int(&cp)) < 0) ||
378 ((nclrs = get_int(&cp)) < 0)) {
381 diag_printf("Can't parse XPM data, sorry\n");
384 // printf("%d rows, %d cols, %d colors\n", nrows, ncols, nclrs);
386 for (i = 0; i < 256; i++) {
389 for (i = 0; i < nclrs; i++) {
391 colors[(unsigned int)*cp] = parse_color(&cp[1]);
392 // printf("Color[%c] = %x\n", *cp, colors[(unsigned int)*cp]);
395 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
398 offset = screen_pos-nrows;
400 for (row = 0; row < nrows; row++) {
401 cp = xpm[nclrs+1+row];
402 for (col = 0; col < ncols; col++) {
403 set_pixel(row+offset, col, colors[(unsigned int)*cp++]);
406 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
407 screen_start = (nrows + (FONT_HEIGHT-1))/FONT_HEIGHT;
408 screen_end = LCD_HEIGHT/FONT_HEIGHT;
412 screen_height = offset / FONT_HEIGHT;
413 screen_end = screen_height;
420 lcd_screen_clear(void)
423 for (row = 0; row < screen_height; row++) {
424 for (col = 0; col < screen_width; col++) {
425 screen[row][col] = ' ';
428 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
429 // Note: Row 0 seems to wrap incorrectly
430 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
433 pos = (LCD_HEIGHT-1);
435 show_xpm(banner_xpm, pos);
436 #endif // CYGOPT_AAED2000_LCD_COMM_LOGO
437 curX = 0; curY = screen_start;
439 lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
445 lcd_moveto(int X, int Y)
448 lcd_drawc(screen[curY][curX], curX-screen_pan, curY);
451 if (X >= screen_width) X = screen_width-1;
453 if (Y < screen_start) Y = screen_start;
454 if (Y >= screen_height) Y = screen_height-1;
457 lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
461 // Render a character at position (X,Y) with current background/foreground
463 lcd_drawc(cyg_int8 c, int x, int y)
468 #ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
473 if ((x < 0) || (x >= VISIBLE_SCREEN_WIDTH) ||
474 (y < 0) || (y >= screen_height)) return;
475 for (l = 0; l < FONT_HEIGHT; l++) {
476 bits = font_table[c-FIRST_CHAR][l];
477 yoff = y*FONT_HEIGHT + l;
478 xoff = x*FONT_HEIGHT;
479 #ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
480 // Caution - only works for little-endian & font sizes multiple of 2
481 fb = lcd_fb(yoff, xoff);
482 for (p = 0; p < FONT_WIDTH; p += 2) {
483 switch (bits & 0x03) {
485 *fb++ = (bg << 16) | bg;
488 *fb++ = (bg << 16) | fg;
491 *fb++ = (fg << 16) | bg;
494 *fb++ = (fg << 16) | fg;
500 for (p = 0; p < FONT_WIDTH; p++) {
501 set_pixel(yoff, xoff + p, (bits & 0x01) ? fg : bg);
513 for (row = screen_start; row < screen_height; row++) {
514 for (col = 0; col < VISIBLE_SCREEN_WIDTH; col++) {
515 if ((col+screen_pan) < screen_width) {
516 lcd_drawc(screen[row][col+screen_pan], col, row);
518 lcd_drawc(' ', col, row);
523 lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
532 cyg_uint32 *lc0, *lc1, *lcn;
533 #ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
534 cyg_uint32 *fb_row0, *fb_row1, *fb_rown;
537 // First scroll up the virtual screen
538 #if ((SCREEN_WIDTH%4) != 0)
539 #error Scroll code optimized for screen with multiple of 4 columns
541 lc0 = (cyg_uint32 *)&screen[0][0];
542 lc1 = (cyg_uint32 *)&screen[1][0];
543 lcn = (cyg_uint32 *)&screen[screen_height][0];
547 c1 = &screen[screen_height-1][0];
548 for (col = 0; col < screen_width; col++) {
551 #ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
552 // Redrawing the screen in this mode is hard :-)
555 fb_row0 = lcd_fb(screen_start*FONT_HEIGHT, 0);
556 fb_row1 = lcd_fb((screen_start+1)*FONT_HEIGHT, 0);
557 fb_rown = lcd_fb(screen_end*FONT_HEIGHT, 0);
559 while (fb_row1 != fb_rown) {
560 *fb_row0++ = *fb_row1++;
563 // Optimized ARM assembly "move" code
568 "10: ldmia r1!,{r3-r10};"
569 "stmia r0!,{r3-r10};"
573 : "r"(fb_row0), "r"(fb_row1), "r"(fb_rown)
574 : "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10"
578 for (col = 0; col < screen_width; col++) {
579 lcd_drawc(' ', col, screen_end-1);
584 // Draw one character at the current position
589 lcd_drawc(screen[curY][curX], curX-screen_pan, curY);
602 if (curY < 0) curY = 0;
603 curX = screen_width-1;
607 if (((cyg_uint8)c < FIRST_CHAR) || ((cyg_uint8)c > LAST_CHAR)) c = '.';
608 screen[curY][curX] = c;
609 lcd_drawc(c, curX-screen_pan, curY);
611 if (curX == screen_width) {
616 if (curY >= screen_height) {
618 curY = (screen_height-1);
621 lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
625 // Basic LCD 'printf()' support
629 #define is_digit(c) ((c >= '0') && (c <= '9'))
632 _cvt(unsigned long val, char *buf, long radix, char *digits)
642 *cp++ = digits[val % radix];
655 lcd_vprintf(void (*putc)(cyg_int8), const char *fmt0, va_list ap)
658 int left_prec, right_prec, zero_fill, length, pad, pad_on_right;
661 while ((c = *fmt0++)) {
666 left_prec = right_prec = pad_on_right = 0;
677 while (is_digit(c)) {
678 left_prec = (left_prec * 10) + (c - '0');
684 while (is_digit(c)) {
685 right_prec = (right_prec * 10) + (c - '0');
689 right_prec = left_prec;
696 val = va_arg(ap, long);
703 length = _cvt(val, buf, 10, "0123456789");
706 length = _cvt(val, buf, 16, "0123456789abcdef");
709 length = _cvt(val, buf, 16, "0123456789ABCDEF");
714 cp = va_arg(ap, char *);
718 c = va_arg(ap, long /*char*/);
724 pad = left_prec - length;
745 while (length-- > 0) {
769 _lcd_printf(char const *fmt, ...)
775 ret = lcd_vprintf(lcd_putc, fmt, ap);
781 lcd_setbg(int red, int green, int blue)
783 bg = RGB_RED(red) | RGB_GREEN(green) | RGB_BLUE(blue);
787 lcd_setfg(int red, int green, int blue)
789 fg = RGB_RED(red) | RGB_GREEN(green) | RGB_BLUE(blue);
793 // Support LCD/keyboard (PS2) as a virtual I/O channel
794 // Adapted from i386/pcmb_screen.c
797 static int _timeout = 500;
800 lcd_comm_getc_nonblock(void* __ch_data, cyg_uint8* ch)
802 if( !aaed2000_KeyboardTest() )
804 *ch = aaed2000_KeyboardGetc();
809 lcd_comm_getc(void* __ch_data)
813 while (!lcd_comm_getc_nonblock(__ch_data, &ch)) ;
818 lcd_comm_putc(void* __ch_data, cyg_uint8 c)
824 lcd_comm_write(void* __ch_data, const cyg_uint8* __buf, cyg_uint32 __len)
827 CYGARC_HAL_SAVE_GP();
830 lcd_comm_putc(__ch_data, *__buf++);
832 CYGARC_HAL_RESTORE_GP();
837 lcd_comm_read(void* __ch_data, cyg_uint8* __buf, cyg_uint32 __len)
840 CYGARC_HAL_SAVE_GP();
843 *__buf++ = lcd_comm_getc(__ch_data);
845 CYGARC_HAL_RESTORE_GP();
850 lcd_comm_getc_timeout(void* __ch_data, cyg_uint8* ch)
855 delay_count = _timeout * 2; // delay in .5 ms steps
857 res = lcd_comm_getc_nonblock(__ch_data, ch);
858 if (res || 0 == delay_count--)
860 CYGACC_CALL_IF_DELAY_US(500);
866 lcd_comm_control(void *__ch_data, __comm_control_cmd_t __func, ...)
868 static int vector = 0;
870 static int irq_state = 0;
872 CYGARC_HAL_SAVE_GP();
875 case __COMMCTL_IRQ_ENABLE:
879 case __COMMCTL_IRQ_DISABLE:
883 case __COMMCTL_DBG_ISR_VECTOR:
886 case __COMMCTL_SET_TIMEOUT:
890 va_start(ap, __func);
893 _timeout = va_arg(ap, cyg_uint32);
898 case __COMMCTL_FLUSH_OUTPUT:
904 CYGARC_HAL_RESTORE_GP();
909 lcd_comm_isr(void *__ch_data, int* __ctrlc,
910 CYG_ADDRWORD __vector, CYG_ADDRWORD __data)
915 cyg_drv_interrupt_acknowledge(__vector);
917 if (lcd_comm_getc_nonblock(__ch_data, &ch)) {
922 return CYG_ISR_HANDLED;
934 hal_virtual_comm_table_t* comm;
935 int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
939 if (!aaed2000_KeyboardInit()) {
940 // No keyboard - no LCD display
944 cursor_enable = true;
948 // Setup procs in the vector table
949 CYGACC_CALL_IF_SET_CONSOLE_COMM(1); // FIXME - should be controlled by CDL
950 comm = CYGACC_CALL_IF_CONSOLE_PROCS();
951 //CYGACC_COMM_IF_CH_DATA_SET(*comm, chan);
952 CYGACC_COMM_IF_WRITE_SET(*comm, lcd_comm_write);
953 CYGACC_COMM_IF_READ_SET(*comm, lcd_comm_read);
954 CYGACC_COMM_IF_PUTC_SET(*comm, lcd_comm_putc);
955 CYGACC_COMM_IF_GETC_SET(*comm, lcd_comm_getc);
956 CYGACC_COMM_IF_CONTROL_SET(*comm, lcd_comm_control);
957 CYGACC_COMM_IF_DBG_ISR_SET(*comm, lcd_comm_isr);
958 CYGACC_COMM_IF_GETC_TIMEOUT_SET(*comm, lcd_comm_getc_timeout);
960 // Restore original console
961 CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
965 #ifdef CYGPKG_REDBOOT
968 // Get here when RedBoot is idle. If it's been long enough, then
969 // dim the LCD. The problem is - how to determine other activities
970 // so at this doesn't get in the way. In the default case, this will
971 // be called from RedBoot every 10ms (CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT)
973 #define MAX_IDLE_TIME (30*100)
978 static int idle_time = 0;
979 static bool was_idled = false;
983 if (++idle_time == MAX_IDLE_TIME) {
997 RedBoot_idle(idle, RedBoot_AFTER_NETIO);