]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/arm/arm9/aaed2000/v2_0/src/lcd_support.c
Initial revision
[karo-tx-redboot.git] / packages / hal / arm / arm9 / aaed2000 / v2_0 / src / lcd_support.c
1 //==========================================================================
2 //
3 //        Lcd_support.c
4 //
5 //        Agilent AAED2000 - LCD support routines
6 //
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.
12 //
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.
16 //
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
20 // for more details.
21 //
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.
25 //
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.
32 //
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.
35 //
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####
42 //
43 // Author(s):     gthomas
44 // Contributors:  gthomas
45 // Date:          2001-11-03
46 // Description:   Simple LCD support
47 //####DESCRIPTIONEND####
48
49 #include <pkgconf/hal.h>
50
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
56
57 #include <cyg/hal/aaed2000.h>  // Board definitions
58 #include <cyg/hal/lcd_support.h>
59 #include <cyg/hal/hal_cache.h>
60
61 #include <string.h>
62
63 #ifdef CYGPKG_ISOINFRA
64 # include <pkgconf/isoinfra.h>
65 # ifdef CYGINT_ISO_STDIO_FORMATTED_IO
66 #  include <stdio.h>  // sscanf
67 # endif
68 #endif
69
70 #include CYGHWR_MEMORY_LAYOUT_H
71 #define LCD_FRAMEBUFFER CYGMEM_REGION_lcd
72 static cyg_uint32 lcd_framebuffer;
73
74 #ifndef FALSE
75 #define FALSE 0
76 #define TRUE  1
77 #endif
78
79 // Physical dimensions of LCD display
80 #define DISPLAY_WIDTH  640
81 #define DISPLAY_HEIGHT 480
82
83 // Logical layout
84 #ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
85 #define LCD_WIDTH  DISPLAY_HEIGHT
86 #define LCD_HEIGHT DISPLAY_WIDTH
87 #else
88 #define LCD_WIDTH  DISPLAY_WIDTH
89 #define LCD_HEIGHT DISPLAY_HEIGHT
90 #endif
91 #define LCD_DEPTH   16
92
93 #define RGB_RED(x)   (((x)&0x1F)<<0)
94 #define RGB_GREEN(x) (((x)&0x1F)<<5)
95 #define RGB_BLUE(x)  (((x)&0x1F)<<10)
96
97 // Physical screen info
98 //static int lcd_depth  = LCD_DEPTH;  // Should be 1, 2, or 4
99 static int lcd_bpp;
100 #ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
101 static int lcd_width  = LCD_WIDTH;
102 #endif
103 static int lcd_height = LCD_HEIGHT;
104
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);
109 #endif
110
111 // Compute the location for a pixel within the framebuffer
112 static cyg_uint32 *
113 lcd_fb(int row, int col)
114 {
115     return (cyg_uint32 *)(lcd_framebuffer+(((row*DISPLAY_WIDTH)+col)*2));
116 }
117
118 void
119 lcd_on(bool enable)
120 {
121     cyg_uint32 ctl;
122
123     if (!enable) return;  // FIXME - need a way to [safely] blank screen
124
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
128     // Turn on Power
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);
137 }
138
139 // Initialize LCD hardware
140
141 void
142 lcd_init(int depth)
143 {
144     cyg_uint32 ctl;
145
146     HAL_VIRT_TO_PHYS_ADDRESS(LCD_FRAMEBUFFER, lcd_framebuffer);
147     lcd_clear();
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);
153         // Now wait a little
154         CYGACC_CALL_IF_DELAY_US(500);
155         // Turn off Power
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);
161     }
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
171 #endif
172     lcd_on(true);
173     lcd_bpp = 16;
174 }
175
176 // Get information about the frame buffer
177 int
178 lcd_getinfo(struct lcd_info *info)
179 {
180     if (lcd_bpp == 0) {
181         return 0;  // LCD not initialized
182     }
183     info->width = DISPLAY_WIDTH;
184     info->height = DISPLAY_HEIGHT;
185     info->bpp = lcd_bpp;
186     info->fb = (void*)LCD_FRAMEBUFFER;
187     info->rlen = DISPLAY_WIDTH * 2;
188     info->type = FB_TRUE_RGB555;
189     return 1; // Information valid
190 }
191
192 // Clear screen
193 void
194 lcd_clear(void)
195 {
196 #ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
197     cyg_uint32 *fb_row0, *fb_rown;
198     cyg_uint32 _bg = (bg<<16)|bg;
199
200     fb_row0 = lcd_fb(0, 0);
201     fb_rown = lcd_fb(lcd_height, 0);
202     while (fb_row0 != fb_rown) {
203         *fb_row0++ = _bg;
204     }
205 #else
206     int row, col;
207     for (row = 0;  row < lcd_height;  row++) {
208         for (col = 0;  col < lcd_width;  col++) {
209             set_pixel(row, col, bg);
210         }
211     }
212 #endif
213 }
214
215 #ifdef CYGSEM_AAED2000_LCD_COMM
216
217 //
218 // Additional support for LCD/Keyboard as 'console' device
219 //
220
221 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
222 #include "banner.xpm"
223 #endif
224 #include "font.h"
225
226 // Virtual screen info
227 static int curX = 0;  // Last used position
228 static int curY = 0;
229 //static int width = LCD_WIDTH / (FONT_WIDTH*NIBBLES_PER_PIXEL);
230 //static int height = LCD_HEIGHT / (FONT_HEIGHT*SCREEN_SCALE);
231
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;
241
242 // Usable area on screen [logical pixel rows]
243 static int screen_start = 0;                       
244 static int screen_end = LCD_HEIGHT/FONT_HEIGHT;
245
246 static bool cursor_enable = true;
247
248 // Functions
249 static void lcd_drawc(cyg_int8 c, int x, int y);
250
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
259 static void
260 set_pixel(int row, int col, unsigned short val)
261 {
262 //    fp->pixels[col][(DISPLAY_WIDTH-1)-row] = val;
263     int _row = (240-1) - col;
264     int _col = row;
265     unsigned char *pxptr = (unsigned char *)(0xC0000000 + (_row * 480) + ((_col * 3) / 2));
266
267     diag_printf("%s\n", __FUNCTION__);  return;
268     if ((row >= LCD_HEIGHT) || (col >= LCD_WIDTH)) return;
269     if (0)
270     {
271         int old = start_console();
272         diag_printf("row=%d/%d, col=%d/%d, pxptr = %p\n", row, _row, col, _col, pxptr);
273         end_console(old);
274     }
275     if ((row % 2) == 0) {
276         // Even row
277         *pxptr++ = RED(val) | (GREEN(val) << 4);
278         *pxptr = (*pxptr & 0xF0) | BLUE(val);   
279     } else {
280         // Odd row
281         *pxptr = (*pxptr & 0x0F) | (RED(val) << 4);
282         *++pxptr = GREEN(val) | (BLUE(val) << 4);
283     }
284 }
285 #else
286
287 static void
288 set_pixel(int row, int col, unsigned short val)
289 {
290     unsigned short *pix = (unsigned short *)lcd_fb(row, col);
291     *pix = val;
292 }
293 #endif
294
295 static int
296 _hexdigit(char c)
297 {
298     if ((c >= '0') && (c <= '9')) {
299         return c - '0';
300     } else
301     if ((c >= 'A') && (c <= 'F')) {
302         return (c - 'A') + 0x0A;
303     } else
304     if ((c >= 'a') && (c <= 'f')) {
305         return (c - 'a') + 0x0a;
306     }
307
308     return 0;
309 }
310
311 static int
312 _hex(char *cp)
313 {
314     return (_hexdigit(*cp)<<4) | _hexdigit(*(cp+1));
315 }
316
317 static unsigned short
318 parse_color(char *cp)
319 {
320     int red, green, blue;
321
322     while (*cp && (*cp != 'c')) cp++;
323     if (cp) {
324         cp += 2;
325         if (*cp == '#') {
326             red = _hex(cp+1);
327             green = _hex(cp+3);
328             blue = _hex(cp+5);
329 #ifdef USE_RGB565
330             return RGB_RED(red>>3) | RGB_GREEN(green>>2) | RGB_BLUE(blue>>3);
331 #else
332             return RGB_RED(red>>3) | RGB_GREEN(green>>3) | RGB_BLUE(blue>>3);
333 #endif
334         } else {
335             // Should be "None"
336             return 0xFFFF;
337         }
338     } else {
339         return 0xFFFF;
340     }
341 }
342
343 #ifndef CYGINT_ISO_STDIO_FORMATTED_IO
344 static int
345 get_int(char **_cp)
346 {
347     char *cp = *_cp;
348     char c;
349     int val = 0;
350     
351     while ((c = *cp++) && (c != ' ')) {
352         if ((c >= '0') && (c <= '9')) {
353             val = val * 10 + (c - '0');
354         } else {
355             return -1;
356         }
357     }
358     *_cp = cp;
359     return val;
360 }
361 #endif
362
363 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
364 int
365 show_xpm(char *xpm[], int screen_pos)
366 {
367     int i, row, col, offset;
368     char *cp;
369     int nrows, ncols, nclrs;
370     unsigned short colors[256];  // Mapped by character index
371
372     cp = xpm[0];
373 #ifdef CYGINT_ISO_STDIO_FORMATTED_IO
374     if (sscanf(cp, "%d %d %d", &ncols, &nrows, &nclrs) != 3) {
375 #else
376     if (((ncols = get_int(&cp)) < 0) ||
377         ((nrows = get_int(&cp)) < 0) ||
378         ((nclrs = get_int(&cp)) < 0)) {
379
380 #endif
381         diag_printf("Can't parse XPM data, sorry\n");
382         return 0;
383     }
384     // printf("%d rows, %d cols, %d colors\n", nrows, ncols, nclrs);
385
386     for (i = 0;  i < 256;  i++) {
387         colors[i] = 0x0000;
388     }
389     for (i = 0;  i < nclrs;  i++) {
390         cp = xpm[i+1];
391         colors[(unsigned int)*cp] = parse_color(&cp[1]);
392         // printf("Color[%c] = %x\n", *cp, colors[(unsigned int)*cp]);
393     }
394
395 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
396     offset = screen_pos;
397 #else
398     offset = screen_pos-nrows;
399 #endif
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++]);
404         }
405     }
406 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
407     screen_start = (nrows + (FONT_HEIGHT-1))/FONT_HEIGHT;
408     screen_end = LCD_HEIGHT/FONT_HEIGHT;
409     return offset+nrows;
410 #else    
411     screen_start = 0;
412     screen_height = offset / FONT_HEIGHT;
413     screen_end = screen_height;
414     return offset;
415 #endif
416 }
417 #endif
418
419 void
420 lcd_screen_clear(void)
421 {
422     int row, col, pos;
423     for (row = 0;  row < screen_height;  row++) {
424         for (col = 0;  col < screen_width;  col++) {
425             screen[row][col] = ' ';
426         }
427     }
428 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO
429     // Note: Row 0 seems to wrap incorrectly
430 #ifdef CYGOPT_AAED2000_LCD_COMM_LOGO_TOP
431     pos = 0;
432 #else
433     pos = (LCD_HEIGHT-1);
434 #endif
435     show_xpm(banner_xpm, pos);
436 #endif // CYGOPT_AAED2000_LCD_COMM_LOGO
437     curX = 0;  curY = screen_start;
438     if (cursor_enable) {
439         lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
440     }
441 }
442
443 // Position cursor
444 void
445 lcd_moveto(int X, int Y)
446 {
447     if (cursor_enable) {
448         lcd_drawc(screen[curY][curX], curX-screen_pan, curY);
449     }
450     if (X < 0) X = 0;
451     if (X >= screen_width) X = screen_width-1;
452     curX = X;
453     if (Y < screen_start) Y = screen_start;
454     if (Y >= screen_height) Y = screen_height-1;
455     curY = Y;
456     if (cursor_enable) {
457         lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
458     }
459 }
460
461 // Render a character at position (X,Y) with current background/foreground
462 static void
463 lcd_drawc(cyg_int8 c, int x, int y)
464 {
465     cyg_uint8 bits;
466     int l, p;
467     int xoff, yoff;
468 #ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
469     cyg_uint32 *fb;
470 #endif
471
472
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) {
484             case 0:
485                 *fb++ = (bg << 16) | bg;
486                 break;
487             case 1:
488                 *fb++ = (bg << 16) | fg;
489                 break;
490             case 2:
491                 *fb++ = (fg << 16) | bg;
492                 break;
493             case 3:
494                 *fb++ = (fg << 16) | fg;
495                 break;
496             }
497             bits >>= 2;
498         }
499 #else
500         for (p = 0;  p < FONT_WIDTH;  p++) {
501             set_pixel(yoff, xoff + p, (bits & 0x01) ? fg : bg);
502             bits >>= 1;
503         }
504 #endif
505     }
506 }
507
508 static void
509 lcd_refresh(void)
510 {
511     int row, col;
512
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);
517             } else {
518                 lcd_drawc(' ', col, row);
519             }
520         }
521     }
522     if (cursor_enable) {
523         lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
524     }
525 }
526
527 static void
528 lcd_scroll(void)
529 {
530     int col;
531     cyg_uint8 *c1;
532     cyg_uint32 *lc0, *lc1, *lcn;
533 #ifndef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
534     cyg_uint32 *fb_row0, *fb_row1, *fb_rown;
535 #endif
536
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
540 #endif
541     lc0 = (cyg_uint32 *)&screen[0][0];
542     lc1 = (cyg_uint32 *)&screen[1][0];
543     lcn = (cyg_uint32 *)&screen[screen_height][0];
544     while (lc1 != lcn) {
545         *lc0++ = *lc1++;
546     }
547     c1 = &screen[screen_height-1][0];
548     for (col = 0;  col < screen_width;  col++) {
549         *c1++ = 0x20;
550     }
551 #ifdef CYGSEM_AAED2000_LCD_PORTRAIT_MODE
552     // Redrawing the screen in this mode is hard :-)
553     lcd_refresh();
554 #else
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);
558 #if 1
559     while (fb_row1 != fb_rown) {
560         *fb_row0++ = *fb_row1++;
561     }
562 #else
563     // Optimized ARM assembly "move" code
564     asm __volatile(
565         "mov r0,%0;"
566         "mov r1,%1;"
567         "mov r2,%2;"
568         "10: ldmia r1!,{r3-r10};"
569         "stmia r0!,{r3-r10};"
570         "cmp r1,r2;"
571         "bne 10b"
572         :
573         : "r"(fb_row0), "r"(fb_row1), "r"(fb_rown)
574         : "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10"
575         );
576 #endif
577     // Erase bottom line
578     for (col = 0;  col < screen_width;  col++) {
579         lcd_drawc(' ', col, screen_end-1);
580     }
581 #endif
582 }
583
584 // Draw one character at the current position
585 void
586 lcd_putc(cyg_int8 c)
587 {
588     if (cursor_enable) {
589         lcd_drawc(screen[curY][curX], curX-screen_pan, curY);
590     }
591     switch (c) {
592     case '\r':
593         curX = 0;
594         break;
595     case '\n':
596         curY++;
597         break;
598     case '\b':
599         curX--;
600         if (curX < 0) {
601             curY--;
602             if (curY < 0) curY = 0;
603             curX = screen_width-1;
604         }
605         break;
606     default:
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);
610         curX++;
611         if (curX == screen_width) {
612             curY++;
613             curX = 0;
614         }
615     } 
616     if (curY >= screen_height) {
617         lcd_scroll();
618         curY = (screen_height-1);
619     }
620     if (cursor_enable) {
621         lcd_drawc(CURSOR_ON, curX-screen_pan, curY);
622     }
623 }
624
625 // Basic LCD 'printf()' support
626
627 #include <stdarg.h>
628
629 #define is_digit(c) ((c >= '0') && (c <= '9'))
630
631 static int
632 _cvt(unsigned long val, char *buf, long radix, char *digits)
633 {
634     char temp[80];
635     char *cp = temp;
636     int length = 0;
637     if (val == 0) {
638         /* Special case */
639         *cp++ = '0';
640     } else {
641         while (val) {
642             *cp++ = digits[val % radix];
643             val /= radix;
644         }
645     }
646     while (cp != temp) {
647         *buf++ = *--cp;
648         length++;
649     }
650     *buf = '\0';
651     return (length);
652 }
653
654 static int
655 lcd_vprintf(void (*putc)(cyg_int8), const char *fmt0, va_list ap)
656 {
657     char c, sign, *cp;
658     int left_prec, right_prec, zero_fill, length, pad, pad_on_right;
659     char buf[32];
660     long val;
661     while ((c = *fmt0++)) {
662         cp = buf;
663         length = 0;
664         if (c == '%') {
665             c = *fmt0++;
666             left_prec = right_prec = pad_on_right = 0;
667             if (c == '-') {
668                 c = *fmt0++;
669                 pad_on_right++;
670             }
671             if (c == '0') {
672                 zero_fill = TRUE;
673                 c = *fmt0++;
674             } else {
675                 zero_fill = FALSE;
676             }
677             while (is_digit(c)) {
678                 left_prec = (left_prec * 10) + (c - '0');
679                 c = *fmt0++;
680             }
681             if (c == '.') {
682                 c = *fmt0++;
683                 zero_fill++;
684                 while (is_digit(c)) {
685                     right_prec = (right_prec * 10) + (c - '0');
686                     c = *fmt0++;
687                 }
688             } else {
689                 right_prec = left_prec;
690             }
691             sign = '\0';
692             switch (c) {
693             case 'd':
694             case 'x':
695             case 'X':
696                 val = va_arg(ap, long);
697                 switch (c) {
698                 case 'd':
699                     if (val < 0) {
700                         sign = '-';
701                         val = -val;
702                     }
703                     length = _cvt(val, buf, 10, "0123456789");
704                     break;
705                 case 'x':
706                     length = _cvt(val, buf, 16, "0123456789abcdef");
707                     break;
708                 case 'X':
709                     length = _cvt(val, buf, 16, "0123456789ABCDEF");
710                     break;
711                 }
712                 break;
713             case 's':
714                 cp = va_arg(ap, char *);
715                 length = strlen(cp);
716                 break;
717             case 'c':
718                 c = va_arg(ap, long /*char*/);
719                 (*putc)(c);
720                 continue;
721             default:
722                 (*putc)('?');
723             }
724             pad = left_prec - length;
725             if (sign != '\0') {
726                 pad--;
727             }
728             if (zero_fill) {
729                 c = '0';
730                 if (sign != '\0') {
731                     (*putc)(sign);
732                     sign = '\0';
733                 }
734             } else {
735                 c = ' ';
736             }
737             if (!pad_on_right) {
738                 while (pad-- > 0) {
739                     (*putc)(c);
740                 }
741             }
742             if (sign != '\0') {
743                 (*putc)(sign);
744             }
745             while (length-- > 0) {
746                 (*putc)(c = *cp++);
747                 if (c == '\n') {
748                     (*putc)('\r');
749                 }
750             }
751             if (pad_on_right) {
752                 while (pad-- > 0) {
753                     (*putc)(' ');
754                 }
755             }
756         } else {
757             (*putc)(c);
758             if (c == '\n') {
759                 (*putc)('\r');
760             }
761         }
762     }
763
764     // FIXME
765     return 0;
766 }
767
768 int
769 _lcd_printf(char const *fmt, ...)
770 {
771     int ret;
772     va_list ap;
773
774     va_start(ap, fmt);
775     ret = lcd_vprintf(lcd_putc, fmt, ap);
776     va_end(ap);
777     return (ret);
778 }
779
780 void
781 lcd_setbg(int red, int green, int blue)
782 {
783     bg = RGB_RED(red) | RGB_GREEN(green) | RGB_BLUE(blue);
784 }
785
786 void
787 lcd_setfg(int red, int green, int blue)
788 {
789     fg = RGB_RED(red) | RGB_GREEN(green) | RGB_BLUE(blue);
790 }
791
792 //
793 // Support LCD/keyboard (PS2) as a virtual I/O channel
794 //   Adapted from i386/pcmb_screen.c
795 //
796
797 static int  _timeout = 500;
798
799 static cyg_bool
800 lcd_comm_getc_nonblock(void* __ch_data, cyg_uint8* ch)
801 {
802     if( !aaed2000_KeyboardTest() )
803         return false;
804     *ch = aaed2000_KeyboardGetc();
805     return true;
806 }
807
808 static cyg_uint8
809 lcd_comm_getc(void* __ch_data)
810 {
811     cyg_uint8 ch;
812
813     while (!lcd_comm_getc_nonblock(__ch_data, &ch)) ;
814     return ch;
815 }
816
817 static void
818 lcd_comm_putc(void* __ch_data, cyg_uint8 c)
819 {
820     lcd_putc(c);
821 }
822
823 static void
824 lcd_comm_write(void* __ch_data, const cyg_uint8* __buf, cyg_uint32 __len)
825 {
826 #if 0
827     CYGARC_HAL_SAVE_GP();
828
829     while(__len-- > 0)
830         lcd_comm_putc(__ch_data, *__buf++);
831
832     CYGARC_HAL_RESTORE_GP();
833 #endif
834 }
835
836 static void
837 lcd_comm_read(void* __ch_data, cyg_uint8* __buf, cyg_uint32 __len)
838 {
839 #if 0
840     CYGARC_HAL_SAVE_GP();
841
842     while(__len-- > 0)
843         *__buf++ = lcd_comm_getc(__ch_data);
844
845     CYGARC_HAL_RESTORE_GP();
846 #endif
847 }
848
849 static cyg_bool
850 lcd_comm_getc_timeout(void* __ch_data, cyg_uint8* ch)
851 {
852     int delay_count;
853     cyg_bool res;
854
855     delay_count = _timeout * 2; // delay in .5 ms steps
856     for(;;) {
857         res = lcd_comm_getc_nonblock(__ch_data, ch);
858         if (res || 0 == delay_count--)
859             break;
860         CYGACC_CALL_IF_DELAY_US(500);
861     }
862     return res;
863 }
864
865 static int
866 lcd_comm_control(void *__ch_data, __comm_control_cmd_t __func, ...)
867 {
868     static int vector = 0;
869     int ret = -1;
870     static int irq_state = 0;
871
872     CYGARC_HAL_SAVE_GP();
873
874     switch (__func) {
875     case __COMMCTL_IRQ_ENABLE:
876         ret = irq_state;
877         irq_state = 1;
878         break;
879     case __COMMCTL_IRQ_DISABLE:
880         ret = irq_state;
881         irq_state = 0;
882         break;
883     case __COMMCTL_DBG_ISR_VECTOR:
884         ret = vector;
885         break;
886     case __COMMCTL_SET_TIMEOUT:
887     {
888         va_list ap;
889
890         va_start(ap, __func);
891
892         ret = _timeout;
893         _timeout = va_arg(ap, cyg_uint32);
894
895         va_end(ap);
896         break;
897     }
898     case __COMMCTL_FLUSH_OUTPUT:
899         ret = 0;
900         break;
901     default:
902         break;
903     }
904     CYGARC_HAL_RESTORE_GP();
905     return ret;
906 }
907
908 static int
909 lcd_comm_isr(void *__ch_data, int* __ctrlc, 
910            CYG_ADDRWORD __vector, CYG_ADDRWORD __data)
911 {
912 #if 0
913     char ch;
914
915     cyg_drv_interrupt_acknowledge(__vector);
916     *__ctrlc = 0;
917     if (lcd_comm_getc_nonblock(__ch_data, &ch)) {
918         if (ch == 0x03) {
919             *__ctrlc = 1;
920         }
921     }
922     return CYG_ISR_HANDLED;
923 #else
924     return 0;
925 #endif
926 }
927
928 void
929 lcd_comm_init(void)
930 {
931     static int init = 0;
932
933     if (!init) {
934         hal_virtual_comm_table_t* comm;
935         int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
936
937         init = 1;
938         lcd_on(false);
939         if (!aaed2000_KeyboardInit()) {
940             // No keyboard - no LCD display
941             return;
942         }
943         // Initialize screen
944         cursor_enable = true;
945         lcd_init(16);
946         lcd_screen_clear();
947
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);
959
960         // Restore original console
961         CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
962     }
963 }
964
965 #ifdef CYGPKG_REDBOOT
966 #include <redboot.h>
967
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)
972
973 #define MAX_IDLE_TIME (30*100)
974
975 static void
976 idle(bool is_idle)
977 {
978     static int idle_time = 0;
979     static bool was_idled = false;
980
981     if (is_idle) {
982         if (!was_idled) {
983             if (++idle_time == MAX_IDLE_TIME) {
984                 was_idled = true;
985                 lcd_on(false);
986             }
987         }
988     } else {        
989         idle_time = 0;
990         if (was_idled) {
991             was_idled = false;
992                 lcd_on(true);
993         }
994     }
995 }
996
997 RedBoot_idle(idle, RedBoot_AFTER_NETIO);
998 #endif
999 #endif