From: "Antonino A. Daplas" <adaplas@hotpop.com>

Accessing the hardware before it is properly initialized can lead to crashes
or screen corruption.  This happens when switching to X then back to console. 
When console comes back from X, the device is in an undefined state.  During
this window, accessing the hardware is disallowed.

A new field in fbcon_par is added (graphics), which will be set to nonzero
just before initialization of the framebuffer and when coming back from
KD_GRAPHICS, then unset when an fb_set_var/fb_set_par is done.  While this
field is set, no accesses to the hardware is done.  The consequence of this
change is, hopefully, more robust switching between KD_GRAPHICS<-> KD_TEXT.

An added benefit coming from this change is that the MODESWITCHLATE hack is
not needed anymore and thus removed.  This hack is used by savagefb, rivafb
and nvidiafb.

Signed-off-by: Antonino Daplas <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/video/console/fbcon.c          |   41 +++++++++----------------
 25-akpm/drivers/video/console/fbcon.h          |    1 
 25-akpm/drivers/video/nvidia/nvidia.c          |    3 +
 25-akpm/drivers/video/riva/fbdev.c             |    3 -
 25-akpm/drivers/video/savage/savagefb_driver.c |    3 -
 25-akpm/include/linux/fb.h                     |    4 --
 6 files changed, 22 insertions(+), 33 deletions(-)

diff -puN drivers/video/console/fbcon.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized drivers/video/console/fbcon.c
--- 25/drivers/video/console/fbcon.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized	2005-03-13 13:32:40.000000000 -0800
+++ 25-akpm/drivers/video/console/fbcon.c	2005-03-13 13:32:40.000000000 -0800
@@ -204,8 +204,10 @@ static irqreturn_t fb_vbl_detect(int irq
 
 static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info)
 {
+	struct fbcon_ops *ops = info->fbcon_par;
+
 	return (info->state != FBINFO_STATE_RUNNING ||
-		vc->vc_mode != KD_TEXT);
+		vc->vc_mode != KD_TEXT || ops->graphics);
 }
 
 static inline int get_color(struct vc_data *vc, struct fb_info *info,
@@ -596,6 +598,8 @@ static void con2fb_init_display(struct v
 	if (info->fbops->fb_set_par)
 		info->fbops->fb_set_par(info);
 
+	ops->graphics = 0;
+
 	if (vc)
 		fbcon_set_disp(info, &info->var, vc);
 	else
@@ -763,6 +767,7 @@ static const char *fbcon_startup(void)
 
 	memset(ops, 0, sizeof(struct fbcon_ops));
 	ops->currcon = -1;
+	ops->graphics = 1;
 	info->fbcon_par = ops;
 	set_blitting_type(vc, info, NULL);
 
@@ -949,6 +954,7 @@ static void fbcon_init(struct vc_data *v
 	if (CON_IS_VISIBLE(vc) && info->fbops->fb_set_par)
 		info->fbops->fb_set_par(info);
 
+	((struct fbcon_ops *) info->fbcon_par)->graphics = 0;
 
 	if ((cap & FBINFO_HWACCEL_COPYAREA) &&
 	    !(cap & FBINFO_HWACCEL_DISABLED))
@@ -1871,7 +1877,6 @@ static int fbcon_resize(struct vc_data *
 			var.activate = FB_ACTIVATE_NOW |
 				FB_ACTIVATE_FORCE;
 			fb_set_var(info, &var);
-			info->flags &= ~FBINFO_MISC_MODESWITCH;
 		}
 		var_to_display(p, &info->var, info);
 	}
@@ -1884,7 +1889,7 @@ static int fbcon_switch(struct vc_data *
 	struct fb_info *info;
 	struct display *p = &fb_display[vc->vc_num];
 	struct fb_var_screeninfo var;
-	int i, prev_console, do_set_par = 0;
+	int i, prev_console;
 
 	info = registered_fb[con2fb_map[vc->vc_num]];
 
@@ -1943,14 +1948,9 @@ static int fbcon_switch(struct vc_data *
 	fb_set_var(info, &var);
 
 	if (prev_console != -1 &&
-	    registered_fb[con2fb_map[prev_console]] != info)
-		do_set_par = 1;
-
-	if (do_set_par || info->flags & FBINFO_MISC_MODESWITCH) {
-		if (info->fbops->fb_set_par)
-			info->fbops->fb_set_par(info);
-		info->flags &= ~FBINFO_MISC_MODESWITCH;
-	}
+	    registered_fb[con2fb_map[prev_console]] != info &&
+	    info->fbops->fb_set_par)
+		info->fbops->fb_set_par(info);
 
 	set_blitting_type(vc, info, p);
 	((struct fbcon_ops *)info->fbcon_par)->cursor_reset = 1;
@@ -2013,29 +2013,20 @@ static int fbcon_blank(struct vc_data *v
 {
 	struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]];
 	struct fbcon_ops *ops = info->fbcon_par;
-	int active = !fbcon_is_inactive(vc, info);
 
 	if (mode_switch) {
 		struct fb_var_screeninfo var = info->var;
-/*
- * HACK ALERT: Some hardware will require reinitializion at this stage,
- *             others will require it to be done as late as possible.
- *             For now, we differentiate this with the
- *             FBINFO_MISC_MODESWITCHLATE bitflag.  Worst case will be
- *             hardware that requires it here and another one later.
- *             A definitive solution may require fixing X or the VT
- *             system.
- */
-		if (info->flags & FBINFO_MISC_MODESWITCHLATE)
-			info->flags |= FBINFO_MISC_MODESWITCH;
 
-		if (!blank && !(info->flags & FBINFO_MISC_MODESWITCHLATE)) {
+		ops->graphics = 1;
+
+		if (!blank) {
 			var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
 			fb_set_var(info, &var);
+			ops->graphics = 0;
 		}
 	}
 
- 	if (active) {
+ 	if (!fbcon_is_inactive(vc, info)) {
 		if (ops->blank_state != blank) {
 			ops->blank_state = blank;
 			fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW);
diff -puN drivers/video/console/fbcon.h~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized drivers/video/console/fbcon.h
--- 25/drivers/video/console/fbcon.h~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized	2005-03-13 13:32:40.000000000 -0800
+++ 25-akpm/drivers/video/console/fbcon.h	2005-03-13 13:32:40.000000000 -0800
@@ -67,6 +67,7 @@ struct fbcon_ops {
 	int    cursor_flash;
 	int    cursor_reset;
 	int    blank_state;
+	int    graphics;
 	char  *cursor_data;
 };
     /*
diff -puN drivers/video/nvidia/nvidia.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized drivers/video/nvidia/nvidia.c
--- 25/drivers/video/nvidia/nvidia.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized	2005-03-13 13:32:40.000000000 -0800
+++ 25-akpm/drivers/video/nvidia/nvidia.c	2005-03-13 13:32:40.000000000 -0800
@@ -1191,6 +1191,7 @@ static int nvidiafb_check_var(struct fb_
 			var->yres_virtual = var->yres;
 			var->xres_virtual = vramlen / var->yres_virtual;
 			var->xres_virtual /= var->bits_per_pixel / 8;
+			var->xres_virtual &= ~63;
 			pitch = (var->xres_virtual *
 				 var->bits_per_pixel + 7) / 8;
 			memlen = pitch * var->yres;
@@ -1301,7 +1302,7 @@ static int __devinit nvidia_set_fbinfo(s
 	    | FBINFO_HWACCEL_IMAGEBLIT
 	    | FBINFO_HWACCEL_FILLRECT
 	    | FBINFO_HWACCEL_COPYAREA
-	    | FBINFO_HWACCEL_YPAN | FBINFO_MISC_MODESWITCHLATE;
+	    | FBINFO_HWACCEL_YPAN;
 
 	fb_videomode_to_modelist(info->monspecs.modedb,
 				 info->monspecs.modedb_len, &info->modelist);
diff -puN drivers/video/riva/fbdev.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized drivers/video/riva/fbdev.c
--- 25/drivers/video/riva/fbdev.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized	2005-03-13 13:32:40.000000000 -0800
+++ 25-akpm/drivers/video/riva/fbdev.c	2005-03-13 13:32:40.000000000 -0800
@@ -1708,8 +1708,7 @@ static int __devinit riva_set_fbinfo(str
 		    | FBINFO_HWACCEL_YPAN
 		    | FBINFO_HWACCEL_COPYAREA
 		    | FBINFO_HWACCEL_FILLRECT
-		    | FBINFO_HWACCEL_IMAGEBLIT
-	            | FBINFO_MISC_MODESWITCHLATE;
+	            | FBINFO_HWACCEL_IMAGEBLIT;
 
 	/* Accel seems to not work properly on NV30 yet...*/
 	if ((par->riva.Architecture == NV_ARCH_30) || noaccel) {
diff -puN drivers/video/savage/savagefb_driver.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized drivers/video/savage/savagefb_driver.c
--- 25/drivers/video/savage/savagefb_driver.c~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized	2005-03-13 13:32:40.000000000 -0800
+++ 25-akpm/drivers/video/savage/savagefb_driver.c	2005-03-13 13:32:40.000000000 -0800
@@ -1883,8 +1883,7 @@ static int __devinit savage_init_fb_info
 	info->fbops          = &savagefb_ops;
 	info->flags          = FBINFO_DEFAULT |
 		               FBINFO_HWACCEL_YPAN |
-		               FBINFO_HWACCEL_XPAN |
-	                       FBINFO_MISC_MODESWITCHLATE;
+		               FBINFO_HWACCEL_XPAN;
 
 	info->pseudo_palette = par->pseudo_palette;
 
diff -puN include/linux/fb.h~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized include/linux/fb.h
--- 25/include/linux/fb.h~fbcon-stop-framebuffer-operations-before-hardware-is-properly-initialized	2005-03-13 13:32:40.000000000 -0800
+++ 25-akpm/include/linux/fb.h	2005-03-13 13:32:40.000000000 -0800
@@ -702,9 +702,7 @@ struct fb_tile_ops {
 
 #define FBINFO_MISC_USEREVENT          0x10000 /* event request
 						  from userspace */
-#define FBINFO_MISC_MODESWITCH         0x20000 /* mode switch */
-#define FBINFO_MISC_MODESWITCHLATE     0x40000 /* init hardware later */
-#define FBINFO_MISC_TILEBLITTING       0x80000 /* use tile blitting */
+#define FBINFO_MISC_TILEBLITTING       0x20000 /* use tile blitting */
 
 struct fb_info {
 	int node;
_