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