linux/drivers/video/fbdev/efifb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Framebuffer driver for EFI/UEFI based system
   4 *
   5 * (c) 2006 Edgar Hucek <gimli@dark-green.com>
   6 * Original efi driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de>
   7 *
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/efi.h>
  12#include <linux/efi-bgrt.h>
  13#include <linux/errno.h>
  14#include <linux/fb.h>
  15#include <linux/pci.h>
  16#include <linux/platform_device.h>
  17#include <linux/printk.h>
  18#include <linux/screen_info.h>
  19#include <video/vga.h>
  20#include <asm/efi.h>
  21#include <drm/drm_utils.h> /* For drm_get_panel_orientation_quirk */
  22#include <drm/drm_connector.h>  /* For DRM_MODE_PANEL_ORIENTATION_* */
  23
  24struct bmp_file_header {
  25        u16 id;
  26        u32 file_size;
  27        u32 reserved;
  28        u32 bitmap_offset;
  29} __packed;
  30
  31struct bmp_dib_header {
  32        u32 dib_header_size;
  33        s32 width;
  34        s32 height;
  35        u16 planes;
  36        u16 bpp;
  37        u32 compression;
  38        u32 bitmap_size;
  39        u32 horz_resolution;
  40        u32 vert_resolution;
  41        u32 colors_used;
  42        u32 colors_important;
  43} __packed;
  44
  45static bool use_bgrt = true;
  46static bool request_mem_succeeded = false;
  47static u64 mem_flags = EFI_MEMORY_WC | EFI_MEMORY_UC;
  48
  49static struct fb_var_screeninfo efifb_defined = {
  50        .activate               = FB_ACTIVATE_NOW,
  51        .height                 = -1,
  52        .width                  = -1,
  53        .right_margin           = 32,
  54        .upper_margin           = 16,
  55        .lower_margin           = 4,
  56        .vsync_len              = 4,
  57        .vmode                  = FB_VMODE_NONINTERLACED,
  58};
  59
  60static struct fb_fix_screeninfo efifb_fix = {
  61        .id                     = "EFI VGA",
  62        .type                   = FB_TYPE_PACKED_PIXELS,
  63        .accel                  = FB_ACCEL_NONE,
  64        .visual                 = FB_VISUAL_TRUECOLOR,
  65};
  66
  67static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
  68                           unsigned blue, unsigned transp,
  69                           struct fb_info *info)
  70{
  71        /*
  72         *  Set a single color register. The values supplied are
  73         *  already rounded down to the hardware's capabilities
  74         *  (according to the entries in the `var' structure). Return
  75         *  != 0 for invalid regno.
  76         */
  77
  78        if (regno >= info->cmap.len)
  79                return 1;
  80
  81        if (regno < 16) {
  82                red   >>= 16 - info->var.red.length;
  83                green >>= 16 - info->var.green.length;
  84                blue  >>= 16 - info->var.blue.length;
  85                ((u32 *)(info->pseudo_palette))[regno] =
  86                        (red   << info->var.red.offset)   |
  87                        (green << info->var.green.offset) |
  88                        (blue  << info->var.blue.offset);
  89        }
  90        return 0;
  91}
  92
  93/*
  94 * If fbcon deffered console takeover is configured, the intent is for the
  95 * framebuffer to show the boot graphics (e.g. vendor logo) until there is some
  96 * (error) message to display. But the boot graphics may have been destroyed by
  97 * e.g. option ROM output, detect this and restore the boot graphics.
  98 */
  99#if defined CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER && \
 100    defined CONFIG_ACPI_BGRT
 101static void efifb_copy_bmp(u8 *src, u32 *dst, int width, struct screen_info *si)
 102{
 103        u8 r, g, b;
 104
 105        while (width--) {
 106                b = *src++;
 107                g = *src++;
 108                r = *src++;
 109                *dst++ = (r << si->red_pos)   |
 110                         (g << si->green_pos) |
 111                         (b << si->blue_pos);
 112        }
 113}
 114
 115#ifdef CONFIG_X86
 116/*
 117 * On x86 some firmwares use a low non native resolution for the display when
 118 * they have shown some text messages. While keeping the bgrt filled with info
 119 * for the native resolution. If the bgrt image intended for the native
 120 * resolution still fits, it will be displayed very close to the right edge of
 121 * the display looking quite bad. This function checks for this.
 122 */
 123static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
 124{
 125        static const int default_resolutions[][2] = {
 126                {  800,  600 },
 127                { 1024,  768 },
 128                { 1280, 1024 },
 129        };
 130        u32 i, right_margin;
 131
 132        for (i = 0; i < ARRAY_SIZE(default_resolutions); i++) {
 133                if (default_resolutions[i][0] == si->lfb_width &&
 134                    default_resolutions[i][1] == si->lfb_height)
 135                        break;
 136        }
 137        /* If not a default resolution used for textmode, this should be fine */
 138        if (i >= ARRAY_SIZE(default_resolutions))
 139                return true;
 140
 141        /* If the right margin is 5 times smaller then the left one, reject */
 142        right_margin = si->lfb_width - (bgrt_tab.image_offset_x + bmp_width);
 143        if (right_margin < (bgrt_tab.image_offset_x / 5))
 144                return false;
 145
 146        return true;
 147}
 148#else
 149static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width)
 150{
 151        return true;
 152}
 153#endif
 154
 155static void efifb_show_boot_graphics(struct fb_info *info)
 156{
 157        u32 bmp_width, bmp_height, bmp_pitch, screen_pitch, dst_x, y, src_y;
 158        struct screen_info *si = &screen_info;
 159        struct bmp_file_header *file_header;
 160        struct bmp_dib_header *dib_header;
 161        void *bgrt_image = NULL;
 162        u8 *dst = info->screen_base;
 163
 164        if (!use_bgrt)
 165                return;
 166
 167        if (!bgrt_tab.image_address) {
 168                pr_info("efifb: No BGRT, not showing boot graphics\n");
 169                return;
 170        }
 171
 172        if (bgrt_tab.status & 0x06) {
 173                pr_info("efifb: BGRT rotation bits set, not showing boot graphics\n");
 174                return;
 175        }
 176
 177        /* Avoid flashing the logo if we're going to print std probe messages */
 178        if (console_loglevel > CONSOLE_LOGLEVEL_QUIET)
 179                return;
 180
 181        /* bgrt_tab.status is unreliable, so we don't check it */
 182
 183        if (si->lfb_depth != 32) {
 184                pr_info("efifb: not 32 bits, not showing boot graphics\n");
 185                return;
 186        }
 187
 188        bgrt_image = memremap(bgrt_tab.image_address, bgrt_image_size,
 189                              MEMREMAP_WB);
 190        if (!bgrt_image) {
 191                pr_warn("efifb: Ignoring BGRT: failed to map image memory\n");
 192                return;
 193        }
 194
 195        if (bgrt_image_size < (sizeof(*file_header) + sizeof(*dib_header)))
 196                goto error;
 197
 198        file_header = bgrt_image;
 199        if (file_header->id != 0x4d42 || file_header->reserved != 0)
 200                goto error;
 201
 202        dib_header = bgrt_image + sizeof(*file_header);
 203        if (dib_header->dib_header_size != 40 || dib_header->width < 0 ||
 204            dib_header->planes != 1 || dib_header->bpp != 24 ||
 205            dib_header->compression != 0)
 206                goto error;
 207
 208        bmp_width = dib_header->width;
 209        bmp_height = abs(dib_header->height);
 210        bmp_pitch = round_up(3 * bmp_width, 4);
 211        screen_pitch = si->lfb_linelength;
 212
 213        if ((file_header->bitmap_offset + bmp_pitch * bmp_height) >
 214                                bgrt_image_size)
 215                goto error;
 216
 217        if ((bgrt_tab.image_offset_x + bmp_width) > si->lfb_width ||
 218            (bgrt_tab.image_offset_y + bmp_height) > si->lfb_height)
 219                goto error;
 220
 221        if (!efifb_bgrt_sanity_check(si, bmp_width))
 222                goto error;
 223
 224        pr_info("efifb: showing boot graphics\n");
 225
 226        for (y = 0; y < si->lfb_height; y++, dst += si->lfb_linelength) {
 227                /* Only background? */
 228                if (y < bgrt_tab.image_offset_y ||
 229                    y >= (bgrt_tab.image_offset_y + bmp_height)) {
 230                        memset(dst, 0, 4 * si->lfb_width);
 231                        continue;
 232                }
 233
 234                src_y = y - bgrt_tab.image_offset_y;
 235                /* Positive header height means upside down row order */
 236                if (dib_header->height > 0)
 237                        src_y = (bmp_height - 1) - src_y;
 238
 239                memset(dst, 0, bgrt_tab.image_offset_x * 4);
 240                dst_x = bgrt_tab.image_offset_x;
 241                efifb_copy_bmp(bgrt_image + file_header->bitmap_offset +
 242                                            src_y * bmp_pitch,
 243                               (u32 *)dst + dst_x, bmp_width, si);
 244                dst_x += bmp_width;
 245                memset((u32 *)dst + dst_x, 0, (si->lfb_width - dst_x) * 4);
 246        }
 247
 248        memunmap(bgrt_image);
 249        return;
 250
 251error:
 252        memunmap(bgrt_image);
 253        pr_warn("efifb: Ignoring BGRT: unexpected or invalid BMP data\n");
 254}
 255#else
 256static inline void efifb_show_boot_graphics(struct fb_info *info) {}
 257#endif
 258
 259static void efifb_destroy(struct fb_info *info)
 260{
 261        if (info->screen_base) {
 262                if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
 263                        iounmap(info->screen_base);
 264                else
 265                        memunmap(info->screen_base);
 266        }
 267        if (request_mem_succeeded)
 268                release_mem_region(info->apertures->ranges[0].base,
 269                                   info->apertures->ranges[0].size);
 270        fb_dealloc_cmap(&info->cmap);
 271}
 272
 273static struct fb_ops efifb_ops = {
 274        .owner          = THIS_MODULE,
 275        .fb_destroy     = efifb_destroy,
 276        .fb_setcolreg   = efifb_setcolreg,
 277        .fb_fillrect    = cfb_fillrect,
 278        .fb_copyarea    = cfb_copyarea,
 279        .fb_imageblit   = cfb_imageblit,
 280};
 281
 282static int efifb_setup(char *options)
 283{
 284        char *this_opt;
 285
 286        if (options && *options) {
 287                while ((this_opt = strsep(&options, ",")) != NULL) {
 288                        if (!*this_opt) continue;
 289
 290                        efifb_setup_from_dmi(&screen_info, this_opt);
 291
 292                        if (!strncmp(this_opt, "base:", 5))
 293                                screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
 294                        else if (!strncmp(this_opt, "stride:", 7))
 295                                screen_info.lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
 296                        else if (!strncmp(this_opt, "height:", 7))
 297                                screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0);
 298                        else if (!strncmp(this_opt, "width:", 6))
 299                                screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0);
 300                        else if (!strcmp(this_opt, "nowc"))
 301                                mem_flags &= ~EFI_MEMORY_WC;
 302                        else if (!strcmp(this_opt, "nobgrt"))
 303                                use_bgrt = false;
 304                }
 305        }
 306
 307        return 0;
 308}
 309
 310static inline bool fb_base_is_valid(void)
 311{
 312        if (screen_info.lfb_base)
 313                return true;
 314
 315        if (!(screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE))
 316                return false;
 317
 318        if (screen_info.ext_lfb_base)
 319                return true;
 320
 321        return false;
 322}
 323
 324#define efifb_attr_decl(name, fmt)                                      \
 325static ssize_t name##_show(struct device *dev,                          \
 326                           struct device_attribute *attr,               \
 327                           char *buf)                                   \
 328{                                                                       \
 329        return sprintf(buf, fmt "\n", (screen_info.lfb_##name));        \
 330}                                                                       \
 331static DEVICE_ATTR_RO(name)
 332
 333efifb_attr_decl(base, "0x%x");
 334efifb_attr_decl(linelength, "%u");
 335efifb_attr_decl(height, "%u");
 336efifb_attr_decl(width, "%u");
 337efifb_attr_decl(depth, "%u");
 338
 339static struct attribute *efifb_attrs[] = {
 340        &dev_attr_base.attr,
 341        &dev_attr_linelength.attr,
 342        &dev_attr_width.attr,
 343        &dev_attr_height.attr,
 344        &dev_attr_depth.attr,
 345        NULL
 346};
 347ATTRIBUTE_GROUPS(efifb);
 348
 349static bool pci_dev_disabled;   /* FB base matches BAR of a disabled device */
 350
 351static struct pci_dev *efifb_pci_dev;   /* dev with BAR covering the efifb */
 352static struct resource *bar_resource;
 353static u64 bar_offset;
 354
 355static int efifb_probe(struct platform_device *dev)
 356{
 357        struct fb_info *info;
 358        int err, orientation;
 359        unsigned int size_vmode;
 360        unsigned int size_remap;
 361        unsigned int size_total;
 362        char *option = NULL;
 363        efi_memory_desc_t md;
 364
 365        if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
 366                return -ENODEV;
 367
 368        if (fb_get_options("efifb", &option))
 369                return -ENODEV;
 370        efifb_setup(option);
 371
 372        /* We don't get linelength from UGA Draw Protocol, only from
 373         * EFI Graphics Protocol.  So if it's not in DMI, and it's not
 374         * passed in from the user, we really can't use the framebuffer.
 375         */
 376        if (!screen_info.lfb_linelength)
 377                return -ENODEV;
 378
 379        if (!screen_info.lfb_depth)
 380                screen_info.lfb_depth = 32;
 381        if (!screen_info.pages)
 382                screen_info.pages = 1;
 383        if (!fb_base_is_valid()) {
 384                printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
 385                return -ENODEV;
 386        }
 387        printk(KERN_INFO "efifb: probing for efifb\n");
 388
 389        /* just assume they're all unset if any are */
 390        if (!screen_info.blue_size) {
 391                screen_info.blue_size = 8;
 392                screen_info.blue_pos = 0;
 393                screen_info.green_size = 8;
 394                screen_info.green_pos = 8;
 395                screen_info.red_size = 8;
 396                screen_info.red_pos = 16;
 397                screen_info.rsvd_size = 8;
 398                screen_info.rsvd_pos = 24;
 399        }
 400
 401        efifb_fix.smem_start = screen_info.lfb_base;
 402
 403        if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
 404                u64 ext_lfb_base;
 405
 406                ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
 407                efifb_fix.smem_start |= ext_lfb_base;
 408        }
 409
 410        if (bar_resource &&
 411            bar_resource->start + bar_offset != efifb_fix.smem_start) {
 412                dev_info(&efifb_pci_dev->dev,
 413                         "BAR has moved, updating efifb address\n");
 414                efifb_fix.smem_start = bar_resource->start + bar_offset;
 415        }
 416
 417        efifb_defined.bits_per_pixel = screen_info.lfb_depth;
 418        efifb_defined.xres = screen_info.lfb_width;
 419        efifb_defined.yres = screen_info.lfb_height;
 420        efifb_fix.line_length = screen_info.lfb_linelength;
 421
 422        /*   size_vmode -- that is the amount of memory needed for the
 423         *                 used video mode, i.e. the minimum amount of
 424         *                 memory we need. */
 425        size_vmode = efifb_defined.yres * efifb_fix.line_length;
 426
 427        /*   size_total -- all video memory we have. Used for
 428         *                 entries, ressource allocation and bounds
 429         *                 checking. */
 430        size_total = screen_info.lfb_size;
 431        if (size_total < size_vmode)
 432                size_total = size_vmode;
 433
 434        /*   size_remap -- the amount of video memory we are going to
 435         *                 use for efifb.  With modern cards it is no
 436         *                 option to simply use size_total as that
 437         *                 wastes plenty of kernel address space. */
 438        size_remap  = size_vmode * 2;
 439        if (size_remap > size_total)
 440                size_remap = size_total;
 441        if (size_remap % PAGE_SIZE)
 442                size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
 443        efifb_fix.smem_len = size_remap;
 444
 445        if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
 446                request_mem_succeeded = true;
 447        } else {
 448                /* We cannot make this fatal. Sometimes this comes from magic
 449                   spaces our resource handlers simply don't know about */
 450                pr_warn("efifb: cannot reserve video memory at 0x%lx\n",
 451                        efifb_fix.smem_start);
 452        }
 453
 454        info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
 455        if (!info) {
 456                err = -ENOMEM;
 457                goto err_release_mem;
 458        }
 459        platform_set_drvdata(dev, info);
 460        info->pseudo_palette = info->par;
 461        info->par = NULL;
 462
 463        info->apertures = alloc_apertures(1);
 464        if (!info->apertures) {
 465                err = -ENOMEM;
 466                goto err_release_fb;
 467        }
 468        info->apertures->ranges[0].base = efifb_fix.smem_start;
 469        info->apertures->ranges[0].size = size_remap;
 470
 471        if (efi_enabled(EFI_BOOT) &&
 472            !efi_mem_desc_lookup(efifb_fix.smem_start, &md)) {
 473                if ((efifb_fix.smem_start + efifb_fix.smem_len) >
 474                    (md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT))) {
 475                        pr_err("efifb: video memory @ 0x%lx spans multiple EFI memory regions\n",
 476                               efifb_fix.smem_start);
 477                        err = -EIO;
 478                        goto err_release_fb;
 479                }
 480                /*
 481                 * If the UEFI memory map covers the efifb region, we may only
 482                 * remap it using the attributes the memory map prescribes.
 483                 */
 484                md.attribute &= EFI_MEMORY_UC | EFI_MEMORY_WC |
 485                                EFI_MEMORY_WT | EFI_MEMORY_WB;
 486                if (md.attribute) {
 487                        mem_flags |= EFI_MEMORY_WT | EFI_MEMORY_WB;
 488                        mem_flags &= md.attribute;
 489                }
 490        }
 491        if (mem_flags & EFI_MEMORY_WC)
 492                info->screen_base = ioremap_wc(efifb_fix.smem_start,
 493                                               efifb_fix.smem_len);
 494        else if (mem_flags & EFI_MEMORY_UC)
 495                info->screen_base = ioremap(efifb_fix.smem_start,
 496                                            efifb_fix.smem_len);
 497        else if (mem_flags & EFI_MEMORY_WT)
 498                info->screen_base = memremap(efifb_fix.smem_start,
 499                                             efifb_fix.smem_len, MEMREMAP_WT);
 500        else if (mem_flags & EFI_MEMORY_WB)
 501                info->screen_base = memremap(efifb_fix.smem_start,
 502                                             efifb_fix.smem_len, MEMREMAP_WB);
 503        if (!info->screen_base) {
 504                pr_err("efifb: abort, cannot remap video memory 0x%x @ 0x%lx\n",
 505                        efifb_fix.smem_len, efifb_fix.smem_start);
 506                err = -EIO;
 507                goto err_release_fb;
 508        }
 509
 510        efifb_show_boot_graphics(info);
 511
 512        pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
 513               efifb_fix.smem_start, size_remap/1024, size_total/1024);
 514        pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
 515               efifb_defined.xres, efifb_defined.yres,
 516               efifb_defined.bits_per_pixel, efifb_fix.line_length,
 517               screen_info.pages);
 518
 519        efifb_defined.xres_virtual = efifb_defined.xres;
 520        efifb_defined.yres_virtual = efifb_fix.smem_len /
 521                                        efifb_fix.line_length;
 522        pr_info("efifb: scrolling: redraw\n");
 523        efifb_defined.yres_virtual = efifb_defined.yres;
 524
 525        /* some dummy values for timing to make fbset happy */
 526        efifb_defined.pixclock     = 10000000 / efifb_defined.xres *
 527                                        1000 / efifb_defined.yres;
 528        efifb_defined.left_margin  = (efifb_defined.xres / 8) & 0xf8;
 529        efifb_defined.hsync_len    = (efifb_defined.xres / 8) & 0xf8;
 530
 531        efifb_defined.red.offset    = screen_info.red_pos;
 532        efifb_defined.red.length    = screen_info.red_size;
 533        efifb_defined.green.offset  = screen_info.green_pos;
 534        efifb_defined.green.length  = screen_info.green_size;
 535        efifb_defined.blue.offset   = screen_info.blue_pos;
 536        efifb_defined.blue.length   = screen_info.blue_size;
 537        efifb_defined.transp.offset = screen_info.rsvd_pos;
 538        efifb_defined.transp.length = screen_info.rsvd_size;
 539
 540        pr_info("efifb: %s: "
 541               "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
 542               "Truecolor",
 543               screen_info.rsvd_size,
 544               screen_info.red_size,
 545               screen_info.green_size,
 546               screen_info.blue_size,
 547               screen_info.rsvd_pos,
 548               screen_info.red_pos,
 549               screen_info.green_pos,
 550               screen_info.blue_pos);
 551
 552        efifb_fix.ypanstep  = 0;
 553        efifb_fix.ywrapstep = 0;
 554
 555        info->fbops = &efifb_ops;
 556        info->var = efifb_defined;
 557        info->fix = efifb_fix;
 558        info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
 559
 560        orientation = drm_get_panel_orientation_quirk(efifb_defined.xres,
 561                                                      efifb_defined.yres);
 562        switch (orientation) {
 563        default:
 564                info->fbcon_rotate_hint = FB_ROTATE_UR;
 565                break;
 566        case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
 567                info->fbcon_rotate_hint = FB_ROTATE_UD;
 568                break;
 569        case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
 570                info->fbcon_rotate_hint = FB_ROTATE_CCW;
 571                break;
 572        case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
 573                info->fbcon_rotate_hint = FB_ROTATE_CW;
 574                break;
 575        }
 576
 577        err = sysfs_create_groups(&dev->dev.kobj, efifb_groups);
 578        if (err) {
 579                pr_err("efifb: cannot add sysfs attrs\n");
 580                goto err_unmap;
 581        }
 582        err = fb_alloc_cmap(&info->cmap, 256, 0);
 583        if (err < 0) {
 584                pr_err("efifb: cannot allocate colormap\n");
 585                goto err_groups;
 586        }
 587        err = register_framebuffer(info);
 588        if (err < 0) {
 589                pr_err("efifb: cannot register framebuffer\n");
 590                goto err_fb_dealoc;
 591        }
 592        fb_info(info, "%s frame buffer device\n", info->fix.id);
 593        return 0;
 594
 595err_fb_dealoc:
 596        fb_dealloc_cmap(&info->cmap);
 597err_groups:
 598        sysfs_remove_groups(&dev->dev.kobj, efifb_groups);
 599err_unmap:
 600        if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
 601                iounmap(info->screen_base);
 602        else
 603                memunmap(info->screen_base);
 604err_release_fb:
 605        framebuffer_release(info);
 606err_release_mem:
 607        if (request_mem_succeeded)
 608                release_mem_region(efifb_fix.smem_start, size_total);
 609        return err;
 610}
 611
 612static int efifb_remove(struct platform_device *pdev)
 613{
 614        struct fb_info *info = platform_get_drvdata(pdev);
 615
 616        unregister_framebuffer(info);
 617        sysfs_remove_groups(&pdev->dev.kobj, efifb_groups);
 618        framebuffer_release(info);
 619
 620        return 0;
 621}
 622
 623static struct platform_driver efifb_driver = {
 624        .driver = {
 625                .name = "efi-framebuffer",
 626        },
 627        .probe = efifb_probe,
 628        .remove = efifb_remove,
 629};
 630
 631builtin_platform_driver(efifb_driver);
 632
 633#if defined(CONFIG_PCI)
 634
 635static void record_efifb_bar_resource(struct pci_dev *dev, int idx, u64 offset)
 636{
 637        u16 word;
 638
 639        efifb_pci_dev = dev;
 640
 641        pci_read_config_word(dev, PCI_COMMAND, &word);
 642        if (!(word & PCI_COMMAND_MEMORY)) {
 643                pci_dev_disabled = true;
 644                dev_err(&dev->dev,
 645                        "BAR %d: assigned to efifb but device is disabled!\n",
 646                        idx);
 647                return;
 648        }
 649
 650        bar_resource = &dev->resource[idx];
 651        bar_offset = offset;
 652
 653        dev_info(&dev->dev, "BAR %d: assigned to efifb\n", idx);
 654}
 655
 656static void efifb_fixup_resources(struct pci_dev *dev)
 657{
 658        u64 base = screen_info.lfb_base;
 659        u64 size = screen_info.lfb_size;
 660        int i;
 661
 662        if (efifb_pci_dev || screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
 663                return;
 664
 665        if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
 666                base |= (u64)screen_info.ext_lfb_base << 32;
 667
 668        if (!base)
 669                return;
 670
 671        for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
 672                struct resource *res = &dev->resource[i];
 673
 674                if (!(res->flags & IORESOURCE_MEM))
 675                        continue;
 676
 677                if (res->start <= base && res->end >= base + size - 1) {
 678                        record_efifb_bar_resource(dev, i, base - res->start);
 679                        break;
 680                }
 681        }
 682}
 683DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY,
 684                               16, efifb_fixup_resources);
 685
 686#endif
 687