vidconsole-uclass.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  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
  8. */
  9. #define LOG_CATEGORY UCLASS_VIDEO_CONSOLE
  10. #include <common.h>
  11. #include <command.h>
  12. #include <console.h>
  13. #include <log.h>
  14. #include <dm.h>
  15. #include <video.h>
  16. #include <video_console.h>
  17. #include <video_font.h> /* Bitmap font for code page 437 */
  18. #include <linux/ctype.h>
  19. /*
  20. * Structure to describe a console color
  21. */
  22. struct vid_rgb {
  23. u32 r;
  24. u32 g;
  25. u32 b;
  26. };
  27. /* By default we scroll by a single line */
  28. #ifndef CONFIG_CONSOLE_SCROLL_LINES
  29. #define CONFIG_CONSOLE_SCROLL_LINES 1
  30. #endif
  31. int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
  32. {
  33. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  34. if (!ops->putc_xy)
  35. return -ENOSYS;
  36. return ops->putc_xy(dev, x, y, ch);
  37. }
  38. int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
  39. uint count)
  40. {
  41. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  42. if (!ops->move_rows)
  43. return -ENOSYS;
  44. return ops->move_rows(dev, rowdst, rowsrc, count);
  45. }
  46. int vidconsole_set_row(struct udevice *dev, uint row, int clr)
  47. {
  48. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  49. if (!ops->set_row)
  50. return -ENOSYS;
  51. return ops->set_row(dev, row, clr);
  52. }
  53. static int vidconsole_entry_start(struct udevice *dev)
  54. {
  55. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  56. if (!ops->entry_start)
  57. return -ENOSYS;
  58. return ops->entry_start(dev);
  59. }
  60. /* Move backwards one space */
  61. static int vidconsole_back(struct udevice *dev)
  62. {
  63. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  64. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  65. int ret;
  66. if (ops->backspace) {
  67. ret = ops->backspace(dev);
  68. if (ret != -ENOSYS)
  69. return ret;
  70. }
  71. priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
  72. if (priv->xcur_frac < priv->xstart_frac) {
  73. priv->xcur_frac = (priv->cols - 1) *
  74. VID_TO_POS(priv->x_charsize);
  75. priv->ycur -= priv->y_charsize;
  76. if (priv->ycur < 0)
  77. priv->ycur = 0;
  78. }
  79. return video_sync(dev->parent, false);
  80. }
  81. /* Move to a newline, scrolling the display if necessary */
  82. static void vidconsole_newline(struct udevice *dev)
  83. {
  84. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  85. struct udevice *vid_dev = dev->parent;
  86. struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
  87. const int rows = CONFIG_CONSOLE_SCROLL_LINES;
  88. int i, ret;
  89. priv->xcur_frac = priv->xstart_frac;
  90. priv->ycur += priv->y_charsize;
  91. /* Check if we need to scroll the terminal */
  92. if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
  93. vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
  94. for (i = 0; i < rows; i++)
  95. vidconsole_set_row(dev, priv->rows - i - 1,
  96. vid_priv->colour_bg);
  97. priv->ycur -= rows * priv->y_charsize;
  98. }
  99. priv->last_ch = 0;
  100. ret = video_sync(dev->parent, false);
  101. if (ret) {
  102. #ifdef DEBUG
  103. console_puts_select_stderr(true, "[vc err: video_sync]");
  104. #endif
  105. }
  106. }
  107. static const struct vid_rgb colors[VID_COLOR_COUNT] = {
  108. { 0x00, 0x00, 0x00 }, /* black */
  109. { 0xc0, 0x00, 0x00 }, /* red */
  110. { 0x00, 0xc0, 0x00 }, /* green */
  111. { 0xc0, 0x60, 0x00 }, /* brown */
  112. { 0x00, 0x00, 0xc0 }, /* blue */
  113. { 0xc0, 0x00, 0xc0 }, /* magenta */
  114. { 0x00, 0xc0, 0xc0 }, /* cyan */
  115. { 0xc0, 0xc0, 0xc0 }, /* light gray */
  116. { 0x80, 0x80, 0x80 }, /* gray */
  117. { 0xff, 0x00, 0x00 }, /* bright red */
  118. { 0x00, 0xff, 0x00 }, /* bright green */
  119. { 0xff, 0xff, 0x00 }, /* yellow */
  120. { 0x00, 0x00, 0xff }, /* bright blue */
  121. { 0xff, 0x00, 0xff }, /* bright magenta */
  122. { 0x00, 0xff, 0xff }, /* bright cyan */
  123. { 0xff, 0xff, 0xff }, /* white */
  124. };
  125. u32 vid_console_color(struct video_priv *priv, unsigned int idx)
  126. {
  127. switch (priv->bpix) {
  128. case VIDEO_BPP16:
  129. if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
  130. return ((colors[idx].r >> 3) << 11) |
  131. ((colors[idx].g >> 2) << 5) |
  132. ((colors[idx].b >> 3) << 0);
  133. }
  134. break;
  135. case VIDEO_BPP32:
  136. if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
  137. return (colors[idx].r << 16) |
  138. (colors[idx].g << 8) |
  139. (colors[idx].b << 0);
  140. }
  141. break;
  142. default:
  143. break;
  144. }
  145. /*
  146. * For unknown bit arrangements just support
  147. * black and white.
  148. */
  149. if (idx)
  150. return 0xffffff; /* white */
  151. return 0x000000; /* black */
  152. }
  153. static char *parsenum(char *s, int *num)
  154. {
  155. char *end;
  156. *num = simple_strtol(s, &end, 10);
  157. return end;
  158. }
  159. /**
  160. * set_cursor_position() - set cursor position
  161. *
  162. * @priv: private data of the video console
  163. * @row: new row
  164. * @col: new column
  165. */
  166. static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
  167. {
  168. /*
  169. * Ensure we stay in the bounds of the screen.
  170. */
  171. if (row >= priv->rows)
  172. row = priv->rows - 1;
  173. if (col >= priv->cols)
  174. col = priv->cols - 1;
  175. priv->ycur = row * priv->y_charsize;
  176. priv->xcur_frac = priv->xstart_frac +
  177. VID_TO_POS(col * priv->x_charsize);
  178. }
  179. /**
  180. * get_cursor_position() - get cursor position
  181. *
  182. * @priv: private data of the video console
  183. * @row: row
  184. * @col: column
  185. */
  186. static void get_cursor_position(struct vidconsole_priv *priv,
  187. int *row, int *col)
  188. {
  189. *row = priv->ycur / priv->y_charsize;
  190. *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
  191. priv->x_charsize;
  192. }
  193. /*
  194. * Process a character while accumulating an escape string. Chars are
  195. * accumulated into escape_buf until the end of escape sequence is
  196. * found, at which point the sequence is parsed and processed.
  197. */
  198. static void vidconsole_escape_char(struct udevice *dev, char ch)
  199. {
  200. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  201. if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
  202. goto error;
  203. /* Sanity checking for bogus ESC sequences: */
  204. if (priv->escape_len >= sizeof(priv->escape_buf))
  205. goto error;
  206. if (priv->escape_len == 0) {
  207. switch (ch) {
  208. case '7':
  209. /* Save cursor position */
  210. get_cursor_position(priv, &priv->row_saved,
  211. &priv->col_saved);
  212. priv->escape = 0;
  213. return;
  214. case '8': {
  215. /* Restore cursor position */
  216. int row = priv->row_saved;
  217. int col = priv->col_saved;
  218. set_cursor_position(priv, row, col);
  219. priv->escape = 0;
  220. return;
  221. }
  222. case '[':
  223. break;
  224. default:
  225. goto error;
  226. }
  227. }
  228. priv->escape_buf[priv->escape_len++] = ch;
  229. /*
  230. * Escape sequences are terminated by a letter, so keep
  231. * accumulating until we get one:
  232. */
  233. if (!isalpha(ch))
  234. return;
  235. /*
  236. * clear escape mode first, otherwise things will get highly
  237. * surprising if you hit any debug prints that come back to
  238. * this console.
  239. */
  240. priv->escape = 0;
  241. switch (ch) {
  242. case 'A':
  243. case 'B':
  244. case 'C':
  245. case 'D':
  246. case 'E':
  247. case 'F': {
  248. int row, col, num;
  249. char *s = priv->escape_buf;
  250. /*
  251. * Cursor up/down: [%dA, [%dB, [%dE, [%dF
  252. * Cursor left/right: [%dD, [%dC
  253. */
  254. s++; /* [ */
  255. s = parsenum(s, &num);
  256. if (num == 0) /* No digit in sequence ... */
  257. num = 1; /* ... means "move by 1". */
  258. get_cursor_position(priv, &row, &col);
  259. if (ch == 'A' || ch == 'F')
  260. row -= num;
  261. if (ch == 'C')
  262. col += num;
  263. if (ch == 'D')
  264. col -= num;
  265. if (ch == 'B' || ch == 'E')
  266. row += num;
  267. if (ch == 'E' || ch == 'F')
  268. col = 0;
  269. if (col < 0)
  270. col = 0;
  271. if (row < 0)
  272. row = 0;
  273. /* Right and bottom overflows are handled in the callee. */
  274. set_cursor_position(priv, row, col);
  275. break;
  276. }
  277. case 'H':
  278. case 'f': {
  279. int row, col;
  280. char *s = priv->escape_buf;
  281. /*
  282. * Set cursor position: [%d;%df or [%d;%dH
  283. */
  284. s++; /* [ */
  285. s = parsenum(s, &row);
  286. s++; /* ; */
  287. s = parsenum(s, &col);
  288. /*
  289. * Video origin is [0, 0], terminal origin is [1, 1].
  290. */
  291. if (row)
  292. --row;
  293. if (col)
  294. --col;
  295. set_cursor_position(priv, row, col);
  296. break;
  297. }
  298. case 'J': {
  299. int mode;
  300. /*
  301. * Clear part/all screen:
  302. * [J or [0J - clear screen from cursor down
  303. * [1J - clear screen from cursor up
  304. * [2J - clear entire screen
  305. *
  306. * TODO we really only handle entire-screen case, others
  307. * probably require some additions to video-uclass (and
  308. * are not really needed yet by efi_console)
  309. */
  310. parsenum(priv->escape_buf + 1, &mode);
  311. if (mode == 2) {
  312. int ret;
  313. video_clear(dev->parent);
  314. ret = video_sync(dev->parent, false);
  315. if (ret) {
  316. #ifdef DEBUG
  317. console_puts_select_stderr(true, "[vc err: video_sync]");
  318. #endif
  319. }
  320. priv->ycur = 0;
  321. priv->xcur_frac = priv->xstart_frac;
  322. } else {
  323. debug("unsupported clear mode: %d\n", mode);
  324. }
  325. break;
  326. }
  327. case 'K': {
  328. struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
  329. int mode;
  330. /*
  331. * Clear (parts of) current line
  332. * [0K - clear line to end
  333. * [2K - clear entire line
  334. */
  335. parsenum(priv->escape_buf + 1, &mode);
  336. if (mode == 2) {
  337. int row, col;
  338. get_cursor_position(priv, &row, &col);
  339. vidconsole_set_row(dev, row, vid_priv->colour_bg);
  340. }
  341. break;
  342. }
  343. case 'm': {
  344. struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
  345. char *s = priv->escape_buf;
  346. char *end = &priv->escape_buf[priv->escape_len];
  347. /*
  348. * Set graphics mode: [%d;...;%dm
  349. *
  350. * Currently only supports the color attributes:
  351. *
  352. * Foreground Colors:
  353. *
  354. * 30 Black
  355. * 31 Red
  356. * 32 Green
  357. * 33 Yellow
  358. * 34 Blue
  359. * 35 Magenta
  360. * 36 Cyan
  361. * 37 White
  362. *
  363. * Background Colors:
  364. *
  365. * 40 Black
  366. * 41 Red
  367. * 42 Green
  368. * 43 Yellow
  369. * 44 Blue
  370. * 45 Magenta
  371. * 46 Cyan
  372. * 47 White
  373. */
  374. s++; /* [ */
  375. while (s < end) {
  376. int val;
  377. s = parsenum(s, &val);
  378. s++;
  379. switch (val) {
  380. case 0:
  381. /* all attributes off */
  382. video_set_default_colors(dev->parent, false);
  383. break;
  384. case 1:
  385. /* bold */
  386. vid_priv->fg_col_idx |= 8;
  387. vid_priv->colour_fg = vid_console_color(
  388. vid_priv, vid_priv->fg_col_idx);
  389. break;
  390. case 7:
  391. /* reverse video */
  392. vid_priv->colour_fg = vid_console_color(
  393. vid_priv, vid_priv->bg_col_idx);
  394. vid_priv->colour_bg = vid_console_color(
  395. vid_priv, vid_priv->fg_col_idx);
  396. break;
  397. case 30 ... 37:
  398. /* foreground color */
  399. vid_priv->fg_col_idx &= ~7;
  400. vid_priv->fg_col_idx |= val - 30;
  401. vid_priv->colour_fg = vid_console_color(
  402. vid_priv, vid_priv->fg_col_idx);
  403. break;
  404. case 40 ... 47:
  405. /* background color, also mask the bold bit */
  406. vid_priv->bg_col_idx &= ~0xf;
  407. vid_priv->bg_col_idx |= val - 40;
  408. vid_priv->colour_bg = vid_console_color(
  409. vid_priv, vid_priv->bg_col_idx);
  410. break;
  411. default:
  412. /* ignore unsupported SGR parameter */
  413. break;
  414. }
  415. }
  416. break;
  417. }
  418. default:
  419. debug("unrecognized escape sequence: %*s\n",
  420. priv->escape_len, priv->escape_buf);
  421. }
  422. return;
  423. error:
  424. /* something went wrong, just revert to normal mode: */
  425. priv->escape = 0;
  426. }
  427. /* Put that actual character on the screen (using the CP437 code page). */
  428. static int vidconsole_output_glyph(struct udevice *dev, char ch)
  429. {
  430. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  431. int ret;
  432. /*
  433. * Failure of this function normally indicates an unsupported
  434. * colour depth. Check this and return an error to help with
  435. * diagnosis.
  436. */
  437. ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
  438. if (ret == -EAGAIN) {
  439. vidconsole_newline(dev);
  440. ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
  441. }
  442. if (ret < 0)
  443. return ret;
  444. priv->xcur_frac += ret;
  445. priv->last_ch = ch;
  446. if (priv->xcur_frac >= priv->xsize_frac)
  447. vidconsole_newline(dev);
  448. return 0;
  449. }
  450. int vidconsole_put_char(struct udevice *dev, char ch)
  451. {
  452. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  453. int ret;
  454. if (priv->escape) {
  455. vidconsole_escape_char(dev, ch);
  456. return 0;
  457. }
  458. switch (ch) {
  459. case '\x1b':
  460. priv->escape_len = 0;
  461. priv->escape = 1;
  462. break;
  463. case '\a':
  464. /* beep */
  465. break;
  466. case '\r':
  467. priv->xcur_frac = priv->xstart_frac;
  468. break;
  469. case '\n':
  470. vidconsole_newline(dev);
  471. vidconsole_entry_start(dev);
  472. break;
  473. case '\t': /* Tab (8 chars alignment) */
  474. priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
  475. + 1) * priv->tab_width_frac;
  476. if (priv->xcur_frac >= priv->xsize_frac)
  477. vidconsole_newline(dev);
  478. break;
  479. case '\b':
  480. vidconsole_back(dev);
  481. priv->last_ch = 0;
  482. break;
  483. default:
  484. ret = vidconsole_output_glyph(dev, ch);
  485. if (ret < 0)
  486. return ret;
  487. break;
  488. }
  489. return 0;
  490. }
  491. int vidconsole_put_string(struct udevice *dev, const char *str)
  492. {
  493. const char *s;
  494. int ret;
  495. for (s = str; *s; s++) {
  496. ret = vidconsole_put_char(dev, *s);
  497. if (ret)
  498. return ret;
  499. }
  500. return 0;
  501. }
  502. static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
  503. {
  504. struct udevice *dev = sdev->priv;
  505. int ret;
  506. ret = vidconsole_put_char(dev, ch);
  507. if (ret) {
  508. #ifdef DEBUG
  509. console_puts_select_stderr(true, "[vc err: putc]");
  510. #endif
  511. }
  512. ret = video_sync(dev->parent, false);
  513. if (ret) {
  514. #ifdef DEBUG
  515. console_puts_select_stderr(true, "[vc err: video_sync]");
  516. #endif
  517. }
  518. }
  519. static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
  520. {
  521. struct udevice *dev = sdev->priv;
  522. int ret;
  523. ret = vidconsole_put_string(dev, s);
  524. if (ret) {
  525. #ifdef DEBUG
  526. char str[30];
  527. snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
  528. console_puts_select_stderr(true, str);
  529. #endif
  530. }
  531. ret = video_sync(dev->parent, false);
  532. if (ret) {
  533. #ifdef DEBUG
  534. console_puts_select_stderr(true, "[vc err: video_sync]");
  535. #endif
  536. }
  537. }
  538. /* Set up the number of rows and colours (rotated drivers override this) */
  539. static int vidconsole_pre_probe(struct udevice *dev)
  540. {
  541. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  542. struct udevice *vid = dev->parent;
  543. struct video_priv *vid_priv = dev_get_uclass_priv(vid);
  544. priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
  545. return 0;
  546. }
  547. /* Register the device with stdio */
  548. static int vidconsole_post_probe(struct udevice *dev)
  549. {
  550. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  551. struct stdio_dev *sdev = &priv->sdev;
  552. if (!priv->tab_width_frac)
  553. priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
  554. if (dev_seq(dev)) {
  555. snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
  556. dev_seq(dev));
  557. } else {
  558. strcpy(sdev->name, "vidconsole");
  559. }
  560. sdev->flags = DEV_FLAGS_OUTPUT;
  561. sdev->putc = vidconsole_putc;
  562. sdev->puts = vidconsole_puts;
  563. sdev->priv = dev;
  564. return stdio_register(sdev);
  565. }
  566. UCLASS_DRIVER(vidconsole) = {
  567. .id = UCLASS_VIDEO_CONSOLE,
  568. .name = "vidconsole0",
  569. .pre_probe = vidconsole_pre_probe,
  570. .post_probe = vidconsole_post_probe,
  571. .per_device_auto = sizeof(struct vidconsole_priv),
  572. };
  573. #ifdef CONFIG_VIDEO_COPY
  574. int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
  575. {
  576. struct udevice *vid = dev_get_parent(dev);
  577. return video_sync_copy(vid, from, to);
  578. }
  579. int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
  580. int size)
  581. {
  582. memmove(dst, src, size);
  583. return vidconsole_sync_copy(dev, dst, dst + size);
  584. }
  585. #endif
  586. #if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
  587. void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
  588. {
  589. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  590. struct udevice *vid_dev = dev->parent;
  591. struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
  592. col *= priv->x_charsize;
  593. row *= priv->y_charsize;
  594. priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
  595. priv->xstart_frac = priv->xcur_frac;
  596. priv->ycur = min_t(short, row, vid_priv->ysize - 1);
  597. }
  598. static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
  599. char *const argv[])
  600. {
  601. unsigned int col, row;
  602. struct udevice *dev;
  603. if (argc != 3)
  604. return CMD_RET_USAGE;
  605. if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
  606. return CMD_RET_FAILURE;
  607. col = dectoul(argv[1], NULL);
  608. row = dectoul(argv[2], NULL);
  609. vidconsole_position_cursor(dev, col, row);
  610. return 0;
  611. }
  612. static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
  613. char *const argv[])
  614. {
  615. struct udevice *dev;
  616. const char *s;
  617. if (argc != 2)
  618. return CMD_RET_USAGE;
  619. if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
  620. return CMD_RET_FAILURE;
  621. for (s = argv[1]; *s; s++)
  622. vidconsole_put_char(dev, *s);
  623. return video_sync(dev->parent, false);
  624. }
  625. U_BOOT_CMD(
  626. setcurs, 3, 1, do_video_setcursor,
  627. "set cursor position within screen",
  628. " <col> <row> in character"
  629. );
  630. U_BOOT_CMD(
  631. lcdputs, 2, 1, do_video_puts,
  632. "print string on video framebuffer",
  633. " <string>"
  634. );
  635. #endif /* CONFIG_IS_ENABLED(CMD_VIDCONSOLE) */