Should be fixed now
This commit is contained in:
parent
50b656c9c7
commit
da65d020a7
@ -1,746 +0,0 @@
|
|||||||
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
|
|
||||||
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
|
|
@ -1,669 +0,0 @@
|
|||||||
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
||||||
index b5633b56391e..5c237445761e 100644
|
|
||||||
--- a/MAINTAINERS
|
|
||||||
+++ b/MAINTAINERS
|
|
||||||
@@ -2712,6 +2712,7 @@ S: Maintained
|
|
||||||
F: drivers/video/fbdev/core/bootsplash*.*
|
|
||||||
F: drivers/video/fbdev/core/dummycon.c
|
|
||||||
F: include/linux/bootsplash.h
|
|
||||||
+F: include/uapi/linux/bootsplash_file.h
|
|
||||||
|
|
||||||
BPF (Safe dynamic programs and tools)
|
|
||||||
M: Alexei Starovoitov <ast@kernel.org>
|
|
||||||
diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile
|
|
||||||
index 66895321928e..6a8d1bab8a01 100644
|
|
||||||
--- a/drivers/video/fbdev/core/Makefile
|
|
||||||
+++ b/drivers/video/fbdev/core/Makefile
|
|
||||||
@@ -31,4 +31,4 @@ obj-$(CONFIG_FB_SVGALIB) += svgalib.o
|
|
||||||
obj-$(CONFIG_FB_DDC) += fb_ddc.o
|
|
||||||
|
|
||||||
obj-$(CONFIG_BOOTSPLASH) += bootsplash.o bootsplash_render.o \
|
|
||||||
- dummyblit.o
|
|
||||||
+ bootsplash_load.o dummyblit.o
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
index e449755af268..843c5400fefc 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
@@ -32,6 +32,7 @@
|
|
||||||
#include <linux/workqueue.h>
|
|
||||||
|
|
||||||
#include "bootsplash_internal.h"
|
|
||||||
+#include "uapi/linux/bootsplash_file.h"
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ -102,10 +103,17 @@ static bool is_fb_compatible(const struct fb_info *info)
|
|
||||||
*/
|
|
||||||
void bootsplash_render_full(struct fb_info *info)
|
|
||||||
{
|
|
||||||
+ mutex_lock(&splash_state.data_lock);
|
|
||||||
+
|
|
||||||
if (!is_fb_compatible(info))
|
|
||||||
- return;
|
|
||||||
+ goto out;
|
|
||||||
+
|
|
||||||
+ bootsplash_do_render_background(info, splash_state.file);
|
|
||||||
+
|
|
||||||
+ bootsplash_do_render_pictures(info, splash_state.file);
|
|
||||||
|
|
||||||
- bootsplash_do_render_background(info);
|
|
||||||
+out:
|
|
||||||
+ mutex_unlock(&splash_state.data_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -116,6 +124,7 @@ bool bootsplash_would_render_now(void)
|
|
||||||
{
|
|
||||||
return !oops_in_progress
|
|
||||||
&& !console_blanked
|
|
||||||
+ && splash_state.file
|
|
||||||
&& bootsplash_is_enabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -252,6 +261,7 @@ static struct platform_driver splash_driver = {
|
|
||||||
void bootsplash_init(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
+ struct splash_file_priv *fp;
|
|
||||||
|
|
||||||
/* Initialized already? */
|
|
||||||
if (splash_state.splash_device)
|
|
||||||
@@ -280,8 +290,26 @@ void bootsplash_init(void)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
+ mutex_init(&splash_state.data_lock);
|
|
||||||
+ set_bit(0, &splash_state.enabled);
|
|
||||||
+
|
|
||||||
INIT_WORK(&splash_state.work_redraw_vc, splash_callback_redraw_vc);
|
|
||||||
|
|
||||||
+
|
|
||||||
+ if (!splash_state.bootfile || !strlen(splash_state.bootfile))
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ fp = bootsplash_load_firmware(&splash_state.splash_device->dev,
|
|
||||||
+ splash_state.bootfile);
|
|
||||||
+
|
|
||||||
+ if (!fp)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&splash_state.data_lock);
|
|
||||||
+ splash_state.splash_fb = NULL;
|
|
||||||
+ splash_state.file = fp;
|
|
||||||
+ mutex_unlock(&splash_state.data_lock);
|
|
||||||
+
|
|
||||||
return;
|
|
||||||
|
|
||||||
err_device:
|
|
||||||
@@ -292,3 +320,7 @@ void bootsplash_init(void)
|
|
||||||
err:
|
|
||||||
pr_err("Failed to initialize.\n");
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+module_param_named(bootfile, splash_state.bootfile, charp, 0444);
|
|
||||||
+MODULE_PARM_DESC(bootfile, "Bootsplash file to load on boot");
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash_internal.h b/drivers/video/fbdev/core/bootsplash_internal.h
|
|
||||||
index b11da5cb90bf..71e2a27ac0b8 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash_internal.h
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash_internal.h
|
|
||||||
@@ -15,15 +15,43 @@
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/fb.h>
|
|
||||||
+#include <linux/firmware.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
|
|
||||||
+#include "uapi/linux/bootsplash_file.h"
|
|
||||||
+
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Runtime types
|
|
||||||
*/
|
|
||||||
+struct splash_blob_priv {
|
|
||||||
+ struct splash_blob_header *blob_header;
|
|
||||||
+ const void *data;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct splash_pic_priv {
|
|
||||||
+ const struct splash_pic_header *pic_header;
|
|
||||||
+
|
|
||||||
+ struct splash_blob_priv *blobs;
|
|
||||||
+ u16 blobs_loaded;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct splash_file_priv {
|
|
||||||
+ const struct firmware *fw;
|
|
||||||
+ const struct splash_file_header *header;
|
|
||||||
+
|
|
||||||
+ struct splash_pic_priv *pics;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
struct splash_priv {
|
|
||||||
+ /* Bootup and runtime state */
|
|
||||||
+ char *bootfile;
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Enabled/disabled state, to be used with atomic bit operations.
|
|
||||||
* Bit 0: 0 = Splash hidden
|
|
||||||
@@ -43,6 +71,13 @@ struct splash_priv {
|
|
||||||
struct platform_device *splash_device;
|
|
||||||
|
|
||||||
struct work_struct work_redraw_vc;
|
|
||||||
+
|
|
||||||
+ /* Splash data structures including lock for everything below */
|
|
||||||
+ struct mutex data_lock;
|
|
||||||
+
|
|
||||||
+ struct fb_info *splash_fb;
|
|
||||||
+
|
|
||||||
+ struct splash_file_priv *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@@ -50,6 +85,14 @@ struct splash_priv {
|
|
||||||
/*
|
|
||||||
* Rendering functions
|
|
||||||
*/
|
|
||||||
-void bootsplash_do_render_background(struct fb_info *info);
|
|
||||||
+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);
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+void bootsplash_free_file(struct splash_file_priv *fp);
|
|
||||||
+struct splash_file_priv *bootsplash_load_firmware(struct device *device,
|
|
||||||
+ const char *path);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash_load.c b/drivers/video/fbdev/core/bootsplash_load.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..fd807571ab7d
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash_load.c
|
|
||||||
@@ -0,0 +1,225 @@
|
|
||||||
+/*
|
|
||||||
+ * Kernel based bootsplash.
|
|
||||||
+ *
|
|
||||||
+ * (Loading and freeing 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/firmware.h>
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/mutex.h>
|
|
||||||
+#include <linux/printk.h>
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+#include <linux/vmalloc.h>
|
|
||||||
+
|
|
||||||
+#include "bootsplash_internal.h"
|
|
||||||
+#include "uapi/linux/bootsplash_file.h"
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Free all vmalloc()'d resources describing a splash file.
|
|
||||||
+ */
|
|
||||||
+void bootsplash_free_file(struct splash_file_priv *fp)
|
|
||||||
+{
|
|
||||||
+ if (!fp)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ if (fp->pics) {
|
|
||||||
+ unsigned int i;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < fp->header->num_pics; i++) {
|
|
||||||
+ struct splash_pic_priv *pp = &fp->pics[i];
|
|
||||||
+
|
|
||||||
+ if (pp->blobs)
|
|
||||||
+ vfree(pp->blobs);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ vfree(fp->pics);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ release_firmware(fp->fw);
|
|
||||||
+ vfree(fp);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Load a splash screen from a "firmware" file.
|
|
||||||
+ *
|
|
||||||
+ * Parsing, and sanity checks.
|
|
||||||
+ */
|
|
||||||
+#ifdef __BIG_ENDIAN
|
|
||||||
+ #define BOOTSPLASH_MAGIC BOOTSPLASH_MAGIC_BE
|
|
||||||
+#else
|
|
||||||
+ #define BOOTSPLASH_MAGIC BOOTSPLASH_MAGIC_LE
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+struct splash_file_priv *bootsplash_load_firmware(struct device *device,
|
|
||||||
+ const char *path)
|
|
||||||
+{
|
|
||||||
+ const struct firmware *fw;
|
|
||||||
+ struct splash_file_priv *fp;
|
|
||||||
+ unsigned int i;
|
|
||||||
+ const u8 *walker;
|
|
||||||
+
|
|
||||||
+ if (request_firmware(&fw, path, device))
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ if (fw->size < sizeof(struct splash_file_header)
|
|
||||||
+ || memcmp(fw->data, BOOTSPLASH_MAGIC, sizeof(fp->header->id))) {
|
|
||||||
+ pr_err("Not a bootsplash file.\n");
|
|
||||||
+
|
|
||||||
+ release_firmware(fw);
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ fp = vzalloc(sizeof(struct splash_file_priv));
|
|
||||||
+ if (!fp) {
|
|
||||||
+ release_firmware(fw);
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pr_info("Loading splash file (%li bytes)\n", fw->size);
|
|
||||||
+
|
|
||||||
+ fp->fw = fw;
|
|
||||||
+ fp->header = (struct splash_file_header *)fw->data;
|
|
||||||
+
|
|
||||||
+ /* Sanity checks */
|
|
||||||
+ if (fp->header->version != BOOTSPLASH_VERSION) {
|
|
||||||
+ pr_err("Loaded v%d file, but we only support version %d\n",
|
|
||||||
+ fp->header->version,
|
|
||||||
+ BOOTSPLASH_VERSION);
|
|
||||||
+
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (fw->size < sizeof(struct splash_file_header)
|
|
||||||
+ + fp->header->num_pics
|
|
||||||
+ * sizeof(struct splash_pic_header)
|
|
||||||
+ + fp->header->num_blobs
|
|
||||||
+ * sizeof(struct splash_blob_header)) {
|
|
||||||
+ pr_err("File incomplete.\n");
|
|
||||||
+
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Read picture headers */
|
|
||||||
+ if (fp->header->num_pics) {
|
|
||||||
+ fp->pics = vzalloc(fp->header->num_pics
|
|
||||||
+ * sizeof(struct splash_pic_priv));
|
|
||||||
+ if (!fp->pics)
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ walker = fw->data + sizeof(struct splash_file_header);
|
|
||||||
+ for (i = 0; i < fp->header->num_pics; i++) {
|
|
||||||
+ struct splash_pic_priv *pp = &fp->pics[i];
|
|
||||||
+ struct splash_pic_header *ph = (void *)walker;
|
|
||||||
+
|
|
||||||
+ pr_debug("Picture %u: Size %ux%u\n", i, ph->width, ph->height);
|
|
||||||
+
|
|
||||||
+ if (ph->num_blobs < 1) {
|
|
||||||
+ pr_err("Picture %u: Zero blobs? Aborting load.\n", i);
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pp->pic_header = ph;
|
|
||||||
+ pp->blobs = vzalloc(ph->num_blobs
|
|
||||||
+ * sizeof(struct splash_blob_priv));
|
|
||||||
+ if (!pp->blobs)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ walker += sizeof(struct splash_pic_header);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Read blob headers */
|
|
||||||
+ for (i = 0; i < fp->header->num_blobs; i++) {
|
|
||||||
+ struct splash_blob_header *bh = (void *)walker;
|
|
||||||
+ struct splash_pic_priv *pp;
|
|
||||||
+
|
|
||||||
+ if (walker + sizeof(struct splash_blob_header)
|
|
||||||
+ > fw->data + fw->size)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ walker += sizeof(struct splash_blob_header);
|
|
||||||
+
|
|
||||||
+ if (walker + bh->length > fw->data + fw->size)
|
|
||||||
+ goto err;
|
|
||||||
+
|
|
||||||
+ if (bh->picture_id >= fp->header->num_pics)
|
|
||||||
+ goto nextblob;
|
|
||||||
+
|
|
||||||
+ pp = &fp->pics[bh->picture_id];
|
|
||||||
+
|
|
||||||
+ pr_debug("Blob %u, pic %u, blobs_loaded %u, num_blobs %u.\n",
|
|
||||||
+ i, bh->picture_id,
|
|
||||||
+ pp->blobs_loaded, pp->pic_header->num_blobs);
|
|
||||||
+
|
|
||||||
+ if (pp->blobs_loaded >= pp->pic_header->num_blobs)
|
|
||||||
+ goto nextblob;
|
|
||||||
+
|
|
||||||
+ switch (bh->type) {
|
|
||||||
+ case 0:
|
|
||||||
+ /* Raw 24-bit packed pixels */
|
|
||||||
+ if (bh->length != pp->pic_header->width
|
|
||||||
+ * pp->pic_header->height * 3) {
|
|
||||||
+ pr_err("Blob %u, type 1: Length doesn't match picture.\n",
|
|
||||||
+ i);
|
|
||||||
+
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
+ default:
|
|
||||||
+ pr_warn("Blob %u, unknown type %u.\n", i, bh->type);
|
|
||||||
+ goto nextblob;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pp->blobs[pp->blobs_loaded].blob_header = bh;
|
|
||||||
+ pp->blobs[pp->blobs_loaded].data = walker;
|
|
||||||
+ pp->blobs_loaded++;
|
|
||||||
+
|
|
||||||
+nextblob:
|
|
||||||
+ walker += bh->length;
|
|
||||||
+ if (bh->length % 16)
|
|
||||||
+ walker += 16 - (bh->length % 16);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (walker != fw->data + fw->size)
|
|
||||||
+ pr_warn("Trailing data in splash file.\n");
|
|
||||||
+
|
|
||||||
+ /* 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];
|
|
||||||
+
|
|
||||||
+ if (pp->blobs_loaded != pp->pic_header->num_blobs) {
|
|
||||||
+ pr_err("Picture %u doesn't have all blob slots filled.\n",
|
|
||||||
+ i);
|
|
||||||
+
|
|
||||||
+ goto err;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pr_info("Loaded (%ld bytes, %u pics, %u blobs).\n",
|
|
||||||
+ fw->size,
|
|
||||||
+ fp->header->num_pics,
|
|
||||||
+ fp->header->num_blobs);
|
|
||||||
+
|
|
||||||
+ return fp;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+err:
|
|
||||||
+ bootsplash_free_file(fp);
|
|
||||||
+ return NULL;
|
|
||||||
+}
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash_render.c b/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
index 4d7e0117f653..2ae36949d0e3 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
@@ -19,6 +19,7 @@
|
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
#include "bootsplash_internal.h"
|
|
||||||
+#include "uapi/linux/bootsplash_file.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -70,16 +71,69 @@ static inline u32 pack_pixel(const struct fb_var_screeninfo *dst_var,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-void bootsplash_do_render_background(struct fb_info *info)
|
|
||||||
+/*
|
|
||||||
+ * Copy from source and blend into the destination picture.
|
|
||||||
+ * Currently assumes that the source picture is 24bpp.
|
|
||||||
+ * Currently assumes that the destination is <= 32bpp.
|
|
||||||
+ */
|
|
||||||
+static int splash_convert_to_fb(u8 *dst,
|
|
||||||
+ const struct fb_var_screeninfo *dst_var,
|
|
||||||
+ unsigned int dst_stride,
|
|
||||||
+ unsigned int dst_xoff,
|
|
||||||
+ unsigned int dst_yoff,
|
|
||||||
+ const u8 *src,
|
|
||||||
+ unsigned int src_width,
|
|
||||||
+ unsigned int src_height)
|
|
||||||
+{
|
|
||||||
+ unsigned int x, y;
|
|
||||||
+ unsigned int src_stride = 3 * src_width; /* Assume 24bpp packed */
|
|
||||||
+ u32 dst_octpp = dst_var->bits_per_pixel / 8;
|
|
||||||
+
|
|
||||||
+ dst_xoff += dst_var->xoffset;
|
|
||||||
+ dst_yoff += dst_var->yoffset;
|
|
||||||
+
|
|
||||||
+ /* Copy with stride and pixel size adjustment */
|
|
||||||
+ for (y = 0;
|
|
||||||
+ y < src_height && y + dst_yoff < dst_var->yres_virtual;
|
|
||||||
+ y++) {
|
|
||||||
+ const u8 *srcline = src + (y * src_stride);
|
|
||||||
+ u8 *dstline = dst + ((y + dst_yoff) * dst_stride)
|
|
||||||
+ + (dst_xoff * dst_octpp);
|
|
||||||
+
|
|
||||||
+ for (x = 0;
|
|
||||||
+ x < src_width && x + dst_xoff < dst_var->xres_virtual;
|
|
||||||
+ x++) {
|
|
||||||
+ u8 red, green, blue;
|
|
||||||
+ u32 dstpix;
|
|
||||||
+
|
|
||||||
+ /* Read pixel */
|
|
||||||
+ red = *srcline++;
|
|
||||||
+ green = *srcline++;
|
|
||||||
+ blue = *srcline++;
|
|
||||||
+
|
|
||||||
+ /* Write pixel */
|
|
||||||
+ dstpix = pack_pixel(dst_var, red, green, blue);
|
|
||||||
+ memcpy(dstline, &dstpix, dst_octpp);
|
|
||||||
+
|
|
||||||
+ dstline += dst_octpp;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+void bootsplash_do_render_background(struct fb_info *info,
|
|
||||||
+ const struct splash_file_priv *fp)
|
|
||||||
{
|
|
||||||
unsigned int x, y;
|
|
||||||
u32 dstpix;
|
|
||||||
u32 dst_octpp = info->var.bits_per_pixel / 8;
|
|
||||||
|
|
||||||
dstpix = pack_pixel(&info->var,
|
|
||||||
- 0,
|
|
||||||
- 0,
|
|
||||||
- 0);
|
|
||||||
+ fp->header->bg_red,
|
|
||||||
+ fp->header->bg_green,
|
|
||||||
+ fp->header->bg_blue);
|
|
||||||
|
|
||||||
for (y = 0; y < info->var.yres_virtual; y++) {
|
|
||||||
u8 *dstline = info->screen_buffer + (y * info->fix.line_length);
|
|
||||||
@@ -91,3 +145,44 @@ void bootsplash_do_render_background(struct fb_info *info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+void bootsplash_do_render_pictures(struct fb_info *info,
|
|
||||||
+ const struct splash_file_priv *fp)
|
|
||||||
+{
|
|
||||||
+ unsigned int i;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < fp->header->num_pics; i++) {
|
|
||||||
+ struct splash_blob_priv *bp;
|
|
||||||
+ struct splash_pic_priv *pp = &fp->pics[i];
|
|
||||||
+ long dst_xoff, dst_yoff;
|
|
||||||
+
|
|
||||||
+ if (pp->blobs_loaded < 1)
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ bp = &pp->blobs[0];
|
|
||||||
+
|
|
||||||
+ if (!bp || bp->blob_header->type != 0)
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ dst_xoff = (info->var.xres - pp->pic_header->width) / 2;
|
|
||||||
+ dst_yoff = (info->var.yres - pp->pic_header->height) / 2;
|
|
||||||
+
|
|
||||||
+ if (dst_xoff < 0
|
|
||||||
+ || dst_yoff < 0
|
|
||||||
+ || dst_xoff + pp->pic_header->width > info->var.xres
|
|
||||||
+ || dst_yoff + pp->pic_header->height > info->var.yres) {
|
|
||||||
+ pr_info_once("Picture %u is out of bounds at current resolution: %dx%d\n"
|
|
||||||
+ "(this will only be printed once every reboot)\n",
|
|
||||||
+ i, info->var.xres, info->var.yres);
|
|
||||||
+
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Draw next splash frame */
|
|
||||||
+ splash_convert_to_fb(info->screen_buffer, &info->var,
|
|
||||||
+ info->fix.line_length, dst_xoff, dst_yoff,
|
|
||||||
+ bp->data,
|
|
||||||
+ pp->pic_header->width, pp->pic_header->height);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
diff --git a/include/uapi/linux/bootsplash_file.h b/include/uapi/linux/bootsplash_file.h
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..89dc9cca8f0c
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/include/uapi/linux/bootsplash_file.h
|
|
||||||
@@ -0,0 +1,118 @@
|
|
||||||
+/*
|
|
||||||
+ * Kernel based bootsplash.
|
|
||||||
+ *
|
|
||||||
+ * (File format)
|
|
||||||
+ *
|
|
||||||
+ * Authors:
|
|
||||||
+ * Max Staudt <mstaudt@suse.de>
|
|
||||||
+ *
|
|
||||||
+ * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#ifndef __BOOTSPLASH_FILE_H
|
|
||||||
+#define __BOOTSPLASH_FILE_H
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+#define BOOTSPLASH_VERSION 55561
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+#include <linux/kernel.h>
|
|
||||||
+#include <linux/types.h>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * On-disk types
|
|
||||||
+ *
|
|
||||||
+ * A splash file consists of:
|
|
||||||
+ * - One single 'struct splash_file_header'
|
|
||||||
+ * - An array of 'struct splash_pic_header'
|
|
||||||
+ * - An array of raw data blocks, each padded to 16 bytes and
|
|
||||||
+ * preceded by a 'struct splash_blob_header'
|
|
||||||
+ *
|
|
||||||
+ * A single-frame splash may look like this:
|
|
||||||
+ *
|
|
||||||
+ * +--------------------+
|
|
||||||
+ * | |
|
|
||||||
+ * | splash_file_header |
|
|
||||||
+ * | -> num_blobs = 1 |
|
|
||||||
+ * | -> num_pics = 1 |
|
|
||||||
+ * | |
|
|
||||||
+ * +--------------------+
|
|
||||||
+ * | |
|
|
||||||
+ * | splash_pic_header |
|
|
||||||
+ * | |
|
|
||||||
+ * +--------------------+
|
|
||||||
+ * | |
|
|
||||||
+ * | splash_blob_header |
|
|
||||||
+ * | -> type = 0 |
|
|
||||||
+ * | -> picture_id = 0 |
|
|
||||||
+ * | |
|
|
||||||
+ * | (raw RGB data) |
|
|
||||||
+ * | (pad to 16 bytes) |
|
|
||||||
+ * | |
|
|
||||||
+ * +--------------------+
|
|
||||||
+ *
|
|
||||||
+ * All multi-byte values are stored on disk in the native format
|
|
||||||
+ * expected by the system the file will be used on.
|
|
||||||
+ */
|
|
||||||
+#define BOOTSPLASH_MAGIC_BE "Linux bootsplash"
|
|
||||||
+#define BOOTSPLASH_MAGIC_LE "hsalpstoob xuniL"
|
|
||||||
+
|
|
||||||
+struct splash_file_header {
|
|
||||||
+ uint8_t id[16]; /* "Linux bootsplash" (no trailing NUL) */
|
|
||||||
+
|
|
||||||
+ /* Splash file format version to avoid clashes */
|
|
||||||
+ uint16_t version;
|
|
||||||
+
|
|
||||||
+ /* The background color */
|
|
||||||
+ uint8_t bg_red;
|
|
||||||
+ uint8_t bg_green;
|
|
||||||
+ uint8_t bg_blue;
|
|
||||||
+ uint8_t bg_reserved;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Number of pic/blobs so we can allocate memory for internal
|
|
||||||
+ * structures ahead of time when reading the file
|
|
||||||
+ */
|
|
||||||
+ uint16_t num_blobs;
|
|
||||||
+ uint8_t num_pics;
|
|
||||||
+
|
|
||||||
+ uint8_t padding[103];
|
|
||||||
+} __attribute__((__packed__));
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct splash_pic_header {
|
|
||||||
+ uint16_t width;
|
|
||||||
+ uint16_t height;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Number of data packages associated with this picture.
|
|
||||||
+ * Currently, the only use for more than 1 is for animations.
|
|
||||||
+ */
|
|
||||||
+ uint8_t num_blobs;
|
|
||||||
+
|
|
||||||
+ uint8_t padding[27];
|
|
||||||
+} __attribute__((__packed__));
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct splash_blob_header {
|
|
||||||
+ /* Length of the data block in bytes. */
|
|
||||||
+ uint32_t length;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Type of the contents.
|
|
||||||
+ * 0 - Raw RGB data.
|
|
||||||
+ */
|
|
||||||
+ uint16_t type;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Picture this blob is associated with.
|
|
||||||
+ * Blobs will be added to a picture in the order they are
|
|
||||||
+ * found in the file.
|
|
||||||
+ */
|
|
||||||
+ uint8_t picture_id;
|
|
||||||
+
|
|
||||||
+ uint8_t padding[9];
|
|
||||||
+} __attribute__((__packed__));
|
|
||||||
+
|
|
||||||
+#endif
|
|
@ -1,66 +0,0 @@
|
|||||||
diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
index 843c5400fefc..815b007f81ca 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
@@ -112,6 +112,8 @@ void bootsplash_render_full(struct fb_info *info)
|
|
||||||
|
|
||||||
bootsplash_do_render_pictures(info, splash_state.file);
|
|
||||||
|
|
||||||
+ bootsplash_do_render_flush(info);
|
|
||||||
+
|
|
||||||
out:
|
|
||||||
mutex_unlock(&splash_state.data_lock);
|
|
||||||
}
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash_internal.h b/drivers/video/fbdev/core/bootsplash_internal.h
|
|
||||||
index 71e2a27ac0b8..0acb383aa4e3 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash_internal.h
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash_internal.h
|
|
||||||
@@ -89,6 +89,7 @@ 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);
|
|
||||||
+void bootsplash_do_render_flush(struct fb_info *info);
|
|
||||||
|
|
||||||
|
|
||||||
void bootsplash_free_file(struct splash_file_priv *fp);
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash_render.c b/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
index 2ae36949d0e3..8c09c306ff67 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
@@ -186,3 +186,36 @@ void bootsplash_do_render_pictures(struct fb_info *info,
|
|
||||||
pp->pic_header->width, pp->pic_header->height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+void bootsplash_do_render_flush(struct fb_info *info)
|
|
||||||
+{
|
|
||||||
+ /*
|
|
||||||
+ * FB drivers using deferred_io (such as Xen) need to sync the
|
|
||||||
+ * screen after modifying its contents. When the FB is mmap()ed
|
|
||||||
+ * from userspace, this happens via a dirty pages callback, but
|
|
||||||
+ * when modifying the FB from the kernel, there is no such thing.
|
|
||||||
+ *
|
|
||||||
+ * So let's issue a fake fb_copyarea (copying the FB onto itself)
|
|
||||||
+ * to trick the FB driver into syncing the screen.
|
|
||||||
+ *
|
|
||||||
+ * A few DRM drivers' FB implementations are broken by not using
|
|
||||||
+ * deferred_io when they really should - we match on the known
|
|
||||||
+ * bad ones manually for now.
|
|
||||||
+ */
|
|
||||||
+ if (info->fbdefio
|
|
||||||
+ || !strcmp(info->fix.id, "astdrmfb")
|
|
||||||
+ || !strcmp(info->fix.id, "cirrusdrmfb")
|
|
||||||
+ || !strcmp(info->fix.id, "mgadrmfb")) {
|
|
||||||
+ struct fb_copyarea area;
|
|
||||||
+
|
|
||||||
+ area.dx = 0;
|
|
||||||
+ area.dy = 0;
|
|
||||||
+ area.width = info->var.xres;
|
|
||||||
+ area.height = info->var.yres;
|
|
||||||
+ area.sx = 0;
|
|
||||||
+ area.sy = 0;
|
|
||||||
+
|
|
||||||
+ info->fbops->fb_copyarea(info, &area);
|
|
||||||
+ }
|
|
||||||
+}
|
|
@ -1,215 +0,0 @@
|
|||||||
diff --git a/drivers/video/fbdev/core/bootsplash_render.c b/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
index 8c09c306ff67..07e3a4eab811 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash_render.c
|
|
||||||
@@ -155,6 +155,7 @@ void bootsplash_do_render_pictures(struct fb_info *info,
|
|
||||||
for (i = 0; i < fp->header->num_pics; i++) {
|
|
||||||
struct splash_blob_priv *bp;
|
|
||||||
struct splash_pic_priv *pp = &fp->pics[i];
|
|
||||||
+ const struct splash_pic_header *ph = pp->pic_header;
|
|
||||||
long dst_xoff, dst_yoff;
|
|
||||||
|
|
||||||
if (pp->blobs_loaded < 1)
|
|
||||||
@@ -165,8 +166,139 @@ void bootsplash_do_render_pictures(struct fb_info *info,
|
|
||||||
if (!bp || bp->blob_header->type != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
- dst_xoff = (info->var.xres - pp->pic_header->width) / 2;
|
|
||||||
- dst_yoff = (info->var.yres - pp->pic_header->height) / 2;
|
|
||||||
+ switch (ph->position) {
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_TOP_LEFT:
|
|
||||||
+ dst_xoff = 0;
|
|
||||||
+ dst_yoff = 0;
|
|
||||||
+
|
|
||||||
+ dst_xoff += ph->position_offset;
|
|
||||||
+ dst_yoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_TOP:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = 0;
|
|
||||||
+
|
|
||||||
+ dst_yoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_TOP_RIGHT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_yoff = 0;
|
|
||||||
+
|
|
||||||
+ dst_xoff -= ph->position_offset;
|
|
||||||
+ dst_yoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_RIGHT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff -= ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_BOTTOM_RIGHT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+
|
|
||||||
+ dst_xoff -= ph->position_offset;
|
|
||||||
+ dst_yoff -= ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_BOTTOM:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+
|
|
||||||
+ dst_yoff -= ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_BOTTOM_LEFT:
|
|
||||||
+ dst_xoff = 0 + ph->position_offset;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height
|
|
||||||
+ - ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_POS_FLAG_CORNER | SPLASH_CORNER_LEFT:
|
|
||||||
+ dst_xoff = 0;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case SPLASH_CORNER_TOP_LEFT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff -= ph->position_offset;
|
|
||||||
+ dst_yoff -= ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_CORNER_TOP:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_yoff -= ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_CORNER_TOP_RIGHT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff += ph->position_offset;
|
|
||||||
+ dst_yoff -= ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_CORNER_RIGHT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_CORNER_BOTTOM_RIGHT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff += ph->position_offset;
|
|
||||||
+ dst_yoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_CORNER_BOTTOM:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_yoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_CORNER_BOTTOM_LEFT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff -= ph->position_offset;
|
|
||||||
+ dst_yoff += ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+ case SPLASH_CORNER_LEFT:
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+
|
|
||||||
+ dst_xoff -= ph->position_offset;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ default:
|
|
||||||
+ /* As a fallback, center the picture. */
|
|
||||||
+ dst_xoff = info->var.xres - pp->pic_header->width;
|
|
||||||
+ dst_xoff /= 2;
|
|
||||||
+ dst_yoff = info->var.yres - pp->pic_header->height;
|
|
||||||
+ dst_yoff /= 2;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
if (dst_xoff < 0
|
|
||||||
|| dst_yoff < 0
|
|
||||||
diff --git a/include/uapi/linux/bootsplash_file.h b/include/uapi/linux/bootsplash_file.h
|
|
||||||
index 89dc9cca8f0c..71cedcc68933 100644
|
|
||||||
--- a/include/uapi/linux/bootsplash_file.h
|
|
||||||
+++ b/include/uapi/linux/bootsplash_file.h
|
|
||||||
@@ -91,7 +91,32 @@ struct splash_pic_header {
|
|
||||||
*/
|
|
||||||
uint8_t num_blobs;
|
|
||||||
|
|
||||||
- uint8_t padding[27];
|
|
||||||
+ /*
|
|
||||||
+ * Corner to move the picture to / from.
|
|
||||||
+ * 0x00 - Top left
|
|
||||||
+ * 0x01 - Top
|
|
||||||
+ * 0x02 - Top right
|
|
||||||
+ * 0x03 - Right
|
|
||||||
+ * 0x04 - Bottom right
|
|
||||||
+ * 0x05 - Bottom
|
|
||||||
+ * 0x06 - Bottom left
|
|
||||||
+ * 0x07 - Left
|
|
||||||
+ *
|
|
||||||
+ * Flags:
|
|
||||||
+ * 0x10 - Calculate offset from the corner towards the center,
|
|
||||||
+ * rather than from the center towards the corner
|
|
||||||
+ */
|
|
||||||
+ uint8_t position;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Pixel offset from the selected position.
|
|
||||||
+ * Example: If the picture is in the top right corner, it will
|
|
||||||
+ * be placed position_offset pixels from the top and
|
|
||||||
+ * position_offset pixels from the right margin.
|
|
||||||
+ */
|
|
||||||
+ uint16_t position_offset;
|
|
||||||
+
|
|
||||||
+ uint8_t padding[24];
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
|
|
||||||
@@ -115,4 +140,22 @@ struct splash_blob_header {
|
|
||||||
uint8_t padding[9];
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+/*
|
|
||||||
+ * Enums for on-disk types
|
|
||||||
+ */
|
|
||||||
+enum splash_position {
|
|
||||||
+ SPLASH_CORNER_TOP_LEFT = 0,
|
|
||||||
+ SPLASH_CORNER_TOP = 1,
|
|
||||||
+ SPLASH_CORNER_TOP_RIGHT = 2,
|
|
||||||
+ SPLASH_CORNER_RIGHT = 3,
|
|
||||||
+ SPLASH_CORNER_BOTTOM_RIGHT = 4,
|
|
||||||
+ SPLASH_CORNER_BOTTOM = 5,
|
|
||||||
+ SPLASH_CORNER_BOTTOM_LEFT = 6,
|
|
||||||
+ SPLASH_CORNER_LEFT = 7,
|
|
||||||
+ SPLASH_POS_FLAG_CORNER = 0x10,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
#endif
|
|
@ -1,327 +0,0 @@
|
|||||||
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
|
|
||||||
+++ 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)
|
|
||||||
+{
|
|
||||||
+ if (bootsplash_would_render_now()) {
|
|
||||||
+ /* This will also re-schedule this delayed worker */
|
|
||||||
+ splash_callback_redraw_vc(ignored);
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
|
|
||||||
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;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ if (!is_update) {
|
|
||||||
+ /* Check whether we actually support this FB. */
|
|
||||||
+ splash_state.splash_fb = NULL;
|
|
||||||
+
|
|
||||||
+ 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;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- 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));
|
|
||||||
+
|
|
||||||
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);
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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))
|
|
||||||
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
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash_internal.h
|
|
||||||
@@ -37,6 +37,8 @@ struct splash_pic_priv {
|
|
||||||
|
|
||||||
struct splash_blob_priv *blobs;
|
|
||||||
u16 blobs_loaded;
|
|
||||||
+
|
|
||||||
+ u16 anim_nextframe;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
@@ -45,6 +47,12 @@ struct splash_file_priv {
|
|
||||||
const struct splash_file_header *header;
|
|
||||||
|
|
||||||
struct splash_pic_priv *pics;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * A local copy of the frame delay in the header.
|
|
||||||
+ * We modify it to keep the code simple.
|
|
||||||
+ */
|
|
||||||
+ 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);
|
|
||||||
+
|
|
||||||
+ ph->anim_type = SPLASH_ANIM_NONE;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
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,
|
|
||||||
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
|
|
||||||
+++ 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;
|
|
||||||
+
|
|
||||||
+ 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+void bootsplash_do_step_animations(struct splash_file_priv *fp)
|
|
||||||
+{
|
|
||||||
+ unsigned int i;
|
|
||||||
+
|
|
||||||
+ /* Step every animation once */
|
|
||||||
+ for (i = 0; i < fp->header->num_pics; i++) {
|
|
||||||
+ struct splash_pic_priv *pp = &fp->pics[i];
|
|
||||||
+
|
|
||||||
+ if (pp->blobs_loaded < 2
|
|
||||||
+ || pp->pic_header->anim_loop > pp->blobs_loaded)
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ 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;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
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;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * 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.
|
|
||||||
+ */
|
|
||||||
+ uint16_t frame_ms;
|
|
||||||
+
|
|
||||||
+ uint8_t padding[100];
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
|
|
||||||
@@ -116,7 +126,23 @@ struct splash_pic_header {
|
|
||||||
*/
|
|
||||||
uint16_t position_offset;
|
|
||||||
|
|
||||||
- uint8_t padding[24];
|
|
||||||
+ /*
|
|
||||||
+ * Animation type.
|
|
||||||
+ * 0 - off
|
|
||||||
+ * 1 - forward loop
|
|
||||||
+ */
|
|
||||||
+ uint8_t anim_type;
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * 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)
|
|
||||||
+ */
|
|
||||||
+ uint8_t anim_loop;
|
|
||||||
+
|
|
||||||
+ uint8_t padding[22];
|
|
||||||
} __attribute__((__packed__));
|
|
||||||
|
|
||||||
|
|
||||||
@@ -158,4 +184,9 @@ enum splash_position {
|
|
||||||
SPLASH_POS_FLAG_CORNER = 0x10,
|
|
||||||
};
|
|
||||||
|
|
||||||
+enum splash_anim_type {
|
|
||||||
+ SPLASH_ANIM_NONE = 0,
|
|
||||||
+ SPLASH_ANIM_LOOP_FORWARD = 1,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
#endif
|
|
@ -1,82 +0,0 @@
|
|||||||
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
|
|
||||||
index 2ebaba16f785..416735ab6dc1 100644
|
|
||||||
--- a/drivers/tty/vt/vt.c
|
|
||||||
+++ b/drivers/tty/vt/vt.c
|
|
||||||
@@ -105,6 +105,7 @@
|
|
||||||
#include <linux/ctype.h>
|
|
||||||
#include <linux/bsearch.h>
|
|
||||||
#include <linux/gcd.h>
|
|
||||||
+#include <linux/bootsplash.h>
|
|
||||||
|
|
||||||
#define MAX_NR_CON_DRIVER 16
|
|
||||||
|
|
||||||
@@ -4235,6 +4236,7 @@ void do_unblank_screen(int leaving_gfx)
|
|
||||||
}
|
|
||||||
|
|
||||||
console_blanked = 0;
|
|
||||||
+ bootsplash_mark_dirty();
|
|
||||||
if (vc->vc_sw->con_blank(vc, 0, leaving_gfx))
|
|
||||||
/* Low-level driver cannot restore -> do it ourselves */
|
|
||||||
update_screen(vc);
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
index c8642142cfea..13fcaabbc2ca 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
@@ -165,6 +165,13 @@ bool bootsplash_would_render_now(void)
|
|
||||||
&& bootsplash_is_enabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
+void bootsplash_mark_dirty(void)
|
|
||||||
+{
|
|
||||||
+ mutex_lock(&splash_state.data_lock);
|
|
||||||
+ splash_state.splash_fb = NULL;
|
|
||||||
+ mutex_unlock(&splash_state.data_lock);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
bool bootsplash_is_enabled(void)
|
|
||||||
{
|
|
||||||
bool was_enabled;
|
|
||||||
@@ -206,9 +213,7 @@ void bootsplash_enable(void)
|
|
||||||
|
|
||||||
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);
|
|
||||||
+ bootsplash_mark_dirty();
|
|
||||||
|
|
||||||
schedule_work(&splash_state.work_redraw_vc);
|
|
||||||
}
|
|
||||||
@@ -272,9 +277,7 @@ 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);
|
|
||||||
+ bootsplash_mark_dirty();
|
|
||||||
|
|
||||||
if (bootsplash_would_render_now())
|
|
||||||
schedule_work(&splash_state.work_redraw_vc);
|
|
||||||
diff --git a/include/linux/bootsplash.h b/include/linux/bootsplash.h
|
|
||||||
index c6dd0b43180d..4075098aaadd 100644
|
|
||||||
--- a/include/linux/bootsplash.h
|
|
||||||
+++ b/include/linux/bootsplash.h
|
|
||||||
@@ -19,6 +19,8 @@ extern void bootsplash_render_full(struct fb_info *info);
|
|
||||||
|
|
||||||
extern bool bootsplash_would_render_now(void);
|
|
||||||
|
|
||||||
+extern void bootsplash_mark_dirty(void);
|
|
||||||
+
|
|
||||||
extern bool bootsplash_is_enabled(void);
|
|
||||||
extern void bootsplash_disable(void);
|
|
||||||
extern void bootsplash_enable(void);
|
|
||||||
@@ -31,6 +33,8 @@ extern void bootsplash_init(void);
|
|
||||||
|
|
||||||
#define bootsplash_would_render_now() (false)
|
|
||||||
|
|
||||||
+#define bootsplash_mark_dirty()
|
|
||||||
+
|
|
||||||
#define bootsplash_is_enabled() (false)
|
|
||||||
#define bootsplash_disable()
|
|
||||||
#define bootsplash_enable()
|
|
@ -1,42 +0,0 @@
|
|||||||
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
|
|
||||||
index f4166263bb3a..a248429194bb 100644
|
|
||||||
--- a/drivers/tty/vt/keyboard.c
|
|
||||||
+++ b/drivers/tty/vt/keyboard.c
|
|
||||||
@@ -49,6 +49,8 @@
|
|
||||||
|
|
||||||
#include <asm/irq_regs.h>
|
|
||||||
|
|
||||||
+#include <linux/bootsplash.h>
|
|
||||||
+
|
|
||||||
/*
|
|
||||||
* Exported functions/variables
|
|
||||||
*/
|
|
||||||
@@ -1413,6 +1415,28 @@ static void kbd_keycode(unsigned int key
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
+ /* Trap keys when bootsplash is shown */
|
|
||||||
+ if (bootsplash_would_render_now()) {
|
|
||||||
+ /* Deactivate bootsplash on ESC or Alt+Fxx VT switch */
|
|
||||||
+ if (keycode >= KEY_F1 && keycode <= KEY_F12) {
|
|
||||||
+ bootsplash_disable();
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * No return here since we want to actually
|
|
||||||
+ * perform the VT switch.
|
|
||||||
+ */
|
|
||||||
+ } else {
|
|
||||||
+ if (keycode == KEY_ESC)
|
|
||||||
+ bootsplash_disable();
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * Just drop any other keys.
|
|
||||||
+ * Their effect would be hidden by the splash.
|
|
||||||
+ */
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (kbd->kbdmode == VC_MEDIUMRAW) {
|
|
||||||
/*
|
|
||||||
* This is extended medium raw mode, with keys above 127
|
|
@ -1,21 +0,0 @@
|
|||||||
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
|
|
||||||
index 3ffc1ce29023..bc6a24c9dfa8 100644
|
|
||||||
--- a/drivers/tty/sysrq.c
|
|
||||||
+++ b/drivers/tty/sysrq.c
|
|
||||||
@@ -49,6 +49,7 @@
|
|
||||||
#include <linux/syscalls.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/rcupdate.h>
|
|
||||||
+#include <linux/bootsplash.h>
|
|
||||||
|
|
||||||
#include <asm/ptrace.h>
|
|
||||||
#include <asm/irq_regs.h>
|
|
||||||
@@ -104,6 +105,8 @@ static void sysrq_handle_SAK(int key)
|
|
||||||
{
|
|
||||||
struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work;
|
|
||||||
schedule_work(SAK_work);
|
|
||||||
+
|
|
||||||
+ bootsplash_disable();
|
|
||||||
}
|
|
||||||
static struct sysrq_key_op sysrq_SAK_op = {
|
|
||||||
.handler = sysrq_handle_SAK,
|
|
@ -1,21 +0,0 @@
|
|||||||
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
|
|
||||||
index 9a39a6fcfe98..8a9c67e1c5d8 100644
|
|
||||||
--- a/drivers/video/fbdev/core/fbcon.c
|
|
||||||
+++ b/drivers/video/fbdev/core/fbcon.c
|
|
||||||
@@ -1343,6 +1343,16 @@ static void fbcon_cursor(struct vc_data *vc, int mode)
|
|
||||||
int y;
|
|
||||||
int c = scr_readw((u16 *) vc->vc_pos);
|
|
||||||
|
|
||||||
+ /*
|
|
||||||
+ * Disable the splash here so we don't have to hook into
|
|
||||||
+ * vt_console_print() in drivers/tty/vt/vt.c
|
|
||||||
+ *
|
|
||||||
+ * We'd disable the splash just before the call to
|
|
||||||
+ * hide_cursor() anyway, so this spot is just fine.
|
|
||||||
+ */
|
|
||||||
+ if (oops_in_progress)
|
|
||||||
+ bootsplash_disable();
|
|
||||||
+
|
|
||||||
ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
|
|
||||||
|
|
||||||
if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1)
|
|
@ -1,321 +0,0 @@
|
|||||||
diff --git a/Documentation/ABI/testing/sysfs-platform-bootsplash b/Documentation/ABI/testing/sysfs-platform-bootsplash
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..742c7b035ded
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/ABI/testing/sysfs-platform-bootsplash
|
|
||||||
@@ -0,0 +1,11 @@
|
|
||||||
+What: /sys/devices/platform/bootsplash.0/enabled
|
|
||||||
+Date: Oct 2017
|
|
||||||
+KernelVersion: 4.14
|
|
||||||
+Contact: Max Staudt <mstaudt@suse.de>
|
|
||||||
+Description:
|
|
||||||
+ Can be set and read.
|
|
||||||
+
|
|
||||||
+ 0: Splash is disabled.
|
|
||||||
+ 1: Splash is shown whenever fbcon would show a text console
|
|
||||||
+ (i.e. no graphical application is running), and a splash
|
|
||||||
+ file is loaded.
|
|
||||||
diff --git a/Documentation/bootsplash.rst b/Documentation/bootsplash.rst
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..611f0c558925
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Documentation/bootsplash.rst
|
|
||||||
@@ -0,0 +1,285 @@
|
|
||||||
+====================
|
|
||||||
+The Linux bootsplash
|
|
||||||
+====================
|
|
||||||
+
|
|
||||||
+:Date: November, 2017
|
|
||||||
+:Author: Max Staudt <mstaudt@suse.de>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+The Linux bootsplash is a graphical replacement for the '``quiet``' boot
|
|
||||||
+option, typically showing a logo and a spinner animation as the system starts.
|
|
||||||
+
|
|
||||||
+Currently, it is a part of the Framebuffer Console support, and can be found
|
|
||||||
+as ``CONFIG_BOOTSPLASH`` in the kernel configuration. This means that as long
|
|
||||||
+as it is enabled, it hijacks fbcon's output and draws a splash screen instead.
|
|
||||||
+
|
|
||||||
+Purely compiling in the bootsplash will not render it functional - to actually
|
|
||||||
+render a splash, you will also need a splash theme file. See the example
|
|
||||||
+utility and script in ``tools/bootsplash`` for a live demo.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Motivation
|
|
||||||
+==========
|
|
||||||
+
|
|
||||||
+- The '``quiet``' boot option only suppresses most messages during boot, but
|
|
||||||
+ errors are still shown.
|
|
||||||
+
|
|
||||||
+- A user space implementation can only show a logo once user space has been
|
|
||||||
+ initialized far enough to allow this. A kernel splash can display a splash
|
|
||||||
+ immediately as soon as fbcon can be displayed.
|
|
||||||
+
|
|
||||||
+- Implementing a splash screen in user space (e.g. Plymouth) is problematic
|
|
||||||
+ due to resource conflicts.
|
|
||||||
+
|
|
||||||
+ For example, if Plymouth is keeping ``/dev/fb0`` (provided via vesafb/efifb)
|
|
||||||
+ open, then most DRM drivers can't replace it because the address space is
|
|
||||||
+ still busy - thus leading to a VRAM reservation error.
|
|
||||||
+
|
|
||||||
+ See: https://bugzilla.opensuse.org/show_bug.cgi?id=980750
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Command line arguments
|
|
||||||
+======================
|
|
||||||
+
|
|
||||||
+``bootsplash.bootfile``
|
|
||||||
+ Which file in the initramfs to load.
|
|
||||||
+
|
|
||||||
+ The splash theme is loaded via request_firmware(), thus to load
|
|
||||||
+ ``/lib/firmware/bootsplash/mytheme`` pass the command line:
|
|
||||||
+
|
|
||||||
+ ``bootsplash.bootfile=bootsplash/mytheme``
|
|
||||||
+
|
|
||||||
+ Note: The splash file *has to be* in the initramfs, as it needs to be
|
|
||||||
+ available when the splash is initialized early on.
|
|
||||||
+
|
|
||||||
+ Default: none, i.e. a non-functional splash, falling back to showing text.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+sysfs run-time configuration
|
|
||||||
+============================
|
|
||||||
+
|
|
||||||
+``/sys/devices/platform/bootsplash.0/enabled``
|
|
||||||
+ Enable/disable the bootsplash.
|
|
||||||
+ The system boots with this set to 1, but will not show a splash unless
|
|
||||||
+ a splash theme file is also loaded.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Kconfig
|
|
||||||
+=======
|
|
||||||
+
|
|
||||||
+``BOOTSPLASH``
|
|
||||||
+ Whether to compile in bootsplash support
|
|
||||||
+ (depends on fbcon compiled in, i.e. ``FRAMEBUFFER_CONSOLE=y``)
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Bootsplash file format
|
|
||||||
+======================
|
|
||||||
+
|
|
||||||
+A file specified in the kernel configuration as ``CONFIG_BOOTSPLASH_FILE``
|
|
||||||
+or specified on the command line as ``bootsplash.bootfile`` will be loaded
|
|
||||||
+and displayed as soon as fbcon is initialized.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Main blocks
|
|
||||||
+-----------
|
|
||||||
+
|
|
||||||
+There are 3 main blocks in each file:
|
|
||||||
+
|
|
||||||
+ - one File header
|
|
||||||
+ - n Picture headers
|
|
||||||
+ - m (Blob header + payload) blocks
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Structures
|
|
||||||
+----------
|
|
||||||
+
|
|
||||||
+The on-disk structures are defined in
|
|
||||||
+``drivers/video/fbdev/core/bootsplash_file.h`` and represent these blocks:
|
|
||||||
+
|
|
||||||
+ - ``struct splash_file_header``
|
|
||||||
+
|
|
||||||
+ Represents the file header, with splash-wide information including:
|
|
||||||
+
|
|
||||||
+ - The magic string "``Linux bootsplash``" on big-endian platforms
|
|
||||||
+ (the reverse on little endian)
|
|
||||||
+ - The file format version (for incompatible updates, hopefully never)
|
|
||||||
+ - The background color
|
|
||||||
+ - Number of picture and blob blocks
|
|
||||||
+ - Animation speed (we only allow one delay for all animations)
|
|
||||||
+
|
|
||||||
+ The file header is followed by the first picture header.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ - ``struct splash_picture_header``
|
|
||||||
+
|
|
||||||
+ Represents an object (picture) drawn on screen, including its immutable
|
|
||||||
+ properties:
|
|
||||||
+ - Width, height
|
|
||||||
+ - Positioning relative to screen corners or in the center
|
|
||||||
+ - Animation, if any
|
|
||||||
+ - Animation type
|
|
||||||
+ - Number of blobs
|
|
||||||
+
|
|
||||||
+ The picture header is followed by another picture header, up until n
|
|
||||||
+ picture headers (as defined in the file header) have been read. Then,
|
|
||||||
+ the (blob header, payload) pairs follow.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ - ``struct splash_blob_header``
|
|
||||||
+ (followed by payload)
|
|
||||||
+
|
|
||||||
+ Represents one raw data stream. So far, only picture data is defined.
|
|
||||||
+
|
|
||||||
+ The blob header is followed by a payload, then padding to n*16 bytes,
|
|
||||||
+ then (if further blobs are defined in the file header) a further blob
|
|
||||||
+ header.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Alignment
|
|
||||||
+---------
|
|
||||||
+
|
|
||||||
+The bootsplash file is designed to be loaded into memory as-is.
|
|
||||||
+
|
|
||||||
+All structures are a multiple of 16 bytes long, all elements therein are
|
|
||||||
+aligned to multiples of their length, and the payloads are always padded
|
|
||||||
+up to multiples of 16 bytes. This is to allow aligned accesses in all
|
|
||||||
+cases while still simply mapping the structures over an in-memory copy of
|
|
||||||
+the bootsplash file.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Further information
|
|
||||||
+-------------------
|
|
||||||
+
|
|
||||||
+Please see ``drivers/video/fbdev/core/bootsplash_file.h`` for further
|
|
||||||
+details and possible values in the file.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Hooks - how the bootsplash is integrated
|
|
||||||
+========================================
|
|
||||||
+
|
|
||||||
+``drivers/video/fbdev/core/fbcon.c``
|
|
||||||
+ ``fbcon_init()`` calls ``bootsplash_init()``, which loads the default
|
|
||||||
+ bootsplash file or the one specified on the kernel command line.
|
|
||||||
+
|
|
||||||
+ ``fbcon_switch()`` draws the bootsplash when it's active, and is also
|
|
||||||
+ one of the callers of ``set_blitting_type()``.
|
|
||||||
+
|
|
||||||
+ ``set_blitting_type()`` calls ``fbcon_set_dummyops()`` when the
|
|
||||||
+ bootsplash is active, overriding the text rendering functions.
|
|
||||||
+
|
|
||||||
+ ``fbcon_cursor()`` will call ``bootsplash_disable()`` when an oops is
|
|
||||||
+ being printed in order to make a kernel panic visible.
|
|
||||||
+
|
|
||||||
+``drivers/video/fbdev/core/dummyblit.c``
|
|
||||||
+ This contains the dummy text rendering functions used to suppress text
|
|
||||||
+ output while the bootsplash is shown.
|
|
||||||
+
|
|
||||||
+``drivers/tty/vt/keyboard.c``
|
|
||||||
+ ``kbd_keycode()`` can call ``bootsplash_disable()`` when the user
|
|
||||||
+ presses ESC or F1-F12 (changing VT). This is to provide a built-in way
|
|
||||||
+ of disabling the splash manually at any time.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+FAQ: Frequently Asked Questions
|
|
||||||
+===============================
|
|
||||||
+
|
|
||||||
+I want to see the log! How do I show the log?
|
|
||||||
+---------------------------------------------
|
|
||||||
+
|
|
||||||
+Press ESC while the splash is shown, or remove the ``bootsplash.bootfile``
|
|
||||||
+parameter from the kernel cmdline. Without that parameter, the bootsplash
|
|
||||||
+will boot disabled.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Why use FB instead of modern DRM/KMS?
|
|
||||||
+-------------------------------------
|
|
||||||
+
|
|
||||||
+This is a semantic problem:
|
|
||||||
+ - What memory to draw the splash to?
|
|
||||||
+ - And what mode will the screen be set to?
|
|
||||||
+
|
|
||||||
+Using the fbdev emulation solves these issues.
|
|
||||||
+
|
|
||||||
+Let's start from a bare KMS system, without fbcon, and without fbdev
|
|
||||||
+emulation. In this case, as long as userspace doesn't open the KMS
|
|
||||||
+device, the state of the screen is undefined. No framebuffer is
|
|
||||||
+allocated in video RAM, and no particular mode is set.
|
|
||||||
+
|
|
||||||
+In this case, we'd have to allocate a framebuffer to show the splash,
|
|
||||||
+and set our mode ourselves. This either wastes a screenful of video RAM
|
|
||||||
+if the splash is to co-exist with the userspace program's own allocated
|
|
||||||
+framebuffer, or there is a flicker as we deactivate and delete the
|
|
||||||
+bootsplash's framebuffer and hand control over to userspace. Since we
|
|
||||||
+may set a different mode than userspace, we'd also have flicker due
|
|
||||||
+to mode switching.
|
|
||||||
+
|
|
||||||
+This logic is already contained in every KMS driver that performs fbdev
|
|
||||||
+emulation. So we might as well use that. And the correct API to do so is
|
|
||||||
+fbdev. Plus, we get compatibility with old, pure fbdev drivers for free.
|
|
||||||
+With the fbdev emulation, there is *always* a well-defined framebuffer
|
|
||||||
+to draw on. And the selection of mode has already been done by the
|
|
||||||
+graphics driver, so we don't need to reinvent that wheel, either.
|
|
||||||
+Finally, if userspace decides to use /dev/fbX, we don't have to worry
|
|
||||||
+about wasting video RAM, either.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Why is the bootsplash integrated in fbcon?
|
|
||||||
+------------------------------------------
|
|
||||||
+
|
|
||||||
+Right now, the bootsplash is drawn from within fbcon, as this allows us
|
|
||||||
+to easily know *when* to draw - i.e. when we're safe from fbcon and
|
|
||||||
+userspace drawing all over our beautiful splash logo.
|
|
||||||
+
|
|
||||||
+Separating them is not easy - see the to-do list below.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+TO DO list for future development
|
|
||||||
+=================================
|
|
||||||
+
|
|
||||||
+Second enable/disable switch for the system
|
|
||||||
+-------------------------------------------
|
|
||||||
+
|
|
||||||
+It may be helpful to differentiate between the system and the user
|
|
||||||
+switching off the bootsplash. Thus, the system may make it disappear and
|
|
||||||
+reappear e.g. for a password prompt, yet once the user has pressed ESC,
|
|
||||||
+it could stay gone.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Fix buggy DRM/KMS drivers
|
|
||||||
+-------------------------
|
|
||||||
+
|
|
||||||
+Currently, the splash code manually checks for fbdev emulation provided by
|
|
||||||
+the ast, cirrus, and mgag200 DRM/KMS drivers.
|
|
||||||
+These drivers use a manual mechanism similar to deferred I/O for their FB
|
|
||||||
+emulation, and thus need to be manually flushed onto the screen in the same
|
|
||||||
+way.
|
|
||||||
+
|
|
||||||
+This may be improved upon in several ways:
|
|
||||||
+
|
|
||||||
+1. Changing these drivers to expose the fbdev BO's memory directly, like
|
|
||||||
+ bochsdrmfb does.
|
|
||||||
+2. Creating a new fb_ops->fb_flush() API to allow the kernel to flush the
|
|
||||||
+ framebuffer once the bootsplash has been drawn into it.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+Separating from fbcon
|
|
||||||
+---------------------
|
|
||||||
+
|
|
||||||
+Separating these two components would yield independence from fbcon being
|
|
||||||
+compiled into the kernel, and thus lowering code size in embedded
|
|
||||||
+applications.
|
|
||||||
+
|
|
||||||
+To do this cleanly will involve a clean separation of users of an FB device
|
|
||||||
+within the kernel, i.e. fbcon, bootsplash, and userspace. Right now, the
|
|
||||||
+legacy fbcon code and VT code co-operate to switch between fbcon and
|
|
||||||
+userspace (by setting the VT into KD_GRAPHICS mode). Installing a muxer
|
|
||||||
+between these components ensues refactoring of old code and checking for
|
|
||||||
+correct locking.
|
|
||||||
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
||||||
index 5c237445761e..7ffac272434e 100644
|
|
||||||
--- a/MAINTAINERS
|
|
||||||
+++ b/MAINTAINERS
|
|
||||||
@@ -2709,6 +2709,8 @@ BOOTSPLASH
|
|
||||||
M: Max Staudt <mstaudt@suse.de>
|
|
||||||
L: linux-fbdev@vger.kernel.org
|
|
||||||
S: Maintained
|
|
||||||
+F: Documentation/ABI/testing/sysfs-platform-bootsplash
|
|
||||||
+F: Documentation/bootsplash.rst
|
|
||||||
F: drivers/video/fbdev/core/bootsplash*.*
|
|
||||||
F: drivers/video/fbdev/core/dummycon.c
|
|
||||||
F: include/linux/bootsplash.h
|
|
@ -1,129 +0,0 @@
|
|||||||
diff --git a/Documentation/ABI/testing/sysfs-platform-bootsplash b/Documentation/ABI/testing/sysfs-platform-bootsplash
|
|
||||||
index 742c7b035ded..f8f4b259220e 100644
|
|
||||||
--- a/Documentation/ABI/testing/sysfs-platform-bootsplash
|
|
||||||
+++ b/Documentation/ABI/testing/sysfs-platform-bootsplash
|
|
||||||
@@ -9,3 +9,35 @@ Description:
|
|
||||||
1: Splash is shown whenever fbcon would show a text console
|
|
||||||
(i.e. no graphical application is running), and a splash
|
|
||||||
file is loaded.
|
|
||||||
+
|
|
||||||
+What: /sys/devices/platform/bootsplash.0/drop_splash
|
|
||||||
+Date: Oct 2017
|
|
||||||
+KernelVersion: 4.14
|
|
||||||
+Contact: Max Staudt <mstaudt@suse.de>
|
|
||||||
+Description:
|
|
||||||
+ Can only be set.
|
|
||||||
+
|
|
||||||
+ Any value written will cause the current splash theme file
|
|
||||||
+ to be unloaded and the text console to be redrawn.
|
|
||||||
+
|
|
||||||
+What: /sys/devices/platform/bootsplash.0/load_file
|
|
||||||
+Date: Oct 2017
|
|
||||||
+KernelVersion: 4.14
|
|
||||||
+Contact: Max Staudt <mstaudt@suse.de>
|
|
||||||
+Description:
|
|
||||||
+ Can only be set.
|
|
||||||
+
|
|
||||||
+ Any value written will cause the splash to be disabled and
|
|
||||||
+ internal memory structures to be freed.
|
|
||||||
+
|
|
||||||
+ A firmware path written will cause a new theme file to be
|
|
||||||
+ loaded and the current bootsplash to be replaced.
|
|
||||||
+ The current enabled/disabled status is not touched.
|
|
||||||
+ If the splash is already active, it will be redrawn.
|
|
||||||
+
|
|
||||||
+ The path has to be a path in /lib/firmware since
|
|
||||||
+ request_firmware() is used to fetch the data.
|
|
||||||
+
|
|
||||||
+ When setting the splash from the shell, echo -n has to be
|
|
||||||
+ used as any trailing '\n' newline will be interpreted as
|
|
||||||
+ part of the path.
|
|
||||||
diff --git a/Documentation/bootsplash.rst b/Documentation/bootsplash.rst
|
|
||||||
index 611f0c558925..b35aba5093e8 100644
|
|
||||||
--- a/Documentation/bootsplash.rst
|
|
||||||
+++ b/Documentation/bootsplash.rst
|
|
||||||
@@ -67,6 +67,14 @@ sysfs run-time configuration
|
|
||||||
a splash theme file is also loaded.
|
|
||||||
|
|
||||||
|
|
||||||
+``/sys/devices/platform/bootsplash.0/drop_splash``
|
|
||||||
+ Unload splash data and free memory.
|
|
||||||
+
|
|
||||||
+``/sys/devices/platform/bootsplash.0/load_file``
|
|
||||||
+ Load a splash file from ``/lib/firmware/``.
|
|
||||||
+ Note that trailing newlines will be interpreted as part of the file name.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
|
|
||||||
Kconfig
|
|
||||||
=======
|
|
||||||
diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
index 13fcaabbc2ca..16cb0493629d 100644
|
|
||||||
--- a/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
+++ b/drivers/video/fbdev/core/bootsplash.c
|
|
||||||
@@ -251,11 +251,65 @@ static ssize_t splash_store_enabled(struct device *device,
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static ssize_t splash_store_drop_splash(struct device *device,
|
|
||||||
+ struct device_attribute *attr,
|
|
||||||
+ const char *buf, size_t count)
|
|
||||||
+{
|
|
||||||
+ struct splash_file_priv *fp;
|
|
||||||
+
|
|
||||||
+ if (!buf || !count || !splash_state.file)
|
|
||||||
+ return count;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&splash_state.data_lock);
|
|
||||||
+ fp = splash_state.file;
|
|
||||||
+ splash_state.file = NULL;
|
|
||||||
+ mutex_unlock(&splash_state.data_lock);
|
|
||||||
+
|
|
||||||
+ /* Redraw the text console */
|
|
||||||
+ schedule_work(&splash_state.work_redraw_vc);
|
|
||||||
+
|
|
||||||
+ bootsplash_free_file(fp);
|
|
||||||
+
|
|
||||||
+ return count;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static ssize_t splash_store_load_file(struct device *device,
|
|
||||||
+ struct device_attribute *attr,
|
|
||||||
+ const char *buf, size_t count)
|
|
||||||
+{
|
|
||||||
+ struct splash_file_priv *fp, *fp_old;
|
|
||||||
+
|
|
||||||
+ if (!count)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ fp = bootsplash_load_firmware(&splash_state.splash_device->dev,
|
|
||||||
+ buf);
|
|
||||||
+
|
|
||||||
+ if (!fp)
|
|
||||||
+ return -ENXIO;
|
|
||||||
+
|
|
||||||
+ mutex_lock(&splash_state.data_lock);
|
|
||||||
+ fp_old = splash_state.file;
|
|
||||||
+ splash_state.splash_fb = NULL;
|
|
||||||
+ splash_state.file = fp;
|
|
||||||
+ mutex_unlock(&splash_state.data_lock);
|
|
||||||
+
|
|
||||||
+ /* Update the splash or text console */
|
|
||||||
+ schedule_work(&splash_state.work_redraw_vc);
|
|
||||||
+
|
|
||||||
+ bootsplash_free_file(fp_old);
|
|
||||||
+ return count;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static DEVICE_ATTR(enabled, 0644, splash_show_enabled, splash_store_enabled);
|
|
||||||
+static DEVICE_ATTR(drop_splash, 0200, NULL, splash_store_drop_splash);
|
|
||||||
+static DEVICE_ATTR(load_file, 0200, NULL, splash_store_load_file);
|
|
||||||
|
|
||||||
|
|
||||||
static struct attribute *splash_dev_attrs[] = {
|
|
||||||
&dev_attr_enabled.attr,
|
|
||||||
+ &dev_attr_drop_splash.attr,
|
|
||||||
+ &dev_attr_load_file.attr,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
@ -1,511 +0,0 @@
|
|||||||
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
||||||
index 7ffac272434e..ddff07cd794c 100644
|
|
||||||
--- a/MAINTAINERS
|
|
||||||
+++ b/MAINTAINERS
|
|
||||||
@@ -2715,6 +2715,7 @@ F: drivers/video/fbdev/core/bootsplash*.*
|
|
||||||
F: drivers/video/fbdev/core/dummycon.c
|
|
||||||
F: include/linux/bootsplash.h
|
|
||||||
F: include/uapi/linux/bootsplash_file.h
|
|
||||||
+F: tools/bootsplash/*
|
|
||||||
|
|
||||||
BPF (Safe dynamic programs and tools)
|
|
||||||
M: Alexei Starovoitov <ast@kernel.org>
|
|
||||||
diff --git a/tools/bootsplash/.gitignore b/tools/bootsplash/.gitignore
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..091b99a17567
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/bootsplash/.gitignore
|
|
||||||
@@ -0,0 +1 @@
|
|
||||||
+bootsplash-packer
|
|
||||||
diff --git a/tools/bootsplash/Makefile b/tools/bootsplash/Makefile
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..0ad8e8a84942
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/bootsplash/Makefile
|
|
||||||
@@ -0,0 +1,9 @@
|
|
||||||
+CC := $(CROSS_COMPILE)gcc
|
|
||||||
+CFLAGS := -I../../usr/include
|
|
||||||
+
|
|
||||||
+PROGS := bootsplash-packer
|
|
||||||
+
|
|
||||||
+all: $(PROGS)
|
|
||||||
+
|
|
||||||
+clean:
|
|
||||||
+ rm -fr $(PROGS)
|
|
||||||
diff --git a/tools/bootsplash/bootsplash-packer.c b/tools/bootsplash/bootsplash-packer.c
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000000..ffb6a8b69885
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tools/bootsplash/bootsplash-packer.c
|
|
||||||
@@ -0,0 +1,471 @@
|
|
||||||
+/*
|
|
||||||
+ * Kernel based bootsplash.
|
|
||||||
+ *
|
|
||||||
+ * (Splash file packer tool)
|
|
||||||
+ *
|
|
||||||
+ * Authors:
|
|
||||||
+ * Max Staudt <mstaudt@suse.de>
|
|
||||||
+ *
|
|
||||||
+ * SPDX-License-Identifier: GPL-2.0
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+#include <endian.h>
|
|
||||||
+#include <getopt.h>
|
|
||||||
+#include <stdint.h>
|
|
||||||
+#include <stdio.h>
|
|
||||||
+#include <stdlib.h>
|
|
||||||
+#include <string.h>
|
|
||||||
+
|
|
||||||
+#include <linux/bootsplash_file.h>
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static void print_help(char *progname)
|
|
||||||
+{
|
|
||||||
+ printf("Usage: %s [OPTIONS] outfile\n", progname);
|
|
||||||
+ printf("\n"
|
|
||||||
+ "Options, executed in order given:\n"
|
|
||||||
+ " -h, --help Print this help message\n"
|
|
||||||
+ "\n"
|
|
||||||
+ " --bg_red <u8> Background color (red part)\n"
|
|
||||||
+ " --bg_green <u8> Background color (green part)\n"
|
|
||||||
+ " --bg_blue <u8> Background color (blue part)\n"
|
|
||||||
+ " --bg_reserved <u8> (do not use)\n"
|
|
||||||
+ " --frame_ms <u16> Minimum milliseconds between animation steps\n"
|
|
||||||
+ "\n"
|
|
||||||
+ " --picture Start describing the next picture\n"
|
|
||||||
+ " --pic_width <u16> Picture width in pixels\n"
|
|
||||||
+ " --pic_height <u16> Picture height in pixels\n"
|
|
||||||
+ " --pic_position <u8> Coarse picture placement:\n"
|
|
||||||
+ " 0x00 - Top left\n"
|
|
||||||
+ " 0x01 - Top\n"
|
|
||||||
+ " 0x02 - Top right\n"
|
|
||||||
+ " 0x03 - Right\n"
|
|
||||||
+ " 0x04 - Bottom right\n"
|
|
||||||
+ " 0x05 - Bottom\n"
|
|
||||||
+ " 0x06 - Bottom left\n"
|
|
||||||
+ " 0x07 - Left\n"
|
|
||||||
+ "\n"
|
|
||||||
+ " Flags:\n"
|
|
||||||
+ " 0x10 - Calculate offset from corner towards center,\n"
|
|
||||||
+ " rather than from center towards corner\n"
|
|
||||||
+ " --pic_position_offset <u16> Distance from base position in pixels\n"
|
|
||||||
+ " --pic_anim_type <u8> Animation type:\n"
|
|
||||||
+ " 0 - None\n"
|
|
||||||
+ " 1 - Forward loop\n"
|
|
||||||
+ " --pic_anim_loop <u8> Loop point for animation\n"
|
|
||||||
+ "\n"
|
|
||||||
+ " --blob <filename> Include next data stream\n"
|
|
||||||
+ " --blob_type <u16> Type of data\n"
|
|
||||||
+ " --blob_picture_id <u8> Picture to associate this blob with, starting at 0\n"
|
|
||||||
+ " (default: number of last --picture)\n"
|
|
||||||
+ "\n");
|
|
||||||
+ printf("This tool will write %s files.\n\n",
|
|
||||||
+#if __BYTE_ORDER == __BIG_ENDIAN
|
|
||||||
+ "Big Endian (BE)");
|
|
||||||
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
|
||||||
+ "Little Endian (LE)");
|
|
||||||
+#else
|
|
||||||
+#error
|
|
||||||
+#endif
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+struct blob_entry {
|
|
||||||
+ struct blob_entry *next;
|
|
||||||
+
|
|
||||||
+ char *fn;
|
|
||||||
+
|
|
||||||
+ struct splash_blob_header header;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+static void dump_file_header(struct splash_file_header *h)
|
|
||||||
+{
|
|
||||||
+ printf(" --- File header ---\n");
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" version: %5u\n", h->version);
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" bg_red: %5u\n", h->bg_red);
|
|
||||||
+ printf(" bg_green: %5u\n", h->bg_green);
|
|
||||||
+ printf(" bg_blue: %5u\n", h->bg_blue);
|
|
||||||
+ printf(" bg_reserved: %5u\n", h->bg_reserved);
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" num_blobs: %5u\n", h->num_blobs);
|
|
||||||
+ printf(" num_pics: %5u\n", h->num_pics);
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" frame_ms: %5u\n", h->frame_ms);
|
|
||||||
+ printf("\n");
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void dump_pic_header(struct splash_pic_header *ph)
|
|
||||||
+{
|
|
||||||
+ printf(" --- Picture header ---\n");
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" width: %5u\n", ph->width);
|
|
||||||
+ printf(" height: %5u\n", ph->height);
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" num_blobs: %5u\n", ph->num_blobs);
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" position: %0x3x\n", ph->position);
|
|
||||||
+ printf(" position_offset: %5u\n", ph->position_offset);
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" anim_type: %5u\n", ph->anim_type);
|
|
||||||
+ printf(" anim_loop: %5u\n", ph->anim_loop);
|
|
||||||
+ printf("\n");
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void dump_blob(struct blob_entry *b)
|
|
||||||
+{
|
|
||||||
+ printf(" --- Blob header ---\n");
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" length: %7u\n", b->header.length);
|
|
||||||
+ printf(" type: %7u\n", b->header.type);
|
|
||||||
+ printf("\n");
|
|
||||||
+ printf(" picture_id: %7u\n", b->header.picture_id);
|
|
||||||
+ printf("\n");
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+#define OPT_MAX(var, max) \
|
|
||||||
+ do { \
|
|
||||||
+ if ((var) > max) { \
|
|
||||||
+ fprintf(stderr, "--%s: Invalid value\n", \
|
|
||||||
+ long_options[option_index].name); \
|
|
||||||
+ break; \
|
|
||||||
+ } \
|
|
||||||
+ } while (0)
|
|
||||||
+
|
|
||||||
+static struct option long_options[] = {
|
|
||||||
+ {"help", 0, 0, 'h'},
|
|
||||||
+ {"bg_red", 1, 0, 10001},
|
|
||||||
+ {"bg_green", 1, 0, 10002},
|
|
||||||
+ {"bg_blue", 1, 0, 10003},
|
|
||||||
+ {"bg_reserved", 1, 0, 10004},
|
|
||||||
+ {"frame_ms", 1, 0, 10005},
|
|
||||||
+ {"picture", 0, 0, 20000},
|
|
||||||
+ {"pic_width", 1, 0, 20001},
|
|
||||||
+ {"pic_height", 1, 0, 20002},
|
|
||||||
+ {"pic_position", 1, 0, 20003},
|
|
||||||
+ {"pic_position_offset", 1, 0, 20004},
|
|
||||||
+ {"pic_anim_type", 1, 0, 20005},
|
|
||||||
+ {"pic_anim_loop", 1, 0, 20006},
|
|
||||||
+ {"blob", 1, 0, 30000},
|
|
||||||
+ {"blob_type", 1, 0, 30001},
|
|
||||||
+ {"blob_picture_id", 1, 0, 30002},
|
|
||||||
+ {NULL, 0, NULL, 0}
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+int main(int argc, char **argv)
|
|
||||||
+{
|
|
||||||
+ FILE *of;
|
|
||||||
+ char *ofn;
|
|
||||||
+ int c;
|
|
||||||
+ int option_index = 0;
|
|
||||||
+
|
|
||||||
+ unsigned long ul;
|
|
||||||
+ struct splash_file_header fh = {};
|
|
||||||
+ struct splash_pic_header ph[255];
|
|
||||||
+ struct blob_entry *blob_first = NULL;
|
|
||||||
+ struct blob_entry *blob_last = NULL;
|
|
||||||
+ struct blob_entry *blob_cur = NULL;
|
|
||||||
+
|
|
||||||
+ if (argc < 2) {
|
|
||||||
+ print_help(argv[0]);
|
|
||||||
+ return EXIT_FAILURE;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ /* Parse and and execute user commands */
|
|
||||||
+ while ((c = getopt_long(argc, argv, "h",
|
|
||||||
+ long_options, &option_index)) != -1) {
|
|
||||||
+ switch (c) {
|
|
||||||
+ case 10001: /* bg_red */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ fh.bg_red = ul;
|
|
||||||
+ break;
|
|
||||||
+ case 10002: /* bg_green */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ fh.bg_green = ul;
|
|
||||||
+ break;
|
|
||||||
+ case 10003: /* bg_blue */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ fh.bg_blue = ul;
|
|
||||||
+ break;
|
|
||||||
+ case 10004: /* bg_reserved */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ fh.bg_reserved = ul;
|
|
||||||
+ break;
|
|
||||||
+ case 10005: /* frame_ms */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 65535);
|
|
||||||
+ fh.frame_ms = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ case 20000: /* picture */
|
|
||||||
+ if (fh.num_pics >= 255) {
|
|
||||||
+ fprintf(stderr, "--%s: Picture array full\n",
|
|
||||||
+ long_options[option_index].name);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ fh.num_pics++;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 20001: /* pic_width */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 65535);
|
|
||||||
+ ph[fh.num_pics - 1].width = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 20002: /* pic_height */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 65535);
|
|
||||||
+ ph[fh.num_pics - 1].height = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 20003: /* pic_position */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ ph[fh.num_pics - 1].position = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 20004: /* pic_position_offset */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ ph[fh.num_pics - 1].position_offset = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 20005: /* pic_anim_type */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ ph[fh.num_pics - 1].anim_type = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 20006: /* pic_anim_loop */
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ ph[fh.num_pics - 1].anim_loop = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ case 30000: /* blob */
|
|
||||||
+ if (fh.num_blobs >= 65535) {
|
|
||||||
+ fprintf(stderr, "--%s: Blob array full\n",
|
|
||||||
+ long_options[option_index].name);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ blob_cur = calloc(1, sizeof(struct blob_entry));
|
|
||||||
+ if (!blob_cur) {
|
|
||||||
+ fprintf(stderr, "--%s: Out of memory\n",
|
|
||||||
+ long_options[option_index].name);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ blob_cur->fn = optarg;
|
|
||||||
+ if (fh.num_pics)
|
|
||||||
+ blob_cur->header.picture_id = fh.num_pics - 1;
|
|
||||||
+
|
|
||||||
+ if (!blob_first)
|
|
||||||
+ blob_first = blob_cur;
|
|
||||||
+ if (blob_last)
|
|
||||||
+ blob_last->next = blob_cur;
|
|
||||||
+ blob_last = blob_cur;
|
|
||||||
+ fh.num_blobs++;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 30001: /* blob_type */
|
|
||||||
+ if (!blob_cur) {
|
|
||||||
+ fprintf(stderr, "--%s: No blob selected\n",
|
|
||||||
+ long_options[option_index].name);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ blob_cur->header.type = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ case 30002: /* blob_picture_id */
|
|
||||||
+ if (!blob_cur) {
|
|
||||||
+ fprintf(stderr, "--%s: No blob selected\n",
|
|
||||||
+ long_options[option_index].name);
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ul = strtoul(optarg, NULL, 0);
|
|
||||||
+ OPT_MAX(ul, 255);
|
|
||||||
+ blob_cur->header.picture_id = ul;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ case 'h':
|
|
||||||
+ case '?':
|
|
||||||
+ default:
|
|
||||||
+ print_help(argv[0]);
|
|
||||||
+ goto EXIT;
|
|
||||||
+ } /* switch (c) */
|
|
||||||
+ } /* while ((c = getopt_long(...)) != -1) */
|
|
||||||
+
|
|
||||||
+ /* Consume and drop lone arguments */
|
|
||||||
+ while (optind < argc) {
|
|
||||||
+ ofn = argv[optind];
|
|
||||||
+ optind++;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ /* Read file lengths */
|
|
||||||
+ for (blob_cur = blob_first; blob_cur; blob_cur = blob_cur->next) {
|
|
||||||
+ FILE *f;
|
|
||||||
+ long pos;
|
|
||||||
+ int i;
|
|
||||||
+
|
|
||||||
+ if (!blob_cur->fn)
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ f = fopen(blob_cur->fn, "rb");
|
|
||||||
+ if (!f)
|
|
||||||
+ goto ERR_FILE_LEN;
|
|
||||||
+
|
|
||||||
+ if (fseek(f, 0, SEEK_END))
|
|
||||||
+ goto ERR_FILE_LEN;
|
|
||||||
+
|
|
||||||
+ pos = ftell(f);
|
|
||||||
+ if (pos < 0 || pos > (1 << 30))
|
|
||||||
+ goto ERR_FILE_LEN;
|
|
||||||
+
|
|
||||||
+ blob_cur->header.length = pos;
|
|
||||||
+
|
|
||||||
+ fclose(f);
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ERR_FILE_LEN:
|
|
||||||
+ fprintf(stderr, "Error getting file length (or too long): %s\n",
|
|
||||||
+ blob_cur->fn);
|
|
||||||
+ if (f)
|
|
||||||
+ fclose(f);
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ /* Set magic headers */
|
|
||||||
+#if __BYTE_ORDER == __BIG_ENDIAN
|
|
||||||
+ memcpy(&fh.id[0], BOOTSPLASH_MAGIC_BE, 16);
|
|
||||||
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
|
||||||
+ memcpy(&fh.id[0], BOOTSPLASH_MAGIC_LE, 16);
|
|
||||||
+#else
|
|
||||||
+#error
|
|
||||||
+#endif
|
|
||||||
+ fh.version = BOOTSPLASH_VERSION;
|
|
||||||
+
|
|
||||||
+ /* Set blob counts */
|
|
||||||
+ for (blob_cur = blob_first; blob_cur; blob_cur = blob_cur->next) {
|
|
||||||
+ if (blob_cur->header.picture_id < fh.num_pics)
|
|
||||||
+ ph[blob_cur->header.picture_id].num_blobs++;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ /* Dump structs */
|
|
||||||
+ dump_file_header(&fh);
|
|
||||||
+
|
|
||||||
+ for (ul = 0; ul < fh.num_pics; ul++)
|
|
||||||
+ dump_pic_header(&ph[ul]);
|
|
||||||
+
|
|
||||||
+ for (blob_cur = blob_first; blob_cur; blob_cur = blob_cur->next)
|
|
||||||
+ dump_blob(blob_cur);
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ /* Write to file */
|
|
||||||
+ printf("Writing splash to file: %s\n", ofn);
|
|
||||||
+ of = fopen(ofn, "wb");
|
|
||||||
+ if (!of)
|
|
||||||
+ goto ERR_WRITING;
|
|
||||||
+
|
|
||||||
+ if (fwrite(&fh, sizeof(struct splash_file_header), 1, of) != 1)
|
|
||||||
+ goto ERR_WRITING;
|
|
||||||
+
|
|
||||||
+ for (ul = 0; ul < fh.num_pics; ul++) {
|
|
||||||
+ if (fwrite(&ph[ul], sizeof(struct splash_pic_header), 1, of)
|
|
||||||
+ != 1)
|
|
||||||
+ goto ERR_WRITING;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ blob_cur = blob_first;
|
|
||||||
+ while (blob_cur) {
|
|
||||||
+ struct blob_entry *blob_old = blob_cur;
|
|
||||||
+ FILE *f;
|
|
||||||
+ char *buf[256];
|
|
||||||
+ uint32_t left;
|
|
||||||
+
|
|
||||||
+ if (fwrite(&blob_cur->header,
|
|
||||||
+ sizeof(struct splash_blob_header), 1, of) != 1)
|
|
||||||
+ goto ERR_WRITING;
|
|
||||||
+
|
|
||||||
+ if (!blob_cur->header.length || !blob_cur->fn)
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ f = fopen(blob_cur->fn, "rb");
|
|
||||||
+ if (!f)
|
|
||||||
+ goto ERR_FILE_COPY;
|
|
||||||
+
|
|
||||||
+ left = blob_cur->header.length;
|
|
||||||
+ while (left >= sizeof(buf)) {
|
|
||||||
+ if (fread(buf, sizeof(buf), 1, f) != 1)
|
|
||||||
+ goto ERR_FILE_COPY;
|
|
||||||
+ if (fwrite(buf, sizeof(buf), 1, of) != 1)
|
|
||||||
+ goto ERR_FILE_COPY;
|
|
||||||
+ left -= sizeof(buf);
|
|
||||||
+ }
|
|
||||||
+ if (left) {
|
|
||||||
+ if (fread(buf, left, 1, f) != 1)
|
|
||||||
+ goto ERR_FILE_COPY;
|
|
||||||
+ if (fwrite(buf, left, 1, of) != 1)
|
|
||||||
+ goto ERR_FILE_COPY;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Pad data stream to 16 bytes */
|
|
||||||
+ if (left % 16) {
|
|
||||||
+ if (fwrite("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
|
|
||||||
+ 16 - (left % 16), 1, of) != 1)
|
|
||||||
+ goto ERR_FILE_COPY;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ fclose(f);
|
|
||||||
+ blob_cur = blob_cur->next;
|
|
||||||
+ free(blob_old);
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ERR_FILE_COPY:
|
|
||||||
+ if (f)
|
|
||||||
+ fclose(f);
|
|
||||||
+ goto ERR_WRITING;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ fclose(of);
|
|
||||||
+
|
|
||||||
+EXIT:
|
|
||||||
+ return EXIT_SUCCESS;
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ERR_WRITING:
|
|
||||||
+ fprintf(stderr, "Error writing splash.\n");
|
|
||||||
+ fprintf(stderr, "The output file is probably corrupt.\n");
|
|
||||||
+ if (of)
|
|
||||||
+ fclose(of);
|
|
||||||
+
|
|
||||||
+ while (blob_cur) {
|
|
||||||
+ struct blob_entry *blob_old = blob_cur;
|
|
||||||
+
|
|
||||||
+ blob_cur = blob_cur->next;
|
|
||||||
+ free(blob_old);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return EXIT_FAILURE;
|
|
||||||
+}
|
|
@ -28,19 +28,6 @@ PATCHES=(
|
|||||||
# Pinephone Keyboard
|
# Pinephone Keyboard
|
||||||
${FILESDIR}/pp-keyboard.patch
|
${FILESDIR}/pp-keyboard.patch
|
||||||
${FILESDIR}/ppp-keyboard.patch
|
${FILESDIR}/ppp-keyboard.patch
|
||||||
# Bootsplash
|
|
||||||
${FILESDIR}/0001-bootsplash.patch
|
|
||||||
${FILESDIR}/0002-bootsplash.patch
|
|
||||||
${FILESDIR}/0003-bootsplash.patch
|
|
||||||
${FILESDIR}/0004-bootsplash.patch
|
|
||||||
${FILESDIR}/0005-bootsplash.patch
|
|
||||||
${FILESDIR}/0006-bootsplash.patch
|
|
||||||
${FILESDIR}/0007-bootsplash.patch
|
|
||||||
${FILESDIR}/0008-bootsplash.patch
|
|
||||||
${FILESDIR}/0009-bootsplash.patch
|
|
||||||
${FILESDIR}/0010-bootsplash.patch
|
|
||||||
${FILESDIR}/0011-bootsplash.patch
|
|
||||||
${FILESDIR}/0012-bootsplash.patch
|
|
||||||
)
|
)
|
||||||
|
|
||||||
src_prepare() {
|
src_prepare() {
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
# Copyright 1999-2022 Gentoo Authors
|
|
||||||
# Distributed under the terms of the GNU General Public License v2
|
|
||||||
|
|
||||||
|
|
||||||
EAPI="8"
|
|
||||||
ETYPE="sources"
|
|
||||||
K_WANT_GENPATCHES="base extras"
|
|
||||||
K_GENPATCHES_VER="2"
|
|
||||||
|
|
||||||
MEGI_PATCH_URI="https://xff.cz/kernels/${PV:0:4}/patches/all.patch"
|
|
||||||
|
|
||||||
inherit kernel-2
|
|
||||||
detect_version
|
|
||||||
detect_arch
|
|
||||||
|
|
||||||
KEYWORDS="~arm64"
|
|
||||||
|
|
||||||
DEPEND="${RDEPEND}
|
|
||||||
>=sys-devel/patch-2.7.5"
|
|
||||||
|
|
||||||
DESCRIPTION="Full sources for the Linux kernel with gentoo patchset and with megi's patch for the PinePhone and PinePhone Pro"
|
|
||||||
|
|
||||||
SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI} ${MEGI_PATCH_URI} -> all-${PV}.patch"
|
|
||||||
|
|
||||||
PATCHES=(
|
|
||||||
#Megi patch set
|
|
||||||
${DISTDIR}/all-${PV}.patch
|
|
||||||
# Pinephone Keyboard
|
|
||||||
${FILESDIR}/pp-keyboard.patch
|
|
||||||
${FILESDIR}/ppp-keyboard.patch
|
|
||||||
# Bootsplash
|
|
||||||
${FILESDIR}/0001-bootsplash.patch
|
|
||||||
${FILESDIR}/0002-bootsplash.patch
|
|
||||||
${FILESDIR}/0003-bootsplash.patch
|
|
||||||
${FILESDIR}/0004-bootsplash.patch
|
|
||||||
${FILESDIR}/0005-bootsplash.patch
|
|
||||||
${FILESDIR}/0006-bootsplash.patch
|
|
||||||
${FILESDIR}/0007-bootsplash.patch
|
|
||||||
${FILESDIR}/0008-bootsplash.patch
|
|
||||||
${FILESDIR}/0009-bootsplash.patch
|
|
||||||
${FILESDIR}/0010-bootsplash.patch
|
|
||||||
${FILESDIR}/0011-bootsplash.patch
|
|
||||||
${FILESDIR}/0012-bootsplash.patch
|
|
||||||
)
|
|
||||||
|
|
||||||
src_prepare() {
|
|
||||||
default
|
|
||||||
eapply_user
|
|
||||||
}
|
|
||||||
|
|
||||||
pkg_postinst() {
|
|
||||||
kernel-2_pkg_postinst
|
|
||||||
einfo "For more info on this patchset, and how to report problems, see:"
|
|
||||||
einfo "${HOMEPAGE}"
|
|
||||||
einfo "To build the kernel use the following command:"
|
|
||||||
einfo "make Image Image.gz modules"
|
|
||||||
einfo "make DTC_FLAGS="-@" dtbs"
|
|
||||||
einfo "make install; make modules_intall; make dtbs_install"
|
|
||||||
einfo "if you want to cross compile pinephone kernel on amd64 host, follow the https://wiki.gentoo.org/wiki/Cross_build_environment"
|
|
||||||
einfo "to setup cross toolchain environment, then create a xmake wrapper like the following, and replace make with xmake in above commands"
|
|
||||||
einfo "#!/bin/sh"
|
|
||||||
einfo "exec make ARCH='arm64' CROSS_COMPILE='aarch64-unknown-linux-gnu-' INSTALL_MOD_PATH='${SYSROOT}' '$@'"
|
|
||||||
}
|
|
||||||
|
|
||||||
pkg_postrm() {
|
|
||||||
kernel-2_pkg_postrm
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
|||||||
# Copyright 1999-2022 Gentoo Authors
|
|
||||||
# Distributed under the terms of the GNU General Public License v2
|
|
||||||
|
|
||||||
|
|
||||||
EAPI="8"
|
|
||||||
ETYPE="sources"
|
|
||||||
K_WANT_GENPATCHES="base extras"
|
|
||||||
K_GENPATCHES_VER="3"
|
|
||||||
|
|
||||||
MEGI_PATCH_URI="https://xff.cz/kernels/${PV:0:4}/patches/all.patch"
|
|
||||||
|
|
||||||
inherit kernel-2
|
|
||||||
detect_version
|
|
||||||
detect_arch
|
|
||||||
|
|
||||||
KEYWORDS="~arm64"
|
|
||||||
|
|
||||||
DEPEND="${RDEPEND}
|
|
||||||
>=sys-devel/patch-2.7.5"
|
|
||||||
|
|
||||||
DESCRIPTION="Full sources for the Linux kernel with gentoo patchset and with megi's patch for the PinePhone and PinePhone Pro"
|
|
||||||
|
|
||||||
SRC_URI="${KERNEL_URI} ${GENPATCHES_URI} ${ARCH_URI} ${MEGI_PATCH_URI} -> all-${PV}.patch"
|
|
||||||
|
|
||||||
PATCHES=(
|
|
||||||
#Megi patch set
|
|
||||||
${DISTDIR}/all-${PV}.patch
|
|
||||||
# Pinephone Keyboard
|
|
||||||
${FILESDIR}/pp-keyboard.patch
|
|
||||||
${FILESDIR}/ppp-keyboard.patch
|
|
||||||
# Bootsplash
|
|
||||||
${FILESDIR}/0001-bootsplash.patch
|
|
||||||
${FILESDIR}/0002-bootsplash.patch
|
|
||||||
${FILESDIR}/0003-bootsplash.patch
|
|
||||||
${FILESDIR}/0004-bootsplash.patch
|
|
||||||
${FILESDIR}/0005-bootsplash.patch
|
|
||||||
${FILESDIR}/0006-bootsplash.patch
|
|
||||||
${FILESDIR}/0007-bootsplash.patch
|
|
||||||
${FILESDIR}/0008-bootsplash.patch
|
|
||||||
${FILESDIR}/0009-bootsplash.patch
|
|
||||||
${FILESDIR}/0010-bootsplash.patch
|
|
||||||
${FILESDIR}/0011-bootsplash.patch
|
|
||||||
${FILESDIR}/0012-bootsplash.patch
|
|
||||||
)
|
|
||||||
|
|
||||||
src_prepare() {
|
|
||||||
default
|
|
||||||
eapply_user
|
|
||||||
}
|
|
||||||
|
|
||||||
pkg_postinst() {
|
|
||||||
kernel-2_pkg_postinst
|
|
||||||
einfo "For more info on this patchset, and how to report problems, see:"
|
|
||||||
einfo "${HOMEPAGE}"
|
|
||||||
einfo "To build the kernel use the following command:"
|
|
||||||
einfo "make Image Image.gz modules"
|
|
||||||
einfo "make DTC_FLAGS="-@" dtbs"
|
|
||||||
einfo "make install; make modules_intall; make dtbs_install"
|
|
||||||
einfo "if you want to cross compile pinephone kernel on amd64 host, follow the https://wiki.gentoo.org/wiki/Cross_build_environment"
|
|
||||||
einfo "to setup cross toolchain environment, then create a xmake wrapper like the following, and replace make with xmake in above commands"
|
|
||||||
einfo "#!/bin/sh"
|
|
||||||
einfo "exec make ARCH='arm64' CROSS_COMPILE='aarch64-unknown-linux-gnu-' INSTALL_MOD_PATH='${SYSROOT}' '$@'"
|
|
||||||
}
|
|
||||||
|
|
||||||
pkg_postrm() {
|
|
||||||
kernel-2_pkg_postrm
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user