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
269 char *s
= priv
->escape_buf
;
272 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
273 * Cursor left/right: [%dD, [%dC
276 s
= parsenum(s
, &num
);
277 if (num
== 0) /* No digit in sequence ... */
278 num
= 1; /* ... means "move by 1". */
280 get_cursor_position(priv
, &row
, &col
);
281 if (ch
== 'A' || ch
== 'F')
287 if (ch
== 'B' || ch
== 'E')
289 if (ch
== 'E' || ch
== 'F')
295 /* Right and bottom overflows are handled in the callee. */
296 set_cursor_position(priv
, row
, col
);
302 char *s
= priv
->escape_buf
;
305 * Set cursor position: [%d;%df or [%d;%dH
308 s
= parsenum(s
, &row
);
310 s
= parsenum(s
, &col
);
313 * Video origin is [0, 0], terminal origin is [1, 1].
320 set_cursor_position(priv
, row
, col
);
328 * Clear part/all screen:
329 * [J or [0J - clear screen from cursor down
330 * [1J - clear screen from cursor up
331 * [2J - clear entire screen
333 * TODO we really only handle entire-screen case, others
334 * probably require some additions to video-uclass (and
335 * are not really needed yet by efi_console)
337 parsenum(priv
->escape_buf
+ 1, &mode
);
340 video_clear(dev
->parent
);
341 video_sync(dev
->parent
, false);
343 priv
->xcur_frac
= priv
->xstart_frac
;
345 debug("unsupported clear mode: %d\n", mode
);
350 struct video_priv
*vid_priv
= dev_get_uclass_priv(dev
->parent
);
351 char *s
= priv
->escape_buf
;
352 char *end
= &priv
->escape_buf
[priv
->escape_len
];
355 * Set graphics mode: [%d;...;%dm
357 * Currently only supports the color attributes:
386 s
= parsenum(s
, &val
);
391 /* all attributes off */
392 video_set_default_colors(dev
->parent
, false);
396 vid_priv
->fg_col_idx
|= 8;
397 vid_priv
->colour_fg
= vid_console_color(
398 vid_priv
, vid_priv
->fg_col_idx
);
402 vid_priv
->colour_fg
= vid_console_color(
403 vid_priv
, vid_priv
->bg_col_idx
);
404 vid_priv
->colour_bg
= vid_console_color(
405 vid_priv
, vid_priv
->fg_col_idx
);
408 /* foreground color */
409 vid_priv
->fg_col_idx
&= ~7;
410 vid_priv
->fg_col_idx
|= val
- 30;
411 vid_priv
->colour_fg
= vid_console_color(
412 vid_priv
, vid_priv
->fg_col_idx
);
415 /* background color, also mask the bold bit */
416 vid_priv
->bg_col_idx
&= ~0xf;
417 vid_priv
->bg_col_idx
|= val
- 40;
418 vid_priv
->colour_bg
= vid_console_color(
419 vid_priv
, vid_priv
->bg_col_idx
);
422 /* ignore unsupported SGR parameter */
430 debug("unrecognized escape sequence: %*s\n",
431 priv
->escape_len
, priv
->escape_buf
);
437 /* something went wrong, just revert to normal mode: */
441 int vidconsole_put_char(struct udevice
*dev
, char ch
)
443 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
447 vidconsole_escape_char(dev
, ch
);
453 priv
->escape_len
= 0;
460 priv
->xcur_frac
= priv
->xstart_frac
;
463 vidconsole_newline(dev
);
464 vidconsole_entry_start(dev
);
466 case '\t': /* Tab (8 chars alignment) */
467 priv
->xcur_frac
= ((priv
->xcur_frac
/ priv
->tab_width_frac
)
468 + 1) * priv
->tab_width_frac
;
470 if (priv
->xcur_frac
>= priv
->xsize_frac
)
471 vidconsole_newline(dev
);
474 vidconsole_back(dev
);
479 * Failure of this function normally indicates an unsupported
480 * colour depth. Check this and return an error to help with
483 ret
= vidconsole_putc_xy(dev
, priv
->xcur_frac
, priv
->ycur
, ch
);
484 if (ret
== -EAGAIN
) {
485 vidconsole_newline(dev
);
486 ret
= vidconsole_putc_xy(dev
, priv
->xcur_frac
,
491 priv
->xcur_frac
+= ret
;
493 if (priv
->xcur_frac
>= priv
->xsize_frac
)
494 vidconsole_newline(dev
);
501 static void vidconsole_putc(struct stdio_dev
*sdev
, const char ch
)
503 struct udevice
*dev
= sdev
->priv
;
505 vidconsole_put_char(dev
, ch
);
506 video_sync(dev
->parent
, false);
509 static void vidconsole_puts(struct stdio_dev
*sdev
, const char *s
)
511 struct udevice
*dev
= sdev
->priv
;
514 vidconsole_put_char(dev
, *s
++);
515 video_sync(dev
->parent
, false);
518 /* Set up the number of rows and colours (rotated drivers override this) */
519 static int vidconsole_pre_probe(struct udevice
*dev
)
521 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
522 struct udevice
*vid
= dev
->parent
;
523 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid
);
525 priv
->xsize_frac
= VID_TO_POS(vid_priv
->xsize
);
530 /* Register the device with stdio */
531 static int vidconsole_post_probe(struct udevice
*dev
)
533 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
534 struct stdio_dev
*sdev
= &priv
->sdev
;
536 if (!priv
->tab_width_frac
)
537 priv
->tab_width_frac
= VID_TO_POS(priv
->x_charsize
) * 8;
540 snprintf(sdev
->name
, sizeof(sdev
->name
), "vidconsole%d",
543 strcpy(sdev
->name
, "vidconsole");
546 sdev
->flags
= DEV_FLAGS_OUTPUT
;
547 sdev
->putc
= vidconsole_putc
;
548 sdev
->puts
= vidconsole_puts
;
551 return stdio_register(sdev
);
554 UCLASS_DRIVER(vidconsole
) = {
555 .id
= UCLASS_VIDEO_CONSOLE
,
556 .name
= "vidconsole0",
557 .pre_probe
= vidconsole_pre_probe
,
558 .post_probe
= vidconsole_post_probe
,
559 .per_device_auto_alloc_size
= sizeof(struct vidconsole_priv
),
562 void vidconsole_position_cursor(struct udevice
*dev
, unsigned col
, unsigned row
)
564 struct vidconsole_priv
*priv
= dev_get_uclass_priv(dev
);
565 struct udevice
*vid_dev
= dev
->parent
;
566 struct video_priv
*vid_priv
= dev_get_uclass_priv(vid_dev
);
568 col
*= priv
->x_charsize
;
569 row
*= priv
->y_charsize
;
570 priv
->xcur_frac
= VID_TO_POS(min_t(short, col
, vid_priv
->xsize
- 1));
571 priv
->ycur
= min_t(short, row
, vid_priv
->ysize
- 1);
574 static int do_video_setcursor(cmd_tbl_t
*cmdtp
, int flag
, int argc
,
577 unsigned int col
, row
;
581 return CMD_RET_USAGE
;
583 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE
, &dev
))
584 return CMD_RET_FAILURE
;
585 col
= simple_strtoul(argv
[1], NULL
, 10);
586 row
= simple_strtoul(argv
[2], NULL
, 10);
587 vidconsole_position_cursor(dev
, col
, row
);
592 static int do_video_puts(cmd_tbl_t
*cmdtp
, int flag
, int argc
,
599 return CMD_RET_USAGE
;
601 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE
, &dev
))
602 return CMD_RET_FAILURE
;
603 for (s
= argv
[1]; *s
; s
++)
604 vidconsole_put_char(dev
, *s
);
606 video_sync(dev
->parent
, false);
612 setcurs
, 3, 1, do_video_setcursor
,
613 "set cursor position within screen",
614 " <col> <row> in character"
618 lcdputs
, 2, 1, do_video_puts
,
619 "print string on video framebuffer",