1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2015 Google, Inc
4 * (C) Copyright 2001-2015
6 * Compulab Ltd - http://compulab.co.il/
7 * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
16 #include <video_console.h>
17 #include <video_font.h> /* Bitmap font for code page 437 */
18 #include <linux/ctype.h>
21 * Structure to describe a console color
29 /* By default we scroll by a single line */
30 #ifndef CONFIG_CONSOLE_SCROLL_LINES
31 #define CONFIG_CONSOLE_SCROLL_LINES 1
34 int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
36 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
40 return ops->putc_xy(dev, x, y, ch);
43 int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
46 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
50 return ops->move_rows(dev, rowdst, rowsrc, count);
53 int vidconsole_set_row(struct udevice *dev, uint row, int clr)
55 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
59 return ops->set_row(dev, row, clr);
62 static int vidconsole_entry_start(struct udevice *dev)
64 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
66 if (!ops->entry_start)
68 return ops->entry_start(dev);
71 /* Move backwards one space */
72 static int vidconsole_back(struct udevice *dev)
74 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
75 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
79 ret = ops->backspace(dev);
84 priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
85 if (priv->xcur_frac < priv->xstart_frac) {
86 priv->xcur_frac = (priv->cols - 1) *
87 VID_TO_POS(priv->x_charsize);
88 priv->ycur -= priv->y_charsize;
92 video_sync(dev->parent, false);
97 /* Move to a newline, scrolling the display if necessary */
98 static void vidconsole_newline(struct udevice *dev)
100 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
101 struct udevice *vid_dev = dev->parent;
102 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
103 const int rows = CONFIG_CONSOLE_SCROLL_LINES;
106 priv->xcur_frac = priv->xstart_frac;
107 priv->ycur += priv->y_charsize;
109 /* Check if we need to scroll the terminal */
110 if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
111 vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
112 for (i = 0; i < rows; i++)
113 vidconsole_set_row(dev, priv->rows - i - 1,
114 vid_priv->colour_bg);
115 priv->ycur -= rows * priv->y_charsize;
119 video_sync(dev->parent, false);
122 static const struct vid_rgb colors[VID_COLOR_COUNT] = {
123 { 0x00, 0x00, 0x00 }, /* black */
124 { 0xc0, 0x00, 0x00 }, /* red */
125 { 0x00, 0xc0, 0x00 }, /* green */
126 { 0xc0, 0x60, 0x00 }, /* brown */
127 { 0x00, 0x00, 0xc0 }, /* blue */
128 { 0xc0, 0x00, 0xc0 }, /* magenta */
129 { 0x00, 0xc0, 0xc0 }, /* cyan */
130 { 0xc0, 0xc0, 0xc0 }, /* light gray */
131 { 0x80, 0x80, 0x80 }, /* gray */
132 { 0xff, 0x00, 0x00 }, /* bright red */
133 { 0x00, 0xff, 0x00 }, /* bright green */
134 { 0xff, 0xff, 0x00 }, /* yellow */
135 { 0x00, 0x00, 0xff }, /* bright blue */
136 { 0xff, 0x00, 0xff }, /* bright magenta */
137 { 0x00, 0xff, 0xff }, /* bright cyan */
138 { 0xff, 0xff, 0xff }, /* white */
141 u32 vid_console_color(struct video_priv *priv, unsigned int idx)
143 switch (priv->bpix) {
145 if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
146 return ((colors[idx].r >> 3) << 11) |
147 ((colors[idx].g >> 2) << 5) |
148 ((colors[idx].b >> 3) << 0);
152 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
153 return (colors[idx].r << 16) |
154 (colors[idx].g << 8) |
155 (colors[idx].b << 0);
163 * For unknown bit arrangements just support
167 return 0xffffff; /* white */
169 return 0x000000; /* black */
172 static char *parsenum(char *s, int *num)
175 *num = simple_strtol(s, &end, 10);
180 * set_cursor_position() - set cursor position
182 * @priv: private data of the video console
186 static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
189 * Ensure we stay in the bounds of the screen.
191 if (row >= priv->rows)
192 row = priv->rows - 1;
193 if (col >= priv->cols)
194 col = priv->cols - 1;
196 priv->ycur = row * priv->y_charsize;
197 priv->xcur_frac = priv->xstart_frac +
198 VID_TO_POS(col * priv->x_charsize);
202 * get_cursor_position() - get cursor position
204 * @priv: private data of the video console
208 static void get_cursor_position(struct vidconsole_priv *priv,
211 *row = priv->ycur / priv->y_charsize;
212 *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
217 * Process a character while accumulating an escape string. Chars are
218 * accumulated into escape_buf until the end of escape sequence is
219 * found, at which point the sequence is parsed and processed.
221 static void vidconsole_escape_char(struct udevice *dev, char ch)
223 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
225 if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
228 /* Sanity checking for bogus ESC sequences: */
229 if (priv->escape_len >= sizeof(priv->escape_buf))
231 if (priv->escape_len == 0) {
234 /* Save cursor position */
235 get_cursor_position(priv, &priv->row_saved,
241 /* Restore cursor position */
242 int row = priv->row_saved;
243 int col = priv->col_saved;
245 set_cursor_position(priv, row, col);
256 priv->escape_buf[priv->escape_len++] = ch;
259 * Escape sequences are terminated by a letter, so keep
260 * accumulating until we get one:
266 * clear escape mode first, otherwise things will get highly
267 * surprising if you hit any debug prints that come back to
280 char *s = priv->escape_buf;
283 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
284 * Cursor left/right: [%dD, [%dC
287 s = parsenum(s, &num);
288 if (num == 0) /* No digit in sequence ... */
289 num = 1; /* ... means "move by 1". */
291 get_cursor_position(priv, &row, &col);
292 if (ch == 'A' || ch == 'F')
298 if (ch == 'B' || ch == 'E')
300 if (ch == 'E' || ch == 'F')
306 /* Right and bottom overflows are handled in the callee. */
307 set_cursor_position(priv, row, col);
313 char *s = priv->escape_buf;
316 * Set cursor position: [%d;%df or [%d;%dH
319 s = parsenum(s, &row);
321 s = parsenum(s, &col);
324 * Video origin is [0, 0], terminal origin is [1, 1].
331 set_cursor_position(priv, row, col);
339 * Clear part/all screen:
340 * [J or [0J - clear screen from cursor down
341 * [1J - clear screen from cursor up
342 * [2J - clear entire screen
344 * TODO we really only handle entire-screen case, others
345 * probably require some additions to video-uclass (and
346 * are not really needed yet by efi_console)
348 parsenum(priv->escape_buf + 1, &mode);
351 video_clear(dev->parent);
352 video_sync(dev->parent, false);
354 priv->xcur_frac = priv->xstart_frac;
356 debug("unsupported clear mode: %d\n", mode);
361 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
365 * Clear (parts of) current line
366 * [0K - clear line to end
367 * [2K - clear entire line
369 parsenum(priv->escape_buf + 1, &mode);
374 get_cursor_position(priv, &row, &col);
375 vidconsole_set_row(dev, row, vid_priv->colour_bg);
380 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
381 char *s = priv->escape_buf;
382 char *end = &priv->escape_buf[priv->escape_len];
385 * Set graphics mode: [%d;...;%dm
387 * Currently only supports the color attributes:
416 s = parsenum(s, &val);
421 /* all attributes off */
422 video_set_default_colors(dev->parent, false);
426 vid_priv->fg_col_idx |= 8;
427 vid_priv->colour_fg = vid_console_color(
428 vid_priv, vid_priv->fg_col_idx);
432 vid_priv->colour_fg = vid_console_color(
433 vid_priv, vid_priv->bg_col_idx);
434 vid_priv->colour_bg = vid_console_color(
435 vid_priv, vid_priv->fg_col_idx);
438 /* foreground color */
439 vid_priv->fg_col_idx &= ~7;
440 vid_priv->fg_col_idx |= val - 30;
441 vid_priv->colour_fg = vid_console_color(
442 vid_priv, vid_priv->fg_col_idx);
445 /* background color, also mask the bold bit */
446 vid_priv->bg_col_idx &= ~0xf;
447 vid_priv->bg_col_idx |= val - 40;
448 vid_priv->colour_bg = vid_console_color(
449 vid_priv, vid_priv->bg_col_idx);
452 /* ignore unsupported SGR parameter */
460 debug("unrecognized escape sequence: %*s\n",
461 priv->escape_len, priv->escape_buf);
467 /* something went wrong, just revert to normal mode: */
471 /* Put that actual character on the screen (using the CP437 code page). */
472 static int vidconsole_output_glyph(struct udevice *dev, char ch)
474 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
478 * Failure of this function normally indicates an unsupported
479 * colour depth. Check this and return an error to help with
482 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
483 if (ret == -EAGAIN) {
484 vidconsole_newline(dev);
485 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
489 priv->xcur_frac += ret;
491 if (priv->xcur_frac >= priv->xsize_frac)
492 vidconsole_newline(dev);
497 int vidconsole_put_char(struct udevice *dev, char ch)
499 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
503 vidconsole_escape_char(dev, ch);
509 priv->escape_len = 0;
516 priv->xcur_frac = priv->xstart_frac;
519 vidconsole_newline(dev);
520 vidconsole_entry_start(dev);
522 case '\t': /* Tab (8 chars alignment) */
523 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
524 + 1) * priv->tab_width_frac;
526 if (priv->xcur_frac >= priv->xsize_frac)
527 vidconsole_newline(dev);
530 vidconsole_back(dev);
534 ret = vidconsole_output_glyph(dev, ch);
543 int vidconsole_put_string(struct udevice *dev, const char *str)
548 for (s = str; *s; s++) {
549 ret = vidconsole_put_char(dev, *s);
557 static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
559 struct udevice *dev = sdev->priv;
562 ret = vidconsole_put_char(dev, ch);
565 console_puts_select_stderr(true, "[vc err: putc]");
568 video_sync(dev->parent, false);
571 static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
573 struct udevice *dev = sdev->priv;
576 ret = vidconsole_put_string(dev, s);
581 snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
582 console_puts_select_stderr(true, str);
585 video_sync(dev->parent, false);
588 /* Set up the number of rows and colours (rotated drivers override this) */
589 static int vidconsole_pre_probe(struct udevice *dev)
591 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
592 struct udevice *vid = dev->parent;
593 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
595 priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
600 /* Register the device with stdio */
601 static int vidconsole_post_probe(struct udevice *dev)
603 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
604 struct stdio_dev *sdev = &priv->sdev;
606 if (!priv->tab_width_frac)
607 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
610 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
613 strcpy(sdev->name, "vidconsole");
616 sdev->flags = DEV_FLAGS_OUTPUT;
617 sdev->putc = vidconsole_putc;
618 sdev->puts = vidconsole_puts;
621 return stdio_register(sdev);
624 UCLASS_DRIVER(vidconsole) = {
625 .id = UCLASS_VIDEO_CONSOLE,
626 .name = "vidconsole0",
627 .pre_probe = vidconsole_pre_probe,
628 .post_probe = vidconsole_post_probe,
629 .per_device_auto = sizeof(struct vidconsole_priv),
632 #ifdef CONFIG_VIDEO_COPY
633 int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
635 struct udevice *vid = dev_get_parent(dev);
637 return video_sync_copy(vid, from, to);
640 int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
643 memmove(dst, src, size);
644 return vidconsole_sync_copy(dev, dst, dst + size);
648 #if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
649 void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
651 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
652 struct udevice *vid_dev = dev->parent;
653 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
655 col *= priv->x_charsize;
656 row *= priv->y_charsize;
657 priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
658 priv->xstart_frac = priv->xcur_frac;
659 priv->ycur = min_t(short, row, vid_priv->ysize - 1);
662 static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
665 unsigned int col, row;
669 return CMD_RET_USAGE;
671 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
672 return CMD_RET_FAILURE;
673 col = simple_strtoul(argv[1], NULL, 10);
674 row = simple_strtoul(argv[2], NULL, 10);
675 vidconsole_position_cursor(dev, col, row);
680 static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
687 return CMD_RET_USAGE;
689 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
690 return CMD_RET_FAILURE;
691 for (s = argv[1]; *s; s++)
692 vidconsole_put_char(dev, *s);
694 video_sync(dev->parent, false);
700 setcurs, 3, 1, do_video_setcursor,
701 "set cursor position within screen",
702 " <col> <row> in character"
706 lcdputs, 2, 1, do_video_puts,
707 "print string on video framebuffer",
710 #endif /* CONFIG_IS_ENABLED(CMD_VIDCONSOLE) */