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