Commit 83d83beb authored by Hans de Goede's avatar Hans de Goede Committed by Bartlomiej Zolnierkiewicz

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: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
parent 3bd3a0e3
...@@ -155,6 +155,13 @@ C. Boot options ...@@ -155,6 +155,13 @@ C. Boot options
used by text. By default, this area will be black. The 'color' value 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. 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 C. Attaching, Detaching and Unloading
Before going on how to attach, detach and unload the framebuffer console, an Before going on how to attach, detach and unload the framebuffer console, an
......
...@@ -150,6 +150,17 @@ config FRAMEBUFFER_CONSOLE_ROTATION ...@@ -150,6 +150,17 @@ config FRAMEBUFFER_CONSOLE_ROTATION
such that other users of the framebuffer will remain normally such that other users of the framebuffer will remain normally
oriented. 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 config STI_CONSOLE
bool "STI text console" bool "STI text console"
depends on PARISC && HAS_IOMEM depends on PARISC && HAS_IOMEM
......
...@@ -26,6 +26,65 @@ ...@@ -26,6 +26,65 @@
#define DUMMY_ROWS CONFIG_DUMMY_CONSOLE_ROWS #define DUMMY_ROWS CONFIG_DUMMY_CONSOLE_ROWS
#endif #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) static const char *dummycon_startup(void)
{ {
return "dummy device"; return "dummy device";
...@@ -44,9 +103,6 @@ static void dummycon_init(struct vc_data *vc, int init) ...@@ -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_deinit(struct vc_data *vc) { }
static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height, static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height,
int width) { } 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 void dummycon_cursor(struct vc_data *vc, int mode) { }
static bool dummycon_scroll(struct vc_data *vc, unsigned int top, static bool dummycon_scroll(struct vc_data *vc, unsigned int top,
...@@ -61,11 +117,6 @@ static int dummycon_switch(struct vc_data *vc) ...@@ -61,11 +117,6 @@ static int dummycon_switch(struct vc_data *vc)
return 0; 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, static int dummycon_font_set(struct vc_data *vc, struct console_font *font,
unsigned int flags) unsigned int flags)
{ {
......
...@@ -129,6 +129,12 @@ static inline void fbcon_map_override(void) ...@@ -129,6 +129,12 @@ static inline void fbcon_map_override(void)
} }
#endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */ #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 */ /* font data */
static char fontname[40]; static char fontname[40];
...@@ -499,6 +505,12 @@ static int __init fb_console_setup(char *this_opt) ...@@ -499,6 +505,12 @@ static int __init fb_console_setup(char *this_opt)
margin_color = simple_strtoul(options, &options, 0); margin_color = simple_strtoul(options, &options, 0);
continue; continue;
} }
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
if (!strcmp(options, "nodefer")) {
deferred_takeover = false;
continue;
}
#endif
} }
return 1; return 1;
} }
...@@ -3100,6 +3112,9 @@ static int fbcon_fb_unregistered(struct fb_info *info) ...@@ -3100,6 +3112,9 @@ static int fbcon_fb_unregistered(struct fb_info *info)
WARN_CONSOLE_UNLOCKED(); WARN_CONSOLE_UNLOCKED();
if (deferred_takeover)
return 0;
idx = info->node; idx = info->node;
for (i = first_fb_vc; i <= last_fb_vc; i++) { for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map[i] == idx) if (con2fb_map[i] == idx)
...@@ -3140,6 +3155,13 @@ static void fbcon_remap_all(int idx) ...@@ -3140,6 +3155,13 @@ static void fbcon_remap_all(int idx)
WARN_CONSOLE_UNLOCKED(); 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++) for (i = first_fb_vc; i <= last_fb_vc; i++)
set_con2fb_map(i, idx, 0); set_con2fb_map(i, idx, 0);
...@@ -3191,6 +3213,11 @@ static int fbcon_fb_registered(struct fb_info *info) ...@@ -3191,6 +3213,11 @@ static int fbcon_fb_registered(struct fb_info *info)
idx = info->node; idx = info->node;
fbcon_select_primary(info); fbcon_select_primary(info);
if (deferred_takeover) {
pr_info("fbcon: Deferring console take-over\n");
return 0;
}
if (info_idx == -1) { if (info_idx == -1) {
for (i = first_fb_vc; i <= last_fb_vc; i++) { for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx) { if (con2fb_map_boot[i] == idx) {
...@@ -3566,8 +3593,46 @@ static int fbcon_init_device(void) ...@@ -3566,8 +3593,46 @@ static int fbcon_init_device(void)
return 0; 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) static void fbcon_start(void)
{ {
if (deferred_takeover) {
fbcon_register_output_notifier();
return;
}
if (num_registered_fb) { if (num_registered_fb) {
int i; int i;
...@@ -3594,6 +3659,13 @@ static void fbcon_exit(void) ...@@ -3594,6 +3659,13 @@ static void fbcon_exit(void)
if (fbcon_has_exited) if (fbcon_has_exited)
return; 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); kfree((void *)softback_buf);
softback_buf = 0UL; softback_buf = 0UL;
......
...@@ -21,6 +21,7 @@ struct console_font_op; ...@@ -21,6 +21,7 @@ struct console_font_op;
struct console_font; struct console_font;
struct module; struct module;
struct tty_struct; struct tty_struct;
struct notifier_block;
/* /*
* this is what the terminal answers to a ESC-Z or csi0c query. * 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; } ...@@ -220,4 +221,8 @@ static inline bool vgacon_text_force(void) { return false; }
extern void console_init(void); 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 */ #endif /* _LINUX_CONSOLE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment