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