1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2015 Google, Inc 4 * (C) Copyright 2001-2015 5 * DENX Software Engineering -- wd@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 /* 217 * Ensure we stay in the bounds of the screen. 218 */ 219 if (row >= priv->rows) 220 row = priv->rows - 1; 221 if (col >= priv->cols) 222 col = priv->cols - 1; 223 224 priv->ycur = row * priv->y_charsize; 225 priv->xcur_frac = priv->xstart_frac + 226 VID_TO_POS(col * priv->x_charsize); 227 228 break; 229 } 230 case 'J': { 231 int mode; 232 233 /* 234 * Clear part/all screen: 235 * [J or [0J - clear screen from cursor down 236 * [1J - clear screen from cursor up 237 * [2J - clear entire screen 238 * 239 * TODO we really only handle entire-screen case, others 240 * probably require some additions to video-uclass (and 241 * are not really needed yet by efi_console) 242 */ 243 parsenum(priv->escape_buf + 1, &mode); 244 245 if (mode == 2) { 246 video_clear(dev->parent); 247 video_sync(dev->parent); 248 priv->ycur = 0; 249 priv->xcur_frac = priv->xstart_frac; 250 } else { 251 debug("unsupported clear mode: %d\n", mode); 252 } 253 break; 254 } 255 case 'm': { 256 struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); 257 char *s = priv->escape_buf; 258 char *end = &priv->escape_buf[priv->escape_len]; 259 260 /* 261 * Set graphics mode: [%d;...;%dm 262 * 263 * Currently only supports the color attributes: 264 * 265 * Foreground Colors: 266 * 267 * 30 Black 268 * 31 Red 269 * 32 Green 270 * 33 Yellow 271 * 34 Blue 272 * 35 Magenta 273 * 36 Cyan 274 * 37 White 275 * 276 * Background Colors: 277 * 278 * 40 Black 279 * 41 Red 280 * 42 Green 281 * 43 Yellow 282 * 44 Blue 283 * 45 Magenta 284 * 46 Cyan 285 * 47 White 286 */ 287 288 s++; /* [ */ 289 while (s < end) { 290 int val; 291 292 s = parsenum(s, &val); 293 s++; 294 295 switch (val) { 296 case 0: 297 /* all attributes off */ 298 video_set_default_colors(vid_priv); 299 break; 300 case 1: 301 /* bold */ 302 vid_priv->fg_col_idx |= 8; 303 vid_priv->colour_fg = vid_console_color( 304 vid_priv, vid_priv->fg_col_idx); 305 break; 306 case 30 ... 37: 307 /* foreground color */ 308 vid_priv->fg_col_idx &= ~7; 309 vid_priv->fg_col_idx |= val - 30; 310 vid_priv->colour_fg = vid_console_color( 311 vid_priv, vid_priv->fg_col_idx); 312 break; 313 case 40 ... 47: 314 /* background color */ 315 vid_priv->colour_bg = vid_console_color( 316 vid_priv, val - 40); 317 break; 318 default: 319 /* ignore unsupported SGR parameter */ 320 break; 321 } 322 } 323 324 break; 325 } 326 default: 327 debug("unrecognized escape sequence: %*s\n", 328 priv->escape_len, priv->escape_buf); 329 } 330 331 return; 332 333 error: 334 /* something went wrong, just revert to normal mode: */ 335 priv->escape = 0; 336 } 337 338 int vidconsole_put_char(struct udevice *dev, char ch) 339 { 340 struct vidconsole_priv *priv = dev_get_uclass_priv(dev); 341 int ret; 342 343 if (priv->escape) { 344 vidconsole_escape_char(dev, ch); 345 return 0; 346 } 347 348 switch (ch) { 349 case '\x1b': 350 priv->escape_len = 0; 351 priv->escape = 1; 352 break; 353 case '\a': 354 /* beep */ 355 break; 356 case '\r': 357 priv->xcur_frac = priv->xstart_frac; 358 break; 359 case '\n': 360 vidconsole_newline(dev); 361 vidconsole_entry_start(dev); 362 break; 363 case '\t': /* Tab (8 chars alignment) */ 364 priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac) 365 + 1) * priv->tab_width_frac; 366 367 if (priv->xcur_frac >= priv->xsize_frac) 368 vidconsole_newline(dev); 369 break; 370 case '\b': 371 vidconsole_back(dev); 372 priv->last_ch = 0; 373 break; 374 default: 375 /* 376 * Failure of this function normally indicates an unsupported 377 * colour depth. Check this and return an error to help with 378 * diagnosis. 379 */ 380 ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch); 381 if (ret == -EAGAIN) { 382 vidconsole_newline(dev); 383 ret = vidconsole_putc_xy(dev, priv->xcur_frac, 384 priv->ycur, ch); 385 } 386 if (ret < 0) 387 return ret; 388 priv->xcur_frac += ret; 389 priv->last_ch = ch; 390 if (priv->xcur_frac >= priv->xsize_frac) 391 vidconsole_newline(dev); 392 break; 393 } 394 395 return 0; 396 } 397 398 static void vidconsole_putc(struct stdio_dev *sdev, const char ch) 399 { 400 struct udevice *dev = sdev->priv; 401 402 vidconsole_put_char(dev, ch); 403 video_sync(dev->parent); 404 } 405 406 static void vidconsole_puts(struct stdio_dev *sdev, const char *s) 407 { 408 struct udevice *dev = sdev->priv; 409 410 while (*s) 411 vidconsole_put_char(dev, *s++); 412 video_sync(dev->parent); 413 } 414 415 /* Set up the number of rows and colours (rotated drivers override this) */ 416 static int vidconsole_pre_probe(struct udevice *dev) 417 { 418 struct vidconsole_priv *priv = dev_get_uclass_priv(dev); 419 struct udevice *vid = dev->parent; 420 struct video_priv *vid_priv = dev_get_uclass_priv(vid); 421 422 priv->xsize_frac = VID_TO_POS(vid_priv->xsize); 423 424 return 0; 425 } 426 427 /* Register the device with stdio */ 428 static int vidconsole_post_probe(struct udevice *dev) 429 { 430 struct vidconsole_priv *priv = dev_get_uclass_priv(dev); 431 struct stdio_dev *sdev = &priv->sdev; 432 433 if (!priv->tab_width_frac) 434 priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8; 435 436 if (dev->seq) { 437 snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d", 438 dev->seq); 439 } else { 440 strcpy(sdev->name, "vidconsole"); 441 } 442 443 sdev->flags = DEV_FLAGS_OUTPUT; 444 sdev->putc = vidconsole_putc; 445 sdev->puts = vidconsole_puts; 446 sdev->priv = dev; 447 448 return stdio_register(sdev); 449 } 450 451 UCLASS_DRIVER(vidconsole) = { 452 .id = UCLASS_VIDEO_CONSOLE, 453 .name = "vidconsole0", 454 .pre_probe = vidconsole_pre_probe, 455 .post_probe = vidconsole_post_probe, 456 .per_device_auto_alloc_size = sizeof(struct vidconsole_priv), 457 }; 458 459 void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row) 460 { 461 struct vidconsole_priv *priv = dev_get_uclass_priv(dev); 462 struct udevice *vid_dev = dev->parent; 463 struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev); 464 465 priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1)); 466 priv->ycur = min_t(short, row, vid_priv->ysize - 1); 467 } 468 469 static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc, 470 char *const argv[]) 471 { 472 unsigned int col, row; 473 struct udevice *dev; 474 475 if (argc != 3) 476 return CMD_RET_USAGE; 477 478 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev)) 479 return CMD_RET_FAILURE; 480 col = simple_strtoul(argv[1], NULL, 10); 481 row = simple_strtoul(argv[2], NULL, 10); 482 vidconsole_position_cursor(dev, col, row); 483 484 return 0; 485 } 486 487 static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc, 488 char *const argv[]) 489 { 490 struct udevice *dev; 491 const char *s; 492 493 if (argc != 2) 494 return CMD_RET_USAGE; 495 496 if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev)) 497 return CMD_RET_FAILURE; 498 for (s = argv[1]; *s; s++) 499 vidconsole_put_char(dev, *s); 500 501 video_sync(dev->parent); 502 503 return 0; 504 } 505 506 U_BOOT_CMD( 507 setcurs, 3, 1, do_video_setcursor, 508 "set cursor position within screen", 509 " <col> <row> in character" 510 ); 511 512 U_BOOT_CMD( 513 lcdputs, 2, 1, do_video_puts, 514 "print string on video framebuffer", 515 " <string>" 516 ); 517