[PATCH] atyfb: Get initial mode timings from LCD BIOS

Reported by: Jean-Philippe Guérard (Bugzilla Bug 1782)

"I've tried with video=atyfb:debug and video=atyfb:debug,mode:1280x600, \
nomtrr.

In both case, the screen stays black, but seems divided into 4 vertical bands.
 Some white lines pop up randomly on each vertical band."

The problem is a combination of an incorrect xclk plus lack of timing
information.  The adapter is attached to an LCD device that can do 1280x600
(which is not a standard resolution).  The global mode database does not have
an entry for it.  Fortunately, the Video BIOS contains the complete timing
info for this display, however, atyfb is not making use of it.

Add support to get the timing information from the BIOS, if available.

Signed-off-by: Antonino Daplas <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 5e4523a..08edbfc 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -2156,11 +2156,38 @@
 
 static struct fb_info *fb_list = NULL;
 
+#if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
+static int __devinit atyfb_get_timings_from_lcd(struct atyfb_par *par,
+						struct fb_var_screeninfo *var)
+{
+	int ret = -EINVAL;
+
+	if (par->lcd_table != 0 && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
+		*var = default_var;
+		var->xres = var->xres_virtual = par->lcd_hdisp;
+		var->right_margin = par->lcd_right_margin;
+		var->left_margin = par->lcd_hblank_len -
+			(par->lcd_right_margin + par->lcd_hsync_dly +
+			 par->lcd_hsync_len);
+		var->hsync_len = par->lcd_hsync_len + par->lcd_hsync_dly;
+		var->yres = var->yres_virtual = par->lcd_vdisp;
+		var->lower_margin = par->lcd_lower_margin;
+		var->upper_margin = par->lcd_vblank_len -
+			(par->lcd_lower_margin + par->lcd_vsync_len);
+		var->vsync_len = par->lcd_vsync_len;
+		var->pixclock = par->lcd_pixclock;
+		ret = 0;
+	}
+
+	return ret;
+}
+#endif /* defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) */
+
 static int __init aty_init(struct fb_info *info, const char *name)
 {
 	struct atyfb_par *par = (struct atyfb_par *) info->par;
 	const char *ramname = NULL, *xtal;
-	int gtb_memsize;
+	int gtb_memsize, has_var = 0;
 	struct fb_var_screeninfo var;
 	u8 pll_ref_div;
 	u32 i;
@@ -2468,8 +2495,8 @@
 		 *         applies to all Mac video cards
 		 */
 		if (mode) {
-			if (!mac_find_mode(&var, info, mode, 8))
-				var = default_var;
+			if (mac_find_mode(&var, info, mode, 8))
+				has_var = 1;
 		} else {
 			if (default_vmode == VMODE_CHOOSE) {
 				if (M64_HAS(G3_PB_1024x768))
@@ -2491,20 +2518,23 @@
 				default_vmode = VMODE_640_480_60;
 			if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
 				default_cmode = CMODE_8;
-			if (mac_vmode_to_var(default_vmode, default_cmode, &var))
-				var = default_var;
+			if (!mac_vmode_to_var(default_vmode, default_cmode,
+					       &var))
+				has_var = 1;
 		}
-	} else
+	}
+
 #endif /* !CONFIG_PPC */
-	if (
-#if defined(CONFIG_SPARC32) || defined(CONFIG_SPARC64)
-	   /* On Sparc, unless the user gave a specific mode
-	    * specification, use the PROM probed values in
-	    * default_var.
-	    */
-	    !mode ||
+
+#if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
+	if (!atyfb_get_timings_from_lcd(par, &var))
+		has_var = 1;
 #endif
-	    !fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8))
+
+	if (mode && fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8))
+		has_var = 1;
+
+	if (!has_var)
 		var = default_var;
 
 	if (noaccel)