]> git.sur5r.net Git - u-boot/blob - drivers/video/vidconsole-uclass.c
37eb2a0ef46da513f6565b9706545a9ec9a6be15
[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 char *parsenum(char *s, int *num)
112 {
113         char *end;
114         *num = simple_strtol(s, &end, 10);
115         return end;
116 }
117
118 /*
119  * Process a character while accumulating an escape string.  Chars are
120  * accumulated into escape_buf until the end of escape sequence is
121  * found, at which point the sequence is parsed and processed.
122  */
123 static void vidconsole_escape_char(struct udevice *dev, char ch)
124 {
125         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
126
127         if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
128                 goto error;
129
130         /* Sanity checking for bogus ESC sequences: */
131         if (priv->escape_len >= sizeof(priv->escape_buf))
132                 goto error;
133         if (priv->escape_len == 0 && ch != '[')
134                 goto error;
135
136         priv->escape_buf[priv->escape_len++] = ch;
137
138         /*
139          * Escape sequences are terminated by a letter, so keep
140          * accumulating until we get one:
141          */
142         if (!isalpha(ch))
143                 return;
144
145         /*
146          * clear escape mode first, otherwise things will get highly
147          * surprising if you hit any debug prints that come back to
148          * this console.
149          */
150         priv->escape = 0;
151
152         switch (ch) {
153         case 'H':
154         case 'f': {
155                 int row, col;
156                 char *s = priv->escape_buf;
157
158                 /*
159                  * Set cursor position: [%d;%df or [%d;%dH
160                  */
161                 s++;    /* [ */
162                 s = parsenum(s, &row);
163                 s++;    /* ; */
164                 s = parsenum(s, &col);
165
166                 priv->ycur = row * priv->y_charsize;
167                 priv->xcur_frac = priv->xstart_frac +
168                         VID_TO_POS(col * priv->x_charsize);
169
170                 break;
171         }
172         case 'J': {
173                 int mode;
174
175                 /*
176                  * Clear part/all screen:
177                  *   [J or [0J - clear screen from cursor down
178                  *   [1J       - clear screen from cursor up
179                  *   [2J       - clear entire screen
180                  *
181                  * TODO we really only handle entire-screen case, others
182                  * probably require some additions to video-uclass (and
183                  * are not really needed yet by efi_console)
184                  */
185                 parsenum(priv->escape_buf + 1, &mode);
186
187                 if (mode == 2) {
188                         video_clear(dev->parent);
189                         video_sync(dev->parent);
190                         priv->ycur = 0;
191                         priv->xcur_frac = priv->xstart_frac;
192                 } else {
193                         debug("unsupported clear mode: %d\n", mode);
194                 }
195                 break;
196         }
197         default:
198                 debug("unrecognized escape sequence: %*s\n",
199                       priv->escape_len, priv->escape_buf);
200         }
201
202         return;
203
204 error:
205         /* something went wrong, just revert to normal mode: */
206         priv->escape = 0;
207 }
208
209 int vidconsole_put_char(struct udevice *dev, char ch)
210 {
211         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
212         int ret;
213
214         if (priv->escape) {
215                 vidconsole_escape_char(dev, ch);
216                 return 0;
217         }
218
219         switch (ch) {
220         case '\x1b':
221                 priv->escape_len = 0;
222                 priv->escape = 1;
223                 break;
224         case '\a':
225                 /* beep */
226                 break;
227         case '\r':
228                 priv->xcur_frac = priv->xstart_frac;
229                 break;
230         case '\n':
231                 vidconsole_newline(dev);
232                 vidconsole_entry_start(dev);
233                 break;
234         case '\t':      /* Tab (8 chars alignment) */
235                 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
236                                 + 1) * priv->tab_width_frac;
237
238                 if (priv->xcur_frac >= priv->xsize_frac)
239                         vidconsole_newline(dev);
240                 break;
241         case '\b':
242                 vidconsole_back(dev);
243                 priv->last_ch = 0;
244                 break;
245         default:
246                 /*
247                  * Failure of this function normally indicates an unsupported
248                  * colour depth. Check this and return an error to help with
249                  * diagnosis.
250                  */
251                 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
252                 if (ret == -EAGAIN) {
253                         vidconsole_newline(dev);
254                         ret = vidconsole_putc_xy(dev, priv->xcur_frac,
255                                                  priv->ycur, ch);
256                 }
257                 if (ret < 0)
258                         return ret;
259                 priv->xcur_frac += ret;
260                 priv->last_ch = ch;
261                 if (priv->xcur_frac >= priv->xsize_frac)
262                         vidconsole_newline(dev);
263                 break;
264         }
265
266         return 0;
267 }
268
269 static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
270 {
271         struct udevice *dev = sdev->priv;
272
273         vidconsole_put_char(dev, ch);
274         video_sync(dev->parent);
275 }
276
277 static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
278 {
279         struct udevice *dev = sdev->priv;
280
281         while (*s)
282                 vidconsole_put_char(dev, *s++);
283         video_sync(dev->parent);
284 }
285
286 /* Set up the number of rows and colours (rotated drivers override this) */
287 static int vidconsole_pre_probe(struct udevice *dev)
288 {
289         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
290         struct udevice *vid = dev->parent;
291         struct video_priv *vid_priv = dev_get_uclass_priv(vid);
292
293         priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
294
295         return 0;
296 }
297
298 /* Register the device with stdio */
299 static int vidconsole_post_probe(struct udevice *dev)
300 {
301         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
302         struct stdio_dev *sdev = &priv->sdev;
303
304         if (!priv->tab_width_frac)
305                 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
306
307         if (dev->seq) {
308                 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
309                          dev->seq);
310         } else {
311                 strcpy(sdev->name, "vidconsole");
312         }
313
314         sdev->flags = DEV_FLAGS_OUTPUT;
315         sdev->putc = vidconsole_putc;
316         sdev->puts = vidconsole_puts;
317         sdev->priv = dev;
318
319         return stdio_register(sdev);
320 }
321
322 UCLASS_DRIVER(vidconsole) = {
323         .id             = UCLASS_VIDEO_CONSOLE,
324         .name           = "vidconsole0",
325         .pre_probe      = vidconsole_pre_probe,
326         .post_probe     = vidconsole_post_probe,
327         .per_device_auto_alloc_size     = sizeof(struct vidconsole_priv),
328 };
329
330 void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
331 {
332         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
333         struct udevice *vid_dev = dev->parent;
334         struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
335
336         priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
337         priv->ycur = min_t(short, row, vid_priv->ysize - 1);
338 }
339
340 static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc,
341                               char *const argv[])
342 {
343         unsigned int col, row;
344         struct udevice *dev;
345
346         if (argc != 3)
347                 return CMD_RET_USAGE;
348
349         if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
350                 return CMD_RET_FAILURE;
351         col = simple_strtoul(argv[1], NULL, 10);
352         row = simple_strtoul(argv[2], NULL, 10);
353         vidconsole_position_cursor(dev, col, row);
354
355         return 0;
356 }
357
358 static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc,
359                          char *const argv[])
360 {
361         struct udevice *dev;
362         const char *s;
363
364         if (argc != 2)
365                 return CMD_RET_USAGE;
366
367         if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
368                 return CMD_RET_FAILURE;
369         for (s = argv[1]; *s; s++)
370                 vidconsole_put_char(dev, *s);
371
372         video_sync(dev->parent);
373
374         return 0;
375 }
376
377 U_BOOT_CMD(
378         setcurs, 3,     1,      do_video_setcursor,
379         "set cursor position within screen",
380         "    <col> <row> in character"
381 );
382
383 U_BOOT_CMD(
384         lcdputs, 2,     1,      do_video_puts,
385         "print string on video framebuffer",
386         "    <string>"
387 );