linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v3 00/13] Kernel based bootsplash
@ 2018-01-17 20:54 Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 01/13] bootsplash: Initial implementation showing black screen Max Staudt
                   ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Dear fbdev/fbcon/dri developers,

Thanks for all the valuable feedback.

I've looked into the suggestions you made, and found that it doesn't
currently make sense to continue working on the splash code, given the
low practical interest I've received on LKML. The code is, and always
has been, intended primarily as a study of what can be done, and at
this point it has fulfilled this requirement.

Please find attached my latest version of the patchset in which I've
clarified the documentation a bit, as well as added a FAQ and To-Do
section for anyone wishing to pick up the code.

The code is still based on v4.14-rc5, sorry about that.

In particular, I hope to have clarified in the FAQ why I'm building on
top of fbdev and fbcon, as I think I haven't made myself clear enough
in the previous discussion. If you still think that my reasoning is
wrong, I'd be thankful for pointers towards a better solution.


I'll be happy to rebase it and continue to work on it if interest
arises.


This project has been a valuable experience - so huge thanks to everyone
involved in any way, from user feedback over code reviews and all the way
to architectural discussion.


Max

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 01/13] bootsplash: Initial implementation showing black screen
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 02/13] bootsplash: Add file reading and picture rendering Max Staudt
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

This is the initial prototype for a lean Linux kernel bootsplash.

It works by replacing fbcon's FB manipulation routines (such as
bitblit, tileblit) with dummy functions, effectively disabling text
output, and drawing the splash directly onto the FB device.

There is a userland API via sysfs, to show/hide the splash on request
by dracut, systemd, or other init systems.

As of this commit, the code will show a black screen rather than a
logo, and only if manually enabled via sysfs by writing:

  echo 1 > /sys/devices/platform/bootsplash.0/enabled

The reasons for implementing a bootsplash in kernel space are:

 - Quieting things more and nicer than with the quiet boot option:
   Currently the 'quiet' boot option does not remove the blinking
   cursor and errors are still printed. There are use cases where this
   is not desirable (such as embedded and desktop systems, digital
   signage, etc.) and a vendor logo is preferable.

 - Showing graphics, and never text, when the GUI crashes:
   This is an extension of the above use case, where recovery is meant
   to happen as invisibly to the user as possible. A system integrator
   needs the flexibility to hide "scary text" from users in all cases
   other than a panic.
   This is especially desirable in embedded systems such as digital
   signage.

 - Racy VT API:
   Userspace bootsplashes and GUIs (e.g. plymouth and X) tend to kick
   each other out via the non-exclusive KDSETMODE ioctl. This can
   result in situations such as the user being stuck in X with chvt
   and Ctrl-Alt-Fx no longer working.

 - Mode switching from FB to KMS:
   We cannot switch from a generic framebuffer (vesafb, efifb) to a
   KMS driver while a userspace splash keeps /dev/fb0 open. The device
   will vanish, but the address space is still busy, so the KMS driver
   cannot reserve its VRAM.

 - Simplification of userspace integration:
   Right now, hooking up a splash screen in userspace is quite complex.
   Having it in the kernel makes this a breeze, as hooks for
   switch_root, remounting r/w, etc. become obsolete.

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 MAINTAINERS                                    |   8 +
 drivers/video/console/Kconfig                  |  24 ++
 drivers/video/fbdev/core/Makefile              |   3 +
 drivers/video/fbdev/core/bootsplash.c          | 294 +++++++++++++++++++++++++
 drivers/video/fbdev/core/bootsplash_internal.h |  55 +++++
 drivers/video/fbdev/core/bootsplash_render.c   |  93 ++++++++
 drivers/video/fbdev/core/dummyblit.c           |  89 ++++++++
 drivers/video/fbdev/core/fbcon.c               |  22 ++
 drivers/video/fbdev/core/fbcon.h               |   5 +
 include/linux/bootsplash.h                     |  43 ++++
 10 files changed, 636 insertions(+)
 create mode 100644 drivers/video/fbdev/core/bootsplash.c
 create mode 100644 drivers/video/fbdev/core/bootsplash_internal.h
 create mode 100644 drivers/video/fbdev/core/bootsplash_render.c
 create mode 100644 drivers/video/fbdev/core/dummyblit.c
 create mode 100644 include/linux/bootsplash.h

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
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 02/13] bootsplash: Add file reading and picture rendering
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 01/13] bootsplash: Initial implementation showing black screen Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 03/13] bootsplash: Flush framebuffer after drawing Max Staudt
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Load logo(s) from a file and render them in the center of the screen.

This removes the "black screen" functionality, which can now be emulated
by providing a splash file with no pictures and a black background.

To enable the bootsplash at boot, provide a theme file *in the initramfs*
and tell the kernel to use it as follows:

  bootsplash.bootfile=mypath/myfile

Since the splash code is using request_firmware() to load the file,
the path has to be beneath /lib/firmware.

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 MAINTAINERS                                    |   1 +
 drivers/video/fbdev/core/Makefile              |   2 +-
 drivers/video/fbdev/core/bootsplash.c          |  36 +++-
 drivers/video/fbdev/core/bootsplash_internal.h |  45 ++++-
 drivers/video/fbdev/core/bootsplash_load.c     | 225 +++++++++++++++++++++++++
 drivers/video/fbdev/core/bootsplash_render.c   | 103 ++++++++++-
 include/uapi/linux/bootsplash_file.h           | 118 +++++++++++++
 7 files changed, 522 insertions(+), 8 deletions(-)
 create mode 100644 drivers/video/fbdev/core/bootsplash_load.c
 create mode 100644 include/uapi/linux/bootsplash_file.h

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
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 03/13] bootsplash: Flush framebuffer after drawing
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 01/13] bootsplash: Initial implementation showing black screen Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 02/13] bootsplash: Add file reading and picture rendering Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 04/13] bootsplash: Add corner positioning Max Staudt
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Framebuffers with deferred I/O need to be flushed to the screen
explicitly, since we use neither the mmap nor the file I/O abstractions
that handle this for userspace FB clients.

Example: xenfb

Some framebuffer drivers implement lazy access to the screen without
actually exposing a fbdefio interface - we also match some known ones,
currently:
 - ast
 - cirrus
 - mgag200

Signed-off-by: Max Staudt <mstaudt@suse.de>
Reviewed-by: Oliver Neukum <oneukum@suse.com>
---
 drivers/video/fbdev/core/bootsplash.c          |  2 ++
 drivers/video/fbdev/core/bootsplash_internal.h |  1 +
 drivers/video/fbdev/core/bootsplash_render.c   | 33 ++++++++++++++++++++++++++
 3 files changed, 36 insertions(+)

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);
+	}
+}
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 04/13] bootsplash: Add corner positioning
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (2 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 03/13] bootsplash: Flush framebuffer after drawing Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 05/13] bootsplash: Add animation support Max Staudt
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

This allows showing multiple logos, each in its own position,
relative to the eight screen corners.

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 drivers/video/fbdev/core/bootsplash_render.c | 136 ++++++++++++++++++++++++++-
 include/uapi/linux/bootsplash_file.h         |  45 ++++++++-
 2 files changed, 178 insertions(+), 3 deletions(-)

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
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 05/13] bootsplash: Add animation support
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (3 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 04/13] bootsplash: Add corner positioning Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 06/13] vt: Redraw bootsplash fully on console_unblank Max Staudt
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Each 'picture' in the splash file can consist of multiple 'blobs'.

If animation is enabled, these blobs become the frames of an animation,
in the order in which they are stored in the file.

Note: There is only one global timer, so all animations happen at
      the same frame rate. It doesn't really make sense to animate
      more than one object at a time anyway.

Furthermore, this patch introduces a check for reusing a framebuffer
where the splash has recently been painted on - in this case, we only
redraw the objects that are animated.

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 drivers/video/fbdev/core/bootsplash.c          | 62 +++++++++++++++++++++++---
 drivers/video/fbdev/core/bootsplash_internal.h | 13 +++++-
 drivers/video/fbdev/core/bootsplash_load.c     | 21 +++++++++
 drivers/video/fbdev/core/bootsplash_render.c   | 30 ++++++++++++-
 include/uapi/linux/bootsplash_file.h           | 35 ++++++++++++++-
 5 files changed, 151 insertions(+), 10 deletions(-)

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
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 06/13] vt: Redraw bootsplash fully on console_unblank
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (4 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 05/13] bootsplash: Add animation support Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 07/13] vt: Add keyboard hook to disable bootsplash Max Staudt
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

After exiting a KD_GRAPHICS program and falling back to the text
console, a previously enabled splash needs to be fully redrawn.

This corner case was introduced with selective re-drawing while
implementing animations.

Without this patch, the following happens:

1. Switch to a text console
2. Enable splash
3. Start X (or any other KD_GRAPHICS program)
4. Exit X
5. Splash is not seen, apart from animations

Signed-off-by: Max Staudt <mstaudt@suse.de>
Reviewed-by: Oliver Neukum <oneukum@suse.com>
---
 drivers/tty/vt/vt.c                   |  2 ++
 drivers/video/fbdev/core/bootsplash.c | 15 +++++++++------
 include/linux/bootsplash.h            |  4 ++++
 3 files changed, 15 insertions(+), 6 deletions(-)

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
@@ -102,6 +102,7 @@
 #include <linux/uaccess.h>
 #include <linux/kdb.h>
 #include <linux/ctype.h>
+#include <linux/bootsplash.h>
 
 #define MAX_NR_CON_DRIVER 16
 
@@ -3903,6 +3904,7 @@ void do_unblank_screen(int leaving_gfx)
 	}
 
 	console_blanked = 0;
+	bootsplash_mark_dirty();
 	if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc))
 		/* 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()
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 07/13] vt: Add keyboard hook to disable bootsplash
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (5 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 06/13] vt: Redraw bootsplash fully on console_unblank Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 08/13] sysrq: Disable bootsplash on SAK Max Staudt
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Let's disable the splash if the user presses ESC or F1-F12 on a VT.

The F1-F12 check is to disable the splash on VT switches.

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 drivers/tty/vt/keyboard.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

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
@@ -47,6 +47,8 @@
 
 #include <asm/irq_regs.h>
 
+#include <linux/bootsplash.h>
+
 extern void ctrl_alt_del(void);
 
 /*
@@ -1353,6 +1355,28 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 	}
 #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
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 08/13] sysrq: Disable bootsplash on SAK
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (6 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 07/13] vt: Add keyboard hook to disable bootsplash Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 09/13] fbcon: Disable bootsplash on oops Max Staudt
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

When the user requests a clean TTY via the SAK SysRq, that means he
really wants to use the console.

Let's disable the bootsplash, even if the request is not on a VT, as
the user probably knows what he's doing and it's more helpful to get
out of his way.

Signed-off-by: Max Staudt <mstaudt@suse.de>
Reviewed-by: Oliver Neukum <oneukum@suse.com>
---
 drivers/tty/sysrq.c | 3 +++
 1 file changed, 3 insertions(+)

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,
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 09/13] fbcon: Disable bootsplash on oops
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (7 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 08/13] sysrq: Disable bootsplash on SAK Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 10/13] Documentation: Add bootsplash main documentation Max Staudt
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Signed-off-by: Max Staudt <mstaudt@suse.de>
Reviewed-by: Oliver Neukum <oneukum@suse.com>
---
 drivers/video/fbdev/core/fbcon.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

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)
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 10/13] Documentation: Add bootsplash main documentation
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (8 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 09/13] fbcon: Disable bootsplash on oops Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 11/13] bootsplash: sysfs entries to load and unload files Max Staudt
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 .../ABI/testing/sysfs-platform-bootsplash          |  11 +
 Documentation/bootsplash.rst                       | 285 +++++++++++++++++++++
 MAINTAINERS                                        |   2 +
 3 files changed, 298 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-bootsplash
 create mode 100644 Documentation/bootsplash.rst

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
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 11/13] bootsplash: sysfs entries to load and unload files
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (9 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 10/13] Documentation: Add bootsplash main documentation Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 12/13] tools/bootsplash: Add a basic splash file creation tool Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 13/13] tools/bootsplash: Add script and data to create sample file Max Staudt
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Users can use this to replace their splash screen at runtime by writing
a path and filename to /sys/devices/platform/bootsplash.0/load_file and
making sure the splash is enabled.

Notes:
  - 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.

Writes to /sys/devices/platform/bootsplash.0/drop_splash will cause the
current splash theme to be freed and the console to switch to text mode,

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 .../ABI/testing/sysfs-platform-bootsplash          | 32 +++++++++++++
 Documentation/bootsplash.rst                       |  8 ++++
 drivers/video/fbdev/core/bootsplash.c              | 54 ++++++++++++++++++++++
 3 files changed, 94 insertions(+)

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
 };
 
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 12/13] tools/bootsplash: Add a basic splash file creation tool
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (10 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 11/13] bootsplash: sysfs entries to load and unload files Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  2018-01-17 20:54 ` [RFC PATCH v3 13/13] tools/bootsplash: Add script and data to create sample file Max Staudt
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 MAINTAINERS                          |   1 +
 tools/bootsplash/.gitignore          |   1 +
 tools/bootsplash/Makefile            |   9 +
 tools/bootsplash/bootsplash-packer.c | 471 +++++++++++++++++++++++++++++++++++
 4 files changed, 482 insertions(+)
 create mode 100644 tools/bootsplash/.gitignore
 create mode 100644 tools/bootsplash/Makefile
 create mode 100644 tools/bootsplash/bootsplash-packer.c

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;
+}
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [RFC PATCH v3 13/13] tools/bootsplash: Add script and data to create sample file
  2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
                   ` (11 preceding siblings ...)
  2018-01-17 20:54 ` [RFC PATCH v3 12/13] tools/bootsplash: Add a basic splash file creation tool Max Staudt
@ 2018-01-17 20:54 ` Max Staudt
  12 siblings, 0 replies; 14+ messages in thread
From: Max Staudt @ 2018-01-17 20:54 UTC (permalink / raw)
  To: b.zolnierkie, linux-fbdev
  Cc: mstaudt, dri-devel, linux-kernel, tiwai, oneukum, msrb, sndirsch,
	michal, philm, bernhard.rosenkranzer, kernel.max

Also, mention this in the bootsplash documentation.

Signed-off-by: Max Staudt <mstaudt@suse.de>
---
 Documentation/bootsplash.rst       |  10 ++++++
 tools/bootsplash/.gitignore        |   3 ++
 tools/bootsplash/ajax-loader.gif   | Bin 0 -> 3208 bytes
 tools/bootsplash/bootsplash-tux.sh |  66 +++++++++++++++++++++++++++++++++++++
 4 files changed, 79 insertions(+)
 create mode 100644 tools/bootsplash/ajax-loader.gif
 create mode 100755 tools/bootsplash/bootsplash-tux.sh

diff --git a/Documentation/bootsplash.rst b/Documentation/bootsplash.rst
index b35aba5093e8..d4f132eca615 100644
--- a/Documentation/bootsplash.rst
+++ b/Documentation/bootsplash.rst
@@ -195,6 +195,16 @@ Hooks - how the bootsplash is integrated
 
 
 
+Crating a bootsplash theme file
+===============================
+
+A simple tool for theme file creation is included in ``tools/bootsplash``.
+
+There is also an example shell script, as an example on how to use the tool
+and in order to generate a reference bootsplash file.
+
+
+
 FAQ: Frequently Asked Questions
 ===============================
 
diff --git a/tools/bootsplash/.gitignore b/tools/bootsplash/.gitignore
index 091b99a17567..5dfced41ba82 100644
--- a/tools/bootsplash/.gitignore
+++ b/tools/bootsplash/.gitignore
@@ -1 +1,4 @@
 bootsplash-packer
+bootsplash
+logo.rgb
+throbber*.rgb
diff --git a/tools/bootsplash/ajax-loader.gif b/tools/bootsplash/ajax-loader.gif
new file mode 100644
index 0000000000000000000000000000000000000000..3288d1035d70bb86517e2c233f1a904e41f06b29
GIT binary patch
literal 3208
zcmc(iX;4#H9>pJdFE7h`I{IF)0|5<6L}(j=N}5%L009EB2nYfyF)E0PvIqo$u!IC;
z4PgyY5|S9AEh38G)(9eq4TbH7_UHg@yWrlIJ$6smIADL7s^P;_O;ykRc<bJ}b<Y2s
zU)AOL`#QVCGXW;>9soXl`UC*LwQJXkii*0rx|*7rI2=x7WaRkx_~XZqFJ8R3c=2Kg
zf@aSAv8+BJ8+^hyay>(QR@t*blbKzsf0}bscEqRc5Hd3o(-N5RyW=zWB*zQw6Zh>*
z2CROCDAbu#D`)S|J_<lj7Yz9)#_Og>o(lL9Yn3l*+8RdiRD_>iNz$#_IAzCna&Wl5
zSF_(rRCDD!wi#i8oAm&jYtn2_@VB%2-H*G%bN#|(6R6N?wM)3u`PiGzwuX7qmTgyF
zpE)h0kuoxQ9?=kW7Y!=R@DmhU9)vwT<ZMc0Y;&y4jY1%TT3z!|H=R-GXDHPiKcVWh
zY+!etO=DI2rIs8{iFWtPv(Lu|O3u|$F3Sbq;+xF{gTX$#T%m?MUUZy&ug3$=zXgXj
zrxrf}reg*D3HB~8JyLgl$UCyV?EQ`@OKjW@tGrvh6ZqPD#+m=rK0T{FT01>*EZWzJ
zrt+=2tqFts72yIp?|gvdLhs8Hfku^Z(){gmN%Y=K#<L1VKWYjwV^JDyeS;Y$p1xw*
z#3VzfAV>P|%fkvg<hUP3U1Q=Hdgg~ik+2zyAc79kpuA<f*-~l+ZBH3*S2jBrEOF0w
zrxe9#Vx$SxnL0JE4WeeXY1)ppOIy3@Vvexu&oeIa&QvoD`jBE#Gd7rT{j&OMLz1Wu
zOEj;)PR^=mxjCG0NOUJb&U;ui6*-`3&wmcQ>Uj~HfIp3CuXqCtYGtJ#me+n+-LmP(
z*XNuk%!aH8bIE@_Bj46>M*dSro|7<6vZ7WUHh5YQzN$>IJFqCb|CT!wj~R2C2%=q{
zpt8rzY$aw?W?=Ustv{jo?Ow@<k6~~d?F>ZRkLe<)NItY>Cyhle*wR59dTdF6(@{5^
zAQBOB*hNtc3bkY-8{Cm$nFS@elbTtSqrt7MB{h_4y+~`!mVa}?c&N>&?P}GqdMuhQ
z&@TD5Czd((DcG_Su~dKKV)Pj$-qi1WHM8_vc^O4?^!oY|tmK~i!{fjd&@_1E(T~r7
z_REZy&hMT^ySJB3W7l<L=l9ZMvC<Gz>$4YhR`M(J7S5S~+4Q&3HPa)z%zPpisOp$^
zTEe99ig2$5_qFr!$;<oK+H}=wcaT3=%Nm!;Kw7MHnU5paWS{tI1+DOU?!7xefZ57L
ze_iPrUrRQct0FSCtTFLtg*<#jo}Z3{E?T{skj>7A6CJ}PJmRhli>w?LC}Y`#HLGy6
zMU4<C6_PR!wGq`HQyoWJb;nj8>EhL~dKCN5Ut;U2jd*83ShB<kA1Y@1U)Ar;N|HhS
znIkwkT(&i5XhkI;xwmC%DvPhGNIi?aY<|8rajSt<ap(2E-#qSPQxAp@jIY@-@>Niu
zcJB0l9>1Modc?-oM<<M{t-|U0{*W+=Ct2ZY_02y-De{7vW<f^HJQhd1l&4)Gw2oOS
zm46KASlsKI@J$sA#$$|7D5QMbewIaFv4fXyNbL5Ac~kS&g^#5XHaYBvNxbF3Y2L*6
ztrn?JmgOFAo1lh99BEb^pp>R<Z&2wFwWd*z2wF6&nmW9}nyMfWMO`hc&zkr2AeBP3
zj75NZQ8-VthLviI^j@e=FN6wxR@1uCRv<b;Y<3t(dr<e}N%b}FQtKxHi9xU2C!#0Z
zO2<#(;s&964KtWfkQVi``vIFT7kbT~d;ITb0T9+U1AwIgET*ciil)~4gl;xgoy5M!
z-UJHerGNh_`lO!vA)%ly=~<}ykhlnQnoP$oqido+`qK(cOpmt^pbhf`n-FQaIK5ix
zq@=#Sl2Y&s<pe8B!1!YA78W7dA?2Xu9v7QHc?}NN)sx(o6iZ#|kHX64nijZG(yB1J
zfMQm;1rb5O!-+1Pov;csFu7z>4?<d6>}3g}UJ%@K);kriq>)e*rh%hdqM)5Q)*+O8
zXm;SEbs@koiYS!9YXIclSg+5m_s~yrW#kKMdiRszg(gCP5HPmP7L)vCf8@fxUh6qY
z@Z#TmkjzAZX{rwE+q|K~F2v5{_@vt%>yT_a#fF03SFt{0RX<yi^Bg0BS3UHmG;U4d
z`2QlHs<l7ezUo)s<V^9ZccYv>vDAiaY~K9CgS1O>frXgAjBCS}mEd4mIWZ$=ovd5|
zR?GRdU}d6+Q`+JRW)|=v7$)X<at#L3(d9WVd8CstDNPh>Nkn3yE`!nAiSCvOB1jKT
zG<1aK3s<0b0m==egTD#8i(<nFTpHvxfx|aIng5yR81z6E<naz8-Ow^p@sCs8mz=%h
zO$v$X0NS?ofjnp~62AE}^z%gY8Nsqj=NwUqyj+o6s$@kK@d+U4Vp-^_G32vzv@8nI
z01{`FL$DXQL%WB*9R<xn7$ya31flsbiVh+-0m=YeB_ocaW;YRxI51d(jP?N!ane91
z9~^yzJ;S;OWRKC8PrrXYkZCaruNYE>Of=1pGDTOCho0XpIOMQ&P87cVKY1W=C6kIg
z9cH=@a&zbm2+`|{(_?YC9fdm?1TY~-pwlBn?>=(~1pDKbco6jloP;0-cqRiwV1A_S
zEyV0Dj8Pwy!nekzaN>{)7rgZ&_QLxK{~1yRe865^<m)Ax^m58MY|zev&92(G7#vQU
zn~8r)5oUrwM9`}05|I<Nx*n}jlvg&C9_310Dd4OT2txd91Z*_U8bRtrNaq+nGd{E#
zVGckZFpr^;mv}%%T{jHtz<a=^%;mPXVY7SR`@6_Uw@(0*>yx>}+a!ECd>#MMwddow
z@CU{l+Rt$xuXuf}?ga{3IAr?Raql^c@a%sI0U5m}HvJ5O1#I%_MMPt#BH>OqUZ{-k
zt>4Xzz=%jT*FVW(uYkWyx}9Gw$HdN*qU?Bit#ji(Wi7p-u|_8?h^%szIS^s^fNM}b
zgGy>|=cbEufpguY5_6w~&ZLv=Bo06UF9EYIY;Er-1VK)SyF&!|J{axiE1z^(hXwVq
zsFS=K-#zC}CcOs^8W{KAt+kK)jYDgDYbCXv{{<mZ_TMxh0{w%6lzzG*pm+Dj4XaZ5
zoJwkk5)~fyUmzYbwMERR3j)XePHj^2P!5GK`~^RXuEz>rwsgqtIU3<910$CJi)s??
z_t8k{>7*0~4l~LLF7$WXT5OSq5QCTbP_l!SN|{R}3D&eWA8~0ltWh1IL+ZBX4rRSt
zWF6Om3WDMu4xK^1(BF`2cL}rUCzhHAB`@j5&R-yk_l*t;mPGY|u2^o|myvcOdrg0W
z%=lX;f^Vkqfp?u7*4qQq%A3Mpf!xspWBSKS@O%r*TSM}?dl(@*%{0Jm_8;(h{R__M
Bt<?Yk

literal 0
HcmV?d00001

diff --git a/tools/bootsplash/bootsplash-tux.sh b/tools/bootsplash/bootsplash-tux.sh
new file mode 100755
index 000000000000..1078f87644b9
--- /dev/null
+++ b/tools/bootsplash/bootsplash-tux.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# A simple script to show how to create a bootsplash.
+# Do with it whatever you wish.
+#
+# This needs ImageMagick for the 'convert' and 'identify' tools.
+#
+
+LOGO=../../Documentation/logo.gif
+LOGO_WIDTH=$(identify $LOGO | cut -d " " -f 3 | cut -d x -f 1)
+LOGO_HEIGHT=$(identify $LOGO | cut -d " " -f 3 | cut -d x -f 2)
+
+THROBBER=ajax-loader.gif
+THROBBER_WIDTH=$(identify $THROBBER | head -1 | cut -d " " -f 3 | \
+						cut -d x -f 1)
+THROBBER_HEIGHT=$(identify $THROBBER | head -1 | cut -d " " -f 3 | \
+						 cut -d x -f 2)
+
+convert -alpha remove \
+	-background "#ff3a40" \
+	$LOGO \
+	logo.rgb
+
+convert -alpha remove \
+	-background "#ff3a40" \
+	$THROBBER \
+	throbber%02d.rgb
+
+
+make clean
+make bootsplash-packer
+
+
+# Let's put Tux in the center of an orange background.
+./bootsplash-packer \
+	--bg_red 0xff \
+	--bg_green 0x3a \
+	--bg_blue 0x40 \
+	--frame_ms 48 \
+	--picture \
+	--pic_width $LOGO_WIDTH \
+	--pic_height $LOGO_HEIGHT \
+	--pic_position 0 \
+	--blob logo.rgb \
+	--picture \
+	--pic_width $THROBBER_WIDTH \
+	--pic_height $THROBBER_HEIGHT \
+	--pic_position 0x14 \
+	--pic_position_offset 20 \
+	--pic_anim_type 1 \
+	--pic_anim_loop 0 \
+	--blob throbber00.rgb \
+	--blob throbber01.rgb \
+	--blob throbber02.rgb \
+	--blob throbber03.rgb \
+	--blob throbber04.rgb \
+	--blob throbber05.rgb \
+	--blob throbber06.rgb \
+	--blob throbber07.rgb \
+	--blob throbber08.rgb \
+	--blob throbber09.rgb \
+	--blob throbber10.rgb \
+	--blob throbber11.rgb \
+	bootsplash
+
+rm *.rgb
-- 
2.12.3

^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2018-01-17 20:59 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-17 20:54 [RFC PATCH v3 00/13] Kernel based bootsplash Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 01/13] bootsplash: Initial implementation showing black screen Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 02/13] bootsplash: Add file reading and picture rendering Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 03/13] bootsplash: Flush framebuffer after drawing Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 04/13] bootsplash: Add corner positioning Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 05/13] bootsplash: Add animation support Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 06/13] vt: Redraw bootsplash fully on console_unblank Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 07/13] vt: Add keyboard hook to disable bootsplash Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 08/13] sysrq: Disable bootsplash on SAK Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 09/13] fbcon: Disable bootsplash on oops Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 10/13] Documentation: Add bootsplash main documentation Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 11/13] bootsplash: sysfs entries to load and unload files Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 12/13] tools/bootsplash: Add a basic splash file creation tool Max Staudt
2018-01-17 20:54 ` [RFC PATCH v3 13/13] tools/bootsplash: Add script and data to create sample file Max Staudt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).