uboot/drivers/video/vidconsole-uclass.c
<<
>>
Prefs
   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 <command.h>
  12#include <console.h>
  13#include <log.h>
  14#include <dm.h>
  15#include <video.h>
  16#include <video_console.h>
  17#include <video_font.h>         /* Bitmap font for code page 437 */
  18#include <linux/ctype.h>
  19
  20/*
  21 * Structure to describe a console color
  22 */
  23struct vid_rgb {
  24        u32 r;
  25        u32 g;
  26        u32 b;
  27};
  28
  29/* By default we scroll by a single line */
  30#ifndef CONFIG_CONSOLE_SCROLL_LINES
  31#define CONFIG_CONSOLE_SCROLL_LINES 1
  32#endif
  33
  34int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
  35{
  36        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  37
  38        if (!ops->putc_xy)
  39                return -ENOSYS;
  40        return ops->putc_xy(dev, x, y, ch);
  41}
  42
  43int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
  44                         uint count)
  45{
  46        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  47
  48        if (!ops->move_rows)
  49                return -ENOSYS;
  50        return ops->move_rows(dev, rowdst, rowsrc, count);
  51}
  52
  53int vidconsole_set_row(struct udevice *dev, uint row, int clr)
  54{
  55        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  56
  57        if (!ops->set_row)
  58                return -ENOSYS;
  59        return ops->set_row(dev, row, clr);
  60}
  61
  62static int vidconsole_entry_start(struct udevice *dev)
  63{
  64        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  65
  66        if (!ops->entry_start)
  67                return -ENOSYS;
  68        return ops->entry_start(dev);
  69}
  70
  71/* Move backwards one space */
  72static int vidconsole_back(struct udevice *dev)
  73{
  74        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
  75        struct vidconsole_ops *ops = vidconsole_get_ops(dev);
  76        int ret;
  77
  78        if (ops->backspace) {
  79                ret = ops->backspace(dev);
  80                if (ret != -ENOSYS)
  81                        return ret;
  82        }
  83
  84        priv->xcur_frac -= VID_TO_POS(priv->x_charsize);
  85        if (priv->xcur_frac < priv->xstart_frac) {
  86                priv->xcur_frac = (priv->cols - 1) *
  87                        VID_TO_POS(priv->x_charsize);
  88                priv->ycur -= priv->y_charsize;
  89                if (priv->ycur < 0)
  90                        priv->ycur = 0;
  91        }
  92        return video_sync(dev->parent, false);
  93}
  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, ret;
 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        ret = video_sync(dev->parent, false);
 118        if (ret) {
 119#ifdef DEBUG
 120                console_puts_select_stderr(true, "[vc err: video_sync]");
 121#endif
 122        }
 123}
 124
 125static const struct vid_rgb colors[VID_COLOR_COUNT] = {
 126        { 0x00, 0x00, 0x00 },  /* black */
 127        { 0xc0, 0x00, 0x00 },  /* red */
 128        { 0x00, 0xc0, 0x00 },  /* green */
 129        { 0xc0, 0x60, 0x00 },  /* brown */
 130        { 0x00, 0x00, 0xc0 },  /* blue */
 131        { 0xc0, 0x00, 0xc0 },  /* magenta */
 132        { 0x00, 0xc0, 0xc0 },  /* cyan */
 133        { 0xc0, 0xc0, 0xc0 },  /* light gray */
 134        { 0x80, 0x80, 0x80 },  /* gray */
 135        { 0xff, 0x00, 0x00 },  /* bright red */
 136        { 0x00, 0xff, 0x00 },  /* bright green */
 137        { 0xff, 0xff, 0x00 },  /* yellow */
 138        { 0x00, 0x00, 0xff },  /* bright blue */
 139        { 0xff, 0x00, 0xff },  /* bright magenta */
 140        { 0x00, 0xff, 0xff },  /* bright cyan */
 141        { 0xff, 0xff, 0xff },  /* white */
 142};
 143
 144u32 vid_console_color(struct video_priv *priv, unsigned int idx)
 145{
 146        switch (priv->bpix) {
 147        case VIDEO_BPP16:
 148                if (CONFIG_IS_ENABLED(VIDEO_BPP16)) {
 149                        return ((colors[idx].r >> 3) << 11) |
 150                               ((colors[idx].g >> 2) <<  5) |
 151                               ((colors[idx].b >> 3) <<  0);
 152                }
 153                break;
 154        case VIDEO_BPP32:
 155                if (CONFIG_IS_ENABLED(VIDEO_BPP32)) {
 156                        return (colors[idx].r << 16) |
 157                               (colors[idx].g <<  8) |
 158                               (colors[idx].b <<  0);
 159                }
 160                break;
 161        default:
 162                break;
 163        }
 164
 165        /*
 166         * For unknown bit arrangements just support
 167         * black and white.
 168         */
 169        if (idx)
 170                return 0xffffff; /* white */
 171
 172        return 0x000000; /* black */
 173}
 174
 175static char *parsenum(char *s, int *num)
 176{
 177        char *end;
 178        *num = simple_strtol(s, &end, 10);
 179        return end;
 180}
 181
 182/**
 183 * set_cursor_position() - set cursor position
 184 *
 185 * @priv:       private data of the video console
 186 * @row:        new row
 187 * @col:        new column
 188 */
 189static void set_cursor_position(struct vidconsole_priv *priv, int row, int col)
 190{
 191        /*
 192         * Ensure we stay in the bounds of the screen.
 193         */
 194        if (row >= priv->rows)
 195                row = priv->rows - 1;
 196        if (col >= priv->cols)
 197                col = priv->cols - 1;
 198
 199        priv->ycur = row * priv->y_charsize;
 200        priv->xcur_frac = priv->xstart_frac +
 201                          VID_TO_POS(col * priv->x_charsize);
 202}
 203
 204/**
 205 * get_cursor_position() - get cursor position
 206 *
 207 * @priv:       private data of the video console
 208 * @row:        row
 209 * @col:        column
 210 */
 211static void get_cursor_position(struct vidconsole_priv *priv,
 212                                int *row, int *col)
 213{
 214        *row = priv->ycur / priv->y_charsize;
 215        *col = VID_TO_PIXEL(priv->xcur_frac - priv->xstart_frac) /
 216               priv->x_charsize;
 217}
 218
 219/*
 220 * Process a character while accumulating an escape string.  Chars are
 221 * accumulated into escape_buf until the end of escape sequence is
 222 * found, at which point the sequence is parsed and processed.
 223 */
 224static void vidconsole_escape_char(struct udevice *dev, char ch)
 225{
 226        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 227
 228        if (!IS_ENABLED(CONFIG_VIDEO_ANSI))
 229                goto error;
 230
 231        /* Sanity checking for bogus ESC sequences: */
 232        if (priv->escape_len >= sizeof(priv->escape_buf))
 233                goto error;
 234        if (priv->escape_len == 0) {
 235                switch (ch) {
 236                case '7':
 237                        /* Save cursor position */
 238                        get_cursor_position(priv, &priv->row_saved,
 239                                            &priv->col_saved);
 240                        priv->escape = 0;
 241
 242                        return;
 243                case '8': {
 244                        /* Restore cursor position */
 245                        int row = priv->row_saved;
 246                        int col = priv->col_saved;
 247
 248                        set_cursor_position(priv, row, col);
 249                        priv->escape = 0;
 250                        return;
 251                }
 252                case '[':
 253                        break;
 254                default:
 255                        goto error;
 256                }
 257        }
 258
 259        priv->escape_buf[priv->escape_len++] = ch;
 260
 261        /*
 262         * Escape sequences are terminated by a letter, so keep
 263         * accumulating until we get one:
 264         */
 265        if (!isalpha(ch))
 266                return;
 267
 268        /*
 269         * clear escape mode first, otherwise things will get highly
 270         * surprising if you hit any debug prints that come back to
 271         * this console.
 272         */
 273        priv->escape = 0;
 274
 275        switch (ch) {
 276        case 'A':
 277        case 'B':
 278        case 'C':
 279        case 'D':
 280        case 'E':
 281        case 'F': {
 282                int row, col, num;
 283                char *s = priv->escape_buf;
 284
 285                /*
 286                 * Cursor up/down: [%dA, [%dB, [%dE, [%dF
 287                 * Cursor left/right: [%dD, [%dC
 288                 */
 289                s++;    /* [ */
 290                s = parsenum(s, &num);
 291                if (num == 0)                   /* No digit in sequence ... */
 292                        num = 1;                /* ... means "move by 1". */
 293
 294                get_cursor_position(priv, &row, &col);
 295                if (ch == 'A' || ch == 'F')
 296                        row -= num;
 297                if (ch == 'C')
 298                        col += num;
 299                if (ch == 'D')
 300                        col -= num;
 301                if (ch == 'B' || ch == 'E')
 302                        row += num;
 303                if (ch == 'E' || ch == 'F')
 304                        col = 0;
 305                if (col < 0)
 306                        col = 0;
 307                if (row < 0)
 308                        row = 0;
 309                /* Right and bottom overflows are handled in the callee. */
 310                set_cursor_position(priv, row, col);
 311                break;
 312        }
 313        case 'H':
 314        case 'f': {
 315                int row, col;
 316                char *s = priv->escape_buf;
 317
 318                /*
 319                 * Set cursor position: [%d;%df or [%d;%dH
 320                 */
 321                s++;    /* [ */
 322                s = parsenum(s, &row);
 323                s++;    /* ; */
 324                s = parsenum(s, &col);
 325
 326                /*
 327                 * Video origin is [0, 0], terminal origin is [1, 1].
 328                 */
 329                if (row)
 330                        --row;
 331                if (col)
 332                        --col;
 333
 334                set_cursor_position(priv, row, col);
 335
 336                break;
 337        }
 338        case 'J': {
 339                int mode;
 340
 341                /*
 342                 * Clear part/all screen:
 343                 *   [J or [0J - clear screen from cursor down
 344                 *   [1J       - clear screen from cursor up
 345                 *   [2J       - clear entire screen
 346                 *
 347                 * TODO we really only handle entire-screen case, others
 348                 * probably require some additions to video-uclass (and
 349                 * are not really needed yet by efi_console)
 350                 */
 351                parsenum(priv->escape_buf + 1, &mode);
 352
 353                if (mode == 2) {
 354                        int ret;
 355
 356                        video_clear(dev->parent);
 357                        ret = video_sync(dev->parent, false);
 358                        if (ret) {
 359#ifdef DEBUG
 360                                console_puts_select_stderr(true, "[vc err: video_sync]");
 361#endif
 362                        }
 363                        priv->ycur = 0;
 364                        priv->xcur_frac = priv->xstart_frac;
 365                } else {
 366                        debug("unsupported clear mode: %d\n", mode);
 367                }
 368                break;
 369        }
 370        case 'K': {
 371                struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
 372                int mode;
 373
 374                /*
 375                 * Clear (parts of) current line
 376                 *   [0K       - clear line to end
 377                 *   [2K       - clear entire line
 378                 */
 379                parsenum(priv->escape_buf + 1, &mode);
 380
 381                if (mode == 2) {
 382                        int row, col;
 383
 384                        get_cursor_position(priv, &row, &col);
 385                        vidconsole_set_row(dev, row, vid_priv->colour_bg);
 386                }
 387                break;
 388        }
 389        case 'm': {
 390                struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
 391                char *s = priv->escape_buf;
 392                char *end = &priv->escape_buf[priv->escape_len];
 393
 394                /*
 395                 * Set graphics mode: [%d;...;%dm
 396                 *
 397                 * Currently only supports the color attributes:
 398                 *
 399                 * Foreground Colors:
 400                 *
 401                 *   30 Black
 402                 *   31 Red
 403                 *   32 Green
 404                 *   33 Yellow
 405                 *   34 Blue
 406                 *   35 Magenta
 407                 *   36 Cyan
 408                 *   37 White
 409                 *
 410                 * Background Colors:
 411                 *
 412                 *   40 Black
 413                 *   41 Red
 414                 *   42 Green
 415                 *   43 Yellow
 416                 *   44 Blue
 417                 *   45 Magenta
 418                 *   46 Cyan
 419                 *   47 White
 420                 */
 421
 422                s++;    /* [ */
 423                while (s < end) {
 424                        int val;
 425
 426                        s = parsenum(s, &val);
 427                        s++;
 428
 429                        switch (val) {
 430                        case 0:
 431                                /* all attributes off */
 432                                video_set_default_colors(dev->parent, false);
 433                                break;
 434                        case 1:
 435                                /* bold */
 436                                vid_priv->fg_col_idx |= 8;
 437                                vid_priv->colour_fg = vid_console_color(
 438                                                vid_priv, vid_priv->fg_col_idx);
 439                                break;
 440                        case 7:
 441                                /* reverse video */
 442                                vid_priv->colour_fg = vid_console_color(
 443                                                vid_priv, vid_priv->bg_col_idx);
 444                                vid_priv->colour_bg = vid_console_color(
 445                                                vid_priv, vid_priv->fg_col_idx);
 446                                break;
 447                        case 30 ... 37:
 448                                /* foreground color */
 449                                vid_priv->fg_col_idx &= ~7;
 450                                vid_priv->fg_col_idx |= val - 30;
 451                                vid_priv->colour_fg = vid_console_color(
 452                                                vid_priv, vid_priv->fg_col_idx);
 453                                break;
 454                        case 40 ... 47:
 455                                /* background color, also mask the bold bit */
 456                                vid_priv->bg_col_idx &= ~0xf;
 457                                vid_priv->bg_col_idx |= val - 40;
 458                                vid_priv->colour_bg = vid_console_color(
 459                                                vid_priv, vid_priv->bg_col_idx);
 460                                break;
 461                        default:
 462                                /* ignore unsupported SGR parameter */
 463                                break;
 464                        }
 465                }
 466
 467                break;
 468        }
 469        default:
 470                debug("unrecognized escape sequence: %*s\n",
 471                      priv->escape_len, priv->escape_buf);
 472        }
 473
 474        return;
 475
 476error:
 477        /* something went wrong, just revert to normal mode: */
 478        priv->escape = 0;
 479}
 480
 481/* Put that actual character on the screen (using the CP437 code page). */
 482static int vidconsole_output_glyph(struct udevice *dev, char ch)
 483{
 484        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 485        int ret;
 486
 487        /*
 488         * Failure of this function normally indicates an unsupported
 489         * colour depth. Check this and return an error to help with
 490         * diagnosis.
 491         */
 492        ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
 493        if (ret == -EAGAIN) {
 494                vidconsole_newline(dev);
 495                ret = vidconsole_putc_xy(dev, priv->xcur_frac, priv->ycur, ch);
 496        }
 497        if (ret < 0)
 498                return ret;
 499        priv->xcur_frac += ret;
 500        priv->last_ch = ch;
 501        if (priv->xcur_frac >= priv->xsize_frac)
 502                vidconsole_newline(dev);
 503
 504        return 0;
 505}
 506
 507int vidconsole_put_char(struct udevice *dev, char ch)
 508{
 509        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 510        int ret;
 511
 512        if (priv->escape) {
 513                vidconsole_escape_char(dev, ch);
 514                return 0;
 515        }
 516
 517        switch (ch) {
 518        case '\x1b':
 519                priv->escape_len = 0;
 520                priv->escape = 1;
 521                break;
 522        case '\a':
 523                /* beep */
 524                break;
 525        case '\r':
 526                priv->xcur_frac = priv->xstart_frac;
 527                break;
 528        case '\n':
 529                vidconsole_newline(dev);
 530                vidconsole_entry_start(dev);
 531                break;
 532        case '\t':      /* Tab (8 chars alignment) */
 533                priv->xcur_frac = ((priv->xcur_frac / priv->tab_width_frac)
 534                                + 1) * priv->tab_width_frac;
 535
 536                if (priv->xcur_frac >= priv->xsize_frac)
 537                        vidconsole_newline(dev);
 538                break;
 539        case '\b':
 540                vidconsole_back(dev);
 541                priv->last_ch = 0;
 542                break;
 543        default:
 544                ret = vidconsole_output_glyph(dev, ch);
 545                if (ret < 0)
 546                        return ret;
 547                break;
 548        }
 549
 550        return 0;
 551}
 552
 553int vidconsole_put_string(struct udevice *dev, const char *str)
 554{
 555        const char *s;
 556        int ret;
 557
 558        for (s = str; *s; s++) {
 559                ret = vidconsole_put_char(dev, *s);
 560                if (ret)
 561                        return ret;
 562        }
 563
 564        return 0;
 565}
 566
 567static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
 568{
 569        struct udevice *dev = sdev->priv;
 570        int ret;
 571
 572        ret = vidconsole_put_char(dev, ch);
 573        if (ret) {
 574#ifdef DEBUG
 575                console_puts_select_stderr(true, "[vc err: putc]");
 576#endif
 577        }
 578        ret = video_sync(dev->parent, false);
 579        if (ret) {
 580#ifdef DEBUG
 581                console_puts_select_stderr(true, "[vc err: video_sync]");
 582#endif
 583        }
 584}
 585
 586static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
 587{
 588        struct udevice *dev = sdev->priv;
 589        int ret;
 590
 591        ret = vidconsole_put_string(dev, s);
 592        if (ret) {
 593#ifdef DEBUG
 594                char str[30];
 595
 596                snprintf(str, sizeof(str), "[vc err: puts %d]", ret);
 597                console_puts_select_stderr(true, str);
 598#endif
 599        }
 600        ret = video_sync(dev->parent, false);
 601        if (ret) {
 602#ifdef DEBUG
 603                console_puts_select_stderr(true, "[vc err: video_sync]");
 604#endif
 605        }
 606}
 607
 608/* Set up the number of rows and colours (rotated drivers override this) */
 609static int vidconsole_pre_probe(struct udevice *dev)
 610{
 611        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 612        struct udevice *vid = dev->parent;
 613        struct video_priv *vid_priv = dev_get_uclass_priv(vid);
 614
 615        priv->xsize_frac = VID_TO_POS(vid_priv->xsize);
 616
 617        return 0;
 618}
 619
 620/* Register the device with stdio */
 621static int vidconsole_post_probe(struct udevice *dev)
 622{
 623        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 624        struct stdio_dev *sdev = &priv->sdev;
 625
 626        if (!priv->tab_width_frac)
 627                priv->tab_width_frac = VID_TO_POS(priv->x_charsize) * 8;
 628
 629        if (dev->seq) {
 630                snprintf(sdev->name, sizeof(sdev->name), "vidconsole%d",
 631                         dev->seq);
 632        } else {
 633                strcpy(sdev->name, "vidconsole");
 634        }
 635
 636        sdev->flags = DEV_FLAGS_OUTPUT;
 637        sdev->putc = vidconsole_putc;
 638        sdev->puts = vidconsole_puts;
 639        sdev->priv = dev;
 640
 641        return stdio_register(sdev);
 642}
 643
 644UCLASS_DRIVER(vidconsole) = {
 645        .id             = UCLASS_VIDEO_CONSOLE,
 646        .name           = "vidconsole0",
 647        .pre_probe      = vidconsole_pre_probe,
 648        .post_probe     = vidconsole_post_probe,
 649        .per_device_auto_alloc_size     = sizeof(struct vidconsole_priv),
 650};
 651
 652#ifdef CONFIG_VIDEO_COPY
 653int vidconsole_sync_copy(struct udevice *dev, void *from, void *to)
 654{
 655        struct udevice *vid = dev_get_parent(dev);
 656
 657        return video_sync_copy(vid, from, to);
 658}
 659
 660int vidconsole_memmove(struct udevice *dev, void *dst, const void *src,
 661                       int size)
 662{
 663        memmove(dst, src, size);
 664        return vidconsole_sync_copy(dev, dst, dst + size);
 665}
 666#endif
 667
 668#if CONFIG_IS_ENABLED(CMD_VIDCONSOLE)
 669void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
 670{
 671        struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
 672        struct udevice *vid_dev = dev->parent;
 673        struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
 674
 675        col *= priv->x_charsize;
 676        row *= priv->y_charsize;
 677        priv->xcur_frac = VID_TO_POS(min_t(short, col, vid_priv->xsize - 1));
 678        priv->xstart_frac = priv->xcur_frac;
 679        priv->ycur = min_t(short, row, vid_priv->ysize - 1);
 680}
 681
 682static int do_video_setcursor(struct cmd_tbl *cmdtp, int flag, int argc,
 683                              char *const argv[])
 684{
 685        unsigned int col, row;
 686        struct udevice *dev;
 687
 688        if (argc != 3)
 689                return CMD_RET_USAGE;
 690
 691        if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
 692                return CMD_RET_FAILURE;
 693        col = simple_strtoul(argv[1], NULL, 10);
 694        row = simple_strtoul(argv[2], NULL, 10);
 695        vidconsole_position_cursor(dev, col, row);
 696
 697        return 0;
 698}
 699
 700static int do_video_puts(struct cmd_tbl *cmdtp, int flag, int argc,
 701                         char *const argv[])
 702{
 703        struct udevice *dev;
 704        const char *s;
 705
 706        if (argc != 2)
 707                return CMD_RET_USAGE;
 708
 709        if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
 710                return CMD_RET_FAILURE;
 711        for (s = argv[1]; *s; s++)
 712                vidconsole_put_char(dev, *s);
 713
 714        return video_sync(dev->parent, false);
 715}
 716
 717U_BOOT_CMD(
 718        setcurs, 3,     1,      do_video_setcursor,
 719        "set cursor position within screen",
 720        "    <col> <row> in character"
 721);
 722
 723U_BOOT_CMD(
 724        lcdputs, 2,     1,      do_video_puts,
 725        "print string on video framebuffer",
 726        "    <string>"
 727);
 728#endif /* CONFIG_IS_ENABLED(CMD_VIDCONSOLE) */
 729