drivers/firmware: move x86 Generic System Framebuffers support
authorJavier Martinez Canillas <javierm@redhat.com>
Fri, 25 Jun 2021 13:09:46 +0000 (15:09 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Wed, 21 Jul 2021 10:04:56 +0000 (12:04 +0200)
The x86 architecture has generic support to register a system framebuffer
platform device. It either registers a "simple-framebuffer" if the config
option CONFIG_X86_SYSFB is enabled, or a legacy VGA/VBE/EFI FB device.

But the code is generic enough to be reused by other architectures and can
be moved out of the arch/x86 directory.

This will allow to also support the simple{fb,drm} drivers on non-x86 EFI
platforms, such as aarch64 where these drivers are only supported with DT.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Borislav Petkov <bp@suse.de>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20210625130947.1803678-2-javierm@redhat.com
13 files changed:
arch/x86/Kconfig
arch/x86/include/asm/sysfb.h [deleted file]
arch/x86/kernel/Makefile
arch/x86/kernel/sysfb.c [deleted file]
arch/x86/kernel/sysfb_efi.c [deleted file]
arch/x86/kernel/sysfb_simplefb.c [deleted file]
drivers/firmware/Kconfig
drivers/firmware/Makefile
drivers/firmware/efi/Makefile
drivers/firmware/efi/sysfb_efi.c [new file with mode: 0644]
drivers/firmware/sysfb.c [new file with mode: 0644]
drivers/firmware/sysfb_simplefb.c [new file with mode: 0644]
include/linux/sysfb.h [new file with mode: 0644]

index 0045e1b44190213e75a098390f18271c851d86ec..cfe2761a37896409c9c200ff96dc0e8d2633acf2 100644 (file)
@@ -2806,32 +2806,6 @@ config AMD_NB
        def_bool y
        depends on CPU_SUP_AMD && PCI
 
-config X86_SYSFB
-       bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
-       help
-         Firmwares often provide initial graphics framebuffers so the BIOS,
-         bootloader or kernel can show basic video-output during boot for
-         user-guidance and debugging. Historically, x86 used the VESA BIOS
-         Extensions and EFI-framebuffers for this, which are mostly limited
-         to x86.
-         This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
-         framebuffers so the new generic system-framebuffer drivers can be
-         used on x86. If the framebuffer is not compatible with the generic
-         modes, it is advertised as fallback platform framebuffer so legacy
-         drivers like efifb, vesafb and uvesafb can pick it up.
-         If this option is not selected, all system framebuffers are always
-         marked as fallback platform framebuffers as usual.
-
-         Note: Legacy fbdev drivers, including vesafb, efifb, uvesafb, will
-         not be able to pick up generic system framebuffers if this option
-         is selected. You are highly encouraged to enable simplefb as
-         replacement if you select this option. simplefb can correctly deal
-         with generic system framebuffers. But you should still keep vesafb
-         and others enabled as fallback if a system framebuffer is
-         incompatible with simplefb.
-
-         If unsure, say Y.
-
 endmenu
 
 
diff --git a/arch/x86/include/asm/sysfb.h b/arch/x86/include/asm/sysfb.h
deleted file mode 100644 (file)
index 9834eef..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-#ifndef _ARCH_X86_KERNEL_SYSFB_H
-#define _ARCH_X86_KERNEL_SYSFB_H
-
-/*
- * Generic System Framebuffers on x86
- * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/platform_data/simplefb.h>
-#include <linux/screen_info.h>
-
-enum {
-       M_I17,          /* 17-Inch iMac */
-       M_I20,          /* 20-Inch iMac */
-       M_I20_SR,       /* 20-Inch iMac (Santa Rosa) */
-       M_I24,          /* 24-Inch iMac */
-       M_I24_8_1,      /* 24-Inch iMac, 8,1th gen */
-       M_I24_10_1,     /* 24-Inch iMac, 10,1th gen */
-       M_I27_11_1,     /* 27-Inch iMac, 11,1th gen */
-       M_MINI,         /* Mac Mini */
-       M_MINI_3_1,     /* Mac Mini, 3,1th gen */
-       M_MINI_4_1,     /* Mac Mini, 4,1th gen */
-       M_MB,           /* MacBook */
-       M_MB_2,         /* MacBook, 2nd rev. */
-       M_MB_3,         /* MacBook, 3rd rev. */
-       M_MB_5_1,       /* MacBook, 5th rev. */
-       M_MB_6_1,       /* MacBook, 6th rev. */
-       M_MB_7_1,       /* MacBook, 7th rev. */
-       M_MB_SR,        /* MacBook, 2nd gen, (Santa Rosa) */
-       M_MBA,          /* MacBook Air */
-       M_MBA_3,        /* Macbook Air, 3rd rev */
-       M_MBP,          /* MacBook Pro */
-       M_MBP_2,        /* MacBook Pro 2nd gen */
-       M_MBP_2_2,      /* MacBook Pro 2,2nd gen */
-       M_MBP_SR,       /* MacBook Pro (Santa Rosa) */
-       M_MBP_4,        /* MacBook Pro, 4th gen */
-       M_MBP_5_1,      /* MacBook Pro, 5,1th gen */
-       M_MBP_5_2,      /* MacBook Pro, 5,2th gen */
-       M_MBP_5_3,      /* MacBook Pro, 5,3rd gen */
-       M_MBP_6_1,      /* MacBook Pro, 6,1th gen */
-       M_MBP_6_2,      /* MacBook Pro, 6,2th gen */
-       M_MBP_7_1,      /* MacBook Pro, 7,1th gen */
-       M_MBP_8_2,      /* MacBook Pro, 8,2nd gen */
-       M_UNKNOWN       /* placeholder */
-};
-
-struct efifb_dmi_info {
-       char *optname;
-       unsigned long base;
-       int stride;
-       int width;
-       int height;
-       int flags;
-};
-
-#ifdef CONFIG_EFI
-
-extern struct efifb_dmi_info efifb_dmi_list[];
-void sysfb_apply_efi_quirks(void);
-
-#else /* CONFIG_EFI */
-
-static inline void sysfb_apply_efi_quirks(void)
-{
-}
-
-#endif /* CONFIG_EFI */
-
-#ifdef CONFIG_X86_SYSFB
-
-bool parse_mode(const struct screen_info *si,
-               struct simplefb_platform_data *mode);
-int create_simplefb(const struct screen_info *si,
-                   const struct simplefb_platform_data *mode);
-
-#else /* CONFIG_X86_SYSFB */
-
-static inline bool parse_mode(const struct screen_info *si,
-                             struct simplefb_platform_data *mode)
-{
-       return false;
-}
-
-static inline int create_simplefb(const struct screen_info *si,
-                                 const struct simplefb_platform_data *mode)
-{
-       return -EINVAL;
-}
-
-#endif /* CONFIG_X86_SYSFB */
-
-#endif /* _ARCH_X86_KERNEL_SYSFB_H */
index 0f66682ac02a62cee39694a84dbfd36dbc7ecedc..4114ea47def242cd575b1dfdf7ca6de8e82d9d8e 100644 (file)
@@ -135,9 +135,6 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
 obj-$(CONFIG_SWIOTLB)                  += pci-swiotlb.o
 obj-$(CONFIG_OF)                       += devicetree.o
 obj-$(CONFIG_UPROBES)                  += uprobes.o
-obj-y                                  += sysfb.o
-obj-$(CONFIG_X86_SYSFB)                        += sysfb_simplefb.o
-obj-$(CONFIG_EFI)                      += sysfb_efi.o
 
 obj-$(CONFIG_PERF_EVENTS)              += perf_regs.o
 obj-$(CONFIG_TRACING)                  += tracepoint.o
diff --git a/arch/x86/kernel/sysfb.c b/arch/x86/kernel/sysfb.c
deleted file mode 100644 (file)
index 014ebd8..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Generic System Framebuffers on x86
- * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
- */
-
-/*
- * Simple-Framebuffer support for x86 systems
- * Create a platform-device for any available boot framebuffer. The
- * simple-framebuffer platform device is already available on DT systems, so
- * this module parses the global "screen_info" object and creates a suitable
- * platform device compatible with the "simple-framebuffer" DT object. If
- * the framebuffer is incompatible, we instead create a legacy
- * "vesa-framebuffer", "efi-framebuffer" or "platform-framebuffer" device and
- * pass the screen_info as platform_data. This allows legacy drivers
- * to pick these devices up without messing with simple-framebuffer drivers.
- * The global "screen_info" is still valid at all times.
- *
- * If CONFIG_X86_SYSFB is not selected, we never register "simple-framebuffer"
- * platform devices, but only use legacy framebuffer devices for
- * backwards compatibility.
- *
- * TODO: We set the dev_id field of all platform-devices to 0. This allows
- * other x86 OF/DT parsers to create such devices, too. However, they must
- * start at offset 1 for this to work.
- */
-
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/platform_data/simplefb.h>
-#include <linux/platform_device.h>
-#include <linux/screen_info.h>
-#include <asm/sysfb.h>
-
-static __init int sysfb_init(void)
-{
-       struct screen_info *si = &screen_info;
-       struct simplefb_platform_data mode;
-       struct platform_device *pd;
-       const char *name;
-       bool compatible;
-       int ret;
-
-       sysfb_apply_efi_quirks();
-
-       /* try to create a simple-framebuffer device */
-       compatible = parse_mode(si, &mode);
-       if (compatible) {
-               ret = create_simplefb(si, &mode);
-               if (!ret)
-                       return 0;
-       }
-
-       /* if the FB is incompatible, create a legacy framebuffer device */
-       if (si->orig_video_isVGA == VIDEO_TYPE_EFI)
-               name = "efi-framebuffer";
-       else if (si->orig_video_isVGA == VIDEO_TYPE_VLFB)
-               name = "vesa-framebuffer";
-       else
-               name = "platform-framebuffer";
-
-       pd = platform_device_register_resndata(NULL, name, 0,
-                                              NULL, 0, si, sizeof(*si));
-       return PTR_ERR_OR_ZERO(pd);
-}
-
-/* must execute after PCI subsystem for EFI quirks */
-device_initcall(sysfb_init);
diff --git a/arch/x86/kernel/sysfb_efi.c b/arch/x86/kernel/sysfb_efi.c
deleted file mode 100644 (file)
index 8a56a6d..0000000
+++ /dev/null
@@ -1,284 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Generic System Framebuffers on x86
- * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
- *
- * EFI Quirks Copyright (c) 2006 Edgar Hucek <gimli@dark-green.com>
- */
-
-/*
- * EFI Quirks
- * Several EFI systems do not correctly advertise their boot framebuffers.
- * Hence, we use this static table of known broken machines and fix up the
- * information so framebuffer drivers can load correctly.
- */
-
-#include <linux/dmi.h>
-#include <linux/err.h>
-#include <linux/efi.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <linux/screen_info.h>
-#include <video/vga.h>
-
-#include <asm/efi.h>
-#include <asm/sysfb.h>
-
-enum {
-       OVERRIDE_NONE = 0x0,
-       OVERRIDE_BASE = 0x1,
-       OVERRIDE_STRIDE = 0x2,
-       OVERRIDE_HEIGHT = 0x4,
-       OVERRIDE_WIDTH = 0x8,
-};
-
-struct efifb_dmi_info efifb_dmi_list[] = {
-       [M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
-       [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050, OVERRIDE_NONE }, /* guess */
-       [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050, OVERRIDE_NONE },
-       [M_I24] = { "i24", 0x80010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE }, /* guess */
-       [M_I24_8_1] = { "imac8", 0xc0060000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
-       [M_I24_10_1] = { "imac10", 0xc0010000, 2048 * 4, 1920, 1080, OVERRIDE_NONE },
-       [M_I27_11_1] = { "imac11", 0xc0010000, 2560 * 4, 2560, 1440, OVERRIDE_NONE },
-       [M_MINI]= { "mini", 0x80000000, 2048 * 4, 1024, 768, OVERRIDE_NONE },
-       [M_MINI_3_1] = { "mini31", 0x40010000, 1024 * 4, 1024, 768, OVERRIDE_NONE },
-       [M_MINI_4_1] = { "mini41", 0xc0010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
-       [M_MB] = { "macbook", 0x80000000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
-       [M_MB_5_1] = { "macbook51", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
-       [M_MB_6_1] = { "macbook61", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
-       [M_MB_7_1] = { "macbook71", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
-       [M_MBA] = { "mba", 0x80000000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
-       /* 11" Macbook Air 3,1 passes the wrong stride */
-       [M_MBA_3] = { "mba3", 0, 2048 * 4, 0, 0, OVERRIDE_STRIDE },
-       [M_MBP] = { "mbp", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
-       [M_MBP_2] = { "mbp2", 0, 0, 0, 0, OVERRIDE_NONE }, /* placeholder */
-       [M_MBP_2_2] = { "mbp22", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
-       [M_MBP_SR] = { "mbp3", 0x80030000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
-       [M_MBP_4] = { "mbp4", 0xc0060000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
-       [M_MBP_5_1] = { "mbp51", 0xc0010000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
-       [M_MBP_5_2] = { "mbp52", 0xc0010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
-       [M_MBP_5_3] = { "mbp53", 0xd0010000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
-       [M_MBP_6_1] = { "mbp61", 0x90030000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
-       [M_MBP_6_2] = { "mbp62", 0x90030000, 2048 * 4, 1680, 1050, OVERRIDE_NONE },
-       [M_MBP_7_1] = { "mbp71", 0xc0010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
-       [M_MBP_8_2] = { "mbp82", 0x90010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
-       [M_UNKNOWN] = { NULL, 0, 0, 0, 0, OVERRIDE_NONE }
-};
-
-void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
-{
-       int i;
-
-       for (i = 0; i < M_UNKNOWN; i++) {
-               if (efifb_dmi_list[i].base != 0 &&
-                   !strcmp(opt, efifb_dmi_list[i].optname)) {
-                       si->lfb_base = efifb_dmi_list[i].base;
-                       si->lfb_linelength = efifb_dmi_list[i].stride;
-                       si->lfb_width = efifb_dmi_list[i].width;
-                       si->lfb_height = efifb_dmi_list[i].height;
-               }
-       }
-}
-
-#define choose_value(dmivalue, fwvalue, field, flags) ({       \
-               typeof(fwvalue) _ret_ = fwvalue;                \
-               if ((flags) & (field))                          \
-                       _ret_ = dmivalue;                       \
-               else if ((fwvalue) == 0)                        \
-                       _ret_ = dmivalue;                       \
-               _ret_;                                          \
-       })
-
-static int __init efifb_set_system(const struct dmi_system_id *id)
-{
-       struct efifb_dmi_info *info = id->driver_data;
-
-       if (info->base == 0 && info->height == 0 && info->width == 0 &&
-           info->stride == 0)
-               return 0;
-
-       /* Trust the bootloader over the DMI tables */
-       if (screen_info.lfb_base == 0) {
-#if defined(CONFIG_PCI)
-               struct pci_dev *dev = NULL;
-               int found_bar = 0;
-#endif
-               if (info->base) {
-                       screen_info.lfb_base = choose_value(info->base,
-                               screen_info.lfb_base, OVERRIDE_BASE,
-                               info->flags);
-
-#if defined(CONFIG_PCI)
-                       /* make sure that the address in the table is actually
-                        * on a VGA device's PCI BAR */
-
-                       for_each_pci_dev(dev) {
-                               int i;
-                               if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
-                                       continue;
-                               for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
-                                       resource_size_t start, end;
-                                       unsigned long flags;
-
-                                       flags = pci_resource_flags(dev, i);
-                                       if (!(flags & IORESOURCE_MEM))
-                                               continue;
-
-                                       if (flags & IORESOURCE_UNSET)
-                                               continue;
-
-                                       if (pci_resource_len(dev, i) == 0)
-                                               continue;
-
-                                       start = pci_resource_start(dev, i);
-                                       end = pci_resource_end(dev, i);
-                                       if (screen_info.lfb_base >= start &&
-                                           screen_info.lfb_base < end) {
-                                               found_bar = 1;
-                                               break;
-                                       }
-                               }
-                       }
-                       if (!found_bar)
-                               screen_info.lfb_base = 0;
-#endif
-               }
-       }
-       if (screen_info.lfb_base) {
-               screen_info.lfb_linelength = choose_value(info->stride,
-                       screen_info.lfb_linelength, OVERRIDE_STRIDE,
-                       info->flags);
-               screen_info.lfb_width = choose_value(info->width,
-                       screen_info.lfb_width, OVERRIDE_WIDTH,
-                       info->flags);
-               screen_info.lfb_height = choose_value(info->height,
-                       screen_info.lfb_height, OVERRIDE_HEIGHT,
-                       info->flags);
-               if (screen_info.orig_video_isVGA == 0)
-                       screen_info.orig_video_isVGA = VIDEO_TYPE_EFI;
-       } else {
-               screen_info.lfb_linelength = 0;
-               screen_info.lfb_width = 0;
-               screen_info.lfb_height = 0;
-               screen_info.orig_video_isVGA = 0;
-               return 0;
-       }
-
-       printk(KERN_INFO "efifb: dmi detected %s - framebuffer at 0x%08x "
-                        "(%dx%d, stride %d)\n", id->ident,
-                        screen_info.lfb_base, screen_info.lfb_width,
-                        screen_info.lfb_height, screen_info.lfb_linelength);
-
-       return 1;
-}
-
-#define EFIFB_DMI_SYSTEM_ID(vendor, name, enumid)              \
-       {                                                       \
-               efifb_set_system,                               \
-               name,                                           \
-               {                                               \
-                       DMI_MATCH(DMI_BIOS_VENDOR, vendor),     \
-                       DMI_MATCH(DMI_PRODUCT_NAME, name)       \
-               },                                              \
-               &efifb_dmi_list[enumid]                         \
-       }
-
-static const struct dmi_system_id efifb_dmi_system_table[] __initconst = {
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17),
-       /* At least one of these two will be right; maybe both? */
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac5,1", M_I20),
-       /* At least one of these two will be right; maybe both? */
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac6,1", M_I24),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac6,1", M_I24),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac7,1", M_I20_SR),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac8,1", M_I24_8_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac10,1", M_I24_10_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac11,1", M_I27_11_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "Macmini1,1", M_MINI),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini3,1", M_MINI_3_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini4,1", M_MINI_4_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook1,1", M_MB),
-       /* At least one of these two will be right; maybe both? */
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook2,1", M_MB),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook2,1", M_MB),
-       /* At least one of these two will be right; maybe both? */
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook3,1", M_MB),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook3,1", M_MB),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook4,1", M_MB),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook5,1", M_MB_5_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook6,1", M_MB_6_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook7,1", M_MB_7_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir1,1", M_MBA),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir3,1", M_MBA_3),
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro1,1", M_MBP),
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,1", M_MBP_2),
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,2", M_MBP_2_2),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro2,1", M_MBP_2),
-       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro3,1", M_MBP_SR),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro3,1", M_MBP_SR),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro4,1", M_MBP_4),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,1", M_MBP_5_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,2", M_MBP_5_2),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,3", M_MBP_5_3),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,1", M_MBP_6_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,2", M_MBP_6_2),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro7,1", M_MBP_7_1),
-       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro8,2", M_MBP_8_2),
-       {},
-};
-
-/*
- * Some devices have a portrait LCD but advertise a landscape resolution (and
- * pitch). We simply swap width and height for these devices so that we can
- * correctly deal with some of them coming with multiple resolutions.
- */
-static const struct dmi_system_id efifb_dmi_swap_width_height[] __initconst = {
-       {
-               /*
-                * Lenovo MIIX310-10ICR, only some batches have the troublesome
-                * 800x1280 portrait screen. Luckily the portrait version has
-                * its own BIOS version, so we match on that.
-                */
-               .matches = {
-                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                       DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
-                       DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1HCN44WW"),
-               },
-       },
-       {
-               /* Lenovo MIIX 320-10ICR with 800x1280 portrait screen */
-               .matches = {
-                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                       DMI_EXACT_MATCH(DMI_PRODUCT_VERSION,
-                                       "Lenovo MIIX 320-10ICR"),
-               },
-       },
-       {
-               /* Lenovo D330 with 800x1280 or 1200x1920 portrait screen */
-               .matches = {
-                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
-                       DMI_EXACT_MATCH(DMI_PRODUCT_VERSION,
-                                       "Lenovo ideapad D330-10IGM"),
-               },
-       },
-       {},
-};
-
-__init void sysfb_apply_efi_quirks(void)
-{
-       if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
-           !(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
-               dmi_check_system(efifb_dmi_system_table);
-
-       if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
-           dmi_check_system(efifb_dmi_swap_width_height)) {
-               u16 temp = screen_info.lfb_width;
-
-               screen_info.lfb_width = screen_info.lfb_height;
-               screen_info.lfb_height = temp;
-               screen_info.lfb_linelength = 4 * screen_info.lfb_width;
-       }
-}
diff --git a/arch/x86/kernel/sysfb_simplefb.c b/arch/x86/kernel/sysfb_simplefb.c
deleted file mode 100644 (file)
index 298fc1e..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Generic System Framebuffers on x86
- * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
- */
-
-/*
- * simple-framebuffer probing
- * Try to convert "screen_info" into a "simple-framebuffer" compatible mode.
- * If the mode is incompatible, we return "false" and let the caller create
- * legacy nodes instead.
- */
-
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/platform_data/simplefb.h>
-#include <linux/platform_device.h>
-#include <linux/screen_info.h>
-#include <asm/sysfb.h>
-
-static const char simplefb_resname[] = "BOOTFB";
-static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
-
-/* try parsing x86 screen_info into a simple-framebuffer mode struct */
-__init bool parse_mode(const struct screen_info *si,
-                      struct simplefb_platform_data *mode)
-{
-       const struct simplefb_format *f;
-       __u8 type;
-       unsigned int i;
-
-       type = si->orig_video_isVGA;
-       if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI)
-               return false;
-
-       for (i = 0; i < ARRAY_SIZE(formats); ++i) {
-               f = &formats[i];
-               if (si->lfb_depth == f->bits_per_pixel &&
-                   si->red_size == f->red.length &&
-                   si->red_pos == f->red.offset &&
-                   si->green_size == f->green.length &&
-                   si->green_pos == f->green.offset &&
-                   si->blue_size == f->blue.length &&
-                   si->blue_pos == f->blue.offset &&
-                   si->rsvd_size == f->transp.length &&
-                   si->rsvd_pos == f->transp.offset) {
-                       mode->format = f->name;
-                       mode->width = si->lfb_width;
-                       mode->height = si->lfb_height;
-                       mode->stride = si->lfb_linelength;
-                       return true;
-               }
-       }
-
-       return false;
-}
-
-__init int create_simplefb(const struct screen_info *si,
-                          const struct simplefb_platform_data *mode)
-{
-       struct platform_device *pd;
-       struct resource res;
-       u64 base, size;
-       u32 length;
-
-       /*
-        * If the 64BIT_BASE capability is set, ext_lfb_base will contain the
-        * upper half of the base address. Assemble the address, then make sure
-        * it is valid and we can actually access it.
-        */
-       base = si->lfb_base;
-       if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE)
-               base |= (u64)si->ext_lfb_base << 32;
-       if (!base || (u64)(resource_size_t)base != base) {
-               printk(KERN_DEBUG "sysfb: inaccessible VRAM base\n");
-               return -EINVAL;
-       }
-
-       /*
-        * Don't use lfb_size as IORESOURCE size, since it may contain the
-        * entire VMEM, and thus require huge mappings. Use just the part we
-        * need, that is, the part where the framebuffer is located. But verify
-        * that it does not exceed the advertised VMEM.
-        * Note that in case of VBE, the lfb_size is shifted by 16 bits for
-        * historical reasons.
-        */
-       size = si->lfb_size;
-       if (si->orig_video_isVGA == VIDEO_TYPE_VLFB)
-               size <<= 16;
-       length = mode->height * mode->stride;
-       if (length > size) {
-               printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n");
-               return -EINVAL;
-       }
-       length = PAGE_ALIGN(length);
-
-       /* setup IORESOURCE_MEM as framebuffer memory */
-       memset(&res, 0, sizeof(res));
-       res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-       res.name = simplefb_resname;
-       res.start = base;
-       res.end = res.start + length - 1;
-       if (res.end <= res.start)
-               return -EINVAL;
-
-       pd = platform_device_register_resndata(NULL, "simple-framebuffer", 0,
-                                              &res, 1, mode, sizeof(*mode));
-       return PTR_ERR_OR_ZERO(pd);
-}
index db0ea2d2d75a2127295c3aa962684dd3cf0cd01b..71f3d97f0c39926c7265b05f35a6e41a608c674f 100644 (file)
@@ -251,6 +251,38 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
 
          Say Y here to enable "download mode" by default.
 
+config SYSFB
+       bool
+       default y
+       depends on X86 || COMPILE_TEST
+
+config X86_SYSFB
+       bool "Mark VGA/VBE/EFI FB as generic system framebuffer"
+       depends on SYSFB
+       help
+         Firmwares often provide initial graphics framebuffers so the BIOS,
+         bootloader or kernel can show basic video-output during boot for
+         user-guidance and debugging. Historically, x86 used the VESA BIOS
+         Extensions and EFI-framebuffers for this, which are mostly limited
+         to x86.
+         This option, if enabled, marks VGA/VBE/EFI framebuffers as generic
+         framebuffers so the new generic system-framebuffer drivers can be
+         used on x86. If the framebuffer is not compatible with the generic
+         modes, it is advertised as fallback platform framebuffer so legacy
+         drivers like efifb, vesafb and uvesafb can pick it up.
+         If this option is not selected, all system framebuffers are always
+         marked as fallback platform framebuffers as usual.
+
+         Note: Legacy fbdev drivers, including vesafb, efifb, uvesafb, will
+         not be able to pick up generic system framebuffers if this option
+         is selected. You are highly encouraged to enable simplefb as
+         replacement if you select this option. simplefb can correctly deal
+         with generic system framebuffers. But you should still keep vesafb
+         and others enabled as fallback if a system framebuffer is
+         incompatible with simplefb.
+
+         If unsure, say Y.
+
 config TI_SCI_PROTOCOL
        tristate "TI System Control Interface (TISCI) Message Protocol"
        depends on TI_MESSAGE_MANAGER
index 5e013b6a3692ef61a617c8d46e914cdb3c47c4f0..ad78f78ffa8d081b600da3a9a110b78801053d27 100644 (file)
@@ -18,6 +18,8 @@ obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
 obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
 obj-$(CONFIG_FW_CFG_SYSFS)     += qemu_fw_cfg.o
 obj-$(CONFIG_QCOM_SCM)         += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
+obj-$(CONFIG_SYSFB)            += sysfb.o
+obj-$(CONFIG_X86_SYSFB)                += sysfb_simplefb.o
 obj-$(CONFIG_TI_SCI_PROTOCOL)  += ti_sci.o
 obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
 obj-$(CONFIG_TURRIS_MOX_RWTM)  += turris-mox-rwtm.o
index 467e94259679f28d95315bb5d66774926b020e1d..c02ff25dd47707090a2ab86ee4f330e467f878f5 100644 (file)
@@ -36,6 +36,8 @@ obj-$(CONFIG_LOAD_UEFI_KEYS)          += mokvar-table.o
 fake_map-y                             += fake_mem.o
 fake_map-$(CONFIG_X86)                 += x86_fake_mem.o
 
+obj-$(CONFIG_SYSFB)                    += sysfb_efi.o
+
 arm-obj-$(CONFIG_EFI)                  := efi-init.o arm-runtime.o
 obj-$(CONFIG_ARM)                      += $(arm-obj-y)
 obj-$(CONFIG_ARM64)                    += $(arm-obj-y)
diff --git a/drivers/firmware/efi/sysfb_efi.c b/drivers/firmware/efi/sysfb_efi.c
new file mode 100644 (file)
index 0000000..9f035b1
--- /dev/null
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic System Framebuffers on x86
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ *
+ * EFI Quirks Copyright (c) 2006 Edgar Hucek <gimli@dark-green.com>
+ */
+
+/*
+ * EFI Quirks
+ * Several EFI systems do not correctly advertise their boot framebuffers.
+ * Hence, we use this static table of known broken machines and fix up the
+ * information so framebuffer drivers can load correctly.
+ */
+
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/screen_info.h>
+#include <linux/sysfb.h>
+#include <video/vga.h>
+
+#include <asm/efi.h>
+
+enum {
+       OVERRIDE_NONE = 0x0,
+       OVERRIDE_BASE = 0x1,
+       OVERRIDE_STRIDE = 0x2,
+       OVERRIDE_HEIGHT = 0x4,
+       OVERRIDE_WIDTH = 0x8,
+};
+
+struct efifb_dmi_info efifb_dmi_list[] = {
+       [M_I17] = { "i17", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
+       [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050, OVERRIDE_NONE }, /* guess */
+       [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050, OVERRIDE_NONE },
+       [M_I24] = { "i24", 0x80010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE }, /* guess */
+       [M_I24_8_1] = { "imac8", 0xc0060000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
+       [M_I24_10_1] = { "imac10", 0xc0010000, 2048 * 4, 1920, 1080, OVERRIDE_NONE },
+       [M_I27_11_1] = { "imac11", 0xc0010000, 2560 * 4, 2560, 1440, OVERRIDE_NONE },
+       [M_MINI]= { "mini", 0x80000000, 2048 * 4, 1024, 768, OVERRIDE_NONE },
+       [M_MINI_3_1] = { "mini31", 0x40010000, 1024 * 4, 1024, 768, OVERRIDE_NONE },
+       [M_MINI_4_1] = { "mini41", 0xc0010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
+       [M_MB] = { "macbook", 0x80000000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
+       [M_MB_5_1] = { "macbook51", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
+       [M_MB_6_1] = { "macbook61", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
+       [M_MB_7_1] = { "macbook71", 0x80010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
+       [M_MBA] = { "mba", 0x80000000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
+       /* 11" Macbook Air 3,1 passes the wrong stride */
+       [M_MBA_3] = { "mba3", 0, 2048 * 4, 0, 0, OVERRIDE_STRIDE },
+       [M_MBP] = { "mbp", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
+       [M_MBP_2] = { "mbp2", 0, 0, 0, 0, OVERRIDE_NONE }, /* placeholder */
+       [M_MBP_2_2] = { "mbp22", 0x80010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
+       [M_MBP_SR] = { "mbp3", 0x80030000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
+       [M_MBP_4] = { "mbp4", 0xc0060000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
+       [M_MBP_5_1] = { "mbp51", 0xc0010000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
+       [M_MBP_5_2] = { "mbp52", 0xc0010000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
+       [M_MBP_5_3] = { "mbp53", 0xd0010000, 2048 * 4, 1440, 900, OVERRIDE_NONE },
+       [M_MBP_6_1] = { "mbp61", 0x90030000, 2048 * 4, 1920, 1200, OVERRIDE_NONE },
+       [M_MBP_6_2] = { "mbp62", 0x90030000, 2048 * 4, 1680, 1050, OVERRIDE_NONE },
+       [M_MBP_7_1] = { "mbp71", 0xc0010000, 2048 * 4, 1280, 800, OVERRIDE_NONE },
+       [M_MBP_8_2] = { "mbp82", 0x90010000, 1472 * 4, 1440, 900, OVERRIDE_NONE },
+       [M_UNKNOWN] = { NULL, 0, 0, 0, 0, OVERRIDE_NONE }
+};
+
+void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
+{
+       int i;
+
+       for (i = 0; i < M_UNKNOWN; i++) {
+               if (efifb_dmi_list[i].base != 0 &&
+                   !strcmp(opt, efifb_dmi_list[i].optname)) {
+                       si->lfb_base = efifb_dmi_list[i].base;
+                       si->lfb_linelength = efifb_dmi_list[i].stride;
+                       si->lfb_width = efifb_dmi_list[i].width;
+                       si->lfb_height = efifb_dmi_list[i].height;
+               }
+       }
+}
+
+#define choose_value(dmivalue, fwvalue, field, flags) ({       \
+               typeof(fwvalue) _ret_ = fwvalue;                \
+               if ((flags) & (field))                          \
+                       _ret_ = dmivalue;                       \
+               else if ((fwvalue) == 0)                        \
+                       _ret_ = dmivalue;                       \
+               _ret_;                                          \
+       })
+
+static int __init efifb_set_system(const struct dmi_system_id *id)
+{
+       struct efifb_dmi_info *info = id->driver_data;
+
+       if (info->base == 0 && info->height == 0 && info->width == 0 &&
+           info->stride == 0)
+               return 0;
+
+       /* Trust the bootloader over the DMI tables */
+       if (screen_info.lfb_base == 0) {
+#if defined(CONFIG_PCI)
+               struct pci_dev *dev = NULL;
+               int found_bar = 0;
+#endif
+               if (info->base) {
+                       screen_info.lfb_base = choose_value(info->base,
+                               screen_info.lfb_base, OVERRIDE_BASE,
+                               info->flags);
+
+#if defined(CONFIG_PCI)
+                       /* make sure that the address in the table is actually
+                        * on a VGA device's PCI BAR */
+
+                       for_each_pci_dev(dev) {
+                               int i;
+                               if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+                                       continue;
+                               for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+                                       resource_size_t start, end;
+                                       unsigned long flags;
+
+                                       flags = pci_resource_flags(dev, i);
+                                       if (!(flags & IORESOURCE_MEM))
+                                               continue;
+
+                                       if (flags & IORESOURCE_UNSET)
+                                               continue;
+
+                                       if (pci_resource_len(dev, i) == 0)
+                                               continue;
+
+                                       start = pci_resource_start(dev, i);
+                                       end = pci_resource_end(dev, i);
+                                       if (screen_info.lfb_base >= start &&
+                                           screen_info.lfb_base < end) {
+                                               found_bar = 1;
+                                               break;
+                                       }
+                               }
+                       }
+                       if (!found_bar)
+                               screen_info.lfb_base = 0;
+#endif
+               }
+       }
+       if (screen_info.lfb_base) {
+               screen_info.lfb_linelength = choose_value(info->stride,
+                       screen_info.lfb_linelength, OVERRIDE_STRIDE,
+                       info->flags);
+               screen_info.lfb_width = choose_value(info->width,
+                       screen_info.lfb_width, OVERRIDE_WIDTH,
+                       info->flags);
+               screen_info.lfb_height = choose_value(info->height,
+                       screen_info.lfb_height, OVERRIDE_HEIGHT,
+                       info->flags);
+               if (screen_info.orig_video_isVGA == 0)
+                       screen_info.orig_video_isVGA = VIDEO_TYPE_EFI;
+       } else {
+               screen_info.lfb_linelength = 0;
+               screen_info.lfb_width = 0;
+               screen_info.lfb_height = 0;
+               screen_info.orig_video_isVGA = 0;
+               return 0;
+       }
+
+       printk(KERN_INFO "efifb: dmi detected %s - framebuffer at 0x%08x "
+                        "(%dx%d, stride %d)\n", id->ident,
+                        screen_info.lfb_base, screen_info.lfb_width,
+                        screen_info.lfb_height, screen_info.lfb_linelength);
+
+       return 1;
+}
+
+#define EFIFB_DMI_SYSTEM_ID(vendor, name, enumid)              \
+       {                                                       \
+               efifb_set_system,                               \
+               name,                                           \
+               {                                               \
+                       DMI_MATCH(DMI_BIOS_VENDOR, vendor),     \
+                       DMI_MATCH(DMI_PRODUCT_NAME, name)       \
+               },                                              \
+               &efifb_dmi_list[enumid]                         \
+       }
+
+static const struct dmi_system_id efifb_dmi_system_table[] __initconst = {
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac4,1", M_I17),
+       /* At least one of these two will be right; maybe both? */
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac5,1", M_I20),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac5,1", M_I20),
+       /* At least one of these two will be right; maybe both? */
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac6,1", M_I24),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac6,1", M_I24),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac7,1", M_I20_SR),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac8,1", M_I24_8_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac10,1", M_I24_10_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac11,1", M_I27_11_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "Macmini1,1", M_MINI),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini3,1", M_MINI_3_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini4,1", M_MINI_4_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook1,1", M_MB),
+       /* At least one of these two will be right; maybe both? */
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook2,1", M_MB),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook2,1", M_MB),
+       /* At least one of these two will be right; maybe both? */
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook3,1", M_MB),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook3,1", M_MB),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook4,1", M_MB),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook5,1", M_MB_5_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook6,1", M_MB_6_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook7,1", M_MB_7_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir1,1", M_MBA),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir3,1", M_MBA_3),
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro1,1", M_MBP),
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,1", M_MBP_2),
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,2", M_MBP_2_2),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro2,1", M_MBP_2),
+       EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro3,1", M_MBP_SR),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro3,1", M_MBP_SR),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro4,1", M_MBP_4),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,1", M_MBP_5_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,2", M_MBP_5_2),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,3", M_MBP_5_3),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,1", M_MBP_6_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,2", M_MBP_6_2),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro7,1", M_MBP_7_1),
+       EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro8,2", M_MBP_8_2),
+       {},
+};
+
+/*
+ * Some devices have a portrait LCD but advertise a landscape resolution (and
+ * pitch). We simply swap width and height for these devices so that we can
+ * correctly deal with some of them coming with multiple resolutions.
+ */
+static const struct dmi_system_id efifb_dmi_swap_width_height[] __initconst = {
+       {
+               /*
+                * Lenovo MIIX310-10ICR, only some batches have the troublesome
+                * 800x1280 portrait screen. Luckily the portrait version has
+                * its own BIOS version, so we match on that.
+                */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "MIIX 310-10ICR"),
+                       DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1HCN44WW"),
+               },
+       },
+       {
+               /* Lenovo MIIX 320-10ICR with 800x1280 portrait screen */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_VERSION,
+                                       "Lenovo MIIX 320-10ICR"),
+               },
+       },
+       {
+               /* Lenovo D330 with 800x1280 or 1200x1920 portrait screen */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_VERSION,
+                                       "Lenovo ideapad D330-10IGM"),
+               },
+       },
+       {},
+};
+
+__init void sysfb_apply_efi_quirks(void)
+{
+       if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
+           !(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
+               dmi_check_system(efifb_dmi_system_table);
+
+       if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
+           dmi_check_system(efifb_dmi_swap_width_height)) {
+               u16 temp = screen_info.lfb_width;
+
+               screen_info.lfb_width = screen_info.lfb_height;
+               screen_info.lfb_height = temp;
+               screen_info.lfb_linelength = 4 * screen_info.lfb_width;
+       }
+}
diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c
new file mode 100644 (file)
index 0000000..1337515
--- /dev/null
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic System Framebuffers on x86
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * Simple-Framebuffer support for x86 systems
+ * Create a platform-device for any available boot framebuffer. The
+ * simple-framebuffer platform device is already available on DT systems, so
+ * this module parses the global "screen_info" object and creates a suitable
+ * platform device compatible with the "simple-framebuffer" DT object. If
+ * the framebuffer is incompatible, we instead create a legacy
+ * "vesa-framebuffer", "efi-framebuffer" or "platform-framebuffer" device and
+ * pass the screen_info as platform_data. This allows legacy drivers
+ * to pick these devices up without messing with simple-framebuffer drivers.
+ * The global "screen_info" is still valid at all times.
+ *
+ * If CONFIG_X86_SYSFB is not selected, we never register "simple-framebuffer"
+ * platform devices, but only use legacy framebuffer devices for
+ * backwards compatibility.
+ *
+ * TODO: We set the dev_id field of all platform-devices to 0. This allows
+ * other x86 OF/DT parsers to create such devices, too. However, they must
+ * start at offset 1 for this to work.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/platform_device.h>
+#include <linux/screen_info.h>
+#include <linux/sysfb.h>
+
+static __init int sysfb_init(void)
+{
+       struct screen_info *si = &screen_info;
+       struct simplefb_platform_data mode;
+       struct platform_device *pd;
+       const char *name;
+       bool compatible;
+       int ret;
+
+       sysfb_apply_efi_quirks();
+
+       /* try to create a simple-framebuffer device */
+       compatible = parse_mode(si, &mode);
+       if (compatible) {
+               ret = create_simplefb(si, &mode);
+               if (!ret)
+                       return 0;
+       }
+
+       /* if the FB is incompatible, create a legacy framebuffer device */
+       if (si->orig_video_isVGA == VIDEO_TYPE_EFI)
+               name = "efi-framebuffer";
+       else if (si->orig_video_isVGA == VIDEO_TYPE_VLFB)
+               name = "vesa-framebuffer";
+       else
+               name = "platform-framebuffer";
+
+       pd = platform_device_register_resndata(NULL, name, 0,
+                                              NULL, 0, si, sizeof(*si));
+       return PTR_ERR_OR_ZERO(pd);
+}
+
+/* must execute after PCI subsystem for EFI quirks */
+device_initcall(sysfb_init);
diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_simplefb.c
new file mode 100644 (file)
index 0000000..df89244
--- /dev/null
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic System Framebuffers on x86
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+/*
+ * simple-framebuffer probing
+ * Try to convert "screen_info" into a "simple-framebuffer" compatible mode.
+ * If the mode is incompatible, we return "false" and let the caller create
+ * legacy nodes instead.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/platform_device.h>
+#include <linux/screen_info.h>
+#include <linux/sysfb.h>
+
+static const char simplefb_resname[] = "BOOTFB";
+static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
+
+/* try parsing x86 screen_info into a simple-framebuffer mode struct */
+__init bool parse_mode(const struct screen_info *si,
+                      struct simplefb_platform_data *mode)
+{
+       const struct simplefb_format *f;
+       __u8 type;
+       unsigned int i;
+
+       type = si->orig_video_isVGA;
+       if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI)
+               return false;
+
+       for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+               f = &formats[i];
+               if (si->lfb_depth == f->bits_per_pixel &&
+                   si->red_size == f->red.length &&
+                   si->red_pos == f->red.offset &&
+                   si->green_size == f->green.length &&
+                   si->green_pos == f->green.offset &&
+                   si->blue_size == f->blue.length &&
+                   si->blue_pos == f->blue.offset &&
+                   si->rsvd_size == f->transp.length &&
+                   si->rsvd_pos == f->transp.offset) {
+                       mode->format = f->name;
+                       mode->width = si->lfb_width;
+                       mode->height = si->lfb_height;
+                       mode->stride = si->lfb_linelength;
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+__init int create_simplefb(const struct screen_info *si,
+                          const struct simplefb_platform_data *mode)
+{
+       struct platform_device *pd;
+       struct resource res;
+       u64 base, size;
+       u32 length;
+
+       /*
+        * If the 64BIT_BASE capability is set, ext_lfb_base will contain the
+        * upper half of the base address. Assemble the address, then make sure
+        * it is valid and we can actually access it.
+        */
+       base = si->lfb_base;
+       if (si->capabilities & VIDEO_CAPABILITY_64BIT_BASE)
+               base |= (u64)si->ext_lfb_base << 32;
+       if (!base || (u64)(resource_size_t)base != base) {
+               printk(KERN_DEBUG "sysfb: inaccessible VRAM base\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Don't use lfb_size as IORESOURCE size, since it may contain the
+        * entire VMEM, and thus require huge mappings. Use just the part we
+        * need, that is, the part where the framebuffer is located. But verify
+        * that it does not exceed the advertised VMEM.
+        * Note that in case of VBE, the lfb_size is shifted by 16 bits for
+        * historical reasons.
+        */
+       size = si->lfb_size;
+       if (si->orig_video_isVGA == VIDEO_TYPE_VLFB)
+               size <<= 16;
+       length = mode->height * mode->stride;
+       if (length > size) {
+               printk(KERN_WARNING "sysfb: VRAM smaller than advertised\n");
+               return -EINVAL;
+       }
+       length = PAGE_ALIGN(length);
+
+       /* setup IORESOURCE_MEM as framebuffer memory */
+       memset(&res, 0, sizeof(res));
+       res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+       res.name = simplefb_resname;
+       res.start = base;
+       res.end = res.start + length - 1;
+       if (res.end <= res.start)
+               return -EINVAL;
+
+       pd = platform_device_register_resndata(NULL, "simple-framebuffer", 0,
+                                              &res, 1, mode, sizeof(*mode));
+       return PTR_ERR_OR_ZERO(pd);
+}
diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h
new file mode 100644 (file)
index 0000000..3e53557
--- /dev/null
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _LINUX_SYSFB_H
+#define _LINUX_SYSFB_H
+
+/*
+ * Generic System Framebuffers on x86
+ * Copyright (c) 2012-2013 David Herrmann <dh.herrmann@gmail.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/screen_info.h>
+
+enum {
+       M_I17,          /* 17-Inch iMac */
+       M_I20,          /* 20-Inch iMac */
+       M_I20_SR,       /* 20-Inch iMac (Santa Rosa) */
+       M_I24,          /* 24-Inch iMac */
+       M_I24_8_1,      /* 24-Inch iMac, 8,1th gen */
+       M_I24_10_1,     /* 24-Inch iMac, 10,1th gen */
+       M_I27_11_1,     /* 27-Inch iMac, 11,1th gen */
+       M_MINI,         /* Mac Mini */
+       M_MINI_3_1,     /* Mac Mini, 3,1th gen */
+       M_MINI_4_1,     /* Mac Mini, 4,1th gen */
+       M_MB,           /* MacBook */
+       M_MB_2,         /* MacBook, 2nd rev. */
+       M_MB_3,         /* MacBook, 3rd rev. */
+       M_MB_5_1,       /* MacBook, 5th rev. */
+       M_MB_6_1,       /* MacBook, 6th rev. */
+       M_MB_7_1,       /* MacBook, 7th rev. */
+       M_MB_SR,        /* MacBook, 2nd gen, (Santa Rosa) */
+       M_MBA,          /* MacBook Air */
+       M_MBA_3,        /* Macbook Air, 3rd rev */
+       M_MBP,          /* MacBook Pro */
+       M_MBP_2,        /* MacBook Pro 2nd gen */
+       M_MBP_2_2,      /* MacBook Pro 2,2nd gen */
+       M_MBP_SR,       /* MacBook Pro (Santa Rosa) */
+       M_MBP_4,        /* MacBook Pro, 4th gen */
+       M_MBP_5_1,      /* MacBook Pro, 5,1th gen */
+       M_MBP_5_2,      /* MacBook Pro, 5,2th gen */
+       M_MBP_5_3,      /* MacBook Pro, 5,3rd gen */
+       M_MBP_6_1,      /* MacBook Pro, 6,1th gen */
+       M_MBP_6_2,      /* MacBook Pro, 6,2th gen */
+       M_MBP_7_1,      /* MacBook Pro, 7,1th gen */
+       M_MBP_8_2,      /* MacBook Pro, 8,2nd gen */
+       M_UNKNOWN       /* placeholder */
+};
+
+struct efifb_dmi_info {
+       char *optname;
+       unsigned long base;
+       int stride;
+       int width;
+       int height;
+       int flags;
+};
+
+#ifdef CONFIG_EFI
+
+extern struct efifb_dmi_info efifb_dmi_list[];
+void sysfb_apply_efi_quirks(void);
+
+#else /* CONFIG_EFI */
+
+static inline void sysfb_apply_efi_quirks(void)
+{
+}
+
+#endif /* CONFIG_EFI */
+
+#ifdef CONFIG_X86_SYSFB
+
+bool parse_mode(const struct screen_info *si,
+               struct simplefb_platform_data *mode);
+int create_simplefb(const struct screen_info *si,
+                   const struct simplefb_platform_data *mode);
+
+#else /* CONFIG_X86_SYSFB */
+
+static inline bool parse_mode(const struct screen_info *si,
+                             struct simplefb_platform_data *mode)
+{
+       return false;
+}
+
+static inline int create_simplefb(const struct screen_info *si,
+                                 const struct simplefb_platform_data *mode)
+{
+       return -EINVAL;
+}
+
+#endif /* CONFIG_X86_SYSFB */
+
+#endif /* _LINUX_SYSFB_H */