uboot/lib/efi_loader/efi_gop.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *  EFI application disk support
   4 *
   5 *  Copyright (c) 2016 Alexander Graf
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <efi_loader.h>
  11#include <lcd.h>
  12#include <malloc.h>
  13#include <video.h>
  14
  15DECLARE_GLOBAL_DATA_PTR;
  16
  17static const efi_guid_t efi_gop_guid = EFI_GOP_GUID;
  18
  19/**
  20 * struct efi_gop_obj - graphical output protocol object
  21 *
  22 * @header:     EFI object header
  23 * @ops:        graphical output protocol interface
  24 * @info:       graphical output mode information
  25 * @mode:       graphical output mode
  26 * @bpix:       bits per pixel
  27 * @fb:         frame buffer
  28 */
  29struct efi_gop_obj {
  30        struct efi_object header;
  31        struct efi_gop ops;
  32        struct efi_gop_mode_info info;
  33        struct efi_gop_mode mode;
  34        /* Fields we only have access to during init */
  35        u32 bpix;
  36        void *fb;
  37};
  38
  39static efi_status_t EFIAPI gop_query_mode(struct efi_gop *this, u32 mode_number,
  40                                          efi_uintn_t *size_of_info,
  41                                          struct efi_gop_mode_info **info)
  42{
  43        struct efi_gop_obj *gopobj;
  44
  45        EFI_ENTRY("%p, %x, %p, %p", this, mode_number, size_of_info, info);
  46
  47        gopobj = container_of(this, struct efi_gop_obj, ops);
  48        *size_of_info = sizeof(gopobj->info);
  49        *info = &gopobj->info;
  50
  51        return EFI_EXIT(EFI_SUCCESS);
  52}
  53
  54static efi_status_t EFIAPI gop_set_mode(struct efi_gop *this, u32 mode_number)
  55{
  56        EFI_ENTRY("%p, %x", this, mode_number);
  57
  58        if (mode_number != 0)
  59                return EFI_EXIT(EFI_INVALID_PARAMETER);
  60
  61        return EFI_EXIT(EFI_SUCCESS);
  62}
  63
  64static __always_inline struct efi_gop_pixel efi_vid16_to_blt_col(u16 vid)
  65{
  66        struct efi_gop_pixel blt = {
  67                .reserved = 0,
  68        };
  69
  70        blt.blue  = (vid & 0x1f) << 3;
  71        vid >>= 5;
  72        blt.green = (vid & 0x3f) << 2;
  73        vid >>= 6;
  74        blt.red   = (vid & 0x1f) << 3;
  75        return blt;
  76}
  77
  78static __always_inline u16 efi_blt_col_to_vid16(struct efi_gop_pixel *blt)
  79{
  80        return (u16)(blt->red   >> 3) << 11 |
  81               (u16)(blt->green >> 2) <<  5 |
  82               (u16)(blt->blue  >> 3);
  83}
  84
  85static __always_inline efi_status_t gop_blt_int(struct efi_gop *this,
  86                                                struct efi_gop_pixel *bufferp,
  87                                                u32 operation, efi_uintn_t sx,
  88                                                efi_uintn_t sy, efi_uintn_t dx,
  89                                                efi_uintn_t dy,
  90                                                efi_uintn_t width,
  91                                                efi_uintn_t height,
  92                                                efi_uintn_t delta,
  93                                                efi_uintn_t vid_bpp)
  94{
  95        struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
  96        efi_uintn_t i, j, linelen, slineoff = 0, dlineoff, swidth, dwidth;
  97        u32 *fb32 = gopobj->fb;
  98        u16 *fb16 = gopobj->fb;
  99        struct efi_gop_pixel *buffer = __builtin_assume_aligned(bufferp, 4);
 100
 101        if (delta) {
 102                /* Check for 4 byte alignment */
 103                if (delta & 3)
 104                        return EFI_INVALID_PARAMETER;
 105                linelen = delta >> 2;
 106        } else {
 107                linelen = width;
 108        }
 109
 110        /* Check source rectangle */
 111        switch (operation) {
 112        case EFI_BLT_VIDEO_FILL:
 113                break;
 114        case EFI_BLT_BUFFER_TO_VIDEO:
 115                if (sx + width > linelen)
 116                        return EFI_INVALID_PARAMETER;
 117                break;
 118        case EFI_BLT_VIDEO_TO_BLT_BUFFER:
 119        case EFI_BLT_VIDEO_TO_VIDEO:
 120                if (sx + width > gopobj->info.width ||
 121                    sy + height > gopobj->info.height)
 122                        return EFI_INVALID_PARAMETER;
 123                break;
 124        default:
 125                return EFI_INVALID_PARAMETER;
 126        }
 127
 128        /* Check destination rectangle */
 129        switch (operation) {
 130        case EFI_BLT_VIDEO_FILL:
 131        case EFI_BLT_BUFFER_TO_VIDEO:
 132        case EFI_BLT_VIDEO_TO_VIDEO:
 133                if (dx + width > gopobj->info.width ||
 134                    dy + height > gopobj->info.height)
 135                        return EFI_INVALID_PARAMETER;
 136                break;
 137        case EFI_BLT_VIDEO_TO_BLT_BUFFER:
 138                if (dx + width > linelen)
 139                        return EFI_INVALID_PARAMETER;
 140                break;
 141        }
 142
 143        /* Calculate line width */
 144        switch (operation) {
 145        case EFI_BLT_BUFFER_TO_VIDEO:
 146                swidth = linelen;
 147                break;
 148        case EFI_BLT_VIDEO_TO_BLT_BUFFER:
 149        case EFI_BLT_VIDEO_TO_VIDEO:
 150                swidth = gopobj->info.width;
 151                if (!vid_bpp)
 152                        return EFI_UNSUPPORTED;
 153                break;
 154        case EFI_BLT_VIDEO_FILL:
 155                swidth = 0;
 156                break;
 157        }
 158
 159        switch (operation) {
 160        case EFI_BLT_BUFFER_TO_VIDEO:
 161        case EFI_BLT_VIDEO_FILL:
 162        case EFI_BLT_VIDEO_TO_VIDEO:
 163                dwidth = gopobj->info.width;
 164                if (!vid_bpp)
 165                        return EFI_UNSUPPORTED;
 166                break;
 167        case EFI_BLT_VIDEO_TO_BLT_BUFFER:
 168                dwidth = linelen;
 169                break;
 170        }
 171
 172        slineoff = swidth * sy;
 173        dlineoff = dwidth * dy;
 174        for (i = 0; i < height; i++) {
 175                for (j = 0; j < width; j++) {
 176                        struct efi_gop_pixel pix;
 177
 178                        /* Read source pixel */
 179                        switch (operation) {
 180                        case EFI_BLT_VIDEO_FILL:
 181                                pix = *buffer;
 182                                break;
 183                        case EFI_BLT_BUFFER_TO_VIDEO:
 184                                pix = buffer[slineoff + j + sx];
 185                                break;
 186                        case EFI_BLT_VIDEO_TO_BLT_BUFFER:
 187                        case EFI_BLT_VIDEO_TO_VIDEO:
 188                                if (vid_bpp == 32)
 189                                        pix = *(struct efi_gop_pixel *)&fb32[
 190                                                slineoff + j + sx];
 191                                else
 192                                        pix = efi_vid16_to_blt_col(fb16[
 193                                                slineoff + j + sx]);
 194                                break;
 195                        }
 196
 197                        /* Write destination pixel */
 198                        switch (operation) {
 199                        case EFI_BLT_VIDEO_TO_BLT_BUFFER:
 200                                buffer[dlineoff + j + dx] = pix;
 201                                break;
 202                        case EFI_BLT_BUFFER_TO_VIDEO:
 203                        case EFI_BLT_VIDEO_FILL:
 204                        case EFI_BLT_VIDEO_TO_VIDEO:
 205                                if (vid_bpp == 32)
 206                                        fb32[dlineoff + j + dx] = *(u32 *)&pix;
 207                                else
 208                                        fb16[dlineoff + j + dx] =
 209                                                efi_blt_col_to_vid16(&pix);
 210                                break;
 211                        }
 212                }
 213                slineoff += swidth;
 214                dlineoff += dwidth;
 215        }
 216
 217        return EFI_SUCCESS;
 218}
 219
 220static efi_uintn_t gop_get_bpp(struct efi_gop *this)
 221{
 222        struct efi_gop_obj *gopobj = container_of(this, struct efi_gop_obj, ops);
 223        efi_uintn_t vid_bpp = 0;
 224
 225        switch (gopobj->bpix) {
 226#ifdef CONFIG_DM_VIDEO
 227        case VIDEO_BPP32:
 228#else
 229        case LCD_COLOR32:
 230#endif
 231                vid_bpp = 32;
 232                break;
 233#ifdef CONFIG_DM_VIDEO
 234        case VIDEO_BPP16:
 235#else
 236        case LCD_COLOR16:
 237#endif
 238                vid_bpp = 16;
 239                break;
 240        }
 241
 242        return vid_bpp;
 243}
 244
 245/*
 246 * GCC can't optimize our BLT function well, but we need to make sure that
 247 * our 2-dimensional loop gets executed very quickly, otherwise the system
 248 * will feel slow.
 249 *
 250 * By manually putting all obvious branch targets into functions which call
 251 * our generic BLT function with constants, the compiler can successfully
 252 * optimize for speed.
 253 */
 254static efi_status_t gop_blt_video_fill(struct efi_gop *this,
 255                                       struct efi_gop_pixel *buffer,
 256                                       u32 foo, efi_uintn_t sx,
 257                                       efi_uintn_t sy, efi_uintn_t dx,
 258                                       efi_uintn_t dy, efi_uintn_t width,
 259                                       efi_uintn_t height, efi_uintn_t delta,
 260                                       efi_uintn_t vid_bpp)
 261{
 262        return gop_blt_int(this, buffer, EFI_BLT_VIDEO_FILL, sx, sy, dx,
 263                           dy, width, height, delta, vid_bpp);
 264}
 265
 266static efi_status_t gop_blt_buf_to_vid16(struct efi_gop *this,
 267                                         struct efi_gop_pixel *buffer,
 268                                         u32 foo, efi_uintn_t sx,
 269                                         efi_uintn_t sy, efi_uintn_t dx,
 270                                         efi_uintn_t dy, efi_uintn_t width,
 271                                         efi_uintn_t height, efi_uintn_t delta)
 272{
 273        return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
 274                           dy, width, height, delta, 16);
 275}
 276
 277static efi_status_t gop_blt_buf_to_vid32(struct efi_gop *this,
 278                                         struct efi_gop_pixel *buffer,
 279                                         u32 foo, efi_uintn_t sx,
 280                                         efi_uintn_t sy, efi_uintn_t dx,
 281                                         efi_uintn_t dy, efi_uintn_t width,
 282                                         efi_uintn_t height, efi_uintn_t delta)
 283{
 284        return gop_blt_int(this, buffer, EFI_BLT_BUFFER_TO_VIDEO, sx, sy, dx,
 285                           dy, width, height, delta, 32);
 286}
 287
 288static efi_status_t gop_blt_vid_to_vid(struct efi_gop *this,
 289                                       struct efi_gop_pixel *buffer,
 290                                       u32 foo, efi_uintn_t sx,
 291                                       efi_uintn_t sy, efi_uintn_t dx,
 292                                       efi_uintn_t dy, efi_uintn_t width,
 293                                       efi_uintn_t height, efi_uintn_t delta,
 294                                       efi_uintn_t vid_bpp)
 295{
 296        return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_VIDEO, sx, sy, dx,
 297                           dy, width, height, delta, vid_bpp);
 298}
 299
 300static efi_status_t gop_blt_vid_to_buf(struct efi_gop *this,
 301                                       struct efi_gop_pixel *buffer,
 302                                       u32 foo, efi_uintn_t sx,
 303                                       efi_uintn_t sy, efi_uintn_t dx,
 304                                       efi_uintn_t dy, efi_uintn_t width,
 305                                       efi_uintn_t height, efi_uintn_t delta,
 306                                       efi_uintn_t vid_bpp)
 307{
 308        return gop_blt_int(this, buffer, EFI_BLT_VIDEO_TO_BLT_BUFFER, sx, sy,
 309                           dx, dy, width, height, delta, vid_bpp);
 310}
 311
 312/*
 313 * Copy rectangle.
 314 *
 315 * This function implements the Blt service of the EFI_GRAPHICS_OUTPUT_PROTOCOL.
 316 * See the Unified Extensible Firmware Interface (UEFI) specification for
 317 * details.
 318 *
 319 * @this:       EFI_GRAPHICS_OUTPUT_PROTOCOL
 320 * @buffer:     pixel buffer
 321 * @sx:         source x-coordinate
 322 * @sy:         source y-coordinate
 323 * @dx:         destination x-coordinate
 324 * @dy:         destination y-coordinate
 325 * @width:      width of rectangle
 326 * @height:     height of rectangle
 327 * @delta:      length in bytes of a line in the pixel buffer (optional)
 328 * @return:     status code
 329 */
 330efi_status_t EFIAPI gop_blt(struct efi_gop *this, struct efi_gop_pixel *buffer,
 331                            u32 operation, efi_uintn_t sx,
 332                            efi_uintn_t sy, efi_uintn_t dx,
 333                            efi_uintn_t dy, efi_uintn_t width,
 334                            efi_uintn_t height, efi_uintn_t delta)
 335{
 336        efi_status_t ret = EFI_INVALID_PARAMETER;
 337        efi_uintn_t vid_bpp;
 338
 339        EFI_ENTRY("%p, %p, %u, %zu, %zu, %zu, %zu, %zu, %zu, %zu", this,
 340                  buffer, operation, sx, sy, dx, dy, width, height, delta);
 341
 342        vid_bpp = gop_get_bpp(this);
 343
 344        /* Allow for compiler optimization */
 345        switch (operation) {
 346        case EFI_BLT_VIDEO_FILL:
 347                ret = gop_blt_video_fill(this, buffer, operation, sx, sy, dx,
 348                                         dy, width, height, delta, vid_bpp);
 349                break;
 350        case EFI_BLT_BUFFER_TO_VIDEO:
 351                /* This needs to be super-fast, so duplicate for 16/32bpp */
 352                if (vid_bpp == 32)
 353                        ret = gop_blt_buf_to_vid32(this, buffer, operation, sx,
 354                                                   sy, dx, dy, width, height,
 355                                                   delta);
 356                else
 357                        ret = gop_blt_buf_to_vid16(this, buffer, operation, sx,
 358                                                   sy, dx, dy, width, height,
 359                                                   delta);
 360                break;
 361        case EFI_BLT_VIDEO_TO_VIDEO:
 362                ret = gop_blt_vid_to_vid(this, buffer, operation, sx, sy, dx,
 363                                         dy, width, height, delta, vid_bpp);
 364                break;
 365        case EFI_BLT_VIDEO_TO_BLT_BUFFER:
 366                ret = gop_blt_vid_to_buf(this, buffer, operation, sx, sy, dx,
 367                                         dy, width, height, delta, vid_bpp);
 368                break;
 369        default:
 370                ret = EFI_UNSUPPORTED;
 371        }
 372
 373        if (ret != EFI_SUCCESS)
 374                return EFI_EXIT(ret);
 375
 376#ifdef CONFIG_DM_VIDEO
 377        video_sync_all();
 378#else
 379        lcd_sync();
 380#endif
 381
 382        return EFI_EXIT(EFI_SUCCESS);
 383}
 384
 385/*
 386 * Install graphical output protocol.
 387 *
 388 * If no supported video device exists this is not considered as an
 389 * error.
 390 */
 391efi_status_t efi_gop_register(void)
 392{
 393        struct efi_gop_obj *gopobj;
 394        u32 bpix, col, row;
 395        u64 fb_base, fb_size;
 396        void *fb;
 397        efi_status_t ret;
 398
 399#ifdef CONFIG_DM_VIDEO
 400        struct udevice *vdev;
 401        struct video_priv *priv;
 402
 403        /* We only support a single video output device for now */
 404        if (uclass_first_device(UCLASS_VIDEO, &vdev) || !vdev) {
 405                debug("WARNING: No video device\n");
 406                return EFI_SUCCESS;
 407        }
 408
 409        priv = dev_get_uclass_priv(vdev);
 410        bpix = priv->bpix;
 411        col = video_get_xsize(vdev);
 412        row = video_get_ysize(vdev);
 413        fb_base = (uintptr_t)priv->fb;
 414        fb_size = priv->fb_size;
 415        fb = priv->fb;
 416#else
 417        int line_len;
 418
 419        bpix = panel_info.vl_bpix;
 420        col = panel_info.vl_col;
 421        row = panel_info.vl_row;
 422        fb_base = gd->fb_base;
 423        fb_size = lcd_get_size(&line_len);
 424        fb = (void*)gd->fb_base;
 425#endif
 426
 427        switch (bpix) {
 428#ifdef CONFIG_DM_VIDEO
 429        case VIDEO_BPP16:
 430        case VIDEO_BPP32:
 431#else
 432        case LCD_COLOR32:
 433        case LCD_COLOR16:
 434#endif
 435                break;
 436        default:
 437                /* So far, we only work in 16 or 32 bit mode */
 438                debug("WARNING: Unsupported video mode\n");
 439                return EFI_SUCCESS;
 440        }
 441
 442        gopobj = calloc(1, sizeof(*gopobj));
 443        if (!gopobj) {
 444                printf("ERROR: Out of memory\n");
 445                return EFI_OUT_OF_RESOURCES;
 446        }
 447
 448        /* Hook up to the device list */
 449        efi_add_handle(&gopobj->header);
 450
 451        /* Fill in object data */
 452        ret = efi_add_protocol(&gopobj->header, &efi_gop_guid,
 453                               &gopobj->ops);
 454        if (ret != EFI_SUCCESS) {
 455                printf("ERROR: Failure adding GOP protocol\n");
 456                return ret;
 457        }
 458        gopobj->ops.query_mode = gop_query_mode;
 459        gopobj->ops.set_mode = gop_set_mode;
 460        gopobj->ops.blt = gop_blt;
 461        gopobj->ops.mode = &gopobj->mode;
 462
 463        gopobj->mode.max_mode = 1;
 464        gopobj->mode.info = &gopobj->info;
 465        gopobj->mode.info_size = sizeof(gopobj->info);
 466
 467#ifdef CONFIG_DM_VIDEO
 468        if (bpix == VIDEO_BPP32)
 469#else
 470        if (bpix == LCD_COLOR32)
 471#endif
 472        {
 473                /*
 474                 * With 32bit color space we can directly expose the frame
 475                 * buffer
 476                 */
 477                gopobj->mode.fb_base = fb_base;
 478                gopobj->mode.fb_size = fb_size;
 479        }
 480
 481        gopobj->info.version = 0;
 482        gopobj->info.width = col;
 483        gopobj->info.height = row;
 484        gopobj->info.pixel_format = EFI_GOT_BGRA8;
 485        gopobj->info.pixels_per_scanline = col;
 486
 487        gopobj->bpix = bpix;
 488        gopobj->fb = fb;
 489
 490        return EFI_SUCCESS;
 491}
 492