202 lines
5.8 KiB
Diff
202 lines
5.8 KiB
Diff
|
From a5f742d7ba70c702bcf67dd1fd8d5dde3f5042fc Mon Sep 17 00:00:00 2001
|
||
|
From: Hans de Goede <hdegoede@redhat.com>
|
||
|
Date: Tue, 3 Jul 2018 17:43:10 +0200
|
||
|
Subject: [PATCH 6/7] efifb: Copy the ACPI BGRT boot graphics to the
|
||
|
framebuffer
|
||
|
|
||
|
On systems where fbcon is configured for deferred console takeover, the
|
||
|
intend is for the framebuffer to show the boot graphics (e.g a vendor
|
||
|
logo) until some message (e.g. an error) is printed or a graphical
|
||
|
session takes over.
|
||
|
|
||
|
Some firmware relies on the OS to show the boot graphics.
|
||
|
|
||
|
This patch adds support to efifb to show the boot graphics and
|
||
|
automatically enables this when fbcon is configured for deferred
|
||
|
console takeover.
|
||
|
|
||
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||
|
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
|
||
|
---
|
||
|
drivers/video/fbdev/efifb.c | 140 ++++++++++++++++++++++++++++++++++++
|
||
|
1 file changed, 140 insertions(+)
|
||
|
|
||
|
diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
|
||
|
index 46a4484e3da7..fa01eecc0a55 100644
|
||
|
--- a/drivers/video/fbdev/efifb.c
|
||
|
+++ b/drivers/video/fbdev/efifb.c
|
||
|
@@ -9,16 +9,39 @@
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/efi.h>
|
||
|
+#include <linux/efi-bgrt.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/fb.h>
|
||
|
#include <linux/pci.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
+#include <linux/printk.h>
|
||
|
#include <linux/screen_info.h>
|
||
|
#include <video/vga.h>
|
||
|
#include <asm/efi.h>
|
||
|
#include <drm/drm_utils.h> /* For drm_get_panel_orientation_quirk */
|
||
|
#include <drm/drm_connector.h> /* For DRM_MODE_PANEL_ORIENTATION_* */
|
||
|
|
||
|
+struct bmp_file_header {
|
||
|
+ u16 id;
|
||
|
+ u32 file_size;
|
||
|
+ u32 reserved;
|
||
|
+ u32 bitmap_offset;
|
||
|
+} __packed;
|
||
|
+
|
||
|
+struct bmp_dib_header {
|
||
|
+ u32 dib_header_size;
|
||
|
+ s32 width;
|
||
|
+ s32 height;
|
||
|
+ u16 planes;
|
||
|
+ u16 bpp;
|
||
|
+ u32 compression;
|
||
|
+ u32 bitmap_size;
|
||
|
+ u32 horz_resolution;
|
||
|
+ u32 vert_resolution;
|
||
|
+ u32 colors_used;
|
||
|
+ u32 colors_important;
|
||
|
+} __packed;
|
||
|
+
|
||
|
static bool request_mem_succeeded = false;
|
||
|
static bool nowc = false;
|
||
|
|
||
|
@@ -66,6 +89,121 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * If fbcon deffered console takeover is configured, the intent is for the
|
||
|
+ * framebuffer to show the boot graphics (e.g. vendor logo) until there is some
|
||
|
+ * (error) message to display. But the boot graphics may have been destroyed by
|
||
|
+ * e.g. option ROM output, detect this and restore the boot graphics.
|
||
|
+ */
|
||
|
+#if defined CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER && \
|
||
|
+ defined CONFIG_ACPI_BGRT
|
||
|
+static void efifb_copy_bmp(u8 *src, u32 *dst, int width, struct screen_info *si)
|
||
|
+{
|
||
|
+ u8 r, g, b;
|
||
|
+
|
||
|
+ while (width--) {
|
||
|
+ b = *src++;
|
||
|
+ g = *src++;
|
||
|
+ r = *src++;
|
||
|
+ *dst++ = (r << si->red_pos) |
|
||
|
+ (g << si->green_pos) |
|
||
|
+ (b << si->blue_pos);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void efifb_show_boot_graphics(struct fb_info *info)
|
||
|
+{
|
||
|
+ u32 bmp_width, bmp_height, bmp_pitch, screen_pitch, dst_x, y, src_y;
|
||
|
+ struct screen_info *si = &screen_info;
|
||
|
+ struct bmp_file_header *file_header;
|
||
|
+ struct bmp_dib_header *dib_header;
|
||
|
+ void *bgrt_image = NULL;
|
||
|
+ u8 *dst = info->screen_base;
|
||
|
+
|
||
|
+ if (!bgrt_tab.image_address) {
|
||
|
+ pr_info("efifb: No BGRT, not showing boot graphics\n");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Avoid flashing the logo if we're going to print std probe messages */
|
||
|
+ if (console_loglevel > CONSOLE_LOGLEVEL_QUIET)
|
||
|
+ return;
|
||
|
+
|
||
|
+ /* bgrt_tab.status is unreliable, so we don't check it */
|
||
|
+
|
||
|
+ if (si->lfb_depth != 32) {
|
||
|
+ pr_info("efifb: not 32 bits, not showing boot graphics\n");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ bgrt_image = memremap(bgrt_tab.image_address, bgrt_image_size,
|
||
|
+ MEMREMAP_WB);
|
||
|
+ if (!bgrt_image) {
|
||
|
+ pr_warn("efifb: Ignoring BGRT: failed to map image memory\n");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (bgrt_image_size < (sizeof(*file_header) + sizeof(*dib_header)))
|
||
|
+ goto error;
|
||
|
+
|
||
|
+ file_header = bgrt_image;
|
||
|
+ if (file_header->id != 0x4d42 || file_header->reserved != 0)
|
||
|
+ goto error;
|
||
|
+
|
||
|
+ dib_header = bgrt_image + sizeof(*file_header);
|
||
|
+ if (dib_header->dib_header_size != 40 || dib_header->width < 0 ||
|
||
|
+ dib_header->planes != 1 || dib_header->bpp != 24 ||
|
||
|
+ dib_header->compression != 0)
|
||
|
+ goto error;
|
||
|
+
|
||
|
+ bmp_width = dib_header->width;
|
||
|
+ bmp_height = abs(dib_header->height);
|
||
|
+ bmp_pitch = round_up(3 * bmp_width, 4);
|
||
|
+ screen_pitch = si->lfb_linelength;
|
||
|
+
|
||
|
+ if ((file_header->bitmap_offset + bmp_pitch * bmp_height) >
|
||
|
+ bgrt_image_size)
|
||
|
+ goto error;
|
||
|
+
|
||
|
+ if ((bgrt_tab.image_offset_x + bmp_width) > si->lfb_width ||
|
||
|
+ (bgrt_tab.image_offset_y + bmp_height) > si->lfb_height)
|
||
|
+ goto error;
|
||
|
+
|
||
|
+ pr_info("efifb: showing boot graphics\n");
|
||
|
+
|
||
|
+ for (y = 0; y < si->lfb_height; y++, dst += si->lfb_linelength) {
|
||
|
+ /* Only background? */
|
||
|
+ if (y < bgrt_tab.image_offset_y ||
|
||
|
+ y >= (bgrt_tab.image_offset_y + bmp_height)) {
|
||
|
+ memset(dst, 0, 4 * si->lfb_width);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ src_y = y - bgrt_tab.image_offset_y;
|
||
|
+ /* Positive header height means upside down row order */
|
||
|
+ if (dib_header->height > 0)
|
||
|
+ src_y = (bmp_height - 1) - src_y;
|
||
|
+
|
||
|
+ memset(dst, 0, bgrt_tab.image_offset_x * 4);
|
||
|
+ dst_x = bgrt_tab.image_offset_x;
|
||
|
+ efifb_copy_bmp(bgrt_image + file_header->bitmap_offset +
|
||
|
+ src_y * bmp_pitch,
|
||
|
+ (u32 *)dst + dst_x, bmp_width, si);
|
||
|
+ dst_x += bmp_width;
|
||
|
+ memset((u32 *)dst + dst_x, 0, (si->lfb_width - dst_x) * 4);
|
||
|
+ }
|
||
|
+
|
||
|
+ memunmap(bgrt_image);
|
||
|
+ return;
|
||
|
+
|
||
|
+error:
|
||
|
+ memunmap(bgrt_image);
|
||
|
+ pr_warn("efifb: Ignoring BGRT: unexpected or invalid BMP data\n");
|
||
|
+}
|
||
|
+#else
|
||
|
+static inline void efifb_show_boot_graphics(struct fb_info *info) {}
|
||
|
+#endif
|
||
|
+
|
||
|
static void efifb_destroy(struct fb_info *info)
|
||
|
{
|
||
|
if (info->screen_base)
|
||
|
@@ -283,6 +421,8 @@ static int efifb_probe(struct platform_device *dev)
|
||
|
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",
|
||
|
--
|
||
|
2.18.0
|
||
|
|