blob: d32b1017581dd962271c20bff97ff263cb1db5da [file] [log] [blame]
Simon Glass84c7fb32016-01-18 19:52:17 -07001/*
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>
Rob Clark06e7a0d2017-09-13 18:12:21 -040012#include <linux/ctype.h>
Simon Glass84c7fb32016-01-18 19:52:17 -070013#include <dm.h>
14#include <video.h>
15#include <video_console.h>
16#include <video_font.h> /* Get font data, width and height */
17
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +010018/*
19 * Structure to describe a console color
20 */
21struct vid_rgb {
22 u32 r;
23 u32 g;
24 u32 b;
25};
26
Simon Glass84c7fb32016-01-18 19:52:17 -070027/* By default we scroll by a single line */
28#ifndef CONFIG_CONSOLE_SCROLL_LINES
29#define CONFIG_CONSOLE_SCROLL_LINES 1
30#endif
31
32int 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
41int 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
51int 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
Simon Glassafee7432016-01-14 18:10:40 -070060static 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
Simon Glass84c7fb32016-01-18 19:52:17 -070069/* Move backwards one space */
Simon Glass33bd3b62016-01-14 18:10:41 -070070static int vidconsole_back(struct udevice *dev)
Simon Glass84c7fb32016-01-18 19:52:17 -070071{
72 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass33bd3b62016-01-14 18:10:41 -070073 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 }
Simon Glass84c7fb32016-01-18 19:52:17 -070081
Simon Glass52c10c52016-01-14 18:10:37 -070082 priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
Simon Glassa74451d2016-01-14 18:10:39 -070083 if (priv->xcur_frac < priv->xstart_frac) {
Simon Glass52c10c52016-01-14 18:10:37 -070084 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;
Simon Glass84c7fb32016-01-18 19:52:17 -070089 }
Simon Glass2f632642017-06-12 06:21:36 -060090 video_sync(dev->parent);
Simon Glass33bd3b62016-01-14 18:10:41 -070091
92 return 0;
Simon Glass84c7fb32016-01-18 19:52:17 -070093}
94
95/* Move to a newline, scrolling the display if necessary */
96static 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
Simon Glassa74451d2016-01-14 18:10:39 -0700104 priv->xcur_frac = priv->xstart_frac;
Simon Glass52c10c52016-01-14 18:10:37 -0700105 priv->ycur += priv->y_charsize;
Simon Glass84c7fb32016-01-18 19:52:17 -0700106
107 /* Check if we need to scroll the terminal */
Simon Glass52c10c52016-01-14 18:10:37 -0700108 if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) {
Simon Glass84c7fb32016-01-18 19:52:17 -0700109 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);
Simon Glass52c10c52016-01-14 18:10:37 -0700113 priv->ycur -= rows * priv->y_charsize;
Simon Glass84c7fb32016-01-18 19:52:17 -0700114 }
Simon Glassafee7432016-01-14 18:10:40 -0700115 priv->last_ch = 0;
116
Simon Glass84c7fb32016-01-18 19:52:17 -0700117 video_sync(dev->parent);
118}
119
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100120static const struct vid_rgb colors[VID_COLOR_COUNT] = {
Rob Clark50509bb2017-09-13 18:12:22 -0400121 { 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
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100131u32 vid_console_color(struct video_priv *priv, unsigned int idx)
Rob Clark50509bb2017-09-13 18:12:22 -0400132{
133 switch (priv->bpix) {
134 case VIDEO_BPP16:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100135 return ((colors[idx].r >> 3) << 11) |
136 ((colors[idx].g >> 2) << 5) |
137 ((colors[idx].b >> 3) << 0);
Rob Clark50509bb2017-09-13 18:12:22 -0400138 case VIDEO_BPP32:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100139 return (colors[idx].r << 16) |
140 (colors[idx].g << 8) |
141 (colors[idx].b << 0);
Rob Clark50509bb2017-09-13 18:12:22 -0400142 default:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100143 /*
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 */
Rob Clark50509bb2017-09-13 18:12:22 -0400151 }
152}
153
Rob Clark06e7a0d2017-09-13 18:12:21 -0400154static 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 */
166static 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 }
Rob Clark50509bb2017-09-13 18:12:22 -0400240 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:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100282 /* foreground color */
283 vid_priv->colour_fg = vid_console_color(
284 vid_priv, val - 30);
Rob Clark50509bb2017-09-13 18:12:22 -0400285 break;
286 case 40 ... 47:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100287 /* background color */
288 vid_priv->colour_bg = vid_console_color(
289 vid_priv, val - 40);
Rob Clark50509bb2017-09-13 18:12:22 -0400290 break;
291 default:
Heinrich Schuchardt290e1d82018-02-08 21:47:11 +0100292 /* ignore unsupported SGR parameter */
Rob Clark50509bb2017-09-13 18:12:22 -0400293 break;
294 }
295 }
296
297 break;
298 }
Rob Clark06e7a0d2017-09-13 18:12:21 -0400299 default:
300 debug("unrecognized escape sequence: %*s\n",
301 priv->escape_len, priv->escape_buf);
302 }
303
304 return;
305
306error:
307 /* something went wrong, just revert to normal mode: */
308 priv->escape = 0;
309}
310
Simon Glass84c7fb32016-01-18 19:52:17 -0700311int vidconsole_put_char(struct udevice *dev, char ch)
312{
313 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
314 int ret;
315
Rob Clark06e7a0d2017-09-13 18:12:21 -0400316 if (priv->escape) {
317 vidconsole_escape_char(dev, ch);
318 return 0;
319 }
320
Simon Glass84c7fb32016-01-18 19:52:17 -0700321 switch (ch) {
Rob Clark06e7a0d2017-09-13 18:12:21 -0400322 case '\x1b':
323 priv->escape_len = 0;
324 priv->escape = 1;
325 break;
Simon Glass37b80202016-01-14 18:10:38 -0700326 case '\a':
327 /* beep */
328 break;
Simon Glass84c7fb32016-01-18 19:52:17 -0700329 case '\r':
Simon Glassa74451d2016-01-14 18:10:39 -0700330 priv->xcur_frac = priv->xstart_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700331 break;
332 case '\n':
333 vidconsole_newline(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700334 vidconsole_entry_start(dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700335 break;
336 case '\t': /* Tab (8 chars alignment) */
Simon Glass52c10c52016-01-14 18:10:37 -0700337 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
338 + 1) * priv->tab_width_frac;
Simon Glass84c7fb32016-01-18 19:52:17 -0700339
Simon Glass52c10c52016-01-14 18:10:37 -0700340 if (priv->xcur_frac >= priv->xsize_frac)
Simon Glass84c7fb32016-01-18 19:52:17 -0700341 vidconsole_newline(dev);
342 break;
343 case '\b':
344 vidconsole_back(dev);
Simon Glassafee7432016-01-14 18:10:40 -0700345 priv->last_ch = 0;
Simon Glass84c7fb32016-01-18 19:52:17 -0700346 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 */
Simon Glass52c10c52016-01-14 18:10:37 -0700353 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)
Simon Glass84c7fb32016-01-18 19:52:17 -0700360 return ret;
Simon Glass52c10c52016-01-14 18:10:37 -0700361 priv->xcur_frac += ret;
Simon Glassafee7432016-01-14 18:10:40 -0700362 priv->last_ch = ch;
Simon Glass52c10c52016-01-14 18:10:37 -0700363 if (priv->xcur_frac >= priv->xsize_frac)
Simon Glass84c7fb32016-01-18 19:52:17 -0700364 vidconsole_newline(dev);
365 break;
366 }
367
368 return 0;
369}
370
371static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
372{
373 struct udevice *dev = sdev->priv;
374
375 vidconsole_put_char(dev, ch);
Rob Clarke9594612017-09-13 18:12:20 -0400376 video_sync(dev->parent);
Simon Glass84c7fb32016-01-18 19:52:17 -0700377}
378
379static 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++);
Simon Glass4f3873d2016-01-30 16:37:41 -0700385 video_sync(dev->parent);
Simon Glass84c7fb32016-01-18 19:52:17 -0700386}
387
388/* Set up the number of rows and colours (rotated drivers override this) */
389static 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
Simon Glass52c10c52016-01-14 18:10:37 -0700395 priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
Simon Glass84c7fb32016-01-18 19:52:17 -0700396
397 return 0;
398}
399
400/* Register the device with stdio */
401static 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;
Simon Glass84c7fb32016-01-18 19:52:17 -0700405
Simon Glass52c10c52016-01-14 18:10:37 -0700406 if (!priv->tab_width_frac)
407 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
408
Simon Glass798ff502016-01-21 19:44:51 -0700409 if (dev->seq) {
410 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
411 dev->seq);
412 } else {
413 strcpy(sdev->name, "vidconsole");
414 }
Simon Glass52c10c52016-01-14 18:10:37 -0700415
Simon Glass84c7fb32016-01-18 19:52:17 -0700416 sdev->flags = DEV_FLAGS_OUTPUT;
417 sdev->putc = vidconsole_putc;
418 sdev->puts = vidconsole_puts;
419 sdev->priv = dev;
Simon Glass84c7fb32016-01-18 19:52:17 -0700420
Masahiro Yamadabf528cd2016-09-06 22:17:33 +0900421 return stdio_register(sdev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700422}
423
424UCLASS_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
432void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
433{
434 struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
Simon Glass52c10c52016-01-14 18:10:37 -0700435 struct udevice *vid_dev = dev->parent;
436 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
Simon Glass84c7fb32016-01-18 19:52:17 -0700437
Simon Glass52c10c52016-01-14 18:10:37 -0700438 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);
Simon Glass84c7fb32016-01-18 19:52:17 -0700440}
441
442static 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
Simon Glassc7298e72016-02-11 13:23:26 -0700451 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700452 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
460static 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
Simon Glassc7298e72016-02-11 13:23:26 -0700469 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
Simon Glass84c7fb32016-01-18 19:52:17 -0700470 return CMD_RET_FAILURE;
471 for (s = argv[1]; *s; s++)
472 vidconsole_put_char(dev, *s);
473
Rob Clarke9594612017-09-13 18:12:20 -0400474 video_sync(dev->parent);
475
Simon Glass84c7fb32016-01-18 19:52:17 -0700476 return 0;
477}
478
479U_BOOT_CMD(
480 setcurs, 3, 1, do_video_setcursor,
481 "set cursor position within screen",
482 " <col> <row> in character"
483);
484
485U_BOOT_CMD(
486 lcdputs, 2, 1, do_video_puts,
487 "print string on video framebuffer",
488 " <string>"
489);