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
 
          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
 
 #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";
 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,
        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)
 {
 
 }
 #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];
 
                                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;
 }
 
        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)
 
        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);
 
        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) {
        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;
 
        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;
 
 
 struct console_font;
 struct module;
 struct tty_struct;
+struct notifier_block;
 
 /*
  * this is what the terminal answers to a ESC-Z or csi0c query.
 
 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 */