2022-08-11 17:27:44 +02:00
|
|
|
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
|
|
index a74227ad082e..b5633b56391e 100644
|
|
|
|
--- a/MAINTAINERS
|
|
|
|
+++ b/MAINTAINERS
|
2022-08-19 02:15:30 +02:00
|
|
|
@@ -3615,6 +3615,14 @@
|
|
|
|
F: Documentation/devicetree/bindings/iio/accel/bosch,bma400.yaml
|
|
|
|
F: drivers/iio/accel/bma400*
|
2022-08-11 17:27:44 +02:00
|
|
|
|
|
|
|
+BOOTSPLASH
|
2022-08-19 02:15:30 +02:00
|
|
|
+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
|
2022-08-11 17:27:44 +02:00
|
|
|
+
|
2022-08-19 02:15:30 +02:00
|
|
|
BPF [GENERAL] (Safe Dynamic Programs and Tools)
|
|
|
|
M: Alexei Starovoitov <ast@kernel.org>
|
|
|
|
M: Daniel Borkmann <daniel@iogearbox.net>
|
2022-08-11 17:27:44 +02:00
|
|
|
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
|
|
|
|
new file mode 100644
|
|
|
|
index 000000000000..e449755af268
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/drivers/video/fbdev/core/bootsplash.c
|
|
|
|
@@ -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 (!(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;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 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;
|
|
|
|
+
|
|
|
|
+ bootsplash_do_render_background(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);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void bootsplash_enable(void)
|
|
|
|
+{
|
|
|
|
+ bool was_enabled;
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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
|
|
|
|
new file mode 100644
|
|
|
|
index 000000000000..b11da5cb90bf
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/drivers/video/fbdev/core/bootsplash_internal.h
|
|
|
|
@@ -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
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#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 {
|
|
|
|
+ /*
|
|
|
|
+ * 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.
|
|
|
|
+ */
|
|
|
|
+ unsigned long enabled;
|
|
|
|
+
|
|
|
|
+ /* Our gateway to userland via sysfs */
|
|
|
|
+ struct platform_device *splash_device;
|
|
|
|
+
|
|
|
|
+ struct work_struct work_redraw_vc;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 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
|
|
|
|
new file mode 100644
|
|
|
|
index 000000000000..4d7e0117f653
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/drivers/video/fbdev/core/bootsplash_render.c
|
|
|
|
@@ -0,0 +1,93 @@
|
|
|
|
+/*
|
|
|
|
+ * Kernel based bootsplash.
|
|
|
|
+ *
|
|
|
|
+ * (Rendering functions)
|
|
|
|
+ *
|
|
|
|
+ * Authors:
|
|
|
|
+ * Max Staudt <mstaudt@suse.de>
|
|
|
|
+ *
|
|
|
|
+ * SPDX-License-Identifier: GPL-2.0
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#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_render_background(struct fb_info *info)
|
|
|
|
+{
|
|
|
|
+ unsigned int x, y;
|
|
|
|
+ u32 dstpix;
|
|
|
|
+ u32 dst_octpp = info->var.bits_per_pixel / 8;
|
|
|
|
+
|
|
|
|
+ dstpix = pack_pixel(&info->var,
|
|
|
|
+ 0,
|
|
|
|
+ 0,
|
|
|
|
+ 0);
|
|
|
|
+
|
|
|
|
+ for (y = 0; y < info->var.yres_virtual; y++) {
|
|
|
|
+ u8 *dstline = info->screen_buffer + (y * info->fix.line_length);
|
|
|
|
+
|
|
|
|
+ for (x = 0; x < info->var.xres_virtual; x++) {
|
|
|
|
+ memcpy(dstline, &dstpix, dst_octpp);
|
|
|
|
+
|
|
|
|
+ dstline += dst_octpp;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
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)
|
|
|
|
+{
|
|
|
|
+ /*
|
|
|
|
+ * Copied from bitblit.c and tileblit.c
|
|
|
|
+ *
|
|
|
|
+ * As of Linux 4.12, nobody seems to care about our return value.
|
|
|
|
+ */
|
|
|
|
+ struct fbcon_ops *ops = info->fbcon_par;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
|
|
|
|
@@ -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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@@ -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);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 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.
|
|
|
|
+ */
|
|
|
|
+ if (bootsplash_would_render_now())
|
|
|
|
+ fbcon_set_dummyops(ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
+ 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
|