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