blob: 81b65f5aaee98865dd451b24be69122ae056827c [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
10#include <common.h>
Simon Glassed38aef2020-05-10 11:40:03 -060011#include <command.h>
Simon Glassf97beb72020-07-02 21:12:14 -060012#include <console.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Simon Glass84c7fb32016-01-18 19:52:17 -070014#include <dm.h>
15#include <video.h>
16#include <video_console.h>
Heinrich Schuchardt2172fa42018-03-02 20:50:17 +010017#include <video_font.h> /* Bitmap font for code page 437 */
Simon Glassf97beb72020-07-02 21:12:14 -060018#include <linux/ctype.h>
Simon Glass84c7fb32016-01-18 19:52:17 -070019
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +010020/*
21 * Structure to describe a console color
22 */
23struct vid_rgb {
24 u32 r;
25 u32 g;
26 u32 b;
27};
28
Simon Glass84c7fb32016-01-18 19:52:17 -070029/* By default we scroll by a single line */
30#ifndef CONFIG_CONSOLE_SCROLL_LINES
31#define CONFIG_CONSOLE_SCROLL_LINES 1
32#endif
33
34int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
35{
36 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
37
38 if (!ops->putc_xy)
39 return -ENOSYS;
40 return ops->putc_xy(dev, x, y, ch);
41}
42
43int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
44 uint count)
45{
46 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
47
48 if (!ops->move_rows)
49 return -ENOSYS;
50 return ops->move_rows(dev, rowdst, rowsrc, count);
51}
52
53int vidconsole_set_row(struct udevice *dev, uint row, int clr)
54{
55 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
56
57 if (!ops->set_row)
58 return -ENOSYS;
59 return ops->set_row(dev, row, clr);
60}
61
Simon Glassafee7432016-01-14 18:10:40 -070062static int vidconsole_entry_start(struct udevice *dev)
63{
64 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
65
66 if (!ops->entry_start)
67 return -ENOSYS;
68 return ops->entry_start(dev);
69}
70
Simon Glass84c7fb32016-01-18 19:52:17 -070071/* Move backwards one space */
Simon Glass33bd3b62016-01-14 18:10:41 -070072static int vidconsole_back(struct udevice *dev)
Simon Glass84c7fb32016-01-18 19:52:17 -070073{
74 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass33bd3b62016-01-14 18:10:41 -070075 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
76 int ret;
77
78 if (ops->backspace) {
79 ret = ops->backspace(dev);
80 if (ret != -ENOSYS)
81 return ret;
82 }
Simon Glass84c7fb32016-01-18 19:52:17 -070083
Simon Glass52c10c52016-01-14 18:10:37 -070084 priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
Simon Glassa74451d2016-01-14 18:10:39 -070085 if (priv->xcur_frac < priv->xstart_frac) {
Simon Glass52c10c52016-01-14 18:10:37 -070086 priv->xcur_frac = (priv->cols - 1) *
87 VID_TO_POS(priv->x_charsize);
88 priv->ycur -= priv->y_charsize;
89 if (priv->ycur < 0)
90 priv->ycur = 0;
Simon Glass84c7fb32016-01-18 19:52:17 -070091 }
Michal Simek632e3d42020-12-14 08:47:52 +010092 return video_sync(dev->parent, false);
Simon Glass84c7fb32016-01-18 19:52:17 -070093}
94
95/* Move to a newline, scrolling the display if necessary */
96static void vidconsole_newline(struct udevice *dev)
97{
98 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
99 struct udevice *vid_dev = dev->parent;
100 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
101 const int rows = CONFIG_CONSOLE_SCROLL_LINES;
Michal Simek632e3d42020-12-14 08:47:52 +0100102 int i, ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700103
Simon Glassa74451d2016-01-14 18:10:39 -0700104 priv->xcur_frac = priv->xstart_frac;
Simon Glass52c10c52016-01-14 18:10:37 -0700105 priv->ycur += priv->y_charsize;
Simon Glass84c7fb32016-01-18 19:52:17 -0700106
107 /* Check if we need to scroll the terminal */
Simon Glass52c10c52016-01-14 18:10:37 -0700108 if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
Simon Glass84c7fb32016-01-18 19:52:17 -0700109 vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
110 for (i = 0; i < rows; i++)
111 vidconsole_set_row(dev, priv->rows - i - 1,
112 vid_priv->colour_bg);
Simon Glass52c10c52016-01-14 18:10:37 -0700113 priv->ycur -= rows * priv->y_charsize;
Simon Glass84c7fb32016-01-18 19:52:17 -0700114 }
Simon Glassafee7432016-01-14 18:10:40 -0700115 priv->last_ch = 0;
116
Michal Simek632e3d42020-12-14 08:47:52 +0100117 ret = video_sync(dev->parent, false);
118 if (ret) {
119#ifdef DEBUG
120 console_puts_select_stderr(true, "[vc err: video_sync]");
121#endif
122 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700123}
124
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100125static const struct vid_rgb colors[VID_COLOR_COUNT] = {
Rob Clark50509bb2017-09-13 18:12:22 -0400126 { 0x00, 0x00, 0x00 }, /* black */
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100127 { 0xc0, 0x00, 0x00 }, /* red */
128 { 0x00, 0xc0, 0x00 }, /* green */
129 { 0xc0, 0x60, 0x00 }, /* brown */
130 { 0x00, 0x00, 0xc0 }, /* blue */
131 { 0xc0, 0x00, 0xc0 }, /* magenta */
132 { 0x00, 0xc0, 0xc0 }, /* cyan */
133 { 0xc0, 0xc0, 0xc0 }, /* light gray */
134 { 0x80, 0x80, 0x80 }, /* gray */
135 { 0xff, 0x00, 0x00 }, /* bright red */
136 { 0x00, 0xff, 0x00 }, /* bright green */
Rob Clark50509bb2017-09-13 18:12:22 -0400137 { 0xff, 0xff, 0x00 }, /* yellow */
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100138 { 0x00, 0x00, 0xff }, /* bright blue */
139 { 0xff, 0x00, 0xff }, /* bright magenta */
140 { 0x00, 0xff, 0xff }, /* bright cyan */
Rob Clark50509bb2017-09-13 18:12:22 -0400141 { 0xff, 0xff, 0xff }, /* white */
142};
143
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100144u32 vid_console_color(struct video_priv *priv, unsigned int idx)
Rob Clark50509bb2017-09-13 18:12:22 -0400145{
146 switch (priv->bpix) {
147 case VIDEO_BPP16:
Simon Glassa4a43c62019-12-20 18:10:36 -0700148 if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
149 return ((colors[idx].r >> 3) << 11) |
150 ((colors[idx].g >> 2) << 5) |
151 ((colors[idx].b >> 3) << 0);
152 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100153 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400154 case VIDEO_BPP32:
Simon Glassa4a43c62019-12-20 18:10:36 -0700155 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
156 return (colors[idx].r << 16) |
157 (colors[idx].g << 8) |
158 (colors[idx].b << 0);
159 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100160 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400161 default:
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100162 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400163 }
Anatolij Gustschin0db23e02020-01-06 23:00:38 +0100164
165 /*
166 * For unknown bit arrangements just support
167 * black and white.
168 */
169 if (idx)
170 return 0xffffff; /* white */
171
172 return 0x000000; /* black */
Rob Clark50509bb2017-09-13 18:12:22 -0400173}
174
Rob Clark06e7a0d2017-09-13 18:12:21 -0400175static char *parsenum(char *s, int *num)
176{
177 char *end;
178 *num = simple_strtol(s, &end, 10);
179 return end;
180}
181
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200182/**
183 * set_cursor_position() - set cursor position
184 *
185 * @priv: private data of the video console
186 * @row: new row
187 * @col: new column
188 */
189static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
190{
191 /*
192 * Ensure we stay in the bounds of the screen.
193 */
194 if (row >= priv->rows)
195 row = priv->rows - 1;
196 if (col >= priv->cols)
197 col = priv->cols - 1;
198
199 priv->ycur = row * priv->y_charsize;
200 priv->xcur_frac = priv->xstart_frac +
201 VID_TO_POS(col * priv->x_charsize);
202}
203
204/**
205 * get_cursor_position() - get cursor position
206 *
207 * @priv: private data of the video console
208 * @row: row
209 * @col: column
210 */
211static void get_cursor_position(struct vidconsole_priv *priv,
212 int *row, int *col)
213{
214 *row = priv->ycur / priv->y_charsize;
215 *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
216 priv->x_charsize;
217}
218
Rob Clark06e7a0d2017-09-13 18:12:21 -0400219/*
220 * Process a character while accumulating an escape string. Chars are
221 * accumulated into escape_buf until the end of escape sequence is
222 * found, at which point the sequence is parsed and processed.
223 */
224static void vidconsole_escape_char(struct udevice *dev, char ch)
225{
226 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
227
228 if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
229 goto error;
230
231 /* Sanity checking for bogus ESC sequences: */
232 if (priv->escape_len >= sizeof(priv->escape_buf))
233 goto error;
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200234 if (priv->escape_len == 0) {
235 switch (ch) {
236 case '7':
237 /* Save cursor position */
238 get_cursor_position(priv, &priv->row_saved,
239 &priv->col_saved);
240 priv->escape = 0;
241
242 return;
243 case '8': {
244 /* Restore cursor position */
245 int row = priv->row_saved;
246 int col = priv->col_saved;
247
248 set_cursor_position(priv, row, col);
249 priv->escape = 0;
250 return;
251 }
252 case '[':
253 break;
254 default:
255 goto error;
256 }
257 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400258
259 priv->escape_buf[priv->escape_len++] = ch;
260
261 /*
262 * Escape sequences are terminated by a letter, so keep
263 * accumulating until we get one:
264 */
265 if (!isalpha(ch))
266 return;
267
268 /*
269 * clear escape mode first, otherwise things will get highly
270 * surprising if you hit any debug prints that come back to
271 * this console.
272 */
273 priv->escape = 0;
274
275 switch (ch) {
Andre Przywarad4a294c2019-03-23 01:29:57 +0000276 case 'A':
277 case 'B':
278 case 'C':
279 case 'D':
280 case 'E':
281 case 'F': {
282 int row, col, num;
283 char *s = priv->escape_buf;
284
285 /*
286 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
287 * Cursor left/right: [%dD, [%dC
288 */
289 s++; /* [ */
290 s = parsenum(s, &num);
291 if (num == 0) /* No digit in sequence ... */
292 num = 1; /* ... means "move by 1". */
293
294 get_cursor_position(priv, &row, &col);
295 if (ch == 'A' || ch == 'F')
296 row -= num;
297 if (ch == 'C')
298 col += num;
299 if (ch == 'D')
300 col -= num;
301 if (ch == 'B' || ch == 'E')
302 row += num;
303 if (ch == 'E' || ch == 'F')
304 col = 0;
305 if (col < 0)
306 col = 0;
307 if (row < 0)
308 row = 0;
309 /* Right and bottom overflows are handled in the callee. */
310 set_cursor_position(priv, row, col);
311 break;
312 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400313 case 'H':
314 case 'f': {
315 int row, col;
316 char *s = priv->escape_buf;
317
318 /*
319 * Set cursor position: [%d;%df or [%d;%dH
320 */
321 s++; /* [ */
322 s = parsenum(s, &row);
323 s++; /* ; */
324 s = parsenum(s, &col);
325
Heinrich Schuchardtc3c69302018-11-10 19:55:48 +0100326 /*
327 * Video origin is [0, 0], terminal origin is [1, 1].
328 */
329 if (row)
330 --row;
331 if (col)
332 --col;
333
Heinrich Schuchardt9e933f12018-09-19 21:31:48 +0200334 set_cursor_position(priv, row, col);
Rob Clark06e7a0d2017-09-13 18:12:21 -0400335
336 break;
337 }
338 case 'J': {
339 int mode;
340
341 /*
342 * Clear part/all screen:
343 * [J or [0J - clear screen from cursor down
344 * [1J - clear screen from cursor up
345 * [2J - clear entire screen
346 *
347 * TODO we really only handle entire-screen case, others
348 * probably require some additions to video-uclass (and
349 * are not really needed yet by efi_console)
350 */
351 parsenum(priv->escape_buf + 1, &mode);
352
353 if (mode == 2) {
Michal Simek632e3d42020-12-14 08:47:52 +0100354 int ret;
355
Rob Clark06e7a0d2017-09-13 18:12:21 -0400356 video_clear(dev->parent);
Michal Simek632e3d42020-12-14 08:47:52 +0100357 ret = video_sync(dev->parent, false);
358 if (ret) {
359#ifdef DEBUG
360 console_puts_select_stderr(true, "[vc err: video_sync]");
361#endif
362 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400363 priv->ycur = 0;
364 priv->xcur_frac = priv->xstart_frac;
365 } else {
366 debug("unsupported clear mode: %d\n", mode);
367 }
368 break;
369 }
Andre Przywara918622a2019-03-23 01:29:58 +0000370 case 'K': {
371 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
372 int mode;
373
374 /*
375 * Clear (parts of) current line
376 * [0K - clear line to end
377 * [2K - clear entire line
378 */
379 parsenum(priv->escape_buf + 1, &mode);
380
381 if (mode == 2) {
382 int row, col;
383
384 get_cursor_position(priv, &row, &col);
385 vidconsole_set_row(dev, row, vid_priv->colour_bg);
386 }
387 break;
388 }
Rob Clark50509bb2017-09-13 18:12:22 -0400389 case 'm': {
390 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
391 char *s = priv->escape_buf;
392 char *end = &priv->escape_buf[priv->escape_len];
393
394 /*
395 * Set graphics mode: [%d;...;%dm
396 *
397 * Currently only supports the color attributes:
398 *
399 * Foreground Colors:
400 *
401 * 30 Black
402 * 31 Red
403 * 32 Green
404 * 33 Yellow
405 * 34 Blue
406 * 35 Magenta
407 * 36 Cyan
408 * 37 White
409 *
410 * Background Colors:
411 *
412 * 40 Black
413 * 41 Red
414 * 42 Green
415 * 43 Yellow
416 * 44 Blue
417 * 45 Magenta
418 * 46 Cyan
419 * 47 White
420 */
421
422 s++; /* [ */
423 while (s < end) {
424 int val;
425
426 s = parsenum(s, &val);
427 s++;
428
429 switch (val) {
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100430 case 0:
431 /* all attributes off */
Simon Glass2b063b82018-11-06 15:21:36 -0700432 video_set_default_colors(dev->parent, false);
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100433 break;
434 case 1:
435 /* bold */
436 vid_priv->fg_col_idx |= 8;
437 vid_priv->colour_fg = vid_console_color(
438 vid_priv, vid_priv->fg_col_idx);
439 break;
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000440 case 7:
441 /* reverse video */
442 vid_priv->colour_fg = vid_console_color(
443 vid_priv, vid_priv->bg_col_idx);
444 vid_priv->colour_bg = vid_console_color(
445 vid_priv, vid_priv->fg_col_idx);
446 break;
Rob Clark50509bb2017-09-13 18:12:22 -0400447 case 30 ... 37:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100448 /* foreground color */
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100449 vid_priv->fg_col_idx &= ~7;
450 vid_priv->fg_col_idx |= val - 30;
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100451 vid_priv->colour_fg = vid_console_color(
Heinrich Schuchardt2a436db2018-02-08 21:47:12 +0100452 vid_priv, vid_priv->fg_col_idx);
Rob Clark50509bb2017-09-13 18:12:22 -0400453 break;
454 case 40 ... 47:
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000455 /* background color, also mask the bold bit */
456 vid_priv->bg_col_idx &= ~0xf;
457 vid_priv->bg_col_idx |= val - 40;
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100458 vid_priv->colour_bg = vid_console_color(
Andre Przywara4ed5bc82019-03-23 01:29:56 +0000459 vid_priv, vid_priv->bg_col_idx);
Rob Clark50509bb2017-09-13 18:12:22 -0400460 break;
461 default:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100462 /* ignore unsupported SGR parameter */
Rob Clark50509bb2017-09-13 18:12:22 -0400463 break;
464 }
465 }
466
467 break;
468 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400469 default:
470 debug("unrecognized escape sequence: %*s\n",
471 priv->escape_len, priv->escape_buf);
472 }
473
474 return;
475
476error:
477 /* something went wrong, just revert to normal mode: */
478 priv->escape = 0;
479}
480
Andre Przywarade86baf2019-03-23 01:29:59 +0000481/* Put that actual character on the screen (using the CP437 code page). */
482static int vidconsole_output_glyph(struct udevice *dev, char ch)
483{
484 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
485 int ret;
486
487 /*
488 * Failure of this function normally indicates an unsupported
489 * colour depth. Check this and return an error to help with
490 * diagnosis.
491 */
492 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
493 if (ret == -EAGAIN) {
494 vidconsole_newline(dev);
495 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
496 }
497 if (ret < 0)
498 return ret;
499 priv->xcur_frac += ret;
500 priv->last_ch = ch;
501 if (priv->xcur_frac >= priv->xsize_frac)
502 vidconsole_newline(dev);
503
504 return 0;
505}
506
Simon Glass84c7fb32016-01-18 19:52:17 -0700507int vidconsole_put_char(struct udevice *dev, char ch)
508{
509 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
510 int ret;
511
Rob Clark06e7a0d2017-09-13 18:12:21 -0400512 if (priv->escape) {
513 vidconsole_escape_char(dev, ch);
514 return 0;
515 }
516
Simon Glass84c7fb32016-01-18 19:52:17 -0700517 switch (ch) {
Rob Clark06e7a0d2017-09-13 18:12:21 -0400518 case '\x1b':
519 priv->escape_len = 0;
520 priv->escape = 1;
521 break;
Simon Glass37b80202016-01-14 18:10:38 -0700522 case '\a':
523 /* beep */
524 break;
Simon Glass84c7fb32016-01-18 19:52:17 -0700525 case '\r':
Simon Glassa74451d2016-01-14 18:10:39 -0700526 priv->xcur_frac = priv->xstart_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700527 break;
528 case '\n':
529 vidconsole_newline(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700530 vidconsole_entry_start(dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700531 break;
532 case '\t': /* Tab (8 chars alignment) */
Simon Glass52c10c52016-01-14 18:10:37 -0700533 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
534 + 1) * priv->tab_width_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700535
Simon Glass52c10c52016-01-14 18:10:37 -0700536 if (priv->xcur_frac >= priv->xsize_frac)
Simon Glass84c7fb32016-01-18 19:52:17 -0700537 vidconsole_newline(dev);
538 break;
539 case '\b':
540 vidconsole_back(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700541 priv->last_ch = 0;
Simon Glass84c7fb32016-01-18 19:52:17 -0700542 break;
543 default:
Andre Przywarade86baf2019-03-23 01:29:59 +0000544 ret = vidconsole_output_glyph(dev, ch);
Simon Glass52c10c52016-01-14 18:10:37 -0700545 if (ret < 0)
Simon Glass84c7fb32016-01-18 19:52:17 -0700546 return ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700547 break;
548 }
549
550 return 0;
551}
552
Marek Vasuta89f9cb2019-05-17 20:22:31 +0200553int vidconsole_put_string(struct udevice *dev, const char *str)
554{
555 const char *s;
556 int ret;
557
558 for (s = str; *s; s++) {
559 ret = vidconsole_put_char(dev, *s);
560 if (ret)
561 return ret;
562 }
563
564 return 0;
565}
566
Simon Glass84c7fb32016-01-18 19:52:17 -0700567static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
568{
569 struct udevice *dev = sdev->priv;
Simon Glassf97beb72020-07-02 21:12:14 -0600570 int ret;
Simon Glass84c7fb32016-01-18 19:52:17 -0700571
Simon Glassf97beb72020-07-02 21:12:14 -0600572 ret = vidconsole_put_char(dev, ch);
573 if (ret) {
574#ifdef DEBUG
575 console_puts_select_stderr(true, "[vc err: putc]");
576#endif
577 }
Michal Simek632e3d42020-12-14 08:47:52 +0100578 ret = video_sync(dev->parent, false);
579 if (ret) {
580#ifdef DEBUG
581 console_puts_select_stderr(true, "[vc err: video_sync]");
582#endif
583 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700584}
585
586static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
587{
588 struct udevice *dev = sdev->priv;
Simon Glassf97beb72020-07-02 21:12:14 -0600589 int ret;
590
591 ret = vidconsole_put_string(dev, s);
592 if (ret) {
593#ifdef DEBUG
594 char str[30];
Simon Glass84c7fb32016-01-18 19:52:17 -0700595
Simon Glassf97beb72020-07-02 21:12:14 -0600596 snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
597 console_puts_select_stderr(true, str);
598#endif
599 }
Michal Simek632e3d42020-12-14 08:47:52 +0100600 ret = video_sync(dev->parent, false);
601 if (ret) {
602#ifdef DEBUG
603 console_puts_select_stderr(true, "[vc err: video_sync]");
604#endif
605 }
Simon Glass84c7fb32016-01-18 19:52:17 -0700606}
607
608/* Set up the number of rows and colours (rotated drivers override this) */
609static int vidconsole_pre_probe(struct udevice *dev)
610{
611 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
612 struct udevice *vid = dev->parent;
613 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
614
Simon Glass52c10c52016-01-14 18:10:37 -0700615 priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
Simon Glass84c7fb32016-01-18 19:52:17 -0700616
617 return 0;
618}
619
620/* Register the device with stdio */
621static int vidconsole_post_probe(struct udevice *dev)
622{
623 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
624 struct stdio_dev *sdev = &priv->sdev;
Simon Glass84c7fb32016-01-18 19:52:17 -0700625
Simon Glass52c10c52016-01-14 18:10:37 -0700626 if (!priv->tab_width_frac)
627 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
628
Simon Glass75e534b2020-12-16 21:20:07 -0700629 if (dev_seq(dev)) {
Simon Glass798ff502016-01-21 19:44:51 -0700630 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
Simon Glass75e534b2020-12-16 21:20:07 -0700631 dev_seq(dev));
Simon Glass798ff502016-01-21 19:44:51 -0700632 } else {
633 strcpy(sdev->name, "vidconsole");
634 }
Simon Glass52c10c52016-01-14 18:10:37 -0700635
Simon Glass84c7fb32016-01-18 19:52:17 -0700636 sdev->flags = DEV_FLAGS_OUTPUT;
637 sdev->putc = vidconsole_putc;
638 sdev->puts = vidconsole_puts;
639 sdev->priv = dev;
Simon Glass84c7fb32016-01-18 19:52:17 -0700640
Masahiro Yamadabf528cd2016-09-06 22:17:33 +0900641 return stdio_register(sdev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700642}
643
644UCLASS_DRIVER(vidconsole) = {
645 .id = UCLASS_VIDEO_CONSOLE,
646 .name = "vidconsole0",
647 .pre_probe = vidconsole_pre_probe,
648 .post_probe = vidconsole_post_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700649 .per_device_auto = sizeof(struct vidconsole_priv),
Simon Glass84c7fb32016-01-18 19:52:17 -0700650};
651
Simon Glass31a7e232020-07-02 21:12:23 -0600652#ifdef CONFIG_VIDEO_COPY
653int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
654{
655 struct udevice *vid = dev_get_parent(dev);
656
657 return video_sync_copy(vid, from, to);
658}
659
660int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
661 int size)
662{
663 memmove(dst, src, size);
664 return vidconsole_sync_copy(dev, dst, dst + size);
665}
666#endif
667
Anatolij Gustschin20b79192020-05-25 21:47:19 +0200668#if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
Simon Glass84c7fb32016-01-18 19:52:17 -0700669void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
670{
671 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass52c10c52016-01-14 18:10:37 -0700672 struct udevice *vid_dev = dev->parent;
673 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700674
Simon Glass59f44212018-10-01 12:22:47 -0600675 col *= priv->x_charsize;
676 row *= priv->y_charsize;
Simon Glass52c10c52016-01-14 18:10:37 -0700677 priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
Ye Li3e374692020-06-10 02:52:21 -0700678 priv->xstart_frac = priv->xcur_frac;
Simon Glass52c10c52016-01-14 18:10:37 -0700679 priv->ycur = min_t(short, row, vid_priv->ysize - 1);
Simon Glass84c7fb32016-01-18 19:52:17 -0700680}
681
Simon Glassed38aef2020-05-10 11:40:03 -0600682static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass84c7fb32016-01-18 19:52:17 -0700683 char *const argv[])
684{
685 unsigned int col, row;
686 struct udevice *dev;
687
688 if (argc != 3)
689 return CMD_RET_USAGE;
690
Simon Glassc7298e72016-02-11 13:23:26 -0700691 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700692 return CMD_RET_FAILURE;
693 col = simple_strtoul(argv[1], NULL, 10);
694 row = simple_strtoul(argv[2], NULL, 10);
695 vidconsole_position_cursor(dev, col, row);
696
697 return 0;
698}
699
Simon Glassed38aef2020-05-10 11:40:03 -0600700static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass84c7fb32016-01-18 19:52:17 -0700701 char *const argv[])
702{
703 struct udevice *dev;
704 const char *s;
705
706 if (argc != 2)
707 return CMD_RET_USAGE;
708
Simon Glassc7298e72016-02-11 13:23:26 -0700709 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700710 return CMD_RET_FAILURE;
711 for (s = argv[1]; *s; s++)
712 vidconsole_put_char(dev, *s);
713
Michal Simek632e3d42020-12-14 08:47:52 +0100714 return video_sync(dev->parent, false);
Simon Glass84c7fb32016-01-18 19:52:17 -0700715}
716
717U_BOOT_CMD(
718 setcurs, 3, 1, do_video_setcursor,
719 "set cursor position within screen",
720 " <col> <row> in character"
721);
722
723U_BOOT_CMD(
724 lcdputs, 2, 1, do_video_puts,
725 "print string on video framebuffer",
726 " <string>"
727);
Anatolij Gustschin20b79192020-05-25 21:47:19 +0200728#endif /* CONFIG_IS_ENABLED(CMD_VIDCONSOLE) */