Skip to content

Commit 83d83be

Browse files
jwrdegoedebzolnier
authored andcommitted
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>
1 parent 3bd3a0e commit 83d83be

File tree

5 files changed

+154
-8
lines changed

5 files changed

+154
-8
lines changed

Documentation/fb/fbcon.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ C. Boot options
155155
used by text. By default, this area will be black. The 'color' value
156156
is an integer number that depends on the framebuffer driver being used.
157157

158+
6. fbcon=nodefer
159+
160+
If the kernel is compiled with deferred fbcon takeover support, normally
161+
the framebuffer contents, left in place by the firmware/bootloader, will
162+
be preserved until there actually is some text is output to the console.
163+
This option causes fbcon to bind immediately to the fbdev device.
164+
158165
C. Attaching, Detaching and Unloading
159166

160167
Before going on how to attach, detach and unload the framebuffer console, an

drivers/video/console/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ config FRAMEBUFFER_CONSOLE_ROTATION
150150
such that other users of the framebuffer will remain normally
151151
oriented.
152152

153+
config FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
154+
bool "Framebuffer Console Deferred Takeover"
155+
depends on FRAMEBUFFER_CONSOLE=y && DUMMY_CONSOLE=y
156+
help
157+
If enabled this defers the framebuffer console taking over the
158+
console from the dummy console until the first text is displayed on
159+
the console. This is useful in combination with the "quiet" kernel
160+
commandline option to keep the framebuffer contents initially put up
161+
by the firmware in place, rather then replacing the contents with a
162+
black screen as soon as fbcon loads.
163+
153164
config STI_CONSOLE
154165
bool "STI text console"
155166
depends on PARISC && HAS_IOMEM

drivers/video/console/dummycon.c

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,65 @@
2626
#define DUMMY_ROWS CONFIG_DUMMY_CONSOLE_ROWS
2727
#endif
2828

29+
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
30+
/* These are both protected by the console_lock */
31+
static RAW_NOTIFIER_HEAD(dummycon_output_nh);
32+
static bool dummycon_putc_called;
33+
34+
void dummycon_register_output_notifier(struct notifier_block *nb)
35+
{
36+
raw_notifier_chain_register(&dummycon_output_nh, nb);
37+
38+
if (dummycon_putc_called)
39+
nb->notifier_call(nb, 0, NULL);
40+
}
41+
42+
void dummycon_unregister_output_notifier(struct notifier_block *nb)
43+
{
44+
raw_notifier_chain_unregister(&dummycon_output_nh, nb);
45+
}
46+
47+
static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos)
48+
{
49+
dummycon_putc_called = true;
50+
raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
51+
}
52+
53+
static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
54+
int count, int ypos, int xpos)
55+
{
56+
int i;
57+
58+
if (!dummycon_putc_called) {
59+
/* Ignore erases */
60+
for (i = 0 ; i < count; i++) {
61+
if (s[i] != vc->vc_video_erase_char)
62+
break;
63+
}
64+
if (i == count)
65+
return;
66+
67+
dummycon_putc_called = true;
68+
}
69+
70+
raw_notifier_call_chain(&dummycon_output_nh, 0, NULL);
71+
}
72+
73+
static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
74+
{
75+
/* Redraw, so that we get putc(s) for output done while blanked */
76+
return 1;
77+
}
78+
#else
79+
static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
80+
static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
81+
int count, int ypos, int xpos) { }
82+
static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
83+
{
84+
return 0;
85+
}
86+
#endif
87+
2988
static const char *dummycon_startup(void)
3089
{
3190
return "dummy device";
@@ -44,9 +103,6 @@ static void dummycon_init(struct vc_data *vc, int init)
44103
static void dummycon_deinit(struct vc_data *vc) { }
45104
static void dummycon_clear(struct vc_data *vc, int sy, int sx, int height,
46105
int width) { }
47-
static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { }
48-
static void dummycon_putcs(struct vc_data *vc, const unsigned short *s,
49-
int count, int ypos, int xpos) { }
50106
static void dummycon_cursor(struct vc_data *vc, int mode) { }
51107

52108
static bool dummycon_scroll(struct vc_data *vc, unsigned int top,
@@ -61,11 +117,6 @@ static int dummycon_switch(struct vc_data *vc)
61117
return 0;
62118
}
63119

64-
static int dummycon_blank(struct vc_data *vc, int blank, int mode_switch)
65-
{
66-
return 0;
67-
}
68-
69120
static int dummycon_font_set(struct vc_data *vc, struct console_font *font,
70121
unsigned int flags)
71122
{

drivers/video/fbdev/core/fbcon.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,12 @@ static inline void fbcon_map_override(void)
129129
}
130130
#endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */
131131

132+
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
133+
static bool deferred_takeover = true;
134+
#else
135+
#define deferred_takeover false
136+
#endif
137+
132138
/* font data */
133139
static char fontname[40];
134140

@@ -499,6 +505,12 @@ static int __init fb_console_setup(char *this_opt)
499505
margin_color = simple_strtoul(options, &options, 0);
500506
continue;
501507
}
508+
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
509+
if (!strcmp(options, "nodefer")) {
510+
deferred_takeover = false;
511+
continue;
512+
}
513+
#endif
502514
}
503515
return 1;
504516
}
@@ -3100,6 +3112,9 @@ static int fbcon_fb_unregistered(struct fb_info *info)
31003112

31013113
WARN_CONSOLE_UNLOCKED();
31023114

3115+
if (deferred_takeover)
3116+
return 0;
3117+
31033118
idx = info->node;
31043119
for (i = first_fb_vc; i <= last_fb_vc; i++) {
31053120
if (con2fb_map[i] == idx)
@@ -3140,6 +3155,13 @@ static void fbcon_remap_all(int idx)
31403155

31413156
WARN_CONSOLE_UNLOCKED();
31423157

3158+
if (deferred_takeover) {
3159+
for (i = first_fb_vc; i <= last_fb_vc; i++)
3160+
con2fb_map_boot[i] = idx;
3161+
fbcon_map_override();
3162+
return;
3163+
}
3164+
31433165
for (i = first_fb_vc; i <= last_fb_vc; i++)
31443166
set_con2fb_map(i, idx, 0);
31453167

@@ -3191,6 +3213,11 @@ static int fbcon_fb_registered(struct fb_info *info)
31913213
idx = info->node;
31923214
fbcon_select_primary(info);
31933215

3216+
if (deferred_takeover) {
3217+
pr_info("fbcon: Deferring console take-over\n");
3218+
return 0;
3219+
}
3220+
31943221
if (info_idx == -1) {
31953222
for (i = first_fb_vc; i <= last_fb_vc; i++) {
31963223
if (con2fb_map_boot[i] == idx) {
@@ -3566,8 +3593,46 @@ static int fbcon_init_device(void)
35663593
return 0;
35673594
}
35683595

3596+
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3597+
static struct notifier_block fbcon_output_nb;
3598+
3599+
static int fbcon_output_notifier(struct notifier_block *nb,
3600+
unsigned long action, void *data)
3601+
{
3602+
int i;
3603+
3604+
WARN_CONSOLE_UNLOCKED();
3605+
3606+
pr_info("fbcon: Taking over console\n");
3607+
3608+
dummycon_unregister_output_notifier(&fbcon_output_nb);
3609+
deferred_takeover = false;
3610+
logo_shown = FBCON_LOGO_DONTSHOW;
3611+
3612+
for (i = 0; i < FB_MAX; i++) {
3613+
if (registered_fb[i])
3614+
fbcon_fb_registered(registered_fb[i]);
3615+
}
3616+
3617+
return NOTIFY_OK;
3618+
}
3619+
3620+
static void fbcon_register_output_notifier(void)
3621+
{
3622+
fbcon_output_nb.notifier_call = fbcon_output_notifier;
3623+
dummycon_register_output_notifier(&fbcon_output_nb);
3624+
}
3625+
#else
3626+
static inline void fbcon_register_output_notifier(void) {}
3627+
#endif
3628+
35693629
static void fbcon_start(void)
35703630
{
3631+
if (deferred_takeover) {
3632+
fbcon_register_output_notifier();
3633+
return;
3634+
}
3635+
35713636
if (num_registered_fb) {
35723637
int i;
35733638

@@ -3594,6 +3659,13 @@ static void fbcon_exit(void)
35943659
if (fbcon_has_exited)
35953660
return;
35963661

3662+
#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER
3663+
if (deferred_takeover) {
3664+
dummycon_unregister_output_notifier(&fbcon_output_nb);
3665+
deferred_takeover = false;
3666+
}
3667+
#endif
3668+
35973669
kfree((void *)softback_buf);
35983670
softback_buf = 0UL;
35993671

include/linux/console.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ struct console_font_op;
2121
struct console_font;
2222
struct module;
2323
struct tty_struct;
24+
struct notifier_block;
2425

2526
/*
2627
* 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; }
220221

221222
extern void console_init(void);
222223

224+
/* For deferred console takeover */
225+
void dummycon_register_output_notifier(struct notifier_block *nb);
226+
void dummycon_unregister_output_notifier(struct notifier_block *nb);
227+
223228
#endif /* _LINUX_CONSOLE_H */

0 commit comments

Comments
 (0)