323 lines
9.9 KiB
Diff
323 lines
9.9 KiB
Diff
|
From 83d83bebf40132e2d55ec58af666713cc76f9764 Mon Sep 17 00:00:00 2001
|
||
|
From: Hans de Goede <hdegoede@redhat.com>
|
||
|
Date: Thu, 28 Jun 2018 15:20:30 +0200
|
||
|
Subject: [PATCH 4/7] console/fbcon: Add support for deferred console takeover
|
||
|
|
||
|
Currently fbcon claims fbdevs as soon as they are registered and takes over
|
||
|
the console as soon as the first fbdev gets registered.
|
||
|
|
||
|
This behavior is undesirable in cases where a smooth graphical bootup is
|
||
|
desired, in such cases we typically want the contents of the framebuffer
|
||
|
(typically a vendor logo) to stay in place as is.
|
||
|
|
||
|
The current solution for this problem (on embedded systems) is to not
|
||
|
enable fbcon.
|
||
|
|
||
|
This commit adds a new FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER config option,
|
||
|
which when enabled defers fbcon taking over the console from the dummy
|
||
|
console until the first text is displayed on the console. Together with the
|
||
|
"quiet" kernel commandline option, this allows fbcon to still be used
|
||
|
together with a smooth graphical bootup, having it take over the console as
|
||
|
soon as e.g. an error message is logged.
|
||
|
|
||
|
Note the choice to detect the first console output in the dummycon driver,
|
||
|
rather then handling this entirely inside the fbcon code, was made after
|
||
|
2 failed attempts to handle this entirely inside the fbcon code. The fbcon
|
||
|
code is woven quite tightly into the console code, making this to only
|
||
|
feasible option.
|
||
|
|
||
|
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
|
||
|
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
||
|
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
|
||
|
---
|
||
|
Documentation/fb/fbcon.txt | 7 ++++
|
||
|
drivers/video/console/Kconfig | 11 +++++
|
||
|
drivers/video/console/dummycon.c | 67 +++++++++++++++++++++++++----
|
||
|
drivers/video/fbdev/core/fbcon.c | 72 ++++++++++++++++++++++++++++++++
|
||
|
include/linux/console.h | 5 +++
|
||
|
5 files changed, 154 insertions(+), 8 deletions(-)
|
||
|
|
||
|
diff --git a/Documentation/fb/fbcon.txt b/Documentation/fb/fbcon.txt
|
||
|
index 79c22d096bbc..d4d642e1ce9c 100644
|
||
|
--- a/Documentation/fb/fbcon.txt
|
||
|
+++ b/Documentation/fb/fbcon.txt
|
||
|
@@ -155,6 +155,13 @@ C. Boot options
|
||
|
used by text. By default, this area will be black. The 'color' value
|
||
|
is an integer number that depends on the framebuffer driver being used.
|
||
|
|
||
|
+6. fbcon=nodefer
|
||
|
+
|
||
|
+ If the kernel is compiled with deferred fbcon takeover support, normally
|
||
|
+ the framebuffer contents, left in place by the firmware/bootloader, will
|
||
|
+ be preserved until there actually is some text is output to the console.
|
||
|
+ This option causes fbcon to bind immediately to the fbdev device.
|
||
|
+
|
||
|
C. Attaching, Detaching and Unloading
|
||
|
|
||
|
Before going on how to attach, detach and unload the framebuffer console, an
|
||
|
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
|
||
|
index 4110ba7d7ca9..e91edef98633 100644
|
||
|
--- a/drivers/video/console/Kconfig
|
||
|
+++ b/drivers/video/console/Kconfig
|
||
|
@@ -150,6 +150,17 @@ config FRAMEBUFFER_CONSOLE_ROTATION
|
||
|
such that other users of the framebuffer will remain normally
|
||
|
oriented.
|
||
|
|
||
|
+config FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
|
||
|
+ bool "Framebuffer Console Deferred Takeover"
|
||
|
+ depends on FRAMEBUFFER_CONSOLE=y && DUMMY_CONSOLE=y
|
||
|
+ help
|
||
|
+ If enabled this defers the framebuffer console taking over the
|
||
|
+ console from the dummy console until the first text is displayed on
|
||
|
+ the console. This is useful in combination with the "quiet" kernel
|
||
|
+ commandline option to keep the framebuffer contents initially put up
|
||
|
+ by the firmware in place, rather then replacing the contents with a
|
||
|
+ black screen as soon as fbcon loads.
|
||
|
+
|
||
|
config STI_CONSOLE
|
||
|
bool "STI text console"
|
||
|
depends on PARISC && HAS_IOMEM
|
||
|
diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c
|
||
|
index f2eafe2ed980..45ad925ad5f8 100644
|
||
|
--- a/drivers/video/console/dummycon.c
|
||
|
+++ b/drivers/video/console/dummycon.c
|
||
|
@@ -26,6 +26,65 @@
|
||
|
#define DUMMY_ROWS CONFIG_DUMMY_CONSOLE_ROWS
|
||
|
#endif
|
||
|
|
||
|
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
|
||
|
+/* These are both protected by the console_lock */
|
||
|
+static RAW_NOTIFIER_HEAD(dummycon_output_nh);
|
||
|
+static bool dummycon_putc_called;
|
||
|
+
|
||
|
+void dummycon_register_output_notifier(struct notifier_block *nb)
|
||
|
+{
|
||
|
+ raw_notifier_chain_register(&dummycon_output_nh, nb);
|
||
|
+
|
||
|
+ if (dummycon_putc_called)
|
||
|
+ nb->notifier_call(nb, 0, NULL);
|
||
|
+}
|
||
|
+
|
||
|
+void dummycon_unregister_output_notifier(struct notifier_block *nb)
|
||
|
+{
|
||
|
+ raw_notifier_chain_unregister(&dummycon_output_nh, nb);
|
||
|
+}
|
||
|
+
|
||
|
+static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos)
|
||
|
+{
|
||
|
+ dummycon_putc_called = true;
|
||
|
+ raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
|
||
|
+}
|
||
|
+
|
||
|
+static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
|
||
|
+ int count, int ypos, int xpos)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (!dummycon_putc_called) {
|
||
|
+ /* Ignore erases */
|
||
|
+ for (i = 0 ; i < count; i++) {
|
||
|
+ if (s[i] != vc->vc_video_erase_char)
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ if (i == count)
|
||
|
+ return;
|
||
|
+
|
||
|
+ dummycon_putc_called = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
|
||
|
+}
|
||
|
+
|
||
|
+static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
|
||
|
+{
|
||
|
+ /* Redraw, so that we get putc(s) for output done while blanked */
|
||
|
+ return 1;
|
||
|
+}
|
||
|
+#else
|
||
|
+static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
|
||
|
+static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
|
||
|
+ int count, int ypos, int xpos) { }
|
||
|
+static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
|
||
|
+{
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
static const char *dummycon_startup(void)
|
||
|
{
|
||
|
return "dummy device";
|
||
|
@@ -44,9 +103,6 @@ static void dummycon_init(struct vc_data *vc, int init)
|
||
|
static void dummycon_deinit(struct vc_data *vc) { }
|
||
|
static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height,
|
||
|
int width) { }
|
||
|
-static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
|
||
|
-static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
|
||
|
- int count, int ypos, int xpos) { }
|
||
|
static void dummycon_cursor(struct vc_data *vc, int mode) { }
|
||
|
|
||
|
static bool dummycon_scroll(struct vc_data *vc, unsigned int top,
|
||
|
@@ -61,11 +117,6 @@ static int dummycon_switch(struct vc_data *vc)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
|
||
|
-{
|
||
|
- return 0;
|
||
|
-}
|
||
|
-
|
||
|
static int dummycon_font_set(struct vc_data *vc, struct console_font *font,
|
||
|
unsigned int flags)
|
||
|
{
|
||
|
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
|
||
|
index cd8d52a967aa..5fb156bdcf4e 100644
|
||
|
--- a/drivers/video/fbdev/core/fbcon.c
|
||
|
+++ b/drivers/video/fbdev/core/fbcon.c
|
||
|
@@ -129,6 +129,12 @@ static inline void fbcon_map_override(void)
|
||
|
}
|
||
|
#endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
|
||
|
|
||
|
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
|
||
|
+static bool deferred_takeover = true;
|
||
|
+#else
|
||
|
+#define deferred_takeover false
|
||
|
+#endif
|
||
|
+
|
||
|
/* font data */
|
||
|
static char fontname[40];
|
||
|
|
||
|
@@ -499,6 +505,12 @@ static int __init fb_console_setup(char *this_opt)
|
||
|
margin_color = simple_strtoul(options, &options, 0);
|
||
|
continue;
|
||
|
}
|
||
|
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
|
||
|
+ if (!strcmp(options, "nodefer")) {
|
||
|
+ deferred_takeover = false;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+#endif
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
@@ -3100,6 +3112,9 @@ static int fbcon_fb_unregistered(struct fb_info *info)
|
||
|
|
||
|
WARN_CONSOLE_UNLOCKED();
|
||
|
|
||
|
+ if (deferred_takeover)
|
||
|
+ return 0;
|
||
|
+
|
||
|
idx = info->node;
|
||
|
for (i = first_fb_vc; i <= last_fb_vc; i++) {
|
||
|
if (con2fb_map[i] == idx)
|
||
|
@@ -3140,6 +3155,13 @@ static void fbcon_remap_all(int idx)
|
||
|
|
||
|
WARN_CONSOLE_UNLOCKED();
|
||
|
|
||
|
+ if (deferred_takeover) {
|
||
|
+ for (i = first_fb_vc; i <= last_fb_vc; i++)
|
||
|
+ con2fb_map_boot[i] = idx;
|
||
|
+ fbcon_map_override();
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
for (i = first_fb_vc; i <= last_fb_vc; i++)
|
||
|
set_con2fb_map(i, idx, 0);
|
||
|
|
||
|
@@ -3191,6 +3213,11 @@ static int fbcon_fb_registered(struct fb_info *info)
|
||
|
idx = info->node;
|
||
|
fbcon_select_primary(info);
|
||
|
|
||
|
+ if (deferred_takeover) {
|
||
|
+ pr_info("fbcon: Deferring console take-over\n");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
if (info_idx == -1) {
|
||
|
for (i = first_fb_vc; i <= last_fb_vc; i++) {
|
||
|
if (con2fb_map_boot[i] == idx) {
|
||
|
@@ -3566,8 +3593,46 @@ static int fbcon_init_device(void)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
|
||
|
+static struct notifier_block fbcon_output_nb;
|
||
|
+
|
||
|
+static int fbcon_output_notifier(struct notifier_block *nb,
|
||
|
+ unsigned long action, void *data)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+
|
||
|
+ WARN_CONSOLE_UNLOCKED();
|
||
|
+
|
||
|
+ pr_info("fbcon: Taking over console\n");
|
||
|
+
|
||
|
+ dummycon_unregister_output_notifier(&fbcon_output_nb);
|
||
|
+ deferred_takeover = false;
|
||
|
+ logo_shown = FBCON_LOGO_DONTSHOW;
|
||
|
+
|
||
|
+ for (i = 0; i < FB_MAX; i++) {
|
||
|
+ if (registered_fb[i])
|
||
|
+ fbcon_fb_registered(registered_fb[i]);
|
||
|
+ }
|
||
|
+
|
||
|
+ return NOTIFY_OK;
|
||
|
+}
|
||
|
+
|
||
|
+static void fbcon_register_output_notifier(void)
|
||
|
+{
|
||
|
+ fbcon_output_nb.notifier_call = fbcon_output_notifier;
|
||
|
+ dummycon_register_output_notifier(&fbcon_output_nb);
|
||
|
+}
|
||
|
+#else
|
||
|
+static inline void fbcon_register_output_notifier(void) {}
|
||
|
+#endif
|
||
|
+
|
||
|
static void fbcon_start(void)
|
||
|
{
|
||
|
+ if (deferred_takeover) {
|
||
|
+ fbcon_register_output_notifier();
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
if (num_registered_fb) {
|
||
|
int i;
|
||
|
|
||
|
@@ -3594,6 +3659,13 @@ static void fbcon_exit(void)
|
||
|
if (fbcon_has_exited)
|
||
|
return;
|
||
|
|
||
|
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
|
||
|
+ if (deferred_takeover) {
|
||
|
+ dummycon_unregister_output_notifier(&fbcon_output_nb);
|
||
|
+ deferred_takeover = false;
|
||
|
+ }
|
||
|
+#endif
|
||
|
+
|
||
|
kfree((void *)softback_buf);
|
||
|
softback_buf = 0UL;
|
||
|
|
||
|
diff --git a/include/linux/console.h b/include/linux/console.h
|
||
|
index dfd6b0e97855..f59f3dbca65c 100644
|
||
|
--- a/include/linux/console.h
|
||
|
+++ b/include/linux/console.h
|
||
|
@@ -21,6 +21,7 @@ struct console_font_op;
|
||
|
struct console_font;
|
||
|
struct module;
|
||
|
struct tty_struct;
|
||
|
+struct notifier_block;
|
||
|
|
||
|
/*
|
||
|
* this is what the terminal answers to a ESC-Z or csi0c query.
|
||
|
@@ -220,4 +221,8 @@ static inline bool vgacon_text_force(void) { return false; }
|
||
|
|
||
|
extern void console_init(void);
|
||
|
|
||
|
+/* For deferred console takeover */
|
||
|
+void dummycon_register_output_notifier(struct notifier_block *nb);
|
||
|
+void dummycon_unregister_output_notifier(struct notifier_block *nb);
|
||
|
+
|
||
|
#endif /* _LINUX_CONSOLE_H */
|
||
|
--
|
||
|
2.18.0
|
||
|
|