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