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