blob: d30e6db6f6fe63389029b55769a2c546b4fae5d0 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass83510762016-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 Glass83510762016-01-18 19:52:17 -07008 */
9
10#include <common.h>
Simon Glass09140112020-05-10 11:40:03 -060011#include <command.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060012#include <log.h>
Rob Clarka085aa12017-09-13 18:12:21 -040013#include <linux/ctype.h>
Simon Glass83510762016-01-18 19:52:17 -070014#include <dm.h>
15#include <video.h>
16#include <video_console.h>
Heinrich Schuchardt5fba5322018-03-02 20:50:17 +010017#include <video_font.h> /* Bitmap font for code page 437 */
Simon Glass83510762016-01-18 19:52:17 -070018
Heinrich Schuchardt5c30fbb2018-02-08 21:47:11 +010019/*
20 * Structure to describe a console color
21 */
22struct vid_rgb {
23 u32 r;
24 u32 g;
25 u32 b;
26};
27
Simon Glass83510762016-01-18 19:52:17 -070028/* By default we scroll by a single line */
29#ifndef CONFIG_CONSOLE_SCROLL_LINES
30#define CONFIG_CONSOLE_SCROLL_LINES 1
31#endif
32
33int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
34{
35 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
36
37 if (!ops->putc_xy)
38 return -ENOSYS;
39 return ops->putc_xy(dev, x, y, ch);
40}
41
42int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
43 uint count)
44{
45 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
46
47 if (!ops->move_rows)
48 return -ENOSYS;
49 return ops->move_rows(dev, rowdst, rowsrc, count);
50}
51
52int vidconsole_set_row(struct udevice *dev, uint row, int clr)
53{
54 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
55
56 if (!ops->set_row)
57 return -ENOSYS;
58 return ops->set_row(dev, row, clr);
59}
60
Simon Glass58c733a2016-01-14 18:10:40 -070061static int vidconsole_entry_start(struct udevice *dev)
62{
63 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
64
65 if (!ops->entry_start)
66 return -ENOSYS;
67 return ops->entry_start(dev);
68}
69
Simon Glass83510762016-01-18 19:52:17 -070070/* Move backwards one space */
Simon Glass7b9f7e42016-01-14 18:10:41 -070071static int vidconsole_back(struct udevice *dev)
Simon Glass83510762016-01-18 19:52:17 -070072{
73 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass7b9f7e42016-01-14 18:10:41 -070074 struct vidconsole_ops *ops = vidconsole_get_ops(dev);
75 int ret;
76
77 if (ops->backspace) {
78 ret = ops->backspace(dev);
79 if (ret != -ENOSYS)
80 return ret;
81 }
Simon Glass83510762016-01-18 19:52:17 -070082
Simon Glassf2661782016-01-14 18:10:37 -070083 priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
Simon Glassc5b77d02016-01-14 18:10:39 -070084 if (priv->xcur_frac < priv->xstart_frac) {
Simon Glassf2661782016-01-14 18:10:37 -070085 priv->xcur_frac = (priv->cols - 1) *
86 VID_TO_POS(priv->x_charsize);
87 priv->ycur -= priv->y_charsize;
88 if (priv->ycur < 0)
89 priv->ycur = 0;
Simon Glass83510762016-01-18 19:52:17 -070090 }
Simon Glass55d39912018-10-01 11:55:14 -060091 video_sync(dev->parent, false);
Simon Glass7b9f7e42016-01-14 18:10:41 -070092
93 return 0;
Simon Glass83510762016-01-18 19:52:17 -070094}
95
96/* Move to a newline, scrolling the display if necessary */
97static void vidconsole_newline(struct udevice *dev)
98{
99 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
100 struct udevice *vid_dev = dev->parent;
101 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
102 const int rows = CONFIG_CONSOLE_SCROLL_LINES;
103 int i;
104
Simon Glassc5b77d02016-01-14 18:10:39 -0700105 priv->xcur_frac = priv->xstart_frac;
Simon Glassf2661782016-01-14 18:10:37 -0700106 priv->ycur += priv->y_charsize;
Simon Glass83510762016-01-18 19:52:17 -0700107
108 /* Check if we need to scroll the terminal */
Simon Glassf2661782016-01-14 18:10:37 -0700109 if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
Simon Glass83510762016-01-18 19:52:17 -0700110 vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
111 for (i = 0; i < rows; i++)
112 vidconsole_set_row(dev, priv->rows - i - 1,
113 vid_priv->colour_bg);
Simon Glassf2661782016-01-14 18:10:37 -0700114 priv->ycur -= rows * priv->y_charsize;
Simon Glass83510762016-01-18 19:52:17 -0700115 }
Simon Glass58c733a2016-01-14 18:10:40 -0700116 priv->last_ch = 0;
117
Simon Glass55d39912018-10-01 11:55:14 -0600118 video_sync(dev->parent, false);
Simon Glass83510762016-01-18 19:52:17 -0700119}
120
Heinrich Schuchardt5c30fbb2018-02-08 21:47:11 +0100121static const struct vid_rgb colors[VID_COLOR_COUNT] = {
Rob Clark703d8852017-09-13 18:12:22 -0400122 { 0x00, 0x00, 0x00 }, /* black */
Heinrich Schuchardt9ffa4d12018-02-08 21:47:12 +0100123 { 0xc0, 0x00, 0x00 }, /* red */
124 { 0x00, 0xc0, 0x00 }, /* green */
125 { 0xc0, 0x60, 0x00 }, /* brown */
126 { 0x00, 0x00, 0xc0 }, /* blue */
127 { 0xc0, 0x00, 0xc0 }, /* magenta */
128 { 0x00, 0xc0, 0xc0 }, /* cyan */
129 { 0xc0, 0xc0, 0xc0 }, /* light gray */
130 { 0x80, 0x80, 0x80 }, /* gray */
131 { 0xff, 0x00, 0x00 }, /* bright red */
132 { 0x00, 0xff, 0x00 }, /* bright green */
Rob Clark703d8852017-09-13 18:12:22 -0400133 { 0xff, 0xff, 0x00 }, /* yellow */
Heinrich Schuchardt9ffa4d12018-02-08 21:47:12 +0100134 { 0x00, 0x00, 0xff }, /* bright blue */
135 { 0xff, 0x00, 0xff }, /* bright magenta */
136 { 0x00, 0xff, 0xff }, /* bright cyan */
Rob Clark703d8852017-09-13 18:12:22 -0400137 { 0xff, 0xff, 0xff }, /* white */
138};
139
Heinrich Schuchardt5c30fbb2018-02-08 21:47:11 +0100140u32 vid_console_color(struct video_priv *priv, unsigned int idx)
Rob Clark703d8852017-09-13 18:12:22 -0400141{
142 switch (priv->bpix) {
143 case VIDEO_BPP16:
Simon Glass775d3322019-12-20 18:10:36 -0700144 if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
145 return ((colors[idx].r >> 3) << 11) |
146 ((colors[idx].g >> 2) << 5) |
147 ((colors[idx].b >> 3) << 0);
148 }
Anatolij Gustschinab6ea932020-01-06 23:00:38 +0100149 break;
Rob Clark703d8852017-09-13 18:12:22 -0400150 case VIDEO_BPP32:
Simon Glass775d3322019-12-20 18:10:36 -0700151 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
152 return (colors[idx].r << 16) |
153 (colors[idx].g << 8) |
154 (colors[idx].b << 0);
155 }
Anatolij Gustschinab6ea932020-01-06 23:00:38 +0100156 break;
Rob Clark703d8852017-09-13 18:12:22 -0400157 default:
Anatolij Gustschinab6ea932020-01-06 23:00:38 +0100158 break;
Rob Clark703d8852017-09-13 18:12:22 -0400159 }
Anatolij Gustschinab6ea932020-01-06 23:00:38 +0100160
161 /*
162 * For unknown bit arrangements just support
163 * black and white.
164 */
165 if (idx)
166 return 0xffffff; /* white */
167
168 return 0x000000; /* black */
Rob Clark703d8852017-09-13 18:12:22 -0400169}
170
Rob Clarka085aa12017-09-13 18:12:21 -0400171static char *parsenum(char *s, int *num)
172{
173 char *end;
174 *num = simple_strtol(s, &end, 10);
175 return end;
176}
177
Heinrich Schuchardt662f3812018-09-19 21:31:48 +0200178/**
179 * set_cursor_position() - set cursor position
180 *
181 * @priv: private data of the video console
182 * @row: new row
183 * @col: new column
184 */
185static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
186{
187 /*
188 * Ensure we stay in the bounds of the screen.
189 */
190 if (row >= priv->rows)
191 row = priv->rows - 1;
192 if (col >= priv->cols)
193 col = priv->cols - 1;
194
195 priv->ycur = row * priv->y_charsize;
196 priv->xcur_frac = priv->xstart_frac +
197 VID_TO_POS(col * priv->x_charsize);
198}
199
200/**
201 * get_cursor_position() - get cursor position
202 *
203 * @priv: private data of the video console
204 * @row: row
205 * @col: column
206 */
207static void get_cursor_position(struct vidconsole_priv *priv,
208 int *row, int *col)
209{
210 *row = priv->ycur / priv->y_charsize;
211 *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
212 priv->x_charsize;
213}
214
Rob Clarka085aa12017-09-13 18:12:21 -0400215/*
216 * Process a character while accumulating an escape string. Chars are
217 * accumulated into escape_buf until the end of escape sequence is
218 * found, at which point the sequence is parsed and processed.
219 */
220static void vidconsole_escape_char(struct udevice *dev, char ch)
221{
222 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
223
224 if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
225 goto error;
226
227 /* Sanity checking for bogus ESC sequences: */
228 if (priv->escape_len >= sizeof(priv->escape_buf))
229 goto error;
Heinrich Schuchardt662f3812018-09-19 21:31:48 +0200230 if (priv->escape_len == 0) {
231 switch (ch) {
232 case '7':
233 /* Save cursor position */
234 get_cursor_position(priv, &priv->row_saved,
235 &priv->col_saved);
236 priv->escape = 0;
237
238 return;
239 case '8': {
240 /* Restore cursor position */
241 int row = priv->row_saved;
242 int col = priv->col_saved;
243
244 set_cursor_position(priv, row, col);
245 priv->escape = 0;
246 return;
247 }
248 case '[':
249 break;
250 default:
251 goto error;
252 }
253 }
Rob Clarka085aa12017-09-13 18:12:21 -0400254
255 priv->escape_buf[priv->escape_len++] = ch;
256
257 /*
258 * Escape sequences are terminated by a letter, so keep
259 * accumulating until we get one:
260 */
261 if (!isalpha(ch))
262 return;
263
264 /*
265 * clear escape mode first, otherwise things will get highly
266 * surprising if you hit any debug prints that come back to
267 * this console.
268 */
269 priv->escape = 0;
270
271 switch (ch) {
Andre Przywara29c158b2019-03-23 01:29:57 +0000272 case 'A':
273 case 'B':
274 case 'C':
275 case 'D':
276 case 'E':
277 case 'F': {
278 int row, col, num;
279 char *s = priv->escape_buf;
280
281 /*
282 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
283 * Cursor left/right: [%dD, [%dC
284 */
285 s++; /* [ */
286 s = parsenum(s, &num);
287 if (num == 0) /* No digit in sequence ... */
288 num = 1; /* ... means "move by 1". */
289
290 get_cursor_position(priv, &row, &col);
291 if (ch == 'A' || ch == 'F')
292 row -= num;
293 if (ch == 'C')
294 col += num;
295 if (ch == 'D')
296 col -= num;
297 if (ch == 'B' || ch == 'E')
298 row += num;
299 if (ch == 'E' || ch == 'F')
300 col = 0;
301 if (col < 0)
302 col = 0;
303 if (row < 0)
304 row = 0;
305 /* Right and bottom overflows are handled in the callee. */
306 set_cursor_position(priv, row, col);
307 break;
308 }
Rob Clarka085aa12017-09-13 18:12:21 -0400309 case 'H':
310 case 'f': {
311 int row, col;
312 char *s = priv->escape_buf;
313
314 /*
315 * Set cursor position: [%d;%df or [%d;%dH
316 */
317 s++; /* [ */
318 s = parsenum(s, &row);
319 s++; /* ; */
320 s = parsenum(s, &col);
321
Heinrich Schuchardt118f0202018-11-10 19:55:48 +0100322 /*
323 * Video origin is [0, 0], terminal origin is [1, 1].
324 */
325 if (row)
326 --row;
327 if (col)
328 --col;
329
Heinrich Schuchardt662f3812018-09-19 21:31:48 +0200330 set_cursor_position(priv, row, col);
Rob Clarka085aa12017-09-13 18:12:21 -0400331
332 break;
333 }
334 case 'J': {
335 int mode;
336
337 /*
338 * Clear part/all screen:
339 * [J or [0J - clear screen from cursor down
340 * [1J - clear screen from cursor up
341 * [2J - clear entire screen
342 *
343 * TODO we really only handle entire-screen case, others
344 * probably require some additions to video-uclass (and
345 * are not really needed yet by efi_console)
346 */
347 parsenum(priv->escape_buf + 1, &mode);
348
349 if (mode == 2) {
350 video_clear(dev->parent);
Simon Glass55d39912018-10-01 11:55:14 -0600351 video_sync(dev->parent, false);
Rob Clarka085aa12017-09-13 18:12:21 -0400352 priv->ycur = 0;
353 priv->xcur_frac = priv->xstart_frac;
354 } else {
355 debug("unsupported clear mode: %d\n", mode);
356 }
357 break;
358 }
Andre Przywara44222942019-03-23 01:29:58 +0000359 case 'K': {
360 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
361 int mode;
362
363 /*
364 * Clear (parts of) current line
365 * [0K - clear line to end
366 * [2K - clear entire line
367 */
368 parsenum(priv->escape_buf + 1, &mode);
369
370 if (mode == 2) {
371 int row, col;
372
373 get_cursor_position(priv, &row, &col);
374 vidconsole_set_row(dev, row, vid_priv->colour_bg);
375 }
376 break;
377 }
Rob Clark703d8852017-09-13 18:12:22 -0400378 case 'm': {
379 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
380 char *s = priv->escape_buf;
381 char *end = &priv->escape_buf[priv->escape_len];
382
383 /*
384 * Set graphics mode: [%d;...;%dm
385 *
386 * Currently only supports the color attributes:
387 *
388 * Foreground Colors:
389 *
390 * 30 Black
391 * 31 Red
392 * 32 Green
393 * 33 Yellow
394 * 34 Blue
395 * 35 Magenta
396 * 36 Cyan
397 * 37 White
398 *
399 * Background Colors:
400 *
401 * 40 Black
402 * 41 Red
403 * 42 Green
404 * 43 Yellow
405 * 44 Blue
406 * 45 Magenta
407 * 46 Cyan
408 * 47 White
409 */
410
411 s++; /* [ */
412 while (s < end) {
413 int val;
414
415 s = parsenum(s, &val);
416 s++;
417
418 switch (val) {
Heinrich Schuchardt9ffa4d12018-02-08 21:47:12 +0100419 case 0:
420 /* all attributes off */
Simon Glassb9f210a2018-11-06 15:21:36 -0700421 video_set_default_colors(dev->parent, false);
Heinrich Schuchardt9ffa4d12018-02-08 21:47:12 +0100422 break;
423 case 1:
424 /* bold */
425 vid_priv->fg_col_idx |= 8;
426 vid_priv->colour_fg = vid_console_color(
427 vid_priv, vid_priv->fg_col_idx);
428 break;
Andre Przywaraeabb0722019-03-23 01:29:56 +0000429 case 7:
430 /* reverse video */
431 vid_priv->colour_fg = vid_console_color(
432 vid_priv, vid_priv->bg_col_idx);
433 vid_priv->colour_bg = vid_console_color(
434 vid_priv, vid_priv->fg_col_idx);
435 break;
Rob Clark703d8852017-09-13 18:12:22 -0400436 case 30 ... 37:
Heinrich Schuchardt5c30fbb2018-02-08 21:47:11 +0100437 /* foreground color */
Heinrich Schuchardt9ffa4d12018-02-08 21:47:12 +0100438 vid_priv->fg_col_idx &= ~7;
439 vid_priv->fg_col_idx |= val - 30;
Heinrich Schuchardt5c30fbb2018-02-08 21:47:11 +0100440 vid_priv->colour_fg = vid_console_color(
Heinrich Schuchardt9ffa4d12018-02-08 21:47:12 +0100441 vid_priv, vid_priv->fg_col_idx);
Rob Clark703d8852017-09-13 18:12:22 -0400442 break;
443 case 40 ... 47:
Andre Przywaraeabb0722019-03-23 01:29:56 +0000444 /* background color, also mask the bold bit */
445 vid_priv->bg_col_idx &= ~0xf;
446 vid_priv->bg_col_idx |= val - 40;
Heinrich Schuchardt5c30fbb2018-02-08 21:47:11 +0100447 vid_priv->colour_bg = vid_console_color(
Andre Przywaraeabb0722019-03-23 01:29:56 +0000448 vid_priv, vid_priv->bg_col_idx);
Rob Clark703d8852017-09-13 18:12:22 -0400449 break;
450 default:
Heinrich Schuchardt5c30fbb2018-02-08 21:47:11 +0100451 /* ignore unsupported SGR parameter */
Rob Clark703d8852017-09-13 18:12:22 -0400452 break;
453 }
454 }
455
456 break;
457 }
Rob Clarka085aa12017-09-13 18:12:21 -0400458 default:
459 debug("unrecognized escape sequence: %*s\n",
460 priv->escape_len, priv->escape_buf);
461 }
462
463 return;
464
465error:
466 /* something went wrong, just revert to normal mode: */
467 priv->escape = 0;
468}
469
Andre Przywara7035ec32019-03-23 01:29:59 +0000470/* Put that actual character on the screen (using the CP437 code page). */
471static int vidconsole_output_glyph(struct udevice *dev, char ch)
472{
473 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
474 int ret;
475
476 /*
477 * Failure of this function normally indicates an unsupported
478 * colour depth. Check this and return an error to help with
479 * diagnosis.
480 */
481 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
482 if (ret == -EAGAIN) {
483 vidconsole_newline(dev);
484 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
485 }
486 if (ret < 0)
487 return ret;
488 priv->xcur_frac += ret;
489 priv->last_ch = ch;
490 if (priv->xcur_frac >= priv->xsize_frac)
491 vidconsole_newline(dev);
492
493 return 0;
494}
495
Simon Glass83510762016-01-18 19:52:17 -0700496int vidconsole_put_char(struct udevice *dev, char ch)
497{
498 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
499 int ret;
500
Rob Clarka085aa12017-09-13 18:12:21 -0400501 if (priv->escape) {
502 vidconsole_escape_char(dev, ch);
503 return 0;
504 }
505
Simon Glass83510762016-01-18 19:52:17 -0700506 switch (ch) {
Rob Clarka085aa12017-09-13 18:12:21 -0400507 case '\x1b':
508 priv->escape_len = 0;
509 priv->escape = 1;
510 break;
Simon Glass5508f102016-01-14 18:10:38 -0700511 case '\a':
512 /* beep */
513 break;
Simon Glass83510762016-01-18 19:52:17 -0700514 case '\r':
Simon Glassc5b77d02016-01-14 18:10:39 -0700515 priv->xcur_frac = priv->xstart_frac;
Simon Glass83510762016-01-18 19:52:17 -0700516 break;
517 case '\n':
518 vidconsole_newline(dev);
Simon Glass58c733a2016-01-14 18:10:40 -0700519 vidconsole_entry_start(dev);
Simon Glass83510762016-01-18 19:52:17 -0700520 break;
521 case '\t': /* Tab (8 chars alignment) */
Simon Glassf2661782016-01-14 18:10:37 -0700522 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
523 + 1) * priv->tab_width_frac;
Simon Glass83510762016-01-18 19:52:17 -0700524
Simon Glassf2661782016-01-14 18:10:37 -0700525 if (priv->xcur_frac >= priv->xsize_frac)
Simon Glass83510762016-01-18 19:52:17 -0700526 vidconsole_newline(dev);
527 break;
528 case '\b':
529 vidconsole_back(dev);
Simon Glass58c733a2016-01-14 18:10:40 -0700530 priv->last_ch = 0;
Simon Glass83510762016-01-18 19:52:17 -0700531 break;
532 default:
Andre Przywara7035ec32019-03-23 01:29:59 +0000533 ret = vidconsole_output_glyph(dev, ch);
Simon Glassf2661782016-01-14 18:10:37 -0700534 if (ret < 0)
Simon Glass83510762016-01-18 19:52:17 -0700535 return ret;
Simon Glass83510762016-01-18 19:52:17 -0700536 break;
537 }
538
539 return 0;
540}
541
Marek Vasute63168a2019-05-17 20:22:31 +0200542int vidconsole_put_string(struct udevice *dev, const char *str)
543{
544 const char *s;
545 int ret;
546
547 for (s = str; *s; s++) {
548 ret = vidconsole_put_char(dev, *s);
549 if (ret)
550 return ret;
551 }
552
553 return 0;
554}
555
Simon Glass83510762016-01-18 19:52:17 -0700556static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
557{
558 struct udevice *dev = sdev->priv;
559
560 vidconsole_put_char(dev, ch);
Simon Glass55d39912018-10-01 11:55:14 -0600561 video_sync(dev->parent, false);
Simon Glass83510762016-01-18 19:52:17 -0700562}
563
564static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
565{
566 struct udevice *dev = sdev->priv;
567
Marek Vasute63168a2019-05-17 20:22:31 +0200568 vidconsole_put_string(dev, s);
Simon Glass55d39912018-10-01 11:55:14 -0600569 video_sync(dev->parent, false);
Simon Glass83510762016-01-18 19:52:17 -0700570}
571
572/* Set up the number of rows and colours (rotated drivers override this) */
573static int vidconsole_pre_probe(struct udevice *dev)
574{
575 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
576 struct udevice *vid = dev->parent;
577 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
578
Simon Glassf2661782016-01-14 18:10:37 -0700579 priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
Simon Glass83510762016-01-18 19:52:17 -0700580
581 return 0;
582}
583
584/* Register the device with stdio */
585static int vidconsole_post_probe(struct udevice *dev)
586{
587 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
588 struct stdio_dev *sdev = &priv->sdev;
Simon Glass83510762016-01-18 19:52:17 -0700589
Simon Glassf2661782016-01-14 18:10:37 -0700590 if (!priv->tab_width_frac)
591 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
592
Simon Glassf1a12472016-01-21 19:44:51 -0700593 if (dev->seq) {
594 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
595 dev->seq);
596 } else {
597 strcpy(sdev->name, "vidconsole");
598 }
Simon Glassf2661782016-01-14 18:10:37 -0700599
Simon Glass83510762016-01-18 19:52:17 -0700600 sdev->flags = DEV_FLAGS_OUTPUT;
601 sdev->putc = vidconsole_putc;
602 sdev->puts = vidconsole_puts;
603 sdev->priv = dev;
Simon Glass83510762016-01-18 19:52:17 -0700604
Masahiro Yamada720873b2016-09-06 22:17:33 +0900605 return stdio_register(sdev);
Simon Glass83510762016-01-18 19:52:17 -0700606}
607
608UCLASS_DRIVER(vidconsole) = {
609 .id = UCLASS_VIDEO_CONSOLE,
610 .name = "vidconsole0",
611 .pre_probe = vidconsole_pre_probe,
612 .post_probe = vidconsole_post_probe,
613 .per_device_auto_alloc_size = sizeof(struct vidconsole_priv),
614};
615
616void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
617{
618 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glassf2661782016-01-14 18:10:37 -0700619 struct udevice *vid_dev = dev->parent;
620 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
Simon Glass83510762016-01-18 19:52:17 -0700621
Simon Glass9949ee82018-10-01 12:22:47 -0600622 col *= priv->x_charsize;
623 row *= priv->y_charsize;
Simon Glassf2661782016-01-14 18:10:37 -0700624 priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
625 priv->ycur = min_t(short, row, vid_priv->ysize - 1);
Simon Glass83510762016-01-18 19:52:17 -0700626}
627
Simon Glass09140112020-05-10 11:40:03 -0600628static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass83510762016-01-18 19:52:17 -0700629 char *const argv[])
630{
631 unsigned int col, row;
632 struct udevice *dev;
633
634 if (argc != 3)
635 return CMD_RET_USAGE;
636
Simon Glass3f603cb2016-02-11 13:23:26 -0700637 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass83510762016-01-18 19:52:17 -0700638 return CMD_RET_FAILURE;
639 col = simple_strtoul(argv[1], NULL, 10);
640 row = simple_strtoul(argv[2], NULL, 10);
641 vidconsole_position_cursor(dev, col, row);
642
643 return 0;
644}
645
Simon Glass09140112020-05-10 11:40:03 -0600646static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
Simon Glass83510762016-01-18 19:52:17 -0700647 char *const argv[])
648{
649 struct udevice *dev;
650 const char *s;
651
652 if (argc != 2)
653 return CMD_RET_USAGE;
654
Simon Glass3f603cb2016-02-11 13:23:26 -0700655 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass83510762016-01-18 19:52:17 -0700656 return CMD_RET_FAILURE;
657 for (s = argv[1]; *s; s++)
658 vidconsole_put_char(dev, *s);
659
Simon Glass55d39912018-10-01 11:55:14 -0600660 video_sync(dev->parent, false);
Rob Clark889808d2017-09-13 18:12:20 -0400661
Simon Glass83510762016-01-18 19:52:17 -0700662 return 0;
663}
664
665U_BOOT_CMD(
666 setcurs, 3, 1, do_video_setcursor,
667 "set cursor position within screen",
668 " <col> <row> in character"
669);
670
671U_BOOT_CMD(
672 lcdputs, 2, 1, do_video_puts,
673 "print string on video framebuffer",
674 " <string>"
675);