Merge tag 'mips-pull-2020-06-29' of https://gitlab.denx.de/u-boot/custodians/u-boot...
[platform/kernel/u-boot.git] / drivers / video / vidconsole-uclass.c
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
10 #include <common.h>
11 #include <command.h>
12 #include <log.h>
13 #include <linux/ctype.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
19 /*
20  * Structure to describe a console color
21  */
22 struct vid_rgb {
23         u32 r;
24         u32 g;
25         u32 b;
26 };
27
28 /* By default we scroll by a single line */
29 #ifndef CONFIG_CONSOLE_SCROLL_LINES
30 #define CONFIG_CONSOLE_SCROLL_LINES 1
31 #endif
32
33 int 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
42 int 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
52 int 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
61 static 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
70 /* Move backwards one space */
71 static int vidconsole_back(struct udevice *dev)
72 {
73         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
74         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         }
82
83         priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
84         if (priv->xcur_frac < priv->xstart_frac) {
85                 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;
90         }
91         video_sync(dev->parent, false);
92
93         return 0;
94 }
95
96 /* Move to a newline, scrolling the display if necessary */
97 static 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
105         priv->xcur_frac = priv->xstart_frac;
106         priv->ycur += priv->y_charsize;
107
108         /* Check if we need to scroll the terminal */
109         if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
110                 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);
114                 priv->ycur -= rows * priv->y_charsize;
115         }
116         priv->last_ch = 0;
117
118         video_sync(dev->parent, false);
119 }
120
121 static const struct vid_rgb colors[VID_COLOR_COUNT] = {
122         { 0x00, 0x00, 0x00 },  /* black */
123         { 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 */
133         { 0xff, 0xff, 0x00 },  /* yellow */
134         { 0x00, 0x00, 0xff },  /* bright blue */
135         { 0xff, 0x00, 0xff },  /* bright magenta */
136         { 0x00, 0xff, 0xff },  /* bright cyan */
137         { 0xff, 0xff, 0xff },  /* white */
138 };
139
140 u32 vid_console_color(struct video_priv *priv, unsigned int idx)
141 {
142         switch (priv->bpix) {
143         case VIDEO_BPP16:
144                 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                 }
149                 break;
150         case VIDEO_BPP32:
151                 if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
152                         return (colors[idx].r << 16) |
153                                (colors[idx].g <<  8) |
154                                (colors[idx].b <<  0);
155                 }
156                 break;
157         default:
158                 break;
159         }
160
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 */
169 }
170
171 static char *parsenum(char *s, int *num)
172 {
173         char *end;
174         *num = simple_strtol(s, &end, 10);
175         return end;
176 }
177
178 /**
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  */
185 static 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  */
207 static 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
215 /*
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  */
220 static 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;
230         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         }
254
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) {
272         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         }
309         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
322                 /*
323                  * Video origin is [0, 0], terminal origin is [1, 1].
324                  */
325                 if (row)
326                         --row;
327                 if (col)
328                         --col;
329
330                 set_cursor_position(priv, row, col);
331
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);
351                         video_sync(dev->parent, false);
352                         priv->ycur = 0;
353                         priv->xcur_frac = priv->xstart_frac;
354                 } else {
355                         debug("unsupported clear mode: %d\n", mode);
356                 }
357                 break;
358         }
359         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         }
378         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) {
419                         case 0:
420                                 /* all attributes off */
421                                 video_set_default_colors(dev->parent, false);
422                                 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;
429                         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;
436                         case 30 ... 37:
437                                 /* foreground color */
438                                 vid_priv->fg_col_idx &= ~7;
439                                 vid_priv->fg_col_idx |= val - 30;
440                                 vid_priv->colour_fg = vid_console_color(
441                                                 vid_priv, vid_priv->fg_col_idx);
442                                 break;
443                         case 40 ... 47:
444                                 /* background color, also mask the bold bit */
445                                 vid_priv->bg_col_idx &= ~0xf;
446                                 vid_priv->bg_col_idx |= val - 40;
447                                 vid_priv->colour_bg = vid_console_color(
448                                                 vid_priv, vid_priv->bg_col_idx);
449                                 break;
450                         default:
451                                 /* ignore unsupported SGR parameter */
452                                 break;
453                         }
454                 }
455
456                 break;
457         }
458         default:
459                 debug("unrecognized escape sequence: %*s\n",
460                       priv->escape_len, priv->escape_buf);
461         }
462
463         return;
464
465 error:
466         /* something went wrong, just revert to normal mode: */
467         priv->escape = 0;
468 }
469
470 /* Put that actual character on the screen (using the CP437 code page). */
471 static 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
496 int vidconsole_put_char(struct udevice *dev, char ch)
497 {
498         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
499         int ret;
500
501         if (priv->escape) {
502                 vidconsole_escape_char(dev, ch);
503                 return 0;
504         }
505
506         switch (ch) {
507         case '\x1b':
508                 priv->escape_len = 0;
509                 priv->escape = 1;
510                 break;
511         case '\a':
512                 /* beep */
513                 break;
514         case '\r':
515                 priv->xcur_frac = priv->xstart_frac;
516                 break;
517         case '\n':
518                 vidconsole_newline(dev);
519                 vidconsole_entry_start(dev);
520                 break;
521         case '\t':      /* Tab (8 chars alignment) */
522                 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
523                                 + 1) * priv->tab_width_frac;
524
525                 if (priv->xcur_frac >= priv->xsize_frac)
526                         vidconsole_newline(dev);
527                 break;
528         case '\b':
529                 vidconsole_back(dev);
530                 priv->last_ch = 0;
531                 break;
532         default:
533                 ret = vidconsole_output_glyph(dev, ch);
534                 if (ret < 0)
535                         return ret;
536                 break;
537         }
538
539         return 0;
540 }
541
542 int 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
556 static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
557 {
558         struct udevice *dev = sdev->priv;
559
560         vidconsole_put_char(dev, ch);
561         video_sync(dev->parent, false);
562 }
563
564 static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
565 {
566         struct udevice *dev = sdev->priv;
567
568         vidconsole_put_string(dev, s);
569         video_sync(dev->parent, false);
570 }
571
572 /* Set up the number of rows and colours (rotated drivers override this) */
573 static 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
579         priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
580
581         return 0;
582 }
583
584 /* Register the device with stdio */
585 static 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;
589
590         if (!priv->tab_width_frac)
591                 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
592
593         if (dev->seq) {
594                 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
595                          dev->seq);
596         } else {
597                 strcpy(sdev->name, "vidconsole");
598         }
599
600         sdev->flags = DEV_FLAGS_OUTPUT;
601         sdev->putc = vidconsole_putc;
602         sdev->puts = vidconsole_puts;
603         sdev->priv = dev;
604
605         return stdio_register(sdev);
606 }
607
608 UCLASS_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
616 #if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
617 void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
618 {
619         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
620         struct udevice *vid_dev = dev->parent;
621         struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
622
623         col *= priv->x_charsize;
624         row *= priv->y_charsize;
625         priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
626         priv->xstart_frac = priv->xcur_frac;
627         priv->ycur = min_t(short, row, vid_priv->ysize - 1);
628 }
629
630 static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
631                               char *const argv[])
632 {
633         unsigned int col, row;
634         struct udevice *dev;
635
636         if (argc != 3)
637                 return CMD_RET_USAGE;
638
639         if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
640                 return CMD_RET_FAILURE;
641         col = simple_strtoul(argv[1], NULL, 10);
642         row = simple_strtoul(argv[2], NULL, 10);
643         vidconsole_position_cursor(dev, col, row);
644
645         return 0;
646 }
647
648 static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
649                          char *const argv[])
650 {
651         struct udevice *dev;
652         const char *s;
653
654         if (argc != 2)
655                 return CMD_RET_USAGE;
656
657         if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
658                 return CMD_RET_FAILURE;
659         for (s = argv[1]; *s; s++)
660                 vidconsole_put_char(dev, *s);
661
662         video_sync(dev->parent, false);
663
664         return 0;
665 }
666
667 U_BOOT_CMD(
668         setcurs, 3,     1,      do_video_setcursor,
669         "set cursor position within screen",
670         "    <col> <row> in character"
671 );
672
673 U_BOOT_CMD(
674         lcdputs, 2,     1,      do_video_puts,
675         "print string on video framebuffer",
676         "    <string>"
677 );
678 #endif /* CONFIG_IS_ENABLED(CMD_VIDCONSOLE) */