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