blob: 8f7d987cdd2c77ceeb853897a4a536560aa2a59a [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)) {
158 return (colors[idx].r << 16) |
159 (colors[idx].g << 8) |
160 (colors[idx].b << 0);
161 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100162 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400163 default:
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100164 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400165 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100166
167 /*
168 * For unknown bit arrangements just support
169 * black and white.
170 */
171 if (idx)
172 return 0xffffff; /* white */
173
174 return 0x000000; /* black */
Rob Clark50509bb2017-09-13 18:12:22 -0400175}
176
Rob Clark06e7a0d2017-09-13 18:12:21 -0400177static char *parsenum(char *s, int *num)
178{
179 char *end;
180 *num = simple_strtol(s, &end, 10);
181 return end;
182}
183
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200184/**
185 * set_cursor_position() - set cursor position
186 *
187 * @priv: private data of the video console
188 * @row: new row
189 * @col: new column
190 */
191static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
192{
193 /*
194 * Ensure we stay in the bounds of the screen.
195 */
196 if (row >= priv->rows)
197 row = priv->rows - 1;
198 if (col >= priv->cols)
199 col = priv->cols - 1;
200
201 priv->ycur = row * priv->y_charsize;
202 priv->xcur_frac = priv->xstart_frac +
203 VID_TO_POS(col * priv->x_charsize);
204}
205
206/**
207 * get_cursor_position() - get cursor position
208 *
209 * @priv: private data of the video console
210 * @row: row
211 * @col: column
212 */
213static void get_cursor_position(struct vidconsole_priv *priv,
214 int *row, int *col)
215{
216 *row = priv->ycur / priv->y_charsize;
217 *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
218 priv->x_charsize;
219}
220
Rob Clark06e7a0d2017-09-13 18:12:21 -0400221/*
222 * Process a character while accumulating an escape string. Chars are
223 * accumulated into escape_buf until the end of escape sequence is
224 * found, at which point the sequence is parsed and processed.
225 */
226static void vidconsole_escape_char(struct udevice *dev, char ch)
227{
228 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
229
230 if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
231 goto error;
232
233 /* Sanity checking for bogus ESC sequences: */
234 if (priv->escape_len >= sizeof(priv->escape_buf))
235 goto error;
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200236 if (priv->escape_len == 0) {
237 switch (ch) {
238 case '7':
239 /* Save cursor position */
240 get_cursor_position(priv, &priv->row_saved,
241 &priv->col_saved);
242 priv->escape = 0;
243
244 return;
245 case '8': {
246 /* Restore cursor position */
247 int row = priv->row_saved;
248 int col = priv->col_saved;
249
250 set_cursor_position(priv, row, col);
251 priv->escape = 0;
252 return;
253 }
254 case '[':
255 break;
256 default:
257 goto error;
258 }
259 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400260
261 priv->escape_buf[priv->escape_len++] = ch;
262
263 /*
264 * Escape sequences are terminated by a letter, so keep
265 * accumulating until we get one:
266 */
267 if (!isalpha(ch))
268 return;
269
270 /*
271 * clear escape mode first, otherwise things will get highly
272 * surprising if you hit any debug prints that come back to
273 * this console.
274 */
275 priv->escape = 0;
276
277 switch (ch) {
Andre Przywarad4a294c2019-03-23 01:29:57 +0000278 case 'A':
279 case 'B':
280 case 'C':
281 case 'D':
282 case 'E':
283 case 'F': {
284 int row, col, num;
285 char *s = priv->escape_buf;
286
287 /*
288 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
289 * Cursor left/right: [%dD, [%dC
290 */
291 s++; /* [ */
292 s = parsenum(s, &num);
293 if (num == 0) /* No digit in sequence ... */
294 num = 1; /* ... means "move by 1". */
295
296 get_cursor_position(priv, &row, &col);
297 if (ch == 'A' || ch == 'F')
298 row -= num;
299 if (ch == 'C')
300 col += num;
301 if (ch == 'D')
302 col -= num;
303 if (ch == 'B' || ch == 'E')
304 row += num;
305 if (ch == 'E' || ch == 'F')
306 col = 0;
307 if (col < 0)
308 col = 0;
309 if (row < 0)
310 row = 0;
311 /* Right and bottom overflows are handled in the callee. */
312 set_cursor_position(priv, row, col);
313 break;
314 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400315 case 'H':
316 case 'f': {
317 int row, col;
318 char *s = priv->escape_buf;
319
320 /*
321 * Set cursor position: [%d;%df or [%d;%dH
322 */
323 s++; /* [ */
324 s = parsenum(s, &row);
325 s++; /* ; */
326 s = parsenum(s, &col);
327
Heinrich Schuchardtc3c69302018-11-10 19:55:48 +0100328 /*
329 * Video origin is [0, 0], terminal origin is [1, 1].
330 */
331 if (row)
332 --row;
333 if (col)
334 --col;
335
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200336 set_cursor_position(priv, row, col);
Rob Clark06e7a0d2017-09-13 18:12:21 -0400337
338 break;
339 }
340 case 'J': {
341 int mode;
342
343 /*
344 * Clear part/all screen:
345 * [J or [0J - clear screen from cursor down
346 * [1J - clear screen from cursor up
347 * [2J - clear entire screen
348 *
349 * TODO we really only handle entire-screen case, others
350 * probably require some additions to video-uclass (and
351 * are not really needed yet by efi_console)
352 */
353 parsenum(priv->escape_buf + 1, &mode);
354
355 if (mode == 2) {
Michal Simek632e3d42020-12-14 08:47:52 +0100356 int ret;
357
Rob Clark06e7a0d2017-09-13 18:12:21 -0400358 video_clear(dev->parent);
Michal Simek632e3d42020-12-14 08:47:52 +0100359 ret = video_sync(dev->parent, false);
360 if (ret) {
361#ifdef DEBUG
362 console_puts_select_stderr(true, "[vc err: video_sync]");
363#endif
364 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400365 priv->ycur = 0;
366 priv->xcur_frac = priv->xstart_frac;
367 } else {
368 debug("unsupported clear mode: %d\n", mode);
369 }
370 break;
371 }
Andre Przywara918622a2019-03-23 01:29:58 +0000372 case 'K': {
373 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
374 int mode;
375
376 /*
377 * Clear (parts of) current line
378 * [0K - clear line to end
379 * [2K - clear entire line
380 */
381 parsenum(priv->escape_buf + 1, &mode);
382
383 if (mode == 2) {
384 int row, col;
385
386 get_cursor_position(priv, &row, &col);
387 vidconsole_set_row(dev, row, vid_priv->colour_bg);
388 }
389 break;
390 }
Rob Clark50509bb2017-09-13 18:12:22 -0400391 case 'm': {
392 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
393 char *s = priv->escape_buf;
394 char *end = &priv->escape_buf[priv->escape_len];
395
396 /*
397 * Set graphics mode: [%d;...;%dm
398 *
399 * Currently only supports the color attributes:
400 *
401 * Foreground Colors:
402 *
403 * 30 Black
404 * 31 Red
405 * 32 Green
406 * 33 Yellow
407 * 34 Blue
408 * 35 Magenta
409 * 36 Cyan
410 * 37 White
411 *
412 * Background Colors:
413 *
414 * 40 Black
415 * 41 Red
416 * 42 Green
417 * 43 Yellow
418 * 44 Blue
419 * 45 Magenta
420 * 46 Cyan
421 * 47 White
422 */
423
424 s++; /* [ */
425 while (s < end) {
426 int val;
427
428 s = parsenum(s, &val);
429 s++;
430
431 switch (val) {
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100432 case 0:
433 /* all attributes off */
Simon Glass2b063b82018-11-06 15:21:36 -0700434 video_set_default_colors(dev->parent, false);
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100435 break;
436 case 1:
437 /* bold */
438 vid_priv->fg_col_idx |= 8;
439 vid_priv->colour_fg = vid_console_color(
440 vid_priv, vid_priv->fg_col_idx);
441 break;
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000442 case 7:
443 /* reverse video */
444 vid_priv->colour_fg = vid_console_color(
445 vid_priv, vid_priv->bg_col_idx);
446 vid_priv->colour_bg = vid_console_color(
447 vid_priv, vid_priv->fg_col_idx);
448 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400449 case 30 ... 37:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100450 /* foreground color */
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100451 vid_priv->fg_col_idx &= ~7;
452 vid_priv->fg_col_idx |= val - 30;
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100453 vid_priv->colour_fg = vid_console_color(
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100454 vid_priv, vid_priv->fg_col_idx);
Rob Clark50509bb2017-09-13 18:12:22 -0400455 break;
456 case 40 ... 47:
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000457 /* background color, also mask the bold bit */
458 vid_priv->bg_col_idx &= ~0xf;
459 vid_priv->bg_col_idx |= val - 40;
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100460 vid_priv->colour_bg = vid_console_color(
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000461 vid_priv, vid_priv->bg_col_idx);
Rob Clark50509bb2017-09-13 18:12:22 -0400462 break;
463 default:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100464 /* ignore unsupported SGR parameter */
Rob Clark50509bb2017-09-13 18:12:22 -0400465 break;
466 }
467 }
468
469 break;
470 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400471 default:
472 debug("unrecognized escape sequence: %*s\n",
473 priv->escape_len, priv->escape_buf);
474 }
475
476 return;
477
478error:
479 /* something went wrong, just revert to normal mode: */
480 priv->escape = 0;
481}
482
Andre Przywarade86baf2019-03-23 01:29:59 +0000483/* Put that actual character on the screen (using the CP437 code page). */
484static int vidconsole_output_glyph(struct udevice *dev, char ch)
485{
486 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
487 int ret;
488
489 /*
490 * Failure of this function normally indicates an unsupported
491 * colour depth. Check this and return an error to help with
492 * diagnosis.
493 */
494 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
495 if (ret == -EAGAIN) {
496 vidconsole_newline(dev);
497 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
498 }
499 if (ret < 0)
500 return ret;
501 priv->xcur_frac += ret;
502 priv->last_ch = ch;
503 if (priv->xcur_frac >= priv->xsize_frac)
504 vidconsole_newline(dev);
505
506 return 0;
507}
508
Simon Glass84c7fb32016-01-18 19:52:17 -0700509int vidconsole_put_char(struct udevice *dev, char ch)
510{
511 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
512 int ret;
513
Rob Clark06e7a0d2017-09-13 18:12:21 -0400514 if (priv->escape) {
515 vidconsole_escape_char(dev, ch);
516 return 0;
517 }
518
Simon Glass84c7fb32016-01-18 19:52:17 -0700519 switch (ch) {
Rob Clark06e7a0d2017-09-13 18:12:21 -0400520 case '\x1b':
521 priv->escape_len = 0;
522 priv->escape = 1;
523 break;
Simon Glass37b80202016-01-14 18:10:38 -0700524 case '\a':
525 /* beep */
526 break;
Simon Glass84c7fb32016-01-18 19:52:17 -0700527 case '\r':
Simon Glassa74451d2016-01-14 18:10:39 -0700528 priv->xcur_frac = priv->xstart_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700529 break;
530 case '\n':
531 vidconsole_newline(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700532 vidconsole_entry_start(dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700533 break;
534 case '\t': /* Tab (8 chars alignment) */
Simon Glass52c10c52016-01-14 18:10:37 -0700535 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
536 + 1) * priv->tab_width_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700537
Simon Glass52c10c52016-01-14 18:10:37 -0700538 if (priv->xcur_frac >= priv->xsize_frac)
Simon Glass84c7fb32016-01-18 19:52:17 -0700539 vidconsole_newline(dev);
540 break;
541 case '\b':
542 vidconsole_back(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700543 priv->last_ch = 0;
Simon Glass84c7fb32016-01-18 19:52:17 -0700544 break;
545 default:
Andre Przywarade86baf2019-03-23 01:29:59 +0000546 ret = vidconsole_output_glyph(dev, ch);
Simon Glass52c10c52016-01-14 18:10:37 -0700547 if (ret < 0)
Simon Glass84c7fb32016-01-18 19:52:17 -0700548 return ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700549 break;
550 }
551
552 return 0;
553}
554
Marek Vasuta89f9cb2019-05-17 20:22:31 +0200555int vidconsole_put_string(struct udevice *dev, const char *str)
556{
557 const char *s;
558 int ret;
559
560 for (s = str; *s; s++) {
561 ret = vidconsole_put_char(dev, *s);
562 if (ret)
563 return ret;
564 }
565
566 return 0;
567}
568
Simon Glass84c7fb32016-01-18 19:52:17 -0700569static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
570{
571 struct udevice *dev = sdev->priv;
Simon Glassf97beb72020-07-02 21:12:14 -0600572 int ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700573
Simon Glassf97beb72020-07-02 21:12:14 -0600574 ret = vidconsole_put_char(dev, ch);
575 if (ret) {
576#ifdef DEBUG
577 console_puts_select_stderr(true, "[vc err: putc]");
578#endif
579 }
Michal Simek632e3d42020-12-14 08:47:52 +0100580 ret = video_sync(dev->parent, false);
581 if (ret) {
582#ifdef DEBUG
583 console_puts_select_stderr(true, "[vc err: video_sync]");
584#endif
585 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700586}
587
588static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
589{
590 struct udevice *dev = sdev->priv;
Simon Glassf97beb72020-07-02 21:12:14 -0600591 int ret;
592
593 ret = vidconsole_put_string(dev, s);
594 if (ret) {
595#ifdef DEBUG
596 char str[30];
Simon Glass84c7fb32016-01-18 19:52:17 -0700597
Simon Glassf97beb72020-07-02 21:12:14 -0600598 snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
599 console_puts_select_stderr(true, str);
600#endif
601 }
Michal Simek632e3d42020-12-14 08:47:52 +0100602 ret = video_sync(dev->parent, false);
603 if (ret) {
604#ifdef DEBUG
605 console_puts_select_stderr(true, "[vc err: video_sync]");
606#endif
607 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700608}
609
610/* Set up the number of rows and colours (rotated drivers override this) */
611static int vidconsole_pre_probe(struct udevice *dev)
612{
613 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
614 struct udevice *vid = dev->parent;
615 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
616
Simon Glass52c10c52016-01-14 18:10:37 -0700617 priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
Simon Glass84c7fb32016-01-18 19:52:17 -0700618
619 return 0;
620}
621
622/* Register the device with stdio */
623static int vidconsole_post_probe(struct udevice *dev)
624{
625 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
626 struct stdio_dev *sdev = &priv->sdev;
Simon Glass84c7fb32016-01-18 19:52:17 -0700627
Simon Glass52c10c52016-01-14 18:10:37 -0700628 if (!priv->tab_width_frac)
629 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
630
Simon Glass75e534b2020-12-16 21:20:07 -0700631 if (dev_seq(dev)) {
Simon Glass798ff502016-01-21 19:44:51 -0700632 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
Simon Glass75e534b2020-12-16 21:20:07 -0700633 dev_seq(dev));
Simon Glass798ff502016-01-21 19:44:51 -0700634 } else {
635 strcpy(sdev->name, "vidconsole");
636 }
Simon Glass52c10c52016-01-14 18:10:37 -0700637
Simon Glass84c7fb32016-01-18 19:52:17 -0700638 sdev->flags = DEV_FLAGS_OUTPUT;
639 sdev->putc = vidconsole_putc;
640 sdev->puts = vidconsole_puts;
641 sdev->priv = dev;
Simon Glass84c7fb32016-01-18 19:52:17 -0700642
Masahiro Yamadabf528cd2016-09-06 22:17:33 +0900643 return stdio_register(sdev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700644}
645
646UCLASS_DRIVER(vidconsole) = {
647 .id = UCLASS_VIDEO_CONSOLE,
648 .name = "vidconsole0",
649 .pre_probe = vidconsole_pre_probe,
650 .post_probe = vidconsole_post_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700651 .per_device_auto = sizeof(struct vidconsole_priv),
Simon Glass84c7fb32016-01-18 19:52:17 -0700652};
653
Simon Glass31a7e232020-07-02 21:12:23 -0600654#ifdef CONFIG_VIDEO_COPY
655int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
656{
657 struct udevice *vid = dev_get_parent(dev);
658
659 return video_sync_copy(vid, from, to);
660}
661
662int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
663 int size)
664{
665 memmove(dst, src, size);
666 return vidconsole_sync_copy(dev, dst, dst + size);
667}
668#endif
669
Anatolij Gustschin20b79192020-05-25 21:47:19 +0200670#if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
Simon Glass84c7fb32016-01-18 19:52:17 -0700671void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
672{
673 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass52c10c52016-01-14 18:10:37 -0700674 struct udevice *vid_dev = dev->parent;
675 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700676
Simon Glass59f44212018-10-01 12:22:47 -0600677 col *= priv->x_charsize;
678 row *= priv->y_charsize;
Simon Glass52c10c52016-01-14 18:10:37 -0700679 priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
Ye Li3e374692020-06-10 02:52:21 -0700680 priv->xstart_frac = priv->xcur_frac;
Simon Glass52c10c52016-01-14 18:10:37 -0700681 priv->ycur = min_t(short, row, vid_priv->ysize - 1);
Simon Glass84c7fb32016-01-18 19:52:17 -0700682}
683
Simon Glassed38aef2020-05-10 11:40:03 -0600684static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass84c7fb32016-01-18 19:52:17 -0700685 char *const argv[])
686{
687 unsigned int col, row;
688 struct udevice *dev;
689
690 if (argc != 3)
691 return CMD_RET_USAGE;
692
Simon Glassc7298e72016-02-11 13:23:26 -0700693 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700694 return CMD_RET_FAILURE;
695 col = simple_strtoul(argv[1], NULL, 10);
696 row = simple_strtoul(argv[2], NULL, 10);
697 vidconsole_position_cursor(dev, col, row);
698
699 return 0;
700}
701
Simon Glassed38aef2020-05-10 11:40:03 -0600702static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass84c7fb32016-01-18 19:52:17 -0700703 char *const argv[])
704{
705 struct udevice *dev;
706 const char *s;
707
708 if (argc != 2)
709 return CMD_RET_USAGE;
710
Simon Glassc7298e72016-02-11 13:23:26 -0700711 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700712 return CMD_RET_FAILURE;
713 for (s = argv[1]; *s; s++)
714 vidconsole_put_char(dev, *s);
715
Michal Simek632e3d42020-12-14 08:47:52 +0100716 return video_sync(dev->parent, false);
Simon Glass84c7fb32016-01-18 19:52:17 -0700717}
718
719U_BOOT_CMD(
720 setcurs, 3, 1, do_video_setcursor,
721 "set cursor position within screen",
722 " <col> <row> in character"
723);
724
725U_BOOT_CMD(
726 lcdputs, 2, 1, do_video_puts,
727 "print string on video framebuffer",
728 " <string>"
729);
Anatolij Gustschin20b79192020-05-25 21:47:19 +0200730#endif /* CONFIG_IS_ENABLED(CMD_VIDCONSOLE) */