diff options
author | charveey | 2020-02-12 05:09:47 +0000 |
---|---|---|
committer | charveey | 2020-02-12 05:09:47 +0000 |
commit | 6255045d3aa088162701c43729ce67f382ebdfeb (patch) | |
tree | 93528583fdd33f40355cd2f980a5faa2fe969a85 /0007-bootsplash.patch | |
parent | a10bc0064f2a2af38bb346929e5b3410f2c67182 (diff) | |
download | aur-6255045d3aa088162701c43729ce67f382ebdfeb.tar.gz |
updated to 5.5.3
Diffstat (limited to '0007-bootsplash.patch')
-rwxr-xr-x[-rw-r--r--] | 0007-bootsplash.patch | 973 |
1 files changed, 696 insertions, 277 deletions
diff --git a/0007-bootsplash.patch b/0007-bootsplash.patch index 2785c5e65277..924f23f33ce9 100644..100755 --- a/0007-bootsplash.patch +++ b/0007-bootsplash.patch @@ -1,327 +1,746 @@ +diff --git a/MAINTAINERS b/MAINTAINERS +index a74227ad082e..b5633b56391e 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -2705,6 +2705,14 @@ S: Supported + F: drivers/net/bonding/ + F: include/uapi/linux/if_bonding.h + ++BOOTSPLASH ++M: Max Staudt <mstaudt@suse.de> ++L: linux-fbdev@vger.kernel.org ++S: Maintained ++F: drivers/video/fbdev/core/bootsplash*.* ++F: drivers/video/fbdev/core/dummycon.c ++F: include/linux/bootsplash.h ++ + BPF (Safe dynamic programs and tools) + M: Alexei Starovoitov <ast@kernel.org> + M: Daniel Borkmann <daniel@iogearbox.net> +diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig +index 7f1f1fbcef9e..f3ff976266fe 100644 +--- a/drivers/video/console/Kconfig ++++ b/drivers/video/console/Kconfig +@@ -151,6 +151,30 @@ config FRAMEBUFFER_CONSOLE_ROTATION + such that other users of the framebuffer will remain normally + oriented. + ++config BOOTSPLASH ++ bool "Bootup splash screen" ++ depends on FRAMEBUFFER_CONSOLE ++ ---help--- ++ This option enables the Linux bootsplash screen. ++ ++ The bootsplash is a full-screen logo or animation indicating a ++ booting system. It replaces the classic scrolling text with a ++ graphical alternative, similar to other systems. ++ ++ Since this is technically implemented as a hook on top of fbcon, ++ it can only work if the FRAMEBUFFER_CONSOLE is enabled and a ++ framebuffer driver is active. Thus, to get a text-free boot, ++ the system needs to boot with vesafb, efifb, or similar. ++ ++ Once built into the kernel, the bootsplash needs to be enabled ++ with bootsplash.enabled=1 and a splash file needs to be supplied. ++ ++ Further documentation can be found in: ++ Documentation/fb/bootsplash.txt ++ ++ If unsure, say N. ++ This is typically used by distributors and system integrators. ++ + config STI_CONSOLE + bool "STI text console" + depends on PARISC +diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile +index 73493bbd7a15..66895321928e 100644 +--- a/drivers/video/fbdev/core/Makefile ++++ b/drivers/video/fbdev/core/Makefile +@@ -29,3 +29,6 @@ obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o + obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o + obj-$(CONFIG_FB_SVGALIB) += svgalib.o + obj-$(CONFIG_FB_DDC) += fb_ddc.o ++ ++obj-$(CONFIG_BOOTSPLASH) += bootsplash.o bootsplash_render.o \ ++ dummyblit.o diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c -index 815b007f81ca..c8642142cfea 100644 ---- a/drivers/video/fbdev/core/bootsplash.c +new file mode 100644 +index 000000000000..e449755af268 +--- /dev/null +++ b/drivers/video/fbdev/core/bootsplash.c -@@ -53,6 +53,14 @@ static void splash_callback_redraw_vc(struct work_struct *ignored) - console_unlock(); - } - -+static void splash_callback_animation(struct work_struct *ignored) +@@ -0,0 +1,294 @@ ++/* ++ * Kernel based bootsplash. ++ * ++ * (Main file: Glue code, workers, timer, PM, kernel and userland API) ++ * ++ * Authors: ++ * Max Staudt <mstaudt@suse.de> ++ * ++ * SPDX-License-Identifier: GPL-2.0 ++ */ ++ ++#define pr_fmt(fmt) "bootsplash: " fmt ++ ++ ++#include <linux/atomic.h> ++#include <linux/bootsplash.h> ++#include <linux/console.h> ++#include <linux/device.h> /* dev_warn() */ ++#include <linux/fb.h> ++#include <linux/fs.h> ++#include <linux/kernel.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/platform_device.h> ++#include <linux/printk.h> ++#include <linux/selection.h> /* console_blanked */ ++#include <linux/stringify.h> ++#include <linux/types.h> ++#include <linux/vmalloc.h> ++#include <linux/vt_kern.h> ++#include <linux/workqueue.h> ++ ++#include "bootsplash_internal.h" ++ ++ ++/* ++ * We only have one splash screen, so let's keep a single ++ * instance of the internal state. ++ */ ++static struct splash_priv splash_state; ++ ++ ++static void splash_callback_redraw_vc(struct work_struct *ignored) ++{ ++ if (console_blanked) ++ return; ++ ++ console_lock(); ++ if (vc_cons[fg_console].d) ++ update_screen(vc_cons[fg_console].d); ++ console_unlock(); ++} ++ ++ ++static bool is_fb_compatible(const struct fb_info *info) +{ -+ if (bootsplash_would_render_now()) { -+ /* This will also re-schedule this delayed worker */ -+ splash_callback_redraw_vc(ignored); ++ if (!(info->flags & FBINFO_BE_MATH) ++ != !fb_be_math((struct fb_info *)info)) { ++ dev_warn(info->device, ++ "Can't draw on foreign endianness framebuffer.\n"); ++ ++ return false; + } ++ ++ if (info->flags & FBINFO_MISC_TILEBLITTING) { ++ dev_warn(info->device, ++ "Can't draw splash on tiling framebuffer.\n"); ++ ++ return false; ++ } ++ ++ if (info->fix.type != FB_TYPE_PACKED_PIXELS ++ || (info->fix.visual != FB_VISUAL_TRUECOLOR ++ && info->fix.visual != FB_VISUAL_DIRECTCOLOR)) { ++ dev_warn(info->device, ++ "Can't draw splash on non-packed or non-truecolor framebuffer.\n"); ++ ++ dev_warn(info->device, ++ " type: %u visual: %u\n", ++ info->fix.type, info->fix.visual); ++ ++ return false; ++ } ++ ++ if (info->var.bits_per_pixel != 16 ++ && info->var.bits_per_pixel != 24 ++ && info->var.bits_per_pixel != 32) { ++ dev_warn(info->device, ++ "We only support drawing on framebuffers with 16, 24, or 32 bpp, not %d.\n", ++ info->var.bits_per_pixel); ++ ++ return false; ++ } ++ ++ return true; +} + - - static bool is_fb_compatible(const struct fb_info *info) - { -@@ -103,17 +111,44 @@ static bool is_fb_compatible(const struct fb_info *info) - */ - void bootsplash_render_full(struct fb_info *info) - { -+ bool is_update = false; -+ - mutex_lock(&splash_state.data_lock); - -- if (!is_fb_compatible(info)) -- goto out; -+ /* -+ * If we've painted on this FB recently, we don't have to do -+ * the sanity checks and background drawing again. -+ */ -+ if (splash_state.splash_fb == info) -+ is_update = true; + ++/* ++ * Called by fbcon_switch() when an instance is activated or refreshed. ++ */ ++void bootsplash_render_full(struct fb_info *info) ++{ ++ if (!is_fb_compatible(info)) ++ return; + -+ if (!is_update) { -+ /* Check whether we actually support this FB. */ -+ splash_state.splash_fb = NULL; ++ bootsplash_do_render_background(info); ++} + -+ if (!is_fb_compatible(info)) -+ goto out; + -+ /* Draw the background only once */ -+ bootsplash_do_render_background(info, splash_state.file); - -- bootsplash_do_render_background(info, splash_state.file); -+ /* Mark this FB as last seen */ -+ splash_state.splash_fb = info; ++/* ++ * External status enquiry and on/off switch ++ */ ++bool bootsplash_would_render_now(void) ++{ ++ return !oops_in_progress ++ && !console_blanked ++ && bootsplash_is_enabled(); ++} ++ ++bool bootsplash_is_enabled(void) ++{ ++ bool was_enabled; ++ ++ /* Make sure we have the newest state */ ++ smp_rmb(); ++ ++ was_enabled = test_bit(0, &splash_state.enabled); ++ ++ return was_enabled; ++} ++ ++void bootsplash_disable(void) ++{ ++ int was_enabled; ++ ++ was_enabled = test_and_clear_bit(0, &splash_state.enabled); ++ ++ if (was_enabled) { ++ if (oops_in_progress) { ++ /* Redraw screen now so we can see a panic */ ++ if (vc_cons[fg_console].d) ++ update_screen(vc_cons[fg_console].d); ++ } else { ++ /* No urgency, redraw at next opportunity */ ++ schedule_work(&splash_state.work_redraw_vc); ++ } + } - -- bootsplash_do_render_pictures(info, splash_state.file); -+ bootsplash_do_render_pictures(info, splash_state.file, is_update); - - bootsplash_do_render_flush(info); - -+ bootsplash_do_step_animations(splash_state.file); ++} + -+ /* Schedule update for animated splash screens */ -+ if (splash_state.file->frame_ms > 0) -+ schedule_delayed_work(&splash_state.dwork_animation, -+ msecs_to_jiffies( -+ splash_state.file->frame_ms)); ++void bootsplash_enable(void) ++{ ++ bool was_enabled; + - out: - mutex_unlock(&splash_state.data_lock); - } -@@ -169,8 +204,14 @@ void bootsplash_enable(void) - - was_enabled = test_and_set_bit(0, &splash_state.enabled); - -- if (!was_enabled) -+ if (!was_enabled) { -+ /* Force a full redraw when the splash is re-activated */ -+ mutex_lock(&splash_state.data_lock); -+ splash_state.splash_fb = NULL; -+ mutex_unlock(&splash_state.data_lock); -+ - schedule_work(&splash_state.work_redraw_vc); ++ if (oops_in_progress) ++ return; ++ ++ was_enabled = test_and_set_bit(0, &splash_state.enabled); ++ ++ if (!was_enabled) ++ schedule_work(&splash_state.work_redraw_vc); ++} ++ ++ ++/* ++ * Userland API via platform device in sysfs ++ */ ++static ssize_t splash_show_enabled(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", bootsplash_is_enabled()); ++} ++ ++static ssize_t splash_store_enabled(struct device *device, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ bool enable; ++ int err; ++ ++ if (!buf || !count) ++ return -EFAULT; ++ ++ err = kstrtobool(buf, &enable); ++ if (err) ++ return err; ++ ++ if (enable) ++ bootsplash_enable(); ++ else ++ bootsplash_disable(); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(enabled, 0644, splash_show_enabled, splash_store_enabled); ++ ++ ++static struct attribute *splash_dev_attrs[] = { ++ &dev_attr_enabled.attr, ++ NULL ++}; ++ ++ATTRIBUTE_GROUPS(splash_dev); ++ ++ ++ ++ ++/* ++ * Power management fixup via platform device ++ * ++ * When the system is woken from sleep or restored after hibernating, we ++ * cannot expect the screen contents to still be present in video RAM. ++ * Thus, we have to redraw the splash if we're currently active. ++ */ ++static int splash_resume(struct device *device) ++{ ++ if (bootsplash_would_render_now()) ++ schedule_work(&splash_state.work_redraw_vc); ++ ++ return 0; ++} ++ ++static int splash_suspend(struct device *device) ++{ ++ cancel_work_sync(&splash_state.work_redraw_vc); ++ ++ return 0; ++} ++ ++ ++static const struct dev_pm_ops splash_pm_ops = { ++ .thaw = splash_resume, ++ .restore = splash_resume, ++ .resume = splash_resume, ++ .suspend = splash_suspend, ++ .freeze = splash_suspend, ++}; ++ ++static struct platform_driver splash_driver = { ++ .driver = { ++ .name = "bootsplash", ++ .pm = &splash_pm_ops, ++ }, ++}; ++ ++ ++/* ++ * Main init ++ */ ++void bootsplash_init(void) ++{ ++ int ret; ++ ++ /* Initialized already? */ ++ if (splash_state.splash_device) ++ return; ++ ++ ++ /* Register platform device to export user API */ ++ ret = platform_driver_register(&splash_driver); ++ if (ret) { ++ pr_err("platform_driver_register() failed: %d\n", ret); ++ goto err; + } - } - - -@@ -227,6 +268,14 @@ ATTRIBUTE_GROUPS(splash_dev); - */ - static int splash_resume(struct device *device) - { -+ /* -+ * Force full redraw on resume since we've probably lost the -+ * framebuffer's contents meanwhile -+ */ -+ mutex_lock(&splash_state.data_lock); -+ splash_state.splash_fb = NULL; -+ mutex_unlock(&splash_state.data_lock); + - if (bootsplash_would_render_now()) - schedule_work(&splash_state.work_redraw_vc); - -@@ -235,6 +284,7 @@ static int splash_resume(struct device *device) - - static int splash_suspend(struct device *device) - { -+ cancel_delayed_work_sync(&splash_state.dwork_animation); - cancel_work_sync(&splash_state.work_redraw_vc); - - return 0; -@@ -296,6 +346,8 @@ void bootsplash_init(void) - set_bit(0, &splash_state.enabled); - - INIT_WORK(&splash_state.work_redraw_vc, splash_callback_redraw_vc); -+ INIT_DELAYED_WORK(&splash_state.dwork_animation, -+ splash_callback_animation); - - - if (!splash_state.bootfile || !strlen(splash_state.bootfile)) ++ splash_state.splash_device ++ = platform_device_alloc("bootsplash", 0); ++ ++ if (!splash_state.splash_device) ++ goto err_driver; ++ ++ splash_state.splash_device->dev.groups = splash_dev_groups; ++ ++ ret = platform_device_add(splash_state.splash_device); ++ if (ret) { ++ pr_err("platform_device_add() failed: %d\n", ret); ++ goto err_device; ++ } ++ ++ ++ INIT_WORK(&splash_state.work_redraw_vc, splash_callback_redraw_vc); ++ ++ return; ++ ++err_device: ++ platform_device_put(splash_state.splash_device); ++ splash_state.splash_device = NULL; ++err_driver: ++ platform_driver_unregister(&splash_driver); ++err: ++ pr_err("Failed to initialize.\n"); ++} diff --git a/drivers/video/fbdev/core/bootsplash_internal.h b/drivers/video/fbdev/core/bootsplash_internal.h -index 0acb383aa4e3..b3a74835d90f 100644 ---- a/drivers/video/fbdev/core/bootsplash_internal.h +new file mode 100644 +index 000000000000..b11da5cb90bf +--- /dev/null +++ b/drivers/video/fbdev/core/bootsplash_internal.h -@@ -37,6 +37,8 @@ struct splash_pic_priv { - - struct splash_blob_priv *blobs; - u16 blobs_loaded; +@@ -0,0 +1,55 @@ ++/* ++ * Kernel based bootsplash. ++ * ++ * (Internal data structures used at runtime) ++ * ++ * Authors: ++ * Max Staudt <mstaudt@suse.de> ++ * ++ * SPDX-License-Identifier: GPL-2.0 ++ */ + -+ u16 anim_nextframe; - }; - - -@@ -45,6 +47,12 @@ struct splash_file_priv { - const struct splash_file_header *header; - - struct splash_pic_priv *pics; ++#ifndef __BOOTSPLASH_INTERNAL_H ++#define __BOOTSPLASH_INTERNAL_H ++ ++ ++#include <linux/types.h> ++#include <linux/fb.h> ++#include <linux/kernel.h> ++#include <linux/mutex.h> ++#include <linux/spinlock.h> + ++ ++/* ++ * Runtime types ++ */ ++struct splash_priv { + /* -+ * A local copy of the frame delay in the header. -+ * We modify it to keep the code simple. ++ * Enabled/disabled state, to be used with atomic bit operations. ++ * Bit 0: 0 = Splash hidden ++ * 1 = Splash shown ++ * ++ * Note: fbcon.c uses this twice, by calling ++ * bootsplash_would_render_now() in set_blitting_type() and ++ * in fbcon_switch(). ++ * This is racy, but eventually consistent: Turning the ++ * splash on/off will cause a redraw, which calls ++ * fbcon_switch(), which calls set_blitting_type(). ++ * So the last on/off toggle will make things consistent. + */ -+ u16 frame_ms; - }; - - -@@ -71,6 +79,7 @@ struct splash_priv { - struct platform_device *splash_device; - - struct work_struct work_redraw_vc; -+ struct delayed_work dwork_animation; - - /* Splash data structures including lock for everything below */ - struct mutex data_lock; -@@ -88,8 +97,10 @@ struct splash_priv { - void bootsplash_do_render_background(struct fb_info *info, - const struct splash_file_priv *fp); - void bootsplash_do_render_pictures(struct fb_info *info, -- const struct splash_file_priv *fp); -+ const struct splash_file_priv *fp, -+ bool is_update); - void bootsplash_do_render_flush(struct fb_info *info); -+void bootsplash_do_step_animations(struct splash_file_priv *fp); - - - void bootsplash_free_file(struct splash_file_priv *fp); -diff --git a/drivers/video/fbdev/core/bootsplash_load.c b/drivers/video/fbdev/core/bootsplash_load.c -index fd807571ab7d..1f661b2d4cc9 100644 ---- a/drivers/video/fbdev/core/bootsplash_load.c -+++ b/drivers/video/fbdev/core/bootsplash_load.c -@@ -71,6 +71,7 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device, - { - const struct firmware *fw; - struct splash_file_priv *fp; -+ bool have_anim = false; - unsigned int i; - const u8 *walker; - -@@ -135,6 +136,13 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device, - goto err; - } - -+ if (ph->anim_type > SPLASH_ANIM_LOOP_FORWARD) { -+ pr_warn("Picture %u: Unsupported animation type %u.\n", -+ i, ph->anim_type); ++ unsigned long enabled; + -+ ph->anim_type = SPLASH_ANIM_NONE; -+ } ++ /* Our gateway to userland via sysfs */ ++ struct platform_device *splash_device; ++ ++ struct work_struct work_redraw_vc; ++}; + - pp->pic_header = ph; - pp->blobs = vzalloc(ph->num_blobs - * sizeof(struct splash_blob_priv)); -@@ -202,6 +210,7 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device, - /* Walk over pictures and ensure all blob slots are filled */ - for (i = 0; i < fp->header->num_pics; i++) { - struct splash_pic_priv *pp = &fp->pics[i]; -+ const struct splash_pic_header *ph = pp->pic_header; - - if (pp->blobs_loaded != pp->pic_header->num_blobs) { - pr_err("Picture %u doesn't have all blob slots filled.\n", -@@ -209,8 +218,20 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device, - - goto err; - } + -+ if (ph->anim_type -+ && ph->num_blobs > 1 -+ && ph->anim_loop < pp->blobs_loaded) -+ have_anim = true; - } - -+ if (!have_anim) -+ /* Disable animation timer if there is nothing to animate */ -+ fp->frame_ms = 0; -+ else -+ /* Enforce minimum delay between frames */ -+ fp->frame_ms = max((u16)20, fp->header->frame_ms); + - pr_info("Loaded (%ld bytes, %u pics, %u blobs).\n", - fw->size, - fp->header->num_pics, ++/* ++ * Rendering functions ++ */ ++void bootsplash_do_render_background(struct fb_info *info); ++ ++#endif diff --git a/drivers/video/fbdev/core/bootsplash_render.c b/drivers/video/fbdev/core/bootsplash_render.c -index 07e3a4eab811..76033606ca8a 100644 ---- a/drivers/video/fbdev/core/bootsplash_render.c +new file mode 100644 +index 000000000000..4d7e0117f653 +--- /dev/null +++ b/drivers/video/fbdev/core/bootsplash_render.c -@@ -148,7 +148,8 @@ void bootsplash_do_render_background(struct fb_info *info, - - - void bootsplash_do_render_pictures(struct fb_info *info, -- const struct splash_file_priv *fp) -+ const struct splash_file_priv *fp, -+ bool is_update) - { - unsigned int i; - -@@ -161,7 +162,11 @@ void bootsplash_do_render_pictures(struct fb_info *info, - if (pp->blobs_loaded < 1) - continue; - -- bp = &pp->blobs[0]; -+ /* Skip static pictures when refreshing animations */ -+ if (ph->anim_type == SPLASH_ANIM_NONE && is_update) -+ continue; +@@ -0,0 +1,93 @@ ++/* ++ * Kernel based bootsplash. ++ * ++ * (Rendering functions) ++ * ++ * Authors: ++ * Max Staudt <mstaudt@suse.de> ++ * ++ * SPDX-License-Identifier: GPL-2.0 ++ */ + -+ bp = &pp->blobs[pp->anim_nextframe]; - - if (!bp || bp->blob_header->type != 0) - continue; -@@ -351,3 +356,24 @@ void bootsplash_do_render_flush(struct fb_info *info) - info->fbops->fb_copyarea(info, &area); - } - } ++#define pr_fmt(fmt) "bootsplash: " fmt ++ ++ ++#include <linux/bootsplash.h> ++#include <linux/fb.h> ++#include <linux/kernel.h> ++#include <linux/printk.h> ++#include <linux/types.h> ++ ++#include "bootsplash_internal.h" ++ ++ ++ ++ ++/* ++ * Rendering: Internal drawing routines ++ */ ++ ++ ++/* ++ * Pack pixel into target format and do Big/Little Endian handling. ++ * This would be a good place to handle endianness conversion if necessary. ++ */ ++static inline u32 pack_pixel(const struct fb_var_screeninfo *dst_var, ++ u8 red, u8 green, u8 blue) ++{ ++ u32 dstpix; ++ ++ /* Quantize pixel */ ++ red = red >> (8 - dst_var->red.length); ++ green = green >> (8 - dst_var->green.length); ++ blue = blue >> (8 - dst_var->blue.length); ++ ++ /* Pack pixel */ ++ dstpix = red << (dst_var->red.offset) ++ | green << (dst_var->green.offset) ++ | blue << (dst_var->blue.offset); ++ ++ /* ++ * Move packed pixel to the beginning of the memory cell, ++ * so we can memcpy() it out easily ++ */ ++#ifdef __BIG_ENDIAN ++ switch (dst_var->bits_per_pixel) { ++ case 16: ++ dstpix <<= 16; ++ break; ++ case 24: ++ dstpix <<= 8; ++ break; ++ case 32: ++ break; ++ } ++#else ++ /* This is intrinsically unnecessary on Little Endian */ ++#endif + ++ return dstpix; ++} + -+void bootsplash_do_step_animations(struct splash_file_priv *fp) ++ ++void bootsplash_do_render_background(struct fb_info *info) +{ -+ unsigned int i; ++ unsigned int x, y; ++ u32 dstpix; ++ u32 dst_octpp = info->var.bits_per_pixel / 8; ++ ++ dstpix = pack_pixel(&info->var, ++ 0, ++ 0, ++ 0); + -+ /* Step every animation once */ -+ for (i = 0; i < fp->header->num_pics; i++) { -+ struct splash_pic_priv *pp = &fp->pics[i]; ++ for (y = 0; y < info->var.yres_virtual; y++) { ++ u8 *dstline = info->screen_buffer + (y * info->fix.line_length); + -+ if (pp->blobs_loaded < 2 -+ || pp->pic_header->anim_loop > pp->blobs_loaded) -+ continue; ++ for (x = 0; x < info->var.xres_virtual; x++) { ++ memcpy(dstline, &dstpix, dst_octpp); + -+ if (pp->pic_header->anim_type == SPLASH_ANIM_LOOP_FORWARD) { -+ pp->anim_nextframe++; -+ if (pp->anim_nextframe >= pp->pic_header->num_blobs) -+ pp->anim_nextframe = pp->pic_header->anim_loop; ++ dstline += dst_octpp; + } + } +} -diff --git a/include/uapi/linux/bootsplash_file.h b/include/uapi/linux/bootsplash_file.h -index 71cedcc68933..b3af0a3c6487 100644 ---- a/include/uapi/linux/bootsplash_file.h -+++ b/include/uapi/linux/bootsplash_file.h -@@ -77,7 +77,17 @@ struct splash_file_header { - uint16_t num_blobs; - uint8_t num_pics; - -- uint8_t padding[103]; -+ uint8_t unused_1; +diff --git a/drivers/video/fbdev/core/dummyblit.c b/drivers/video/fbdev/core/dummyblit.c +new file mode 100644 +index 000000000000..8c22ff92ce24 +--- /dev/null ++++ b/drivers/video/fbdev/core/dummyblit.c +@@ -0,0 +1,89 @@ ++/* ++ * linux/drivers/video/fbdev/core/dummyblit.c -- Dummy Blitting Operation ++ * ++ * Authors: ++ * Max Staudt <mstaudt@suse.de> ++ * ++ * These functions are used in place of blitblit/tileblit to suppress ++ * fbcon's text output while a splash is shown. ++ * ++ * Only suppressing actual rendering keeps the text buffer in the VC layer ++ * intact and makes it easy to switch back from the bootsplash to a full ++ * text console with a simple redraw (with the original functions in place). ++ * ++ * Based on linux/drivers/video/fbdev/core/bitblit.c ++ * and linux/drivers/video/fbdev/core/tileblit.c ++ * ++ * SPDX-License-Identifier: GPL-2.0 ++ */ ++ ++#include <linux/module.h> ++#include <linux/fb.h> ++#include <linux/vt_kern.h> ++#include <linux/console.h> ++#include <asm/types.h> ++#include "fbcon.h" ++ ++static void dummy_bmove(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int dy, int dx, int height, int width) ++{ ++ ; ++} ++ ++static void dummy_clear(struct vc_data *vc, struct fb_info *info, int sy, ++ int sx, int height, int width) ++{ ++ ; ++} + ++static void dummy_putcs(struct vc_data *vc, struct fb_info *info, ++ const unsigned short *s, int count, int yy, int xx, ++ int fg, int bg) ++{ ++ ; ++} ++ ++static void dummy_clear_margins(struct vc_data *vc, struct fb_info *info, ++ int color, int bottom_only) ++{ ++ ; ++} ++ ++static void dummy_cursor(struct vc_data *vc, struct fb_info *info, int mode, ++ int softback_lines, int fg, int bg) ++{ ++ ; ++} ++ ++static int dummy_update_start(struct fb_info *info) ++{ + /* -+ * Milliseconds to wait before painting the next frame in -+ * an animation. -+ * This is actually a minimum, as the system is allowed to -+ * stall for longer between frames. ++ * Copied from bitblit.c and tileblit.c ++ * ++ * As of Linux 4.12, nobody seems to care about our return value. + */ -+ uint16_t frame_ms; ++ struct fbcon_ops *ops = info->fbcon_par; ++ int err; + -+ uint8_t padding[100]; - } __attribute__((__packed__)); - ++ err = fb_pan_display(info, &ops->var); ++ ops->var.xoffset = info->var.xoffset; ++ ops->var.yoffset = info->var.yoffset; ++ ops->var.vmode = info->var.vmode; ++ return err; ++} ++ ++void fbcon_set_dummyops(struct fbcon_ops *ops) ++{ ++ ops->bmove = dummy_bmove; ++ ops->clear = dummy_clear; ++ ops->putcs = dummy_putcs; ++ ops->clear_margins = dummy_clear_margins; ++ ops->cursor = dummy_cursor; ++ ops->update_start = dummy_update_start; ++ ops->rotate_font = NULL; ++} ++EXPORT_SYMBOL_GPL(fbcon_set_dummyops); ++ ++MODULE_AUTHOR("Max Staudt <mstaudt@suse.de>"); ++MODULE_DESCRIPTION("Dummy Blitting Operation"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c +index 04612f938bab..9a39a6fcfe98 100644 +--- a/drivers/video/fbdev/core/fbcon.c ++++ b/drivers/video/fbdev/core/fbcon.c +@@ -80,6 +80,7 @@ + #include <asm/irq.h> + + #include "fbcon.h" ++#include <linux/bootsplash.h> + + #ifdef FBCONDEBUG + # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) +@@ -542,6 +543,8 @@ static int do_fbcon_takeover(int show_logo) + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = info_idx; + ++ bootsplash_init(); ++ + err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); -@@ -116,7 +126,23 @@ struct splash_pic_header { - */ - uint16_t position_offset; +@@ -661,6 +664,9 @@ static void set_blitting_type(struct vc_data *vc, struct fb_info *info) + else { + fbcon_set_rotation(info); + fbcon_set_bitops(ops); ++ ++ if (bootsplash_would_render_now()) ++ fbcon_set_dummyops(ops); + } + } -- uint8_t padding[24]; -+ /* -+ * Animation type. -+ * 0 - off -+ * 1 - forward loop -+ */ -+ uint8_t anim_type; +@@ -683,6 +689,19 @@ static void set_blitting_type(struct vc_data *vc, struct fb_info *info) + ops->p = &fb_display[vc->vc_num]; + fbcon_set_rotation(info); + fbcon_set_bitops(ops); + + /* -+ * Animation loop point. -+ * Actual meaning depends on animation type: -+ * Type 0 - Unused -+ * 1 - Frame at which to restart the forward loop -+ * (allowing for "intro" frames) ++ * Note: ++ * This is *eventually correct*. ++ * Setting the fbcon operations and drawing the splash happen at ++ * different points in time. If the splash is enabled/disabled ++ * in between, then bootsplash_{en,dis}able will schedule a ++ * redraw, which will again render the splash (or not) and set ++ * the correct fbcon ops. ++ * The last run will then be the right one. + */ -+ uint8_t anim_loop; -+ -+ uint8_t padding[22]; - } __attribute__((__packed__)); - ++ if (bootsplash_would_render_now()) ++ fbcon_set_dummyops(ops); + } -@@ -158,4 +184,9 @@ enum splash_position { - SPLASH_POS_FLAG_CORNER = 0x10, - }; + static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) +@@ -2184,6 +2203,9 @@ static int fbcon_switch(struct vc_data *vc) + info = registered_fb[con2fb_map[vc->vc_num]]; + ops = info->fbcon_par; -+enum splash_anim_type { -+ SPLASH_ANIM_NONE = 0, -+ SPLASH_ANIM_LOOP_FORWARD = 1, -+}; ++ if (bootsplash_would_render_now()) ++ bootsplash_render_full(info); + + if (softback_top) { + if (softback_lines) + fbcon_set_origin(vc); +diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h +index 18f3ac144237..45f94347fe5e 100644 +--- a/drivers/video/fbdev/core/fbcon.h ++++ b/drivers/video/fbdev/core/fbcon.h +@@ -214,6 +214,11 @@ static inline int attr_col_ec(int shift, struct vc_data *vc, + #define SCROLL_REDRAW 0x004 + #define SCROLL_PAN_REDRAW 0x005 + ++#ifdef CONFIG_BOOTSPLASH ++extern void fbcon_set_dummyops(struct fbcon_ops *ops); ++#else /* CONFIG_BOOTSPLASH */ ++#define fbcon_set_dummyops(x) ++#endif /* CONFIG_BOOTSPLASH */ + #ifdef CONFIG_FB_TILEBLITTING + extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info); #endif +diff --git a/include/linux/bootsplash.h b/include/linux/bootsplash.h +new file mode 100644 +index 000000000000..c6dd0b43180d +--- /dev/null ++++ b/include/linux/bootsplash.h +@@ -0,0 +1,43 @@ ++/* ++ * Kernel based bootsplash. ++ * ++ * Authors: ++ * Max Staudt <mstaudt@suse.de> ++ * ++ * SPDX-License-Identifier: GPL-2.0 ++ */ ++ ++#ifndef __LINUX_BOOTSPLASH_H ++#define __LINUX_BOOTSPLASH_H ++ ++#include <linux/fb.h> ++ ++ ++#ifdef CONFIG_BOOTSPLASH ++ ++extern void bootsplash_render_full(struct fb_info *info); ++ ++extern bool bootsplash_would_render_now(void); ++ ++extern bool bootsplash_is_enabled(void); ++extern void bootsplash_disable(void); ++extern void bootsplash_enable(void); ++ ++extern void bootsplash_init(void); ++ ++#else /* CONFIG_BOOTSPLASH */ ++ ++#define bootsplash_render_full(x) ++ ++#define bootsplash_would_render_now() (false) ++ ++#define bootsplash_is_enabled() (false) ++#define bootsplash_disable() ++#define bootsplash_enable() ++ ++#define bootsplash_init() ++ ++#endif /* CONFIG_BOOTSPLASH */ ++ ++ ++#endif |