nongnu: Add linux-pinephone and linux-pinephone-pro.

* nongnu/packages/linux.scm (linux-pinephone-urls, pinephone-linux, linux-pinephone, linux-pinephone-pro): New variables.
* nongnu/packages/patches/linux-pinephone-pro-defconfig-guix-fix.patch: New
file.
* nongnu/packages/patches/pinephone-0001-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0002-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0003-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0004-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0005-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0006-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0007-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0008-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0009-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0010-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-0011-bootsplash.patch: New file.
* nongnu/packages/patches/pinephone-drop-modem-power-node.patch: New file.
* nongnu/packages/patches/pinephone-fbcon-remove-no-op-fbcon_set_origin.patch: New file.
* nongnu/packages/patches/pinephone-pro-add-modem-ri.patch.patch: New file.
* nongnu/packages/patches/pinephone-pro-remove-modem-node.patch: New file.
* nongnu/packages/patches/pinephone-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch: New file.
* nongnu/packages/patches/pinephone-revert-fbcon-remove-soft-scrollback-code.patch: New file.

asd
This commit is contained in:
Petr Hodina 2022-08-24 06:23:03 +02:00
parent 8c0a857ceb
commit d6fa79a01b
19 changed files with 4059 additions and 0 deletions

View File

@ -35,6 +35,7 @@
#:use-module (guix build-system linux-module) #:use-module (guix build-system linux-module)
#:use-module (guix build-system trivial) #:use-module (guix build-system trivial)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (nonguix licenses) #:use-module (nonguix licenses)
#:export (corrupt-linux)) #:export (corrupt-linux))
@ -43,6 +44,12 @@
(list (string-append "https://www.kernel.org/pub/linux/kernel/v" (list (string-append "https://www.kernel.org/pub/linux/kernel/v"
(version-major version) ".x/linux-" version ".tar.xz"))) (version-major version) ".x/linux-" version ".tar.xz")))
(define (linux-pinephone-urls version commit)
"Return a list of URLS for Linux VERSION."
(list (string-append
"https://github.com/megous/linux/archive/refs/tags/orange-pi-"
(version-major+minor version) "-" commit ".tar.gz")))
(define* (corrupt-linux freedo #:key (name "linux")) (define* (corrupt-linux freedo #:key (name "linux"))
(package (package
(inherit (inherit
@ -59,6 +66,55 @@
"The unmodified Linux kernel, including nonfree blobs, for running Guix "The unmodified Linux kernel, including nonfree blobs, for running Guix
System on hardware which requires nonfree software to function."))) System on hardware which requires nonfree software to function.")))
;; Linux kernel fork for PinePhone and PinePhone Pro
(define* (pinephone-linux freedo version-megi release defconfig #:key (name "linux"))
(package
(inherit
(customize-linux
#:name name
#:defconfig defconfig
#:source (origin (inherit (package-source freedo))
(method url-fetch)
(file-name (string-append name "-" (version-major+minor
version-megi)
"-guix.tar.gz"))
(uri (linux-pinephone-urls version-megi
release))
(patches
(parameterize
((%patch-path
(map (lambda (directory)
(string-append directory "/nongnu/packages/patches"))
%load-path)))
(search-patches
"linux-pinephone-pro-defconfig-guix-fix.patch"))))))
; ;; Port bootsplash patches
;;;"pinephone-0001-bootsplash.patch"
;;;"pinephone-0002-bootsplash.patch"
;;"pinephone-revert-fbcon-remove-now-unusued-softback_lines-cursor-argument.patch"
;;;"pinephone-0003-bootsplash.patch"
;;"pinephone-fbcon-remove-no-op-fbcon_set_origin.patch"
;;;"pinephone-0004-bootsplash.patch"
;;"pinephone-revert-fbcon-remove-soft-scrollback-code.patch"
;;;"pinephone-0005-bootsplash.patch"
;;;"pinephone-0004-bootsplash.patch"
;;;"pinephone-0006-bootsplash.patch"
;;;"pinephone-0007-bootsplash.patch"
;;;"pinephone-0008-bootsplash.patch"
;;;"pinephone-0009-bootsplash.patch"
;;;"pinephone-0010-bootsplash.patch"
;;;"pinephone-0011-bootsplash.patch"
;;"pinephone-drop-modem-power-node.patch"
;;"pinephone-pro-add-modem-ri.patch.patch"
;;"pinephone-pro-remove-modem-node.patch"
; ))))))
(version (package-version freedo))
(home-page "https://www.kernel.org/")
(synopsis "Linux kernel with nonfree binary blobs included")
(description
"The unmodified Linux kernel, including nonfree blobs, for running Guix
System on hardware which requires nonfree software to function.")))
(define-public linux-6.1 (define-public linux-6.1
(corrupt-linux linux-libre-6.1)) (corrupt-linux linux-libre-6.1))
@ -91,6 +147,14 @@ System on hardware which requires nonfree software to function.")))
(define-public linux-arm64-generic-lts linux-arm64-generic-5.15) (define-public linux-arm64-generic-lts linux-arm64-generic-5.15)
(define-public linux-pinephone-pro
(pinephone-linux linux-libre-arm64-generic "6.1" "20230104-1712" "pinephone_pro_defconfig"
#:name "linux-pinephone-pro"))
(define-public linux-pinephone
(pinephone-linux linux-libre-arm64-generic "6.1" "20230104-1712" "pinephone_defconfig"
#:name "linux-pinephone"))
(define-public linux-firmware (define-public linux-firmware
(package (package
(name "linux-firmware") (name "linux-firmware")

View File

@ -0,0 +1,13 @@
--- a/arch/arm64/configs/pinephone_pro_defconfig.orig 2023-01-14 08:25:28.004411948 +0100
+++ b/arch/arm64/configs/pinephone_pro_defconfig 2023-01-14 08:47:08.696514744 +0100
@@ -252,8 +252,8 @@
CONFIG_UEVENT_HELPER=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
-#CONFIG_EXTRA_FIRMWARE="regulatory.db regulatory.db.p7s brcm/brcmfmac43455-sdio.bin brcm/brcmfmac43455-sdio.pine64,pinephone-pro.txt brcm/brcmfmac43455-sdio.clm_blob brcm/BCM4345C0.hcd rockchip/dptx.bin"
-#CONFIG_EXTRA_FIRMWARE_DIR="/workspace/megous.com/orangepi-pc/firmware"
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_EXTRA_FIRMWARE_DIR is not set
CONFIG_ARM_SCMI_PROTOCOL=y
CONFIG_ARM_SCPI_PROTOCOL=y
CONFIG_MTD=y

View File

@ -0,0 +1,750 @@
diff --git a/MAINTAINERS b/MAINTAINERS
index a74227ad082e..b5633b56391e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3582,6 +3614,18 @@ F: drivers/net/bonding/
F: include/net/bond*
F: include/uapi/linux/if_bonding.h
+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
+F: include/uapi/linux/bootsplash_file.h
+F: tools/bootsplash/*
+
BOSCH SENSORTEC BMA400 ACCELEROMETER IIO DRIVER
M: Dan Robertson <dan@dlrobertson.com>
L: linux-iio@vger.kernel.org
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

View File

@ -0,0 +1,657 @@
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

View File

@ -0,0 +1,66 @@
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);
+ }
+}

View File

@ -0,0 +1,215 @@
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

View File

@ -0,0 +1,327 @@
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

View File

@ -0,0 +1,82 @@
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 2ebaba16f785..416735ab6dc1 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -105,6 +105,7 @@
#include <linux/ctype.h>
#include <linux/bsearch.h>
#include <linux/gcd.h>
+#include <linux/bootsplash.h>
#define MAX_NR_CON_DRIVER 16
@@ -4235,6 +4236,7 @@ void do_unblank_screen(int leaving_gfx)
}
console_blanked = 0;
+ bootsplash_mark_dirty();
if (vc->vc_sw->con_blank(vc, 0, leaving_gfx))
/* Low-level driver cannot restore -> do it ourselves */
update_screen(vc);
diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c
index c8642142cfea..13fcaabbc2ca 100644
--- a/drivers/video/fbdev/core/bootsplash.c
+++ b/drivers/video/fbdev/core/bootsplash.c
@@ -165,6 +165,13 @@ bool bootsplash_would_render_now(void)
&& bootsplash_is_enabled();
}
+void bootsplash_mark_dirty(void)
+{
+ mutex_lock(&splash_state.data_lock);
+ splash_state.splash_fb = NULL;
+ mutex_unlock(&splash_state.data_lock);
+}
+
bool bootsplash_is_enabled(void)
{
bool was_enabled;
@@ -206,9 +213,7 @@ void bootsplash_enable(void)
if (!was_enabled) {
/* Force a full redraw when the splash is re-activated */
- mutex_lock(&splash_state.data_lock);
- splash_state.splash_fb = NULL;
- mutex_unlock(&splash_state.data_lock);
+ bootsplash_mark_dirty();
schedule_work(&splash_state.work_redraw_vc);
}
@@ -272,9 +277,7 @@ static int splash_resume(struct device *device)
* Force full redraw on resume since we've probably lost the
* framebuffer's contents meanwhile
*/
- mutex_lock(&splash_state.data_lock);
- splash_state.splash_fb = NULL;
- mutex_unlock(&splash_state.data_lock);
+ bootsplash_mark_dirty();
if (bootsplash_would_render_now())
schedule_work(&splash_state.work_redraw_vc);
diff --git a/include/linux/bootsplash.h b/include/linux/bootsplash.h
index c6dd0b43180d..4075098aaadd 100644
--- a/include/linux/bootsplash.h
+++ b/include/linux/bootsplash.h
@@ -19,6 +19,8 @@ extern void bootsplash_render_full(struct fb_info *info);
extern bool bootsplash_would_render_now(void);
+extern void bootsplash_mark_dirty(void);
+
extern bool bootsplash_is_enabled(void);
extern void bootsplash_disable(void);
extern void bootsplash_enable(void);
@@ -31,6 +33,8 @@ extern void bootsplash_init(void);
#define bootsplash_would_render_now() (false)
+#define bootsplash_mark_dirty()
+
#define bootsplash_is_enabled() (false)
#define bootsplash_disable()
#define bootsplash_enable()

View File

@ -0,0 +1,42 @@
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index f4166263bb3a..a248429194bb 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -49,6 +49,8 @@
#include <asm/irq_regs.h>
+#include <linux/bootsplash.h>
+
/*
* Exported functions/variables
*/
@@ -1413,6 +1415,28 @@ static void kbd_keycode(unsigned int key
}
#endif
+ /* Trap keys when bootsplash is shown */
+ if (bootsplash_would_render_now()) {
+ /* Deactivate bootsplash on ESC or Alt+Fxx VT switch */
+ if (keycode >= KEY_F1 && keycode <= KEY_F12) {
+ bootsplash_disable();
+
+ /*
+ * No return here since we want to actually
+ * perform the VT switch.
+ */
+ } else {
+ if (keycode == KEY_ESC)
+ bootsplash_disable();
+
+ /*
+ * Just drop any other keys.
+ * Their effect would be hidden by the splash.
+ */
+ return;
+ }
+ }
+
if (kbd->kbdmode == VC_MEDIUMRAW) {
/*
* This is extended medium raw mode, with keys above 127

View File

@ -0,0 +1,21 @@
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,

View File

@ -0,0 +1,21 @@
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)

View File

@ -0,0 +1,308 @@
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.

View File

@ -0,0 +1,499 @@
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;
+}

View File

@ -0,0 +1,175 @@
From 602d05e416ae0d0fba3022fa2c3d195164b406c6 Mon Sep 17 00:00:00 2001
From: Clayton Craft <clayton@craftyguy.net>
Date: Wed, 16 Dec 2020 20:16:14 -0800
Subject: [PATCH] dts: pinephone: drop modem-power node
---
.../allwinner/sun50i-a64-pinephone-1.0.dts | 26 +++---------------
.../allwinner/sun50i-a64-pinephone-1.1.dts | 27 +++----------------
.../allwinner/sun50i-a64-pinephone-1.2.dts | 27 +++----------------
.../dts/allwinner/sun50i-a64-pinephone.dtsi | 12 +++++++++
4 files changed, 24 insertions(+), 68 deletions(-)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts
index a21c6d78a..7f0cfdafe 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.0.dts
@@ -86,28 +86,6 @@ &reg_drivevbus {
status = "okay";
};
-&uart3 {
- modem {
- compatible = "quectel,eg25";
- char-device-name = "modem-power";
-
- power-supply = <&reg_vbat_bb>; /* PL7 */
-
- enable-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */
- reset-gpios = <&pio 2 4 GPIO_ACTIVE_HIGH>; /* PC4 */
- pwrkey-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
-
- sleep-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */
- wakeup-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2-RI */
-
- cts-gpios = <&pio 3 5 GPIO_ACTIVE_HIGH>; /* PD5-CTS */
- dtr-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6-DTR */
- rts-gpios = <&pio 3 4 GPIO_ACTIVE_HIGH>; /* PD4-RTS */
-
- quectel,qdai = "1,1,0,1,0,0,1,1";
- };
-};
-
&usbphy {
usb-role-switch;
@@ -118,6 +96,10 @@ usb0_drd_sw: endpoint {
};
};
+&ring_indicator {
+ gpios = <&pio 1 2 GPIO_ACTIVE_LOW>; /* PB2 */
+};
+
&sgm3140 {
enable-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */
flash-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts
index 61ff56b17..5e85ddc12 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.1.dts
@@ -109,34 +109,15 @@ &reg_drivevbus {
status = "okay";
};
+&ring_indicator {
+ gpios = <&pio 1 2 GPIO_ACTIVE_LOW>; /* PB2 */
+};
+
&sgm3140 {
enable-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */
flash-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */
};
-&uart3 {
- modem {
- compatible = "quectel,eg25";
- char-device-name = "modem-power";
-
- power-supply = <&reg_vbat_bb>; /* PL7 */
-
- enable-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */
- reset-gpios = <&pio 2 4 GPIO_ACTIVE_HIGH>; /* PC4 */
- pwrkey-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
- //status-pwrkey-multiplexed; /* status acts as pwrkey */
-
- sleep-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */
- wakeup-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2-RI */
-
- dtr-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6-DTR */
- cts-gpios = <&pio 3 5 GPIO_ACTIVE_HIGH>; /* PD5-CTS */
- rts-gpios = <&pio 3 4 GPIO_ACTIVE_HIGH>; /* PD4-RTS */
-
- quectel,qdai = "1,1,0,1,0,0,1,1";
- };
-};
-
&usbphy {
usb-role-switch;
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts
index fe7d567a8..f4b9b0991 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone-1.2.dts
@@ -101,34 +101,15 @@ &reg_anx1v0 {
enable-active-high;
};
+&ring_indicator {
+ gpios = <&r_pio 0 6 GPIO_ACTIVE_LOW>; /* PL6 */
+};
+
&sgm3140 {
enable-gpios = <&pio 3 24 GPIO_ACTIVE_HIGH>; /* PD24 */
flash-gpios = <&pio 2 3 GPIO_ACTIVE_HIGH>; /* PC3 */
};
-&uart3 {
- modem {
- compatible = "quectel,eg25";
- char-device-name = "modem-power";
-
- power-supply = <&reg_vbat_bb>; /* PL7 */
-
- enable-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */
- reset-gpios = <&pio 2 4 GPIO_ACTIVE_HIGH>; /* PC4 */
- status-gpios = <&pio 7 9 GPIO_ACTIVE_HIGH>; /* PH9 */
- pwrkey-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */
-
- host-ready-gpios = <&pio 7 7 GPIO_ACTIVE_HIGH>; /* PH7 */
- wakeup-gpios = <&r_pio 0 6 GPIO_ACTIVE_HIGH>; /* PL6-RI */
-
- dtr-gpios = <&pio 1 2 GPIO_ACTIVE_HIGH>; /* PB2-DTR */
- cts-gpios = <&pio 3 5 GPIO_ACTIVE_HIGH>; /* PD5-CTS */
- rts-gpios = <&pio 3 4 GPIO_ACTIVE_HIGH>; /* PD4-RTS */
-
- quectel,qdai = "1,1,0,1,0,0,1,1";
- };
-};
-
&usbphy {
usb-role-switch;
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
index 346113382..7b48126d1 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pinephone.dtsi
@@ -192,6 +192,17 @@ ec25_codec: ec25-codec {
sound-name-prefix = "Modem";
};
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ ring_indicator: ring-indicator {
+ label = "Ring Indicator";
+ linux,can-disable;
+ linux,code = <KEY_WAKEUP>;
+ wakeup-source;
+ };
+ };
+
i2c_csi: i2c-csi {
compatible = "i2c-gpio";
sda-gpios = <&pio 4 13 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; /* PE13 */
@@ -264,6 +275,7 @@ reg_usb_5v: usb-5v {
reg_vbat_bb: vbat-bb {
compatible = "regulator-fixed";
regulator-name = "vbat-bb";
+ regulator-always-on;
regulator-min-microvolt = <3500000>;
regulator-max-microvolt = <3500000>;
gpio = <&r_pio 0 7 GPIO_ACTIVE_HIGH>; /* PL7 */
--
2.31.1

View File

@ -0,0 +1,31 @@
--- b/drivers/video/fbdev/core/fbcon.c
+++ a/drivers/video/fbdev/core/fbcon.c
@@ -163,6 +163,8 @@
#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
+static int fbcon_set_origin(struct vc_data *);
+
static int fbcon_cursor_noblink;
#define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1)
@@ -2633,6 +2635,11 @@
}
}
+static int fbcon_set_origin(struct vc_data *vc)
+{
+ return 0;
+}
+
void fbcon_suspended(struct fb_info *info)
{
struct vc_data *vc = NULL;
@@ -3103,6 +3110,7 @@
.con_font_default = fbcon_set_def_font,
.con_font_copy = fbcon_copy_font,
.con_set_palette = fbcon_set_palette,
+ .con_set_origin = fbcon_set_origin,
.con_invert_region = fbcon_invert_region,
.con_screen_pos = fbcon_screen_pos,
.con_getxy = fbcon_getxy,

View File

@ -0,0 +1,52 @@
From fc38b73e2556211d698849b78a4623dc163b7d49 Mon Sep 17 00:00:00 2001
From: Arnaud Ferraris <arnaud.ferraris@gmail.com>
Date: Wed, 8 Dec 2021 23:43:08 +0100
Subject: [PATCH 3/4] arm64: dts: rk3399-pinephone-pro: add modem RI pin
Taht way the modem can wake the phone on incoming calls/messages.
Signed-off-by: Arnaud Ferraris <arnaud.ferraris@gmail.com>
---
.../dts/rockchip/rk3399-pinephone-pro.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
index c9365e604..b3b091ea7 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
@@ -160,6 +160,21 @@ power {
};
};
+ gpio-key-ri {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&ri_pin>;
+
+ ring_indicator: ring-indicator {
+ label = "ring-indicator";
+ linux,can-disable;
+ linux,code = <KEY_WAKEUP>;
+ gpios = <&gpio0 RK_PA1 GPIO_ACTIVE_LOW>;
+ wakeup-event-action = <EV_ACT_ASSERTED>;
+ wakeup-source;
+ };
+ };
+
// in1 - digital mic daughhterboard
// in2 - headset mic
// in3 - modem output (muxed with mono)
@@ -1150,6 +1165,10 @@ flash_pins: flash-pins {
};
modem {
+ ri_pin: ri-pin {
+ rockchip,pins = <0 RK_PA1 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+
vcc_4g_5v_en: vcc-4g-5v-en-pin {
rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
};
--
2.34.1

View File

@ -0,0 +1,86 @@
From 60d8aedea7c8c390ee744730ab3e565ea84496fb Mon Sep 17 00:00:00 2001
From: Danct12 <danct12@disroot.org>
Date: Fri, 10 Dec 2021 23:01:34 +0700
Subject: [PATCH] arm64: dts: rk3399-pinephone-pro: Remove modem node
Since we don't use modem-power driver, this can be removed
for eg25-manager.
---
.../dts/rockchip/rk3399-pinephone-pro.dts | 40 +------------------
1 file changed, 2 insertions(+), 38 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
index 61c990764..13141c643 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
@@ -326,6 +326,7 @@ vcc_4g_5v: vcc-4g-5v {
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
vin-supply = <&vcc5v0_sys>;
+ regulator-always-on;
};
vcc_4g: vcc-4g {
@@ -338,6 +339,7 @@ vcc_4g: vcc-4g {
regulator-min-microvolt = <3800000>;
regulator-max-microvolt = <3800000>;
vin-supply = <&vcc_sysin>;
+ regulator-always-on;
};
vcc1v8_codec: vcc1v8-codec-regulator {
@@ -1058,31 +1060,6 @@ mipi_in_panel: endpoint {
&uart3 {
status = "okay";
-
- modem {
- compatible = "quectel,eg25";
- char-device-name = "modem-power";
-
- pinctrl-names = "default";
- pinctrl-0 = <&modem_control_pins>;
-
- power-supply = <&vcc_4g>;
- vbus-supply = <&vcc_4g_5v>;
-
- enable-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>; // W_DISABLE#
- reset-gpios = <&gpio3 RK_PB0 GPIO_ACTIVE_HIGH>;
- status-gpios = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;
- pwrkey-gpios = <&gpio0 RK_PB5 GPIO_ACTIVE_HIGH>;
-
- host-ready-gpios = <&gpio0 RK_PB4 GPIO_ACTIVE_HIGH>; // apready
- wakeup-gpios = <&gpio0 RK_PA1 GPIO_ACTIVE_HIGH>; // ri
-
- dtr-gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_HIGH>;
- cts-gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>;
- rts-gpios = <&gpio3 RK_PC1 GPIO_ACTIVE_HIGH>;
-
- quectel,qdai = "3,0,0,4,0,0,1,1";
- };
};
&pmu_io_domains {
@@ -1153,19 +1130,6 @@ vcc_4g_5v_en: vcc-4g-5v-en-pin {
vcc_4g_en: vcc-4g-en-pin {
rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
};
-
- modem_control_pins: modem-control-pins {
- rockchip,pins =
- <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>,
- <3 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>,
- <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>,
- <0 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>,
- <0 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>,
- <0 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>,
- <0 RK_PA3 RK_FUNC_GPIO &pcfg_pull_none>,
- <3 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>,
- <3 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>;
- };
};
pmic {
--
2.34.1

View File

@ -0,0 +1,150 @@
--- b/drivers/video/fbdev/core/bitblit.c
+++ a/drivers/video/fbdev/core/bitblit.c
@@ -234,7 +234,7 @@
}
static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode,
+ int softback_lines, int fg, int bg)
- int fg, int bg)
{
struct fb_cursor cursor;
struct fbcon_ops *ops = info->fbcon_par;
@@ -247,6 +247,15 @@
cursor.set = 0;
+ if (softback_lines) {
+ if (y + softback_lines >= vc->vc_rows) {
+ mode = CM_ERASE;
+ ops->cursor_flash = 0;
+ return;
+ } else
+ y += softback_lines;
+ }
+
c = scr_readw((u16 *) vc->vc_pos);
attribute = get_attribute(info, c);
src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height));
--- b/drivers/video/fbdev/core/fbcon.c
+++ a/drivers/video/fbdev/core/fbcon.c
@@ -394,7 +394,7 @@
c = scr_readw((u16 *) vc->vc_pos);
mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
CM_ERASE : CM_DRAW;
+ ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1),
- ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
get_color(vc, info, c, 0));
console_unlock();
}
@@ -1345,7 +1345,7 @@
ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
+ ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1),
- ops->cursor(vc, info, mode, get_color(vc, info, c, 1),
get_color(vc, info, c, 0));
}
--- b/drivers/video/fbdev/core/fbcon.h
+++ a/drivers/video/fbdev/core/fbcon.h
@@ -62,7 +62,7 @@
void (*clear_margins)(struct vc_data *vc, struct fb_info *info,
int color, int bottom_only);
void (*cursor)(struct vc_data *vc, struct fb_info *info, int mode,
+ int softback_lines, int fg, int bg);
- int fg, int bg);
int (*update_start)(struct fb_info *info);
int (*rotate_font)(struct fb_info *info, struct vc_data *vc);
struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */
--- b/drivers/video/fbdev/core/fbcon_ccw.c
+++ a/drivers/video/fbdev/core/fbcon_ccw.c
@@ -219,7 +219,7 @@
}
static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode,
+ int softback_lines, int fg, int bg)
- int fg, int bg)
{
struct fb_cursor cursor;
struct fbcon_ops *ops = info->fbcon_par;
@@ -236,6 +236,15 @@
cursor.set = 0;
+ if (softback_lines) {
+ if (y + softback_lines >= vc->vc_rows) {
+ mode = CM_ERASE;
+ ops->cursor_flash = 0;
+ return;
+ } else
+ y += softback_lines;
+ }
+
c = scr_readw((u16 *) vc->vc_pos);
attribute = get_attribute(info, c);
src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
--- b/drivers/video/fbdev/core/fbcon_cw.c
+++ a/drivers/video/fbdev/core/fbcon_cw.c
@@ -202,7 +202,7 @@
}
static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode,
+ int softback_lines, int fg, int bg)
- int fg, int bg)
{
struct fb_cursor cursor;
struct fbcon_ops *ops = info->fbcon_par;
@@ -219,6 +219,15 @@
cursor.set = 0;
+ if (softback_lines) {
+ if (y + softback_lines >= vc->vc_rows) {
+ mode = CM_ERASE;
+ ops->cursor_flash = 0;
+ return;
+ } else
+ y += softback_lines;
+ }
+
c = scr_readw((u16 *) vc->vc_pos);
attribute = get_attribute(info, c);
src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width));
--- b/drivers/video/fbdev/core/fbcon_ud.c
+++ a/drivers/video/fbdev/core/fbcon_ud.c
@@ -249,7 +249,7 @@
}
static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode,
+ int softback_lines, int fg, int bg)
- int fg, int bg)
{
struct fb_cursor cursor;
struct fbcon_ops *ops = info->fbcon_par;
@@ -267,6 +267,15 @@
cursor.set = 0;
+ if (softback_lines) {
+ if (y + softback_lines >= vc->vc_rows) {
+ mode = CM_ERASE;
+ ops->cursor_flash = 0;
+ return;
+ } else
+ y += softback_lines;
+ }
+
c = scr_readw((u16 *) vc->vc_pos);
attribute = get_attribute(info, c);
src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height));
--- b/drivers/video/fbdev/core/tileblit.c
+++ a/drivers/video/fbdev/core/tileblit.c
@@ -80,7 +80,7 @@
}
static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode,
+ int softback_lines, int fg, int bg)
- int fg, int bg)
{
struct fb_tilecursor cursor;
int use_sw = (vc->vc_cursor_type & 0x10);

View File

@ -0,0 +1,500 @@
--- b/drivers/video/fbdev/core/fbcon.c
+++ a/drivers/video/fbdev/core/fbcon.c
@@ -124,6 +124,12 @@ static int logo_lines;
/* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO
enums. */
static int logo_shown = FBCON_LOGO_CANSHOW;
+/* Software scrollback */
+static int fbcon_softback_size = 32768;
+static unsigned long softback_buf, softback_curr;
+static unsigned long softback_in;
+static unsigned long softback_top, softback_end;
+static int softback_lines;
/* console mappings */
static unsigned int first_fb_vc;
static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;
@@ -163,6 +169,8 @@ static int margin_color;
static const struct consw fb_con;
+#define CM_SOFTBACK (8)
+
#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
static int fbcon_set_origin(struct vc_data *);
@@ -347,6 +355,18 @@ static int get_color(struct vc_data *vc,
return color;
}
+static void fbcon_update_softback(struct vc_data *vc)
+{
+ int l = fbcon_softback_size / vc->vc_size_row;
+
+ if (l > 5)
+ softback_end = softback_buf + l * vc->vc_size_row;
+ else
+ /* Smaller scrollback makes no sense, and 0 would screw
+ the operation totally */
+ softback_top = 0;
+}
+
static void fb_flashcursor(struct work_struct *work)
{
struct fbcon_ops *ops = container_of(work, struct fbcon_ops, cursor_work.work);
@@ -379,7 +399,7 @@ static void fb_flashcursor(struct work_s
c = scr_readw((u16 *) vc->vc_pos);
mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
CM_ERASE : CM_DRAW;
- ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1),
+ ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1),
get_color(vc, info, c, 0));
console_unlock();
@@ -419,7 +439,13 @@ static int __init fb_console_setup(char
}
if (!strncmp(options, "scrollback:", 11)) {
- pr_warn("Ignoring scrollback size option\n");
+ options += 11;
+ if (*options) {
+ fbcon_softback_size = simple_strtoul(options, &options, 0);
+ if (*options == 'k' || *options == 'K') {
+ fbcon_softback_size *= 1024;
+ }
+ }
continue;
}
@@ -959,6 +985,31 @@ static const char *fbcon_startup(void)
set_blitting_type(vc, info);
+ if (info->fix.type != FB_TYPE_TEXT) {
+ if (fbcon_softback_size) {
+ if (!softback_buf) {
+ softback_buf =
+ (unsigned long)
+ kvmalloc(fbcon_softback_size,
+ GFP_KERNEL);
+ if (!softback_buf) {
+ fbcon_softback_size = 0;
+ softback_top = 0;
+ }
+ }
+ } else {
+ if (softback_buf) {
+ kvfree((void *) softback_buf);
+ softback_buf = 0;
+ softback_top = 0;
+ }
+ }
+ if (softback_buf)
+ softback_in = softback_top = softback_curr =
+ softback_buf;
+ softback_lines = 0;
+ }
+
/* Setup default font */
if (!p->fontdata && !vc->vc_font.data) {
if (!fontname[0] || !(font = find_font(fontname)))
@@ -1129,6 +1180,9 @@ static void fbcon_init(struct vc_data *v
if (logo)
fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows);
+ if (vc == svc && softback_buf)
+ fbcon_update_softback(vc);
+
if (ops->rotate_font && ops->rotate_font(info, vc)) {
ops->rotate = FB_ROTATE_UR;
set_blitting_type(vc, info);
@@ -1152,6 +1206,9 @@ static void fbcon_release_all(void)
struct fb_info *info;
int i, j, mapped;
+ kvfree((void *)softback_buf);
+ softback_buf = 0UL;
+
fbcon_for_each_registered_fb(i) {
mapped = 0;
info = fbcon_registered_fb[i];
@@ -1312,6 +1369,7 @@ static void fbcon_cursor(struct vc_data
{
struct fb_info *info = fbcon_info_from_console(vc->vc_num);
struct fbcon_ops *ops = info->fbcon_par;
+ int y;
int c = scr_readw((u16 *) vc->vc_pos);
ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms);
@@ -1325,11 +1383,19 @@ static void fbcon_cursor(struct vc_data
fbcon_add_cursor_work(info);
ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1;
+ if (mode & CM_SOFTBACK) {
+ mode &= ~CM_SOFTBACK;
+ y = softback_lines;
+ } else {
+ if (softback_lines)
+ fbcon_set_origin(vc);
+ y = 0;
+ }
if (!ops->cursor)
return;
- ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1),
+ ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1),
get_color(vc, info, c, 0));
}
@@ -1399,6 +1465,8 @@ static void fbcon_set_disp(struct fb_inf
if (con_is_visible(vc)) {
update_screen(vc);
+ if (softback_buf)
+ fbcon_update_softback(vc);
}
}
@@ -1536,6 +1604,99 @@ static __inline__ void ypan_down_redraw(
scrollback_current = 0;
}
+static void fbcon_redraw_softback(struct vc_data *vc, struct fbcon_display *p,
+ long delta)
+{
+ int count = vc->vc_rows;
+ unsigned short *d, *s;
+ unsigned long n;
+ int line = 0;
+
+ d = (u16 *) softback_curr;
+ if (d == (u16 *) softback_in)
+ d = (u16 *) vc->vc_origin;
+ n = softback_curr + delta * vc->vc_size_row;
+ softback_lines -= delta;
+ if (delta < 0) {
+ if (softback_curr < softback_top && n < softback_buf) {
+ n += softback_end - softback_buf;
+ if (n < softback_top) {
+ softback_lines -=
+ (softback_top - n) / vc->vc_size_row;
+ n = softback_top;
+ }
+ } else if (softback_curr >= softback_top
+ && n < softback_top) {
+ softback_lines -=
+ (softback_top - n) / vc->vc_size_row;
+ n = softback_top;
+ }
+ } else {
+ if (softback_curr > softback_in && n >= softback_end) {
+ n += softback_buf - softback_end;
+ if (n > softback_in) {
+ n = softback_in;
+ softback_lines = 0;
+ }
+ } else if (softback_curr <= softback_in && n > softback_in) {
+ n = softback_in;
+ softback_lines = 0;
+ }
+ }
+ if (n == softback_curr)
+ return;
+ softback_curr = n;
+ s = (u16 *) softback_curr;
+ if (s == (u16 *) softback_in)
+ s = (u16 *) vc->vc_origin;
+ while (count--) {
+ unsigned short *start;
+ unsigned short *le;
+ unsigned short c;
+ int x = 0;
+ unsigned short attr = 1;
+
+ start = s;
+ le = advance_row(s, 1);
+ do {
+ c = scr_readw(s);
+ if (attr != (c & 0xff00)) {
+ attr = c & 0xff00;
+ if (s > start) {
+ fbcon_putcs(vc, start, s - start,
+ line, x);
+ x += s - start;
+ start = s;
+ }
+ }
+ if (c == scr_readw(d)) {
+ if (s > start) {
+ fbcon_putcs(vc, start, s - start,
+ line, x);
+ x += s - start + 1;
+ start = s + 1;
+ } else {
+ x++;
+ start++;
+ }
+ }
+ s++;
+ d++;
+ } while (s < le);
+ if (s > start)
+ fbcon_putcs(vc, start, s - start, line, x);
+ line++;
+ if (d == (u16 *) softback_end)
+ d = (u16 *) softback_buf;
+ if (d == (u16 *) softback_in)
+ d = (u16 *) vc->vc_origin;
+ if (s == (u16 *) softback_end)
+ s = (u16 *) softback_buf;
+ if (s == (u16 *) softback_in)
+ s = (u16 *) vc->vc_origin;
+ }
+}
+
static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p,
int line, int count, int dy)
{
@@ -1740,6 +1901,31 @@ static void fbcon_bmove(struct vc_data *
p->vrows - p->yscroll);
}
+static inline void fbcon_softback_note(struct vc_data *vc, int t,
+ int count)
+{
+ unsigned short *p;
+
+ if (vc->vc_num != fg_console)
+ return;
+ p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
+
+ while (count) {
+ scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row);
+ count--;
+ p = advance_row(p, 1);
+ softback_in += vc->vc_size_row;
+ if (softback_in == softback_end)
+ softback_in = softback_buf;
+ if (softback_in == softback_top) {
+ softback_top += vc->vc_size_row;
+ if (softback_top == softback_end)
+ softback_top = softback_buf;
+ }
+ }
+ softback_curr = softback_in;
+}
+
static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
enum con_scroll dir, unsigned int count)
{
@@ -1762,6 +1948,8 @@ static bool fbcon_scroll(struct vc_data
case SM_UP:
if (count > vc->vc_rows) /* Maximum realistic size */
count = vc->vc_rows;
+ if (softback_top)
+ fbcon_softback_note(vc, t, count);
switch (fb_scrollmode(p)) {
case SCROLL_MOVE:
fbcon_redraw_blit(vc, info, p, t, b - t - count,
@@ -2076,6 +2264,14 @@ static int fbcon_switch(struct vc_data *
info = fbcon_info_from_console(vc->vc_num);
ops = info->fbcon_par;
+ if (softback_top) {
+ if (softback_lines)
+ fbcon_set_origin(vc);
+ softback_top = softback_curr = softback_in = softback_buf;
+ softback_lines = 0;
+ fbcon_update_softback(vc);
+ }
+
if (logo_shown >= 0) {
struct vc_data *conp2 = vc_cons[logo_shown].d;
@@ -2406,6 +2602,9 @@ static int fbcon_do_set_font(struct vc_d
int resize;
char *old_data = NULL;
+ if (con_is_visible(vc) && softback_lines)
+ fbcon_set_origin(vc);
+
resize = (w != vc->vc_font.width) || (h != vc->vc_font.height);
if (p->userfont)
old_data = vc->vc_font.data;
@@ -2428,6 +2627,8 @@ static int fbcon_do_set_font(struct vc_d
cols /= w;
rows /= h;
vc_resize(vc, cols, rows);
+ if (con_is_visible(vc) && softback_buf)
+ fbcon_update_softback(vc);
} else if (con_is_visible(vc)
&& vc->vc_mode == KD_TEXT) {
fbcon_clear_margins(vc, 0);
@@ -2582,7 +2783,19 @@ static void fbcon_set_palette(struct vc_
static u16 *fbcon_screen_pos(const struct vc_data *vc, int offset)
{
- return (u16 *) (vc->vc_origin + offset);
+ unsigned long p;
+ int line;
+
+ if (vc->vc_num != fg_console || !softback_lines)
+ return (u16 *) (vc->vc_origin + offset);
+ line = offset / vc->vc_size_row;
+ if (line >= softback_lines)
+ return (u16 *) (vc->vc_origin + offset -
+ softback_lines * vc->vc_size_row);
+ p = softback_curr + offset;
+ if (p >= softback_end)
+ p += softback_buf - softback_end;
+ return (u16 *) p;
}
static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos,
@@ -2596,7 +2809,22 @@ static unsigned long fbcon_getxy(struct
x = offset % vc->vc_cols;
y = offset / vc->vc_cols;
+ if (vc->vc_num == fg_console)
+ y += softback_lines;
+ ret = pos + (vc->vc_cols - x) * 2;
+ } else if (vc->vc_num == fg_console && softback_lines) {
+ unsigned long offset = pos - softback_curr;
+
+ if (pos < softback_curr)
+ offset += softback_end - softback_buf;
+ offset /= 2;
+ x = offset % vc->vc_cols;
+ y = offset / vc->vc_cols;
ret = pos + (vc->vc_cols - x) * 2;
+ if (ret == softback_end)
+ ret = softback_buf;
+ if (ret == softback_in)
+ ret = vc->vc_origin;
} else {
/* Should not happen */
x = y = 0;
@@ -2624,11 +2852,106 @@ static void fbcon_invert_region(struct v
a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) |
(((a) & 0x0700) << 4);
scr_writew(a, p++);
+ if (p == (u16 *) softback_end)
+ p = (u16 *) softback_buf;
+ if (p == (u16 *) softback_in)
+ p = (u16 *) vc->vc_origin;
+ }
+}
+
+static void fbcon_scrolldelta(struct vc_data *vc, int lines)
+{
+ struct fb_info *info = registered_fb[con2fb_map[fg_console]];
+ struct fbcon_ops *ops = info->fbcon_par;
+ struct fbcon_display *disp = &fb_display[fg_console];
+ int offset, limit, scrollback_old;
+
+ if (softback_top) {
+ if (vc->vc_num != fg_console)
+ return;
+ if (vc->vc_mode != KD_TEXT || !lines)
+ return;
+ if (logo_shown >= 0) {
+ struct vc_data *conp2 = vc_cons[logo_shown].d;
+
+ if (conp2->vc_top == logo_lines
+ && conp2->vc_bottom == conp2->vc_rows)
+ conp2->vc_top = 0;
+ if (logo_shown == vc->vc_num) {
+ unsigned long p, q;
+ int i;
+
+ p = softback_in;
+ q = vc->vc_origin +
+ logo_lines * vc->vc_size_row;
+ for (i = 0; i < logo_lines; i++) {
+ if (p == softback_top)
+ break;
+ if (p == softback_buf)
+ p = softback_end;
+ p -= vc->vc_size_row;
+ q -= vc->vc_size_row;
+ scr_memcpyw((u16 *) q, (u16 *) p,
+ vc->vc_size_row);
+ }
+ softback_in = softback_curr = p;
+ update_region(vc, vc->vc_origin,
+ logo_lines * vc->vc_cols);
+ }
+ logo_shown = FBCON_LOGO_CANSHOW;
+ }
+ fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK);
+ fbcon_redraw_softback(vc, disp, lines);
+ fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK);
+ return;
}
+
+ if (!scrollback_phys_max)
+ return;
+
+ scrollback_old = scrollback_current;
+ scrollback_current -= lines;
+ if (scrollback_current < 0)
+ scrollback_current = 0;
+ else if (scrollback_current > scrollback_max)
+ scrollback_current = scrollback_max;
+ if (scrollback_current == scrollback_old)
+ return;
+
+ if (fbcon_is_inactive(vc, info))
+ return;
+
+ fbcon_cursor(vc, CM_ERASE);
+
+ offset = disp->yscroll - scrollback_current;
+ limit = disp->vrows;
+ switch (disp->scrollmode) {
+ case SCROLL_WRAP_MOVE:
+ info->var.vmode |= FB_VMODE_YWRAP;
+ break;
+ case SCROLL_PAN_MOVE:
+ case SCROLL_PAN_REDRAW:
+ limit -= vc->vc_rows;
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+ break;
+ }
+ if (offset < 0)
+ offset += limit;
+ else if (offset >= limit)
+ offset -= limit;
+
+ ops->var.xoffset = 0;
+ ops->var.yoffset = offset * vc->vc_font.height;
+ ops->update_start(info);
+
+ if (!scrollback_current)
+ fbcon_cursor(vc, CM_DRAW);
}
static int fbcon_set_origin(struct vc_data *vc)
{
+ if (softback_lines)
+ fbcon_scrolldelta(vc, softback_lines);
return 0;
}
@@ -2692,6 +3015,8 @@ static void fbcon_modechanged(struct fb_
fbcon_set_palette(vc, color_table);
update_screen(vc);
+ if (softback_buf)
+ fbcon_update_softback(vc);
}
}
@@ -3154,6 +3479,7 @@ static const struct consw fb_con = {
.con_font_get = fbcon_get_font,
.con_font_default = fbcon_set_def_font,
.con_set_palette = fbcon_set_palette,
+ .con_scrolldelta = fbcon_scrolldelta,
.con_set_origin = fbcon_set_origin,
.con_invert_region = fbcon_invert_region,
.con_screen_pos = fbcon_screen_pos,