[PATCH] savagefb: Driver updates

- Fallback to firmware EDID if chipset has no DDC/I2C support or if I2C
  probing failed

- Add fb_blank hook

- Fix savagefb_suspend/resume to enable driver to successfully suspend and
  resume from S3, memory or disk

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/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c
index 117ad42..abad90a 100644
--- a/drivers/video/savage/savagefb_driver.c
+++ b/drivers/video/savage/savagefb_driver.c
@@ -1400,6 +1400,58 @@
 	return 0;
 }
 
+static int savagefb_blank(int blank, struct fb_info *info)
+{
+	struct savagefb_par *par = info->par;
+	u8 sr8 = 0, srd = 0;
+
+	if (par->display_type == DISP_CRT) {
+		vga_out8(0x3c4, 0x08);
+		sr8 = vga_in8(0x3c5);
+		sr8 |= 0x06;
+		vga_out8(0x3c5, sr8);
+		vga_out8(0x3c4, 0x0d);
+		srd = vga_in8(0x3c5);
+		srd &= 0x03;
+
+		switch (blank) {
+		case FB_BLANK_UNBLANK:
+		case FB_BLANK_NORMAL:
+			break;
+		case FB_BLANK_VSYNC_SUSPEND:
+			srd |= 0x10;
+			break;
+		case FB_BLANK_HSYNC_SUSPEND:
+			srd |= 0x40;
+			break;
+		case FB_BLANK_POWERDOWN:
+			srd |= 0x50;
+			break;
+		}
+
+		vga_out8(0x3c4, 0x0d);
+		vga_out8(0x3c5, srd);
+	}
+
+	if (par->display_type == DISP_LCD ||
+	    par->display_type == DISP_DFP) {
+		switch(blank) {
+		case FB_BLANK_UNBLANK:
+		case FB_BLANK_NORMAL:
+			vga_out8(0x3c4, 0x31); /* SR31 bit 4 - FP enable */
+			vga_out8(0x3c5, vga_in8(0x3c5) | 0x10);
+			break;
+		case FB_BLANK_VSYNC_SUSPEND:
+		case FB_BLANK_HSYNC_SUSPEND:
+		case FB_BLANK_POWERDOWN:
+			vga_out8(0x3c4, 0x31); /* SR31 bit 4 - FP enable */
+			vga_out8(0x3c5, vga_in8(0x3c5) & ~0x10);
+			break;
+		}
+	}
+
+	return (blank == FB_BLANK_NORMAL) ? 1 : 0;
+}
 
 static struct fb_ops savagefb_ops = {
 	.owner          = THIS_MODULE,
@@ -1407,6 +1459,7 @@
 	.fb_set_par     = savagefb_set_par,
 	.fb_setcolreg   = savagefb_setcolreg,
 	.fb_pan_display = savagefb_pan_display,
+	.fb_blank       = savagefb_blank,
 #if defined(CONFIG_FB_SAVAGE_ACCEL)
 	.fb_fillrect    = savagefb_fillrect,
 	.fb_copyarea    = savagefb_copyarea,
@@ -1583,8 +1636,7 @@
 	static unsigned char RamSavage4[] =  { 2, 4, 8, 12, 16, 32, 64, 32 };
 	static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
 	static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
-
-	int videoRam, videoRambytes;
+	int videoRam, videoRambytes, dvi;
 
 	DBG("savage_init_hw");
 
@@ -1705,6 +1757,30 @@
 	printk (KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
 		par->MCLK);
 
+	/* check for DVI/flat panel */
+	dvi = 0;
+
+	if (par->chip == S3_SAVAGE4) {
+		unsigned char sr30 = 0x00;
+
+		vga_out8(0x3c4, 0x30);
+		/* clear bit 1 */
+		vga_out8(0x3c5, vga_in8(0x3c5) & ~0x02);
+		sr30 = vga_in8(0x3c5);
+		if (sr30 & 0x02 /*0x04 */) {
+			dvi = 1;
+			printk("savagefb: Digital Flat Panel Detected\n");
+		}
+	}
+
+	if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
+	    (S3_MOBILE_TWISTER_SERIES(par->chip) && !par->crtonly))
+		par->display_type = DISP_LCD;
+	else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi))
+		par->display_type = DISP_DFP;
+	else
+		par->display_type = DISP_CRT;
+
 	/* Check LCD panel parrmation */
 
 	if (par->chip == S3_SAVAGE_MX) {
@@ -1759,7 +1835,8 @@
 			par->SavagePanelWidth = panelX;
 			par->SavagePanelHeight = panelY;
 
-		}
+		} else
+			par->display_type = DISP_CRT;
 	}
 
 	savage_get_default_par (par);
@@ -1845,15 +1922,15 @@
 		snprintf (info->fix.id, 16, "ProSavageKM");
 		break;
 	case FB_ACCEL_S3TWISTER_P:
-		par->chip = S3_PROSAVAGE;
+		par->chip = S3_TWISTER;
 		snprintf (info->fix.id, 16, "TwisterP");
 		break;
 	case FB_ACCEL_S3TWISTER_K:
-		par->chip = S3_PROSAVAGE;
+		par->chip = S3_TWISTER;
 		snprintf (info->fix.id, 16, "TwisterK");
 		break;
 	case FB_ACCEL_PROSAVAGE_DDR:
-		par->chip = S3_PROSAVAGE;
+		par->chip = S3_PROSAVAGEDDR;
 		snprintf (info->fix.id, 16, "ProSavageDDR");
 		break;
 	case FB_ACCEL_PROSAVAGE_DDRK:
@@ -1959,7 +2036,8 @@
 	INIT_LIST_HEAD(&info->modelist);
 #if defined(CONFIG_FB_SAVAGE_I2C)
 	savagefb_create_i2c_busses(info);
-	savagefb_probe_i2c_connector(par, &par->edid);
+	savagefb_probe_i2c_connector(info, &par->edid);
+	kfree(par->edid);
 	fb_edid_to_monspecs(par->edid, &info->monspecs);
 	fb_videomode_to_modelist(info->monspecs.modedb,
 				 info->monspecs.modedb_len,
@@ -2111,13 +2189,30 @@
 
 	DBG("savagefb_suspend");
 
-	acquire_console_sem();
-	fb_set_suspend(info, pci_choose_state(dev, state));
-	savage_disable_mmio(par);
-	release_console_sem();
 
+	par->pm_state = state.event;
+
+	/*
+	 * For PM_EVENT_FREEZE, do not power down so the console
+	 * can remain active.
+	 */
+	if (state.event == PM_EVENT_FREEZE) {
+		dev->dev.power.power_state = state;
+		return 0;
+	}
+
+	acquire_console_sem();
+	fb_set_suspend(info, 1);
+
+	if (info->fbops->fb_sync)
+		info->fbops->fb_sync(info);
+
+	savagefb_blank(FB_BLANK_POWERDOWN, info);
+	savage_disable_mmio(par);
+	pci_save_state(dev);
 	pci_disable_device(dev);
 	pci_set_power_state(dev, pci_choose_state(dev, state));
+	release_console_sem();
 
 	return 0;
 }
@@ -2127,22 +2222,34 @@
 	struct fb_info *info =
 		(struct fb_info *)pci_get_drvdata(dev);
 	struct savagefb_par *par = (struct savagefb_par *)info->par;
+	int cur_state = par->pm_state;
 
 	DBG("savage_resume");
 
-	pci_set_power_state(dev, 0);
-	pci_restore_state(dev);
-	if(pci_enable_device(dev))
-		DBG("err");
+	par->pm_state = PM_EVENT_ON;
 
-	SavagePrintRegs();
+	/*
+	 * The adapter was not powered down coming back from a
+	 * PM_EVENT_FREEZE.
+	 */
+	if (cur_state == PM_EVENT_FREEZE) {
+		pci_set_power_state(dev, PCI_D0);
+		return 0;
+	}
 
 	acquire_console_sem();
 
+	pci_set_power_state(dev, PCI_D0);
+	pci_restore_state(dev);
+
+	if(pci_enable_device(dev))
+		DBG("err");
+
+	pci_set_master(dev);
 	savage_enable_mmio(par);
 	savage_init_hw(par);
 	savagefb_set_par (info);
-
+	savagefb_blank(FB_BLANK_UNBLANK, info);
 	fb_set_suspend (info, 0);
 	release_console_sem();