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/errno.h>
  13#include <linux/fb.h>
  14#include <linux/pci.h>
  15#include <linux/platform_device.h>
  16#include <linux/screen_info.h>
  17#include <video/vga.h>
  18#include <asm/efi.h>
  19#include <drm/drm_utils.h> /* For drm_get_panel_orientation_quirk */
  20#include <drm/drm_connector.h>  /* For DRM_MODE_PANEL_ORIENTATION_* */
  21
  22static bool request_mem_succeeded = false;
  23static bool nowc = false;
  24
  25static struct fb_var_screeninfo efifb_defined = {
  26        .activate               = FB_ACTIVATE_NOW,
  27        .height                 = -1,
  28        .width                  = -1,
  29        .right_margin           = 32,
  30        .upper_margin           = 16,
  31        .lower_margin           = 4,
  32        .vsync_len              = 4,
  33        .vmode                  = FB_VMODE_NONINTERLACED,
  34};
  35
  36static struct fb_fix_screeninfo efifb_fix = {
  37        .id                     = "EFI VGA",
  38        .type                   = FB_TYPE_PACKED_PIXELS,
  39        .accel                  = FB_ACCEL_NONE,
  40        .visual                 = FB_VISUAL_TRUECOLOR,
  41};
  42
  43static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
  44                           unsigned blue, unsigned transp,
  45                           struct fb_info *info)
  46{
  47        /*
  48         *  Set a single color register. The values supplied are
  49         *  already rounded down to the hardware's capabilities
  50         *  (according to the entries in the `var' structure). Return
  51         *  != 0 for invalid regno.
  52         */
  53
  54        if (regno >= info->cmap.len)
  55                return 1;
  56
  57        if (regno < 16) {
  58                red   >>= 16 - info->var.red.length;
  59                green >>= 16 - info->var.green.length;
  60                blue  >>= 16 - info->var.blue.length;
  61                ((u32 *)(info->pseudo_palette))[regno] =
  62                        (red   << info->var.red.offset)   |
  63                        (green << info->var.green.offset) |
  64                        (blue  << info->var.blue.offset);
  65        }
  66        return 0;
  67}
  68
  69static void efifb_destroy(struct fb_info *info)
  70{
  71        if (info->screen_base)
  72                iounmap(info->screen_base);
  73        if (request_mem_succeeded)
  74                release_mem_region(info->apertures->ranges[0].base,
  75                                   info->apertures->ranges[0].size);
  76        fb_dealloc_cmap(&info->cmap);
  77}
  78
  79static struct fb_ops efifb_ops = {
  80        .owner          = THIS_MODULE,
  81        .fb_destroy     = efifb_destroy,
  82        .fb_setcolreg   = efifb_setcolreg,
  83        .fb_fillrect    = cfb_fillrect,
  84        .fb_copyarea    = cfb_copyarea,
  85        .fb_imageblit   = cfb_imageblit,
  86};
  87
  88static int efifb_setup(char *options)
  89{
  90        char *this_opt;
  91
  92        if (options && *options) {
  93                while ((this_opt = strsep(&options, ",")) != NULL) {
  94                        if (!*this_opt) continue;
  95
  96                        efifb_setup_from_dmi(&screen_info, this_opt);
  97
  98                        if (!strncmp(this_opt, "base:", 5))
  99                                screen_info.lfb_base = simple_strtoul(this_opt+5, NULL, 0);
 100                        else if (!strncmp(this_opt, "stride:", 7))
 101                                screen_info.lfb_linelength = simple_strtoul(this_opt+7, NULL, 0) * 4;
 102                        else if (!strncmp(this_opt, "height:", 7))
 103                                screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0);
 104                        else if (!strncmp(this_opt, "width:", 6))
 105                                screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0);
 106                        else if (!strcmp(this_opt, "nowc"))
 107                                nowc = true;
 108                }
 109        }
 110
 111        return 0;
 112}
 113
 114static inline bool fb_base_is_valid(void)
 115{
 116        if (screen_info.lfb_base)
 117                return true;
 118
 119        if (!(screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE))
 120                return false;
 121
 122        if (screen_info.ext_lfb_base)
 123                return true;
 124
 125        return false;
 126}
 127
 128#define efifb_attr_decl(name, fmt)                                      \
 129static ssize_t name##_show(struct device *dev,                          \
 130                           struct device_attribute *attr,               \
 131                           char *buf)                                   \
 132{                                                                       \
 133        return sprintf(buf, fmt "\n", (screen_info.lfb_##name));        \
 134}                                                                       \
 135static DEVICE_ATTR_RO(name)
 136
 137efifb_attr_decl(base, "0x%x");
 138efifb_attr_decl(linelength, "%u");
 139efifb_attr_decl(height, "%u");
 140efifb_attr_decl(width, "%u");
 141efifb_attr_decl(depth, "%u");
 142
 143static struct attribute *efifb_attrs[] = {
 144        &dev_attr_base.attr,
 145        &dev_attr_linelength.attr,
 146        &dev_attr_width.attr,
 147        &dev_attr_height.attr,
 148        &dev_attr_depth.attr,
 149        NULL
 150};
 151ATTRIBUTE_GROUPS(efifb);
 152
 153static bool pci_dev_disabled;   /* FB base matches BAR of a disabled device */
 154
 155static struct pci_dev *efifb_pci_dev;   /* dev with BAR covering the efifb */
 156static struct resource *bar_resource;
 157static u64 bar_offset;
 158
 159static int efifb_probe(struct platform_device *dev)
 160{
 161        struct fb_info *info;
 162        int err, orientation;
 163        unsigned int size_vmode;
 164        unsigned int size_remap;
 165        unsigned int size_total;
 166        char *option = NULL;
 167
 168        if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
 169                return -ENODEV;
 170
 171        if (fb_get_options("efifb", &option))
 172                return -ENODEV;
 173        efifb_setup(option);
 174
 175        /* We don't get linelength from UGA Draw Protocol, only from
 176         * EFI Graphics Protocol.  So if it's not in DMI, and it's not
 177         * passed in from the user, we really can't use the framebuffer.
 178         */
 179        if (!screen_info.lfb_linelength)
 180                return -ENODEV;
 181
 182        if (!screen_info.lfb_depth)
 183                screen_info.lfb_depth = 32;
 184        if (!screen_info.pages)
 185                screen_info.pages = 1;
 186        if (!fb_base_is_valid()) {
 187                printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
 188                return -ENODEV;
 189        }
 190        printk(KERN_INFO "efifb: probing for efifb\n");
 191
 192        /* just assume they're all unset if any are */
 193        if (!screen_info.blue_size) {
 194                screen_info.blue_size = 8;
 195                screen_info.blue_pos = 0;
 196                screen_info.green_size = 8;
 197                screen_info.green_pos = 8;
 198                screen_info.red_size = 8;
 199                screen_info.red_pos = 16;
 200                screen_info.rsvd_size = 8;
 201                screen_info.rsvd_pos = 24;
 202        }
 203
 204        efifb_fix.smem_start = screen_info.lfb_base;
 205
 206        if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
 207                u64 ext_lfb_base;
 208
 209                ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
 210                efifb_fix.smem_start |= ext_lfb_base;
 211        }
 212
 213        if (bar_resource &&
 214            bar_resource->start + bar_offset != efifb_fix.smem_start) {
 215                dev_info(&efifb_pci_dev->dev,
 216                         "BAR has moved, updating efifb address\n");
 217                efifb_fix.smem_start = bar_resource->start + bar_offset;
 218        }
 219
 220        efifb_defined.bits_per_pixel = screen_info.lfb_depth;
 221        efifb_defined.xres = screen_info.lfb_width;
 222        efifb_defined.yres = screen_info.lfb_height;
 223        efifb_fix.line_length = screen_info.lfb_linelength;
 224
 225        /*   size_vmode -- that is the amount of memory needed for the
 226         *                 used video mode, i.e. the minimum amount of
 227         *                 memory we need. */
 228        size_vmode = efifb_defined.yres * efifb_fix.line_length;
 229
 230        /*   size_total -- all video memory we have. Used for
 231         *                 entries, ressource allocation and bounds
 232         *                 checking. */
 233        size_total = screen_info.lfb_size;
 234        if (size_total < size_vmode)
 235                size_total = size_vmode;
 236
 237        /*   size_remap -- the amount of video memory we are going to
 238         *                 use for efifb.  With modern cards it is no
 239         *                 option to simply use size_total as that
 240         *                 wastes plenty of kernel address space. */
 241        size_remap  = size_vmode * 2;
 242        if (size_remap > size_total)
 243                size_remap = size_total;
 244        if (size_remap % PAGE_SIZE)
 245                size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
 246        efifb_fix.smem_len = size_remap;
 247
 248        if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
 249                request_mem_succeeded = true;
 250        } else {
 251                /* We cannot make this fatal. Sometimes this comes from magic
 252                   spaces our resource handlers simply don't know about */
 253                pr_warn("efifb: cannot reserve video memory at 0x%lx\n",
 254                        efifb_fix.smem_start);
 255        }
 256
 257        info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
 258        if (!info) {
 259                pr_err("efifb: cannot allocate framebuffer\n");
 260                err = -ENOMEM;
 261                goto err_release_mem;
 262        }
 263        platform_set_drvdata(dev, info);
 264        info->pseudo_palette = info->par;
 265        info->par = NULL;
 266
 267        info->apertures = alloc_apertures(1);
 268        if (!info->apertures) {
 269                err = -ENOMEM;
 270                goto err_release_fb;
 271        }
 272        info->apertures->ranges[0].base = efifb_fix.smem_start;
 273        info->apertures->ranges[0].size = size_remap;
 274
 275        if (nowc)
 276                info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len);
 277        else
 278                info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len);
 279        if (!info->screen_base) {
 280                pr_err("efifb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n",
 281                        efifb_fix.smem_len, efifb_fix.smem_start);
 282                err = -EIO;
 283                goto err_release_fb;
 284        }
 285
 286        pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
 287               efifb_fix.smem_start, size_remap/1024, size_total/1024);
 288        pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
 289               efifb_defined.xres, efifb_defined.yres,
 290               efifb_defined.bits_per_pixel, efifb_fix.line_length,
 291               screen_info.pages);
 292
 293        efifb_defined.xres_virtual = efifb_defined.xres;
 294        efifb_defined.yres_virtual = efifb_fix.smem_len /
 295                                        efifb_fix.line_length;
 296        pr_info("efifb: scrolling: redraw\n");
 297        efifb_defined.yres_virtual = efifb_defined.yres;
 298
 299        /* some dummy values for timing to make fbset happy */
 300        efifb_defined.pixclock     = 10000000 / efifb_defined.xres *
 301                                        1000 / efifb_defined.yres;
 302        efifb_defined.left_margin  = (efifb_defined.xres / 8) & 0xf8;
 303        efifb_defined.hsync_len    = (efifb_defined.xres / 8) & 0xf8;
 304
 305        efifb_defined.red.offset    = screen_info.red_pos;
 306        efifb_defined.red.length    = screen_info.red_size;
 307        efifb_defined.green.offset  = screen_info.green_pos;
 308        efifb_defined.green.length  = screen_info.green_size;
 309        efifb_defined.blue.offset   = screen_info.blue_pos;
 310        efifb_defined.blue.length   = screen_info.blue_size;
 311        efifb_defined.transp.offset = screen_info.rsvd_pos;
 312        efifb_defined.transp.length = screen_info.rsvd_size;
 313
 314        pr_info("efifb: %s: "
 315               "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
 316               "Truecolor",
 317               screen_info.rsvd_size,
 318               screen_info.red_size,
 319               screen_info.green_size,
 320               screen_info.blue_size,
 321               screen_info.rsvd_pos,
 322               screen_info.red_pos,
 323               screen_info.green_pos,
 324               screen_info.blue_pos);
 325
 326        efifb_fix.ypanstep  = 0;
 327        efifb_fix.ywrapstep = 0;
 328
 329        info->fbops = &efifb_ops;
 330        info->var = efifb_defined;
 331        info->fix = efifb_fix;
 332        info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
 333
 334        orientation = drm_get_panel_orientation_quirk(efifb_defined.xres,
 335                                                      efifb_defined.yres);
 336        switch (orientation) {
 337        default:
 338                info->fbcon_rotate_hint = FB_ROTATE_UR;
 339                break;
 340        case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
 341                info->fbcon_rotate_hint = FB_ROTATE_UD;
 342                break;
 343        case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
 344                info->fbcon_rotate_hint = FB_ROTATE_CCW;
 345                break;
 346        case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
 347                info->fbcon_rotate_hint = FB_ROTATE_CW;
 348                break;
 349        }
 350
 351        err = sysfs_create_groups(&dev->dev.kobj, efifb_groups);
 352        if (err) {
 353                pr_err("efifb: cannot add sysfs attrs\n");
 354                goto err_unmap;
 355        }
 356        err = fb_alloc_cmap(&info->cmap, 256, 0);
 357        if (err < 0) {
 358                pr_err("efifb: cannot allocate colormap\n");
 359                goto err_groups;
 360        }
 361        err = register_framebuffer(info);
 362        if (err < 0) {
 363                pr_err("efifb: cannot register framebuffer\n");
 364                goto err_fb_dealoc;
 365        }
 366        fb_info(info, "%s frame buffer device\n", info->fix.id);
 367        return 0;
 368
 369err_fb_dealoc:
 370        fb_dealloc_cmap(&info->cmap);
 371err_groups:
 372        sysfs_remove_groups(&dev->dev.kobj, efifb_groups);
 373err_unmap:
 374        iounmap(info->screen_base);
 375err_release_fb:
 376        framebuffer_release(info);
 377err_release_mem:
 378        if (request_mem_succeeded)
 379                release_mem_region(efifb_fix.smem_start, size_total);
 380        return err;
 381}
 382
 383static int efifb_remove(struct platform_device *pdev)
 384{
 385        struct fb_info *info = platform_get_drvdata(pdev);
 386
 387        unregister_framebuffer(info);
 388        sysfs_remove_groups(&pdev->dev.kobj, efifb_groups);
 389        framebuffer_release(info);
 390
 391        return 0;
 392}
 393
 394static struct platform_driver efifb_driver = {
 395        .driver = {
 396                .name = "efi-framebuffer",
 397        },
 398        .probe = efifb_probe,
 399        .remove = efifb_remove,
 400};
 401
 402builtin_platform_driver(efifb_driver);
 403
 404#if defined(CONFIG_PCI)
 405
 406static void record_efifb_bar_resource(struct pci_dev *dev, int idx, u64 offset)
 407{
 408        u16 word;
 409
 410        efifb_pci_dev = dev;
 411
 412        pci_read_config_word(dev, PCI_COMMAND, &word);
 413        if (!(word & PCI_COMMAND_MEMORY)) {
 414                pci_dev_disabled = true;
 415                dev_err(&dev->dev,
 416                        "BAR %d: assigned to efifb but device is disabled!\n",
 417                        idx);
 418                return;
 419        }
 420
 421        bar_resource = &dev->resource[idx];
 422        bar_offset = offset;
 423
 424        dev_info(&dev->dev, "BAR %d: assigned to efifb\n", idx);
 425}
 426
 427static void efifb_fixup_resources(struct pci_dev *dev)
 428{
 429        u64 base = screen_info.lfb_base;
 430        u64 size = screen_info.lfb_size;
 431        int i;
 432
 433        if (efifb_pci_dev || screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
 434                return;
 435
 436        if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
 437                base |= (u64)screen_info.ext_lfb_base << 32;
 438
 439        if (!base)
 440                return;
 441
 442        for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
 443                struct resource *res = &dev->resource[i];
 444
 445                if (!(res->flags & IORESOURCE_MEM))
 446                        continue;
 447
 448                if (res->start <= base && res->end >= base + size - 1) {
 449                        record_efifb_bar_resource(dev, i, base - res->start);
 450                        break;
 451                }
 452        }
 453}
 454DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY,
 455                               16, efifb_fixup_resources);
 456
 457#endif
 458