]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/arm/edb7xxx/v2_0/misc/lcd_support.c
Initial revision
[karo-tx-redboot.git] / packages / hal / arm / edb7xxx / v2_0 / misc / lcd_support.c
1 //==========================================================================
2 //
3 //        lcd_support.c
4 //
5 //        Cirrus EDB7xxx eval board LCD support code
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:          1999-09-01
46 // Description:   Functions to drive LCD display
47 //####DESCRIPTIONEND####
48
49 // 8x8 Font - from Helios
50
51 #define FIRST_CHAR 0x20
52 #define LAST_CHAR  0x7E
53 #define FONT_HEIGHT 8
54 #define FONT_WIDTH  8
55 #define CURSOR_ON  0x5F
56 #define CURSOR_OFF 0x20
57 static char font_table[LAST_CHAR-FIRST_CHAR+1][8] =
58 {
59         {        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /*   */
60         {        0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00 }, /* ! */
61         {        0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* " */
62         {        0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00 }, /* # */
63         {        0x30, 0xFC, 0x16, 0x7C, 0xD0, 0x7E, 0x18, 0x00 }, /* $ */
64         {        0x06, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x60, 0x00 }, /* % */
65         {        0x1C, 0x36, 0x36, 0x1C, 0xB6, 0x66, 0xDC, 0x00 }, /* & */
66         {        0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ' */
67         {        0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00 }, /* ( */
68         {        0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00 }, /* ) */
69         {        0x00, 0x18, 0x7E, 0x3C, 0x7E, 0x18, 0x00, 0x00 }, /* * */
70         {        0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00 }, /* + */
71         {        0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C }, /* , */
72         {        0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00 }, /* - */
73         {        0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00 }, /* . */
74         {        0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00 }, /* / */
75         {        0x3C, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x3C, 0x00 }, /* 0 */
76         {        0x18, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00 }, /* 1 */
77         {        0x3C, 0x66, 0x60, 0x30, 0x18, 0x0C, 0x7E, 0x00 }, /* 2 */
78         {        0x3C, 0x66, 0x60, 0x38, 0x60, 0x66, 0x3C, 0x00 }, /* 3 */
79         {        0x30, 0x38, 0x3C, 0x36, 0x7E, 0x30, 0x30, 0x00 }, /* 4 */
80         {        0x7E, 0x06, 0x3E, 0x60, 0x60, 0x66, 0x3C, 0x00 }, /* 5 */
81         {        0x38, 0x0C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00 }, /* 6 */
82         {        0x7E, 0x60, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00 }, /* 7 */
83         {        0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00 }, /* 8 */
84         {        0x3C, 0x66, 0x66, 0x7C, 0x60, 0x30, 0x1C, 0x00 }, /* 9 */
85         {        0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00 }, /* : */
86         {        0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x0C }, /* ; */
87         {        0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00 }, /* < */
88         {        0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00 }, /* = */
89         {        0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00 }, /* > */
90         {        0x3C, 0x66, 0x30, 0x18, 0x18, 0x00, 0x18, 0x00 }, /* ? */
91         {        0x3C, 0x66, 0x76, 0x56, 0x76, 0x06, 0x3C, 0x00 }, /* @ */
92         {        0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00 }, /* A */
93         {        0x3E, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00 }, /* B */
94         {        0x3C, 0x66, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00 }, /* C */
95         {        0x1E, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1E, 0x00 }, /* D */
96         {        0x7E, 0x06, 0x06, 0x3E, 0x06, 0x06, 0x7E, 0x00 }, /* E */
97         {        0x7E, 0x06, 0x06, 0x3E, 0x06, 0x06, 0x06, 0x00 }, /* F */
98         {        0x3C, 0x66, 0x06, 0x76, 0x66, 0x66, 0x3C, 0x00 }, /* G */
99         {        0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00 }, /* H */
100         {        0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00 }, /* I */
101         {        0x7C, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00 }, /* J */
102         {        0x66, 0x36, 0x1E, 0x0E, 0x1E, 0x36, 0x66, 0x00 }, /* K */
103         {        0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00 }, /* L */
104         {        0xC6, 0xEE, 0xFE, 0xD6, 0xD6, 0xC6, 0xC6, 0x00 }, /* M */
105         {        0x66, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00 }, /* N */
106         {        0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00 }, /* O */
107         {        0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00 }, /* P */
108         {        0x3C, 0x66, 0x66, 0x66, 0x56, 0x36, 0x6C, 0x00 }, /* Q */
109         {        0x3E, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x66, 0x00 }, /* R */
110         {        0x3C, 0x66, 0x06, 0x3C, 0x60, 0x66, 0x3C, 0x00 }, /* S */
111         {        0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 }, /* T */
112         {        0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00 }, /* U */
113         {        0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00 }, /* V */
114         {        0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0xEE, 0xC6, 0x00 }, /* W */
115         {        0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00 }, /* X */
116         {        0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00 }, /* Y */
117         {        0x7E, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00 }, /* Z */
118         {        0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00 }, /* [ */
119         {        0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00 }, /* \ */
120         {        0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x00 }, /* ] */ 
121         {        0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ^ */
122         {        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, /* _ */
123         {        0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* ` */
124         {        0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00 }, /* a */
125         {        0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00 }, /* b */
126         {        0x00, 0x00, 0x3C, 0x66, 0x06, 0x66, 0x3C, 0x00 }, /* c */
127         {        0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00 }, /* d */
128         {        0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00 }, /* e */
129         {        0x38, 0x0C, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x00 }, /* f */
130         {        0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x3C }, /* g */
131         {        0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00 }, /* h */
132         {        0x18, 0x00, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00 }, /* i */
133         {        0x18, 0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x0E }, /* j */
134         {        0x06, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x00 }, /* k */
135         {        0x1C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00 }, /* l */
136         {        0x00, 0x00, 0x6C, 0xFE, 0xD6, 0xD6, 0xC6, 0x00 }, /* m */
137         {        0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00 }, /* n */
138         {        0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00 }, /* o */
139         {        0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x06 }, /* p */
140         {        0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0xE0 }, /* q */
141         {        0x00, 0x00, 0x36, 0x6E, 0x06, 0x06, 0x06, 0x00 }, /* r */
142         {        0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00 }, /* s */
143         {        0x0C, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x38, 0x00 }, /* t */
144         {        0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00 }, /* u */
145         {        0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00 }, /* v */
146         {        0x00, 0x00, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00 }, /* w */
147         {        0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00 }, /* x */
148         {        0x00, 0x00, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x3C }, /* y */
149         {        0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00 }, /* z */
150         {        0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00 }, /* { */
151         {        0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00 }, /* | */
152         {        0x0C, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0C, 0x00 }, /* } */
153         {        0x8C, 0xD6, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00 }  /* ~ */
154 };
155
156 #define ALPS_LH
157 #ifdef ALPS_LH
158 #define LCD_WIDTH  640
159 #define LCD_HEIGHT 240
160 #else
161 #define LCD_WIDTH  320
162 #define LCD_HEIGHT 240
163 #endif
164 // This can be 1 or 2
165 #define SCREEN_SCALE 2
166 #define NIBBLES_PER_PIXEL (1*SCREEN_SCALE)
167 #define PIXELS_PER_BYTE (2/NIBBLES_PER_PIXEL)
168 #define PIXEL_MASK      ((1<<PIXELS_PER_BYTE)-1)
169
170 // Physical screen info
171 static int lcd_depth;  // Should be 1, 2, or 4
172 static int lcd_width  = LCD_WIDTH;
173 static int lcd_height = LCD_HEIGHT;
174 static cyg_uint8 *lcd_base = (cyg_uint8 *)0xC0000000;
175
176 // Virtual screen info
177 static int curX = 0;  // Last used position
178 static int curY = 0;
179 static int width = LCD_WIDTH / (FONT_WIDTH*NIBBLES_PER_PIXEL);
180 static int height = LCD_HEIGHT / (FONT_HEIGHT*SCREEN_SCALE);
181 static int fg = 0x0F;
182 static int bg = 0x00;
183
184 // Functions
185 static void lcd_drawc(cyg_int8 c, int x, int y);
186
187 void
188 lcd_on(int depth)
189 {
190     cyg_int32 ctl_word = 0;
191     lcd_depth = depth;
192     switch (depth) {
193     case 1:
194         *(volatile cyg_int32 *)PALLSW = 0xF0F0F0F0;
195         *(volatile cyg_int32 *)PALMSW = 0xF0F0F0F0;
196         ctl_word = 0;
197         break;
198     case 2:
199         *(volatile cyg_int32 *)PALLSW = 0xFA50FA50;
200         *(volatile cyg_int32 *)PALMSW = 0xFA50FA50;
201         ctl_word = LCDCON_GSEN;
202         break;
203     case 4:
204         *(volatile cyg_int32 *)PALLSW = 0x76543210;
205         *(volatile cyg_int32 *)PALMSW = 0xFEDCBA98;
206         ctl_word = LCDCON_GSEN | LCDCON_GSMD;
207         break;
208     }
209     // Video buffer size
210     ctl_word |= ((lcd_width * lcd_height * lcd_depth) / 128 - 1) << LCDCON_BUFSIZ_S;
211     // Line length
212     ctl_word |= ((lcd_width / 16) - 1) << LCDCON_LINE_LENGTH_S;
213     // Pixel prescale
214     ctl_word |= ((526628 / (lcd_height * lcd_width)) - 1) << LCDCON_PIX_PRESCALE_S;
215     // AC frequency bias
216     ctl_word |= 0 << LCDCON_AC_PRESCALE_S;
217     *(volatile cyg_int32 *)LCDCON = ctl_word;
218     diag_printf("Depth: %d, ctl: %x\n", depth, ctl_word);
219
220 #ifdef ALPS_LH
221 #define LCD_DCDC      0x02
222 #define LCD_ENABLE    0x04
223 #define LCD_BACKLIGHT 0x08
224 #define LCD_INIT (LCD_DCDC|LCD_ENABLE|LCD_BACKLIGHT)
225     *(volatile cyg_uint8 *)PDDDR   = 0x40;      // 1's are inputs (backwards from A/B/E)
226     *(volatile cyg_uint8 *)PDDR    = LCD_INIT;  // Enable video + backlight + DC-DC converter
227 #else
228 #ifdef _CL7111
229     *(volatile cyg_uint8 *)PDDR    = 0x20;  // Enable video
230     *(volatile cyg_uint8 *)PEDDR   = 0x0F;  // Register E data direction
231     *(volatile cyg_uint8 *)PEDR   |= 0x01;  // Enable PE2
232
233     *(volatile cyg_uint32 *)PMPCON = 0x500; // Enable DC-to-DC pump #1
234 #endif
235 #endif
236
237     *(volatile cyg_uint8 *)FRBADDR = 0x0C;  // Highest order nibble of LCD frame address
238  
239     *(volatile cyg_uint32 *)SYSCON1 |= SYSCON1_LCDEN;
240 }
241
242 // Clear screen
243 void
244 lcd_clear(void)
245 {
246     cyg_int8 *ptr = lcd_base;
247     int i;
248     for (i = 0;  i < (lcd_width*lcd_height*lcd_depth)/8;  i++) {
249         *ptr++ = 0;
250     }
251     curX = 0;  curY = 0;
252     lcd_drawc(CURSOR_ON, curX, curY);
253 }
254
255 // Position cursor
256 void
257 lcd_moveto(int X, int Y)
258 {
259     lcd_drawc(CURSOR_OFF, curX, curY);
260     if (X < 0) X = 0;
261     if (X >= width) X = width-1;
262     curX = X;
263     if (Y < 0) Y = 0;
264     if (Y >= height) Y = height-1;
265     curY = Y;
266     lcd_drawc(CURSOR_ON, curX, curY);
267 }
268
269 // Render a character at position (X,Y) with current background/foreground
270 static void
271 lcd_drawc(cyg_int8 c, int x, int y)
272 {
273     // Note: this only works for fonts which are a multiple of 8 bits wide!
274     cyg_uint8 *bufptr, bits;
275     int l, p, w;
276     switch (lcd_depth) {
277     case 1:
278         bufptr = lcd_base + ((y * FONT_HEIGHT * (lcd_width*lcd_depth)) + (x * FONT_WIDTH)) / 8;
279         for (l = 0;  l < FONT_HEIGHT;  l++) {
280             *bufptr = font_table[c][l];
281             bufptr += (lcd_width*lcd_depth)/8;
282         }
283         break;
284     case 2:
285         diag_printf("Depth 2 not implemented\n");
286         break;
287     case 4:
288         bufptr = lcd_base + ((y * (FONT_HEIGHT*SCREEN_SCALE) * (lcd_width*lcd_depth)) 
289                              + (x * (FONT_WIDTH*SCREEN_SCALE) * lcd_depth)) / 8;
290         for (l = 0;  l < FONT_HEIGHT;  l++) {
291             for (w = 0;  w < SCREEN_SCALE;  w++) {
292                 bits = font_table[c-FIRST_CHAR][l]; 
293                 for (p = 0;  p < 8;  p += PIXELS_PER_BYTE) {
294                     switch (bits & PIXEL_MASK) {
295                     case 0:
296                         *bufptr++ = (bg << 4) | bg;
297                         break;
298                     case 1:
299 #if SCREEN_SCALE == 1
300                         *bufptr++ = (bg << 4) | fg;
301 #else
302                         *bufptr++ = (fg << 4) | fg;
303 #endif
304                         break;
305 #if SCREEN_SCALE == 1
306                     case 2:
307                         *bufptr++ = (fg << 4) | bg;
308                         break;
309                     case 3:
310                         *bufptr++ = (fg << 4) | fg;
311                         break;
312 #endif
313                     }
314                     bits >>= PIXELS_PER_BYTE;
315                 }
316                 bufptr += (lcd_width*lcd_depth)/8 - (8/PIXELS_PER_BYTE);
317             }
318         }
319         break;
320     }
321 }
322
323 // Draw one character at the current position
324 void
325 lcd_putc(cyg_int8 c)
326 {
327     lcd_drawc(CURSOR_OFF, curX, curY);
328     switch (c) {
329     case '\r':
330         curX = 0;
331         break;
332     case '\n':
333         curY++;
334         break;
335     case '\b':
336         curX--;
337         if (curX < 0) {
338             curY--;
339             if (curY < 0) curY = 0;
340             curX = width-1;
341         }
342         break;
343     default:
344         lcd_drawc(c, curX, curY);
345         curX++;
346         if (curX == width) {
347             curY++;
348             curX = 0;
349         }
350     } 
351     lcd_drawc(CURSOR_ON, curX, curY);
352 }
353
354 // Basic LCD 'printf()' support
355
356 #ifndef FALSE
357 #define FALSE 0
358 #define TRUE  1
359 #endif
360
361 #include <stdarg.h>
362
363 #define is_digit(c) ((c >= '0') && (c <= '9'))
364
365 static int
366 _cvt(unsigned long val, char *buf, long radix, char *digits)
367 {
368     char temp[80];
369     char *cp = temp;
370     int length = 0;
371     if (val == 0) {
372         /* Special case */
373         *cp++ = '0';
374     } else {
375         while (val) {
376             *cp++ = digits[val % radix];
377             val /= radix;
378         }
379     }
380     while (cp != temp) {
381         *buf++ = *--cp;
382         length++;
383     }
384     *buf = '\0';
385     return (length);
386 }
387
388 int
389 lcd_vprintf(void (*putc)(cyg_int8), const char *fmt0, va_list ap)
390 {
391     char c, sign, *cp;
392     int left_prec, right_prec, zero_fill, length, pad, pad_on_right;
393     char buf[32];
394     long val;
395     while ((c = *fmt0++)) {
396         cp = buf;
397         length = 0;
398         if (c == '%') {
399             c = *fmt0++;
400             left_prec = right_prec = pad_on_right = 0;
401             if (c == '-') {
402                 c = *fmt0++;
403                 pad_on_right++;
404             }
405             if (c == '0') {
406                 zero_fill = TRUE;
407                 c = *fmt0++;
408             } else {
409                 zero_fill = FALSE;
410             }
411             while (is_digit(c)) {
412                 left_prec = (left_prec * 10) + (c - '0');
413                 c = *fmt0++;
414             }
415             if (c == '.') {
416                 c = *fmt0++;
417                 zero_fill++;
418                 while (is_digit(c)) {
419                     right_prec = (right_prec * 10) + (c - '0');
420                     c = *fmt0++;
421                 }
422             } else {
423                 right_prec = left_prec;
424             }
425             sign = '\0';
426             switch (c) {
427             case 'd':
428             case 'x':
429             case 'X':
430                 val = va_arg(ap, long);
431                 switch (c) {
432                 case 'd':
433                     if (val < 0) {
434                         sign = '-';
435                         val = -val;
436                     }
437                     length = _cvt(val, buf, 10, "0123456789");
438                     break;
439                 case 'x':
440                     length = _cvt(val, buf, 16, "0123456789abcdef");
441                     break;
442                 case 'X':
443                     length = _cvt(val, buf, 16, "0123456789ABCDEF");
444                     break;
445                 }
446                 break;
447             case 's':
448                 cp = va_arg(ap, char *);
449                 length = strlen(cp);
450                 break;
451             case 'c':
452                 c = va_arg(ap, long /*char*/);
453                 (*putc)(c);
454                 continue;
455             default:
456                 (*putc)('?');
457             }
458             pad = left_prec - length;
459             if (sign != '\0') {
460                 pad--;
461             }
462             if (zero_fill) {
463                 c = '0';
464                 if (sign != '\0') {
465                     (*putc)(sign);
466                     sign = '\0';
467                 }
468             } else {
469                 c = ' ';
470             }
471             if (!pad_on_right) {
472                 while (pad-- > 0) {
473                     (*putc)(c);
474                 }
475             }
476             if (sign != '\0') {
477                 (*putc)(sign);
478             }
479             while (length-- > 0) {
480                 (*putc)(c = *cp++);
481                 if (c == '\n') {
482                     (*putc)('\r');
483                 }
484             }
485             if (pad_on_right) {
486                 while (pad-- > 0) {
487                     (*putc)(' ');
488                 }
489             }
490         } else {
491             (*putc)(c);
492             if (c == '\n') {
493                 (*putc)('\r');
494             }
495         }
496     }
497 }
498
499 int
500 lcd_printf(char const *fmt, ...)
501 {
502         int ret;
503         va_list ap;
504
505         va_start(ap, fmt);
506         ret = lcd_vprintf(lcd_putc, fmt, ap);
507         va_end(ap);
508         return (ret);
509 }
510