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