diff options
author | Matt Fleming <matt.fleming@intel.com> | 2015-08-25 16:32:55 +0100 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2015-10-12 14:20:06 +0100 |
commit | ae2ee627dc87a70910de91b791b3cd0e9c6facdd (patch) | |
tree | eaea57763339636e508428f537c7ab5526a5198d /drivers/video | |
parent | 7968c0e338085eba0ee2f0e0b0d833057a966679 (diff) |
efifb: Add support for 64-bit frame buffer addresses
The EFI Graphics Output Protocol uses 64-bit frame buffer addresses
but these get truncated to 32-bit by the EFI boot stub when storing
the address in the 'lfb_base' field of 'struct screen_info'.
Add a 'ext_lfb_base' field for the upper 32-bits of the frame buffer
address and set VIDEO_TYPE_CAPABILITY_64BIT_BASE when the field is
useable.
It turns out that the reason no one has required this support so far
is that there's actually code in tianocore to "downgrade" PCI
resources that have option ROMs and 64-bit BARS from 64-bit to 32-bit
to cope with legacy option ROMs that can't handle 64-bit addresses.
The upshot is that basically all GOP devices in the wild use a 32-bit
frame buffer address.
Still, it is possible to build firmware that uses a full 64-bit GOP
frame buffer address. Chad did, which led to him reporting this issue.
Add support in anticipation of GOP devices using 64-bit addresses more
widely, and so that efifb works out of the box when that happens.
Reported-by: Chad Page <chad.page@znyx.com>
Cc: Pete Hawkins <pete.hawkins@znyx.com>
Acked-by: Peter Jones <pjones@redhat.com>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/fbdev/efifb.c | 24 |
1 files changed, 23 insertions, 1 deletions
diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index 4bfff349b1fb..95d293b7445a 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -114,6 +114,20 @@ static int efifb_setup(char *options) return 0; } +static inline bool fb_base_is_valid(void) +{ + if (screen_info.lfb_base) + return true; + + if (!(screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)) + return false; + + if (screen_info.ext_lfb_base) + return true; + + return false; +} + static int efifb_probe(struct platform_device *dev) { struct fb_info *info; @@ -141,7 +155,7 @@ static int efifb_probe(struct platform_device *dev) screen_info.lfb_depth = 32; if (!screen_info.pages) screen_info.pages = 1; - if (!screen_info.lfb_base) { + if (!fb_base_is_valid()) { printk(KERN_DEBUG "efifb: invalid framebuffer address\n"); return -ENODEV; } @@ -160,6 +174,14 @@ static int efifb_probe(struct platform_device *dev) } 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; + } + efifb_defined.bits_per_pixel = screen_info.lfb_depth; efifb_defined.xres = screen_info.lfb_width; efifb_defined.yres = screen_info.lfb_height; |