blob: f42db40d4cde0de8bdc784ed0364afcb38a5807e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass84c7fb32016-01-18 19:52:17 -07002/*
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
Simon Glass84c7fb32016-01-18 19:52:17 -07008 */
9
Patrick Delaunay81313352021-04-27 11:02:19 +020010#define LOG_CATEGORY UCLASS_VIDEO_CONSOLE
11
Simon Glass84c7fb32016-01-18 19:52:17 -070012#include <common.h>
Simon Glassed38aef2020-05-10 11:40:03 -060013#include <command.h>
Simon Glassf97beb72020-07-02 21:12:14 -060014#include <console.h>
Simon Glass0f2af882020-05-10 11:40:05 -060015#include <log.h>
Simon Glass84c7fb32016-01-18 19:52:17 -070016#include <dm.h>
17#include <video.h>
18#include <video_console.h>
Heinrich Schuchardt2172fa42018-03-02 20:50:17 +010019#include <video_font.h> /* Bitmap font for code page 437 */
Simon Glassf97beb72020-07-02 21:12:14 -060020#include <linux/ctype.h>
Simon Glass84c7fb32016-01-18 19:52:17 -070021
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +010022/*
23 * Structure to describe a console color
24 */
25struct vid_rgb {
26 u32 r;
27 u32 g;
28 u32 b;
29};
30
Simon Glass84c7fb32016-01-18 19:52:17 -070031/* By default we scroll by a single line */
32#ifndef CONFIG_CONSOLE_SCROLL_LINES
33#define CONFIG_CONSOLE_SCROLL_LINES 1
34#endif
35
36int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
37{
38 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
39
40 if (!ops->putc_xy)
41 return -ENOSYS;
42 return ops->putc_xy(dev, x, y, ch);
43}
44
45int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
46 uint count)
47{
48 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
49
50 if (!ops->move_rows)
51 return -ENOSYS;
52 return ops->move_rows(dev, rowdst, rowsrc, count);
53}
54
55int vidconsole_set_row(struct udevice *dev, uint row, int clr)
56{
57 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
58
59 if (!ops->set_row)
60 return -ENOSYS;
61 return ops->set_row(dev, row, clr);
62}
63
Simon Glassafee7432016-01-14 18:10:40 -070064static int vidconsole_entry_start(struct udevice *dev)
65{
66 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
67
68 if (!ops->entry_start)
69 return -ENOSYS;
70 return ops->entry_start(dev);
71}
72
Simon Glass84c7fb32016-01-18 19:52:17 -070073/* Move backwards one space */
Simon Glass33bd3b62016-01-14 18:10:41 -070074static int vidconsole_back(struct udevice *dev)
Simon Glass84c7fb32016-01-18 19:52:17 -070075{
76 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass33bd3b62016-01-14 18:10:41 -070077 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
78 int ret;
79
80 if (ops->backspace) {
81 ret = ops->backspace(dev);
82 if (ret != -ENOSYS)
83 return ret;
84 }
Simon Glass84c7fb32016-01-18 19:52:17 -070085
Simon Glass52c10c52016-01-14 18:10:37 -070086 priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
Simon Glassa74451d2016-01-14 18:10:39 -070087 if (priv->xcur_frac < priv->xstart_frac) {
Simon Glass52c10c52016-01-14 18:10:37 -070088 priv->xcur_frac = (priv->cols - 1) *
89 VID_TO_POS(priv->x_charsize);
90 priv->ycur -= priv->y_charsize;
91 if (priv->ycur < 0)
92 priv->ycur = 0;
Simon Glass84c7fb32016-01-18 19:52:17 -070093 }
Michal Simek632e3d42020-12-14 08:47:52 +010094 return video_sync(dev->parent, false);
Simon Glass84c7fb32016-01-18 19:52:17 -070095}
96
97/* Move to a newline, scrolling the display if necessary */
98static void vidconsole_newline(struct udevice *dev)
99{
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;
Michal Simek632e3d42020-12-14 08:47:52 +0100104 int i, ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700105
Simon Glassa74451d2016-01-14 18:10:39 -0700106 priv->xcur_frac = priv->xstart_frac;
Simon Glass52c10c52016-01-14 18:10:37 -0700107 priv->ycur += priv->y_charsize;
Simon Glass84c7fb32016-01-18 19:52:17 -0700108
109 /* Check if we need to scroll the terminal */
Simon Glass52c10c52016-01-14 18:10:37 -0700110 if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
Simon Glass84c7fb32016-01-18 19:52:17 -0700111 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);
Simon Glass52c10c52016-01-14 18:10:37 -0700115 priv->ycur -= rows * priv->y_charsize;
Simon Glass84c7fb32016-01-18 19:52:17 -0700116 }
Simon Glassafee7432016-01-14 18:10:40 -0700117 priv->last_ch = 0;
118
Michal Simek632e3d42020-12-14 08:47:52 +0100119 ret = video_sync(dev->parent, false);
120 if (ret) {
121#ifdef DEBUG
122 console_puts_select_stderr(true, "[vc err: video_sync]");
123#endif
124 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700125}
126
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100127static const struct vid_rgb colors[VID_COLOR_COUNT] = {
Rob Clark50509bb2017-09-13 18:12:22 -0400128 { 0x00, 0x00, 0x00 }, /* black */
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100129 { 0xc0, 0x00, 0x00 }, /* red */
130 { 0x00, 0xc0, 0x00 }, /* green */
131 { 0xc0, 0x60, 0x00 }, /* brown */
132 { 0x00, 0x00, 0xc0 }, /* blue */
133 { 0xc0, 0x00, 0xc0 }, /* magenta */
134 { 0x00, 0xc0, 0xc0 }, /* cyan */
135 { 0xc0, 0xc0, 0xc0 }, /* light gray */
136 { 0x80, 0x80, 0x80 }, /* gray */
137 { 0xff, 0x00, 0x00 }, /* bright red */
138 { 0x00, 0xff, 0x00 }, /* bright green */
Rob Clark50509bb2017-09-13 18:12:22 -0400139 { 0xff, 0xff, 0x00 }, /* yellow */
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100140 { 0x00, 0x00, 0xff }, /* bright blue */
141 { 0xff, 0x00, 0xff }, /* bright magenta */
142 { 0x00, 0xff, 0xff }, /* bright cyan */
Rob Clark50509bb2017-09-13 18:12:22 -0400143 { 0xff, 0xff, 0xff }, /* white */
144};
145
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100146u32 vid_console_color(struct video_priv *priv, unsigned int idx)
Rob Clark50509bb2017-09-13 18:12:22 -0400147{
148 switch (priv->bpix) {
149 case VIDEO_BPP16:
Simon Glassa4a43c62019-12-20 18:10:36 -0700150 if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
151 return ((colors[idx].r >> 3) << 11) |
152 ((colors[idx].g >> 2) << 5) |
153 ((colors[idx].b >> 3) << 0);
154 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100155 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400156 case VIDEO_BPP32:
Simon Glassa4a43c62019-12-20 18:10:36 -0700157 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
Mark Kettenis32b73682021-09-25 22:47:36 +0200158 if (priv->format == VIDEO_X2R10G10B10)
159 return (colors[idx].r << 22) |
160 (colors[idx].g << 12) |
161 (colors[idx].b << 2);
162 else
163 return (colors[idx].r << 16) |
164 (colors[idx].g << 8) |
165 (colors[idx].b << 0);
Simon Glassa4a43c62019-12-20 18:10:36 -0700166 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100167 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400168 default:
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100169 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400170 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100171
172 /*
173 * For unknown bit arrangements just support
174 * black and white.
175 */
176 if (idx)
177 return 0xffffff; /* white */
178
179 return 0x000000; /* black */
Rob Clark50509bb2017-09-13 18:12:22 -0400180}
181
Rob Clark06e7a0d2017-09-13 18:12:21 -0400182static char *parsenum(char *s, int *num)
183{
184 char *end;
185 *num = simple_strtol(s, &end, 10);
186 return end;
187}
188
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200189/**
190 * set_cursor_position() - set cursor position
191 *
192 * @priv: private data of the video console
193 * @row: new row
194 * @col: new column
195 */
196static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
197{
198 /*
199 * Ensure we stay in the bounds of the screen.
200 */
201 if (row >= priv->rows)
202 row = priv->rows - 1;
203 if (col >= priv->cols)
204 col = priv->cols - 1;
205
206 priv->ycur = row * priv->y_charsize;
207 priv->xcur_frac = priv->xstart_frac +
208 VID_TO_POS(col * priv->x_charsize);
209}
210
211/**
212 * get_cursor_position() - get cursor position
213 *
214 * @priv: private data of the video console
215 * @row: row
216 * @col: column
217 */
218static void get_cursor_position(struct vidconsole_priv *priv,
219 int *row, int *col)
220{
221 *row = priv->ycur / priv->y_charsize;
222 *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
223 priv->x_charsize;
224}
225
Rob Clark06e7a0d2017-09-13 18:12:21 -0400226/*
227 * Process a character while accumulating an escape string. Chars are
228 * accumulated into escape_buf until the end of escape sequence is
229 * found, at which point the sequence is parsed and processed.
230 */
231static void vidconsole_escape_char(struct udevice *dev, char ch)
232{
233 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
234
235 if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
236 goto error;
237
238 /* Sanity checking for bogus ESC sequences: */
239 if (priv->escape_len >= sizeof(priv->escape_buf))
240 goto error;
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200241 if (priv->escape_len == 0) {
242 switch (ch) {
243 case '7':
244 /* Save cursor position */
245 get_cursor_position(priv, &priv->row_saved,
246 &priv->col_saved);
247 priv->escape = 0;
248
249 return;
250 case '8': {
251 /* Restore cursor position */
252 int row = priv->row_saved;
253 int col = priv->col_saved;
254
255 set_cursor_position(priv, row, col);
256 priv->escape = 0;
257 return;
258 }
259 case '[':
260 break;
261 default:
262 goto error;
263 }
264 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400265
266 priv->escape_buf[priv->escape_len++] = ch;
267
268 /*
269 * Escape sequences are terminated by a letter, so keep
270 * accumulating until we get one:
271 */
272 if (!isalpha(ch))
273 return;
274
275 /*
276 * clear escape mode first, otherwise things will get highly
277 * surprising if you hit any debug prints that come back to
278 * this console.
279 */
280 priv->escape = 0;
281
282 switch (ch) {
Andre Przywarad4a294c2019-03-23 01:29:57 +0000283 case 'A':
284 case 'B':
285 case 'C':
286 case 'D':
287 case 'E':
288 case 'F': {
289 int row, col, num;
290 char *s = priv->escape_buf;
291
292 /*
293 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
294 * Cursor left/right: [%dD, [%dC
295 */
296 s++; /* [ */
297 s = parsenum(s, &num);
298 if (num == 0) /* No digit in sequence ... */
299 num = 1; /* ... means "move by 1". */
300
301 get_cursor_position(priv, &row, &col);
302 if (ch == 'A' || ch == 'F')
303 row -= num;
304 if (ch == 'C')
305 col += num;
306 if (ch == 'D')
307 col -= num;
308 if (ch == 'B' || ch == 'E')
309 row += num;
310 if (ch == 'E' || ch == 'F')
311 col = 0;
312 if (col < 0)
313 col = 0;
314 if (row < 0)
315 row = 0;
316 /* Right and bottom overflows are handled in the callee. */
317 set_cursor_position(priv, row, col);
318 break;
319 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400320 case 'H':
321 case 'f': {
322 int row, col;
323 char *s = priv->escape_buf;
324
325 /*
326 * Set cursor position: [%d;%df or [%d;%dH
327 */
328 s++; /* [ */
329 s = parsenum(s, &row);
330 s++; /* ; */
331 s = parsenum(s, &col);
332
Heinrich Schuchardtc3c69302018-11-10 19:55:48 +0100333 /*
334 * Video origin is [0, 0], terminal origin is [1, 1].
335 */
336 if (row)
337 --row;
338 if (col)
339 --col;
340
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200341 set_cursor_position(priv, row, col);
Rob Clark06e7a0d2017-09-13 18:12:21 -0400342
343 break;
344 }
345 case 'J': {
346 int mode;
347
348 /*
349 * Clear part/all screen:
350 * [J or [0J - clear screen from cursor down
351 * [1J - clear screen from cursor up
352 * [2J - clear entire screen
353 *
354 * TODO we really only handle entire-screen case, others
355 * probably require some additions to video-uclass (and
356 * are not really needed yet by efi_console)
357 */
358 parsenum(priv->escape_buf + 1, &mode);
359
360 if (mode == 2) {
Michal Simek632e3d42020-12-14 08:47:52 +0100361 int ret;
362
Rob Clark06e7a0d2017-09-13 18:12:21 -0400363 video_clear(dev->parent);
Michal Simek632e3d42020-12-14 08:47:52 +0100364 ret = video_sync(dev->parent, false);
365 if (ret) {
366#ifdef DEBUG
367 console_puts_select_stderr(true, "[vc err: video_sync]");
368#endif
369 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400370 priv->ycur = 0;
371 priv->xcur_frac = priv->xstart_frac;
372 } else {
373 debug("unsupported clear mode: %d\n", mode);
374 }
375 break;
376 }
Andre Przywara918622a2019-03-23 01:29:58 +0000377 case 'K': {
378 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
379 int mode;
380
381 /*
382 * Clear (parts of) current line
383 * [0K - clear line to end
384 * [2K - clear entire line
385 */
386 parsenum(priv->escape_buf + 1, &mode);
387
388 if (mode == 2) {
389 int row, col;
390
391 get_cursor_position(priv, &row, &col);
392 vidconsole_set_row(dev, row, vid_priv->colour_bg);
393 }
394 break;
395 }
Rob Clark50509bb2017-09-13 18:12:22 -0400396 case 'm': {
397 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
398 char *s = priv->escape_buf;
399 char *end = &priv->escape_buf[priv->escape_len];
400
401 /*
402 * Set graphics mode: [%d;...;%dm
403 *
404 * Currently only supports the color attributes:
405 *
406 * Foreground Colors:
407 *
408 * 30 Black
409 * 31 Red
410 * 32 Green
411 * 33 Yellow
412 * 34 Blue
413 * 35 Magenta
414 * 36 Cyan
415 * 37 White
416 *
417 * Background Colors:
418 *
419 * 40 Black
420 * 41 Red
421 * 42 Green
422 * 43 Yellow
423 * 44 Blue
424 * 45 Magenta
425 * 46 Cyan
426 * 47 White
427 */
428
429 s++; /* [ */
430 while (s < end) {
431 int val;
432
433 s = parsenum(s, &val);
434 s++;
435
436 switch (val) {
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100437 case 0:
438 /* all attributes off */
Simon Glass2b063b82018-11-06 15:21:36 -0700439 video_set_default_colors(dev->parent, false);
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100440 break;
441 case 1:
442 /* bold */
443 vid_priv->fg_col_idx |= 8;
444 vid_priv->colour_fg = vid_console_color(
445 vid_priv, vid_priv->fg_col_idx);
446 break;
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000447 case 7:
448 /* reverse video */
449 vid_priv->colour_fg = vid_console_color(
450 vid_priv, vid_priv->bg_col_idx);
451 vid_priv->colour_bg = vid_console_color(
452 vid_priv, vid_priv->fg_col_idx);
453 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400454 case 30 ... 37:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100455 /* foreground color */
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100456 vid_priv->fg_col_idx &= ~7;
457 vid_priv->fg_col_idx |= val - 30;
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100458 vid_priv->colour_fg = vid_console_color(
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100459 vid_priv, vid_priv->fg_col_idx);
Rob Clark50509bb2017-09-13 18:12:22 -0400460 break;
461 case 40 ... 47:
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000462 /* background color, also mask the bold bit */
463 vid_priv->bg_col_idx &= ~0xf;
464 vid_priv->bg_col_idx |= val - 40;
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100465 vid_priv->colour_bg = vid_console_color(
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000466 vid_priv, vid_priv->bg_col_idx);
Rob Clark50509bb2017-09-13 18:12:22 -0400467 break;
468 default:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100469 /* ignore unsupported SGR parameter */
Rob Clark50509bb2017-09-13 18:12:22 -0400470 break;
471 }
472 }
473
474 break;
475 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400476 default:
477 debug("unrecognized escape sequence: %*s\n",
478 priv->escape_len, priv->escape_buf);
479 }
480
481 return;
482
483error:
484 /* something went wrong, just revert to normal mode: */
485 priv->escape = 0;
486}
487
Andre Przywarade86baf2019-03-23 01:29:59 +0000488/* Put that actual character on the screen (using the CP437 code page). */
489static int vidconsole_output_glyph(struct udevice *dev, char ch)
490{
491 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
492 int ret;
493
494 /*
495 * Failure of this function normally indicates an unsupported
496 * colour depth. Check this and return an error to help with
497 * diagnosis.
498 */
499 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
500 if (ret == -EAGAIN) {
501 vidconsole_newline(dev);
502 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
503 }
504 if (ret < 0)
505 return ret;
506 priv->xcur_frac += ret;
507 priv->last_ch = ch;
508 if (priv->xcur_frac >= priv->xsize_frac)
509 vidconsole_newline(dev);
510
511 return 0;
512}
513
Simon Glass84c7fb32016-01-18 19:52:17 -0700514int vidconsole_put_char(struct udevice *dev, char ch)
515{
516 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
517 int ret;
518
Rob Clark06e7a0d2017-09-13 18:12:21 -0400519 if (priv->escape) {
520 vidconsole_escape_char(dev, ch);
521 return 0;
522 }
523
Simon Glass84c7fb32016-01-18 19:52:17 -0700524 switch (ch) {
Rob Clark06e7a0d2017-09-13 18:12:21 -0400525 case '\x1b':
526 priv->escape_len = 0;
527 priv->escape = 1;
528 break;
Simon Glass37b80202016-01-14 18:10:38 -0700529 case '\a':
530 /* beep */
531 break;
Simon Glass84c7fb32016-01-18 19:52:17 -0700532 case '\r':
Simon Glassa74451d2016-01-14 18:10:39 -0700533 priv->xcur_frac = priv->xstart_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700534 break;
535 case '\n':
536 vidconsole_newline(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700537 vidconsole_entry_start(dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700538 break;
539 case '\t': /* Tab (8 chars alignment) */
Simon Glass52c10c52016-01-14 18:10:37 -0700540 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
541 + 1) * priv->tab_width_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700542
Simon Glass52c10c52016-01-14 18:10:37 -0700543 if (priv->xcur_frac >= priv->xsize_frac)
Simon Glass84c7fb32016-01-18 19:52:17 -0700544 vidconsole_newline(dev);
545 break;
546 case '\b':
547 vidconsole_back(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700548 priv->last_ch = 0;
Simon Glass84c7fb32016-01-18 19:52:17 -0700549 break;
550 default:
Andre Przywarade86baf2019-03-23 01:29:59 +0000551 ret = vidconsole_output_glyph(dev, ch);
Simon Glass52c10c52016-01-14 18:10:37 -0700552 if (ret < 0)
Simon Glass84c7fb32016-01-18 19:52:17 -0700553 return ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700554 break;
555 }
556
557 return 0;
558}
559
Marek Vasuta89f9cb2019-05-17 20:22:31 +0200560int vidconsole_put_string(struct udevice *dev, const char *str)
561{
562 const char *s;
563 int ret;
564
565 for (s = str; *s; s++) {
566 ret = vidconsole_put_char(dev, *s);
567 if (ret)
568 return ret;
569 }
570
571 return 0;
572}
573
Simon Glass84c7fb32016-01-18 19:52:17 -0700574static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
575{
576 struct udevice *dev = sdev->priv;
Simon Glassf97beb72020-07-02 21:12:14 -0600577 int ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700578
Simon Glassf97beb72020-07-02 21:12:14 -0600579 ret = vidconsole_put_char(dev, ch);
580 if (ret) {
581#ifdef DEBUG
582 console_puts_select_stderr(true, "[vc err: putc]");
583#endif
584 }
Michal Simek632e3d42020-12-14 08:47:52 +0100585 ret = video_sync(dev->parent, false);
586 if (ret) {
587#ifdef DEBUG
588 console_puts_select_stderr(true, "[vc err: video_sync]");
589#endif
590 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700591}
592
593static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
594{
595 struct udevice *dev = sdev->priv;
Simon Glassf97beb72020-07-02 21:12:14 -0600596 int ret;
597
598 ret = vidconsole_put_string(dev, s);
599 if (ret) {
600#ifdef DEBUG
601 char str[30];
Simon Glass84c7fb32016-01-18 19:52:17 -0700602
Simon Glassf97beb72020-07-02 21:12:14 -0600603 snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
604 console_puts_select_stderr(true, str);
605#endif
606 }
Michal Simek632e3d42020-12-14 08:47:52 +0100607 ret = video_sync(dev->parent, false);
608 if (ret) {
609#ifdef DEBUG
610 console_puts_select_stderr(true, "[vc err: video_sync]");
611#endif
612 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700613}
614
615/* Set up the number of rows and colours (rotated drivers override this) */
616static int vidconsole_pre_probe(struct udevice *dev)
617{
618 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
619 struct udevice *vid = dev->parent;
620 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
621
Simon Glass52c10c52016-01-14 18:10:37 -0700622 priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
Simon Glass84c7fb32016-01-18 19:52:17 -0700623
624 return 0;
625}
626
627/* Register the device with stdio */
628static int vidconsole_post_probe(struct udevice *dev)
629{
630 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
631 struct stdio_dev *sdev = &priv->sdev;
Simon Glass84c7fb32016-01-18 19:52:17 -0700632
Simon Glass52c10c52016-01-14 18:10:37 -0700633 if (!priv->tab_width_frac)
634 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
635
Simon Glass75e534b2020-12-16 21:20:07 -0700636 if (dev_seq(dev)) {
Simon Glass798ff502016-01-21 19:44:51 -0700637 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
Simon Glass75e534b2020-12-16 21:20:07 -0700638 dev_seq(dev));
Simon Glass798ff502016-01-21 19:44:51 -0700639 } else {
640 strcpy(sdev->name, "vidconsole");
641 }
Simon Glass52c10c52016-01-14 18:10:37 -0700642
Simon Glass84c7fb32016-01-18 19:52:17 -0700643 sdev->flags = DEV_FLAGS_OUTPUT;
644 sdev->putc = vidconsole_putc;
645 sdev->puts = vidconsole_puts;
646 sdev->priv = dev;
Simon Glass84c7fb32016-01-18 19:52:17 -0700647
Masahiro Yamadabf528cd2016-09-06 22:17:33 +0900648 return stdio_register(sdev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700649}
650
651UCLASS_DRIVER(vidconsole) = {
652 .id = UCLASS_VIDEO_CONSOLE,
653 .name = "vidconsole0",
654 .pre_probe = vidconsole_pre_probe,
655 .post_probe = vidconsole_post_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700656 .per_device_auto = sizeof(struct vidconsole_priv),
Simon Glass84c7fb32016-01-18 19:52:17 -0700657};
658
Simon Glass31a7e232020-07-02 21:12:23 -0600659#ifdef CONFIG_VIDEO_COPY
660int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
661{
662 struct udevice *vid = dev_get_parent(dev);
663
664 return video_sync_copy(vid, from, to);
665}
666
667int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
668 int size)
669{
670 memmove(dst, src, size);
671 return vidconsole_sync_copy(dev, dst, dst + size);
672}
673#endif
674
Anatolij Gustschin20b79192020-05-25 21:47:19 +0200675#if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
Simon Glass84c7fb32016-01-18 19:52:17 -0700676void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
677{
678 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass52c10c52016-01-14 18:10:37 -0700679 struct udevice *vid_dev = dev->parent;
680 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700681
Simon Glass59f44212018-10-01 12:22:47 -0600682 col *= priv->x_charsize;
683 row *= priv->y_charsize;
Simon Glass52c10c52016-01-14 18:10:37 -0700684 priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
Ye Li3e374692020-06-10 02:52:21 -0700685 priv->xstart_frac = priv->xcur_frac;
Simon Glass52c10c52016-01-14 18:10:37 -0700686 priv->ycur = min_t(short, row, vid_priv->ysize - 1);
Simon Glass84c7fb32016-01-18 19:52:17 -0700687}
688
Simon Glassed38aef2020-05-10 11:40:03 -0600689static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass84c7fb32016-01-18 19:52:17 -0700690 char *const argv[])
691{
692 unsigned int col, row;
693 struct udevice *dev;
694
695 if (argc != 3)
696 return CMD_RET_USAGE;
697
Simon Glassc7298e72016-02-11 13:23:26 -0700698 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700699 return CMD_RET_FAILURE;
Simon Glassff9b9032021-07-24 09:03:30 -0600700 col = dectoul(argv[1], NULL);
701 row = dectoul(argv[2], NULL);
Simon Glass84c7fb32016-01-18 19:52:17 -0700702 vidconsole_position_cursor(dev, col, row);
703
704 return 0;
705}
706
Simon Glassed38aef2020-05-10 11:40:03 -0600707static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass84c7fb32016-01-18 19:52:17 -0700708 char *const argv[])
709{
710 struct udevice *dev;
711 const char *s;
712
713 if (argc != 2)
714 return CMD_RET_USAGE;
715
Simon Glassc7298e72016-02-11 13:23:26 -0700716 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700717 return CMD_RET_FAILURE;
718 for (s = argv[1]; *s; s++)
719 vidconsole_put_char(dev, *s);
720
Michal Simek632e3d42020-12-14 08:47:52 +0100721 return video_sync(dev->parent, false);
Simon Glass84c7fb32016-01-18 19:52:17 -0700722}
723
724U_BOOT_CMD(
725 setcurs, 3, 1, do_video_setcursor,
726 "set cursor position within screen",
727 " <col> <row> in character"
728);
729
730U_BOOT_CMD(
731 lcdputs, 2, 1, do_video_puts,
732 "print string on video framebuffer",
733 " <string>"
734);
Anatolij Gustschin20b79192020-05-25 21:47:19 +0200735#endif /* CONFIG_IS_ENABLED(CMD_VIDCONSOLE) */