vidconsole-uclass.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  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. #include <common.h>
  10. #include <linux/ctype.h>
  11. #include <dm.h>
  12. #include <video.h>
  13. #include <video_console.h>
  14. #include <video_font.h> /* Bitmap font for code page 437 */
  15. /*
  16. * Structure to describe a console color
  17. */
  18. struct vid_rgb {
  19. u32 r;
  20. u32 g;
  21. u32 b;
  22. };
  23. /* By default we scroll by a single line */
  24. #ifndef CONFIG_CONSOLE_SCROLL_LINES
  25. #define CONFIG_CONSOLE_SCROLL_LINES 1
  26. #endif
  27. int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
  28. {
  29. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  30. if (!ops->putc_xy)
  31. return -ENOSYS;
  32. return ops->putc_xy(dev, x, y, ch);
  33. }
  34. int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
  35. uint count)
  36. {
  37. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  38. if (!ops->move_rows)
  39. return -ENOSYS;
  40. return ops->move_rows(dev, rowdst, rowsrc, count);
  41. }
  42. int vidconsole_set_row(struct udevice *dev, uint row, int clr)
  43. {
  44. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  45. if (!ops->set_row)
  46. return -ENOSYS;
  47. return ops->set_row(dev, row, clr);
  48. }
  49. static int vidconsole_entry_start(struct udevice *dev)
  50. {
  51. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  52. if (!ops->entry_start)
  53. return -ENOSYS;
  54. return ops->entry_start(dev);
  55. }
  56. /* Move backwards one space */
  57. static int vidconsole_back(struct udevice *dev)
  58. {
  59. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  60. struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  61. int ret;
  62. if (ops->backspace) {
  63. ret = ops->backspace(dev);
  64. if (ret != -ENOSYS)
  65. return ret;
  66. }
  67. priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
  68. if (priv->xcur_frac < priv->xstart_frac) {
  69. priv->xcur_frac = (priv->cols - 1) *
  70. VID_TO_POS(priv->x_charsize);
  71. priv->ycur -= priv->y_charsize;
  72. if (priv->ycur < 0)
  73. priv->ycur = 0;
  74. }
  75. video_sync(dev->parent);
  76. return 0;
  77. }
  78. /* Move to a newline, scrolling the display if necessary */
  79. static void vidconsole_newline(struct udevice *dev)
  80. {
  81. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  82. struct udevice *vid_dev = dev->parent;
  83. struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
  84. const int rows = CONFIG_CONSOLE_SCROLL_LINES;
  85. int i;
  86. priv->xcur_frac = priv->xstart_frac;
  87. priv->ycur += priv->y_charsize;
  88. /* Check if we need to scroll the terminal */
  89. if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
  90. vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
  91. for (i = 0; i < rows; i++)
  92. vidconsole_set_row(dev, priv->rows - i - 1,
  93. vid_priv->colour_bg);
  94. priv->ycur -= rows * priv->y_charsize;
  95. }
  96. priv->last_ch = 0;
  97. video_sync(dev->parent);
  98. }
  99. static const struct vid_rgb colors[VID_COLOR_COUNT] = {
  100. { 0x00, 0x00, 0x00 }, /* black */
  101. { 0xc0, 0x00, 0x00 }, /* red */
  102. { 0x00, 0xc0, 0x00 }, /* green */
  103. { 0xc0, 0x60, 0x00 }, /* brown */
  104. { 0x00, 0x00, 0xc0 }, /* blue */
  105. { 0xc0, 0x00, 0xc0 }, /* magenta */
  106. { 0x00, 0xc0, 0xc0 }, /* cyan */
  107. { 0xc0, 0xc0, 0xc0 }, /* light gray */
  108. { 0x80, 0x80, 0x80 }, /* gray */
  109. { 0xff, 0x00, 0x00 }, /* bright red */
  110. { 0x00, 0xff, 0x00 }, /* bright green */
  111. { 0xff, 0xff, 0x00 }, /* yellow */
  112. { 0x00, 0x00, 0xff }, /* bright blue */
  113. { 0xff, 0x00, 0xff }, /* bright magenta */
  114. { 0x00, 0xff, 0xff }, /* bright cyan */
  115. { 0xff, 0xff, 0xff }, /* white */
  116. };
  117. u32 vid_console_color(struct video_priv *priv, unsigned int idx)
  118. {
  119. switch (priv->bpix) {
  120. case VIDEO_BPP16:
  121. return ((colors[idx].r >> 3) << 11) |
  122. ((colors[idx].g >> 2) << 5) |
  123. ((colors[idx].b >> 3) << 0);
  124. case VIDEO_BPP32:
  125. return (colors[idx].r << 16) |
  126. (colors[idx].g << 8) |
  127. (colors[idx].b << 0);
  128. default:
  129. /*
  130. * For unknown bit arrangements just support
  131. * black and white.
  132. */
  133. if (idx)
  134. return 0xffffff; /* white */
  135. else
  136. return 0x000000; /* black */
  137. }
  138. }
  139. static char *parsenum(char *s, int *num)
  140. {
  141. char *end;
  142. *num = simple_strtol(s, &end, 10);
  143. return end;
  144. }
  145. /*
  146. * Process a character while accumulating an escape string. Chars are
  147. * accumulated into escape_buf until the end of escape sequence is
  148. * found, at which point the sequence is parsed and processed.
  149. */
  150. static void vidconsole_escape_char(struct udevice *dev, char ch)
  151. {
  152. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  153. if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
  154. goto error;
  155. /* Sanity checking for bogus ESC sequences: */
  156. if (priv->escape_len >= sizeof(priv->escape_buf))
  157. goto error;
  158. if (priv->escape_len == 0 && ch != '[')
  159. goto error;
  160. priv->escape_buf[priv->escape_len++] = ch;
  161. /*
  162. * Escape sequences are terminated by a letter, so keep
  163. * accumulating until we get one:
  164. */
  165. if (!isalpha(ch))
  166. return;
  167. /*
  168. * clear escape mode first, otherwise things will get highly
  169. * surprising if you hit any debug prints that come back to
  170. * this console.
  171. */
  172. priv->escape = 0;
  173. switch (ch) {
  174. case 'H':
  175. case 'f': {
  176. int row, col;
  177. char *s = priv->escape_buf;
  178. /*
  179. * Set cursor position: [%d;%df or [%d;%dH
  180. */
  181. s++; /* [ */
  182. s = parsenum(s, &row);
  183. s++; /* ; */
  184. s = parsenum(s, &col);
  185. priv->ycur = row * priv->y_charsize;
  186. priv->xcur_frac = priv->xstart_frac +
  187. VID_TO_POS(col * priv->x_charsize);
  188. break;
  189. }
  190. case 'J': {
  191. int mode;
  192. /*
  193. * Clear part/all screen:
  194. * [J or [0J - clear screen from cursor down
  195. * [1J - clear screen from cursor up
  196. * [2J - clear entire screen
  197. *
  198. * TODO we really only handle entire-screen case, others
  199. * probably require some additions to video-uclass (and
  200. * are not really needed yet by efi_console)
  201. */
  202. parsenum(priv->escape_buf + 1, &mode);
  203. if (mode == 2) {
  204. video_clear(dev->parent);
  205. video_sync(dev->parent);
  206. priv->ycur = 0;
  207. priv->xcur_frac = priv->xstart_frac;
  208. } else {
  209. debug("unsupported clear mode: %d\n", mode);
  210. }
  211. break;
  212. }
  213. case 'm': {
  214. struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
  215. char *s = priv->escape_buf;
  216. char *end = &priv->escape_buf[priv->escape_len];
  217. /*
  218. * Set graphics mode: [%d;...;%dm
  219. *
  220. * Currently only supports the color attributes:
  221. *
  222. * Foreground Colors:
  223. *
  224. * 30 Black
  225. * 31 Red
  226. * 32 Green
  227. * 33 Yellow
  228. * 34 Blue
  229. * 35 Magenta
  230. * 36 Cyan
  231. * 37 White
  232. *
  233. * Background Colors:
  234. *
  235. * 40 Black
  236. * 41 Red
  237. * 42 Green
  238. * 43 Yellow
  239. * 44 Blue
  240. * 45 Magenta
  241. * 46 Cyan
  242. * 47 White
  243. */
  244. s++; /* [ */
  245. while (s < end) {
  246. int val;
  247. s = parsenum(s, &val);
  248. s++;
  249. switch (val) {
  250. case 0:
  251. /* all attributes off */
  252. video_set_default_colors(vid_priv);
  253. break;
  254. case 1:
  255. /* bold */
  256. vid_priv->fg_col_idx |= 8;
  257. vid_priv->colour_fg = vid_console_color(
  258. vid_priv, vid_priv->fg_col_idx);
  259. break;
  260. case 30 ... 37:
  261. /* foreground color */
  262. vid_priv->fg_col_idx &= ~7;
  263. vid_priv->fg_col_idx |= val - 30;
  264. vid_priv->colour_fg = vid_console_color(
  265. vid_priv, vid_priv->fg_col_idx);
  266. break;
  267. case 40 ... 47:
  268. /* background color */
  269. vid_priv->colour_bg = vid_console_color(
  270. vid_priv, val - 40);
  271. break;
  272. default:
  273. /* ignore unsupported SGR parameter */
  274. break;
  275. }
  276. }
  277. break;
  278. }
  279. default:
  280. debug("unrecognized escape sequence: %*s\n",
  281. priv->escape_len, priv->escape_buf);
  282. }
  283. return;
  284. error:
  285. /* something went wrong, just revert to normal mode: */
  286. priv->escape = 0;
  287. }
  288. int vidconsole_put_char(struct udevice *dev, char ch)
  289. {
  290. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  291. int ret;
  292. if (priv->escape) {
  293. vidconsole_escape_char(dev, ch);
  294. return 0;
  295. }
  296. switch (ch) {
  297. case '\x1b':
  298. priv->escape_len = 0;
  299. priv->escape = 1;
  300. break;
  301. case '\a':
  302. /* beep */
  303. break;
  304. case '\r':
  305. priv->xcur_frac = priv->xstart_frac;
  306. break;
  307. case '\n':
  308. vidconsole_newline(dev);
  309. vidconsole_entry_start(dev);
  310. break;
  311. case '\t': /* Tab (8 chars alignment) */
  312. priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
  313. + 1) * priv->tab_width_frac;
  314. if (priv->xcur_frac >= priv->xsize_frac)
  315. vidconsole_newline(dev);
  316. break;
  317. case '\b':
  318. vidconsole_back(dev);
  319. priv->last_ch = 0;
  320. break;
  321. default:
  322. /*
  323. * Failure of this function normally indicates an unsupported
  324. * colour depth. Check this and return an error to help with
  325. * diagnosis.
  326. */
  327. ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
  328. if (ret == -EAGAIN) {
  329. vidconsole_newline(dev);
  330. ret = vidconsole_putc_xy(dev, priv->xcur_frac,
  331. priv->ycur, ch);
  332. }
  333. if (ret < 0)
  334. return ret;
  335. priv->xcur_frac += ret;
  336. priv->last_ch = ch;
  337. if (priv->xcur_frac >= priv->xsize_frac)
  338. vidconsole_newline(dev);
  339. break;
  340. }
  341. return 0;
  342. }
  343. static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
  344. {
  345. struct udevice *dev = sdev->priv;
  346. vidconsole_put_char(dev, ch);
  347. video_sync(dev->parent);
  348. }
  349. static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
  350. {
  351. struct udevice *dev = sdev->priv;
  352. while (*s)
  353. vidconsole_put_char(dev, *s++);
  354. video_sync(dev->parent);
  355. }
  356. /* Set up the number of rows and colours (rotated drivers override this) */
  357. static int vidconsole_pre_probe(struct udevice *dev)
  358. {
  359. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  360. struct udevice *vid = dev->parent;
  361. struct video_priv *vid_priv = dev_get_uclass_priv(vid);
  362. priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
  363. return 0;
  364. }
  365. /* Register the device with stdio */
  366. static int vidconsole_post_probe(struct udevice *dev)
  367. {
  368. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  369. struct stdio_dev *sdev = &priv->sdev;
  370. if (!priv->tab_width_frac)
  371. priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
  372. if (dev->seq) {
  373. snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
  374. dev->seq);
  375. } else {
  376. strcpy(sdev->name, "vidconsole");
  377. }
  378. sdev->flags = DEV_FLAGS_OUTPUT;
  379. sdev->putc = vidconsole_putc;
  380. sdev->puts = vidconsole_puts;
  381. sdev->priv = dev;
  382. return stdio_register(sdev);
  383. }
  384. UCLASS_DRIVER(vidconsole) = {
  385. .id = UCLASS_VIDEO_CONSOLE,
  386. .name = "vidconsole0",
  387. .pre_probe = vidconsole_pre_probe,
  388. .post_probe = vidconsole_post_probe,
  389. .per_device_auto_alloc_size = sizeof(struct vidconsole_priv),
  390. };
  391. void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
  392. {
  393. struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  394. struct udevice *vid_dev = dev->parent;
  395. struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
  396. priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
  397. priv->ycur = min_t(short, row, vid_priv->ysize - 1);
  398. }
  399. static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc,
  400. char *const argv[])
  401. {
  402. unsigned int col, row;
  403. struct udevice *dev;
  404. if (argc != 3)
  405. return CMD_RET_USAGE;
  406. if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
  407. return CMD_RET_FAILURE;
  408. col = simple_strtoul(argv[1], NULL, 10);
  409. row = simple_strtoul(argv[2], NULL, 10);
  410. vidconsole_position_cursor(dev, col, row);
  411. return 0;
  412. }
  413. static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc,
  414. char *const argv[])
  415. {
  416. struct udevice *dev;
  417. const char *s;
  418. if (argc != 2)
  419. return CMD_RET_USAGE;
  420. if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
  421. return CMD_RET_FAILURE;
  422. for (s = argv[1]; *s; s++)
  423. vidconsole_put_char(dev, *s);
  424. video_sync(dev->parent);
  425. return 0;
  426. }
  427. U_BOOT_CMD(
  428. setcurs, 3, 1, do_video_setcursor,
  429. "set cursor position within screen",
  430. " <col> <row> in character"
  431. );
  432. U_BOOT_CMD(
  433. lcdputs, 2, 1, do_video_puts,
  434. "print string on video framebuffer",
  435. " <string>"
  436. );