1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2015 Google, Inc
4 * (C) Copyright 2001-2015
5 * DENX Software Engineering -- wd@denx.de
6 * Compulab Ltd - http://compulab.co.il/
7 * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
11 #include <linux/ctype.h>
14 #include <video_console.h>
15 #include <video_font.h> /* Bitmap font for code page 437 */
18 * Structure to describe a console color
26 /* By default we scroll by a single line */
27 #ifndef CONFIG_CONSOLE_SCROLL_LINES
28 #define CONFIG_CONSOLE_SCROLL_LINES 1
31 int vidconsole_putc_xy(struct udevice
*dev
, uint x
, uint y
, char ch
)
33 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
37 return ops
->putc_xy(dev
, x
, y
, ch
);
40 int vidconsole_move_rows(struct udevice
*dev
, uint rowdst
, uint rowsrc
,
43 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
47 return ops
->move_rows(dev
, rowdst
, rowsrc
, count
);
50 int vidconsole_set_row(struct udevice
*dev
, uint row
, int clr
)
52 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
56 return ops
->set_row(dev
, row
, clr
);
59 static int vidconsole_entry_start(struct udevice
*dev
)
61 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
63 if (!ops
->entry_start
)
65 return ops
->entry_start(dev
);
68 /* Move backwards one space */
69 static int vidconsole_back(struct udevice
*dev
)
71 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
72 struct vidconsole_ops
*ops
= vidconsole_get_ops(dev
);
76 ret
= ops
->backspace(dev
);
81 priv
->xcur_frac
-= VID_TO_POS(priv
->x_charsize
);
82 if (priv
->xcur_frac
< priv
->xstart_frac
) {
83 priv
->xcur_frac
= (priv
->cols
- 1) *
84 VID_TO_POS(priv
->x_charsize
);
85 priv
->ycur
-= priv
->y_charsize
;
89 video_sync(dev
->parent
, false);
94 /* Move to a newline, scrolling the display if necessary */
95 static void vidconsole_newline(struct udevice
*dev
)
97 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
98 struct udevice
*vid_dev
= dev
->parent
;
99 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid_dev
);
100 const int rows
= CONFIG_CONSOLE_SCROLL_LINES
;
103 priv
->xcur_frac
= priv
->xstart_frac
;
104 priv
->ycur
+= priv
->y_charsize
;
106 /* Check if we need to scroll the terminal */
107 if ((priv
->ycur
+ priv
->y_charsize
) / priv
->y_charsize
> priv
->rows
) {
108 vidconsole_move_rows(dev
, 0, rows
, priv
->rows
- rows
);
109 for (i
= 0; i
< rows
; i
++)
110 vidconsole_set_row(dev
, priv
->rows
- i
- 1,
111 vid_priv
->colour_bg
);
112 priv
->ycur
-= rows
* priv
->y_charsize
;
116 video_sync(dev
->parent
, false);
119 static const struct vid_rgb colors
[VID_COLOR_COUNT
] = {
120 { 0x00, 0x00, 0x00 }, /* black */
121 { 0xc0, 0x00, 0x00 }, /* red */
122 { 0x00, 0xc0, 0x00 }, /* green */
123 { 0xc0, 0x60, 0x00 }, /* brown */
124 { 0x00, 0x00, 0xc0 }, /* blue */
125 { 0xc0, 0x00, 0xc0 }, /* magenta */
126 { 0x00, 0xc0, 0xc0 }, /* cyan */
127 { 0xc0, 0xc0, 0xc0 }, /* light gray */
128 { 0x80, 0x80, 0x80 }, /* gray */
129 { 0xff, 0x00, 0x00 }, /* bright red */
130 { 0x00, 0xff, 0x00 }, /* bright green */
131 { 0xff, 0xff, 0x00 }, /* yellow */
132 { 0x00, 0x00, 0xff }, /* bright blue */
133 { 0xff, 0x00, 0xff }, /* bright magenta */
134 { 0x00, 0xff, 0xff }, /* bright cyan */
135 { 0xff, 0xff, 0xff }, /* white */
138 u32
vid_console_color(struct video_priv
*priv
, unsigned int idx
)
140 switch (priv
->bpix
) {
142 return ((colors
[idx
].r
>> 3) << 11) |
143 ((colors
[idx
].g
>> 2) << 5) |
144 ((colors
[idx
].b
>> 3) << 0);
146 return (colors
[idx
].r
<< 16) |
147 (colors
[idx
].g
<< 8) |
148 (colors
[idx
].b
<< 0);
151 * For unknown bit arrangements just support
155 return 0xffffff; /* white */
157 return 0x000000; /* black */
161 static char *parsenum(char *s
, int *num
)
164 *num
= simple_strtol(s
, &end
, 10);
169 * set_cursor_position() - set cursor position
171 * @priv: private data of the video console
175 static void set_cursor_position(struct vidconsole_priv
*priv
, int row
, int col
)
178 * Ensure we stay in the bounds of the screen.
180 if (row
>= priv
->rows
)
181 row
= priv
->rows
- 1;
182 if (col
>= priv
->cols
)
183 col
= priv
->cols
- 1;
185 priv
->ycur
= row
* priv
->y_charsize
;
186 priv
->xcur_frac
= priv
->xstart_frac
+
187 VID_TO_POS(col
* priv
->x_charsize
);
191 * get_cursor_position() - get cursor position
193 * @priv: private data of the video console
197 static void get_cursor_position(struct vidconsole_priv
*priv
,
200 *row
= priv
->ycur
/ priv
->y_charsize
;
201 *col
= VID_TO_PIXEL(priv
->xcur_frac
- priv
->xstart_frac
) /
206 * Process a character while accumulating an escape string. Chars are
207 * accumulated into escape_buf until the end of escape sequence is
208 * found, at which point the sequence is parsed and processed.
210 static void vidconsole_escape_char(struct udevice
*dev
, char ch
)
212 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
214 if (!IS_ENABLED(CONFIG_VIDEO_ANSI
))
217 /* Sanity checking for bogus ESC sequences: */
218 if (priv
->escape_len
>= sizeof(priv
->escape_buf
))
220 if (priv
->escape_len
== 0) {
223 /* Save cursor position */
224 get_cursor_position(priv
, &priv
->row_saved
,
230 /* Restore cursor position */
231 int row
= priv
->row_saved
;
232 int col
= priv
->col_saved
;
234 set_cursor_position(priv
, row
, col
);
245 priv
->escape_buf
[priv
->escape_len
++] = ch
;
248 * Escape sequences are terminated by a letter, so keep
249 * accumulating until we get one:
255 * clear escape mode first, otherwise things will get highly
256 * surprising if you hit any debug prints that come back to
265 char *s
= priv
->escape_buf
;
268 * Set cursor position: [%d;%df or [%d;%dH
271 s
= parsenum(s
, &row
);
273 s
= parsenum(s
, &col
);
276 * Video origin is [0, 0], terminal origin is [1, 1].
283 set_cursor_position(priv
, row
, col
);
291 * Clear part/all screen:
292 * [J or [0J - clear screen from cursor down
293 * [1J - clear screen from cursor up
294 * [2J - clear entire screen
296 * TODO we really only handle entire-screen case, others
297 * probably require some additions to video-uclass (and
298 * are not really needed yet by efi_console)
300 parsenum(priv
->escape_buf
+ 1, &mode
);
303 video_clear(dev
->parent
);
304 video_sync(dev
->parent
, false);
306 priv
->xcur_frac
= priv
->xstart_frac
;
308 debug("unsupported clear mode: %d\n", mode
);
313 struct video_priv
*vid_priv
= dev_get_uclass_priv(dev
->parent
);
314 char *s
= priv
->escape_buf
;
315 char *end
= &priv
->escape_buf
[priv
->escape_len
];
318 * Set graphics mode: [%d;...;%dm
320 * Currently only supports the color attributes:
349 s
= parsenum(s
, &val
);
354 /* all attributes off */
355 video_set_default_colors(dev
->parent
, false);
359 vid_priv
->fg_col_idx
|= 8;
360 vid_priv
->colour_fg
= vid_console_color(
361 vid_priv
, vid_priv
->fg_col_idx
);
365 vid_priv
->colour_fg
= vid_console_color(
366 vid_priv
, vid_priv
->bg_col_idx
);
367 vid_priv
->colour_bg
= vid_console_color(
368 vid_priv
, vid_priv
->fg_col_idx
);
371 /* foreground color */
372 vid_priv
->fg_col_idx
&= ~7;
373 vid_priv
->fg_col_idx
|= val
- 30;
374 vid_priv
->colour_fg
= vid_console_color(
375 vid_priv
, vid_priv
->fg_col_idx
);
378 /* background color, also mask the bold bit */
379 vid_priv
->bg_col_idx
&= ~0xf;
380 vid_priv
->bg_col_idx
|= val
- 40;
381 vid_priv
->colour_bg
= vid_console_color(
382 vid_priv
, vid_priv
->bg_col_idx
);
385 /* ignore unsupported SGR parameter */
393 debug("unrecognized escape sequence: %*s\n",
394 priv
->escape_len
, priv
->escape_buf
);
400 /* something went wrong, just revert to normal mode: */
404 int vidconsole_put_char(struct udevice
*dev
, char ch
)
406 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
410 vidconsole_escape_char(dev
, ch
);
416 priv
->escape_len
= 0;
423 priv
->xcur_frac
= priv
->xstart_frac
;
426 vidconsole_newline(dev
);
427 vidconsole_entry_start(dev
);
429 case '\t': /* Tab (8 chars alignment) */
430 priv
->xcur_frac
= ((priv
->xcur_frac
/ priv
->tab_width_frac
)
431 + 1) * priv
->tab_width_frac
;
433 if (priv
->xcur_frac
>= priv
->xsize_frac
)
434 vidconsole_newline(dev
);
437 vidconsole_back(dev
);
442 * Failure of this function normally indicates an unsupported
443 * colour depth. Check this and return an error to help with
446 ret
= vidconsole_putc_xy(dev
, priv
->xcur_frac
, priv
->ycur
, ch
);
447 if (ret
== -EAGAIN
) {
448 vidconsole_newline(dev
);
449 ret
= vidconsole_putc_xy(dev
, priv
->xcur_frac
,
454 priv
->xcur_frac
+= ret
;
456 if (priv
->xcur_frac
>= priv
->xsize_frac
)
457 vidconsole_newline(dev
);
464 static void vidconsole_putc(struct stdio_dev
*sdev
, const char ch
)
466 struct udevice
*dev
= sdev
->priv
;
468 vidconsole_put_char(dev
, ch
);
469 video_sync(dev
->parent
, false);
472 static void vidconsole_puts(struct stdio_dev
*sdev
, const char *s
)
474 struct udevice
*dev
= sdev
->priv
;
477 vidconsole_put_char(dev
, *s
++);
478 video_sync(dev
->parent
, false);
481 /* Set up the number of rows and colours (rotated drivers override this) */
482 static int vidconsole_pre_probe(struct udevice
*dev
)
484 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
485 struct udevice
*vid
= dev
->parent
;
486 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid
);
488 priv
->xsize_frac
= VID_TO_POS(vid_priv
->xsize
);
493 /* Register the device with stdio */
494 static int vidconsole_post_probe(struct udevice
*dev
)
496 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
497 struct stdio_dev
*sdev
= &priv
->sdev
;
499 if (!priv
->tab_width_frac
)
500 priv
->tab_width_frac
= VID_TO_POS(priv
->x_charsize
) * 8;
503 snprintf(sdev
->name
, sizeof(sdev
->name
), "vidconsole%d",
506 strcpy(sdev
->name
, "vidconsole");
509 sdev
->flags
= DEV_FLAGS_OUTPUT
;
510 sdev
->putc
= vidconsole_putc
;
511 sdev
->puts
= vidconsole_puts
;
514 return stdio_register(sdev
);
517 UCLASS_DRIVER(vidconsole
) = {
518 .id
= UCLASS_VIDEO_CONSOLE
,
519 .name
= "vidconsole0",
520 .pre_probe
= vidconsole_pre_probe
,
521 .post_probe
= vidconsole_post_probe
,
522 .per_device_auto_alloc_size
= sizeof(struct vidconsole_priv
),
525 void vidconsole_position_cursor(struct udevice
*dev
, unsigned col
, unsigned row
)
527 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
528 struct udevice
*vid_dev
= dev
->parent
;
529 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid_dev
);
531 col
*= priv
->x_charsize
;
532 row
*= priv
->y_charsize
;
533 priv
->xcur_frac
= VID_TO_POS(min_t(short, col
, vid_priv
->xsize
- 1));
534 priv
->ycur
= min_t(short, row
, vid_priv
->ysize
- 1);
537 static int do_video_setcursor(cmd_tbl_t
*cmdtp
, int flag
, int argc
,
540 unsigned int col
, row
;
544 return CMD_RET_USAGE
;
546 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE
, &dev
))
547 return CMD_RET_FAILURE
;
548 col
= simple_strtoul(argv
[1], NULL
, 10);
549 row
= simple_strtoul(argv
[2], NULL
, 10);
550 vidconsole_position_cursor(dev
, col
, row
);
555 static int do_video_puts(cmd_tbl_t
*cmdtp
, int flag
, int argc
,
562 return CMD_RET_USAGE
;
564 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE
, &dev
))
565 return CMD_RET_FAILURE
;
566 for (s
= argv
[1]; *s
; s
++)
567 vidconsole_put_char(dev
, *s
);
569 video_sync(dev
->parent
, false);
575 setcurs
, 3, 1, do_video_setcursor
,
576 "set cursor position within screen",
577 " <col> <row> in character"
581 lcdputs
, 2, 1, do_video_puts
,
582 "print string on video framebuffer",