in fbdev/efifb.c [344:617]
static int efifb_probe(struct platform_device *dev)
{
struct fb_info *info;
int err, orientation;
unsigned int size_vmode;
unsigned int size_remap;
unsigned int size_total;
char *option = NULL;
efi_memory_desc_t md;
/*
* Generic drivers must not be registered if a framebuffer exists.
* If a native driver was probed, the display hardware was already
* taken and attempting to use the system framebuffer is dangerous.
*/
if (num_registered_fb > 0) {
dev_err(&dev->dev,
"efifb: a framebuffer is already registered\n");
return -EINVAL;
}
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI || pci_dev_disabled)
return -ENODEV;
if (fb_get_options("efifb", &option))
return -ENODEV;
efifb_setup(option);
/* We don't get linelength from UGA Draw Protocol, only from
* EFI Graphics Protocol. So if it's not in DMI, and it's not
* passed in from the user, we really can't use the framebuffer.
*/
if (!screen_info.lfb_linelength)
return -ENODEV;
if (!screen_info.lfb_depth)
screen_info.lfb_depth = 32;
if (!screen_info.pages)
screen_info.pages = 1;
if (!fb_base_is_valid()) {
printk(KERN_DEBUG "efifb: invalid framebuffer address\n");
return -ENODEV;
}
printk(KERN_INFO "efifb: probing for efifb\n");
/* just assume they're all unset if any are */
if (!screen_info.blue_size) {
screen_info.blue_size = 8;
screen_info.blue_pos = 0;
screen_info.green_size = 8;
screen_info.green_pos = 8;
screen_info.red_size = 8;
screen_info.red_pos = 16;
screen_info.rsvd_size = 8;
screen_info.rsvd_pos = 24;
}
efifb_fix.smem_start = screen_info.lfb_base;
if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) {
u64 ext_lfb_base;
ext_lfb_base = (u64)(unsigned long)screen_info.ext_lfb_base << 32;
efifb_fix.smem_start |= ext_lfb_base;
}
if (bar_resource &&
bar_resource->start + bar_offset != efifb_fix.smem_start) {
dev_info(&efifb_pci_dev->dev,
"BAR has moved, updating efifb address\n");
efifb_fix.smem_start = bar_resource->start + bar_offset;
}
efifb_defined.bits_per_pixel = screen_info.lfb_depth;
efifb_defined.xres = screen_info.lfb_width;
efifb_defined.yres = screen_info.lfb_height;
efifb_fix.line_length = screen_info.lfb_linelength;
/* size_vmode -- that is the amount of memory needed for the
* used video mode, i.e. the minimum amount of
* memory we need. */
size_vmode = efifb_defined.yres * efifb_fix.line_length;
/* size_total -- all video memory we have. Used for
* entries, ressource allocation and bounds
* checking. */
size_total = screen_info.lfb_size;
if (size_total < size_vmode)
size_total = size_vmode;
/* size_remap -- the amount of video memory we are going to
* use for efifb. With modern cards it is no
* option to simply use size_total as that
* wastes plenty of kernel address space. */
size_remap = size_vmode * 2;
if (size_remap > size_total)
size_remap = size_total;
if (size_remap % PAGE_SIZE)
size_remap += PAGE_SIZE - (size_remap % PAGE_SIZE);
efifb_fix.smem_len = size_remap;
if (request_mem_region(efifb_fix.smem_start, size_remap, "efifb")) {
request_mem_succeeded = true;
} else {
/* We cannot make this fatal. Sometimes this comes from magic
spaces our resource handlers simply don't know about */
pr_warn("efifb: cannot reserve video memory at 0x%lx\n",
efifb_fix.smem_start);
}
info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
if (!info) {
err = -ENOMEM;
goto err_release_mem;
}
platform_set_drvdata(dev, info);
info->pseudo_palette = info->par;
info->par = NULL;
info->apertures = alloc_apertures(1);
if (!info->apertures) {
err = -ENOMEM;
goto err_release_fb;
}
info->apertures->ranges[0].base = efifb_fix.smem_start;
info->apertures->ranges[0].size = size_remap;
if (efi_enabled(EFI_MEMMAP) &&
!efi_mem_desc_lookup(efifb_fix.smem_start, &md)) {
if ((efifb_fix.smem_start + efifb_fix.smem_len) >
(md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT))) {
pr_err("efifb: video memory @ 0x%lx spans multiple EFI memory regions\n",
efifb_fix.smem_start);
err = -EIO;
goto err_release_fb;
}
/*
* If the UEFI memory map covers the efifb region, we may only
* remap it using the attributes the memory map prescribes.
*/
md.attribute &= EFI_MEMORY_UC | EFI_MEMORY_WC |
EFI_MEMORY_WT | EFI_MEMORY_WB;
if (md.attribute) {
mem_flags |= EFI_MEMORY_WT | EFI_MEMORY_WB;
mem_flags &= md.attribute;
}
}
if (mem_flags & EFI_MEMORY_WC)
info->screen_base = ioremap_wc(efifb_fix.smem_start,
efifb_fix.smem_len);
else if (mem_flags & EFI_MEMORY_UC)
info->screen_base = ioremap(efifb_fix.smem_start,
efifb_fix.smem_len);
else if (mem_flags & EFI_MEMORY_WT)
info->screen_base = memremap(efifb_fix.smem_start,
efifb_fix.smem_len, MEMREMAP_WT);
else if (mem_flags & EFI_MEMORY_WB)
info->screen_base = memremap(efifb_fix.smem_start,
efifb_fix.smem_len, MEMREMAP_WB);
if (!info->screen_base) {
pr_err("efifb: abort, cannot remap video memory 0x%x @ 0x%lx\n",
efifb_fix.smem_len, efifb_fix.smem_start);
err = -EIO;
goto err_release_fb;
}
efifb_show_boot_graphics(info);
pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n",
efifb_fix.smem_start, size_remap/1024, size_total/1024);
pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
efifb_defined.xres, efifb_defined.yres,
efifb_defined.bits_per_pixel, efifb_fix.line_length,
screen_info.pages);
efifb_defined.xres_virtual = efifb_defined.xres;
efifb_defined.yres_virtual = efifb_fix.smem_len /
efifb_fix.line_length;
pr_info("efifb: scrolling: redraw\n");
efifb_defined.yres_virtual = efifb_defined.yres;
/* some dummy values for timing to make fbset happy */
efifb_defined.pixclock = 10000000 / efifb_defined.xres *
1000 / efifb_defined.yres;
efifb_defined.left_margin = (efifb_defined.xres / 8) & 0xf8;
efifb_defined.hsync_len = (efifb_defined.xres / 8) & 0xf8;
efifb_defined.red.offset = screen_info.red_pos;
efifb_defined.red.length = screen_info.red_size;
efifb_defined.green.offset = screen_info.green_pos;
efifb_defined.green.length = screen_info.green_size;
efifb_defined.blue.offset = screen_info.blue_pos;
efifb_defined.blue.length = screen_info.blue_size;
efifb_defined.transp.offset = screen_info.rsvd_pos;
efifb_defined.transp.length = screen_info.rsvd_size;
pr_info("efifb: %s: "
"size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
"Truecolor",
screen_info.rsvd_size,
screen_info.red_size,
screen_info.green_size,
screen_info.blue_size,
screen_info.rsvd_pos,
screen_info.red_pos,
screen_info.green_pos,
screen_info.blue_pos);
efifb_fix.ypanstep = 0;
efifb_fix.ywrapstep = 0;
info->fbops = &efifb_ops;
info->var = efifb_defined;
info->fix = efifb_fix;
info->flags = FBINFO_FLAG_DEFAULT | FBINFO_MISC_FIRMWARE;
orientation = drm_get_panel_orientation_quirk(efifb_defined.xres,
efifb_defined.yres);
switch (orientation) {
default:
info->fbcon_rotate_hint = FB_ROTATE_UR;
break;
case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
info->fbcon_rotate_hint = FB_ROTATE_UD;
break;
case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
info->fbcon_rotate_hint = FB_ROTATE_CCW;
break;
case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
info->fbcon_rotate_hint = FB_ROTATE_CW;
break;
}
err = sysfs_create_groups(&dev->dev.kobj, efifb_groups);
if (err) {
pr_err("efifb: cannot add sysfs attrs\n");
goto err_unmap;
}
err = fb_alloc_cmap(&info->cmap, 256, 0);
if (err < 0) {
pr_err("efifb: cannot allocate colormap\n");
goto err_groups;
}
if (efifb_pci_dev)
WARN_ON(pm_runtime_get_sync(&efifb_pci_dev->dev) < 0);
err = register_framebuffer(info);
if (err < 0) {
pr_err("efifb: cannot register framebuffer\n");
goto err_put_rpm_ref;
}
fb_info(info, "%s frame buffer device\n", info->fix.id);
return 0;
err_put_rpm_ref:
if (efifb_pci_dev)
pm_runtime_put(&efifb_pci_dev->dev);
fb_dealloc_cmap(&info->cmap);
err_groups:
sysfs_remove_groups(&dev->dev.kobj, efifb_groups);
err_unmap:
if (mem_flags & (EFI_MEMORY_UC | EFI_MEMORY_WC))
iounmap(info->screen_base);
else
memunmap(info->screen_base);
err_release_fb:
framebuffer_release(info);
err_release_mem:
if (request_mem_succeeded)
release_mem_region(efifb_fix.smem_start, size_total);
return err;
}