2 * Copyright (c) 2015 Google, Inc
3 * (C) Copyright 2001-2015
4 * DENX Software Engineering -- wd@denx.de
5 * Compulab Ltd - http://compulab.co.il/
6 * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
8 * SPDX-License-Identifier: GPL-2.0+
12 #include <linux/ctype.h>
15 #include <video_console.h>
16 #include <video_font.h> /* Get font data, width and height */
18 /* By default we scroll by a single line */
19 #ifndef CONFIG_CONSOLE_SCROLL_LINES
20 #define CONFIG_CONSOLE_SCROLL_LINES 1
23 int vidconsole_putc_xy(struct udevice
*dev
, uint x
, uint y
, char ch
)
25 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
29 return ops
->putc_xy(dev
, x
, y
, ch
);
32 int vidconsole_move_rows(struct udevice
*dev
, uint rowdst
, uint rowsrc
,
35 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
39 return ops
->move_rows(dev
, rowdst
, rowsrc
, count
);
42 int vidconsole_set_row(struct udevice
*dev
, uint row
, int clr
)
44 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
48 return ops
->set_row(dev
, row
, clr
);
51 static int vidconsole_entry_start(struct udevice
*dev
)
53 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
55 if (!ops
->entry_start
)
57 return ops
->entry_start(dev
);
60 /* Move backwards one space */
61 static int vidconsole_back(struct udevice
*dev
)
63 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
64 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
68 ret
= ops
->backspace(dev
);
73 priv
->xcur_frac
-= VID_TO_POS(priv
->x_charsize
);
74 if (priv
->xcur_frac
< priv
->xstart_frac
) {
75 priv
->xcur_frac
= (priv
->cols
- 1) *
76 VID_TO_POS(priv
->x_charsize
);
77 priv
->ycur
-= priv
->y_charsize
;
81 video_sync(dev
->parent
);
86 /* Move to a newline, scrolling the display if necessary */
87 static void vidconsole_newline(struct udevice
*dev
)
89 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
90 struct udevice
*vid_dev
= dev
->parent
;
91 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid_dev
);
92 const int rows
= CONFIG_CONSOLE_SCROLL_LINES
;
95 priv
->xcur_frac
= priv
->xstart_frac
;
96 priv
->ycur
+= priv
->y_charsize
;
98 /* Check if we need to scroll the terminal */
99 if ((priv
->ycur
+ priv
->y_charsize
) / priv
->y_charsize
> priv
->rows
) {
100 vidconsole_move_rows(dev
, 0, rows
, priv
->rows
- rows
);
101 for (i
= 0; i
< rows
; i
++)
102 vidconsole_set_row(dev
, priv
->rows
- i
- 1,
103 vid_priv
->colour_bg
);
104 priv
->ycur
-= rows
* priv
->y_charsize
;
108 video_sync(dev
->parent
);
111 static const struct {
116 { 0x00, 0x00, 0x00 }, /* black */
117 { 0xff, 0x00, 0x00 }, /* red */
118 { 0x00, 0xff, 0x00 }, /* green */
119 { 0xff, 0xff, 0x00 }, /* yellow */
120 { 0x00, 0x00, 0xff }, /* blue */
121 { 0xff, 0x00, 0xff }, /* magenta */
122 { 0x00, 0xff, 0xff }, /* cyan */
123 { 0xff, 0xff, 0xff }, /* white */
126 static void set_color(struct video_priv
*priv
, unsigned idx
, unsigned *c
)
128 switch (priv
->bpix
) {
130 *c
= ((colors
[idx
].r
>> 3) << 0) |
131 ((colors
[idx
].g
>> 2) << 5) |
132 ((colors
[idx
].b
>> 3) << 11);
136 (colors
[idx
].r
<< 0) |
137 (colors
[idx
].g
<< 8) |
138 (colors
[idx
].b
<< 16);
141 /* unsupported, leave current color in place */
146 static char *parsenum(char *s
, int *num
)
149 *num
= simple_strtol(s
, &end
, 10);
154 * Process a character while accumulating an escape string. Chars are
155 * accumulated into escape_buf until the end of escape sequence is
156 * found, at which point the sequence is parsed and processed.
158 static void vidconsole_escape_char(struct udevice
*dev
, char ch
)
160 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
162 if (!IS_ENABLED(CONFIG_VIDEO_ANSI
))
165 /* Sanity checking for bogus ESC sequences: */
166 if (priv
->escape_len
>= sizeof(priv
->escape_buf
))
168 if (priv
->escape_len
== 0 && ch
!= '[')
171 priv
->escape_buf
[priv
->escape_len
++] = ch
;
174 * Escape sequences are terminated by a letter, so keep
175 * accumulating until we get one:
181 * clear escape mode first, otherwise things will get highly
182 * surprising if you hit any debug prints that come back to
191 char *s
= priv
->escape_buf
;
194 * Set cursor position: [%d;%df or [%d;%dH
197 s
= parsenum(s
, &row
);
199 s
= parsenum(s
, &col
);
201 priv
->ycur
= row
* priv
->y_charsize
;
202 priv
->xcur_frac
= priv
->xstart_frac
+
203 VID_TO_POS(col
* priv
->x_charsize
);
211 * Clear part/all screen:
212 * [J or [0J - clear screen from cursor down
213 * [1J - clear screen from cursor up
214 * [2J - clear entire screen
216 * TODO we really only handle entire-screen case, others
217 * probably require some additions to video-uclass (and
218 * are not really needed yet by efi_console)
220 parsenum(priv
->escape_buf
+ 1, &mode
);
223 video_clear(dev
->parent
);
224 video_sync(dev
->parent
);
226 priv
->xcur_frac
= priv
->xstart_frac
;
228 debug("unsupported clear mode: %d\n", mode
);
233 struct video_priv
*vid_priv
= dev_get_uclass_priv(dev
->parent
);
234 char *s
= priv
->escape_buf
;
235 char *end
= &priv
->escape_buf
[priv
->escape_len
];
238 * Set graphics mode: [%d;...;%dm
240 * Currently only supports the color attributes:
269 s
= parsenum(s
, &val
);
275 set_color(vid_priv
, val
- 30,
276 (unsigned *)&vid_priv
->colour_fg
);
280 set_color(vid_priv
, val
- 40,
281 (unsigned *)&vid_priv
->colour_bg
);
284 /* unknown/unsupported */
292 debug("unrecognized escape sequence: %*s\n",
293 priv
->escape_len
, priv
->escape_buf
);
299 /* something went wrong, just revert to normal mode: */
303 int vidconsole_put_char(struct udevice
*dev
, char ch
)
305 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
309 vidconsole_escape_char(dev
, ch
);
315 priv
->escape_len
= 0;
322 priv
->xcur_frac
= priv
->xstart_frac
;
325 vidconsole_newline(dev
);
326 vidconsole_entry_start(dev
);
328 case '\t': /* Tab (8 chars alignment) */
329 priv
->xcur_frac
= ((priv
->xcur_frac
/ priv
->tab_width_frac
)
330 + 1) * priv
->tab_width_frac
;
332 if (priv
->xcur_frac
>= priv
->xsize_frac
)
333 vidconsole_newline(dev
);
336 vidconsole_back(dev
);
341 * Failure of this function normally indicates an unsupported
342 * colour depth. Check this and return an error to help with
345 ret
= vidconsole_putc_xy(dev
, priv
->xcur_frac
, priv
->ycur
, ch
);
346 if (ret
== -EAGAIN
) {
347 vidconsole_newline(dev
);
348 ret
= vidconsole_putc_xy(dev
, priv
->xcur_frac
,
353 priv
->xcur_frac
+= ret
;
355 if (priv
->xcur_frac
>= priv
->xsize_frac
)
356 vidconsole_newline(dev
);
363 static void vidconsole_putc(struct stdio_dev
*sdev
, const char ch
)
365 struct udevice
*dev
= sdev
->priv
;
367 vidconsole_put_char(dev
, ch
);
368 video_sync(dev
->parent
);
371 static void vidconsole_puts(struct stdio_dev
*sdev
, const char *s
)
373 struct udevice
*dev
= sdev
->priv
;
376 vidconsole_put_char(dev
, *s
++);
377 video_sync(dev
->parent
);
380 /* Set up the number of rows and colours (rotated drivers override this) */
381 static int vidconsole_pre_probe(struct udevice
*dev
)
383 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
384 struct udevice
*vid
= dev
->parent
;
385 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid
);
387 priv
->xsize_frac
= VID_TO_POS(vid_priv
->xsize
);
392 /* Register the device with stdio */
393 static int vidconsole_post_probe(struct udevice
*dev
)
395 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
396 struct stdio_dev
*sdev
= &priv
->sdev
;
398 if (!priv
->tab_width_frac
)
399 priv
->tab_width_frac
= VID_TO_POS(priv
->x_charsize
) * 8;
402 snprintf(sdev
->name
, sizeof(sdev
->name
), "vidconsole%d",
405 strcpy(sdev
->name
, "vidconsole");
408 sdev
->flags
= DEV_FLAGS_OUTPUT
;
409 sdev
->putc
= vidconsole_putc
;
410 sdev
->puts
= vidconsole_puts
;
413 return stdio_register(sdev
);
416 UCLASS_DRIVER(vidconsole
) = {
417 .id
= UCLASS_VIDEO_CONSOLE
,
418 .name
= "vidconsole0",
419 .pre_probe
= vidconsole_pre_probe
,
420 .post_probe
= vidconsole_post_probe
,
421 .per_device_auto_alloc_size
= sizeof(struct vidconsole_priv
),
424 void vidconsole_position_cursor(struct udevice
*dev
, unsigned col
, unsigned row
)
426 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
427 struct udevice
*vid_dev
= dev
->parent
;
428 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid_dev
);
430 priv
->xcur_frac
= VID_TO_POS(min_t(short, col
, vid_priv
->xsize
- 1));
431 priv
->ycur
= min_t(short, row
, vid_priv
->ysize
- 1);
434 static int do_video_setcursor(cmd_tbl_t
*cmdtp
, int flag
, int argc
,
437 unsigned int col
, row
;
441 return CMD_RET_USAGE
;
443 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE
, &dev
))
444 return CMD_RET_FAILURE
;
445 col
= simple_strtoul(argv
[1], NULL
, 10);
446 row
= simple_strtoul(argv
[2], NULL
, 10);
447 vidconsole_position_cursor(dev
, col
, row
);
452 static int do_video_puts(cmd_tbl_t
*cmdtp
, int flag
, int argc
,
459 return CMD_RET_USAGE
;
461 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE
, &dev
))
462 return CMD_RET_FAILURE
;
463 for (s
= argv
[1]; *s
; s
++)
464 vidconsole_put_char(dev
, *s
);
466 video_sync(dev
->parent
);
472 setcurs
, 3, 1, do_video_setcursor
,
473 "set cursor position within screen",
474 " <col> <row> in character"
478 lcdputs
, 2, 1, do_video_puts
,
479 "print string on video framebuffer",