1
2
3
4
5
6
7
8
9
10#define LOG_CATEGORY UCLASS_VIDEO_CONSOLE
11
12#include <common.h>
13#include <command.h>
14#include <console.h>
15#include <log.h>
16#include <dm.h>
17#include <video.h>
18#include <video_console.h>
19#include <video_font.h>
20#include <linux/ctype.h>
21
22
23
24
25struct vid_rgb {
26 u32 r;
27 u32 g;
28 u32 b;
29};
30
31
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
64static 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
73
74static int vidconsole_back(struct udevice *dev)
75{
76 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
77 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 }
85
86 priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
87 if (priv->xcur_frac < priv->xstart_frac) {
88 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;
93 }
94 return video_sync(dev->parent, false);
95}
96
97
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;
104 int i, ret;
105
106 priv->xcur_frac = priv->xstart_frac;
107 priv->ycur += priv->y_charsize;
108
109
110 if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
111 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);
115 priv->ycur -= rows * priv->y_charsize;
116 }
117 priv->last_ch = 0;
118
119 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 }
125}
126
127static const struct vid_rgb colors[VID_COLOR_COUNT] = {
128 { 0x00, 0x00, 0x00 },
129 { 0xc0, 0x00, 0x00 },
130 { 0x00, 0xc0, 0x00 },
131 { 0xc0, 0x60, 0x00 },
132 { 0x00, 0x00, 0xc0 },
133 { 0xc0, 0x00, 0xc0 },
134 { 0x00, 0xc0, 0xc0 },
135 { 0xc0, 0xc0, 0xc0 },
136 { 0x80, 0x80, 0x80 },
137 { 0xff, 0x00, 0x00 },
138 { 0x00, 0xff, 0x00 },
139 { 0xff, 0xff, 0x00 },
140 { 0x00, 0x00, 0xff },
141 { 0xff, 0x00, 0xff },
142 { 0x00, 0xff, 0xff },
143 { 0xff, 0xff, 0xff },
144};
145
146u32 vid_console_color(struct video_priv *priv, unsigned int idx)
147{
148 switch (priv->bpix) {
149 case VIDEO_BPP16:
150 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 }
155 break;
156 case VIDEO_BPP32:
157 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
158 if (priv->format == VIDEO_X2R10G10B10)
159 return (colors[idx].r << 22) |
160 (colors[idx].g << 12) |
161 (colors[idx].b << 2);
162 else
163 return (colors[idx].r << 16) |
164 (colors[idx].g << 8) |
165 (colors[idx].b << 0);
166 }
167 break;
168 default:
169 break;
170 }
171
172
173
174
175
176 if (idx)
177 return 0xffffff;
178
179 return 0x000000;
180}
181
182static char *parsenum(char *s, int *num)
183{
184 char *end;
185 *num = simple_strtol(s, &end, 10);
186 return end;
187}
188
189
190
191
192
193
194
195
196static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
197{
198
199
200
201 if (row >= priv->rows)
202 row = priv->rows - 1;
203 if (col >= priv->cols)
204 col = priv->cols - 1;
205
206 priv->ycur = row * priv->y_charsize;
207 priv->xcur_frac = priv->xstart_frac +
208 VID_TO_POS(col * priv->x_charsize);
209}
210
211
212
213
214
215
216
217
218static void get_cursor_position(struct vidconsole_priv *priv,
219 int *row, int *col)
220{
221 *row = priv->ycur / priv->y_charsize;
222 *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
223 priv->x_charsize;
224}
225
226
227
228
229
230
231static void vidconsole_escape_char(struct udevice *dev, char ch)
232{
233 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
234
235 if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
236 goto error;
237
238
239 if (priv->escape_len >= sizeof(priv->escape_buf))
240 goto error;
241 if (priv->escape_len == 0) {
242 switch (ch) {
243 case '7':
244
245 get_cursor_position(priv, &priv->row_saved,
246 &priv->col_saved);
247 priv->escape = 0;
248
249 return;
250 case '8': {
251
252 int row = priv->row_saved;
253 int col = priv->col_saved;
254
255 set_cursor_position(priv, row, col);
256 priv->escape = 0;
257 return;
258 }
259 case '[':
260 break;
261 default:
262 goto error;
263 }
264 }
265
266 priv->escape_buf[priv->escape_len++] = ch;
267
268
269
270
271
272 if (!isalpha(ch))
273 return;
274
275
276
277
278
279
280 priv->escape = 0;
281
282 switch (ch) {
283 case 'A':
284 case 'B':
285 case 'C':
286 case 'D':
287 case 'E':
288 case 'F': {
289 int row, col, num;
290 char *s = priv->escape_buf;
291
292
293
294
295
296 s++;
297 s = parsenum(s, &num);
298 if (num == 0)
299 num = 1;
300
301 get_cursor_position(priv, &row, &col);
302 if (ch == 'A' || ch == 'F')
303 row -= num;
304 if (ch == 'C')
305 col += num;
306 if (ch == 'D')
307 col -= num;
308 if (ch == 'B' || ch == 'E')
309 row += num;
310 if (ch == 'E' || ch == 'F')
311 col = 0;
312 if (col < 0)
313 col = 0;
314 if (row < 0)
315 row = 0;
316
317 set_cursor_position(priv, row, col);
318 break;
319 }
320 case 'H':
321 case 'f': {
322 int row, col;
323 char *s = priv->escape_buf;
324
325
326
327
328 s++;
329 s = parsenum(s, &row);
330 s++;
331 s = parsenum(s, &col);
332
333
334
335
336 if (row)
337 --row;
338 if (col)
339 --col;
340
341 set_cursor_position(priv, row, col);
342
343 break;
344 }
345 case 'J': {
346 int mode;
347
348
349
350
351
352
353
354
355
356
357
358 parsenum(priv->escape_buf + 1, &mode);
359
360 if (mode == 2) {
361 int ret;
362
363 video_clear(dev->parent);
364 ret = video_sync(dev->parent, false);
365 if (ret) {
366#ifdef DEBUG
367 console_puts_select_stderr(true, "[vc err: video_sync]");
368#endif
369 }
370 priv->ycur = 0;
371 priv->xcur_frac = priv->xstart_frac;
372 } else {
373 debug("unsupported clear mode: %d\n", mode);
374 }
375 break;
376 }
377 case 'K': {
378 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
379 int mode;
380
381
382
383
384
385
386 parsenum(priv->escape_buf + 1, &mode);
387
388 if (mode == 2) {
389 int row, col;
390
391 get_cursor_position(priv, &row, &col);
392 vidconsole_set_row(dev, row, vid_priv->colour_bg);
393 }
394 break;
395 }
396 case 'm': {
397 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
398 char *s = priv->escape_buf;
399 char *end = &priv->escape_buf[priv->escape_len];
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429 s++;
430 while (s < end) {
431 int val;
432
433 s = parsenum(s, &val);
434 s++;
435
436 switch (val) {
437 case 0:
438
439 video_set_default_colors(dev->parent, false);
440 break;
441 case 1:
442
443 vid_priv->fg_col_idx |= 8;
444 vid_priv->colour_fg = vid_console_color(
445 vid_priv, vid_priv->fg_col_idx);
446 break;
447 case 7:
448
449 vid_priv->colour_fg = vid_console_color(
450 vid_priv, vid_priv->bg_col_idx);
451 vid_priv->colour_bg = vid_console_color(
452 vid_priv, vid_priv->fg_col_idx);
453 break;
454 case 30 ... 37:
455
456 vid_priv->fg_col_idx &= ~7;
457 vid_priv->fg_col_idx |= val - 30;
458 vid_priv->colour_fg = vid_console_color(
459 vid_priv, vid_priv->fg_col_idx);
460 break;
461 case 40 ... 47:
462
463 vid_priv->bg_col_idx &= ~0xf;
464 vid_priv->bg_col_idx |= val - 40;
465 vid_priv->colour_bg = vid_console_color(
466 vid_priv, vid_priv->bg_col_idx);
467 break;
468 default:
469
470 break;
471 }
472 }
473
474 break;
475 }
476 default:
477 debug("unrecognized escape sequence: %*s\n",
478 priv->escape_len, priv->escape_buf);
479 }
480
481 return;
482
483error:
484
485 priv->escape = 0;
486}
487
488
489static int vidconsole_output_glyph(struct udevice *dev, char ch)
490{
491 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
492 int ret;
493
494
495
496
497
498
499 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
500 if (ret == -EAGAIN) {
501 vidconsole_newline(dev);
502 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
503 }
504 if (ret < 0)
505 return ret;
506 priv->xcur_frac += ret;
507 priv->last_ch = ch;
508 if (priv->xcur_frac >= priv->xsize_frac)
509 vidconsole_newline(dev);
510
511 return 0;
512}
513
514int vidconsole_put_char(struct udevice *dev, char ch)
515{
516 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
517 int ret;
518
519 if (priv->escape) {
520 vidconsole_escape_char(dev, ch);
521 return 0;
522 }
523
524 switch (ch) {
525 case '\x1b':
526 priv->escape_len = 0;
527 priv->escape = 1;
528 break;
529 case '\a':
530
531 break;
532 case '\r':
533 priv->xcur_frac = priv->xstart_frac;
534 break;
535 case '\n':
536 vidconsole_newline(dev);
537 vidconsole_entry_start(dev);
538 break;
539 case '\t':
540 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
541 + 1) * priv->tab_width_frac;
542
543 if (priv->xcur_frac >= priv->xsize_frac)
544 vidconsole_newline(dev);
545 break;
546 case '\b':
547 vidconsole_back(dev);
548 priv->last_ch = 0;
549 break;
550 default:
551 ret = vidconsole_output_glyph(dev, ch);
552 if (ret < 0)
553 return ret;
554 break;
555 }
556
557 return 0;
558}
559
560int vidconsole_put_string(struct udevice *dev, const char *str)
561{
562 const char *s;
563 int ret;
564
565 for (s = str; *s; s++) {
566 ret = vidconsole_put_char(dev, *s);
567 if (ret)
568 return ret;
569 }
570
571 return 0;
572}
573
574static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
575{
576 struct udevice *dev = sdev->priv;
577 int ret;
578
579 ret = vidconsole_put_char(dev, ch);
580 if (ret) {
581#ifdef DEBUG
582 console_puts_select_stderr(true, "[vc err: putc]");
583#endif
584 }
585 ret = video_sync(dev->parent, false);
586 if (ret) {
587#ifdef DEBUG
588 console_puts_select_stderr(true, "[vc err: video_sync]");
589#endif
590 }
591}
592
593static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
594{
595 struct udevice *dev = sdev->priv;
596 int ret;
597
598 ret = vidconsole_put_string(dev, s);
599 if (ret) {
600#ifdef DEBUG
601 char str[30];
602
603 snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
604 console_puts_select_stderr(true, str);
605#endif
606 }
607 ret = video_sync(dev->parent, false);
608 if (ret) {
609#ifdef DEBUG
610 console_puts_select_stderr(true, "[vc err: video_sync]");
611#endif
612 }
613}
614
615
616static int vidconsole_pre_probe(struct udevice *dev)
617{
618 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
619 struct udevice *vid = dev->parent;
620 struct video_priv *vid_priv = dev_get_uclass_priv(vid);
621
622 priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
623
624 return 0;
625}
626
627
628static int vidconsole_post_probe(struct udevice *dev)
629{
630 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
631 struct stdio_dev *sdev = &priv->sdev;
632
633 if (!priv->tab_width_frac)
634 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
635
636 if (dev_seq(dev)) {
637 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
638 dev_seq(dev));
639 } else {
640 strcpy(sdev->name, "vidconsole");
641 }
642
643 sdev->flags = DEV_FLAGS_OUTPUT;
644 sdev->putc = vidconsole_putc;
645 sdev->puts = vidconsole_puts;
646 sdev->priv = dev;
647
648 return stdio_register(sdev);
649}
650
651UCLASS_DRIVER(vidconsole) = {
652 .id = UCLASS_VIDEO_CONSOLE,
653 .name = "vidconsole0",
654 .pre_probe = vidconsole_pre_probe,
655 .post_probe = vidconsole_post_probe,
656 .per_device_auto = sizeof(struct vidconsole_priv),
657};
658
659#ifdef CONFIG_VIDEO_COPY
660int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
661{
662 struct udevice *vid = dev_get_parent(dev);
663
664 return video_sync_copy(vid, from, to);
665}
666
667int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
668 int size)
669{
670 memmove(dst, src, size);
671 return vidconsole_sync_copy(dev, dst, dst + size);
672}
673#endif
674
675#if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
676void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
677{
678 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
679 struct udevice *vid_dev = dev->parent;
680 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
681
682 col *= priv->x_charsize;
683 row *= priv->y_charsize;
684 priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
685 priv->xstart_frac = priv->xcur_frac;
686 priv->ycur = min_t(short, row, vid_priv->ysize - 1);
687}
688
689static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
690 char *const argv[])
691{
692 unsigned int col, row;
693 struct udevice *dev;
694
695 if (argc != 3)
696 return CMD_RET_USAGE;
697
698 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
699 return CMD_RET_FAILURE;
700 col = dectoul(argv[1], NULL);
701 row = dectoul(argv[2], NULL);
702 vidconsole_position_cursor(dev, col, row);
703
704 return 0;
705}
706
707static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
708 char *const argv[])
709{
710 struct udevice *dev;
711 const char *s;
712
713 if (argc != 2)
714 return CMD_RET_USAGE;
715
716 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
717 return CMD_RET_FAILURE;
718 for (s = argv[1]; *s; s++)
719 vidconsole_put_char(dev, *s);
720
721 return video_sync(dev->parent, false);
722}
723
724U_BOOT_CMD(
725 setcurs, 3, 1, do_video_setcursor,
726 "set cursor position within screen",
727 " <col> <row> in character"
728);
729
730U_BOOT_CMD(
731 lcdputs, 2, 1, do_video_puts,
732 "print string on video framebuffer",
733 " <string>"
734);
735#endif
736