Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/video/aty/Makefile b/drivers/video/aty/Makefile
new file mode 100644
index 0000000..9dec962
--- /dev/null
+++ b/drivers/video/aty/Makefile
@@ -0,0 +1,15 @@
+obj-$(CONFIG_FB_ATY) += atyfb.o
+obj-$(CONFIG_FB_ATY128) += aty128fb.o
+obj-$(CONFIG_FB_RADEON) += radeonfb.o
+
+atyfb-y				:= atyfb_base.o mach64_accel.o mach64_cursor.o
+atyfb-$(CONFIG_FB_ATY_GX)	+= mach64_gx.o
+atyfb-$(CONFIG_FB_ATY_CT)	+= mach64_ct.o
+atyfb-$(CONFIG_FB_ATY_XL_INIT)  += xlinit.o
+
+atyfb-objs			:= $(atyfb-y)
+
+radeonfb-y			:= radeon_base.o radeon_pm.o radeon_monitor.o radeon_accel.o
+radeonfb-$(CONFIG_FB_RADEON_I2C)	+= radeon_i2c.o
+radeonfb-objs			:= $(radeonfb-y)
+
diff --git a/drivers/video/aty/ati_ids.h b/drivers/video/aty/ati_ids.h
new file mode 100644
index 0000000..13321c6
--- /dev/null
+++ b/drivers/video/aty/ati_ids.h
@@ -0,0 +1,211 @@
+/*
+ * ATI PCI IDs from XFree86, kept here to make sync'ing with
+ * XFree much simpler. Currently, this list is only used by
+ * radeonfb
+ */
+
+#define PCI_CHIP_RV380_3150             0x3150
+#define PCI_CHIP_RV380_3151             0x3151
+#define PCI_CHIP_RV380_3152             0x3152
+#define PCI_CHIP_RV380_3153             0x3153
+#define PCI_CHIP_RV380_3154             0x3154
+#define PCI_CHIP_RV380_3156             0x3156
+#define PCI_CHIP_RV380_3E50             0x3E50
+#define PCI_CHIP_RV380_3E51             0x3E51
+#define PCI_CHIP_RV380_3E52             0x3E52
+#define PCI_CHIP_RV380_3E53             0x3E53
+#define PCI_CHIP_RV380_3E54             0x3E54
+#define PCI_CHIP_RV380_3E56             0x3E56
+#define PCI_CHIP_RS100_4136		0x4136
+#define PCI_CHIP_RS200_4137		0x4137
+#define PCI_CHIP_R300_AD		0x4144
+#define PCI_CHIP_R300_AE		0x4145
+#define PCI_CHIP_R300_AF		0x4146
+#define PCI_CHIP_R300_AG		0x4147
+#define PCI_CHIP_R350_AH                0x4148
+#define PCI_CHIP_R350_AI                0x4149
+#define PCI_CHIP_R350_AJ                0x414A
+#define PCI_CHIP_R350_AK                0x414B
+#define PCI_CHIP_RV350_AP               0x4150
+#define PCI_CHIP_RV350_AQ               0x4151
+#define PCI_CHIP_RV360_AR               0x4152
+#define PCI_CHIP_RV350_AS               0x4153
+#define PCI_CHIP_RV350_AT               0x4154
+#define PCI_CHIP_RV350_AV               0x4156
+#define PCI_CHIP_MACH32			0x4158
+#define PCI_CHIP_RS250_4237		0x4237
+#define PCI_CHIP_R200_BB		0x4242
+#define PCI_CHIP_R200_BC		0x4243
+#define PCI_CHIP_RS100_4336		0x4336
+#define PCI_CHIP_RS200_4337		0x4337
+#define PCI_CHIP_MACH64CT		0x4354
+#define PCI_CHIP_MACH64CX		0x4358
+#define PCI_CHIP_RS250_4437		0x4437
+#define PCI_CHIP_MACH64ET		0x4554
+#define PCI_CHIP_MACH64GB		0x4742
+#define PCI_CHIP_MACH64GD		0x4744
+#define PCI_CHIP_MACH64GI		0x4749
+#define PCI_CHIP_MACH64GL		0x474C
+#define PCI_CHIP_MACH64GM		0x474D
+#define PCI_CHIP_MACH64GN		0x474E
+#define PCI_CHIP_MACH64GO		0x474F
+#define PCI_CHIP_MACH64GP		0x4750
+#define PCI_CHIP_MACH64GQ		0x4751
+#define PCI_CHIP_MACH64GR		0x4752
+#define PCI_CHIP_MACH64GS		0x4753
+#define PCI_CHIP_MACH64GT		0x4754
+#define PCI_CHIP_MACH64GU		0x4755
+#define PCI_CHIP_MACH64GV		0x4756
+#define PCI_CHIP_MACH64GW		0x4757
+#define PCI_CHIP_MACH64GX		0x4758
+#define PCI_CHIP_MACH64GY		0x4759
+#define PCI_CHIP_MACH64GZ		0x475A
+#define PCI_CHIP_RV250_Id		0x4964
+#define PCI_CHIP_RV250_Ie		0x4965
+#define PCI_CHIP_RV250_If		0x4966
+#define PCI_CHIP_RV250_Ig		0x4967
+#define PCI_CHIP_R420_JH                0x4A48
+#define PCI_CHIP_R420_JI                0x4A49
+#define PCI_CHIP_R420_JJ                0x4A4A
+#define PCI_CHIP_R420_JK                0x4A4B
+#define PCI_CHIP_R420_JL                0x4A4C
+#define PCI_CHIP_R420_JM                0x4A4D
+#define PCI_CHIP_R420_JN                0x4A4E
+#define PCI_CHIP_R420_JP                0x4A50
+#define PCI_CHIP_MACH64LB		0x4C42
+#define PCI_CHIP_MACH64LD		0x4C44
+#define PCI_CHIP_RAGE128LE		0x4C45
+#define PCI_CHIP_RAGE128LF		0x4C46
+#define PCI_CHIP_MACH64LG		0x4C47
+#define PCI_CHIP_MACH64LI		0x4C49
+#define PCI_CHIP_MACH64LM		0x4C4D
+#define PCI_CHIP_MACH64LN		0x4C4E
+#define PCI_CHIP_MACH64LP		0x4C50
+#define PCI_CHIP_MACH64LQ		0x4C51
+#define PCI_CHIP_MACH64LR		0x4C52
+#define PCI_CHIP_MACH64LS		0x4C53
+#define PCI_CHIP_MACH64LT		0x4C54
+#define PCI_CHIP_RADEON_LW		0x4C57
+#define PCI_CHIP_RADEON_LX		0x4C58
+#define PCI_CHIP_RADEON_LY		0x4C59
+#define PCI_CHIP_RADEON_LZ		0x4C5A
+#define PCI_CHIP_RV250_Ld		0x4C64
+#define PCI_CHIP_RV250_Le		0x4C65
+#define PCI_CHIP_RV250_Lf		0x4C66
+#define PCI_CHIP_RV250_Lg		0x4C67
+#define PCI_CHIP_RV250_Ln		0x4C6E
+#define PCI_CHIP_RAGE128MF		0x4D46
+#define PCI_CHIP_RAGE128ML		0x4D4C
+#define PCI_CHIP_R300_ND		0x4E44
+#define PCI_CHIP_R300_NE		0x4E45
+#define PCI_CHIP_R300_NF		0x4E46
+#define PCI_CHIP_R300_NG		0x4E47
+#define PCI_CHIP_R350_NH                0x4E48  
+#define PCI_CHIP_R350_NI                0x4E49  
+#define PCI_CHIP_R360_NJ                0x4E4A  
+#define PCI_CHIP_R350_NK                0x4E4B  
+#define PCI_CHIP_RV350_NP               0x4E50
+#define PCI_CHIP_RV350_NQ               0x4E51
+#define PCI_CHIP_RV350_NR               0x4E52
+#define PCI_CHIP_RV350_NS               0x4E53
+#define PCI_CHIP_RV350_NT               0x4E54
+#define PCI_CHIP_RV350_NV               0x4E56
+#define PCI_CHIP_RAGE128PA		0x5041
+#define PCI_CHIP_RAGE128PB		0x5042
+#define PCI_CHIP_RAGE128PC		0x5043
+#define PCI_CHIP_RAGE128PD		0x5044
+#define PCI_CHIP_RAGE128PE		0x5045
+#define PCI_CHIP_RAGE128PF		0x5046
+#define PCI_CHIP_RAGE128PG		0x5047
+#define PCI_CHIP_RAGE128PH		0x5048
+#define PCI_CHIP_RAGE128PI		0x5049
+#define PCI_CHIP_RAGE128PJ		0x504A
+#define PCI_CHIP_RAGE128PK		0x504B
+#define PCI_CHIP_RAGE128PL		0x504C
+#define PCI_CHIP_RAGE128PM		0x504D
+#define PCI_CHIP_RAGE128PN		0x504E
+#define PCI_CHIP_RAGE128PO		0x504F
+#define PCI_CHIP_RAGE128PP		0x5050
+#define PCI_CHIP_RAGE128PQ		0x5051
+#define PCI_CHIP_RAGE128PR		0x5052
+#define PCI_CHIP_RAGE128PS		0x5053
+#define PCI_CHIP_RAGE128PT		0x5054
+#define PCI_CHIP_RAGE128PU		0x5055
+#define PCI_CHIP_RAGE128PV		0x5056
+#define PCI_CHIP_RAGE128PW		0x5057
+#define PCI_CHIP_RAGE128PX		0x5058
+#define PCI_CHIP_RADEON_QD		0x5144
+#define PCI_CHIP_RADEON_QE		0x5145
+#define PCI_CHIP_RADEON_QF		0x5146
+#define PCI_CHIP_RADEON_QG		0x5147
+#define PCI_CHIP_R200_QH		0x5148
+#define PCI_CHIP_R200_QI		0x5149
+#define PCI_CHIP_R200_QJ		0x514A
+#define PCI_CHIP_R200_QK		0x514B
+#define PCI_CHIP_R200_QL		0x514C
+#define PCI_CHIP_R200_QM		0x514D
+#define PCI_CHIP_R200_QN		0x514E
+#define PCI_CHIP_R200_QO		0x514F
+#define PCI_CHIP_RV200_QW		0x5157
+#define PCI_CHIP_RV200_QX		0x5158
+#define PCI_CHIP_RV100_QY		0x5159
+#define PCI_CHIP_RV100_QZ		0x515A
+#define PCI_CHIP_RAGE128RE		0x5245
+#define PCI_CHIP_RAGE128RF		0x5246
+#define PCI_CHIP_RAGE128RG		0x5247
+#define PCI_CHIP_RAGE128RK		0x524B
+#define PCI_CHIP_RAGE128RL		0x524C
+#define PCI_CHIP_RAGE128SE		0x5345
+#define PCI_CHIP_RAGE128SF		0x5346
+#define PCI_CHIP_RAGE128SG		0x5347
+#define PCI_CHIP_RAGE128SH		0x5348
+#define PCI_CHIP_RAGE128SK		0x534B
+#define PCI_CHIP_RAGE128SL		0x534C
+#define PCI_CHIP_RAGE128SM		0x534D
+#define PCI_CHIP_RAGE128SN		0x534E
+#define PCI_CHIP_RAGE128TF		0x5446
+#define PCI_CHIP_RAGE128TL		0x544C
+#define PCI_CHIP_RAGE128TR		0x5452
+#define PCI_CHIP_RAGE128TS		0x5453
+#define PCI_CHIP_RAGE128TT		0x5454
+#define PCI_CHIP_RAGE128TU		0x5455
+#define PCI_CHIP_RV370_5460             0x5460
+#define PCI_CHIP_RV370_5461             0x5461
+#define PCI_CHIP_RV370_5462             0x5462
+#define PCI_CHIP_RV370_5463             0x5463
+#define PCI_CHIP_RV370_5464             0x5464
+#define PCI_CHIP_RV370_5465             0x5465
+#define PCI_CHIP_RV370_5466             0x5466
+#define PCI_CHIP_RV370_5467             0x5467
+#define PCI_CHIP_R423_UH                0x5548
+#define PCI_CHIP_R423_UI                0x5549
+#define PCI_CHIP_R423_UJ                0x554A
+#define PCI_CHIP_R423_UK                0x554B
+#define PCI_CHIP_R423_UQ                0x5551
+#define PCI_CHIP_R423_UR                0x5552
+#define PCI_CHIP_R423_UT                0x5554
+#define PCI_CHIP_MACH64VT		0x5654
+#define PCI_CHIP_MACH64VU		0x5655
+#define PCI_CHIP_MACH64VV		0x5656
+#define PCI_CHIP_RS300_5834		0x5834
+#define PCI_CHIP_RS300_5835		0x5835
+#define PCI_CHIP_RS300_5836		0x5836
+#define PCI_CHIP_RS300_5837		0x5837
+#define PCI_CHIP_RV370_5B60             0x5B60
+#define PCI_CHIP_RV370_5B61             0x5B61
+#define PCI_CHIP_RV370_5B62             0x5B62
+#define PCI_CHIP_RV370_5B63             0x5B63
+#define PCI_CHIP_RV370_5B64             0x5B64
+#define PCI_CHIP_RV370_5B65             0x5B65
+#define PCI_CHIP_RV370_5B66             0x5B66
+#define PCI_CHIP_RV370_5B67             0x5B67
+#define PCI_CHIP_RV280_5960		0x5960
+#define PCI_CHIP_RV280_5961		0x5961
+#define PCI_CHIP_RV280_5962		0x5962
+#define PCI_CHIP_RV280_5964		0x5964
+#define PCI_CHIP_RV280_5C61		0x5C61
+#define PCI_CHIP_RV280_5C63		0x5C63
+#define PCI_CHIP_R423_5D57              0x5D57
+#define PCI_CHIP_RS350_7834             0x7834
+#define PCI_CHIP_RS350_7835             0x7835
+
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
new file mode 100644
index 0000000..8a4ba3b
--- /dev/null
+++ b/drivers/video/aty/aty128fb.c
@@ -0,0 +1,2485 @@
+/* $Id: aty128fb.c,v 1.1.1.1.36.1 1999/12/11 09:03:05 Exp $
+ *  linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128
+ *
+ *  Copyright (C) 1999-2003, Brad Douglas <brad@neruo.com>
+ *  Copyright (C) 1999, Anthony Tong <atong@uiuc.edu>
+ *
+ *                Ani Joshi / Jeff Garzik
+ *                      - Code cleanup
+ *
+ *                Michel Danzer <michdaen@iiic.ethz.ch>
+ *                      - 15/16 bit cleanup
+ *                      - fix panning
+ *
+ *                Benjamin Herrenschmidt
+ *                      - pmac-specific PM stuff
+ *			- various fixes & cleanups
+ *
+ *                Andreas Hundt <andi@convergence.de>
+ *                      - FB_ACTIVATE fixes
+ *
+ *		  Paul Mackerras <paulus@samba.org>
+ *			- Convert to new framebuffer API,
+ *			  fix colormap setting at 16 bits/pixel (565)
+ *
+ *		  Paul Mundt 
+ *		  	- PCI hotplug
+ *
+ *		  Jon Smirl <jonsmirl@yahoo.com>
+ * 			- PCI ID update
+ * 			- replace ROM BIOS search
+ *
+ *  Based off of Geert's atyfb.c and vfb.c.
+ *
+ *  TODO:
+ *		- monitor sensing (DDC)
+ *              - virtual display
+ *		- other platform support (only ppc/x86 supported)
+ *		- hardware cursor support
+ *
+ *    Please cc: your patches to brad@neruo.com.
+ */
+
+/*
+ * A special note of gratitude to ATI's devrel for providing documentation,
+ * example code and hardware. Thanks Nitya.	-atong and brad
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/console.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_PPC_PMAC
+#include <asm/pmac_feature.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include "../macmodes.h"
+#endif
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif /* CONFIG_BOOTX_TEXT */
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include <video/aty128.h>
+
+/* Debug flag */
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt, args...)		printk(KERN_DEBUG "aty128fb: %s " fmt, __FUNCTION__, ##args);
+#else
+#define DBG(fmt, args...)
+#endif
+
+#ifndef CONFIG_PPC_PMAC
+/* default mode */
+static struct fb_var_screeninfo default_var __initdata = {
+	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
+	640, 480, 640, 480, 0, 0, 8, 0,
+	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
+	0, FB_VMODE_NONINTERLACED
+};
+
+#else /* CONFIG_PPC_PMAC */
+/* default to 1024x768 at 75Hz on PPC - this will work
+ * on the iMac, the usual 640x480 @ 60Hz doesn't. */
+static struct fb_var_screeninfo default_var = {
+	/* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
+	1024, 768, 1024, 768, 0, 0, 8, 0,
+	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	0, 0, -1, -1, 0, 12699, 160, 32, 28, 1, 96, 3,
+	FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	FB_VMODE_NONINTERLACED
+};
+#endif /* CONFIG_PPC_PMAC */
+
+/* default modedb mode */
+/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
+static struct fb_videomode defaultmode __initdata = {
+	.refresh =	60,
+	.xres =		640,
+	.yres =		480,
+	.pixclock =	39722,
+	.left_margin =	48,
+	.right_margin =	16,
+	.upper_margin =	33,
+	.lower_margin =	10,
+	.hsync_len =	96,
+	.vsync_len =	2,
+	.sync =		0,
+	.vmode =	FB_VMODE_NONINTERLACED
+};
+
+/* Chip generations */
+enum {
+	rage_128,
+	rage_128_pci,
+	rage_128_pro,
+	rage_128_pro_pci,
+	rage_M3,
+	rage_M3_pci,
+	rage_M4,
+	rage_128_ultra,
+};
+
+/* Must match above enum */
+static const char *r128_family[] __devinitdata = {
+	"AGP",
+	"PCI",
+	"PRO AGP",
+	"PRO PCI",
+	"M3 AGP",
+	"M3 PCI",
+	"M4 AGP",
+	"Ultra AGP",
+};
+
+/*
+ * PCI driver prototypes
+ */
+static int aty128_probe(struct pci_dev *pdev,
+                               const struct pci_device_id *ent);
+static void aty128_remove(struct pci_dev *pdev);
+static int aty128_pci_suspend(struct pci_dev *pdev, pm_message_t state);
+static int aty128_pci_resume(struct pci_dev *pdev);
+static int aty128_do_resume(struct pci_dev *pdev);
+
+/* supported Rage128 chipsets */
+static struct pci_device_id aty128_pci_tbl[] = {
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, aty128_pci_tbl);
+
+static struct pci_driver aty128fb_driver = {
+	.name		= "aty128fb",
+	.id_table	= aty128_pci_tbl,
+	.probe		= aty128_probe,
+	.remove		= __devexit_p(aty128_remove),
+	.suspend	= aty128_pci_suspend,
+	.resume		= aty128_pci_resume,
+};
+
+/* packed BIOS settings */
+#ifndef CONFIG_PPC
+typedef struct {
+	u8 clock_chip_type;
+	u8 struct_size;
+	u8 accelerator_entry;
+	u8 VGA_entry;
+	u16 VGA_table_offset;
+	u16 POST_table_offset;
+	u16 XCLK;
+	u16 MCLK;
+	u8 num_PLL_blocks;
+	u8 size_PLL_blocks;
+	u16 PCLK_ref_freq;
+	u16 PCLK_ref_divider;
+	u32 PCLK_min_freq;
+	u32 PCLK_max_freq;
+	u16 MCLK_ref_freq;
+	u16 MCLK_ref_divider;
+	u32 MCLK_min_freq;
+	u32 MCLK_max_freq;
+	u16 XCLK_ref_freq;
+	u16 XCLK_ref_divider;
+	u32 XCLK_min_freq;
+	u32 XCLK_max_freq;
+} __attribute__ ((packed)) PLL_BLOCK;
+#endif /* !CONFIG_PPC */
+
+/* onboard memory information */
+struct aty128_meminfo {
+	u8 ML;
+	u8 MB;
+	u8 Trcd;
+	u8 Trp;
+	u8 Twr;
+	u8 CL;
+	u8 Tr2w;
+	u8 LoopLatency;
+	u8 DspOn;
+	u8 Rloop;
+	const char *name;
+};
+
+/* various memory configurations */
+static const struct aty128_meminfo sdr_128   =
+	{ 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" };
+static const struct aty128_meminfo sdr_64    =
+	{ 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" };
+static const struct aty128_meminfo sdr_sgram =
+	{ 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" };
+static const struct aty128_meminfo ddr_sgram =
+	{ 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
+
+static struct fb_fix_screeninfo aty128fb_fix __initdata = {
+	.id		= "ATY Rage128",
+	.type		= FB_TYPE_PACKED_PIXELS,
+	.visual		= FB_VISUAL_PSEUDOCOLOR,
+	.xpanstep	= 8,
+	.ypanstep	= 1,
+	.mmio_len	= 0x2000,
+	.accel		= FB_ACCEL_ATI_RAGE128,
+};
+
+static char *mode_option __initdata = NULL;
+
+#ifdef CONFIG_PPC_PMAC
+static int default_vmode __initdata = VMODE_1024_768_60;
+static int default_cmode __initdata = CMODE_8;
+#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+static int default_crt_on __initdata = 0;
+static int default_lcd_on __initdata = 1;
+#endif
+
+#ifdef CONFIG_MTRR
+static int mtrr = 1;
+#endif
+
+/* PLL constants */
+struct aty128_constants {
+	u32 ref_clk;
+	u32 ppll_min;
+	u32 ppll_max;
+	u32 ref_divider;
+	u32 xclk;
+	u32 fifo_width;
+	u32 fifo_depth;
+};
+
+struct aty128_crtc {
+	u32 gen_cntl;
+	u32 h_total, h_sync_strt_wid;
+	u32 v_total, v_sync_strt_wid;
+	u32 pitch;
+	u32 offset, offset_cntl;
+	u32 xoffset, yoffset;
+	u32 vxres, vyres;
+	u32 depth, bpp;
+};
+
+struct aty128_pll {
+	u32 post_divider;
+	u32 feedback_divider;
+	u32 vclk;
+};
+
+struct aty128_ddafifo {
+	u32 dda_config;
+	u32 dda_on_off;
+};
+
+/* register values for a specific mode */
+struct aty128fb_par {
+	struct aty128_crtc crtc;
+	struct aty128_pll pll;
+	struct aty128_ddafifo fifo_reg;
+	u32 accel_flags;
+	struct aty128_constants constants;  /* PLL and others      */
+	void __iomem *regbase;              /* remapped mmio       */
+	u32 vram_size;                      /* onboard video ram   */
+	int chip_gen;
+	const struct aty128_meminfo *mem;   /* onboard mem info    */
+#ifdef CONFIG_MTRR
+	struct { int vram; int vram_valid; } mtrr;
+#endif
+	int blitter_may_be_busy;
+	int fifo_slots;                 /* free slots in FIFO (64 max) */
+
+	int	pm_reg;
+	int crt_on, lcd_on;
+	struct pci_dev *pdev;
+	struct fb_info *next;
+	int	asleep;
+	int	lock_blank;
+
+	u8	red[32];		/* see aty128fb_setcolreg */
+	u8	green[64];
+	u8	blue[32];
+	u32	pseudo_palette[16];	/* used for TRUECOLOR */
+};
+
+
+#define round_div(n, d) ((n+(d/2))/d)
+
+static int aty128fb_check_var(struct fb_var_screeninfo *var,
+			      struct fb_info *info);
+static int aty128fb_set_par(struct fb_info *info);
+static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			      u_int transp, struct fb_info *info);
+static int aty128fb_pan_display(struct fb_var_screeninfo *var,
+			   struct fb_info *fb);
+static int aty128fb_blank(int blank, struct fb_info *fb);
+static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			  u_long arg, struct fb_info *info);
+static int aty128fb_sync(struct fb_info *info);
+
+    /*
+     *  Internal routines
+     */
+
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+                             const struct aty128fb_par *par);
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+                             struct aty128fb_par *par);
+#if 0
+static void __init aty128_get_pllinfo(struct aty128fb_par *par,
+				      void __iomem *bios);
+static void __init __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par);
+#endif
+static void aty128_timings(struct aty128fb_par *par);
+static void aty128_init_engine(struct aty128fb_par *par);
+static void aty128_reset_engine(const struct aty128fb_par *par);
+static void aty128_flush_pixel_cache(const struct aty128fb_par *par);
+static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par);
+static void wait_for_fifo(u16 entries, struct aty128fb_par *par);
+static void wait_for_idle(struct aty128fb_par *par);
+static u32 depth_to_dst(u32 depth);
+
+#define BIOS_IN8(v)  	(readb(bios + (v)))
+#define BIOS_IN16(v) 	(readb(bios + (v)) | \
+			  (readb(bios + (v) + 1) << 8))
+#define BIOS_IN32(v) 	(readb(bios + (v)) | \
+			  (readb(bios + (v) + 1) << 8) | \
+			  (readb(bios + (v) + 2) << 16) | \
+			  (readb(bios + (v) + 3) << 24))
+
+
+static struct fb_ops aty128fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_check_var	= aty128fb_check_var,
+	.fb_set_par	= aty128fb_set_par,
+	.fb_setcolreg	= aty128fb_setcolreg,
+	.fb_pan_display = aty128fb_pan_display,
+	.fb_blank	= aty128fb_blank,
+	.fb_ioctl	= aty128fb_ioctl,
+	.fb_sync	= aty128fb_sync,
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+	.fb_cursor	= soft_cursor,
+};
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int aty128_set_backlight_enable(int on, int level, void* data);
+static int aty128_set_backlight_level(int level, void* data);
+
+static struct backlight_controller aty128_backlight_controller = {
+	aty128_set_backlight_enable,
+	aty128_set_backlight_level
+};
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+    /*
+     * Functions to read from/write to the mmio registers
+     *	- endian conversions may possibly be avoided by
+     *    using the other register aperture. TODO.
+     */
+static inline u32 _aty_ld_le32(volatile unsigned int regindex, 
+			       const struct aty128fb_par *par)
+{
+	return readl (par->regbase + regindex);
+}
+
+static inline void _aty_st_le32(volatile unsigned int regindex, u32 val, 
+				const struct aty128fb_par *par)
+{
+	writel (val, par->regbase + regindex);
+}
+
+static inline u8 _aty_ld_8(unsigned int regindex,
+			   const struct aty128fb_par *par)
+{
+	return readb (par->regbase + regindex);
+}
+
+static inline void _aty_st_8(unsigned int regindex, u8 val,
+			     const struct aty128fb_par *par)
+{
+	writeb (val, par->regbase + regindex);
+}
+
+#define aty_ld_le32(regindex)		_aty_ld_le32(regindex, par)
+#define aty_st_le32(regindex, val)	_aty_st_le32(regindex, val, par)
+#define aty_ld_8(regindex)		_aty_ld_8(regindex, par)
+#define aty_st_8(regindex, val)		_aty_st_8(regindex, val, par)
+
+    /*
+     * Functions to read from/write to the pll registers
+     */
+
+#define aty_ld_pll(pll_index)		_aty_ld_pll(pll_index, par)
+#define aty_st_pll(pll_index, val)	_aty_st_pll(pll_index, val, par)
+
+
+static u32 _aty_ld_pll(unsigned int pll_index,
+		       const struct aty128fb_par *par)
+{       
+	aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F);
+	return aty_ld_le32(CLOCK_CNTL_DATA);
+}
+
+    
+static void _aty_st_pll(unsigned int pll_index, u32 val,
+			const struct aty128fb_par *par)
+{
+	aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN);
+	aty_st_le32(CLOCK_CNTL_DATA, val);
+}
+
+
+/* return true when the PLL has completed an atomic update */
+static int aty_pll_readupdate(const struct aty128fb_par *par)
+{
+	return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R);
+}
+
+
+static void aty_pll_wait_readupdate(const struct aty128fb_par *par)
+{
+	unsigned long timeout = jiffies + HZ/100; // should be more than enough
+	int reset = 1;
+
+	while (time_before(jiffies, timeout))
+		if (aty_pll_readupdate(par)) {
+			reset = 0;
+			break;
+		}
+
+	if (reset)	/* reset engine?? */
+		printk(KERN_DEBUG "aty128fb: PLL write timeout!\n");
+}
+
+
+/* tell PLL to update */
+static void aty_pll_writeupdate(const struct aty128fb_par *par)
+{
+	aty_pll_wait_readupdate(par);
+
+	aty_st_pll(PPLL_REF_DIV,
+		   aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W);
+}
+
+
+/* write to the scratch register to test r/w functionality */
+static int __init register_test(const struct aty128fb_par *par)
+{
+	u32 val;
+	int flag = 0;
+
+	val = aty_ld_le32(BIOS_0_SCRATCH);
+
+	aty_st_le32(BIOS_0_SCRATCH, 0x55555555);
+	if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) {
+		aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA);
+
+		if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA)
+			flag = 1; 
+	}
+
+	aty_st_le32(BIOS_0_SCRATCH, val);	// restore value
+	return flag;
+}
+
+
+/*
+ * Accelerator engine functions
+ */
+static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par)
+{
+	int i;
+
+	for (;;) {
+		for (i = 0; i < 2000000; i++) {
+			par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff;
+			if (par->fifo_slots >= entries)
+				return;
+		}
+		aty128_reset_engine(par);
+	}
+}
+
+
+static void wait_for_idle(struct aty128fb_par *par)
+{
+	int i;
+
+	do_wait_for_fifo(64, par);
+
+	for (;;) {
+		for (i = 0; i < 2000000; i++) {
+			if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) {
+				aty128_flush_pixel_cache(par);
+				par->blitter_may_be_busy = 0;
+				return;
+			}
+		}
+		aty128_reset_engine(par);
+	}
+}
+
+
+static void wait_for_fifo(u16 entries, struct aty128fb_par *par)
+{
+	if (par->fifo_slots < entries)
+		do_wait_for_fifo(64, par);
+	par->fifo_slots -= entries;
+}
+
+
+static void aty128_flush_pixel_cache(const struct aty128fb_par *par)
+{
+	int i;
+	u32 tmp;
+
+	tmp = aty_ld_le32(PC_NGUI_CTLSTAT);
+	tmp &= ~(0x00ff);
+	tmp |= 0x00ff;
+	aty_st_le32(PC_NGUI_CTLSTAT, tmp);
+
+	for (i = 0; i < 2000000; i++)
+		if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY))
+			break;
+}
+
+
+static void aty128_reset_engine(const struct aty128fb_par *par)
+{
+	u32 gen_reset_cntl, clock_cntl_index, mclk_cntl;
+
+	aty128_flush_pixel_cache(par);
+
+	clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX);
+	mclk_cntl = aty_ld_pll(MCLK_CNTL);
+
+	aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000);
+
+	gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL);
+	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI);
+	aty_ld_le32(GEN_RESET_CNTL);
+	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI));
+	aty_ld_le32(GEN_RESET_CNTL);
+
+	aty_st_pll(MCLK_CNTL, mclk_cntl);
+	aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index);
+	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl);
+
+	/* use old pio mode */
+	aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4);
+
+	DBG("engine reset");
+}
+
+
+static void aty128_init_engine(struct aty128fb_par *par)
+{
+	u32 pitch_value;
+
+	wait_for_idle(par);
+
+	/* 3D scaler not spoken here */
+	wait_for_fifo(1, par);
+	aty_st_le32(SCALE_3D_CNTL, 0x00000000);
+
+	aty128_reset_engine(par);
+
+	pitch_value = par->crtc.pitch;
+	if (par->crtc.bpp == 24) {
+		pitch_value = pitch_value * 3;
+	}
+
+	wait_for_fifo(4, par);
+	/* setup engine offset registers */
+	aty_st_le32(DEFAULT_OFFSET, 0x00000000);
+
+	/* setup engine pitch registers */
+	aty_st_le32(DEFAULT_PITCH, pitch_value);
+
+	/* set the default scissor register to max dimensions */
+	aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF);
+
+	/* set the drawing controls registers */
+	aty_st_le32(DP_GUI_MASTER_CNTL,
+		    GMC_SRC_PITCH_OFFSET_DEFAULT		|
+		    GMC_DST_PITCH_OFFSET_DEFAULT		|
+		    GMC_SRC_CLIP_DEFAULT			|
+		    GMC_DST_CLIP_DEFAULT			|
+		    GMC_BRUSH_SOLIDCOLOR			|
+		    (depth_to_dst(par->crtc.depth) << 8)	|
+		    GMC_SRC_DSTCOLOR			|
+		    GMC_BYTE_ORDER_MSB_TO_LSB		|
+		    GMC_DP_CONVERSION_TEMP_6500		|
+		    ROP3_PATCOPY				|
+		    GMC_DP_SRC_RECT				|
+		    GMC_3D_FCN_EN_CLR			|
+		    GMC_DST_CLR_CMP_FCN_CLEAR		|
+		    GMC_AUX_CLIP_CLEAR			|
+		    GMC_WRITE_MASK_SET);
+
+	wait_for_fifo(8, par);
+	/* clear the line drawing registers */
+	aty_st_le32(DST_BRES_ERR, 0);
+	aty_st_le32(DST_BRES_INC, 0);
+	aty_st_le32(DST_BRES_DEC, 0);
+
+	/* set brush color registers */
+	aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */
+	aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */
+
+	/* set source color registers */
+	aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF);   /* white */
+	aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000);   /* black */
+
+	/* default write mask */
+	aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF);
+
+	/* Wait for all the writes to be completed before returning */
+	wait_for_idle(par);
+}
+
+
+/* convert depth values to their register representation */
+static u32 depth_to_dst(u32 depth)
+{
+	if (depth <= 8)
+		return DST_8BPP;
+	else if (depth <= 15)
+		return DST_15BPP;
+	else if (depth == 16)
+		return DST_16BPP;
+	else if (depth <= 24)
+		return DST_24BPP;
+	else if (depth <= 32)
+		return DST_32BPP;
+
+	return -EINVAL;
+}
+
+/*
+ * PLL informations retreival
+ */
+
+
+#ifndef __sparc__
+static void __iomem * __init aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev)
+{
+	u16 dptr;
+	u8 rom_type;
+	void __iomem *bios;
+	size_t rom_size;
+
+    	/* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */
+    	unsigned int temp;
+	temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
+	temp &= 0x00ffffffu;
+	temp |= 0x04 << 24;
+	aty_st_le32(RAGE128_MPP_TB_CONFIG, temp);
+	temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
+
+	bios = pci_map_rom(dev, &rom_size);
+
+	if (!bios) {
+		printk(KERN_ERR "aty128fb: ROM failed to map\n");
+		return NULL;
+	}
+
+	/* Very simple test to make sure it appeared */
+	if (BIOS_IN16(0) != 0xaa55) {
+		printk(KERN_ERR "aty128fb: Invalid ROM signature %x should be 0xaa55\n",
+		       BIOS_IN16(0));
+		goto failed;
+	}
+
+	/* Look for the PCI data to check the ROM type */
+	dptr = BIOS_IN16(0x18);
+
+	/* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM
+	 * for now, until I've verified this works everywhere. The goal here is more
+	 * to phase out Open Firmware images.
+	 *
+	 * Currently, we only look at the first PCI data, we could iteratre and deal with
+	 * them all, and we should use fb_bios_start relative to start of image and not
+	 * relative start of ROM, but so far, I never found a dual-image ATI card
+	 *
+	 * typedef struct {
+	 * 	u32	signature;	+ 0x00
+	 * 	u16	vendor;		+ 0x04
+	 * 	u16	device;		+ 0x06
+	 * 	u16	reserved_1;	+ 0x08
+	 * 	u16	dlen;		+ 0x0a
+	 * 	u8	drevision;	+ 0x0c
+	 * 	u8	class_hi;	+ 0x0d
+	 * 	u16	class_lo;	+ 0x0e
+	 * 	u16	ilen;		+ 0x10
+	 * 	u16	irevision;	+ 0x12
+	 * 	u8	type;		+ 0x14
+	 * 	u8	indicator;	+ 0x15
+	 * 	u16	reserved_2;	+ 0x16
+	 * } pci_data_t;
+	 */
+	if (BIOS_IN32(dptr) !=  (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) {
+		printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n",
+		       BIOS_IN32(dptr));
+		goto anyway;
+	}
+	rom_type = BIOS_IN8(dptr + 0x14);
+	switch(rom_type) {
+	case 0:
+		printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n");
+		break;
+	case 1:
+		printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n");
+		goto failed;
+	case 2:
+		printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n");
+		goto failed;
+	default:
+		printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", rom_type);
+		goto failed;
+	}
+ anyway:
+	return bios;
+
+ failed:
+	pci_unmap_rom(dev, bios);
+	return NULL;
+}
+
+static void __init aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios)
+{
+	unsigned int bios_hdr;
+	unsigned int bios_pll;
+
+	bios_hdr = BIOS_IN16(0x48);
+	bios_pll = BIOS_IN16(bios_hdr + 0x30);
+	
+	par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16);
+	par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12);
+	par->constants.xclk = BIOS_IN16(bios_pll + 0x08);
+	par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10);
+	par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e);
+
+	DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n",
+			par->constants.ppll_max, par->constants.ppll_min,
+			par->constants.xclk, par->constants.ref_divider,
+			par->constants.ref_clk);
+
+}           
+
+#ifdef CONFIG_X86
+static void __iomem *  __devinit aty128_find_mem_vbios(struct aty128fb_par *par)
+{
+	/* I simplified this code as we used to miss the signatures in
+	 * a lot of case. It's now closer to XFree, we just don't check
+	 * for signatures at all... Something better will have to be done
+	 * if we end up having conflicts
+	 */
+        u32  segstart;
+        unsigned char __iomem *rom_base = NULL;
+                                                
+        for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
+                rom_base = ioremap(segstart, 0x10000);
+		if (rom_base == NULL)
+			return NULL;
+		if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa)
+	                break;
+                iounmap(rom_base);
+		rom_base = NULL;
+        }
+	return rom_base;
+}
+#endif
+#endif /* ndef(__sparc__) */
+
+/* fill in known card constants if pll_block is not available */
+static void __init aty128_timings(struct aty128fb_par *par)
+{
+#ifdef CONFIG_PPC_OF
+	/* instead of a table lookup, assume OF has properly
+	 * setup the PLL registers and use their values
+	 * to set the XCLK values and reference divider values */
+
+	u32 x_mpll_ref_fb_div;
+	u32 xclk_cntl;
+	u32 Nx, M;
+	unsigned PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 };
+#endif
+
+	if (!par->constants.ref_clk)
+		par->constants.ref_clk = 2950;
+
+#ifdef CONFIG_PPC_OF
+	x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV);
+	xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7;
+	Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8;
+	M  = x_mpll_ref_fb_div & 0x0000ff;
+
+	par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk),
+					(M * PostDivSet[xclk_cntl]));
+
+	par->constants.ref_divider =
+		aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
+#endif
+
+	if (!par->constants.ref_divider) {
+		par->constants.ref_divider = 0x3b;
+
+		aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e);
+		aty_pll_writeupdate(par);
+	}
+	aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider);
+	aty_pll_writeupdate(par);
+
+	/* from documentation */
+	if (!par->constants.ppll_min)
+		par->constants.ppll_min = 12500;
+	if (!par->constants.ppll_max)
+		par->constants.ppll_max = 25000;    /* 23000 on some cards? */
+	if (!par->constants.xclk)
+		par->constants.xclk = 0x1d4d;	     /* same as mclk */
+
+	par->constants.fifo_width = 128;
+	par->constants.fifo_depth = 32;
+
+	switch (aty_ld_le32(MEM_CNTL) & 0x3) {
+	case 0:
+		par->mem = &sdr_128;
+		break;
+	case 1:
+		par->mem = &sdr_sgram;
+		break;
+	case 2:
+		par->mem = &ddr_sgram;
+		break;
+	default:
+		par->mem = &sdr_sgram;
+	}
+}
+
+
+
+/*
+ * CRTC programming
+ */
+
+/* Program the CRTC registers */
+static void aty128_set_crtc(const struct aty128_crtc *crtc,
+			    const struct aty128fb_par *par)
+{
+	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl);
+	aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total);
+	aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid);
+	aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total);
+	aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid);
+	aty_st_le32(CRTC_PITCH, crtc->pitch);
+	aty_st_le32(CRTC_OFFSET, crtc->offset);
+	aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
+	/* Disable ATOMIC updating.  Is this the right place? */
+	aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000));
+}
+
+
+static int aty128_var_to_crtc(const struct fb_var_screeninfo *var,
+			      struct aty128_crtc *crtc,
+			      const struct aty128fb_par *par)
+{
+	u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst;
+	u32 left, right, upper, lower, hslen, vslen, sync, vmode;
+	u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol;
+	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
+	u32 depth, bytpp;
+	u8 mode_bytpp[7] = { 0, 0, 1, 2, 2, 3, 4 };
+
+	/* input */
+	xres = var->xres;
+	yres = var->yres;
+	vxres   = var->xres_virtual;
+	vyres   = var->yres_virtual;
+	xoffset = var->xoffset;
+	yoffset = var->yoffset;
+	bpp   = var->bits_per_pixel;
+	left  = var->left_margin;
+	right = var->right_margin;
+	upper = var->upper_margin;
+	lower = var->lower_margin;
+	hslen = var->hsync_len;
+	vslen = var->vsync_len;
+	sync  = var->sync;
+	vmode = var->vmode;
+
+	if (bpp != 16)
+		depth = bpp;
+	else
+		depth = (var->green.length == 6) ? 16 : 15;
+
+	/* check for mode eligibility
+	 * accept only non interlaced modes */
+	if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+		return -EINVAL;
+
+	/* convert (and round up) and validate */
+	xres = (xres + 7) & ~7;
+	xoffset = (xoffset + 7) & ~7;
+
+	if (vxres < xres + xoffset)
+		vxres = xres + xoffset;
+
+	if (vyres < yres + yoffset)
+		vyres = yres + yoffset;
+
+	/* convert depth into ATI register depth */
+	dst = depth_to_dst(depth);
+
+	if (dst == -EINVAL) {
+		printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n");
+		return -EINVAL;
+	}
+
+	/* convert register depth to bytes per pixel */
+	bytpp = mode_bytpp[dst];
+
+	/* make sure there is enough video ram for the mode */
+	if ((u32)(vxres * vyres * bytpp) > par->vram_size) {
+		printk(KERN_ERR "aty128fb: Not enough memory for mode\n");
+		return -EINVAL;
+	}
+
+	h_disp = (xres >> 3) - 1;
+	h_total = (((xres + right + hslen + left) >> 3) - 1) & 0xFFFFL;
+
+	v_disp = yres - 1;
+	v_total = (yres + upper + vslen + lower - 1) & 0xFFFFL;
+
+	/* check to make sure h_total and v_total are in range */
+	if (((h_total >> 3) - 1) > 0x1ff || (v_total - 1) > 0x7FF) {
+		printk(KERN_ERR "aty128fb: invalid width ranges\n");
+		return -EINVAL;
+	}
+
+	h_sync_wid = (hslen + 7) >> 3;
+	if (h_sync_wid == 0)
+		h_sync_wid = 1;
+	else if (h_sync_wid > 0x3f)        /* 0x3f = max hwidth */
+		h_sync_wid = 0x3f;
+
+	h_sync_strt = (h_disp << 3) + right;
+
+	v_sync_wid = vslen;
+	if (v_sync_wid == 0)
+		v_sync_wid = 1;
+	else if (v_sync_wid > 0x1f)        /* 0x1f = max vwidth */
+		v_sync_wid = 0x1f;
+    
+	v_sync_strt = v_disp + lower;
+
+	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+    
+	c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
+
+	crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8);
+
+	crtc->h_total = h_total | (h_disp << 16);
+	crtc->v_total = v_total | (v_disp << 16);
+
+	crtc->h_sync_strt_wid = h_sync_strt | (h_sync_wid << 16) |
+	        (h_sync_pol << 23);
+	crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
+                (v_sync_pol << 23);
+
+	crtc->pitch = vxres >> 3;
+
+	crtc->offset = 0;
+
+	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
+		crtc->offset_cntl = 0x00010000;
+	else
+		crtc->offset_cntl = 0;
+
+	crtc->vxres = vxres;
+	crtc->vyres = vyres;
+	crtc->xoffset = xoffset;
+	crtc->yoffset = yoffset;
+	crtc->depth = depth;
+	crtc->bpp = bpp;
+
+	return 0;
+}
+
+
+static int aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var)
+{
+
+	/* fill in pixel info */
+	var->red.msb_right = 0;
+	var->green.msb_right = 0;
+	var->blue.offset = 0;
+	var->blue.msb_right = 0;
+	var->transp.offset = 0;
+	var->transp.length = 0;
+	var->transp.msb_right = 0;
+	switch (pix_width) {
+	case CRTC_PIX_WIDTH_8BPP:
+		var->bits_per_pixel = 8;
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->green.offset = 0;
+		var->green.length = 8;
+		var->blue.length = 8;
+		break;
+	case CRTC_PIX_WIDTH_15BPP:
+		var->bits_per_pixel = 16;
+		var->red.offset = 10;
+		var->red.length = 5;
+		var->green.offset = 5;
+		var->green.length = 5;
+		var->blue.length = 5;
+		break;
+	case CRTC_PIX_WIDTH_16BPP:
+		var->bits_per_pixel = 16;
+		var->red.offset = 11;
+		var->red.length = 5;
+		var->green.offset = 5;
+		var->green.length = 6;
+		var->blue.length = 5;
+		break;
+	case CRTC_PIX_WIDTH_24BPP:
+		var->bits_per_pixel = 24;
+		var->red.offset = 16;
+		var->red.length = 8;
+		var->green.offset = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+		break;
+	case CRTC_PIX_WIDTH_32BPP:
+		var->bits_per_pixel = 32;
+		var->red.offset = 16;
+		var->red.length = 8;
+		var->green.offset = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+		var->transp.offset = 24;
+		var->transp.length = 8;
+		break;
+	default:
+		printk(KERN_ERR "aty128fb: Invalid pixel width\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static int aty128_crtc_to_var(const struct aty128_crtc *crtc,
+			      struct fb_var_screeninfo *var)
+{
+	u32 xres, yres, left, right, upper, lower, hslen, vslen, sync;
+	u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
+	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
+	u32 pix_width;
+
+	/* fun with masking */
+	h_total     = crtc->h_total & 0x1ff;
+	h_disp      = (crtc->h_total >> 16) & 0xff;
+	h_sync_strt = (crtc->h_sync_strt_wid >> 3) & 0x1ff;
+	h_sync_dly  = crtc->h_sync_strt_wid & 0x7;
+	h_sync_wid  = (crtc->h_sync_strt_wid >> 16) & 0x3f;
+	h_sync_pol  = (crtc->h_sync_strt_wid >> 23) & 0x1;
+	v_total     = crtc->v_total & 0x7ff;
+	v_disp      = (crtc->v_total >> 16) & 0x7ff;
+	v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
+	v_sync_wid  = (crtc->v_sync_strt_wid >> 16) & 0x1f;
+	v_sync_pol  = (crtc->v_sync_strt_wid >> 23) & 0x1;
+	c_sync      = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+	pix_width   = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+	/* do conversions */
+	xres  = (h_disp + 1) << 3;
+	yres  = v_disp + 1;
+	left  = ((h_total - h_sync_strt - h_sync_wid) << 3) - h_sync_dly;
+	right = ((h_sync_strt - h_disp) << 3) + h_sync_dly;
+	hslen = h_sync_wid << 3;
+	upper = v_total - v_sync_strt - v_sync_wid;
+	lower = v_sync_strt - v_disp;
+	vslen = v_sync_wid;
+	sync  = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+		(v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+		(c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+
+	aty128_pix_width_to_var(pix_width, var);
+
+	var->xres = xres;
+	var->yres = yres;
+	var->xres_virtual = crtc->vxres;
+	var->yres_virtual = crtc->vyres;
+	var->xoffset = crtc->xoffset;
+	var->yoffset = crtc->yoffset;
+	var->left_margin  = left;
+	var->right_margin = right;
+	var->upper_margin = upper;
+	var->lower_margin = lower;
+	var->hsync_len = hslen;
+	var->vsync_len = vslen;
+	var->sync  = sync;
+	var->vmode = FB_VMODE_NONINTERLACED;
+
+	return 0;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static void aty128_set_crt_enable(struct aty128fb_par *par, int on)
+{
+	if (on) {
+		aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | CRT_CRTC_ON);
+		aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | DAC_PALETTE2_SNOOP_EN));
+	} else
+		aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & ~CRT_CRTC_ON);
+}
+
+static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
+{
+	u32 reg;
+
+	if (on) {
+		reg = aty_ld_le32(LVDS_GEN_CNTL);
+		reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION;
+		reg &= ~LVDS_DISPLAY_DIS;
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef CONFIG_PMAC_BACKLIGHT
+		aty128_set_backlight_enable(get_backlight_enable(),
+					    get_backlight_level(), par);
+#endif	
+	} else {
+#ifdef CONFIG_PMAC_BACKLIGHT
+		aty128_set_backlight_enable(0, 0, par);
+#endif	
+		reg = aty_ld_le32(LVDS_GEN_CNTL);
+		reg |= LVDS_DISPLAY_DIS;
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+		mdelay(100);
+		reg &= ~(LVDS_ON /*| LVDS_EN*/);
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+	}
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+static void aty128_set_pll(struct aty128_pll *pll, const struct aty128fb_par *par)
+{
+	u32 div3;
+
+	unsigned char post_conv[] =	/* register values for post dividers */
+        { 2, 0, 1, 4, 2, 2, 6, 2, 3, 2, 2, 2, 7 };
+
+	/* select PPLL_DIV_3 */
+	aty_st_le32(CLOCK_CNTL_INDEX, aty_ld_le32(CLOCK_CNTL_INDEX) | (3 << 8));
+
+	/* reset PLL */
+	aty_st_pll(PPLL_CNTL,
+		   aty_ld_pll(PPLL_CNTL) | PPLL_RESET | PPLL_ATOMIC_UPDATE_EN);
+
+	/* write the reference divider */
+	aty_pll_wait_readupdate(par);
+	aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider & 0x3ff);
+	aty_pll_writeupdate(par);
+
+	div3 = aty_ld_pll(PPLL_DIV_3);
+	div3 &= ~PPLL_FB3_DIV_MASK;
+	div3 |= pll->feedback_divider;
+	div3 &= ~PPLL_POST3_DIV_MASK;
+	div3 |= post_conv[pll->post_divider] << 16;
+
+	/* write feedback and post dividers */
+	aty_pll_wait_readupdate(par);
+	aty_st_pll(PPLL_DIV_3, div3);
+	aty_pll_writeupdate(par);
+
+	aty_pll_wait_readupdate(par);
+	aty_st_pll(HTOTAL_CNTL, 0);	/* no horiz crtc adjustment */
+	aty_pll_writeupdate(par);
+
+	/* clear the reset, just in case */
+	aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET);
+}
+
+
+static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
+			     const struct aty128fb_par *par)
+{
+	const struct aty128_constants c = par->constants;
+	unsigned char post_dividers[] = {1,2,4,8,3,6,12};
+	u32 output_freq;
+	u32 vclk;        /* in .01 MHz */
+	int i;
+	u32 n, d;
+
+	vclk = 100000000 / period_in_ps;	/* convert units to 10 kHz */
+
+	/* adjust pixel clock if necessary */
+	if (vclk > c.ppll_max)
+		vclk = c.ppll_max;
+	if (vclk * 12 < c.ppll_min)
+		vclk = c.ppll_min/12;
+
+	/* now, find an acceptable divider */
+	for (i = 0; i < sizeof(post_dividers); i++) {
+		output_freq = post_dividers[i] * vclk;
+		if (output_freq >= c.ppll_min && output_freq <= c.ppll_max)
+			break;
+	}
+
+	/* calculate feedback divider */
+	n = c.ref_divider * output_freq;
+	d = c.ref_clk;
+
+	pll->post_divider = post_dividers[i];
+	pll->feedback_divider = round_div(n, d);
+	pll->vclk = vclk;
+
+	DBG("post %d feedback %d vlck %d output %d ref_divider %d "
+	    "vclk_per: %d\n", pll->post_divider,
+	    pll->feedback_divider, vclk, output_freq,
+	    c.ref_divider, period_in_ps);
+
+	return 0;
+}
+
+
+static int aty128_pll_to_var(const struct aty128_pll *pll, struct fb_var_screeninfo *var)
+{
+	var->pixclock = 100000000 / pll->vclk;
+
+	return 0;
+}
+
+
+static void aty128_set_fifo(const struct aty128_ddafifo *dsp,
+			    const struct aty128fb_par *par)
+{
+	aty_st_le32(DDA_CONFIG, dsp->dda_config);
+	aty_st_le32(DDA_ON_OFF, dsp->dda_on_off);
+}
+
+
+static int aty128_ddafifo(struct aty128_ddafifo *dsp,
+			  const struct aty128_pll *pll,
+			  u32 depth,
+			  const struct aty128fb_par *par)
+{
+	const struct aty128_meminfo *m = par->mem;
+	u32 xclk = par->constants.xclk;
+	u32 fifo_width = par->constants.fifo_width;
+	u32 fifo_depth = par->constants.fifo_depth;
+	s32 x, b, p, ron, roff;
+	u32 n, d, bpp;
+
+	/* round up to multiple of 8 */
+	bpp = (depth+7) & ~7;
+
+	n = xclk * fifo_width;
+	d = pll->vclk * bpp;
+	x = round_div(n, d);
+
+	ron = 4 * m->MB +
+		3 * ((m->Trcd - 2 > 0) ? m->Trcd - 2 : 0) +
+		2 * m->Trp +
+		m->Twr +
+		m->CL +
+		m->Tr2w +
+		x;
+
+	DBG("x %x\n", x);
+
+	b = 0;
+	while (x) {
+		x >>= 1;
+		b++;
+	}
+	p = b + 1;
+
+	ron <<= (11 - p);
+
+	n <<= (11 - p);
+	x = round_div(n, d);
+	roff = x * (fifo_depth - 4);
+
+	if ((ron + m->Rloop) >= roff) {
+		printk(KERN_ERR "aty128fb: Mode out of range!\n");
+		return -EINVAL;
+	}
+
+	DBG("p: %x rloop: %x x: %x ron: %x roff: %x\n",
+	    p, m->Rloop, x, ron, roff);
+
+	dsp->dda_config = p << 16 | m->Rloop << 20 | x;
+	dsp->dda_on_off = ron << 16 | roff;
+
+	return 0;
+}
+
+
+/*
+ * This actually sets the video mode.
+ */
+static int aty128fb_set_par(struct fb_info *info)
+{ 
+	struct aty128fb_par *par = info->par;
+	u32 config;
+	int err;
+
+	if ((err = aty128_decode_var(&info->var, par)) != 0)
+		return err;
+
+	if (par->blitter_may_be_busy)
+		wait_for_idle(par);
+
+	/* clear all registers that may interfere with mode setting */
+	aty_st_le32(OVR_CLR, 0);
+	aty_st_le32(OVR_WID_LEFT_RIGHT, 0);
+	aty_st_le32(OVR_WID_TOP_BOTTOM, 0);
+	aty_st_le32(OV0_SCALE_CNTL, 0);
+	aty_st_le32(MPP_TB_CONFIG, 0);
+	aty_st_le32(MPP_GP_CONFIG, 0);
+	aty_st_le32(SUBPIC_CNTL, 0);
+	aty_st_le32(VIPH_CONTROL, 0);
+	aty_st_le32(I2C_CNTL_1, 0);         /* turn off i2c */
+	aty_st_le32(GEN_INT_CNTL, 0);	/* turn off interrupts */
+	aty_st_le32(CAP0_TRIG_CNTL, 0);
+	aty_st_le32(CAP1_TRIG_CNTL, 0);
+
+	aty_st_8(CRTC_EXT_CNTL + 1, 4);	/* turn video off */
+
+	aty128_set_crtc(&par->crtc, par);
+	aty128_set_pll(&par->pll, par);
+	aty128_set_fifo(&par->fifo_reg, par);
+
+	config = aty_ld_le32(CONFIG_CNTL) & ~3;
+
+#if defined(__BIG_ENDIAN)
+	if (par->crtc.bpp == 32)
+		config |= 2;	/* make aperture do 32 bit swapping */
+	else if (par->crtc.bpp == 16)
+		config |= 1;	/* make aperture do 16 bit swapping */
+#endif
+
+	aty_st_le32(CONFIG_CNTL, config);
+	aty_st_8(CRTC_EXT_CNTL + 1, 0);	/* turn the video back on */
+
+	info->fix.line_length = (par->crtc.vxres * par->crtc.bpp) >> 3;
+	info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR
+		: FB_VISUAL_DIRECTCOLOR;
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (par->chip_gen == rage_M3) {
+		aty128_set_crt_enable(par, par->crt_on);
+		aty128_set_lcd_enable(par, par->lcd_on);
+	}
+#endif
+	if (par->accel_flags & FB_ACCELF_TEXT)
+		aty128_init_engine(par);
+
+#ifdef CONFIG_BOOTX_TEXT
+	btext_update_display(info->fix.smem_start,
+			     (((par->crtc.h_total>>16) & 0xff)+1)*8,
+			     ((par->crtc.v_total>>16) & 0x7ff)+1,
+			     par->crtc.bpp,
+			     par->crtc.vxres*par->crtc.bpp/8);
+#endif /* CONFIG_BOOTX_TEXT */
+
+	return 0;
+}
+
+/*
+ *  encode/decode the User Defined Part of the Display
+ */
+
+static int aty128_decode_var(struct fb_var_screeninfo *var, struct aty128fb_par *par)
+{
+	int err;
+	struct aty128_crtc crtc;
+	struct aty128_pll pll;
+	struct aty128_ddafifo fifo_reg;
+
+	if ((err = aty128_var_to_crtc(var, &crtc, par)))
+		return err;
+
+	if ((err = aty128_var_to_pll(var->pixclock, &pll, par)))
+		return err;
+
+	if ((err = aty128_ddafifo(&fifo_reg, &pll, crtc.depth, par)))
+		return err;
+
+	par->crtc = crtc;
+	par->pll = pll;
+	par->fifo_reg = fifo_reg;
+	par->accel_flags = var->accel_flags;
+
+	return 0;
+}
+
+
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+			     const struct aty128fb_par *par)
+{
+	int err;
+
+	if ((err = aty128_crtc_to_var(&par->crtc, var)))
+		return err;
+
+	if ((err = aty128_pll_to_var(&par->pll, var)))
+		return err;
+
+	var->nonstd = 0;
+	var->activate = 0;
+
+	var->height = -1;
+	var->width = -1;
+	var->accel_flags = par->accel_flags;
+
+	return 0;
+}           
+
+
+static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct aty128fb_par par;
+	int err;
+
+	par = *(struct aty128fb_par *)info->par;
+	if ((err = aty128_decode_var(var, &par)) != 0)
+		return err;
+	aty128_encode_var(var, &par);
+	return 0;
+}
+
+
+/*
+ *  Pan or Wrap the Display
+ */
+static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb) 
+{
+	struct aty128fb_par *par = fb->par;
+	u32 xoffset, yoffset;
+	u32 offset;
+	u32 xres, yres;
+
+	xres = (((par->crtc.h_total >> 16) & 0xff) + 1) << 3;
+	yres = ((par->crtc.v_total >> 16) & 0x7ff) + 1;
+
+	xoffset = (var->xoffset +7) & ~7;
+	yoffset = var->yoffset;
+
+	if (xoffset+xres > par->crtc.vxres || yoffset+yres > par->crtc.vyres)
+		return -EINVAL;
+
+	par->crtc.xoffset = xoffset;
+	par->crtc.yoffset = yoffset;
+
+	offset = ((yoffset * par->crtc.vxres + xoffset)*(par->crtc.bpp >> 3)) & ~7;
+
+	if (par->crtc.bpp == 24)
+		offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
+
+	aty_st_le32(CRTC_OFFSET, offset);
+
+	return 0;
+}
+
+
+/*
+ *  Helper function to store a single palette register
+ */
+static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
+			  struct aty128fb_par *par)
+{
+	if (par->chip_gen == rage_M3) {
+#if 0
+		/* Note: For now, on M3, we set palette on both heads, which may
+		 * be useless. Can someone with a M3 check this ?
+		 * 
+		 * This code would still be useful if using the second CRTC to 
+		 * do mirroring
+		 */
+
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
+		aty_st_8(PALETTE_INDEX, regno);
+		aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+#endif
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+	}
+
+	aty_st_8(PALETTE_INDEX, regno);
+	aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+}
+
+static int aty128fb_sync(struct fb_info *info)
+{
+	struct aty128fb_par *par = info->par;
+
+	if (par->blitter_may_be_busy)
+		wait_for_idle(par);
+	return 0;
+}
+
+#ifndef MODULE
+static int __init aty128fb_setup(char *options)
+{
+	char *this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+#ifdef CONFIG_PMAC_PBOOK
+		if (!strncmp(this_opt, "lcd:", 4)) {
+			default_lcd_on = simple_strtoul(this_opt+4, NULL, 0);
+			continue;
+		} else if (!strncmp(this_opt, "crt:", 4)) {
+			default_crt_on = simple_strtoul(this_opt+4, NULL, 0);
+			continue;
+		}
+#endif
+#ifdef CONFIG_MTRR
+		if(!strncmp(this_opt, "nomtrr", 6)) {
+			mtrr = 0;
+			continue;
+		}
+#endif
+#ifdef CONFIG_PPC_PMAC
+		/* vmode and cmode deprecated */
+		if (!strncmp(this_opt, "vmode:", 6)) {
+			unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
+			if (vmode > 0 && vmode <= VMODE_MAX)
+				default_vmode = vmode;
+			continue;
+		} else if (!strncmp(this_opt, "cmode:", 6)) {
+			unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0);
+			switch (cmode) {
+			case 0:
+			case 8:
+				default_cmode = CMODE_8;
+				break;
+			case 15:
+			case 16:
+				default_cmode = CMODE_16;
+				break;
+			case 24:
+			case 32:
+				default_cmode = CMODE_32;
+				break;
+			}
+			continue;
+		}
+#endif /* CONFIG_PPC_PMAC */
+		mode_option = this_opt;
+	}
+	return 0;
+}
+#endif  /*  MODULE  */
+
+
+/*
+ *  Initialisation
+ */
+
+#ifdef CONFIG_PPC_PMAC
+static void aty128_early_resume(void *data)
+{
+        struct aty128fb_par *par = data;
+
+	if (try_acquire_console_sem())
+		return;
+	aty128_do_resume(par->pdev);
+	release_console_sem();
+}
+#endif /* CONFIG_PPC_PMAC */
+
+static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct aty128fb_par *par = info->par;
+	struct fb_var_screeninfo var;
+	char video_card[DEVICE_NAME_SIZE];
+	u8 chip_rev;
+	u32 dac;
+
+	if (!par->vram_size)	/* may have already been probed */
+		par->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+
+	/* Get the chip revision */
+	chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F;
+
+	strcpy(video_card, "Rage128 XX ");
+	video_card[8] = ent->device >> 8;
+	video_card[9] = ent->device & 0xFF;
+	    
+	/* range check to make sure */
+	if (ent->driver_data < (sizeof(r128_family)/sizeof(char *)))
+	    strncat(video_card, r128_family[ent->driver_data], sizeof(video_card));
+
+	printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
+
+	if (par->vram_size % (1024 * 1024) == 0)
+		printk("%dM %s\n", par->vram_size / (1024*1024), par->mem->name);
+	else
+		printk("%dk %s\n", par->vram_size / 1024, par->mem->name);
+
+	par->chip_gen = ent->driver_data;
+
+	/* fill in info */
+	info->fbops = &aty128fb_ops;
+	info->flags = FBINFO_FLAG_DEFAULT;
+
+#ifdef CONFIG_PMAC_PBOOK
+	par->lcd_on = default_lcd_on;
+	par->crt_on = default_crt_on;
+#endif
+
+	var = default_var;
+#ifdef CONFIG_PPC_PMAC
+	if (_machine == _MACH_Pmac) {
+		/* Indicate sleep capability */
+		if (par->chip_gen == rage_M3) {
+			pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
+			pmac_set_early_video_resume(aty128_early_resume, par);
+		}
+
+		/* Find default mode */
+		if (mode_option) {
+			if (!mac_find_mode(&var, info, mode_option, 8))
+				var = default_var;
+		} else {
+			if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+				default_vmode = VMODE_1024_768_60;
+
+			/* iMacs need that resolution
+			 * PowerMac2,1 first r128 iMacs
+			 * PowerMac2,2 summer 2000 iMacs
+			 * PowerMac4,1 january 2001 iMacs "flower power"
+			 */
+			if (machine_is_compatible("PowerMac2,1") ||
+			    machine_is_compatible("PowerMac2,2") ||
+			    machine_is_compatible("PowerMac4,1"))
+				default_vmode = VMODE_1024_768_75;
+
+			/* iBook SE */
+			if (machine_is_compatible("PowerBook2,2"))
+				default_vmode = VMODE_800_600_60;
+
+			/* PowerBook Firewire (Pismo), iBook Dual USB */
+			if (machine_is_compatible("PowerBook3,1") ||
+			    machine_is_compatible("PowerBook4,1"))
+				default_vmode = VMODE_1024_768_60;
+
+			/* PowerBook Titanium */
+			if (machine_is_compatible("PowerBook3,2"))
+				default_vmode = VMODE_1152_768_60;
+	
+			if (default_cmode > 16) 
+			    default_cmode = CMODE_32;
+			else if (default_cmode > 8) 
+			    default_cmode = CMODE_16;
+			else 
+			    default_cmode = CMODE_8;
+
+			if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+				var = default_var;
+		}
+	} else
+#endif /* CONFIG_PPC_PMAC */
+	{
+		if (mode_option)
+			if (fb_find_mode(&var, info, mode_option, NULL, 
+					 0, &defaultmode, 8) == 0)
+				var = default_var;
+	}
+
+	var.accel_flags &= ~FB_ACCELF_TEXT;
+//	var.accel_flags |= FB_ACCELF_TEXT;/* FIXME Will add accel later */
+
+	if (aty128fb_check_var(&var, info)) {
+		printk(KERN_ERR "aty128fb: Cannot set default mode.\n");
+		return 0;
+	}
+
+	/* setup the DAC the way we like it */
+	dac = aty_ld_le32(DAC_CNTL);
+	dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL);
+	dac |= DAC_MASK;
+	if (par->chip_gen == rage_M3)
+		dac |= DAC_PALETTE2_SNOOP_EN;
+	aty_st_le32(DAC_CNTL, dac);
+
+	/* turn off bus mastering, just in case */
+	aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_MASTER_DIS);
+
+	info->var = var;
+	fb_alloc_cmap(&info->cmap, 256, 0);
+
+	var.activate = FB_ACTIVATE_NOW;
+
+	aty128_init_engine(par);
+
+	if (register_framebuffer(info) < 0)
+		return 0;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	/* Could be extended to Rage128Pro LVDS output too */
+	if (par->chip_gen == rage_M3)
+		register_backlight_controller(&aty128_backlight_controller, par, "ati");
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+	par->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM);
+	par->pdev = pdev;
+	par->asleep = 0;
+	par->lock_blank = 0;
+	
+	printk(KERN_INFO "fb%d: %s frame buffer device on %s\n",
+	       info->node, info->fix.id, video_card);
+
+	return 1;	/* success! */
+}
+
+#ifdef CONFIG_PCI
+/* register a card    ++ajoshi */
+static int __init aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	unsigned long fb_addr, reg_addr;
+	struct aty128fb_par *par;
+	struct fb_info *info;
+	int err;
+#ifndef __sparc__
+	void __iomem *bios = NULL;
+#endif
+
+	/* Enable device in PCI config */
+	if ((err = pci_enable_device(pdev))) {
+		printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n",
+				err);
+		return -ENODEV;
+	}
+
+	fb_addr = pci_resource_start(pdev, 0);
+	if (!request_mem_region(fb_addr, pci_resource_len(pdev, 0),
+				"aty128fb FB")) {
+		printk(KERN_ERR "aty128fb: cannot reserve frame "
+				"buffer memory\n");
+		return -ENODEV;
+	}
+
+	reg_addr = pci_resource_start(pdev, 2);
+	if (!request_mem_region(reg_addr, pci_resource_len(pdev, 2),
+				"aty128fb MMIO")) {
+		printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n");
+		goto err_free_fb;
+	}
+
+	/* We have the resources. Now virtualize them */
+	info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev);
+	if (info == NULL) {
+		printk(KERN_ERR "aty128fb: can't alloc fb_info_aty128\n");
+		goto err_free_mmio;
+	}
+	par = info->par;
+
+	info->pseudo_palette = par->pseudo_palette;
+	info->fix = aty128fb_fix;
+
+	/* Virtualize mmio region */
+	info->fix.mmio_start = reg_addr;
+	par->regbase = ioremap(reg_addr, pci_resource_len(pdev, 2));
+	if (!par->regbase)
+		goto err_free_info;
+
+	/* Grab memory size from the card */
+	// How does this relate to the resource length from the PCI hardware?
+	par->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
+
+	/* Virtualize the framebuffer */
+	info->screen_base = ioremap(fb_addr, par->vram_size);
+	if (!info->screen_base)
+		goto err_unmap_out;
+
+	/* Set up info->fix */
+	info->fix = aty128fb_fix;
+	info->fix.smem_start = fb_addr;
+	info->fix.smem_len = par->vram_size;
+	info->fix.mmio_start = reg_addr;
+
+	/* If we can't test scratch registers, something is seriously wrong */
+	if (!register_test(par)) {
+		printk(KERN_ERR "aty128fb: Can't write to video register!\n");
+		goto err_out;
+	}
+
+#ifndef __sparc__
+	bios = aty128_map_ROM(par, pdev);
+#ifdef CONFIG_X86
+	if (bios == NULL)
+		bios = aty128_find_mem_vbios(par);
+#endif
+	if (bios == NULL)
+		printk(KERN_INFO "aty128fb: BIOS not located, guessing timings.\n");
+	else {
+		printk(KERN_INFO "aty128fb: Rage128 BIOS located\n");
+		aty128_get_pllinfo(par, bios);
+		pci_unmap_rom(pdev, bios);
+	}
+#endif /* __sparc__ */
+
+	aty128_timings(par);
+	pci_set_drvdata(pdev, info);
+
+	if (!aty128_init(pdev, ent))
+		goto err_out;
+
+#ifdef CONFIG_MTRR
+	if (mtrr) {
+		par->mtrr.vram = mtrr_add(info->fix.smem_start,
+				par->vram_size, MTRR_TYPE_WRCOMB, 1);
+		par->mtrr.vram_valid = 1;
+		/* let there be speed */
+		printk(KERN_INFO "aty128fb: Rage128 MTRR set to ON\n");
+	}
+#endif /* CONFIG_MTRR */
+	return 0;
+
+err_out:
+	iounmap(info->screen_base);
+err_unmap_out:
+	iounmap(par->regbase);
+err_free_info:
+	framebuffer_release(info);
+err_free_mmio:
+	release_mem_region(pci_resource_start(pdev, 2),
+			pci_resource_len(pdev, 2));
+err_free_fb:
+	release_mem_region(pci_resource_start(pdev, 0),
+			pci_resource_len(pdev, 0));
+	return -ENODEV;
+}
+
+static void __devexit aty128_remove(struct pci_dev *pdev)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct aty128fb_par *par;
+
+	if (!info)
+		return;
+
+	par = info->par;
+
+	unregister_framebuffer(info);
+#ifdef CONFIG_MTRR
+	if (par->mtrr.vram_valid)
+		mtrr_del(par->mtrr.vram, info->fix.smem_start,
+			 par->vram_size);
+#endif /* CONFIG_MTRR */
+	iounmap(par->regbase);
+	iounmap(info->screen_base);
+
+	release_mem_region(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+	release_mem_region(pci_resource_start(pdev, 2),
+			   pci_resource_len(pdev, 2));
+	framebuffer_release(info);
+}
+#endif /* CONFIG_PCI */
+
+
+
+    /*
+     *  Blank the display.
+     */
+static int aty128fb_blank(int blank, struct fb_info *fb)
+{
+	struct aty128fb_par *par = fb->par;
+	u8 state = 0;
+
+	if (par->lock_blank || par->asleep)
+		return 0;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if ((_machine == _MACH_Pmac) && blank)
+		set_backlight_enable(0);
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+	if (blank & FB_BLANK_VSYNC_SUSPEND)
+		state |= 2;
+	if (blank & FB_BLANK_HSYNC_SUSPEND)
+		state |= 1;
+	if (blank & FB_BLANK_POWERDOWN)
+		state |= 4;
+
+	aty_st_8(CRTC_EXT_CNTL+1, state);
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (par->chip_gen == rage_M3) {
+		aty128_set_crt_enable(par, par->crt_on && !blank);
+		aty128_set_lcd_enable(par, par->lcd_on && !blank);
+	}
+#endif	
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if ((_machine == _MACH_Pmac) && !blank)
+		set_backlight_enable(1);
+#endif /* CONFIG_PMAC_BACKLIGHT */
+	return 0;
+}
+
+/*
+ *  Set a single color register. The values supplied are already
+ *  rounded down to the hardware's capabilities (according to the
+ *  entries in the var structure). Return != 0 for invalid regno.
+ */
+static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			      u_int transp, struct fb_info *info)
+{
+	struct aty128fb_par *par = info->par;
+
+	if (regno > 255
+	    || (par->crtc.depth == 16 && regno > 63)
+	    || (par->crtc.depth == 15 && regno > 31))
+		return 1;
+
+	red >>= 8;
+	green >>= 8;
+	blue >>= 8;
+
+	if (regno < 16) {
+		int i;
+		u32 *pal = info->pseudo_palette;
+
+		switch (par->crtc.depth) {
+		case 15:
+			pal[regno] = (regno << 10) | (regno << 5) | regno;
+			break;
+		case 16:
+			pal[regno] = (regno << 11) | (regno << 6) | regno;
+			break;
+		case 24:
+			pal[regno] = (regno << 16) | (regno << 8) | regno;
+			break;
+		case 32:
+			i = (regno << 8) | regno;
+			pal[regno] = (i << 16) | i;
+			break;
+		}
+	}
+
+	if (par->crtc.depth == 16 && regno > 0) {
+		/*
+		 * With the 5-6-5 split of bits for RGB at 16 bits/pixel, we
+		 * have 32 slots for R and B values but 64 slots for G values.
+		 * Thus the R and B values go in one slot but the G value
+		 * goes in a different slot, and we have to avoid disturbing
+		 * the other fields in the slots we touch.
+		 */
+		par->green[regno] = green;
+		if (regno < 32) {
+			par->red[regno] = red;
+			par->blue[regno] = blue;
+			aty128_st_pal(regno * 8, red, par->green[regno*2],
+				      blue, par);
+		}
+		red = par->red[regno/2];
+		blue = par->blue[regno/2];
+		regno <<= 2;
+	} else if (par->crtc.bpp == 16)
+		regno <<= 3;
+	aty128_st_pal(regno, red, green, blue, par);
+
+	return 0;
+}
+
+#define ATY_MIRROR_LCD_ON	0x00000001
+#define ATY_MIRROR_CRT_ON	0x00000002
+
+/* out param: u32*	backlight value: 0 to 15 */
+#define FBIO_ATY128_GET_MIRROR	_IOR('@', 1, __u32)
+/* in param: u32*	backlight value: 0 to 15 */
+#define FBIO_ATY128_SET_MIRROR	_IOW('@', 2, __u32)
+
+static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			  u_long arg, struct fb_info *info)
+{
+#ifdef CONFIG_PMAC_PBOOK
+	struct aty128fb_par *par = info->par;
+	u32 value;
+	int rc;
+    
+	switch (cmd) {
+	case FBIO_ATY128_SET_MIRROR:
+		if (par->chip_gen != rage_M3)
+			return -EINVAL;
+		rc = get_user(value, (__u32 __user *)arg);
+		if (rc)
+			return rc;
+		par->lcd_on = (value & 0x01) != 0;
+		par->crt_on = (value & 0x02) != 0;
+		if (!par->crt_on && !par->lcd_on)
+			par->lcd_on = 1;
+		aty128_set_crt_enable(par, par->crt_on);	
+		aty128_set_lcd_enable(par, par->lcd_on);	
+		return 0;
+	case FBIO_ATY128_GET_MIRROR:
+		if (par->chip_gen != rage_M3)
+			return -EINVAL;
+		value = (par->crt_on << 1) | par->lcd_on;
+		return put_user(value, (__u32 __user *)arg);
+	}
+#endif
+	return -EINVAL;
+}
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int backlight_conv[] = {
+	0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
+	0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
+};
+
+/* We turn off the LCD completely instead of just dimming the backlight.
+ * This provides greater power saving and the display is useless without
+ * backlight anyway
+ */
+#define BACKLIGHT_LVDS_OFF
+/* That one prevents proper CRT output with LCD off */
+#undef BACKLIGHT_DAC_OFF
+
+static int aty128_set_backlight_enable(int on, int level, void *data)
+{
+	struct aty128fb_par *par = data;
+	unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
+
+	if (!par->lcd_on)
+		on = 0;
+	reg |= LVDS_BL_MOD_EN | LVDS_BLON;
+	if (on && level > BACKLIGHT_OFF) {
+		reg |= LVDS_DIGION;
+		if (!(reg & LVDS_ON)) {
+			reg &= ~LVDS_BLON;
+			aty_st_le32(LVDS_GEN_CNTL, reg);
+			(void)aty_ld_le32(LVDS_GEN_CNTL);
+			mdelay(10);
+			reg |= LVDS_BLON;
+			aty_st_le32(LVDS_GEN_CNTL, reg);
+		}
+		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
+		reg |= (backlight_conv[level] << LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+		reg |= LVDS_ON | LVDS_EN;
+		reg &= ~LVDS_DISPLAY_DIS;
+#endif
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
+#endif		
+	} else {
+		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
+		reg |= (backlight_conv[0] << LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+		reg |= LVDS_DISPLAY_DIS;
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+		(void)aty_ld_le32(LVDS_GEN_CNTL);
+		udelay(10);
+		reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
+#endif		
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
+#endif		
+	}
+
+	return 0;
+}
+
+static int aty128_set_backlight_level(int level, void* data)
+{
+	return aty128_set_backlight_enable(1, level, data);
+}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+#if 0
+    /*
+     *  Accelerated functions
+     */
+
+static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
+				   u_int width, u_int height,
+				   struct fb_info_aty128 *par)
+{
+    u32 save_dp_datatype, save_dp_cntl, dstval;
+
+    if (!width || !height)
+        return;
+
+    dstval = depth_to_dst(par->current_par.crtc.depth);
+    if (dstval == DST_24BPP) {
+        srcx *= 3;
+        dstx *= 3;
+        width *= 3;
+    } else if (dstval == -EINVAL) {
+        printk("aty128fb: invalid depth or RGBA\n");
+        return;
+    }
+
+    wait_for_fifo(2, par);
+    save_dp_datatype = aty_ld_le32(DP_DATATYPE);
+    save_dp_cntl     = aty_ld_le32(DP_CNTL);
+
+    wait_for_fifo(6, par);
+    aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
+    aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
+    aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
+    aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
+
+    aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
+    aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
+
+    par->blitter_may_be_busy = 1;
+
+    wait_for_fifo(2, par);
+    aty_st_le32(DP_DATATYPE, save_dp_datatype);
+    aty_st_le32(DP_CNTL, save_dp_cntl); 
+}
+
+
+    /*
+     * Text mode accelerated functions
+     */
+
+static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			int height, int width)
+{
+    sx     *= fontwidth(p);
+    sy     *= fontheight(p);
+    dx     *= fontwidth(p);
+    dy     *= fontheight(p);
+    width  *= fontwidth(p);
+    height *= fontheight(p);
+
+    aty128_rectcopy(sx, sy, dx, dy, width, height,
+			(struct fb_info_aty128 *)p->fb_info);
+}
+#endif /* 0 */
+
+static void aty128_set_suspend(struct aty128fb_par *par, int suspend)
+{
+	u32	pmgt;
+	u16	pwr_command;
+	struct pci_dev *pdev = par->pdev;
+
+	if (!par->pm_reg)
+		return;
+		
+	/* Set the chip into the appropriate suspend mode (we use D2,
+	 * D3 would require a complete re-initialisation of the chip,
+	 * including PCI config registers, clocks, AGP configuration, ...)
+	 */
+	if (suspend) {
+		/* Make sure CRTC2 is reset. Remove that the day we decide to
+		 * actually use CRTC2 and replace it with real code for disabling
+		 * the CRTC2 output during sleep
+		 */
+		aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) &
+			~(CRTC2_EN));
+
+		/* Set the power management mode to be PCI based */
+		/* Use this magic value for now */
+		pmgt = 0x0c005407;
+		aty_st_pll(POWER_MANAGEMENT, pmgt);
+		(void)aty_ld_pll(POWER_MANAGEMENT);
+		aty_st_le32(BUS_CNTL1, 0x00000010);
+		aty_st_le32(MEM_POWER_MISC, 0x0c830000);
+		mdelay(100);
+		pci_read_config_word(pdev, par->pm_reg+PCI_PM_CTRL, &pwr_command);
+		/* Switch PCI power management to D2 */
+		pci_write_config_word(pdev, par->pm_reg+PCI_PM_CTRL,
+			(pwr_command & ~PCI_PM_CTRL_STATE_MASK) | 2);
+		pci_read_config_word(pdev, par->pm_reg+PCI_PM_CTRL, &pwr_command);
+	} else {
+		/* Switch back PCI power management to D0 */
+		mdelay(100);
+		pci_write_config_word(pdev, par->pm_reg+PCI_PM_CTRL, 0);
+		pci_read_config_word(pdev, par->pm_reg+PCI_PM_CTRL, &pwr_command);
+		mdelay(100);
+	}
+}
+
+static int aty128_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct aty128fb_par *par = info->par;
+	u8 agp;
+
+	/* We don't do anything but D2, for now we return 0, but
+	 * we may want to change that. How do we know if the BIOS
+	 * can properly take care of D3 ? Also, with swsusp, we
+	 * know we'll be rebooted, ...
+	 */
+#ifdef CONFIG_PPC_PMAC
+	/* HACK ALERT ! Once I find a proper way to say to each driver
+	 * individually what will happen with it's PCI slot, I'll change
+	 * that. On laptops, the AGP slot is just unclocked, so D2 is
+	 * expected, while on desktops, the card is powered off
+	 */
+	if (state >= 3)
+		state = 2;
+#endif /* CONFIG_PPC_PMAC */
+	 
+	if (state != 2 || state == pdev->dev.power.power_state)
+		return 0;
+
+	printk(KERN_DEBUG "aty128fb: suspending...\n");
+	
+	acquire_console_sem();
+
+	fb_set_suspend(info, 1);
+
+	/* Make sure engine is reset */
+	wait_for_idle(par);
+	aty128_reset_engine(par);
+	wait_for_idle(par);
+
+	/* Blank display and LCD */
+	aty128fb_blank(VESA_POWERDOWN, info);
+
+	/* Sleep */
+	par->asleep = 1;
+	par->lock_blank = 1;
+
+	/* Disable AGP. The AGP host should have done it, but since ordering
+	 * isn't always properly guaranteed in this specific case, let's make
+	 * sure it's disabled on card side now. Ultimately, when merging fbdev
+	 * and dri into some common infrastructure, this will be handled
+	 * more nicely. The host bridge side will (or will not) be dealt with
+	 * by the bridge AGP driver, we don't attempt to touch it here.
+	 */
+	agp = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+	if (agp) {
+		u32 cmd;
+
+		pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd);
+		if (cmd & PCI_AGP_COMMAND_AGP) {
+			printk(KERN_INFO "aty128fb: AGP was enabled, "
+			       "disabling ...\n");
+			cmd &= ~PCI_AGP_COMMAND_AGP;
+			pci_write_config_dword(pdev, agp + PCI_AGP_COMMAND,
+					       cmd);
+		}
+	}
+
+	/* We need a way to make sure the fbdev layer will _not_ touch the
+	 * framebuffer before we put the chip to suspend state. On 2.4, I
+	 * used dummy fb ops, 2.5 need proper support for this at the
+	 * fbdev level
+	 */
+	if (state == 2)
+		aty128_set_suspend(par, 1);
+
+	release_console_sem();
+
+	pdev->dev.power.power_state = state;
+
+	return 0;
+}
+
+static int aty128_do_resume(struct pci_dev *pdev)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct aty128fb_par *par = info->par;
+
+	if (pdev->dev.power.power_state == 0)
+		return 0;
+
+	/* Wakeup chip */
+	if (pdev->dev.power.power_state == 2)
+		aty128_set_suspend(par, 0);
+	par->asleep = 0;
+
+	/* Restore display & engine */
+	aty128_reset_engine(par);
+	wait_for_idle(par);
+	aty128fb_set_par(info);
+	fb_pan_display(info, &info->var);
+	fb_set_cmap(&info->cmap, info);
+
+	/* Refresh */
+	fb_set_suspend(info, 0);
+
+	/* Unblank */
+	par->lock_blank = 0;
+	aty128fb_blank(0, info);
+
+	pdev->dev.power.power_state = PMSG_ON;
+
+	printk(KERN_DEBUG "aty128fb: resumed !\n");
+
+	return 0;
+}
+
+static int aty128_pci_resume(struct pci_dev *pdev)
+{
+	int rc;
+
+	acquire_console_sem();
+	rc = aty128_do_resume(pdev);
+	release_console_sem();
+
+	return rc;
+}
+
+
+static int __init aty128fb_init(void)
+{
+#ifndef MODULE
+	char *option = NULL;
+
+	if (fb_get_options("aty128fb", &option))
+		return -ENODEV;
+	aty128fb_setup(option);
+#endif
+
+	return pci_register_driver(&aty128fb_driver);
+}
+
+static void __exit aty128fb_exit(void)
+{
+	pci_unregister_driver(&aty128fb_driver);
+}
+
+module_init(aty128fb_init);
+
+module_exit(aty128fb_exit);
+
+MODULE_AUTHOR("(c)1999-2003 Brad Douglas <brad@neruo.com>");
+MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
+MODULE_LICENSE("GPL");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
+#ifdef CONFIG_MTRR
+module_param_named(nomtrr, mtrr, invbool, 0);
+MODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)");
+#endif
+
diff --git a/drivers/video/aty/atyfb.h b/drivers/video/aty/atyfb.h
new file mode 100644
index 0000000..09de173
--- /dev/null
+++ b/drivers/video/aty/atyfb.h
@@ -0,0 +1,359 @@
+/*
+ *  ATI Frame Buffer Device Driver Core Definitions
+ */
+
+#include <linux/config.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+    /*
+     *  Elements of the hardware specific atyfb_par structure
+     */
+
+struct crtc {
+	u32 vxres;
+	u32 vyres;
+	u32 xoffset;
+	u32 yoffset;
+	u32 bpp;
+	u32 h_tot_disp;
+	u32 h_sync_strt_wid;
+	u32 v_tot_disp;
+	u32 v_sync_strt_wid;
+	u32 vline_crnt_vline;
+	u32 off_pitch;
+	u32 gen_cntl;
+	u32 dp_pix_width;	/* acceleration */
+	u32 dp_chain_mask;	/* acceleration */
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	u32 horz_stretching;
+	u32 vert_stretching;
+	u32 ext_vert_stretch;
+	u32 shadow_h_tot_disp;
+	u32 shadow_h_sync_strt_wid;
+	u32 shadow_v_tot_disp;
+	u32 shadow_v_sync_strt_wid;
+	u32 lcd_gen_cntl;
+	u32 lcd_config_panel;
+	u32 lcd_index;
+#endif
+};
+
+struct aty_interrupt {
+	wait_queue_head_t wait;
+	unsigned int count;
+	int pan_display;
+};
+
+struct pll_info {
+	int pll_max;
+	int pll_min;
+	int sclk, mclk, mclk_pm, xclk;
+	int ref_div;
+	int ref_clk;
+};
+
+typedef struct {
+	u16 unknown1;
+	u16 PCLK_min_freq;
+	u16 PCLK_max_freq;
+	u16 unknown2;
+	u16 ref_freq;
+	u16 ref_divider;
+	u16 unknown3;
+	u16 MCLK_pwd;
+	u16 MCLK_max_freq;
+	u16 XCLK_max_freq;
+	u16 SCLK_freq;
+} __attribute__ ((packed)) PLL_BLOCK_MACH64;
+
+struct pll_514 {
+	u8 m;
+	u8 n;
+};
+
+struct pll_18818 {
+	u32 program_bits;
+	u32 locationAddr;
+	u32 period_in_ps;
+	u32 post_divider;
+};
+
+struct pll_ct {
+	u8 pll_ref_div;
+	u8 pll_gen_cntl;
+	u8 mclk_fb_div;
+	u8 mclk_fb_mult; /* 2 ro 4 */
+	u8 sclk_fb_div;
+	u8 pll_vclk_cntl;
+	u8 vclk_post_div;
+	u8 vclk_fb_div;
+	u8 pll_ext_cntl;
+	u8 ext_vpll_cntl;
+	u8 spll_cntl2;
+	u32 dsp_config; /* Mach64 GTB DSP */
+	u32 dsp_on_off; /* Mach64 GTB DSP */
+	u32 dsp_loop_latency;
+	u32 fifo_size;
+	u32 xclkpagefaultdelay;
+	u32 xclkmaxrasdelay;
+	u8 xclk_ref_div;
+	u8 xclk_post_div;
+	u8 mclk_post_div_real;
+	u8 xclk_post_div_real;
+	u8 vclk_post_div_real;
+	u8 features;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	u32 xres; /* use for LCD stretching/scaling */
+#endif
+};
+
+/*
+	for pll_ct.features
+*/
+#define DONT_USE_SPLL 0x1
+#define DONT_USE_XDLL 0x2
+#define USE_CPUCLK    0x4
+#define POWERDOWN_PLL 0x8
+
+union aty_pll {
+	struct pll_ct ct;
+	struct pll_514 ibm514;
+	struct pll_18818 ics2595;
+};
+
+    /*
+     *  The hardware parameters for each card
+     */
+
+struct atyfb_par {
+	struct aty_cmap_regs __iomem *aty_cmap_regs;
+	struct { u8 red, green, blue; } palette[256];
+	const struct aty_dac_ops *dac_ops;
+	const struct aty_pll_ops *pll_ops;
+	void __iomem *ati_regbase;
+	unsigned long clk_wr_offset; /* meaning overloaded, clock id by CT */
+	struct crtc crtc;
+	union aty_pll pll;
+	struct pll_info pll_limits;
+	u32 features;
+	u32 ref_clk_per;
+	u32 pll_per;
+	u32 mclk_per;
+	u32 xclk_per;
+	u8 bus_type;
+	u8 ram_type;
+	u8 mem_refresh_rate;
+	u16 pci_id;
+	u32 accel_flags;
+	int blitter_may_be_busy;
+	int asleep;
+	int lock_blank;
+	unsigned long res_start;
+	unsigned long res_size;
+#ifdef __sparc__
+	struct pci_mmap_map *mmap_map;
+	u8 mmaped;
+#endif
+	int open;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	unsigned long bios_base_phys;
+	unsigned long bios_base;
+	unsigned long lcd_table;
+	u16 lcd_width;
+	u16 lcd_height;
+	u32 lcd_pixclock;
+	u16 lcd_refreshrate;
+	u16 lcd_htotal;
+	u16 lcd_hdisp;
+	u16 lcd_hsync_dly;
+	u16 lcd_hsync_len;
+	u16 lcd_vtotal;
+	u16 lcd_vdisp;
+	u16 lcd_vsync_len;
+	u16 lcd_right_margin;
+	u16 lcd_lower_margin;
+	u16 lcd_hblank_len;
+	u16 lcd_vblank_len;
+#endif
+	unsigned long aux_start; /* auxiliary aperture */
+	unsigned long aux_size;
+	struct aty_interrupt vblank;
+	unsigned long irq_flags;
+	unsigned int irq;
+	spinlock_t int_lock;
+#ifdef CONFIG_MTRR
+	int mtrr_aper;
+	int mtrr_reg;
+#endif
+};
+
+    /*
+     *  ATI Mach64 features
+     */
+
+#define M64_HAS(feature)	((par)->features & (M64F_##feature))
+
+#define M64F_RESET_3D		0x00000001
+#define M64F_MAGIC_FIFO		0x00000002
+#define M64F_GTB_DSP		0x00000004
+#define M64F_FIFO_32		0x00000008
+#define M64F_SDRAM_MAGIC_PLL	0x00000010
+#define M64F_MAGIC_POSTDIV	0x00000020
+#define M64F_INTEGRATED		0x00000040
+#define M64F_CT_BUS		0x00000080
+#define M64F_VT_BUS		0x00000100
+#define M64F_MOBIL_BUS		0x00000200
+#define M64F_GX			0x00000400
+#define M64F_CT			0x00000800
+#define M64F_VT			0x00001000
+#define M64F_GT			0x00002000
+#define M64F_MAGIC_VRAM_SIZE	0x00004000
+#define M64F_G3_PB_1_1		0x00008000
+#define M64F_G3_PB_1024x768	0x00010000
+#define M64F_EXTRA_BRIGHT	0x00020000
+#define M64F_LT_LCD_REGS	0x00040000
+#define M64F_XL_DLL		0x00080000
+#define M64F_MFB_FORCE_4	0x00100000
+#define M64F_HW_TRIPLE		0x00200000
+    /*
+     *  Register access
+     */
+
+static inline u32 aty_ld_le32(int regindex, const struct atyfb_par *par)
+{
+	/* Hack for bloc 1, should be cleanly optimized by compiler */
+	if (regindex >= 0x400)
+		regindex -= 0x800;
+
+#ifdef CONFIG_ATARI
+	return in_le32((volatile u32 *)(par->ati_regbase + regindex));
+#else
+	return readl(par->ati_regbase + regindex);
+#endif
+}
+
+static inline void aty_st_le32(int regindex, u32 val, const struct atyfb_par *par)
+{
+	/* Hack for bloc 1, should be cleanly optimized by compiler */
+	if (regindex >= 0x400)
+		regindex -= 0x800;
+
+#ifdef CONFIG_ATARI
+	out_le32((volatile u32 *)(par->ati_regbase + regindex), val);
+#else
+	writel(val, par->ati_regbase + regindex);
+#endif
+}
+
+static inline void aty_st_le16(int regindex, u16 val,
+			       const struct atyfb_par *par)
+{
+	/* Hack for bloc 1, should be cleanly optimized by compiler */
+	if (regindex >= 0x400)
+		regindex -= 0x800;
+#ifdef CONFIG_ATARI
+	out_le16((volatile u16 *)(par->ati_regbase + regindex), val);
+#else
+	writel(val, par->ati_regbase + regindex);
+#endif
+}
+
+static inline u8 aty_ld_8(int regindex, const struct atyfb_par *par)
+{
+	/* Hack for bloc 1, should be cleanly optimized by compiler */
+	if (regindex >= 0x400)
+		regindex -= 0x800;
+#ifdef CONFIG_ATARI
+	return in_8(par->ati_regbase + regindex);
+#else
+	return readb(par->ati_regbase + regindex);
+#endif
+}
+
+static inline void aty_st_8(int regindex, u8 val, const struct atyfb_par *par)
+{
+	/* Hack for bloc 1, should be cleanly optimized by compiler */
+	if (regindex >= 0x400)
+		regindex -= 0x800;
+
+#ifdef CONFIG_ATARI
+	out_8(par->ati_regbase + regindex, val);
+#else
+	writeb(val, par->ati_regbase + regindex);
+#endif
+}
+
+#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD)
+extern void aty_st_lcd(int index, u32 val, const struct atyfb_par *par);
+extern u32 aty_ld_lcd(int index, const struct atyfb_par *par);
+#endif
+
+    /*
+     *  DAC operations
+     */
+
+struct aty_dac_ops {
+	int (*set_dac) (const struct fb_info * info,
+		const union aty_pll * pll, u32 bpp, u32 accel);
+};
+
+extern const struct aty_dac_ops aty_dac_ibm514; /* IBM RGB514 */
+extern const struct aty_dac_ops aty_dac_ati68860b; /* ATI 68860-B */
+extern const struct aty_dac_ops aty_dac_att21c498; /* AT&T 21C498 */
+extern const struct aty_dac_ops aty_dac_unsupported; /* unsupported */
+extern const struct aty_dac_ops aty_dac_ct; /* Integrated */
+
+
+    /*
+     *  Clock operations
+     */
+
+struct aty_pll_ops {
+	int (*var_to_pll) (const struct fb_info * info, u32 vclk_per, u32 bpp, union aty_pll * pll);
+	u32 (*pll_to_var) (const struct fb_info * info, const union aty_pll * pll);
+	void (*set_pll)   (const struct fb_info * info, const union aty_pll * pll);
+	void (*get_pll)   (const struct fb_info *info, union aty_pll * pll);
+	int (*init_pll)   (const struct fb_info * info, union aty_pll * pll);
+};
+
+extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */
+extern const struct aty_pll_ops aty_pll_stg1703; /* STG 1703 */
+extern const struct aty_pll_ops aty_pll_ch8398; /* Chrontel 8398 */
+extern const struct aty_pll_ops aty_pll_att20c408; /* AT&T 20C408 */
+extern const struct aty_pll_ops aty_pll_ibm514; /* IBM RGB514 */
+extern const struct aty_pll_ops aty_pll_unsupported; /* unsupported */
+extern const struct aty_pll_ops aty_pll_ct; /* Integrated */
+
+
+extern void aty_set_pll_ct(const struct fb_info *info, const union aty_pll *pll);
+extern u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par);
+
+
+    /*
+     *  Hardware cursor support
+     */
+
+extern int aty_init_cursor(struct fb_info *info);
+
+    /*
+     *  Hardware acceleration
+     */
+
+static inline void wait_for_fifo(u16 entries, const struct atyfb_par *par)
+{
+	while ((aty_ld_le32(FIFO_STAT, par) & 0xffff) >
+	       ((u32) (0x8000 >> entries)));
+}
+
+static inline void wait_for_idle(struct atyfb_par *par)
+{
+	wait_for_fifo(16, par);
+	while ((aty_ld_le32(GUI_STAT, par) & 1) != 0);
+	par->blitter_may_be_busy = 0;
+}
+
+extern void aty_reset_engine(const struct atyfb_par *par);
+extern void aty_init_engine(struct atyfb_par *par, struct fb_info *info);
+extern int  atyfb_xl_init(struct fb_info *info);
+extern void aty_st_pll_ct(int offset, u8 val, const struct atyfb_par *par);
+extern u8   aty_ld_pll_ct(int offset, const struct atyfb_par *par);
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
new file mode 100644
index 0000000..8c42538
--- /dev/null
+++ b/drivers/video/aty/atyfb_base.c
@@ -0,0 +1,3720 @@
+/*
+ *  ATI Frame Buffer Device Driver Core
+ *
+ *	Copyright (C) 2004  Alex Kern <alex.kern@gmx.de>
+ *	Copyright (C) 1997-2001  Geert Uytterhoeven
+ *	Copyright (C) 1998  Bernd Harries
+ *	Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
+ *
+ *  This driver supports the following ATI graphics chips:
+ *    - ATI Mach64
+ *
+ *  To do: add support for
+ *    - ATI Rage128 (from aty128fb.c)
+ *    - ATI Radeon (from radeonfb.c)
+ *
+ *  This driver is partly based on the PowerMac console driver:
+ *
+ *	Copyright (C) 1996 Paul Mackerras
+ *
+ *  and on the PowerMac ATI/mach64 display driver:
+ *
+ *	Copyright (C) 1997 Michael AK Tesch
+ *
+ *	      with work by Jon Howell
+ *			   Harry AC Eaton
+ *			   Anthony Tong <atong@uiuc.edu>
+ *
+ *  Generic LCD support written by Daniel Mantione, ported from 2.4.20 by Alex Kern
+ *  Many Thanks to Ville Syrjälä for patches and fixing nasting 16 bit color bug.
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ *
+ *  Many thanks to Nitya from ATI devrel for support and patience !
+ */
+
+/******************************************************************************
+
+  TODO:
+
+    - cursor support on all cards and all ramdacs.
+    - cursor parameters controlable via ioctl()s.
+    - guess PLL and MCLK based on the original PLL register values initialized
+      by Open Firmware (if they are initialized). BIOS is done
+
+    (Anyone with Mac to help with this?)
+
+******************************************************************************/
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <video/mach64.h>
+#include "atyfb.h"
+#include "ati_ids.h"
+
+#ifdef __powerpc__
+#include <asm/prom.h>
+#include "../macmodes.h"
+#endif
+#ifdef __sparc__
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#endif
+
+#ifdef CONFIG_ADB_PMU
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#endif
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+/*
+ * Debug flags.
+ */
+#undef DEBUG
+/*#define DEBUG*/
+
+/* Make sure n * PAGE_SIZE is protected at end of Aperture for GUI-regs */
+/*  - must be large enough to catch all GUI-Regs   */
+/*  - must be aligned to a PAGE boundary           */
+#define GUI_RESERVE	(1 * PAGE_SIZE)
+
+/* FIXME: remove the FAIL definition */
+#define FAIL(msg) do { printk(KERN_CRIT "atyfb: " msg "\n"); return -EINVAL; } while (0)
+#define FAIL_MAX(msg, x, _max_) do { if(x > _max_) { printk(KERN_CRIT "atyfb: " msg " %x(%x)\n", x, _max_); return -EINVAL; } } while (0)
+
+#ifdef DEBUG
+#define DPRINTK(fmt, args...)	printk(KERN_DEBUG "atyfb: " fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define PRINTKI(fmt, args...)	printk(KERN_INFO "atyfb: " fmt, ## args)
+#define PRINTKE(fmt, args...)	 printk(KERN_ERR "atyfb: " fmt, ## args)
+
+#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD)
+static const u32 lt_lcd_regs[] = {
+	CONFIG_PANEL_LG,
+	LCD_GEN_CNTL_LG,
+	DSTN_CONTROL_LG,
+	HFB_PITCH_ADDR_LG,
+	HORZ_STRETCHING_LG,
+	VERT_STRETCHING_LG,
+	0, /* EXT_VERT_STRETCH */
+	LT_GIO_LG,
+	POWER_MANAGEMENT_LG
+};
+
+void aty_st_lcd(int index, u32 val, const struct atyfb_par *par)
+{
+	if (M64_HAS(LT_LCD_REGS)) {
+		aty_st_le32(lt_lcd_regs[index], val, par);
+	} else {
+		unsigned long temp;
+
+		/* write addr byte */
+		temp = aty_ld_le32(LCD_INDEX, par);
+		aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
+		/* write the register value */
+		aty_st_le32(LCD_DATA, val, par);
+	}
+}
+
+u32 aty_ld_lcd(int index, const struct atyfb_par *par)
+{
+	if (M64_HAS(LT_LCD_REGS)) {
+		return aty_ld_le32(lt_lcd_regs[index], par);
+	} else {
+		unsigned long temp;
+
+		/* write addr byte */
+		temp = aty_ld_le32(LCD_INDEX, par);
+		aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
+		/* read the register value */
+		return aty_ld_le32(LCD_DATA, par);
+	}
+}
+#endif /* defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) */
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+/*
+ * ATIReduceRatio --
+ *
+ * Reduce a fraction by factoring out the largest common divider of the
+ * fraction's numerator and denominator.
+ */
+static void ATIReduceRatio(int *Numerator, int *Denominator)
+{
+    int Multiplier, Divider, Remainder;
+
+    Multiplier = *Numerator;
+    Divider = *Denominator;
+
+    while ((Remainder = Multiplier % Divider))
+    {
+        Multiplier = Divider;
+        Divider = Remainder;
+    }
+
+    *Numerator /= Divider;
+    *Denominator /= Divider;
+}
+#endif
+    /*
+     *  The Hardware parameters for each card
+     */
+
+struct aty_cmap_regs {
+	u8 windex;
+	u8 lut;
+	u8 mask;
+	u8 rindex;
+	u8 cntl;
+};
+
+struct pci_mmap_map {
+	unsigned long voff;
+	unsigned long poff;
+	unsigned long size;
+	unsigned long prot_flag;
+	unsigned long prot_mask;
+};
+
+static struct fb_fix_screeninfo atyfb_fix __devinitdata = {
+	.id		= "ATY Mach64",
+	.type		= FB_TYPE_PACKED_PIXELS,
+	.visual		= FB_VISUAL_PSEUDOCOLOR,
+	.xpanstep	= 8,
+	.ypanstep	= 1,
+};
+
+    /*
+     *  Frame buffer device API
+     */
+
+static int atyfb_open(struct fb_info *info, int user);
+static int atyfb_release(struct fb_info *info, int user);
+static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+static int atyfb_set_par(struct fb_info *info);
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+	u_int transp, struct fb_info *info);
+static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
+static int atyfb_blank(int blank, struct fb_info *info);
+static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+	u_long arg, struct fb_info *info);
+extern void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
+extern void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+extern void atyfb_imageblit(struct fb_info *info, const struct fb_image *image);
+#ifdef __sparc__
+static int atyfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma);
+#endif
+static int atyfb_sync(struct fb_info *info);
+
+    /*
+     *  Internal routines
+     */
+
+static int aty_init(struct fb_info *info, const char *name);
+#ifdef CONFIG_ATARI
+static int store_video_par(char *videopar, unsigned char m64_num);
+#endif
+
+static struct crtc saved_crtc;
+static union aty_pll saved_pll;
+static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc);
+
+static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc);
+static int aty_var_to_crtc(const struct fb_info *info, const struct fb_var_screeninfo *var, struct crtc *crtc);
+static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *var);
+static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info);
+#ifdef CONFIG_PPC
+static int read_aty_sense(const struct atyfb_par *par);
+#endif
+
+
+    /*
+     *  Interface used by the world
+     */
+
+static struct fb_var_screeninfo default_var = {
+	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
+	640, 480, 640, 480, 0, 0, 8, 0,
+	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
+	0, FB_VMODE_NONINTERLACED
+};
+
+static struct fb_videomode defmode = {
+	/* 640x480 @ 60 Hz, 31.5 kHz hsync */
+	NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
+	0, FB_VMODE_NONINTERLACED
+};
+
+static struct fb_ops atyfb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_open	= atyfb_open,
+	.fb_release	= atyfb_release,
+	.fb_check_var	= atyfb_check_var,
+	.fb_set_par	= atyfb_set_par,
+	.fb_setcolreg	= atyfb_setcolreg,
+	.fb_pan_display	= atyfb_pan_display,
+	.fb_blank	= atyfb_blank,
+	.fb_ioctl	= atyfb_ioctl,
+	.fb_fillrect	= atyfb_fillrect,
+	.fb_copyarea	= atyfb_copyarea,
+	.fb_imageblit	= atyfb_imageblit,
+	.fb_cursor	= soft_cursor,
+#ifdef __sparc__
+	.fb_mmap	= atyfb_mmap,
+#endif
+	.fb_sync	= atyfb_sync,
+};
+
+static int noaccel;
+#ifdef CONFIG_MTRR
+static int nomtrr;
+#endif
+static int vram;
+static int pll;
+static int mclk;
+static int xclk;
+static int comp_sync __initdata = -1;
+static char *mode;
+
+#ifdef CONFIG_PPC
+static int default_vmode __initdata = VMODE_CHOOSE;
+static int default_cmode __initdata = CMODE_CHOOSE;
+
+module_param_named(vmode, default_vmode, int, 0);
+MODULE_PARM_DESC(vmode, "int: video mode for mac");
+module_param_named(cmode, default_cmode, int, 0);
+MODULE_PARM_DESC(cmode, "int: color mode for mac");
+#endif
+
+#ifdef CONFIG_ATARI
+static unsigned int mach64_count __initdata = 0;
+static unsigned long phys_vmembase[FB_MAX] __initdata = { 0, };
+static unsigned long phys_size[FB_MAX] __initdata = { 0, };
+static unsigned long phys_guiregbase[FB_MAX] __initdata = { 0, };
+#endif
+
+/* top -> down is an evolution of mach64 chipset, any corrections? */
+#define ATI_CHIP_88800GX   (M64F_GX)
+#define ATI_CHIP_88800CX   (M64F_GX)
+
+#define ATI_CHIP_264CT     (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
+#define ATI_CHIP_264ET     (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
+
+#define ATI_CHIP_264VT     (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_MAGIC_FIFO)
+#define ATI_CHIP_264GT     (M64F_GT | M64F_INTEGRATED               | M64F_MAGIC_FIFO | M64F_EXTRA_BRIGHT)
+
+#define ATI_CHIP_264VTB    (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP)
+#define ATI_CHIP_264VT3    (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL)
+#define ATI_CHIP_264VT4    (M64F_VT | M64F_INTEGRATED               | M64F_GTB_DSP)
+
+#define ATI_CHIP_264LT     (M64F_GT | M64F_INTEGRATED               | M64F_GTB_DSP)
+
+/* make sets shorter */
+#define ATI_MODERN_SET     (M64F_GT | M64F_INTEGRATED               | M64F_GTB_DSP | M64F_EXTRA_BRIGHT)
+
+#define ATI_CHIP_264GTB    (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
+/*#define ATI_CHIP_264GTDVD  ?*/
+#define ATI_CHIP_264LTG    (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
+
+#define ATI_CHIP_264GT2C   (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE)
+#define ATI_CHIP_264GTPRO  (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
+#define ATI_CHIP_264LTPRO  (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
+
+#define ATI_CHIP_264XL     (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4)
+#define ATI_CHIP_MOBILITY  (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_MOBIL_BUS)
+
+static struct {
+	u16 pci_id;
+	const char *name;
+	int pll, mclk, xclk;
+	u32 features;
+} aty_chips[] __devinitdata = {
+#ifdef CONFIG_FB_ATY_GX
+	/* Mach64 GX */
+	{ PCI_CHIP_MACH64GX, "ATI888GX00 (Mach64 GX)", 135, 50, 50, ATI_CHIP_88800GX },
+	{ PCI_CHIP_MACH64CX, "ATI888CX00 (Mach64 CX)", 135, 50, 50, ATI_CHIP_88800CX },
+#endif /* CONFIG_FB_ATY_GX */
+
+#ifdef CONFIG_FB_ATY_CT
+	{ PCI_CHIP_MACH64CT, "ATI264CT (Mach64 CT)", 135, 60, 60, ATI_CHIP_264CT },
+	{ PCI_CHIP_MACH64ET, "ATI264ET (Mach64 ET)", 135, 60, 60, ATI_CHIP_264ET },
+	{ PCI_CHIP_MACH64VT, "ATI264VT? (Mach64 VT)", 170, 67, 67, ATI_CHIP_264VT },
+	{ PCI_CHIP_MACH64GT, "3D RAGE (Mach64 GT)", 135, 63, 63, ATI_CHIP_264GT },
+	/* FIXME { ...ATI_264GU, maybe ATI_CHIP_264GTDVD }, */
+	{ PCI_CHIP_MACH64GU, "3D RAGE II+ (Mach64 GTB)", 200, 67, 67, ATI_CHIP_264GTB  },
+	{ PCI_CHIP_MACH64VU, "ATI264VTB (Mach64 VU)", 200, 67, 67, ATI_CHIP_264VT3 },
+
+	{ PCI_CHIP_MACH64LT, "3D RAGE LT (Mach64 LT)", 135, 63, 63, ATI_CHIP_264LT },
+	 /* FIXME chipset maybe ATI_CHIP_264LTPRO ? */
+	{ PCI_CHIP_MACH64LG, "3D RAGE LT-G (Mach64 LG)", 230, 63, 63, ATI_CHIP_264LTG | M64F_LT_LCD_REGS | M64F_G3_PB_1024x768 },
+
+	{ PCI_CHIP_MACH64VV, "ATI264VT4 (Mach64 VV)", 230, 83, 83, ATI_CHIP_264VT4 },
+
+	{ PCI_CHIP_MACH64GV, "3D RAGE IIC (Mach64 GV, PCI)", 230, 83, 83, ATI_CHIP_264GT2C },
+	{ PCI_CHIP_MACH64GW, "3D RAGE IIC (Mach64 GW, AGP)", 230, 83, 83, ATI_CHIP_264GT2C },
+	{ PCI_CHIP_MACH64GY, "3D RAGE IIC (Mach64 GY, PCI)", 230, 83, 83, ATI_CHIP_264GT2C },
+	{ PCI_CHIP_MACH64GZ, "3D RAGE IIC (Mach64 GZ, AGP)", 230, 83, 83, ATI_CHIP_264GT2C },
+
+	{ PCI_CHIP_MACH64GB, "3D RAGE PRO (Mach64 GB, BGA, AGP)", 230, 100, 100, ATI_CHIP_264GTPRO },
+	{ PCI_CHIP_MACH64GD, "3D RAGE PRO (Mach64 GD, BGA, AGP 1x)", 230, 100, 100, ATI_CHIP_264GTPRO },
+	{ PCI_CHIP_MACH64GI, "3D RAGE PRO (Mach64 GI, BGA, PCI)", 230, 100, 100, ATI_CHIP_264GTPRO | M64F_MAGIC_VRAM_SIZE },
+	{ PCI_CHIP_MACH64GP, "3D RAGE PRO (Mach64 GP, PQFP, PCI)", 230, 100, 100, ATI_CHIP_264GTPRO },
+	{ PCI_CHIP_MACH64GQ, "3D RAGE PRO (Mach64 GQ, PQFP, PCI, limited 3D)", 230, 100, 100, ATI_CHIP_264GTPRO },
+
+	{ PCI_CHIP_MACH64LB, "3D RAGE LT PRO (Mach64 LB, AGP)", 236, 75, 100, ATI_CHIP_264LTPRO },
+	{ PCI_CHIP_MACH64LD, "3D RAGE LT PRO (Mach64 LD, AGP)", 230, 100, 100, ATI_CHIP_264LTPRO },
+	{ PCI_CHIP_MACH64LI, "3D RAGE LT PRO (Mach64 LI, PCI)", 230, 100, 100, ATI_CHIP_264LTPRO | M64F_G3_PB_1_1 | M64F_G3_PB_1024x768 },
+	{ PCI_CHIP_MACH64LP, "3D RAGE LT PRO (Mach64 LP, PCI)", 230, 100, 100, ATI_CHIP_264LTPRO },
+	{ PCI_CHIP_MACH64LQ, "3D RAGE LT PRO (Mach64 LQ, PCI)", 230, 100, 100, ATI_CHIP_264LTPRO },
+
+	{ PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP)", 230, 83, 63, ATI_CHIP_264XL },
+	{ PCI_CHIP_MACH64GN, "3D RAGE XL (Mach64 GN, AGP)", 230, 83, 63, ATI_CHIP_264XL },
+	{ PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66/BGA)", 230, 83, 63, ATI_CHIP_264XL },
+	{ PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33MHz)", 230, 83, 63, ATI_CHIP_264XL },
+	{ PCI_CHIP_MACH64GL, "3D RAGE XL (Mach64 GL, PCI)", 230, 83, 63, ATI_CHIP_264XL },
+	{ PCI_CHIP_MACH64GS, "3D RAGE XL (Mach64 GS, PCI)", 230, 83, 63, ATI_CHIP_264XL },
+
+	{ PCI_CHIP_MACH64LM, "3D RAGE Mobility P/M (Mach64 LM, AGP 2x)", 230, 83, 125, ATI_CHIP_MOBILITY },
+	{ PCI_CHIP_MACH64LN, "3D RAGE Mobility L (Mach64 LN, AGP 2x)", 230, 83, 125, ATI_CHIP_MOBILITY },
+	{ PCI_CHIP_MACH64LR, "3D RAGE Mobility P/M (Mach64 LR, PCI)", 230, 83, 125, ATI_CHIP_MOBILITY },
+	{ PCI_CHIP_MACH64LS, "3D RAGE Mobility L (Mach64 LS, PCI)", 230, 83, 125, ATI_CHIP_MOBILITY },
+#endif /* CONFIG_FB_ATY_CT */
+};
+
+/* can not fail */
+static int __devinit correct_chipset(struct atyfb_par *par)
+{
+	u8 rev;
+	u16 type;
+	u32 chip_id;
+	const char *name;
+	int i;
+
+	for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--)
+		if (par->pci_id == aty_chips[i].pci_id)
+			break;
+
+	name = aty_chips[i].name;
+	par->pll_limits.pll_max = aty_chips[i].pll;
+	par->pll_limits.mclk = aty_chips[i].mclk;
+	par->pll_limits.xclk = aty_chips[i].xclk;
+	par->features = aty_chips[i].features;
+
+	chip_id = aty_ld_le32(CONFIG_CHIP_ID, par);
+	type = chip_id & CFG_CHIP_TYPE;
+	rev = (chip_id & CFG_CHIP_REV) >> 24;
+
+	switch(par->pci_id) {
+#ifdef CONFIG_FB_ATY_GX
+	case PCI_CHIP_MACH64GX:
+		if(type != 0x00d7)
+			return -ENODEV;
+		break;
+	case PCI_CHIP_MACH64CX:
+		if(type != 0x0057)
+			return -ENODEV;
+		break;
+#endif
+#ifdef CONFIG_FB_ATY_CT
+	case PCI_CHIP_MACH64VT:
+		rev &= 0xc7;
+		if(rev == 0x00) {
+			name = "ATI264VTA3 (Mach64 VT)";
+			par->pll_limits.pll_max = 170;
+			par->pll_limits.mclk = 67;
+			par->pll_limits.xclk = 67;
+			par->features = ATI_CHIP_264VT;
+		} else if(rev == 0x40) {
+			name = "ATI264VTA4 (Mach64 VT)";
+			par->pll_limits.pll_max = 200;
+			par->pll_limits.mclk = 67;
+			par->pll_limits.xclk = 67;
+			par->features = ATI_CHIP_264VT | M64F_MAGIC_POSTDIV;
+		} else {
+			name = "ATI264VTB (Mach64 VT)";
+			par->pll_limits.pll_max = 200;
+			par->pll_limits.mclk = 67;
+			par->pll_limits.xclk = 67;
+			par->features = ATI_CHIP_264VTB;
+		}
+		break;
+	case PCI_CHIP_MACH64GT:
+		rev &= 0x07;
+		if(rev == 0x01) {
+			par->pll_limits.pll_max = 170;
+			par->pll_limits.mclk = 67;
+			par->pll_limits.xclk = 67;
+			par->features = ATI_CHIP_264GTB;
+		} else if(rev == 0x02) {
+			par->pll_limits.pll_max = 200;
+			par->pll_limits.mclk = 67;
+			par->pll_limits.xclk = 67;
+			par->features = ATI_CHIP_264GTB;
+		}
+		break;
+#endif
+	}
+
+	PRINTKI("%s [0x%04x rev 0x%02x]\n", name, type, rev);
+	return 0;
+}
+
+static char ram_dram[] __devinitdata = "DRAM";
+static char ram_resv[] __devinitdata = "RESV";
+#ifdef CONFIG_FB_ATY_GX
+static char ram_vram[] __devinitdata = "VRAM";
+#endif /* CONFIG_FB_ATY_GX */
+#ifdef CONFIG_FB_ATY_CT
+static char ram_edo[] __devinitdata = "EDO";
+static char ram_sdram[] __devinitdata = "SDRAM (1:1)";
+static char ram_sgram[] __devinitdata = "SGRAM (1:1)";
+static char ram_sdram32[] __devinitdata = "SDRAM (2:1) (32-bit)";
+static char ram_off[] __devinitdata = "OFF";
+#endif /* CONFIG_FB_ATY_CT */
+
+
+static u32 pseudo_palette[17];
+
+#ifdef CONFIG_FB_ATY_GX
+static char *aty_gx_ram[8] __devinitdata = {
+	ram_dram, ram_vram, ram_vram, ram_dram,
+	ram_dram, ram_vram, ram_vram, ram_resv
+};
+#endif /* CONFIG_FB_ATY_GX */
+
+#ifdef CONFIG_FB_ATY_CT
+static char *aty_ct_ram[8] __devinitdata = {
+	ram_off, ram_dram, ram_edo, ram_edo,
+	ram_sdram, ram_sgram, ram_sdram32, ram_resv
+};
+#endif /* CONFIG_FB_ATY_CT */
+
+static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *par)
+{
+	u32 pixclock = var->pixclock;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	u32 lcd_on_off;
+	par->pll.ct.xres = 0;
+	if (par->lcd_table != 0) {
+		lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par);
+		if(lcd_on_off & LCD_ON) {
+			par->pll.ct.xres = var->xres;
+			pixclock = par->lcd_pixclock;
+		}
+	}
+#endif
+	return pixclock;
+}
+
+#if defined(CONFIG_PPC)
+
+/*
+ *  Apple monitor sense
+ */
+
+static int __init read_aty_sense(const struct atyfb_par *par)
+{
+	int sense, i;
+
+	aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */
+	__delay(200);
+	aty_st_le32(GP_IO, 0, par); /* turn off outputs */
+	__delay(2000);
+	i = aty_ld_le32(GP_IO, par); /* get primary sense value */
+	sense = ((i & 0x3000) >> 3) | (i & 0x100);
+
+	/* drive each sense line low in turn and collect the other 2 */
+	aty_st_le32(GP_IO, 0x20000000, par); /* drive A low */
+	__delay(2000);
+	i = aty_ld_le32(GP_IO, par);
+	sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
+	aty_st_le32(GP_IO, 0x20002000, par); /* drive A high again */
+	__delay(200);
+
+	aty_st_le32(GP_IO, 0x10000000, par); /* drive B low */
+	__delay(2000);
+	i = aty_ld_le32(GP_IO, par);
+	sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
+	aty_st_le32(GP_IO, 0x10001000, par); /* drive B high again */
+	__delay(200);
+
+	aty_st_le32(GP_IO, 0x01000000, par); /* drive C low */
+	__delay(2000);
+	sense |= (aty_ld_le32(GP_IO, par) & 0x3000) >> 12;
+	aty_st_le32(GP_IO, 0, par); /* turn off outputs */
+	return sense;
+}
+
+#endif /* defined(CONFIG_PPC) */
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ *  CRTC programming
+ */
+
+static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc)
+{
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		if(!M64_HAS(LT_LCD_REGS)) {
+		    crtc->lcd_index = aty_ld_le32(LCD_INDEX, par);
+		    aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
+		}
+		crtc->lcd_config_panel = aty_ld_lcd(CONFIG_PANEL, par);
+		crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par);
+
+
+		/* switch to non shadow registers */
+		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
+                    ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
+
+		/* save stretching */
+		crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
+		crtc->vert_stretching = aty_ld_lcd(VERT_STRETCHING, par);
+		if (!M64_HAS(LT_LCD_REGS))
+			crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par);
+	}
+#endif
+	crtc->h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
+	crtc->h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
+	crtc->v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
+	crtc->v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
+	crtc->vline_crnt_vline = aty_ld_le32(CRTC_VLINE_CRNT_VLINE, par);
+	crtc->off_pitch = aty_ld_le32(CRTC_OFF_PITCH, par);
+	crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		/* switch to shadow registers */
+		aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
+			SHADOW_EN | SHADOW_RW_EN, par);
+
+		crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
+		crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
+		crtc->shadow_v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
+		crtc->shadow_v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
+
+		aty_st_le32(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
+	}
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+}
+
+static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc)
+{
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		/* stop CRTC */
+		aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~(CRTC_EXT_DISP_EN | CRTC_EN), par);
+
+		/* update non-shadow registers first */
+		aty_st_lcd(CONFIG_PANEL, crtc->lcd_config_panel, par);
+		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
+			~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
+
+		/* temporarily disable stretching */
+		aty_st_lcd(HORZ_STRETCHING,
+			crtc->horz_stretching &
+			~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par);
+		aty_st_lcd(VERT_STRETCHING,
+			crtc->vert_stretching &
+			~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 |
+			VERT_STRETCH_USE0 | VERT_STRETCH_EN), par);
+	}
+#endif
+	/* turn off CRT */
+	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~CRTC_EN, par);
+
+	DPRINTK("setting up CRTC\n");
+	DPRINTK("set primary CRT to %ix%i %c%c composite %c\n",
+	    ((((crtc->h_tot_disp>>16) & 0xff) + 1)<<3), (((crtc->v_tot_disp>>16) & 0x7ff) + 1),
+	    (crtc->h_sync_strt_wid & 0x200000)?'N':'P', (crtc->v_sync_strt_wid & 0x200000)?'N':'P',
+	    (crtc->gen_cntl & CRTC_CSYNC_EN)?'P':'N');
+
+	DPRINTK("CRTC_H_TOTAL_DISP: %x\n",crtc->h_tot_disp);
+	DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n",crtc->h_sync_strt_wid);
+	DPRINTK("CRTC_V_TOTAL_DISP: %x\n",crtc->v_tot_disp);
+	DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n",crtc->v_sync_strt_wid);
+	DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch);
+	DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline);
+	DPRINTK("CRTC_GEN_CNTL: %x\n",crtc->gen_cntl);
+
+	aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par);
+	aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par);
+	aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_tot_disp, par);
+	aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid, par);
+	aty_st_le32(CRTC_OFF_PITCH, crtc->off_pitch, par);
+	aty_st_le32(CRTC_VLINE_CRNT_VLINE, crtc->vline_crnt_vline, par);
+
+	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl, par);
+#if 0
+	FIXME
+	if (par->accel_flags & FB_ACCELF_TEXT)
+		aty_init_engine(par, info);
+#endif
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	/* after setting the CRTC registers we should set the LCD registers. */
+	if (par->lcd_table != 0) {
+		/* switch to shadow registers */
+		aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
+			(SHADOW_EN | SHADOW_RW_EN), par);
+
+		DPRINTK("set secondary CRT to %ix%i %c%c\n",
+		    ((((crtc->shadow_h_tot_disp>>16) & 0xff) + 1)<<3), (((crtc->shadow_v_tot_disp>>16) & 0x7ff) + 1),
+		    (crtc->shadow_h_sync_strt_wid & 0x200000)?'N':'P', (crtc->shadow_v_sync_strt_wid & 0x200000)?'N':'P');
+
+		DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n", crtc->shadow_h_tot_disp);
+		DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n", crtc->shadow_h_sync_strt_wid);
+		DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n", crtc->shadow_v_tot_disp);
+		DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n", crtc->shadow_v_sync_strt_wid);
+
+		aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par);
+		aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par);
+		aty_st_le32(CRTC_V_TOTAL_DISP, crtc->shadow_v_tot_disp, par);
+		aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->shadow_v_sync_strt_wid, par);
+
+		/* restore CRTC selection & shadow state and enable stretching */
+		DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl);
+		DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching);
+		DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching);
+		if(!M64_HAS(LT_LCD_REGS))
+		    DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch);
+
+		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
+		aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par);
+		aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par);
+		if(!M64_HAS(LT_LCD_REGS)) {
+		    aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par);
+		    aty_ld_le32(LCD_INDEX, par);
+		    aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
+		}
+	}
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+}
+
+static int aty_var_to_crtc(const struct fb_info *info,
+	const struct fb_var_screeninfo *var, struct crtc *crtc)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
+	u32 sync, vmode, vdisplay;
+	u32 h_total, h_disp, h_sync_strt, h_sync_end, h_sync_dly, h_sync_wid, h_sync_pol;
+	u32 v_total, v_disp, v_sync_strt, v_sync_end, v_sync_wid, v_sync_pol, c_sync;
+	u32 pix_width, dp_pix_width, dp_chain_mask;
+
+	/* input */
+	xres = var->xres;
+	yres = var->yres;
+	vxres = var->xres_virtual;
+	vyres = var->yres_virtual;
+	xoffset = var->xoffset;
+	yoffset = var->yoffset;
+	bpp = var->bits_per_pixel;
+	if (bpp == 16)
+		bpp = (var->green.length == 5) ? 15 : 16;
+	sync = var->sync;
+	vmode = var->vmode;
+
+	/* convert (and round up) and validate */
+	if (vxres < xres + xoffset)
+		vxres = xres + xoffset;
+	h_disp = xres;
+
+	if (vyres < yres + yoffset)
+		vyres = yres + yoffset;
+	v_disp = yres;
+
+	if (bpp <= 8) {
+		bpp = 8;
+		pix_width = CRTC_PIX_WIDTH_8BPP;
+		dp_pix_width =
+		    HOST_8BPP | SRC_8BPP | DST_8BPP |
+		    BYTE_ORDER_LSB_TO_MSB;
+		dp_chain_mask = DP_CHAIN_8BPP;
+	} else if (bpp <= 15) {
+		bpp = 16;
+		pix_width = CRTC_PIX_WIDTH_15BPP;
+		dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP |
+		    BYTE_ORDER_LSB_TO_MSB;
+		dp_chain_mask = DP_CHAIN_15BPP;
+	} else if (bpp <= 16) {
+		bpp = 16;
+		pix_width = CRTC_PIX_WIDTH_16BPP;
+		dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP |
+		    BYTE_ORDER_LSB_TO_MSB;
+		dp_chain_mask = DP_CHAIN_16BPP;
+	} else if (bpp <= 24 && M64_HAS(INTEGRATED)) {
+		bpp = 24;
+		pix_width = CRTC_PIX_WIDTH_24BPP;
+		dp_pix_width =
+		    HOST_8BPP | SRC_8BPP | DST_8BPP |
+		    BYTE_ORDER_LSB_TO_MSB;
+		dp_chain_mask = DP_CHAIN_24BPP;
+	} else if (bpp <= 32) {
+		bpp = 32;
+		pix_width = CRTC_PIX_WIDTH_32BPP;
+		dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP |
+		    BYTE_ORDER_LSB_TO_MSB;
+		dp_chain_mask = DP_CHAIN_32BPP;
+	} else
+		FAIL("invalid bpp");
+
+	if (vxres * vyres * bpp / 8 > info->fix.smem_len)
+		FAIL("not enough video RAM");
+
+	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+	if((xres > 1600) || (yres > 1200)) {
+		FAIL("MACH64 chips are designed for max 1600x1200\n"
+		"select anoter resolution.");
+	}
+	h_sync_strt = h_disp + var->right_margin;
+	h_sync_end = h_sync_strt + var->hsync_len;
+	h_sync_dly  = var->right_margin & 7;
+	h_total = h_sync_end + h_sync_dly + var->left_margin;
+
+	v_sync_strt = v_disp + var->lower_margin;
+	v_sync_end = v_sync_strt + var->vsync_len;
+	v_total = v_sync_end + var->upper_margin;
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		if(!M64_HAS(LT_LCD_REGS)) {
+		    u32 lcd_index = aty_ld_le32(LCD_INDEX, par);
+		    crtc->lcd_index = lcd_index &
+			~(LCD_INDEX_MASK | LCD_DISPLAY_DIS | LCD_SRC_SEL | CRTC2_DISPLAY_DIS);
+		    aty_st_le32(LCD_INDEX, lcd_index, par);
+		}
+
+		if (!M64_HAS(MOBIL_BUS))
+			crtc->lcd_index |= CRTC2_DISPLAY_DIS;
+
+		crtc->lcd_config_panel = aty_ld_lcd(CONFIG_PANEL, par) | 0x4000;
+		crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par) & ~CRTC_RW_SELECT;
+
+		crtc->lcd_gen_cntl &=
+			~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | TVCLK_PM_EN |
+			/*VCLK_DAC_PM_EN | USE_SHADOWED_VEND |*/
+			USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN);
+		crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT;
+
+		if((crtc->lcd_gen_cntl & LCD_ON) &&
+			((xres > par->lcd_width) || (yres > par->lcd_height))) {
+			/* We cannot display the mode on the LCD. If the CRT is enabled
+			   we can turn off the LCD.
+			   If the CRT is off, it isn't a good idea to switch it on; we don't
+			   know if one is connected. So it's better to fail then.
+			 */
+			if (crtc->lcd_gen_cntl & CRT_ON) {
+				PRINTKI("Disable lcd panel, because video mode does not fit.\n");
+				crtc->lcd_gen_cntl &= ~LCD_ON;
+				/*aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);*/
+			} else {
+				FAIL("Video mode exceeds size of lcd panel.\nConnect this computer to a conventional monitor if you really need this mode.");
+			}
+		}
+	}
+
+	if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) {
+		int VScan = 1;
+		/* bpp -> bytespp, 1,4 -> 0; 8 -> 2; 15,16 -> 1; 24 -> 6; 32 -> 5
+		const u8 DFP_h_sync_dly_LT[] = { 0, 2, 1, 6, 5 };
+		const u8 ADD_to_strt_wid_and_dly_LT_DAC[] = { 0, 5, 6, 9, 9, 12, 12 };  */
+
+		vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED);
+
+		/* This is horror! When we simulate, say 640x480 on an 800x600
+		   lcd monitor, the CRTC should be programmed 800x600 values for
+		   the non visible part, but 640x480 for the visible part.
+		   This code has been tested on a laptop with it's 1400x1050 lcd
+		   monitor and a conventional monitor both switched on.
+		   Tested modes: 1280x1024, 1152x864, 1024x768, 800x600,
+		    works with little glitches also with DOUBLESCAN modes
+		 */
+		if (yres < par->lcd_height) {
+			VScan = par->lcd_height / yres;
+			if(VScan > 1) {
+				VScan = 2;
+				vmode |= FB_VMODE_DOUBLE;
+			}
+		}
+
+		h_sync_strt = h_disp + par->lcd_right_margin;
+		h_sync_end = h_sync_strt + par->lcd_hsync_len;
+		h_sync_dly = /*DFP_h_sync_dly[ ( bpp + 1 ) / 3 ]; */par->lcd_hsync_dly;
+		h_total = h_disp + par->lcd_hblank_len;
+
+		v_sync_strt = v_disp + par->lcd_lower_margin / VScan;
+		v_sync_end = v_sync_strt + par->lcd_vsync_len / VScan;
+		v_total = v_disp + par->lcd_vblank_len / VScan;
+	}
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+
+	h_disp = (h_disp >> 3) - 1;
+	h_sync_strt = (h_sync_strt >> 3) - 1;
+	h_sync_end = (h_sync_end >> 3) - 1;
+	h_total = (h_total >> 3) - 1;
+	h_sync_wid = h_sync_end - h_sync_strt;
+
+	FAIL_MAX("h_disp too large", h_disp, 0xff);
+	FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff);
+	/*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/
+	if(h_sync_wid > 0x1f)
+		h_sync_wid = 0x1f;
+	FAIL_MAX("h_total too large", h_total, 0x1ff);
+
+	if (vmode & FB_VMODE_DOUBLE) {
+		v_disp <<= 1;
+		v_sync_strt <<= 1;
+		v_sync_end <<= 1;
+		v_total <<= 1;
+	}
+
+	vdisplay = yres;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON))
+		vdisplay  = par->lcd_height;
+#endif
+
+	if(vdisplay < 400) {
+		h_sync_pol = 1;
+		v_sync_pol = 0;
+	} else if(vdisplay < 480) {
+		h_sync_pol = 0;
+		v_sync_pol = 1;
+	} else if(vdisplay < 768) {
+		h_sync_pol = 0;
+		v_sync_pol = 0;
+	} else {
+		h_sync_pol = 1;
+		v_sync_pol = 1;
+	}
+
+	v_disp--;
+	v_sync_strt--;
+	v_sync_end--;
+	v_total--;
+	v_sync_wid = v_sync_end - v_sync_strt;
+
+	FAIL_MAX("v_disp too large", v_disp, 0x7ff);
+	FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff);
+	/*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/
+	if(v_sync_wid > 0x1f)
+		v_sync_wid = 0x1f;
+	FAIL_MAX("v_total too large", v_total, 0x7ff);
+
+	c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0;
+
+	/* output */
+	crtc->vxres = vxres;
+	crtc->vyres = vyres;
+	crtc->xoffset = xoffset;
+	crtc->yoffset = yoffset;
+	crtc->bpp = bpp;
+	crtc->off_pitch = ((yoffset*vxres+xoffset)*bpp/64) | (vxres<<19);
+	crtc->vline_crnt_vline = 0;
+
+	crtc->h_tot_disp = h_total | (h_disp<<16);
+	crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly<<8) |
+		((h_sync_strt & 0x100)<<4) | (h_sync_wid<<16) | (h_sync_pol<<21);
+	crtc->v_tot_disp = v_total | (v_disp<<16);
+	crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid<<16) | (v_sync_pol<<21);
+
+	/* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */
+	crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync;
+	crtc->gen_cntl |= CRTC_VGA_LINEAR;
+
+	/* Enable doublescan mode if requested */
+	if (vmode & FB_VMODE_DOUBLE)
+		crtc->gen_cntl |= CRTC_DBL_SCAN_EN;
+	/* Enable interlaced mode if requested */
+	if (vmode & FB_VMODE_INTERLACED)
+		crtc->gen_cntl |= CRTC_INTERLACE_EN;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		vdisplay = yres;
+		if(vmode & FB_VMODE_DOUBLE)
+			vdisplay <<= 1;
+		if(vmode & FB_VMODE_INTERLACED) {
+			vdisplay >>= 1;
+
+			/* The prefered mode for the lcd is not interlaced, so disable it if
+			   it was enabled. For doublescan there is no problem, because we can
+			   compensate for it in the hardware stretching (we stretch half as much)
+			 */
+			vmode &= ~FB_VMODE_INTERLACED;
+			/*crtc->gen_cntl &= ~CRTC_INTERLACE_EN;*/
+		}
+		crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH);
+		crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 |
+			/*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/
+			USE_SHADOWED_VEND | USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN);
+		crtc->lcd_gen_cntl |= (DONT_SHADOW_VPAR/* | LOCK_8DOT*/);
+
+		/* MOBILITY M1 tested, FIXME: LT */
+		crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
+		if (!M64_HAS(LT_LCD_REGS))
+			crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) &
+				~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3);
+
+		crtc->horz_stretching &=
+			~(HORZ_STRETCH_RATIO | HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO |
+			HORZ_STRETCH_MODE | HORZ_STRETCH_EN);
+		if (xres < par->lcd_width) {
+			do {
+				/*
+				* The horizontal blender misbehaves when HDisplay is less than a
+				* a certain threshold (440 for a 1024-wide panel).  It doesn't
+				* stretch such modes enough.  Use pixel replication instead of
+				* blending to stretch modes that can be made to exactly fit the
+				* panel width.  The undocumented "NoLCDBlend" option allows the
+				* pixel-replicated mode to be slightly wider or narrower than the
+				* panel width.  It also causes a mode that is exactly half as wide
+				* as the panel to be pixel-replicated, rather than blended.
+				*/
+				int HDisplay  = xres & ~7;
+				int nStretch  = par->lcd_width / HDisplay;
+				int Remainder = par->lcd_width % HDisplay;
+
+				if ((!Remainder && ((nStretch > 2))) ||
+					(((HDisplay * 16) / par->lcd_width) < 7)) {
+					static const char StretchLoops[] = {10, 12, 13, 15, 16};
+					int horz_stretch_loop = -1, BestRemainder;
+					int Numerator = HDisplay, Denominator = par->lcd_width;
+					int Index = 5;
+					ATIReduceRatio(&Numerator, &Denominator);
+
+					BestRemainder = (Numerator * 16) / Denominator;
+					while (--Index >= 0) {
+						Remainder = ((Denominator - Numerator) * StretchLoops[Index]) %
+							Denominator;
+						if (Remainder < BestRemainder) {
+							horz_stretch_loop = Index;
+							if (!(BestRemainder = Remainder))
+								break;
+						}
+					}
+
+					if ((horz_stretch_loop >= 0) && !BestRemainder) {
+						int horz_stretch_ratio = 0, Accumulator = 0;
+						int reuse_previous = 1;
+
+						Index = StretchLoops[horz_stretch_loop];
+
+						while (--Index >= 0) {
+							if (Accumulator > 0)
+								horz_stretch_ratio |= reuse_previous;
+							else
+								Accumulator += Denominator;
+							Accumulator -= Numerator;
+							reuse_previous <<= 1;
+						}
+
+						crtc->horz_stretching |= (HORZ_STRETCH_EN |
+							((horz_stretch_loop & HORZ_STRETCH_LOOP) << 16) |
+							(horz_stretch_ratio & HORZ_STRETCH_RATIO));
+						break;      /* Out of the do { ... } while (0) */
+					}
+				}
+
+				crtc->horz_stretching |= (HORZ_STRETCH_MODE | HORZ_STRETCH_EN |
+					(((HDisplay * (HORZ_STRETCH_BLEND + 1)) / par->lcd_width) & HORZ_STRETCH_BLEND));
+			} while (0);
+		}
+
+		if (vdisplay < par->lcd_height) {
+			crtc->vert_stretching = (VERT_STRETCH_USE0 | VERT_STRETCH_EN |
+				(((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0));
+
+			if (!M64_HAS(LT_LCD_REGS) &&
+			    xres <= (M64_HAS(MOBIL_BUS)?1024:800))
+				crtc->ext_vert_stretch |= VERT_STRETCH_MODE;
+		} else {
+			/*
+			 * Don't use vertical blending if the mode is too wide or not
+			 * vertically stretched.
+			 */
+			crtc->vert_stretching = 0;
+		}
+		/* copy to shadow crtc */
+		crtc->shadow_h_tot_disp = crtc->h_tot_disp;
+		crtc->shadow_h_sync_strt_wid = crtc->h_sync_strt_wid;
+		crtc->shadow_v_tot_disp = crtc->v_tot_disp;
+		crtc->shadow_v_sync_strt_wid = crtc->v_sync_strt_wid;
+	}
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+
+	if (M64_HAS(MAGIC_FIFO)) {
+		/* Not VTB/GTB */
+		/* FIXME: magic FIFO values */
+		crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC2_PIX_WIDTH);
+	}
+	crtc->dp_pix_width = dp_pix_width;
+	crtc->dp_chain_mask = dp_chain_mask;
+
+	return 0;
+}
+
+static int aty_crtc_to_var(const struct crtc *crtc, struct fb_var_screeninfo *var)
+{
+	u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync;
+	u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid,
+	    h_sync_pol;
+	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
+	u32 pix_width;
+	u32 double_scan, interlace;
+
+	/* input */
+	h_total = crtc->h_tot_disp & 0x1ff;
+	h_disp = (crtc->h_tot_disp >> 16) & 0xff;
+	h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | ((crtc->h_sync_strt_wid >> 4) & 0x100);
+	h_sync_dly = (crtc->h_sync_strt_wid >> 8) & 0x7;
+	h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x1f;
+	h_sync_pol = (crtc->h_sync_strt_wid >> 21) & 0x1;
+	v_total = crtc->v_tot_disp & 0x7ff;
+	v_disp = (crtc->v_tot_disp >> 16) & 0x7ff;
+	v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
+	v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f;
+	v_sync_pol = (crtc->v_sync_strt_wid >> 21) & 0x1;
+	c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+	pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+	double_scan = crtc->gen_cntl & CRTC_DBL_SCAN_EN;
+	interlace = crtc->gen_cntl & CRTC_INTERLACE_EN;
+
+	/* convert */
+	xres = (h_disp + 1) * 8;
+	yres = v_disp + 1;
+	left = (h_total - h_sync_strt - h_sync_wid) * 8 - h_sync_dly;
+	right = (h_sync_strt - h_disp) * 8 + h_sync_dly;
+	hslen = h_sync_wid * 8;
+	upper = v_total - v_sync_strt - v_sync_wid;
+	lower = v_sync_strt - v_disp;
+	vslen = v_sync_wid;
+	sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+	    (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+	    (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+
+	switch (pix_width) {
+#if 0
+	case CRTC_PIX_WIDTH_4BPP:
+		bpp = 4;
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->green.offset = 0;
+		var->green.length = 8;
+		var->blue.offset = 0;
+		var->blue.length = 8;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		break;
+#endif
+	case CRTC_PIX_WIDTH_8BPP:
+		bpp = 8;
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->green.offset = 0;
+		var->green.length = 8;
+		var->blue.offset = 0;
+		var->blue.length = 8;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		break;
+	case CRTC_PIX_WIDTH_15BPP:	/* RGB 555 */
+		bpp = 16;
+		var->red.offset = 10;
+		var->red.length = 5;
+		var->green.offset = 5;
+		var->green.length = 5;
+		var->blue.offset = 0;
+		var->blue.length = 5;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		break;
+	case CRTC_PIX_WIDTH_16BPP:	/* RGB 565 */
+		bpp = 16;
+		var->red.offset = 11;
+		var->red.length = 5;
+		var->green.offset = 5;
+		var->green.length = 6;
+		var->blue.offset = 0;
+		var->blue.length = 5;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		break;
+	case CRTC_PIX_WIDTH_24BPP:	/* RGB 888 */
+		bpp = 24;
+		var->red.offset = 16;
+		var->red.length = 8;
+		var->green.offset = 8;
+		var->green.length = 8;
+		var->blue.offset = 0;
+		var->blue.length = 8;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		break;
+	case CRTC_PIX_WIDTH_32BPP:	/* ARGB 8888 */
+		bpp = 32;
+		var->red.offset = 16;
+		var->red.length = 8;
+		var->green.offset = 8;
+		var->green.length = 8;
+		var->blue.offset = 0;
+		var->blue.length = 8;
+		var->transp.offset = 24;
+		var->transp.length = 8;
+		break;
+	default:
+		FAIL("Invalid pixel width");
+	}
+
+	/* output */
+	var->xres = xres;
+	var->yres = yres;
+	var->xres_virtual = crtc->vxres;
+	var->yres_virtual = crtc->vyres;
+	var->bits_per_pixel = bpp;
+	var->left_margin = left;
+	var->right_margin = right;
+	var->upper_margin = upper;
+	var->lower_margin = lower;
+	var->hsync_len = hslen;
+	var->vsync_len = vslen;
+	var->sync = sync;
+	var->vmode = FB_VMODE_NONINTERLACED;
+	/* In double scan mode, the vertical parameters are doubled, so we need to
+	   half them to get the right values.
+	   In interlaced mode the values are already correct, so no correction is
+	   necessary.
+	 */
+	if (interlace)
+		var->vmode = FB_VMODE_INTERLACED;
+
+	if (double_scan) {
+		var->vmode = FB_VMODE_DOUBLE;
+		var->yres>>=1;
+		var->upper_margin>>=1;
+		var->lower_margin>>=1;
+		var->vsync_len>>=1;
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int atyfb_set_par(struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	struct fb_var_screeninfo *var = &info->var;
+	u32 tmp, pixclock;
+	int err;
+#ifdef DEBUG
+	struct fb_var_screeninfo debug;
+	u32 pixclock_in_ps;
+#endif
+	if (par->asleep)
+		return 0;
+
+	if ((err = aty_var_to_crtc(info, var, &par->crtc)))
+		return err;
+
+	pixclock = atyfb_get_pixclock(var, par);
+
+	if (pixclock == 0) {
+		FAIL("Invalid pixclock");
+	} else {
+		if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &par->pll)))
+			return err;
+	}
+
+	par->accel_flags = var->accel_flags; /* hack */
+
+	if (par->blitter_may_be_busy)
+		wait_for_idle(par);
+
+	aty_set_crtc(par, &par->crtc);
+	par->dac_ops->set_dac(info, &par->pll, var->bits_per_pixel, par->accel_flags);
+	par->pll_ops->set_pll(info, &par->pll);
+
+#ifdef DEBUG
+	if(par->pll_ops && par->pll_ops->pll_to_var)
+		pixclock_in_ps = par->pll_ops->pll_to_var(info, &(par->pll));
+	else
+		pixclock_in_ps = 0;
+
+	if(0 == pixclock_in_ps) {
+		PRINTKE("ALERT ops->pll_to_var get 0\n");
+		pixclock_in_ps = pixclock;
+	}
+
+	memset(&debug, 0, sizeof(debug));
+	if(!aty_crtc_to_var(&(par->crtc), &debug)) {
+		u32 hSync, vRefresh;
+		u32 h_disp, h_sync_strt, h_sync_end, h_total;
+		u32 v_disp, v_sync_strt, v_sync_end, v_total;
+
+		h_disp = debug.xres;
+		h_sync_strt = h_disp + debug.right_margin;
+		h_sync_end = h_sync_strt + debug.hsync_len;
+		h_total = h_sync_end + debug.left_margin;
+		v_disp = debug.yres;
+		v_sync_strt = v_disp + debug.lower_margin;
+		v_sync_end = v_sync_strt + debug.vsync_len;
+		v_total = v_sync_end + debug.upper_margin;
+
+		hSync = 1000000000 / (pixclock_in_ps * h_total);
+		vRefresh = (hSync * 1000) / v_total;
+        	if (par->crtc.gen_cntl & CRTC_INTERLACE_EN)
+            	vRefresh *= 2;
+        	if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
+            	vRefresh /= 2;
+
+		DPRINTK("atyfb_set_par\n");
+		DPRINTK(" Set Visible Mode to %ix%i-%i\n", var->xres, var->yres, var->bits_per_pixel);
+		DPRINTK(" Virtual resolution %ix%i, pixclock_in_ps %i (calculated %i)\n",
+			var->xres_virtual, var->yres_virtual, pixclock, pixclock_in_ps);
+		DPRINTK(" Dot clock:           %i MHz\n", 1000000 / pixclock_in_ps);
+		DPRINTK(" Horizontal sync:     %i kHz\n", hSync);
+		DPRINTK(" Vertical refresh:    %i Hz\n", vRefresh);
+		DPRINTK(" x  style: %i.%03i %i %i %i %i   %i %i %i %i\n",
+			1000000 / pixclock_in_ps, 1000000 % pixclock_in_ps,
+			h_disp, h_sync_strt, h_sync_end, h_total,
+			v_disp, v_sync_strt, v_sync_end, v_total);
+		DPRINTK(" fb style: %i  %i %i %i %i %i %i %i %i\n",
+			pixclock_in_ps,
+			debug.left_margin, h_disp, debug.right_margin, debug.hsync_len,
+			debug.upper_margin, v_disp, debug.lower_margin, debug.vsync_len);
+	}
+#endif /* DEBUG */
+
+	if (!M64_HAS(INTEGRATED)) {
+		/* Don't forget MEM_CNTL */
+		tmp = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff;
+		switch (var->bits_per_pixel) {
+		case 8:
+			tmp |= 0x02000000;
+			break;
+		case 16:
+			tmp |= 0x03000000;
+			break;
+		case 32:
+			tmp |= 0x06000000;
+			break;
+		}
+		aty_st_le32(MEM_CNTL, tmp, par);
+	} else {
+		tmp = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff;
+		if (!M64_HAS(MAGIC_POSTDIV))
+			tmp |= par->mem_refresh_rate << 20;
+		switch (var->bits_per_pixel) {
+		case 8:
+		case 24:
+			tmp |= 0x00000000;
+			break;
+		case 16:
+			tmp |= 0x04000000;
+			break;
+		case 32:
+			tmp |= 0x08000000;
+			break;
+		}
+		if (M64_HAS(CT_BUS)) {
+			aty_st_le32(DAC_CNTL, 0x87010184, par);
+			aty_st_le32(BUS_CNTL, 0x680000f9, par);
+		} else if (M64_HAS(VT_BUS)) {
+			aty_st_le32(DAC_CNTL, 0x87010184, par);
+			aty_st_le32(BUS_CNTL, 0x680000f9, par);
+		} else if (M64_HAS(MOBIL_BUS)) {
+			aty_st_le32(DAC_CNTL, 0x80010102, par);
+			aty_st_le32(BUS_CNTL, 0x7b33a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
+		} else {
+			/* GT */
+			aty_st_le32(DAC_CNTL, 0x86010102, par);
+			aty_st_le32(BUS_CNTL, 0x7b23a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
+			aty_st_le32(EXT_MEM_CNTL, aty_ld_le32(EXT_MEM_CNTL, par) | 0x5000001, par);
+		}
+		aty_st_le32(MEM_CNTL, tmp, par);
+	}
+	aty_st_8(DAC_MASK, 0xff, par);
+
+	info->fix.line_length = var->xres_virtual * var->bits_per_pixel/8;
+	info->fix.visual = var->bits_per_pixel <= 8 ?
+		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
+
+	/* Initialize the graphics engine */
+	if (par->accel_flags & FB_ACCELF_TEXT)
+		aty_init_engine(par, info);
+
+#ifdef CONFIG_BOOTX_TEXT
+	btext_update_display(info->fix.smem_start,
+		(((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8,
+		((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1,
+		var->bits_per_pixel,
+		par->crtc.vxres * var->bits_per_pixel / 8);
+#endif /* CONFIG_BOOTX_TEXT */
+#if 0
+	/* switch to accelerator mode */
+	if (!(par->crtc.gen_cntl & CRTC_EXT_DISP_EN))
+		aty_st_le32(CRTC_GEN_CNTL, par->crtc.gen_cntl | CRTC_EXT_DISP_EN, par);
+#endif
+#ifdef DEBUG
+{
+	/* dump non shadow CRTC, pll, LCD registers */
+	int i; u32 base;
+
+	/* CRTC registers */
+	base = 0x2000;
+	printk("debug atyfb: Mach64 non-shadow register values:");
+	for (i = 0; i < 256; i = i+4) {
+		if(i%16 == 0) printk("\ndebug atyfb: 0x%04X: ", base + i);
+		printk(" %08X", aty_ld_le32(i, par));
+	}
+	printk("\n\n");
+
+#ifdef CONFIG_FB_ATY_CT
+	/* PLL registers */
+	base = 0x00;
+	printk("debug atyfb: Mach64 PLL register values:");
+	for (i = 0; i < 64; i++) {
+		if(i%16 == 0) printk("\ndebug atyfb: 0x%02X: ", base + i);
+		if(i%4 == 0)  printk(" ");
+		printk("%02X", aty_ld_pll_ct(i, par));
+	}
+	printk("\n\n");
+#endif	/* CONFIG_FB_ATY_CT */
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		/* LCD registers */
+		base = 0x00;
+		printk("debug atyfb: LCD register values:");
+		if(M64_HAS(LT_LCD_REGS)) {
+		    for(i = 0; i <= POWER_MANAGEMENT; i++) {
+			if(i == EXT_VERT_STRETCH)
+			    continue;
+			printk("\ndebug atyfb: 0x%04X: ", lt_lcd_regs[i]);
+			printk(" %08X", aty_ld_lcd(i, par));
+		    }
+
+		} else {
+		    for (i = 0; i < 64; i++) {
+			if(i%4 == 0) printk("\ndebug atyfb: 0x%02X: ", base + i);
+			printk(" %08X", aty_ld_lcd(i, par));
+		    }
+		}
+		printk("\n\n");
+	}
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+}
+#endif /* DEBUG */
+	return 0;
+}
+
+static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	int err;
+	struct crtc crtc;
+	union aty_pll pll;
+	u32 pixclock;
+
+	memcpy(&pll, &(par->pll), sizeof(pll));
+
+	if((err = aty_var_to_crtc(info, var, &crtc)))
+		return err;
+
+	pixclock = atyfb_get_pixclock(var, par);
+
+	if (pixclock == 0) {
+		FAIL("Invalid pixclock");
+	} else {
+		if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &pll)))
+			return err;
+	}
+
+	if (var->accel_flags & FB_ACCELF_TEXT)
+		info->var.accel_flags = FB_ACCELF_TEXT;
+	else
+		info->var.accel_flags = 0;
+
+#if 0 /* fbmon is not done. uncomment for 2.5.x -brad */
+	if (!fbmon_valid_timings(pixclock, htotal, vtotal, info))
+		return -EINVAL;
+#endif
+	aty_crtc_to_var(&crtc, var);
+	var->pixclock = par->pll_ops->pll_to_var(info, &pll);
+	return 0;
+}
+
+static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info)
+{
+	u32 xoffset = info->var.xoffset;
+	u32 yoffset = info->var.yoffset;
+	u32 vxres = par->crtc.vxres;
+	u32 bpp = info->var.bits_per_pixel;
+
+	par->crtc.off_pitch = ((yoffset * vxres + xoffset) * bpp / 64) | (vxres << 19);
+}
+
+
+    /*
+     *  Open/Release the frame buffer device
+     */
+
+static int atyfb_open(struct fb_info *info, int user)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+	if (user) {
+		par->open++;
+#ifdef __sparc__
+		par->mmaped = 0;
+#endif
+	}
+	return (0);
+}
+
+static irqreturn_t aty_irq(int irq, void *dev_id, struct pt_regs *fp)
+{
+	struct atyfb_par *par = dev_id;
+	int handled = 0;
+	u32 int_cntl;
+
+	spin_lock(&par->int_lock);
+
+	int_cntl = aty_ld_le32(CRTC_INT_CNTL, par);
+
+	if (int_cntl & CRTC_VBLANK_INT) {
+		/* clear interrupt */
+		aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) | CRTC_VBLANK_INT_AK, par);
+		par->vblank.count++;
+		if (par->vblank.pan_display) {
+			par->vblank.pan_display = 0;
+			aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
+		}
+		wake_up_interruptible(&par->vblank.wait);
+		handled = 1;
+	}
+
+	spin_unlock(&par->int_lock);
+
+	return IRQ_RETVAL(handled);
+}
+
+static int aty_enable_irq(struct atyfb_par *par, int reenable)
+{
+	u32 int_cntl;
+
+	if (!test_and_set_bit(0, &par->irq_flags)) {
+		if (request_irq(par->irq, aty_irq, SA_SHIRQ, "atyfb", par)) {
+			clear_bit(0, &par->irq_flags);
+			return -EINVAL;
+		}
+		spin_lock_irq(&par->int_lock);
+		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
+		/* clear interrupt */
+		aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_AK, par);
+		/* enable interrupt */
+		aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par);
+		spin_unlock_irq(&par->int_lock);
+	} else if (reenable) {
+		spin_lock_irq(&par->int_lock);
+		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
+		if (!(int_cntl & CRTC_VBLANK_INT_EN)) {
+			printk("atyfb: someone disabled IRQ [%08x]\n", int_cntl);
+			/* re-enable interrupt */
+			aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par );
+		}
+		spin_unlock_irq(&par->int_lock);
+	}
+
+	return 0;
+}
+
+static int aty_disable_irq(struct atyfb_par *par)
+{
+	u32 int_cntl;
+
+	if (test_and_clear_bit(0, &par->irq_flags)) {
+		if (par->vblank.pan_display) {
+			par->vblank.pan_display = 0;
+			aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
+		}
+		spin_lock_irq(&par->int_lock);
+		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
+		/* disable interrupt */
+		aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par );
+		spin_unlock_irq(&par->int_lock);
+		free_irq(par->irq, par);
+	}
+
+	return 0;
+}
+
+static int atyfb_release(struct fb_info *info, int user)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	if (user) {
+		par->open--;
+		mdelay(1);
+		wait_for_idle(par);
+		if (!par->open) {
+#ifdef __sparc__
+			int was_mmaped = par->mmaped;
+
+			par->mmaped = 0;
+
+			if (was_mmaped) {
+				struct fb_var_screeninfo var;
+
+				/* Now reset the default display config, we have no
+				 * idea what the program(s) which mmap'd the chip did
+				 * to the configuration, nor whether it restored it
+				 * correctly.
+				 */
+				var = default_var;
+				if (noaccel)
+					var.accel_flags &= ~FB_ACCELF_TEXT;
+				else
+					var.accel_flags |= FB_ACCELF_TEXT;
+				if (var.yres == var.yres_virtual) {
+					u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2));
+					var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual;
+					if (var.yres_virtual < var.yres)
+						var.yres_virtual = var.yres;
+				}
+			}
+#endif
+			aty_disable_irq(par);
+		}
+	}
+	return (0);
+}
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 xres, yres, xoffset, yoffset;
+
+	xres = (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8;
+	yres = ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1;
+	if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
+		yres >>= 1;
+	xoffset = (var->xoffset + 7) & ~7;
+	yoffset = var->yoffset;
+	if (xoffset + xres > par->crtc.vxres || yoffset + yres > par->crtc.vyres)
+		return -EINVAL;
+	info->var.xoffset = xoffset;
+	info->var.yoffset = yoffset;
+	if (par->asleep)
+		return 0;
+
+	set_off_pitch(par, info);
+	if ((var->activate & FB_ACTIVATE_VBL) && !aty_enable_irq(par, 0)) {
+		par->vblank.pan_display = 1;
+	} else {
+		par->vblank.pan_display = 0;
+		aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
+	}
+
+	return 0;
+}
+
+static int aty_waitforvblank(struct atyfb_par *par, u32 crtc)
+{
+	struct aty_interrupt *vbl;
+	unsigned int count;
+	int ret;
+
+	switch (crtc) {
+	case 0:
+		vbl = &par->vblank;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	ret = aty_enable_irq(par, 0);
+	if (ret)
+		return ret;
+
+	count = vbl->count;
+	ret = wait_event_interruptible_timeout(vbl->wait, count != vbl->count, HZ/10);
+	if (ret < 0) {
+		return ret;
+	}
+	if (ret == 0) {
+		aty_enable_irq(par, 1);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+
+#ifdef DEBUG
+#define ATYIO_CLKR		0x41545900	/* ATY\00 */
+#define ATYIO_CLKW		0x41545901	/* ATY\01 */
+
+struct atyclk {
+	u32 ref_clk_per;
+	u8 pll_ref_div;
+	u8 mclk_fb_div;
+	u8 mclk_post_div;	/* 1,2,3,4,8 */
+	u8 mclk_fb_mult;	/* 2 or 4 */
+	u8 xclk_post_div;	/* 1,2,3,4,8 */
+	u8 vclk_fb_div;
+	u8 vclk_post_div;	/* 1,2,3,4,6,8,12 */
+	u32 dsp_xclks_per_row;	/* 0-16383 */
+	u32 dsp_loop_latency;	/* 0-15 */
+	u32 dsp_precision;	/* 0-7 */
+	u32 dsp_on;		/* 0-2047 */
+	u32 dsp_off;		/* 0-2047 */
+};
+
+#define ATYIO_FEATR		0x41545902	/* ATY\02 */
+#define ATYIO_FEATW		0x41545903	/* ATY\03 */
+#endif
+
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
+static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+	u_long arg, struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+#ifdef __sparc__
+	struct fbtype fbtyp;
+#endif
+
+	switch (cmd) {
+#ifdef __sparc__
+	case FBIOGTYPE:
+		fbtyp.fb_type = FBTYPE_PCI_GENERIC;
+		fbtyp.fb_width = par->crtc.vxres;
+		fbtyp.fb_height = par->crtc.vyres;
+		fbtyp.fb_depth = info->var.bits_per_pixel;
+		fbtyp.fb_cmsize = info->cmap.len;
+		fbtyp.fb_size = info->fix.smem_len;
+		if (copy_to_user((struct fbtype __user *) arg, &fbtyp, sizeof(fbtyp)))
+			return -EFAULT;
+		break;
+#endif /* __sparc__ */
+
+	case FBIO_WAITFORVSYNC:
+		{
+			u32 crtc;
+
+			if (get_user(crtc, (__u32 __user *) arg))
+				return -EFAULT;
+
+			return aty_waitforvblank(par, crtc);
+		}
+		break;
+
+#if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
+	case ATYIO_CLKR:
+		if (M64_HAS(INTEGRATED)) {
+			struct atyclk clk;
+			union aty_pll *pll = &(par->pll);
+			u32 dsp_config = pll->ct.dsp_config;
+			u32 dsp_on_off = pll->ct.dsp_on_off;
+			clk.ref_clk_per = par->ref_clk_per;
+			clk.pll_ref_div = pll->ct.pll_ref_div;
+			clk.mclk_fb_div = pll->ct.mclk_fb_div;
+			clk.mclk_post_div = pll->ct.mclk_post_div_real;
+			clk.mclk_fb_mult = pll->ct.mclk_fb_mult;
+			clk.xclk_post_div = pll->ct.xclk_post_div_real;
+			clk.vclk_fb_div = pll->ct.vclk_fb_div;
+			clk.vclk_post_div = pll->ct.vclk_post_div_real;
+			clk.dsp_xclks_per_row = dsp_config & 0x3fff;
+			clk.dsp_loop_latency = (dsp_config >> 16) & 0xf;
+			clk.dsp_precision = (dsp_config >> 20) & 7;
+			clk.dsp_off = dsp_on_off & 0x7ff;
+			clk.dsp_on = (dsp_on_off >> 16) & 0x7ff;
+			if (copy_to_user((struct atyclk __user *) arg, &clk,
+					 sizeof(clk)))
+				return -EFAULT;
+		} else
+			return -EINVAL;
+		break;
+	case ATYIO_CLKW:
+		if (M64_HAS(INTEGRATED)) {
+			struct atyclk clk;
+			union aty_pll *pll = &(par->pll);
+			if (copy_from_user(&clk, (struct atyclk __user *) arg, sizeof(clk)))
+				return -EFAULT;
+			par->ref_clk_per = clk.ref_clk_per;
+			pll->ct.pll_ref_div = clk.pll_ref_div;
+			pll->ct.mclk_fb_div = clk.mclk_fb_div;
+			pll->ct.mclk_post_div_real = clk.mclk_post_div;
+			pll->ct.mclk_fb_mult = clk.mclk_fb_mult;
+			pll->ct.xclk_post_div_real = clk.xclk_post_div;
+			pll->ct.vclk_fb_div = clk.vclk_fb_div;
+			pll->ct.vclk_post_div_real = clk.vclk_post_div;
+			pll->ct.dsp_config = (clk.dsp_xclks_per_row & 0x3fff) |
+				((clk.dsp_loop_latency & 0xf)<<16)| ((clk.dsp_precision & 7)<<20);
+			pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) | ((clk.dsp_on & 0x7ff)<<16);
+			/*aty_calc_pll_ct(info, &pll->ct);*/
+			aty_set_pll_ct(info, pll);
+		} else
+			return -EINVAL;
+		break;
+	case ATYIO_FEATR:
+		if (get_user(par->features, (u32 __user *) arg))
+			return -EFAULT;
+		break;
+	case ATYIO_FEATW:
+		if (put_user(par->features, (u32 __user *) arg))
+			return -EFAULT;
+		break;
+#endif /* DEBUG && CONFIG_FB_ATY_CT */
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int atyfb_sync(struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+	if (par->blitter_may_be_busy)
+		wait_for_idle(par);
+	return 0;
+}
+
+#ifdef __sparc__
+static int atyfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	unsigned int size, page, map_size = 0;
+	unsigned long map_offset = 0;
+	unsigned long off;
+	int i;
+
+	if (!par->mmap_map)
+		return -ENXIO;
+
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+		return -EINVAL;
+
+	off = vma->vm_pgoff << PAGE_SHIFT;
+	size = vma->vm_end - vma->vm_start;
+
+	/* To stop the swapper from even considering these pages. */
+	vma->vm_flags |= (VM_IO | VM_RESERVED);
+
+	if (((vma->vm_pgoff == 0) && (size == info->fix.smem_len)) ||
+	    ((off == info->fix.smem_len) && (size == PAGE_SIZE)))
+		off += 0x8000000000000000UL;
+
+	vma->vm_pgoff = off >> PAGE_SHIFT;	/* propagate off changes */
+
+	/* Each page, see which map applies */
+	for (page = 0; page < size;) {
+		map_size = 0;
+		for (i = 0; par->mmap_map[i].size; i++) {
+			unsigned long start = par->mmap_map[i].voff;
+			unsigned long end = start + par->mmap_map[i].size;
+			unsigned long offset = off + page;
+
+			if (start > offset)
+				continue;
+			if (offset >= end)
+				continue;
+
+			map_size = par->mmap_map[i].size - (offset - start);
+			map_offset =
+			    par->mmap_map[i].poff + (offset - start);
+			break;
+		}
+		if (!map_size) {
+			page += PAGE_SIZE;
+			continue;
+		}
+		if (page + map_size > size)
+			map_size = size - page;
+
+		pgprot_val(vma->vm_page_prot) &=
+		    ~(par->mmap_map[i].prot_mask);
+		pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag;
+
+		if (remap_pfn_range(vma, vma->vm_start + page,
+			map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot))
+			return -EAGAIN;
+
+		page += map_size;
+	}
+
+	if (!map_size)
+		return -EINVAL;
+
+	if (!par->mmaped)
+		par->mmaped = 1;
+	return 0;
+}
+
+static struct {
+	u32 yoffset;
+	u8 r[2][256];
+	u8 g[2][256];
+	u8 b[2][256];
+} atyfb_save;
+
+static void atyfb_save_palette(struct atyfb_par *par, int enter)
+{
+	int i, tmp;
+
+	for (i = 0; i < 256; i++) {
+		tmp = aty_ld_8(DAC_CNTL, par) & 0xfc;
+		if (M64_HAS(EXTRA_BRIGHT))
+			tmp |= 0x2;
+		aty_st_8(DAC_CNTL, tmp, par);
+		aty_st_8(DAC_MASK, 0xff, par);
+
+		writeb(i, &par->aty_cmap_regs->rindex);
+		atyfb_save.r[enter][i] = readb(&par->aty_cmap_regs->lut);
+		atyfb_save.g[enter][i] = readb(&par->aty_cmap_regs->lut);
+		atyfb_save.b[enter][i] = readb(&par->aty_cmap_regs->lut);
+		writeb(i, &par->aty_cmap_regs->windex);
+		writeb(atyfb_save.r[1 - enter][i],
+		       &par->aty_cmap_regs->lut);
+		writeb(atyfb_save.g[1 - enter][i],
+		       &par->aty_cmap_regs->lut);
+		writeb(atyfb_save.b[1 - enter][i],
+		       &par->aty_cmap_regs->lut);
+	}
+}
+
+static void atyfb_palette(int enter)
+{
+	struct atyfb_par *par;
+	struct fb_info *info;
+	int i;
+
+	for (i = 0; i < FB_MAX; i++) {
+		info = registered_fb[i];
+		if (info && info->fbops == &atyfb_ops) {
+			par = (struct atyfb_par *) info->par;
+			
+			atyfb_save_palette(par, enter);
+			if (enter) {
+				atyfb_save.yoffset = info->var.yoffset;
+				info->var.yoffset = 0;
+				set_off_pitch(par, info);
+			} else {
+				info->var.yoffset = atyfb_save.yoffset;
+				set_off_pitch(par, info);
+			}
+			aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
+			break;
+		}
+	}
+}
+#endif /* __sparc__ */
+
+
+
+#if defined(CONFIG_PM) && defined(CONFIG_PCI)
+
+/* Power management routines. Those are used for PowerBook sleep.
+ */
+static int aty_power_mgmt(int sleep, struct atyfb_par *par)
+{
+	u32 pm;
+	int timeout;
+
+	pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+	pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG;
+	aty_st_lcd(POWER_MANAGEMENT, pm, par);
+	pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+
+	timeout = 2000;
+	if (sleep) {
+		/* Sleep */
+		pm &= ~PWR_MGT_ON;
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+		udelay(10);
+		pm &= ~(PWR_BLON | AUTO_PWR_UP);
+		pm |= SUSPEND_NOW;
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+		udelay(10);
+		pm |= PWR_MGT_ON;
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+		do {
+			pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+			mdelay(1);
+			if ((--timeout) == 0)
+				break;
+		} while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND);
+	} else {
+		/* Wakeup */
+		pm &= ~PWR_MGT_ON;
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+		udelay(10);
+		pm &= ~SUSPEND_NOW;
+		pm |= (PWR_BLON | AUTO_PWR_UP);
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+		udelay(10);
+		pm |= PWR_MGT_ON;
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+		do {
+			pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+			mdelay(1);
+			if ((--timeout) == 0)
+				break;
+		} while ((pm & PWR_MGT_STATUS_MASK) != 0);
+	}
+	mdelay(500);
+
+	return timeout ? 0 : -EIO;
+}
+
+static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+#ifdef CONFIG_PPC_PMAC
+	/* HACK ALERT ! Once I find a proper way to say to each driver
+	 * individually what will happen with it's PCI slot, I'll change
+	 * that. On laptops, the AGP slot is just unclocked, so D2 is
+	 * expected, while on desktops, the card is powered off
+	 */
+	if (state >= 3)
+		state = 2;
+#endif /* CONFIG_PPC_PMAC */
+
+	if (state != 2 || state == pdev->dev.power.power_state)
+		return 0;
+
+	acquire_console_sem();
+
+	fb_set_suspend(info, 1);
+
+	/* Idle & reset engine */
+	wait_for_idle(par);
+	aty_reset_engine(par);
+
+	/* Blank display and LCD */
+	atyfb_blank(FB_BLANK_POWERDOWN, info);
+
+	par->asleep = 1;
+	par->lock_blank = 1;
+
+	/* Set chip to "suspend" mode */
+	if (aty_power_mgmt(1, par)) {
+		par->asleep = 0;
+		par->lock_blank = 0;
+		atyfb_blank(FB_BLANK_UNBLANK, info);
+		fb_set_suspend(info, 0);
+		release_console_sem();
+		return -EIO;
+	}
+
+	release_console_sem();
+
+	pdev->dev.power.power_state = state;
+
+	return 0;
+}
+
+static int atyfb_pci_resume(struct pci_dev *pdev)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+	if (pdev->dev.power.power_state == 0)
+		return 0;
+
+	acquire_console_sem();
+
+	if (pdev->dev.power.power_state == 2)
+		aty_power_mgmt(0, par);
+	par->asleep = 0;
+
+	/* Restore display */
+	atyfb_set_par(info);
+
+	/* Refresh */
+	fb_set_suspend(info, 0);
+
+	/* Unblank */
+	par->lock_blank = 0;
+	atyfb_blank(FB_BLANK_UNBLANK, info);
+
+	release_console_sem();
+
+	pdev->dev.power.power_state = PMSG_ON;
+
+	return 0;
+}
+
+#endif /*  defined(CONFIG_PM) && defined(CONFIG_PCI) */
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+
+    /*
+     *   LCD backlight control
+     */
+
+static int backlight_conv[] = {
+	0x00, 0x3f, 0x4c, 0x59, 0x66, 0x73, 0x80, 0x8d,
+	0x9a, 0xa7, 0xb4, 0xc1, 0xcf, 0xdc, 0xe9, 0xff
+};
+
+static int aty_set_backlight_enable(int on, int level, void *data)
+{
+	struct fb_info *info = (struct fb_info *) data;
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par);
+
+	reg |= (BLMOD_EN | BIASMOD_EN);
+	if (on && level > BACKLIGHT_OFF) {
+		reg &= ~BIAS_MOD_LEVEL_MASK;
+		reg |= (backlight_conv[level] << BIAS_MOD_LEVEL_SHIFT);
+	} else {
+		reg &= ~BIAS_MOD_LEVEL_MASK;
+		reg |= (backlight_conv[0] << BIAS_MOD_LEVEL_SHIFT);
+	}
+	aty_st_lcd(LCD_MISC_CNTL, reg, par);
+	return 0;
+}
+
+static int aty_set_backlight_level(int level, void *data)
+{
+	return aty_set_backlight_enable(1, level, data);
+}
+
+static struct backlight_controller aty_backlight_controller = {
+	aty_set_backlight_enable,
+	aty_set_backlight_level
+};
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+static void __init aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
+{
+	const int ragepro_tbl[] = {
+		44, 50, 55, 66, 75, 80, 100
+	};
+	const int ragexl_tbl[] = {
+		50, 66, 75, 83, 90, 95, 100, 105,
+		110, 115, 120, 125, 133, 143, 166
+	};
+	const int *refresh_tbl;
+	int i, size;
+
+	if (IS_XL(par->pci_id) || IS_MOBILITY(par->pci_id)) {
+		refresh_tbl = ragexl_tbl;
+		size = sizeof(ragexl_tbl)/sizeof(int);
+	} else {
+		refresh_tbl = ragepro_tbl;
+		size = sizeof(ragepro_tbl)/sizeof(int);
+	}
+
+	for (i=0; i < size; i++) {
+		if (xclk < refresh_tbl[i])
+		break;
+	}
+	par->mem_refresh_rate = i;
+}
+
+    /*
+     *  Initialisation
+     */
+
+static struct fb_info *fb_list = NULL;
+
+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;
+	struct fb_var_screeninfo var;
+	u8 pll_ref_div;
+	u32 i;
+#if defined(CONFIG_PPC)
+	int sense;
+#endif
+
+	init_waitqueue_head(&par->vblank.wait);
+	spin_lock_init(&par->int_lock);
+
+	par->aty_cmap_regs =
+	    (struct aty_cmap_regs __iomem *) (par->ati_regbase + 0xc0);
+
+#ifdef CONFIG_PPC_PMAC
+	/* The Apple iBook1 uses non-standard memory frequencies. We detect it
+	 * and set the frequency manually. */
+	if (machine_is_compatible("PowerBook2,1")) {
+		par->pll_limits.mclk = 70;
+		par->pll_limits.xclk = 53;
+	}
+#endif
+	if (pll)
+		par->pll_limits.pll_max = pll;
+	if (mclk)
+		par->pll_limits.mclk = mclk;
+	if (xclk)
+		par->pll_limits.xclk = xclk;
+
+	aty_calc_mem_refresh(par, par->pll_limits.xclk);
+	par->pll_per = 1000000/par->pll_limits.pll_max;
+	par->mclk_per = 1000000/par->pll_limits.mclk;
+	par->xclk_per = 1000000/par->pll_limits.xclk;
+
+	par->ref_clk_per = 1000000000000ULL / 14318180;
+	xtal = "14.31818";
+
+#ifdef CONFIG_FB_ATY_GX
+	if (!M64_HAS(INTEGRATED)) {
+		u32 stat0;
+		u8 dac_type, dac_subtype, clk_type;
+		stat0 = aty_ld_le32(CONFIG_STAT0, par);
+		par->bus_type = (stat0 >> 0) & 0x07;
+		par->ram_type = (stat0 >> 3) & 0x07;
+		ramname = aty_gx_ram[par->ram_type];
+		/* FIXME: clockchip/RAMDAC probing? */
+		dac_type = (aty_ld_le32(DAC_CNTL, par) >> 16) & 0x07;
+#ifdef CONFIG_ATARI
+		clk_type = CLK_ATI18818_1;
+		dac_type = (stat0 >> 9) & 0x07;
+		if (dac_type == 0x07)
+			dac_subtype = DAC_ATT20C408;
+		else
+			dac_subtype = (aty_ld_8(SCRATCH_REG1 + 1, par) & 0xF0) | dac_type;
+#else
+		dac_type = DAC_IBMRGB514;
+		dac_subtype = DAC_IBMRGB514;
+		clk_type = CLK_IBMRGB514;
+#endif
+		switch (dac_subtype) {
+		case DAC_IBMRGB514:
+			par->dac_ops = &aty_dac_ibm514;
+			break;
+		case DAC_ATI68860_B:
+		case DAC_ATI68860_C:
+			par->dac_ops = &aty_dac_ati68860b;
+			break;
+		case DAC_ATT20C408:
+		case DAC_ATT21C498:
+			par->dac_ops = &aty_dac_att21c498;
+			break;
+		default:
+			PRINTKI("aty_init: DAC type not implemented yet!\n");
+			par->dac_ops = &aty_dac_unsupported;
+			break;
+		}
+		switch (clk_type) {
+		case CLK_ATI18818_1:
+			par->pll_ops = &aty_pll_ati18818_1;
+			break;
+		case CLK_STG1703:
+			par->pll_ops = &aty_pll_stg1703;
+			break;
+		case CLK_CH8398:
+			par->pll_ops = &aty_pll_ch8398;
+			break;
+		case CLK_ATT20C408:
+			par->pll_ops = &aty_pll_att20c408;
+			break;
+		case CLK_IBMRGB514:
+			par->pll_ops = &aty_pll_ibm514;
+			break;
+		default:
+			PRINTKI("aty_init: CLK type not implemented yet!");
+			par->pll_ops = &aty_pll_unsupported;
+			break;
+		}
+	}
+#endif /* CONFIG_FB_ATY_GX */
+#ifdef CONFIG_FB_ATY_CT
+	if (M64_HAS(INTEGRATED)) {
+		par->dac_ops = &aty_dac_ct;
+		par->pll_ops = &aty_pll_ct;
+		par->bus_type = PCI;
+#ifdef CONFIG_FB_ATY_XL_INIT
+		if (IS_XL(par->pci_id))
+			atyfb_xl_init(info);
+#endif
+		par->ram_type = (aty_ld_le32(CONFIG_STAT0, par) & 0x07);
+		ramname = aty_ct_ram[par->ram_type];
+		/* for many chips, the mclk is 67 MHz for SDRAM, 63 MHz otherwise */
+		if (par->pll_limits.mclk == 67 && par->ram_type < SDRAM)
+			par->pll_limits.mclk = 63;
+	}
+
+	if (M64_HAS(GTB_DSP)
+	    && (pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par))) {
+		int diff1, diff2;
+		diff1 = 510 * 14 / pll_ref_div - par->pll_limits.pll_max;
+		diff2 = 510 * 29 / pll_ref_div - par->pll_limits.pll_max;
+		if (diff1 < 0)
+			diff1 = -diff1;
+		if (diff2 < 0)
+			diff2 = -diff2;
+		if (diff2 < diff1) {
+			par->ref_clk_per = 1000000000000ULL / 29498928;
+			xtal = "29.498928";
+		}
+	}
+#endif /* CONFIG_FB_ATY_CT */
+
+	/* save previous video mode */
+	aty_get_crtc(par, &saved_crtc);
+	if(par->pll_ops->get_pll)
+		par->pll_ops->get_pll(info, &saved_pll);
+
+	i = aty_ld_le32(MEM_CNTL, par);
+	gtb_memsize = M64_HAS(GTB_DSP);
+	if (gtb_memsize)
+		switch (i & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
+		case MEM_SIZE_512K:
+			info->fix.smem_len = 0x80000;
+			break;
+		case MEM_SIZE_1M:
+			info->fix.smem_len = 0x100000;
+			break;
+		case MEM_SIZE_2M_GTB:
+			info->fix.smem_len = 0x200000;
+			break;
+		case MEM_SIZE_4M_GTB:
+			info->fix.smem_len = 0x400000;
+			break;
+		case MEM_SIZE_6M_GTB:
+			info->fix.smem_len = 0x600000;
+			break;
+		case MEM_SIZE_8M_GTB:
+			info->fix.smem_len = 0x800000;
+			break;
+		default:
+			info->fix.smem_len = 0x80000;
+	} else
+		switch (i & MEM_SIZE_ALIAS) {
+		case MEM_SIZE_512K:
+			info->fix.smem_len = 0x80000;
+			break;
+		case MEM_SIZE_1M:
+			info->fix.smem_len = 0x100000;
+			break;
+		case MEM_SIZE_2M:
+			info->fix.smem_len = 0x200000;
+			break;
+		case MEM_SIZE_4M:
+			info->fix.smem_len = 0x400000;
+			break;
+		case MEM_SIZE_6M:
+			info->fix.smem_len = 0x600000;
+			break;
+		case MEM_SIZE_8M:
+			info->fix.smem_len = 0x800000;
+			break;
+		default:
+			info->fix.smem_len = 0x80000;
+		}
+
+	if (M64_HAS(MAGIC_VRAM_SIZE)) {
+		if (aty_ld_le32(CONFIG_STAT1, par) & 0x40000000)
+			info->fix.smem_len += 0x400000;
+	}
+
+	if (vram) {
+		info->fix.smem_len = vram * 1024;
+		i = i & ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS);
+		if (info->fix.smem_len <= 0x80000)
+			i |= MEM_SIZE_512K;
+		else if (info->fix.smem_len <= 0x100000)
+			i |= MEM_SIZE_1M;
+		else if (info->fix.smem_len <= 0x200000)
+			i |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M;
+		else if (info->fix.smem_len <= 0x400000)
+			i |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M;
+		else if (info->fix.smem_len <= 0x600000)
+			i |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M;
+		else
+			i |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M;
+		aty_st_le32(MEM_CNTL, i, par);
+	}
+
+	/*
+	 *  Reg Block 0 (CT-compatible block) is at mmio_start
+	 *  Reg Block 1 (multimedia extensions) is at mmio_start - 0x400
+	 */
+	if (M64_HAS(GX)) {
+		info->fix.mmio_len = 0x400;
+		info->fix.accel = FB_ACCEL_ATI_MACH64GX;
+	} else if (M64_HAS(CT)) {
+		info->fix.mmio_len = 0x400;
+		info->fix.accel = FB_ACCEL_ATI_MACH64CT;
+	} else if (M64_HAS(VT)) {
+		info->fix.mmio_start -= 0x400;
+		info->fix.mmio_len = 0x800;
+		info->fix.accel = FB_ACCEL_ATI_MACH64VT;
+	} else {/* GT */
+		info->fix.mmio_start -= 0x400;
+		info->fix.mmio_len = 0x800;
+		info->fix.accel = FB_ACCEL_ATI_MACH64GT;
+	}
+
+	PRINTKI("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK, %d MHz XCLK\n",
+	       info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len >> 20),
+	       info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal, par->pll_limits.pll_max,
+	       par->pll_limits.mclk, par->pll_limits.xclk);
+
+#if defined(DEBUG) && defined(CONFIG_ATY_CT)
+	if (M64_HAS(INTEGRATED)) {
+		int i;
+		printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL EXT_MEM_CNTL CRTC_GEN_CNTL "
+		       "DSP_CONFIG DSP_ON_OFF CLOCK_CNTL\n"
+		       "debug atyfb: %08x %08x %08x %08x     %08x      %08x   %08x   %08x\n"
+		       "debug atyfb: PLL",
+			aty_ld_le32(BUS_CNTL, par), aty_ld_le32(DAC_CNTL, par),
+			aty_ld_le32(MEM_CNTL, par), aty_ld_le32(EXT_MEM_CNTL, par),
+			aty_ld_le32(CRTC_GEN_CNTL, par), aty_ld_le32(DSP_CONFIG, par),
+			aty_ld_le32(DSP_ON_OFF, par), aty_ld_le32(CLOCK_CNTL, par));
+		for (i = 0; i < 40; i++)
+			printk(" %02x", aty_ld_pll_ct(i, par));
+		printk("\n");
+	}
+#endif
+	if(par->pll_ops->init_pll)
+		par->pll_ops->init_pll(info, &par->pll);
+
+	/*
+	 *  Last page of 8 MB (4 MB on ISA) aperture is MMIO
+	 *  FIXME: we should use the auxiliary aperture instead so we can access
+	 *  the full 8 MB of video RAM on 8 MB boards
+	 */
+
+	if (!par->aux_start &&
+		(info->fix.smem_len == 0x800000 || (par->bus_type == ISA && info->fix.smem_len == 0x400000)))
+		info->fix.smem_len -= GUI_RESERVE;
+
+	/*
+	 *  Disable register access through the linear aperture
+	 *  if the auxiliary aperture is used so we can access
+	 *  the full 8 MB of video RAM on 8 MB boards.
+	 */
+	if (par->aux_start)
+		aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par);
+
+#ifdef CONFIG_MTRR
+	par->mtrr_aper = -1;
+	par->mtrr_reg = -1;
+	if (!nomtrr) {
+		/* Cover the whole resource. */
+		 par->mtrr_aper = mtrr_add(par->res_start, par->res_size, MTRR_TYPE_WRCOMB, 1);
+		 if (par->mtrr_aper >= 0 && !par->aux_start) {
+			/* Make a hole for mmio. */
+			par->mtrr_reg = mtrr_add(par->res_start + 0x800000 - GUI_RESERVE,
+				GUI_RESERVE, MTRR_TYPE_UNCACHABLE, 1);
+			if (par->mtrr_reg < 0) {
+				mtrr_del(par->mtrr_aper, 0, 0);
+				par->mtrr_aper = -1;
+			}
+		 }
+	}
+#endif
+
+	info->fbops = &atyfb_ops;
+	info->pseudo_palette = pseudo_palette;
+	info->flags = FBINFO_FLAG_DEFAULT;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if (M64_HAS(G3_PB_1_1) && machine_is_compatible("PowerBook1,1")) {
+		/* these bits let the 101 powerbook wake up from sleep -- paulus */
+		aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par)
+			   | (USE_F32KHZ | TRISTATE_MEM_EN), par);
+	} else if (M64_HAS(MOBIL_BUS))
+		register_backlight_controller(&aty_backlight_controller, info, "ati");
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+	memset(&var, 0, sizeof(var));
+#ifdef CONFIG_PPC
+	if (_machine == _MACH_Pmac) {
+		/*
+		 *  FIXME: The NVRAM stuff should be put in a Mac-specific file, as it
+		 *         applies to all Mac video cards
+		 */
+		if (mode) {
+			if (!mac_find_mode(&var, info, mode, 8))
+				var = default_var;
+		} else {
+			if (default_vmode == VMODE_CHOOSE) {
+				if (M64_HAS(G3_PB_1024x768))
+					/* G3 PowerBook with 1024x768 LCD */
+					default_vmode = VMODE_1024_768_60;
+				else if (machine_is_compatible("iMac"))
+					default_vmode = VMODE_1024_768_75;
+				else if (machine_is_compatible
+					 ("PowerBook2,1"))
+					/* iBook with 800x600 LCD */
+					default_vmode = VMODE_800_600_60;
+				else
+					default_vmode = VMODE_640_480_67;
+				sense = read_aty_sense(par);
+				PRINTKI("monitor sense=%x, mode %d\n",
+					sense,  mac_map_monitor_sense(sense));
+			}
+			if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+				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;
+		}
+	} 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 ||
+#endif
+	    !fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8))
+		var = default_var;
+
+	if (noaccel)
+		var.accel_flags &= ~FB_ACCELF_TEXT;
+	else
+		var.accel_flags |= FB_ACCELF_TEXT;
+
+	if (comp_sync != -1) {
+		if (!comp_sync)
+			var.sync &= ~FB_SYNC_COMP_HIGH_ACT;
+		else
+			var.sync |= FB_SYNC_COMP_HIGH_ACT;
+	}
+
+	if (var.yres == var.yres_virtual) {
+		u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2));
+		var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual;
+		if (var.yres_virtual < var.yres)
+			var.yres_virtual = var.yres;
+	}
+
+	if (atyfb_check_var(&var, info)) {
+		PRINTKE("can't set default video mode\n");
+		goto aty_init_exit;
+	}
+
+#ifdef __sparc__
+	atyfb_save_palette(par, 0);
+#endif
+
+#ifdef CONFIG_FB_ATY_CT
+	if (!noaccel && M64_HAS(INTEGRATED))
+		aty_init_cursor(info);
+#endif /* CONFIG_FB_ATY_CT */
+	info->var = var;
+
+	fb_alloc_cmap(&info->cmap, 256, 0);
+
+	if (register_framebuffer(info) < 0)
+		goto aty_init_exit;
+
+	fb_list = info;
+
+	PRINTKI("fb%d: %s frame buffer device on %s\n",
+	       info->node, info->fix.id, name);
+	return 0;
+
+aty_init_exit:
+	/* restore video mode */
+	aty_set_crtc(par, &saved_crtc);
+	par->pll_ops->set_pll(info, &saved_pll);
+
+#ifdef CONFIG_MTRR
+	if (par->mtrr_reg >= 0) {
+	    mtrr_del(par->mtrr_reg, 0, 0);
+	    par->mtrr_reg = -1;
+	}
+	if (par->mtrr_aper >= 0) {
+	    mtrr_del(par->mtrr_aper, 0, 0);
+	    par->mtrr_aper = -1;
+	}
+#endif
+	return -1;
+}
+
+#ifdef CONFIG_ATARI
+static int __init store_video_par(char *video_str, unsigned char m64_num)
+{
+	char *p;
+	unsigned long vmembase, size, guiregbase;
+
+	PRINTKI("store_video_par() '%s' \n", video_str);
+
+	if (!(p = strsep(&video_str, ";")) || !*p)
+		goto mach64_invalid;
+	vmembase = simple_strtoul(p, NULL, 0);
+	if (!(p = strsep(&video_str, ";")) || !*p)
+		goto mach64_invalid;
+	size = simple_strtoul(p, NULL, 0);
+	if (!(p = strsep(&video_str, ";")) || !*p)
+		goto mach64_invalid;
+	guiregbase = simple_strtoul(p, NULL, 0);
+
+	phys_vmembase[m64_num] = vmembase;
+	phys_size[m64_num] = size;
+	phys_guiregbase[m64_num] = guiregbase;
+	PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size,
+	       guiregbase);
+	return 0;
+
+      mach64_invalid:
+	phys_vmembase[m64_num] = 0;
+	return -1;
+}
+#endif /* CONFIG_ATARI */
+
+    /*
+     *  Blank the display.
+     */
+
+static int atyfb_blank(int blank, struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u8 gen_cntl;
+
+	if (par->lock_blank || par->asleep)
+		return 0;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if ((_machine == _MACH_Pmac) && blank)
+		set_backlight_enable(0);
+#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
+	if (par->lcd_table && blank &&
+	    (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
+		u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+		pm &= ~PWR_BLON;
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+	}
+#endif
+
+	gen_cntl = aty_ld_8(CRTC_GEN_CNTL, par);
+	switch (blank) {
+        	case FB_BLANK_UNBLANK:
+			gen_cntl &= ~(0x4c);
+			break;
+		case FB_BLANK_NORMAL:
+			gen_cntl |= 0x40;
+			break;
+		case FB_BLANK_VSYNC_SUSPEND:
+			gen_cntl |= 0x8;
+			break;
+		case FB_BLANK_HSYNC_SUSPEND:
+			gen_cntl |= 0x4;
+			break;
+		case FB_BLANK_POWERDOWN:
+			gen_cntl |= 0x4c;
+			break;
+	}
+	aty_st_8(CRTC_GEN_CNTL, gen_cntl, par);
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if ((_machine == _MACH_Pmac) && !blank)
+		set_backlight_enable(1);
+#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
+	if (par->lcd_table && !blank &&
+	    (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
+		u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+		pm |= PWR_BLON;
+		aty_st_lcd(POWER_MANAGEMENT, pm, par);
+	}
+#endif
+
+	return 0;
+}
+
+static void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue,
+		       const struct atyfb_par *par)
+{
+#ifdef CONFIG_ATARI
+	out_8(&par->aty_cmap_regs->windex, regno);
+	out_8(&par->aty_cmap_regs->lut, red);
+	out_8(&par->aty_cmap_regs->lut, green);
+	out_8(&par->aty_cmap_regs->lut, blue);
+#else
+	writeb(regno, &par->aty_cmap_regs->windex);
+	writeb(red, &par->aty_cmap_regs->lut);
+	writeb(green, &par->aty_cmap_regs->lut);
+	writeb(blue, &par->aty_cmap_regs->lut);
+#endif
+}
+
+    /*
+     *  Set a single color register. The values supplied are already
+     *  rounded down to the hardware's capabilities (according to the
+     *  entries in the var structure). Return != 0 for invalid regno.
+     *  !! 4 & 8 =  PSEUDO, > 8 = DIRECTCOLOR
+     */
+
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+	u_int transp, struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	int i, depth;
+	u32 *pal = info->pseudo_palette;
+
+	depth = info->var.bits_per_pixel;
+	if (depth == 16)
+		depth = (info->var.green.length == 5) ? 15 : 16;
+
+	if (par->asleep)
+		return 0;
+
+	if (regno > 255 ||
+	    (depth == 16 && regno > 63) ||
+	    (depth == 15 && regno > 31))
+		return 1;
+
+	red >>= 8;
+	green >>= 8;
+	blue >>= 8;
+
+	par->palette[regno].red = red;
+	par->palette[regno].green = green;
+	par->palette[regno].blue = blue;
+
+	if (regno < 16) {
+		switch (depth) {
+		case 15:
+			pal[regno] = (regno << 10) | (regno << 5) | regno;
+			break;
+		case 16:
+			pal[regno] = (regno << 11) | (regno << 5) | regno;
+			break;
+		case 24:
+			pal[regno] = (regno << 16) | (regno << 8) | regno;
+			break;
+		case 32:
+			i = (regno << 8) | regno;
+			pal[regno] = (i << 16) | i;
+			break;
+		}
+	}
+
+	i = aty_ld_8(DAC_CNTL, par) & 0xfc;
+	if (M64_HAS(EXTRA_BRIGHT))
+		i |= 0x2; /* DAC_CNTL | 0x2 turns off the extra brightness for gt */
+	aty_st_8(DAC_CNTL, i, par);
+	aty_st_8(DAC_MASK, 0xff, par);
+
+	if (M64_HAS(INTEGRATED)) {
+		if (depth == 16) {
+			if (regno < 32)
+				aty_st_pal(regno << 3, red,
+					   par->palette[regno<<1].green,
+					   blue, par);
+			red = par->palette[regno>>1].red;
+			blue = par->palette[regno>>1].blue;
+			regno <<= 2;
+		} else if (depth == 15) {
+			regno <<= 3;
+			for(i = 0; i < 8; i++) {
+			    aty_st_pal(regno + i, red, green, blue, par);
+			}
+		}
+	}
+	aty_st_pal(regno, red, green, blue, par);
+
+	return 0;
+}
+
+#ifdef CONFIG_PCI
+
+#ifdef __sparc__
+
+extern void (*prom_palette) (int);
+
+static int __devinit atyfb_setup_sparc(struct pci_dev *pdev,
+			struct fb_info *info, unsigned long addr)
+{
+	extern int con_is_present(void);
+
+	struct atyfb_par *par = info->par;
+	struct pcidev_cookie *pcp;
+	char prop[128];
+	int node, len, i, j, ret;
+	u32 mem, chip_id;
+
+	/* Do not attach when we have a serial console. */
+	if (!con_is_present())
+		return -ENXIO;
+
+	/*
+	 * Map memory-mapped registers.
+	 */
+	par->ati_regbase = (void *)addr + 0x7ffc00UL;
+	info->fix.mmio_start = addr + 0x7ffc00UL;
+
+	/*
+	 * Map in big-endian aperture.
+	 */
+	info->screen_base = (char *) (addr + 0x800000UL);
+	info->fix.smem_start = addr + 0x800000UL;
+
+	/*
+	 * Figure mmap addresses from PCI config space.
+	 * Split Framebuffer in big- and little-endian halfs.
+	 */
+	for (i = 0; i < 6 && pdev->resource[i].start; i++)
+		/* nothing */ ;
+	j = i + 4;
+
+	par->mmap_map = kmalloc(j * sizeof(*par->mmap_map), GFP_ATOMIC);
+	if (!par->mmap_map) {
+		PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n");
+		return -ENOMEM;
+	}
+	memset(par->mmap_map, 0, j * sizeof(*par->mmap_map));
+
+	for (i = 0, j = 2; i < 6 && pdev->resource[i].start; i++) {
+		struct resource *rp = &pdev->resource[i];
+		int io, breg = PCI_BASE_ADDRESS_0 + (i << 2);
+		unsigned long base;
+		u32 size, pbase;
+
+		base = rp->start;
+
+		io = (rp->flags & IORESOURCE_IO);
+
+		size = rp->end - base + 1;
+
+		pci_read_config_dword(pdev, breg, &pbase);
+
+		if (io)
+			size &= ~1;
+
+		/*
+		 * Map the framebuffer a second time, this time without
+		 * the braindead _PAGE_IE setting. This is used by the
+		 * fixed Xserver, but we need to maintain the old mapping
+		 * to stay compatible with older ones...
+		 */
+		if (base == addr) {
+			par->mmap_map[j].voff = (pbase + 0x10000000) & PAGE_MASK;
+			par->mmap_map[j].poff = base & PAGE_MASK;
+			par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK;
+			par->mmap_map[j].prot_mask = _PAGE_CACHE;
+			par->mmap_map[j].prot_flag = _PAGE_E;
+			j++;
+		}
+
+		/*
+		 * Here comes the old framebuffer mapping with _PAGE_IE
+		 * set for the big endian half of the framebuffer...
+		 */
+		if (base == addr) {
+			par->mmap_map[j].voff = (pbase + 0x800000) & PAGE_MASK;
+			par->mmap_map[j].poff = (base + 0x800000) & PAGE_MASK;
+			par->mmap_map[j].size = 0x800000;
+			par->mmap_map[j].prot_mask = _PAGE_CACHE;
+			par->mmap_map[j].prot_flag = _PAGE_E | _PAGE_IE;
+			size -= 0x800000;
+			j++;
+		}
+
+		par->mmap_map[j].voff = pbase & PAGE_MASK;
+		par->mmap_map[j].poff = base & PAGE_MASK;
+		par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK;
+		par->mmap_map[j].prot_mask = _PAGE_CACHE;
+		par->mmap_map[j].prot_flag = _PAGE_E;
+		j++;
+	}
+
+	if((ret = correct_chipset(par)))
+		return ret;
+
+	if (IS_XL(pdev->device)) {
+		/*
+		 * Fix PROMs idea of MEM_CNTL settings...
+		 */
+		mem = aty_ld_le32(MEM_CNTL, par);
+		chip_id = aty_ld_le32(CONFIG_CHIP_ID, par);
+		if (((chip_id & CFG_CHIP_TYPE) == VT_CHIP_ID) && !((chip_id >> 24) & 1)) {
+			switch (mem & 0x0f) {
+			case 3:
+				mem = (mem & ~(0x0f)) | 2;
+				break;
+			case 7:
+				mem = (mem & ~(0x0f)) | 3;
+				break;
+			case 9:
+				mem = (mem & ~(0x0f)) | 4;
+				break;
+			case 11:
+				mem = (mem & ~(0x0f)) | 5;
+				break;
+			default:
+				break;
+			}
+			if ((aty_ld_le32(CONFIG_STAT0, par) & 7) >= SDRAM)
+				mem &= ~(0x00700000);
+		}
+		mem &= ~(0xcf80e000);	/* Turn off all undocumented bits. */
+		aty_st_le32(MEM_CNTL, mem, par);
+	}
+
+	/*
+	 * If this is the console device, we will set default video
+	 * settings to what the PROM left us with.
+	 */
+	node = prom_getchild(prom_root_node);
+	node = prom_searchsiblings(node, "aliases");
+	if (node) {
+		len = prom_getproperty(node, "screen", prop, sizeof(prop));
+		if (len > 0) {
+			prop[len] = '\0';
+			node = prom_finddevice(prop);
+		} else
+			node = 0;
+	}
+
+	pcp = pdev->sysdata;
+	if (node == pcp->prom_node) {
+		struct fb_var_screeninfo *var = &default_var;
+		unsigned int N, P, Q, M, T, R;
+		u32 v_total, h_total;
+		struct crtc crtc;
+		u8 pll_regs[16];
+		u8 clock_cntl;
+
+		crtc.vxres = prom_getintdefault(node, "width", 1024);
+		crtc.vyres = prom_getintdefault(node, "height", 768);
+		var->bits_per_pixel = prom_getintdefault(node, "depth", 8);
+		var->xoffset = var->yoffset = 0;
+		crtc.h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
+		crtc.h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
+		crtc.v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
+		crtc.v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
+		crtc.gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
+		aty_crtc_to_var(&crtc, var);
+
+		h_total = var->xres + var->right_margin + var->hsync_len + var->left_margin;
+		v_total = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
+
+		/*
+		 * Read the PLL to figure actual Refresh Rate.
+		 */
+		clock_cntl = aty_ld_8(CLOCK_CNTL, par);
+		/* DPRINTK("CLOCK_CNTL %02x\n", clock_cntl); */
+		for (i = 0; i < 16; i++)
+			pll_regs[i] = aty_ld_pll_ct(i, par);
+
+		/*
+		 * PLL Reference Divider M:
+		 */
+		M = pll_regs[2];
+
+		/*
+		 * PLL Feedback Divider N (Dependant on CLOCK_CNTL):
+		 */
+		N = pll_regs[7 + (clock_cntl & 3)];
+
+		/*
+		 * PLL Post Divider P (Dependant on CLOCK_CNTL):
+		 */
+		P = 1 << (pll_regs[6] >> ((clock_cntl & 3) << 1));
+
+		/*
+		 * PLL Divider Q:
+		 */
+		Q = N / P;
+
+		/*
+		 * Target Frequency:
+		 *
+		 *      T * M
+		 * Q = -------
+		 *      2 * R
+		 *
+		 * where R is XTALIN (= 14318 or 29498 kHz).
+		 */
+		if (IS_XL(pdev->device))
+			R = 29498;
+		else
+			R = 14318;
+
+		T = 2 * Q * R / M;
+
+		default_var.pixclock = 1000000000 / T;
+	}
+
+	return 0;
+}
+
+#else /* __sparc__ */
+
+#ifdef __i386__
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+static void aty_init_lcd(struct atyfb_par *par, u32 bios_base)
+{
+	u32 driv_inf_tab, sig;
+	u16 lcd_ofs;
+
+	/* To support an LCD panel, we should know it's dimensions and
+	 *  it's desired pixel clock.
+	 * There are two ways to do it:
+	 *  - Check the startup video mode and calculate the panel
+	 *    size from it. This is unreliable.
+	 *  - Read it from the driver information table in the video BIOS.
+	*/
+	/* Address of driver information table is at offset 0x78. */
+	driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78));
+
+	/* Check for the driver information table signature. */
+	sig = (*(u32 *)driv_inf_tab);
+	if ((sig == 0x54504c24) || /* Rage LT pro */
+		(sig == 0x544d5224) || /* Rage mobility */
+		(sig == 0x54435824) || /* Rage XC */
+		(sig == 0x544c5824)) { /* Rage XL */
+		PRINTKI("BIOS contains driver information table.\n");
+		lcd_ofs = (*(u16 *)(driv_inf_tab + 10));
+		par->lcd_table = 0;
+		if (lcd_ofs != 0) {
+			par->lcd_table = bios_base + lcd_ofs;
+		}
+	}
+
+	if (par->lcd_table != 0) {
+		char model[24];
+		char strbuf[16];
+		char refresh_rates_buf[100];
+		int id, tech, f, i, m, default_refresh_rate;
+		char *txtcolour;
+		char *txtmonitor;
+		char *txtdual;
+		char *txtformat;
+		u16 width, height, panel_type, refresh_rates;
+		u16 *lcdmodeptr;
+		u32 format;
+		u8 lcd_refresh_rates[16] = {50,56,60,67,70,72,75,76,85,90,100,120,140,150,160,200};
+		/* The most important information is the panel size at
+		 * offset 25 and 27, but there's some other nice information
+		 * which we print to the screen.
+		 */
+		id = *(u8 *)par->lcd_table;
+		strncpy(model,(char *)par->lcd_table+1,24);
+		model[23]=0;
+
+		width = par->lcd_width = *(u16 *)(par->lcd_table+25);
+		height = par->lcd_height = *(u16 *)(par->lcd_table+27);
+		panel_type = *(u16 *)(par->lcd_table+29);
+		if (panel_type & 1)
+			txtcolour = "colour";
+		else
+			txtcolour = "monochrome";
+		if (panel_type & 2)
+			txtdual = "dual (split) ";
+		else
+			txtdual = "";
+		tech = (panel_type>>2) & 63;
+		switch (tech) {
+		case 0:
+			txtmonitor = "passive matrix";
+			break;
+		case 1:
+			txtmonitor = "active matrix";
+			break;
+		case 2:
+			txtmonitor = "active addressed STN";
+			break;
+		case 3:
+			txtmonitor = "EL";
+			break;
+		case 4:
+			txtmonitor = "plasma";
+			break;
+		default:
+			txtmonitor = "unknown";
+		}
+		format = *(u32 *)(par->lcd_table+57);
+		if (tech == 0 || tech == 2) {
+			switch (format & 7) {
+			case 0:
+				txtformat = "12 bit interface";
+				break;
+			case 1:
+				txtformat = "16 bit interface";
+				break;
+			case 2:
+				txtformat = "24 bit interface";
+				break;
+			default:
+				txtformat = "unkown format";
+			}
+		} else {
+			switch (format & 7) {
+			case 0:
+				txtformat = "8 colours";
+				break;
+			case 1:
+				txtformat = "512 colours";
+				break;
+			case 2:
+				txtformat = "4096 colours";
+				break;
+			case 4:
+				txtformat = "262144 colours (LT mode)";
+				break;
+			case 5:
+				txtformat = "16777216 colours";
+				break;
+			case 6:
+				txtformat = "262144 colours (FDPI-2 mode)";
+				break;
+			default:
+				txtformat = "unkown format";
+			}
+		}
+		PRINTKI("%s%s %s monitor detected: %s\n",
+			txtdual ,txtcolour, txtmonitor, model);
+		PRINTKI("       id=%d, %dx%d pixels, %s\n",
+			id, width, height, txtformat);
+		refresh_rates_buf[0] = 0;
+		refresh_rates = *(u16 *)(par->lcd_table+62);
+		m = 1;
+		f = 0;
+		for (i=0;i<16;i++) {
+			if (refresh_rates & m) {
+				if (f == 0) {
+					sprintf(strbuf, "%d", lcd_refresh_rates[i]);
+					f++;
+				} else {
+					sprintf(strbuf, ",%d", lcd_refresh_rates[i]);
+				}
+				strcat(refresh_rates_buf,strbuf);
+			}
+			m = m << 1;
+		}
+		default_refresh_rate = (*(u8 *)(par->lcd_table+61) & 0xf0) >> 4;
+		PRINTKI("       supports refresh rates [%s], default %d Hz\n",
+			refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]);
+		par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate];
+		/* We now need to determine the crtc parameters for the
+		 * lcd monitor. This is tricky, because they are not stored
+		 * individually in the BIOS. Instead, the BIOS contains a
+		 * table of display modes that work for this monitor.
+		 *
+		 * The idea is that we search for a mode of the same dimensions
+		 * as the dimensions of the lcd monitor. Say our lcd monitor
+		 * is 800x600 pixels, we search for a 800x600 monitor.
+		 * The CRTC parameters we find here are the ones that we need
+		 * to use to simulate other resolutions on the lcd screen.
+		 */
+		lcdmodeptr = (u16 *)(par->lcd_table + 64);
+		while (*lcdmodeptr != 0) {
+			u32 modeptr;
+			u16 mwidth, mheight, lcd_hsync_start, lcd_vsync_start;
+			modeptr = bios_base + *lcdmodeptr;
+
+			mwidth = *((u16 *)(modeptr+0));
+			mheight = *((u16 *)(modeptr+2));
+
+			if (mwidth == width && mheight == height) {
+				par->lcd_pixclock = 100000000 / *((u16 *)(modeptr+9));
+				par->lcd_htotal = *((u16 *)(modeptr+17)) & 511;
+				par->lcd_hdisp = *((u16 *)(modeptr+19)) & 511;
+				lcd_hsync_start = *((u16 *)(modeptr+21)) & 511;
+				par->lcd_hsync_dly = (*((u16 *)(modeptr+21)) >> 9) & 7;
+				par->lcd_hsync_len = *((u8 *)(modeptr+23)) & 63;
+
+				par->lcd_vtotal = *((u16 *)(modeptr+24)) & 2047;
+				par->lcd_vdisp = *((u16 *)(modeptr+26)) & 2047;
+				lcd_vsync_start = *((u16 *)(modeptr+28)) & 2047;
+				par->lcd_vsync_len = (*((u16 *)(modeptr+28)) >> 11) & 31;
+
+				par->lcd_htotal = (par->lcd_htotal + 1) * 8;
+				par->lcd_hdisp = (par->lcd_hdisp + 1) * 8;
+				lcd_hsync_start = (lcd_hsync_start + 1) * 8;
+				par->lcd_hsync_len = par->lcd_hsync_len * 8;
+
+				par->lcd_vtotal++;
+				par->lcd_vdisp++;
+				lcd_vsync_start++;
+
+				par->lcd_right_margin = lcd_hsync_start - par->lcd_hdisp;
+				par->lcd_lower_margin = lcd_vsync_start - par->lcd_vdisp;
+				par->lcd_hblank_len = par->lcd_htotal - par->lcd_hdisp;
+				par->lcd_vblank_len = par->lcd_vtotal - par->lcd_vdisp;
+				break;
+			}
+
+			lcdmodeptr++;
+		}
+		if (*lcdmodeptr == 0) {
+			PRINTKE("LCD monitor CRTC parameters not found!!!\n");
+			/* To do: Switch to CRT if possible. */
+		} else {
+			PRINTKI("       LCD CRTC parameters: %d.%d  %d %d %d %d  %d %d %d %d\n",
+				1000000 / par->lcd_pixclock, 1000000 % par->lcd_pixclock,
+				par->lcd_hdisp,
+				par->lcd_hdisp + par->lcd_right_margin,
+				par->lcd_hdisp + par->lcd_right_margin
+					+ par->lcd_hsync_dly + par->lcd_hsync_len,
+				par->lcd_htotal,
+				par->lcd_vdisp,
+				par->lcd_vdisp + par->lcd_lower_margin,
+				par->lcd_vdisp + par->lcd_lower_margin + par->lcd_vsync_len,
+				par->lcd_vtotal);
+			PRINTKI("                          : %d %d %d %d %d %d %d %d %d\n",
+				par->lcd_pixclock,
+				par->lcd_hblank_len - (par->lcd_right_margin +
+					par->lcd_hsync_dly + par->lcd_hsync_len),
+				par->lcd_hdisp,
+				par->lcd_right_margin,
+				par->lcd_hsync_len,
+				par->lcd_vblank_len - (par->lcd_lower_margin + par->lcd_vsync_len),
+				par->lcd_vdisp,
+				par->lcd_lower_margin,
+				par->lcd_vsync_len);
+		}
+	}
+}
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+
+static int __devinit init_from_bios(struct atyfb_par *par)
+{
+	u32 bios_base, rom_addr;
+	int ret;
+
+	rom_addr = 0xc0000 + ((aty_ld_le32(SCRATCH_REG1, par) & 0x7f) << 11);
+	bios_base = (unsigned long)ioremap(rom_addr, 0x10000);
+
+	/* The BIOS starts with 0xaa55. */
+	if (*((u16 *)bios_base) == 0xaa55) {
+
+		u8 *bios_ptr;
+		u16 rom_table_offset, freq_table_offset;
+		PLL_BLOCK_MACH64 pll_block;
+
+		PRINTKI("Mach64 BIOS is located at %x, mapped at %x.\n", rom_addr, bios_base);
+
+		/* check for frequncy table */
+		bios_ptr = (u8*)bios_base;
+		rom_table_offset = (u16)(bios_ptr[0x48] | (bios_ptr[0x49] << 8));
+		freq_table_offset = bios_ptr[rom_table_offset + 16] | (bios_ptr[rom_table_offset + 17] << 8);
+		memcpy(&pll_block, bios_ptr + freq_table_offset, sizeof(PLL_BLOCK_MACH64));
+
+		PRINTKI("BIOS frequency table:\n");
+		PRINTKI("PCLK_min_freq %d, PCLK_max_freq %d, ref_freq %d, ref_divider %d\n",
+			pll_block.PCLK_min_freq, pll_block.PCLK_max_freq,
+			pll_block.ref_freq, pll_block.ref_divider);
+		PRINTKI("MCLK_pwd %d, MCLK_max_freq %d, XCLK_max_freq %d, SCLK_freq %d\n",
+			pll_block.MCLK_pwd, pll_block.MCLK_max_freq,
+			pll_block.XCLK_max_freq, pll_block.SCLK_freq);
+
+		par->pll_limits.pll_min = pll_block.PCLK_min_freq/100;
+		par->pll_limits.pll_max = pll_block.PCLK_max_freq/100;
+		par->pll_limits.ref_clk = pll_block.ref_freq/100;
+		par->pll_limits.ref_div = pll_block.ref_divider;
+		par->pll_limits.sclk = pll_block.SCLK_freq/100;
+		par->pll_limits.mclk = pll_block.MCLK_max_freq/100;
+		par->pll_limits.mclk_pm = pll_block.MCLK_pwd/100;
+		par->pll_limits.xclk = pll_block.XCLK_max_freq/100;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+		aty_init_lcd(par, bios_base);
+#endif
+		ret = 0;
+	} else {
+		PRINTKE("no BIOS frequency table found, use parameters\n");
+		ret = -ENXIO;
+	}
+	iounmap((void* __iomem )bios_base);
+
+	return ret;
+}
+#endif /* __i386__ */
+
+static int __devinit atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, unsigned long addr)
+{
+	struct atyfb_par *par = info->par;
+	u16 tmp;
+	unsigned long raddr;
+	struct resource *rrp;
+	int ret = 0;
+
+	raddr = addr + 0x7ff000UL;
+	rrp = &pdev->resource[2];
+	if ((rrp->flags & IORESOURCE_MEM) && request_mem_region(rrp->start, rrp->end - rrp->start + 1, "atyfb")) {
+		par->aux_start = rrp->start;
+		par->aux_size = rrp->end - rrp->start + 1;
+		raddr = rrp->start;
+		PRINTKI("using auxiliary register aperture\n");
+	}
+
+	info->fix.mmio_start = raddr;
+	par->ati_regbase = ioremap(info->fix.mmio_start, 0x1000);
+	if (par->ati_regbase == 0)
+		return -ENOMEM;
+
+	info->fix.mmio_start += par->aux_start ? 0x400 : 0xc00;
+	par->ati_regbase += par->aux_start ? 0x400 : 0xc00;
+
+	/*
+	 * Enable memory-space accesses using config-space
+	 * command register.
+	 */
+	pci_read_config_word(pdev, PCI_COMMAND, &tmp);
+	if (!(tmp & PCI_COMMAND_MEMORY)) {
+		tmp |= PCI_COMMAND_MEMORY;
+		pci_write_config_word(pdev, PCI_COMMAND, tmp);
+	}
+#ifdef __BIG_ENDIAN
+	/* Use the big-endian aperture */
+	addr += 0x800000;
+#endif
+
+	/* Map in frame buffer */
+	info->fix.smem_start = addr;
+	info->screen_base = ioremap(addr, 0x800000);
+	if (info->screen_base == NULL) {
+		ret = -ENOMEM;
+		goto atyfb_setup_generic_fail;
+	}
+
+	if((ret = correct_chipset(par)))
+		goto atyfb_setup_generic_fail;
+#ifdef __i386__
+	if((ret = init_from_bios(par)))
+		goto atyfb_setup_generic_fail;
+#endif
+	if (!(aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_EXT_DISP_EN))
+		par->clk_wr_offset = (inb(R_GENMO) & 0x0CU) >> 2;
+	else
+		par->clk_wr_offset = aty_ld_8(CLOCK_CNTL, par) & 0x03U;
+
+	/* according to ATI, we should use clock 3 for acelerated mode */
+	par->clk_wr_offset = 3;
+
+	return 0;
+
+atyfb_setup_generic_fail:
+	iounmap(par->ati_regbase);
+	par->ati_regbase = NULL;
+	return ret;
+}
+
+#endif /* !__sparc__ */
+
+static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	unsigned long addr, res_start, res_size;
+	struct fb_info *info;
+	struct resource *rp;
+	struct atyfb_par *par;
+	int i, rc = -ENOMEM;
+
+	for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--)
+		if (pdev->device == aty_chips[i].pci_id)
+			break;
+
+	if (i < 0)
+		return -ENODEV;
+
+	/* Enable device in PCI config */
+	if (pci_enable_device(pdev)) {
+		PRINTKE("Cannot enable PCI device\n");
+		return -ENXIO;
+	}
+
+	/* Find which resource to use */
+	rp = &pdev->resource[0];
+	if (rp->flags & IORESOURCE_IO)
+		rp = &pdev->resource[1];
+	addr = rp->start;
+	if (!addr)
+		return -ENXIO;
+
+	/* Reserve space */
+	res_start = rp->start;
+	res_size = rp->end - rp->start + 1;
+	if (!request_mem_region (res_start, res_size, "atyfb"))
+		return -EBUSY;
+
+        /* Allocate framebuffer */
+	info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev);
+	if (!info) {
+		PRINTKE("atyfb_pci_probe() can't alloc fb_info\n");
+		return -ENOMEM;
+	}
+	par = info->par;
+	info->fix = atyfb_fix;
+	info->device = &pdev->dev;
+	par->pci_id = aty_chips[i].pci_id;
+	par->res_start = res_start;
+	par->res_size = res_size;
+	par->irq = pdev->irq;
+
+	/* Setup "info" structure */
+#ifdef __sparc__
+	rc = atyfb_setup_sparc(pdev, info, addr);
+#else
+	rc = atyfb_setup_generic(pdev, info, addr);
+#endif
+	if (rc)
+		goto err_release_mem;
+
+	pci_set_drvdata(pdev, info);
+
+	/* Init chip & register framebuffer */
+	if (aty_init(info, "PCI"))
+		goto err_release_io;
+
+#ifdef __sparc__
+	if (!prom_palette)
+		prom_palette = atyfb_palette;
+
+	/*
+	 * Add /dev/fb mmap values.
+	 */
+	par->mmap_map[0].voff = 0x8000000000000000UL;
+	par->mmap_map[0].poff = (unsigned long) info->screen_base & PAGE_MASK;
+	par->mmap_map[0].size = info->fix.smem_len;
+	par->mmap_map[0].prot_mask = _PAGE_CACHE;
+	par->mmap_map[0].prot_flag = _PAGE_E;
+	par->mmap_map[1].voff = par->mmap_map[0].voff + info->fix.smem_len;
+	par->mmap_map[1].poff = (long)par->ati_regbase & PAGE_MASK;
+	par->mmap_map[1].size = PAGE_SIZE;
+	par->mmap_map[1].prot_mask = _PAGE_CACHE;
+	par->mmap_map[1].prot_flag = _PAGE_E;
+#endif /* __sparc__ */
+
+	return 0;
+
+err_release_io:
+#ifdef __sparc__
+	kfree(par->mmap_map);
+#else
+	if (par->ati_regbase)
+		iounmap(par->ati_regbase);
+	if (info->screen_base)
+		iounmap(info->screen_base);
+#endif
+err_release_mem:
+	if (par->aux_start)
+		release_mem_region(par->aux_start, par->aux_size);
+
+	release_mem_region(par->res_start, par->res_size);
+	framebuffer_release(info);
+
+	return rc;
+}
+
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_ATARI
+
+static int __devinit atyfb_atari_probe(void)
+{
+	struct aty_par *par;
+	struct fb_info *info;
+	int m64_num;
+	u32 clock_r;
+
+	for (m64_num = 0; m64_num < mach64_count; m64_num++) {
+		if (!phys_vmembase[m64_num] || !phys_size[m64_num] ||
+		    !phys_guiregbase[m64_num]) {
+		    PRINTKI("phys_*[%d] parameters not set => returning early. \n", m64_num);
+			continue;
+		}
+
+		info = framebuffer_alloc(sizeof(struct atyfb_par), NULL);
+		if (!info) {
+			PRINTKE("atyfb_atari_probe() can't alloc fb_info\n");
+			return -ENOMEM;
+		}
+		par = info->par;
+
+		info->fix = atyfb_fix;
+
+		par->irq = (unsigned int) -1; /* something invalid */
+
+		/*
+		 *  Map the video memory (physical address given) to somewhere in the
+		 *  kernel address space.
+		 */
+		info->screen_base = ioremap(phys_vmembase[m64_num], phys_size[m64_num]);
+		info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */
+		par->ati_regbase = ioremap(phys_guiregbase[m64_num], 0x10000) +
+						0xFC00ul;
+		info->fix.mmio_start = (unsigned long)par->ati_regbase; /* Fake! */
+
+		aty_st_le32(CLOCK_CNTL, 0x12345678, par);
+		clock_r = aty_ld_le32(CLOCK_CNTL, par);
+
+		switch (clock_r & 0x003F) {
+		case 0x12:
+			par->clk_wr_offset = 3; /*  */
+			break;
+		case 0x34:
+			par->clk_wr_offset = 2; /* Medusa ST-IO ISA Adapter etc. */
+			break;
+		case 0x16:
+			par->clk_wr_offset = 1; /*  */
+			break;
+		case 0x38:
+			par->clk_wr_offset = 0; /* Panther 1 ISA Adapter (Gerald) */
+			break;
+		}
+
+		if (aty_init(info, "ISA bus")) {
+			framebuffer_release(info);
+			/* This is insufficient! kernel_map has added two large chunks!! */
+			return -ENXIO;
+		}
+	}
+}
+
+#endif /* CONFIG_ATARI */
+
+static void __devexit atyfb_remove(struct fb_info *info)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+	/* restore video mode */
+	aty_set_crtc(par, &saved_crtc);
+	par->pll_ops->set_pll(info, &saved_pll);
+
+	unregister_framebuffer(info);
+
+#ifdef CONFIG_MTRR
+	if (par->mtrr_reg >= 0) {
+	    mtrr_del(par->mtrr_reg, 0, 0);
+	    par->mtrr_reg = -1;
+	}
+	if (par->mtrr_aper >= 0) {
+	    mtrr_del(par->mtrr_aper, 0, 0);
+	    par->mtrr_aper = -1;
+	}
+#endif
+#ifndef __sparc__
+	if (par->ati_regbase)
+		iounmap(par->ati_regbase);
+	if (info->screen_base)
+		iounmap(info->screen_base);
+#ifdef __BIG_ENDIAN
+	if (info->sprite.addr)
+		iounmap(info->sprite.addr);
+#endif
+#endif
+#ifdef __sparc__
+	kfree(par->mmap_map);
+#endif
+	if (par->aux_start)
+		release_mem_region(par->aux_start, par->aux_size);
+
+	if (par->res_start)
+		release_mem_region(par->res_start, par->res_size);
+
+	framebuffer_release(info);
+}
+
+#ifdef CONFIG_PCI
+
+static void __devexit atyfb_pci_remove(struct pci_dev *pdev)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+
+	atyfb_remove(info);
+}
+
+/*
+ * This driver uses its own matching table. That will be more difficult
+ * to fix, so for now, we just match against any ATI ID and let the
+ * probe() function find out what's up. That also mean we don't have
+ * a module ID table though.
+ */
+static struct pci_device_id atyfb_pci_tbl[] = {
+	{ PCI_VENDOR_ID_ATI, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+	  PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0 },
+	{ 0, }
+};
+
+static struct pci_driver atyfb_driver = {
+	.name		= "atyfb",
+	.id_table	= atyfb_pci_tbl,
+	.probe		= atyfb_pci_probe,
+	.remove		= __devexit_p(atyfb_pci_remove),
+#ifdef CONFIG_PM
+	.suspend	= atyfb_pci_suspend,
+	.resume		= atyfb_pci_resume,
+#endif /* CONFIG_PM */
+};
+
+#endif /* CONFIG_PCI */
+
+#ifndef MODULE
+static int __init atyfb_setup(char *options)
+{
+	char *this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!strncmp(this_opt, "noaccel", 7)) {
+			noaccel = 1;
+#ifdef CONFIG_MTRR
+		} else if (!strncmp(this_opt, "nomtrr", 6)) {
+			nomtrr = 1;
+#endif
+		} else if (!strncmp(this_opt, "vram:", 5))
+			vram = simple_strtoul(this_opt + 5, NULL, 0);
+		else if (!strncmp(this_opt, "pll:", 4))
+			pll = simple_strtoul(this_opt + 4, NULL, 0);
+		else if (!strncmp(this_opt, "mclk:", 5))
+			mclk = simple_strtoul(this_opt + 5, NULL, 0);
+		else if (!strncmp(this_opt, "xclk:", 5))
+			xclk = simple_strtoul(this_opt+5, NULL, 0);
+		else if (!strncmp(this_opt, "comp_sync:", 10))
+			comp_sync = simple_strtoul(this_opt+10, NULL, 0);
+#ifdef CONFIG_PPC
+		else if (!strncmp(this_opt, "vmode:", 6)) {
+			unsigned int vmode =
+			    simple_strtoul(this_opt + 6, NULL, 0);
+			if (vmode > 0 && vmode <= VMODE_MAX)
+				default_vmode = vmode;
+		} else if (!strncmp(this_opt, "cmode:", 6)) {
+			unsigned int cmode =
+			    simple_strtoul(this_opt + 6, NULL, 0);
+			switch (cmode) {
+			case 0:
+			case 8:
+				default_cmode = CMODE_8;
+				break;
+			case 15:
+			case 16:
+				default_cmode = CMODE_16;
+				break;
+			case 24:
+			case 32:
+				default_cmode = CMODE_32;
+				break;
+			}
+		}
+#endif
+#ifdef CONFIG_ATARI
+		/*
+		 * Why do we need this silly Mach64 argument?
+		 * We are already here because of mach64= so its redundant.
+		 */
+		else if (MACH_IS_ATARI
+			 && (!strncmp(this_opt, "Mach64:", 7))) {
+			static unsigned char m64_num;
+			static char mach64_str[80];
+			strlcpy(mach64_str, this_opt + 7, sizeof(mach64_str));
+			if (!store_video_par(mach64_str, m64_num)) {
+				m64_num++;
+				mach64_count = m64_num;
+			}
+		}
+#endif
+		else
+			mode = this_opt;
+	}
+	return 0;
+}
+#endif  /*  MODULE  */
+
+static int __init atyfb_init(void)
+{
+#ifndef MODULE
+    char *option = NULL;
+
+    if (fb_get_options("atyfb", &option))
+	return -ENODEV;
+    atyfb_setup(option);
+#endif
+
+#ifdef CONFIG_PCI
+    pci_register_driver(&atyfb_driver);
+#endif
+#ifdef CONFIG_ATARI
+    atyfb_atari_probe();
+#endif
+    return 0;
+}
+
+static void __exit atyfb_exit(void)
+{
+#ifdef CONFIG_PCI
+	pci_unregister_driver(&atyfb_driver);
+#endif
+}
+
+module_init(atyfb_init);
+module_exit(atyfb_exit);
+
+MODULE_DESCRIPTION("FBDev driver for ATI Mach64 cards");
+MODULE_LICENSE("GPL");
+module_param(noaccel, bool, 0);
+MODULE_PARM_DESC(noaccel, "bool: disable acceleration");
+module_param(vram, int, 0);
+MODULE_PARM_DESC(vram, "int: override size of video ram");
+module_param(pll, int, 0);
+MODULE_PARM_DESC(pll, "int: override video clock");
+module_param(mclk, int, 0);
+MODULE_PARM_DESC(mclk, "int: override memory clock");
+module_param(xclk, int, 0);
+MODULE_PARM_DESC(xclk, "int: override accelerated engine clock");
+module_param(comp_sync, int, 0);
+MODULE_PARM_DESC(comp_sync,
+		 "Set composite sync signal to low (0) or high (1)");
+module_param(mode, charp, 0);
+MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
+#ifdef CONFIG_MTRR
+module_param(nomtrr, bool, 0);
+MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
+#endif
diff --git a/drivers/video/aty/mach64_accel.c b/drivers/video/aty/mach64_accel.c
new file mode 100644
index 0000000..c98f4a4
--- /dev/null
+++ b/drivers/video/aty/mach64_accel.c
@@ -0,0 +1,433 @@
+
+/*
+ *  ATI Mach64 Hardware Acceleration
+ */
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <video/mach64.h>
+#include "atyfb.h"
+
+    /*
+     *  Generic Mach64 routines
+     */
+
+/* this is for DMA GUI engine! work in progress */
+typedef struct {
+	u32 frame_buf_offset;
+	u32 system_mem_addr;
+	u32 command;
+	u32 reserved;
+} BM_DESCRIPTOR_ENTRY;
+
+#define LAST_DESCRIPTOR (1 << 31)
+#define SYSTEM_TO_FRAME_BUFFER 0
+
+static u32 rotation24bpp(u32 dx, u32 direction)
+{
+	u32 rotation;
+	if (direction & DST_X_LEFT_TO_RIGHT) {
+		rotation = (dx / 4) % 6;
+	} else {
+		rotation = ((dx + 2) / 4) % 6;
+	}
+
+	return ((rotation << 8) | DST_24_ROTATION_ENABLE);
+}
+
+void aty_reset_engine(const struct atyfb_par *par)
+{
+	/* reset engine */
+	aty_st_le32(GEN_TEST_CNTL,
+		aty_ld_le32(GEN_TEST_CNTL, par) & ~GUI_ENGINE_ENABLE, par);
+	/* enable engine */
+	aty_st_le32(GEN_TEST_CNTL,
+		aty_ld_le32(GEN_TEST_CNTL, par) | GUI_ENGINE_ENABLE, par);
+	/* ensure engine is not locked up by clearing any FIFO or */
+	/* HOST errors */
+	aty_st_le32(BUS_CNTL,
+		aty_ld_le32(BUS_CNTL, par) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK, par);
+}
+
+static void reset_GTC_3D_engine(const struct atyfb_par *par)
+{
+	aty_st_le32(SCALE_3D_CNTL, 0xc0, par);
+	mdelay(GTC_3D_RESET_DELAY);
+	aty_st_le32(SETUP_CNTL, 0x00, par);
+	mdelay(GTC_3D_RESET_DELAY);
+	aty_st_le32(SCALE_3D_CNTL, 0x00, par);
+	mdelay(GTC_3D_RESET_DELAY);
+}
+
+void aty_init_engine(struct atyfb_par *par, struct fb_info *info)
+{
+	u32 pitch_value;
+
+	/* determine modal information from global mode structure */
+	pitch_value = info->var.xres_virtual;
+
+	if (info->var.bits_per_pixel == 24) {
+		/* In 24 bpp, the engine is in 8 bpp - this requires that all */
+		/* horizontal coordinates and widths must be adjusted */
+		pitch_value *= 3;
+	}
+
+	/* On GTC (RagePro), we need to reset the 3D engine before */
+	if (M64_HAS(RESET_3D))
+		reset_GTC_3D_engine(par);
+
+	/* Reset engine, enable, and clear any engine errors */
+	aty_reset_engine(par);
+	/* Ensure that vga page pointers are set to zero - the upper */
+	/* page pointers are set to 1 to handle overflows in the */
+	/* lower page */
+	aty_st_le32(MEM_VGA_WP_SEL, 0x00010000, par);
+	aty_st_le32(MEM_VGA_RP_SEL, 0x00010000, par);
+
+	/* ---- Setup standard engine context ---- */
+
+	/* All GUI registers here are FIFOed - therefore, wait for */
+	/* the appropriate number of empty FIFO entries */
+	wait_for_fifo(14, par);
+
+	/* enable all registers to be loaded for context loads */
+	aty_st_le32(CONTEXT_MASK, 0xFFFFFFFF, par);
+
+	/* set destination pitch to modal pitch, set offset to zero */
+	aty_st_le32(DST_OFF_PITCH, (pitch_value / 8) << 22, par);
+
+	/* zero these registers (set them to a known state) */
+	aty_st_le32(DST_Y_X, 0, par);
+	aty_st_le32(DST_HEIGHT, 0, par);
+	aty_st_le32(DST_BRES_ERR, 0, par);
+	aty_st_le32(DST_BRES_INC, 0, par);
+	aty_st_le32(DST_BRES_DEC, 0, par);
+
+	/* set destination drawing attributes */
+	aty_st_le32(DST_CNTL, DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
+		    DST_X_LEFT_TO_RIGHT, par);
+
+	/* set source pitch to modal pitch, set offset to zero */
+	aty_st_le32(SRC_OFF_PITCH, (pitch_value / 8) << 22, par);
+
+	/* set these registers to a known state */
+	aty_st_le32(SRC_Y_X, 0, par);
+	aty_st_le32(SRC_HEIGHT1_WIDTH1, 1, par);
+	aty_st_le32(SRC_Y_X_START, 0, par);
+	aty_st_le32(SRC_HEIGHT2_WIDTH2, 1, par);
+
+	/* set source pixel retrieving attributes */
+	aty_st_le32(SRC_CNTL, SRC_LINE_X_LEFT_TO_RIGHT, par);
+
+	/* set host attributes */
+	wait_for_fifo(13, par);
+	aty_st_le32(HOST_CNTL, 0, par);
+
+	/* set pattern attributes */
+	aty_st_le32(PAT_REG0, 0, par);
+	aty_st_le32(PAT_REG1, 0, par);
+	aty_st_le32(PAT_CNTL, 0, par);
+
+	/* set scissors to modal size */
+	aty_st_le32(SC_LEFT, 0, par);
+	aty_st_le32(SC_TOP, 0, par);
+	aty_st_le32(SC_BOTTOM, par->crtc.vyres - 1, par);
+	aty_st_le32(SC_RIGHT, pitch_value - 1, par);
+
+	/* set background color to minimum value (usually BLACK) */
+	aty_st_le32(DP_BKGD_CLR, 0, par);
+
+	/* set foreground color to maximum value (usually WHITE) */
+	aty_st_le32(DP_FRGD_CLR, 0xFFFFFFFF, par);
+
+	/* set write mask to effect all pixel bits */
+	aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF, par);
+
+	/* set foreground mix to overpaint and background mix to */
+	/* no-effect */
+	aty_st_le32(DP_MIX, FRGD_MIX_S | BKGD_MIX_D, par);
+
+	/* set primary source pixel channel to foreground color */
+	/* register */
+	aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR, par);
+
+	/* set compare functionality to false (no-effect on */
+	/* destination) */
+	wait_for_fifo(3, par);
+	aty_st_le32(CLR_CMP_CLR, 0, par);
+	aty_st_le32(CLR_CMP_MASK, 0xFFFFFFFF, par);
+	aty_st_le32(CLR_CMP_CNTL, 0, par);
+
+	/* set pixel depth */
+	wait_for_fifo(2, par);
+	aty_st_le32(DP_PIX_WIDTH, par->crtc.dp_pix_width, par);
+	aty_st_le32(DP_CHAIN_MASK, par->crtc.dp_chain_mask, par);
+
+	wait_for_fifo(5, par);
+ 	aty_st_le32(SCALE_3D_CNTL, 0, par);
+	aty_st_le32(Z_CNTL, 0, par);
+	aty_st_le32(CRTC_INT_CNTL, aty_ld_le32(CRTC_INT_CNTL, par) & ~0x20,
+		    par);
+	aty_st_le32(GUI_TRAJ_CNTL, 0x100023, par);
+
+	/* insure engine is idle before leaving */
+	wait_for_idle(par);
+}
+
+    /*
+     *  Accelerated functions
+     */
+
+static inline void draw_rect(s16 x, s16 y, u16 width, u16 height,
+			     struct atyfb_par *par)
+{
+	/* perform rectangle fill */
+	wait_for_fifo(2, par);
+	aty_st_le32(DST_Y_X, (x << 16) | y, par);
+	aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | height, par);
+	par->blitter_may_be_busy = 1;
+}
+
+void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 dy = area->dy, sy = area->sy, direction = DST_LAST_PEL;
+	u32 sx = area->sx, dx = area->dx, width = area->width, rotation = 0;
+
+	if (par->asleep)
+		return;
+	if (!area->width || !area->height)
+		return;
+	if (!par->accel_flags) {
+		if (par->blitter_may_be_busy)
+			wait_for_idle(par);
+		cfb_copyarea(info, area);
+		return;
+	}
+
+	if (info->var.bits_per_pixel == 24) {
+		/* In 24 bpp, the engine is in 8 bpp - this requires that all */
+		/* horizontal coordinates and widths must be adjusted */
+		sx *= 3;
+		dx *= 3;
+		width *= 3;
+	}
+
+	if (area->sy < area->dy) {
+		dy += area->height - 1;
+		sy += area->height - 1;
+	} else
+		direction |= DST_Y_TOP_TO_BOTTOM;
+
+	if (sx < dx) {
+		dx += width - 1;
+		sx += width - 1;
+	} else
+		direction |= DST_X_LEFT_TO_RIGHT;
+
+	if (info->var.bits_per_pixel == 24) {
+		rotation = rotation24bpp(dx, direction);
+	}
+
+	wait_for_fifo(4, par);
+	aty_st_le32(DP_SRC, FRGD_SRC_BLIT, par);
+	aty_st_le32(SRC_Y_X, (sx << 16) | sy, par);
+	aty_st_le32(SRC_HEIGHT1_WIDTH1, (width << 16) | area->height, par);
+	aty_st_le32(DST_CNTL, direction | rotation, par);
+	draw_rect(dx, dy, width, area->height, par);
+}
+
+void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 color = rect->color, dx = rect->dx, width = rect->width, rotation = 0;
+
+	if (par->asleep)
+		return;
+	if (!rect->width || !rect->height)
+		return;
+	if (!par->accel_flags) {
+		if (par->blitter_may_be_busy)
+			wait_for_idle(par);
+		cfb_fillrect(info, rect);
+		return;
+	}
+
+	color |= (rect->color << 8);
+	color |= (rect->color << 16);
+
+	if (info->var.bits_per_pixel == 24) {
+		/* In 24 bpp, the engine is in 8 bpp - this requires that all */
+		/* horizontal coordinates and widths must be adjusted */
+		dx *= 3;
+		width *= 3;
+		rotation = rotation24bpp(dx, DST_X_LEFT_TO_RIGHT);
+	}
+
+	wait_for_fifo(3, par);
+	aty_st_le32(DP_FRGD_CLR, color, par);
+	aty_st_le32(DP_SRC,
+		    BKGD_SRC_BKGD_CLR | FRGD_SRC_FRGD_CLR | MONO_SRC_ONE,
+		    par);
+	aty_st_le32(DST_CNTL,
+		    DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
+		    DST_X_LEFT_TO_RIGHT | rotation, par);
+	draw_rect(dx, rect->dy, width, rect->height, par);
+}
+
+void atyfb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 src_bytes, dx = image->dx, dy = image->dy, width = image->width;
+	u32 pix_width_save, pix_width, host_cntl, rotation = 0, src, mix;
+
+	if (par->asleep)
+		return;
+	if (!image->width || !image->height)
+		return;
+	if (!par->accel_flags ||
+	    (image->depth != 1 && info->var.bits_per_pixel != image->depth)) {
+		if (par->blitter_may_be_busy)
+			wait_for_idle(par);
+
+		cfb_imageblit(info, image);
+		return;
+	}
+
+	wait_for_idle(par);
+	pix_width = pix_width_save = aty_ld_le32(DP_PIX_WIDTH, par);
+	host_cntl = aty_ld_le32(HOST_CNTL, par) | HOST_BYTE_ALIGN;
+
+	switch (image->depth) {
+	case 1:
+	    pix_width &= ~(BYTE_ORDER_MASK | HOST_MASK);
+	    pix_width |= (BYTE_ORDER_MSB_TO_LSB | HOST_1BPP);
+	    break;
+	case 4:
+	    pix_width &= ~(BYTE_ORDER_MASK | HOST_MASK);
+	    pix_width |= (BYTE_ORDER_MSB_TO_LSB | HOST_4BPP);
+	    break;
+	case 8:
+	    pix_width &= ~HOST_MASK;
+	    pix_width |= HOST_8BPP;
+	    break;
+	case 15:
+	    pix_width &= ~HOST_MASK;
+	    pix_width |= HOST_15BPP;
+	    break;
+	case 16:
+	    pix_width &= ~HOST_MASK;
+	    pix_width |= HOST_16BPP;
+	    break;
+	case 24:
+	    pix_width &= ~HOST_MASK;
+	    pix_width |= HOST_24BPP;
+	    break;
+	case 32:
+	    pix_width &= ~HOST_MASK;
+	    pix_width |= HOST_32BPP;
+	    break;
+	}
+
+	if (info->var.bits_per_pixel == 24) {
+		/* In 24 bpp, the engine is in 8 bpp - this requires that all */
+		/* horizontal coordinates and widths must be adjusted */
+		dx *= 3;
+		width *= 3;
+
+		rotation = rotation24bpp(dx, DST_X_LEFT_TO_RIGHT);
+
+		pix_width &= ~DST_MASK;
+		pix_width |= DST_8BPP;
+
+		/*
+		 * since Rage 3D IIc we have DP_HOST_TRIPLE_EN bit
+		 * this hwaccelerated triple has an issue with not aligned data
+		 */
+		if (M64_HAS(HW_TRIPLE) && image->width % 8 == 0)
+			pix_width |= DP_HOST_TRIPLE_EN;
+	}
+
+	if (image->depth == 1) {
+		u32 fg, bg;
+		if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+		    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+			fg = ((u32*)(info->pseudo_palette))[image->fg_color];
+			bg = ((u32*)(info->pseudo_palette))[image->bg_color];
+		} else {
+			fg = image->fg_color;
+			bg = image->bg_color;
+		}
+
+		wait_for_fifo(2, par);
+		aty_st_le32(DP_BKGD_CLR, bg, par);
+		aty_st_le32(DP_FRGD_CLR, fg, par);
+		src = MONO_SRC_HOST | FRGD_SRC_FRGD_CLR | BKGD_SRC_BKGD_CLR;
+		mix = FRGD_MIX_S | BKGD_MIX_S;
+	} else {
+		src = MONO_SRC_ONE | FRGD_SRC_HOST;
+		mix = FRGD_MIX_D_XOR_S | BKGD_MIX_D;
+	}
+
+	wait_for_fifo(6, par);
+	aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF, par);
+	aty_st_le32(DP_PIX_WIDTH, pix_width, par);
+	aty_st_le32(DP_MIX, mix, par);
+	aty_st_le32(DP_SRC, src, par);
+	aty_st_le32(HOST_CNTL, host_cntl, par);
+	aty_st_le32(DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT | rotation, par);
+
+	draw_rect(dx, dy, width, image->height, par);
+	src_bytes = (((image->width * image->depth) + 7) / 8) * image->height;
+
+	/* manual triple each pixel */
+	if (info->var.bits_per_pixel == 24 && !(pix_width & DP_HOST_TRIPLE_EN)) {
+		int inbit, outbit, mult24, byte_id_in_dword, width;
+		u8 *pbitmapin = (u8*)image->data, *pbitmapout;
+		u32 hostdword;
+
+		for (width = image->width, inbit = 7, mult24 = 0; src_bytes; ) {
+			for (hostdword = 0, pbitmapout = (u8*)&hostdword, byte_id_in_dword = 0;
+				byte_id_in_dword < 4 && src_bytes;
+				byte_id_in_dword++, pbitmapout++) {
+				for (outbit = 7; outbit >= 0; outbit--) {
+					*pbitmapout |= (((*pbitmapin >> inbit) & 1) << outbit);
+					mult24++;
+					/* next bit */
+					if (mult24 == 3) {
+						mult24 = 0;
+						inbit--;
+						width--;
+					}
+
+					/* next byte */
+					if (inbit < 0 || width == 0) {
+						src_bytes--;
+						pbitmapin++;
+						inbit = 7;
+
+						if (width == 0) {
+						    width = image->width;
+						    outbit = 0;
+						}
+					}
+				}
+			}
+			wait_for_fifo(1, par);
+			aty_st_le32(HOST_DATA0, hostdword, par);
+		}
+	} else {
+		u32 *pbitmap, dwords = (src_bytes + 3) / 4;
+		for (pbitmap = (u32*)(image->data); dwords; dwords--, pbitmap++) {
+			wait_for_fifo(1, par);
+			aty_st_le32(HOST_DATA0, le32_to_cpup(pbitmap), par);
+		}
+	}
+
+	wait_for_idle(par);
+
+	/* restore pix_width */
+	wait_for_fifo(1, par);
+	aty_st_le32(DP_PIX_WIDTH, pix_width_save, par);
+}
diff --git a/drivers/video/aty/mach64_ct.c b/drivers/video/aty/mach64_ct.c
new file mode 100644
index 0000000..9bdb2aa
--- /dev/null
+++ b/drivers/video/aty/mach64_ct.c
@@ -0,0 +1,619 @@
+
+/*
+ *  ATI Mach64 CT/VT/GT/LT Support
+ */
+
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <video/mach64.h>
+#include "atyfb.h"
+
+#undef DEBUG
+
+static int aty_valid_pll_ct (const struct fb_info *info, u32 vclk_per, struct pll_ct *pll);
+static int aty_dsp_gt       (const struct fb_info *info, u32 bpp, struct pll_ct *pll);
+static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, u32 bpp, union aty_pll *pll);
+static u32 aty_pll_to_var_ct(const struct fb_info *info, const union aty_pll *pll);
+
+u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par)
+{
+	u8 res;
+
+	/* write addr byte */
+	aty_st_8(CLOCK_CNTL_ADDR, (offset << 2) & PLL_ADDR, par);
+	/* read the register value */
+	res = aty_ld_8(CLOCK_CNTL_DATA, par);
+	return res;
+}
+
+void aty_st_pll_ct(int offset, u8 val, const struct atyfb_par *par)
+{
+	/* write addr byte */
+	aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) | PLL_WR_EN, par);
+	/* write the register value */
+	aty_st_8(CLOCK_CNTL_DATA, val & PLL_DATA, par);
+	aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) & ~PLL_WR_EN, par);
+}
+
+/*
+ * by Daniel Mantione
+ *                                  <daniel.mantione@freepascal.org>
+ *
+ *
+ * ATI Mach64 CT clock synthesis description.
+ *
+ * All clocks on the Mach64 can be calculated using the same principle:
+ *
+ *       XTALIN * x * FB_DIV
+ * CLK = ----------------------
+ *       PLL_REF_DIV * POST_DIV
+ *
+ * XTALIN is a fixed speed clock. Common speeds are 14.31 MHz and 29.50 MHz.
+ * PLL_REF_DIV can be set by the user, but is the same for all clocks.
+ * FB_DIV can be set by the user for each clock individually, it should be set
+ * between 128 and 255, the chip will generate a bad clock signal for too low
+ * values.
+ * x depends on the type of clock; usually it is 2, but for the MCLK it can also
+ * be set to 4.
+ * POST_DIV can be set by the user for each clock individually, Possible values
+ * are 1,2,4,8 and for some clocks other values are available too.
+ * CLK is of course the clock speed that is generated.
+ *
+ * The Mach64 has these clocks:
+ *
+ * MCLK			The clock rate of the chip
+ * XCLK			The clock rate of the on-chip memory
+ * VCLK0		First pixel clock of first CRT controller
+ * VCLK1    Second pixel clock of first CRT controller
+ * VCLK2		Third pixel clock of first CRT controller
+ * VCLK3    Fourth pixel clock of first CRT controller
+ * VCLK			Selected pixel clock, one of VCLK0, VCLK1, VCLK2, VCLK3
+ * V2CLK		Pixel clock of the second CRT controller.
+ * SCLK			Multi-purpose clock
+ *
+ * - MCLK and XCLK use the same FB_DIV
+ * - VCLK0 .. VCLK3 use the same FB_DIV
+ * - V2CLK is needed when the second CRTC is used (can be used for dualhead);
+ *   i.e. CRT monitor connected to laptop has different resolution than built
+ *   in LCD monitor.
+ * - SCLK is not available on all cards; it is know to exist on the Rage LT-PRO,
+ *   Rage XL and Rage Mobility. It is know not to exist on the Mach64 VT.
+ * - V2CLK is not available on all cards, most likely only the Rage LT-PRO,
+ *   the Rage XL and the Rage Mobility
+ *
+ * SCLK can be used to:
+ * - Clock the chip instead of MCLK
+ * - Replace XTALIN with a user defined frequency
+ * - Generate the pixel clock for the LCD monitor (instead of VCLK)
+ */
+
+ /*
+  * It can be quite hard to calculate XCLK and MCLK if they don't run at the
+  * same frequency. Luckily, until now all cards that need asynchrone clock
+  * speeds seem to have SCLK.
+  * So this driver uses SCLK to clock the chip and XCLK to clock the memory.
+  */
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ *  PLL programming (Mach64 CT family)
+ *
+ *
+ * This procedure sets the display fifo. The display fifo is a buffer that
+ * contains data read from the video memory that waits to be processed by
+ * the CRT controller.
+ *
+ * On the more modern Mach64 variants, the chip doesn't calculate the
+ * interval after which the display fifo has to be reloaded from memory
+ * automatically, the driver has to do it instead.
+ */
+
+#define Maximum_DSP_PRECISION 7
+static u8 postdividers[] = {1,2,4,8,3};
+
+static int aty_dsp_gt(const struct fb_info *info, u32 bpp, struct pll_ct *pll)
+{
+	u32 dsp_off, dsp_on, dsp_xclks;
+	u32 multiplier, divider, ras_multiplier, ras_divider, tmp;
+	u8 vshift, xshift;
+	s8 dsp_precision;
+
+	multiplier = ((u32)pll->mclk_fb_div) * pll->vclk_post_div_real;
+	divider = ((u32)pll->vclk_fb_div) * pll->xclk_ref_div;
+
+	ras_multiplier = pll->xclkmaxrasdelay;
+	ras_divider = 1;
+
+	if (bpp>=8)
+		divider = divider * (bpp >> 2);
+
+	vshift = (6 - 2) - pll->xclk_post_div;	/* FIFO is 64 bits wide in accelerator mode ... */
+
+	if (bpp == 0)
+		vshift--;	/* ... but only 32 bits in VGA mode. */
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (pll->xres != 0) {
+		struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+		multiplier = multiplier * par->lcd_width;
+		divider = divider * pll->xres & ~7;
+
+		ras_multiplier = ras_multiplier * par->lcd_width;
+		ras_divider = ras_divider * pll->xres & ~7;
+	}
+#endif
+	/* If we don't do this, 32 bits for multiplier & divider won't be
+	enough in certain situations! */
+	while (((multiplier | divider) & 1) == 0) {
+		multiplier = multiplier >> 1;
+		divider = divider >> 1;
+	}
+
+	/* Determine DSP precision first */
+	tmp = ((multiplier * pll->fifo_size) << vshift) / divider;
+
+	for (dsp_precision = -5;  tmp;  dsp_precision++)
+		tmp >>= 1;
+	if (dsp_precision < 0)
+		dsp_precision = 0;
+	else if (dsp_precision > Maximum_DSP_PRECISION)
+		dsp_precision = Maximum_DSP_PRECISION;
+
+	xshift = 6 - dsp_precision;
+	vshift += xshift;
+
+	/* Move on to dsp_off */
+	dsp_off = ((multiplier * (pll->fifo_size - 1)) << vshift) / divider -
+		(1 << (vshift - xshift));
+
+/*    if (bpp == 0)
+        dsp_on = ((multiplier * 20 << vshift) + divider) / divider;
+    else */
+	{
+		dsp_on = ((multiplier << vshift) + divider) / divider;
+		tmp = ((ras_multiplier << xshift) + ras_divider) / ras_divider;
+		if (dsp_on < tmp)
+		dsp_on = tmp;
+		dsp_on = dsp_on + (tmp * 2) + (pll->xclkpagefaultdelay << xshift);
+	}
+
+	/* Calculate rounding factor and apply it to dsp_on */
+	tmp = ((1 << (Maximum_DSP_PRECISION - dsp_precision)) - 1) >> 1;
+	dsp_on = ((dsp_on + tmp) / (tmp + 1)) * (tmp + 1);
+
+	if (dsp_on >= ((dsp_off / (tmp + 1)) * (tmp + 1))) {
+		dsp_on = dsp_off - (multiplier << vshift) / divider;
+		dsp_on = (dsp_on / (tmp + 1)) * (tmp + 1);
+	}
+
+	/* Last but not least:  dsp_xclks */
+	dsp_xclks = ((multiplier << (vshift + 5)) + divider) / divider;
+
+	/* Get register values. */
+	pll->dsp_on_off = (dsp_on << 16) + dsp_off;
+	pll->dsp_config = (dsp_precision << 20) | (pll->dsp_loop_latency << 16) | dsp_xclks;
+#ifdef DEBUG
+	printk("atyfb(%s): dsp_config 0x%08x, dsp_on_off 0x%08x\n",
+		__FUNCTION__, pll->dsp_config, pll->dsp_on_off);
+#endif
+	return 0;
+}
+
+static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per, struct pll_ct *pll)
+{
+	u32 q;
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+#ifdef DEBUG
+	int pllvclk;
+#endif
+
+	/* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */
+	q = par->ref_clk_per * pll->pll_ref_div * 4 / vclk_per;
+	if (q < 16*8 || q > 255*8) {
+		printk(KERN_CRIT "atyfb: vclk out of range\n");
+		return -EINVAL;
+	} else {
+		pll->vclk_post_div  = (q < 128*8);
+		pll->vclk_post_div += (q <  64*8);
+		pll->vclk_post_div += (q <  32*8);
+	}
+	pll->vclk_post_div_real = postdividers[pll->vclk_post_div];
+	//    pll->vclk_post_div <<= 6;
+	pll->vclk_fb_div = q * pll->vclk_post_div_real / 8;
+#ifdef DEBUG
+	pllvclk = (1000000 * 2 * pll->vclk_fb_div) /
+		(par->ref_clk_per * pll->pll_ref_div);
+	printk("atyfb(%s): pllvclk=%d MHz, vclk=%d MHz\n",
+		__FUNCTION__, pllvclk, pllvclk / pll->vclk_post_div_real);
+#endif
+	pll->pll_vclk_cntl = 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */
+	return 0;
+}
+
+static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, u32 bpp, union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	int err;
+
+	if ((err = aty_valid_pll_ct(info, vclk_per, &pll->ct)))
+		return err;
+	if (M64_HAS(GTB_DSP) && (err = aty_dsp_gt(info, bpp, &pll->ct)))
+		return err;
+	/*aty_calc_pll_ct(info, &pll->ct);*/
+	return 0;
+}
+
+static u32 aty_pll_to_var_ct(const struct fb_info *info, const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 ret;
+	ret = par->ref_clk_per * pll->ct.pll_ref_div * pll->ct.vclk_post_div_real / pll->ct.vclk_fb_div / 2;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if(pll->ct.xres > 0) {
+		ret *= par->lcd_width;
+		ret /= pll->ct.xres;
+	}
+#endif
+#ifdef DEBUG
+	printk("atyfb(%s): calculated 0x%08X(%i)\n", __FUNCTION__, ret, ret);
+#endif
+	return ret;
+}
+
+void aty_set_pll_ct(const struct fb_info *info, const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 crtc_gen_cntl, lcd_gen_cntrl;
+	u8 tmp, tmp2;
+
+	lcd_gen_cntrl = 0;
+#ifdef DEBUG
+	printk("atyfb(%s): about to program:\n"
+		"pll_ext_cntl=0x%02x pll_gen_cntl=0x%02x pll_vclk_cntl=0x%02x\n",
+		__FUNCTION__,
+		pll->ct.pll_ext_cntl, pll->ct.pll_gen_cntl, pll->ct.pll_vclk_cntl);
+
+	printk("atyfb(%s): setting clock %lu for FeedBackDivider %i, ReferenceDivider %i, PostDivider %i(%i)\n",
+		__FUNCTION__,
+		par->clk_wr_offset, pll->ct.vclk_fb_div,
+		pll->ct.pll_ref_div, pll->ct.vclk_post_div, pll->ct.vclk_post_div_real);
+#endif
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		/* turn off LCD */
+		lcd_gen_cntrl = aty_ld_lcd(LCD_GEN_CNTL, par);
+		aty_st_lcd(LCD_GEN_CNTL, lcd_gen_cntrl & ~LCD_ON, par);
+	}
+#endif
+	aty_st_8(CLOCK_CNTL, par->clk_wr_offset | CLOCK_STROBE, par);
+
+	/* Temporarily switch to accelerator mode */
+	crtc_gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
+	if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
+		aty_st_le32(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN, par);
+
+	/* Reset VCLK generator */
+	aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par);
+
+	/* Set post-divider */
+	tmp2 = par->clk_wr_offset << 1;
+	tmp = aty_ld_pll_ct(VCLK_POST_DIV, par);
+	tmp &= ~(0x03U << tmp2);
+	tmp |= ((pll->ct.vclk_post_div & 0x03U) << tmp2);
+	aty_st_pll_ct(VCLK_POST_DIV, tmp, par);
+
+	/* Set extended post-divider */
+	tmp = aty_ld_pll_ct(PLL_EXT_CNTL, par);
+	tmp &= ~(0x10U << par->clk_wr_offset);
+	tmp &= 0xF0U;
+	tmp |= pll->ct.pll_ext_cntl;
+	aty_st_pll_ct(PLL_EXT_CNTL, tmp, par);
+
+	/* Set feedback divider */
+	tmp = VCLK0_FB_DIV + par->clk_wr_offset;
+	aty_st_pll_ct(tmp, (pll->ct.vclk_fb_div & 0xFFU), par);
+
+	aty_st_pll_ct(PLL_GEN_CNTL, (pll->ct.pll_gen_cntl & (~(PLL_OVERRIDE | PLL_MCLK_RST))) | OSC_EN, par);
+
+	/* End VCLK generator reset */
+	aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl & ~(PLL_VCLK_RST), par);
+	mdelay(5);
+
+	aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par);
+	aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par);
+	mdelay(1);
+
+	/* Restore mode register */
+	if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
+		aty_st_le32(CRTC_GEN_CNTL, crtc_gen_cntl, par);
+
+	if (M64_HAS(GTB_DSP)) {
+		u8 dll_cntl;
+
+		if (M64_HAS(XL_DLL))
+			dll_cntl = 0x80;
+		else if (par->ram_type >= SDRAM)
+			dll_cntl = 0xa6;
+		else
+			dll_cntl = 0xa0;
+		aty_st_pll_ct(DLL_CNTL, dll_cntl, par);
+		aty_st_pll_ct(VFC_CNTL, 0x1b, par);
+		aty_st_le32(DSP_CONFIG, pll->ct.dsp_config, par);
+		aty_st_le32(DSP_ON_OFF, pll->ct.dsp_on_off, par);
+
+		mdelay(10);
+		aty_st_pll_ct(DLL_CNTL, dll_cntl, par);
+		mdelay(10);
+		aty_st_pll_ct(DLL_CNTL, dll_cntl | 0x40, par);
+		mdelay(10);
+		aty_st_pll_ct(DLL_CNTL, dll_cntl & ~0x40, par);
+	}
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+	if (par->lcd_table != 0) {
+		/* restore LCD */
+		aty_st_lcd(LCD_GEN_CNTL, lcd_gen_cntrl, par);
+	}
+#endif
+}
+
+static void __init aty_get_pll_ct(const struct fb_info *info,
+				  union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u8 tmp, clock;
+
+	clock = aty_ld_8(CLOCK_CNTL, par) & 0x03U;
+	tmp = clock << 1;
+	pll->ct.vclk_post_div = (aty_ld_pll_ct(VCLK_POST_DIV, par) >> tmp) & 0x03U;
+
+	pll->ct.pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par) & 0x0FU;
+	pll->ct.vclk_fb_div = aty_ld_pll_ct(VCLK0_FB_DIV + clock, par) & 0xFFU;
+	pll->ct.pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par);
+	pll->ct.mclk_fb_div = aty_ld_pll_ct(MCLK_FB_DIV, par);
+
+	pll->ct.pll_gen_cntl = aty_ld_pll_ct(PLL_GEN_CNTL, par);
+	pll->ct.pll_vclk_cntl = aty_ld_pll_ct(PLL_VCLK_CNTL, par);
+
+	if (M64_HAS(GTB_DSP)) {
+		pll->ct.dsp_config = aty_ld_le32(DSP_CONFIG, par);
+		pll->ct.dsp_on_off = aty_ld_le32(DSP_ON_OFF, par);
+	}
+}
+
+static int __init aty_init_pll_ct(const struct fb_info *info,
+				 union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u8 mpost_div, xpost_div, sclk_post_div_real, sclk_fb_div, spll_cntl2;
+	u32 q, i, memcntl, trp;
+	u32 dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off;
+#ifdef DEBUG
+	int pllmclk, pllsclk;
+#endif
+	pll->ct.pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par);
+	pll->ct.xclk_post_div = pll->ct.pll_ext_cntl & 0x07;
+	pll->ct.xclk_ref_div = 1;
+	switch (pll->ct.xclk_post_div) {
+	case 0:  case 1:  case 2:  case 3:
+		break;
+
+	case 4:
+		pll->ct.xclk_ref_div = 3;
+		pll->ct.xclk_post_div = 0;
+		break;
+
+	default:
+		printk(KERN_CRIT "atyfb: Unsupported xclk source:  %d.\n", pll->ct.xclk_post_div);
+		return -EINVAL;
+	}
+	pll->ct.mclk_fb_mult = 2;
+	if(pll->ct.pll_ext_cntl & PLL_MFB_TIMES_4_2B) {
+		pll->ct.mclk_fb_mult = 4;
+		pll->ct.xclk_post_div -= 1;
+	}
+
+#ifdef DEBUG
+	printk("atyfb(%s): mclk_fb_mult=%d, xclk_post_div=%d\n",
+		__FUNCTION__, pll->ct.mclk_fb_mult, pll->ct.xclk_post_div);
+#endif
+
+	memcntl = aty_ld_le32(MEM_CNTL, par);
+	trp = (memcntl & 0x300) >> 8;
+
+	pll->ct.xclkpagefaultdelay = ((memcntl & 0xc00) >> 10) + ((memcntl & 0x1000) >> 12) + trp + 2;
+	pll->ct.xclkmaxrasdelay = ((memcntl & 0x70000) >> 16) + trp + 2;
+
+	if (M64_HAS(FIFO_32)) {
+		pll->ct.fifo_size = 32;
+	} else {
+		pll->ct.fifo_size = 24;
+		pll->ct.xclkpagefaultdelay += 2;
+		pll->ct.xclkmaxrasdelay += 3;
+	}
+
+	switch (par->ram_type) {
+	case DRAM:
+		if (info->fix.smem_len<=ONE_MB) {
+			pll->ct.dsp_loop_latency = 10;
+		} else {
+			pll->ct.dsp_loop_latency = 8;
+			pll->ct.xclkpagefaultdelay += 2;
+		}
+		break;
+	case EDO:
+	case PSEUDO_EDO:
+		if (info->fix.smem_len<=ONE_MB) {
+			pll->ct.dsp_loop_latency = 9;
+		} else {
+			pll->ct.dsp_loop_latency = 8;
+			pll->ct.xclkpagefaultdelay += 1;
+		}
+		break;
+	case SDRAM:
+		if (info->fix.smem_len<=ONE_MB) {
+			pll->ct.dsp_loop_latency = 11;
+		} else {
+			pll->ct.dsp_loop_latency = 10;
+			pll->ct.xclkpagefaultdelay += 1;
+		}
+		break;
+	case SGRAM:
+		pll->ct.dsp_loop_latency = 8;
+		pll->ct.xclkpagefaultdelay += 3;
+		break;
+	default:
+		pll->ct.dsp_loop_latency = 11;
+		pll->ct.xclkpagefaultdelay += 3;
+		break;
+	}
+
+	if (pll->ct.xclkmaxrasdelay <= pll->ct.xclkpagefaultdelay)
+		pll->ct.xclkmaxrasdelay = pll->ct.xclkpagefaultdelay + 1;
+
+	/* Allow BIOS to override */
+	dsp_config = aty_ld_le32(DSP_CONFIG, par);
+	dsp_on_off = aty_ld_le32(DSP_ON_OFF, par);
+	vga_dsp_config = aty_ld_le32(VGA_DSP_CONFIG, par);
+	vga_dsp_on_off = aty_ld_le32(VGA_DSP_ON_OFF, par);
+
+	if (dsp_config)
+		pll->ct.dsp_loop_latency = (dsp_config & DSP_LOOP_LATENCY) >> 16;
+#if 0
+	FIXME: is it relevant for us?
+	if ((!dsp_on_off && !M64_HAS(RESET_3D)) ||
+		((dsp_on_off == vga_dsp_on_off) &&
+		(!dsp_config || !((dsp_config ^ vga_dsp_config) & DSP_XCLKS_PER_QW)))) {
+		vga_dsp_on_off &= VGA_DSP_OFF;
+		vga_dsp_config &= VGA_DSP_XCLKS_PER_QW;
+		if (ATIDivide(vga_dsp_on_off, vga_dsp_config, 5, 1) > 24)
+			pll->ct.fifo_size = 32;
+		else
+			pll->ct.fifo_size = 24;
+	}
+#endif
+	/* Exit if the user does not want us to tamper with the clock
+	rates of her chip. */
+	if (par->mclk_per == 0) {
+		u8 mclk_fb_div, pll_ext_cntl;
+		pll->ct.pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par);
+		pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par);
+		pll->ct.xclk_post_div_real = postdividers[pll_ext_cntl & 0x07];
+		mclk_fb_div = aty_ld_pll_ct(MCLK_FB_DIV, par);
+		if (pll_ext_cntl & PLL_MFB_TIMES_4_2B)
+			mclk_fb_div <<= 1;
+		pll->ct.mclk_fb_div = mclk_fb_div;
+		return 0;
+	}
+
+	pll->ct.pll_ref_div = par->pll_per * 2 * 255 / par->ref_clk_per;
+
+	/* FIXME: use the VTB/GTB /3 post divider if it's better suited */
+	q = par->ref_clk_per * pll->ct.pll_ref_div * 8 /
+		(pll->ct.mclk_fb_mult * par->xclk_per);
+
+	if (q < 16*8 || q > 255*8) {
+		printk(KERN_CRIT "atxfb: xclk out of range\n");
+		return -EINVAL;
+	} else {
+		xpost_div  = (q < 128*8);
+		xpost_div += (q <  64*8);
+		xpost_div += (q <  32*8);
+	}
+	pll->ct.xclk_post_div_real = postdividers[xpost_div];
+	pll->ct.mclk_fb_div = q * pll->ct.xclk_post_div_real / 8;
+
+#ifdef DEBUG
+	pllmclk = (1000000 * pll->ct.mclk_fb_mult * pll->ct.mclk_fb_div) /
+			(par->ref_clk_per * pll->ct.pll_ref_div);
+	printk("atyfb(%s): pllmclk=%d MHz, xclk=%d MHz\n",
+		__FUNCTION__, pllmclk, pllmclk / pll->ct.xclk_post_div_real);
+#endif
+
+	if (M64_HAS(SDRAM_MAGIC_PLL) && (par->ram_type >= SDRAM))
+		pll->ct.pll_gen_cntl = OSC_EN;
+	else
+		pll->ct.pll_gen_cntl = OSC_EN | DLL_PWDN /* | FORCE_DCLK_TRI_STATE */;
+
+	if (M64_HAS(MAGIC_POSTDIV))
+		pll->ct.pll_ext_cntl = 0;
+	else
+		pll->ct.pll_ext_cntl = xpost_div;
+
+	if (pll->ct.mclk_fb_mult == 4)
+		pll->ct.pll_ext_cntl |= PLL_MFB_TIMES_4_2B;
+
+	if (par->mclk_per == par->xclk_per) {
+		pll->ct.pll_gen_cntl |= (xpost_div << 4); /* mclk == xclk */
+	} else {
+		/*
+		* The chip clock is not equal to the memory clock.
+		* Therefore we will use sclk to clock the chip.
+		*/
+		pll->ct.pll_gen_cntl |= (6 << 4); /* mclk == sclk */
+
+		q = par->ref_clk_per * pll->ct.pll_ref_div * 4 / par->mclk_per;
+		if (q < 16*8 || q > 255*8) {
+			printk(KERN_CRIT "atyfb: mclk out of range\n");
+			return -EINVAL;
+		} else {
+			mpost_div  = (q < 128*8);
+			mpost_div += (q <  64*8);
+			mpost_div += (q <  32*8);
+		}
+		sclk_post_div_real = postdividers[mpost_div];
+		sclk_fb_div = q * sclk_post_div_real / 8;
+		spll_cntl2 = mpost_div << 4;
+#ifdef DEBUG
+		pllsclk = (1000000 * 2 * sclk_fb_div) /
+			(par->ref_clk_per * pll->ct.pll_ref_div);
+		printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n",
+			__FUNCTION__, pllsclk, pllsclk / sclk_post_div_real);
+#endif
+		/*
+		* This disables the sclk, crashes the computer as reported:
+		* aty_st_pll_ct(SPLL_CNTL2, 3, info);
+		*
+		* So it seems the sclk must be enabled before it is used;
+		* so PLL_GEN_CNTL must be programmed *after* the sclk.
+		*/
+		aty_st_pll_ct(SCLK_FB_DIV, sclk_fb_div, par);
+		aty_st_pll_ct(SPLL_CNTL2, spll_cntl2, par);
+		/*
+		 * The sclk has been started. However, I believe the first clock
+		 * ticks it generates are not very stable. Hope this primitive loop
+		 * helps for Rage Mobilities that sometimes crash when
+		 * we switch to sclk. (Daniel Mantione, 13-05-2003)
+		 */
+		for (i=0;i<=0x1ffff;i++);
+	}
+
+	aty_st_pll_ct(PLL_REF_DIV, pll->ct.pll_ref_div, par);
+	aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par);
+	aty_st_pll_ct(MCLK_FB_DIV, pll->ct.mclk_fb_div, par);
+	aty_st_pll_ct(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par);
+	/* Disable the extra precision pixel clock controls since we do not use them. */
+	aty_st_pll_ct(EXT_VPLL_CNTL, aty_ld_pll_ct(EXT_VPLL_CNTL, par) &
+		~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC), par);
+
+	return 0;
+}
+
+static int dummy(void)
+{
+	return 0;
+}
+
+const struct aty_dac_ops aty_dac_ct = {
+	.set_dac	= (void *) dummy,
+};
+
+const struct aty_pll_ops aty_pll_ct = {
+	.var_to_pll	= aty_var_to_pll_ct,
+	.pll_to_var	= aty_pll_to_var_ct,
+	.set_pll	= aty_set_pll_ct,
+	.get_pll	= aty_get_pll_ct,
+	.init_pll       = aty_init_pll_ct
+};
diff --git a/drivers/video/aty/mach64_cursor.c b/drivers/video/aty/mach64_cursor.c
new file mode 100644
index 0000000..ad8b749
--- /dev/null
+++ b/drivers/video/aty/mach64_cursor.c
@@ -0,0 +1,226 @@
+/*
+ *  ATI Mach64 CT/VT/GT/LT Cursor Support
+ */
+
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifdef __sparc__
+#include <asm/pbm.h>
+#include <asm/fbio.h>
+#endif
+
+#include <video/mach64.h>
+#include "atyfb.h"
+
+/*
+ * The hardware cursor definition requires 2 bits per pixel. The
+ * Cursor size reguardless of the visible cursor size is 64 pixels
+ * by 64 lines. The total memory required to define the cursor is
+ * 16 bytes / line for 64 lines or 1024 bytes of data. The data
+ * must be in a contigiuos format. The 2 bit cursor code values are
+ * as follows:
+ *
+ *	00 - pixel colour = CURSOR_CLR_0
+ *	01 - pixel colour = CURSOR_CLR_1
+ *	10 - pixel colour = transparent (current display pixel)
+ *	11 - pixel colour = 1's complement of current display pixel
+ *
+ *	Cursor Offset        64 pixels		 Actual Displayed Area
+ *            \_________________________/
+ *	      |			|	|	|
+ *	      |<--------------->|	|	|
+ *	      | CURS_HORZ_OFFSET|	|	|
+ *	      |			|_______|	|  64 Lines
+ *	      |			   ^	|	|
+ *	      |			   |	|	|
+ *	      |		CURS_VERT_OFFSET|	|
+ *	      |			   |	|	|
+ *	      |____________________|____|	|
+ *
+ *
+ * The Screen position of the top left corner of the displayed
+ * cursor is specificed by CURS_HORZ_VERT_POSN. Care must be taken
+ * when the cursor hot spot is not the top left corner and the
+ * physical cursor position becomes negative. It will be be displayed
+ * if either the horizontal or vertical cursor position is negative
+ *
+ * If x becomes negative the cursor manager must adjust the CURS_HORZ_OFFSET
+ * to a larger number and saturate CUR_HORZ_POSN to zero.
+ *
+ * if Y becomes negative, CUR_VERT_OFFSET must be adjusted to a larger number,
+ * CUR_OFFSET must be adjusted to a point to the appropraite line in the cursor
+ * definitation and CUR_VERT_POSN must be saturated to zero.
+ */
+
+    /*
+     *  Hardware Cursor support.
+     */
+static const u8 cursor_bits_lookup[16] = {
+	0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
+	0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
+};
+
+static const u8 cursor_mask_lookup[16] = {
+	0xaa, 0x2a, 0x8a, 0x0a, 0xa2, 0x22, 0x82, 0x02,
+	0xa8, 0x28, 0x88, 0x08, 0xa0, 0x20, 0x80, 0x00
+};
+
+static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u16 xoff, yoff;
+	int x, y, h;
+
+#ifdef __sparc__
+	if (par->mmaped)
+		return -EPERM;
+#endif
+	if (par->asleep)
+		return -EPERM;
+
+	/* Hide cursor */
+	wait_for_fifo(1, par);
+	aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par) & ~HWCURSOR_ENABLE, par);
+
+	/* set position */
+	if (cursor->set & FB_CUR_SETPOS) {
+		x = cursor->image.dx - cursor->hot.x - info->var.xoffset;
+		if (x < 0) {
+			xoff = -x;
+			x = 0;
+		} else {
+			xoff = 0;
+		}
+
+		y = cursor->image.dy - cursor->hot.y - info->var.yoffset;
+		if (y < 0) {
+			yoff = -y;
+			y = 0;
+		} else {
+			yoff = 0;
+		}
+
+		h = cursor->image.height;
+
+		/*
+		 * In doublescan mode, the cursor location
+		 * and heigh also needs to be doubled.
+		 */
+                if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) {
+			y<<=1;
+			h<<=1;
+		}
+		wait_for_fifo(4, par);
+		aty_st_le32(CUR_OFFSET, (info->fix.smem_len >> 3) + (yoff << 1), par);
+		aty_st_le32(CUR_HORZ_VERT_OFF,
+			    ((u32) (64 - h + yoff) << 16) | xoff, par);
+		aty_st_le32(CUR_HORZ_VERT_POSN, ((u32) y << 16) | x, par);
+	}
+
+	/* Set color map */
+	if (cursor->set & FB_CUR_SETCMAP) {
+		u32 fg_idx, bg_idx, fg, bg;
+
+		fg_idx = cursor->image.fg_color;
+		bg_idx = cursor->image.bg_color;
+
+		fg = (info->cmap.red[fg_idx] << 24) |
+		     (info->cmap.green[fg_idx] << 16) |
+		     (info->cmap.blue[fg_idx] << 8) | 15;
+
+		bg = (info->cmap.red[bg_idx] << 24) |
+		     (info->cmap.green[bg_idx] << 16) |
+		     (info->cmap.blue[bg_idx] << 8);
+
+		wait_for_fifo(2, par);
+		aty_st_le32(CUR_CLR0, bg, par);
+		aty_st_le32(CUR_CLR1, fg, par);
+	}
+
+	if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
+	    u8 *src = (u8 *)cursor->image.data;
+	    u8 *msk = (u8 *)cursor->mask;
+	    u8 __iomem *dst = (u8 __iomem *)info->sprite.addr;
+	    unsigned int width = (cursor->image.width + 7) >> 3;
+	    unsigned int height = cursor->image.height;
+	    unsigned int align = info->sprite.scan_align;
+
+	    unsigned int i, j, offset;
+	    u8 m, b;
+
+	    // Clear cursor image with 1010101010...
+	    fb_memset(dst, 0xaa, 1024);
+
+	    offset = align - width*2;
+
+	    for (i = 0; i < height; i++) {
+		for (j = 0; j < width; j++) {
+			b = *src++;
+			m = *msk++;
+			switch (cursor->rop) {
+			case ROP_XOR:
+			    // Upper 4 bits of mask data
+			    fb_writeb(cursor_mask_lookup[m >> 4 ] |
+				cursor_bits_lookup[(b ^ m) >> 4], dst++);
+			    // Lower 4 bits of mask
+			    fb_writeb(cursor_mask_lookup[m & 0x0f ] |
+				cursor_bits_lookup[(b ^ m) & 0x0f], dst++);
+			    break;
+			case ROP_COPY:
+			    // Upper 4 bits of mask data
+			    fb_writeb(cursor_mask_lookup[m >> 4 ] |
+				cursor_bits_lookup[(b & m) >> 4], dst++);
+			    // Lower 4 bits of mask
+			    fb_writeb(cursor_mask_lookup[m & 0x0f ] |
+				cursor_bits_lookup[(b & m) & 0x0f], dst++);
+			    break;
+			}
+		}
+		dst += offset;
+	    }
+	}
+
+	if (cursor->enable) {
+		wait_for_fifo(1, par);
+		aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par)
+			    | HWCURSOR_ENABLE, par);
+	}
+	return 0;
+}
+
+int __init aty_init_cursor(struct fb_info *info)
+{
+	unsigned long addr;
+
+	info->fix.smem_len -= PAGE_SIZE;
+
+#ifdef __sparc__
+	addr = (unsigned long) info->screen_base - 0x800000 + info->fix.smem_len;
+	info->sprite.addr = (u8 *) addr;
+#else
+#ifdef __BIG_ENDIAN
+	addr = info->fix.smem_start - 0x800000 + info->fix.smem_len;
+	info->sprite.addr = (u8 *) ioremap(addr, 1024);
+#else
+	addr = (unsigned long) info->screen_base + info->fix.smem_len;
+	info->sprite.addr = (u8 *) addr;
+#endif
+#endif
+	if (!info->sprite.addr)
+		return -ENXIO;
+	info->sprite.size = PAGE_SIZE;
+	info->sprite.scan_align = 16;	/* Scratch pad 64 bytes wide */
+	info->sprite.buf_align = 16; 	/* and 64 lines tall. */
+	info->sprite.flags = FB_PIXMAP_IO;
+
+	info->fbops->fb_cursor = atyfb_cursor;
+
+	return 0;
+}
+
diff --git a/drivers/video/aty/mach64_gx.c b/drivers/video/aty/mach64_gx.c
new file mode 100644
index 0000000..01fdff7
--- /dev/null
+++ b/drivers/video/aty/mach64_gx.c
@@ -0,0 +1,912 @@
+
+/*
+ *  ATI Mach64 GX Support
+ */
+
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/sched.h>
+
+#include <asm/io.h>
+
+#include <video/mach64.h>
+#include "atyfb.h"
+
+/* Definitions for the ICS 2595 == ATI 18818_1 Clockchip */
+
+#define REF_FREQ_2595       1432	/*  14.33 MHz  (exact   14.31818) */
+#define REF_DIV_2595          46	/* really 43 on ICS 2595 !!!  */
+				  /* ohne Prescaler */
+#define MAX_FREQ_2595      15938	/* 159.38 MHz  (really 170.486) */
+#define MIN_FREQ_2595       8000	/*  80.00 MHz  (        85.565) */
+				  /* mit Prescaler 2, 4, 8 */
+#define ABS_MIN_FREQ_2595   1000	/*  10.00 MHz  (really  10.697) */
+#define N_ADJ_2595           257
+
+#define STOP_BITS_2595     0x1800
+
+
+#define MIN_N_408		2
+
+#define MIN_N_1703		6
+
+#define MIN_M		2
+#define MAX_M		30
+#define MIN_N		35
+#define MAX_N		255-8
+
+
+    /*
+     *  Support Functions
+     */
+
+static void aty_dac_waste4(const struct atyfb_par *par)
+{
+	(void) aty_ld_8(DAC_REGS, par);
+
+	(void) aty_ld_8(DAC_REGS + 2, par);
+	(void) aty_ld_8(DAC_REGS + 2, par);
+	(void) aty_ld_8(DAC_REGS + 2, par);
+	(void) aty_ld_8(DAC_REGS + 2, par);
+}
+
+static void aty_StrobeClock(const struct atyfb_par *par)
+{
+	u8 tmp;
+
+	udelay(26);
+
+	tmp = aty_ld_8(CLOCK_CNTL, par);
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset, tmp | CLOCK_STROBE, par);
+	return;
+}
+
+
+    /*
+     *  IBM RGB514 DAC and Clock Chip
+     */
+
+static void aty_st_514(int offset, u8 val, const struct atyfb_par *par)
+{
+	aty_st_8(DAC_CNTL, 1, par);
+	/* right addr byte */
+	aty_st_8(DAC_W_INDEX, offset & 0xff, par);
+	/* left addr byte */
+	aty_st_8(DAC_DATA, (offset >> 8) & 0xff, par);
+	aty_st_8(DAC_MASK, val, par);
+	aty_st_8(DAC_CNTL, 0, par);
+}
+
+static int aty_set_dac_514(const struct fb_info *info,
+			   const union aty_pll *pll, u32 bpp, u32 accel)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	static struct {
+		u8 pixel_dly;
+		u8 misc2_cntl;
+		u8 pixel_rep;
+		u8 pixel_cntl_index;
+		u8 pixel_cntl_v1;
+	} tab[3] = {
+		{
+		0, 0x41, 0x03, 0x71, 0x45},	/* 8 bpp */
+		{
+		0, 0x45, 0x04, 0x0c, 0x01},	/* 555 */
+		{
+		0, 0x45, 0x06, 0x0e, 0x00},	/* XRGB */
+	};
+	int i;
+
+	switch (bpp) {
+	case 8:
+	default:
+		i = 0;
+		break;
+	case 16:
+		i = 1;
+		break;
+	case 32:
+		i = 2;
+		break;
+	}
+	aty_st_514(0x90, 0x00, par);	/* VRAM Mask Low */
+	aty_st_514(0x04, tab[i].pixel_dly, par);	/* Horizontal Sync Control */
+	aty_st_514(0x05, 0x00, par);	/* Power Management */
+	aty_st_514(0x02, 0x01, par);	/* Misc Clock Control */
+	aty_st_514(0x71, tab[i].misc2_cntl, par);	/* Misc Control 2 */
+	aty_st_514(0x0a, tab[i].pixel_rep, par);	/* Pixel Format */
+	aty_st_514(tab[i].pixel_cntl_index, tab[i].pixel_cntl_v1, par);
+	/* Misc Control 2 / 16 BPP Control / 32 BPP Control */
+	return 0;
+}
+
+static int aty_var_to_pll_514(const struct fb_info *info, u32 vclk_per,
+			      u32 bpp, union aty_pll *pll)
+{
+	/*
+	 *  FIXME: use real calculations instead of using fixed values from the old
+	 *         driver
+	 */
+	static struct {
+		u32 limit;	/* pixlock rounding limit (arbitrary) */
+		u8 m;		/* (df<<6) | vco_div_count */
+		u8 n;		/* ref_div_count */
+	} RGB514_clocks[7] = {
+		{
+		8000, (3 << 6) | 20, 9},	/*  7395 ps / 135.2273 MHz */
+		{
+		10000, (1 << 6) | 19, 3},	/*  9977 ps / 100.2273 MHz */
+		{
+		13000, (1 << 6) | 2, 3},	/* 12509 ps /  79.9432 MHz */
+		{
+		14000, (2 << 6) | 8, 7},	/* 13394 ps /  74.6591 MHz */
+		{
+		16000, (1 << 6) | 44, 6},	/* 15378 ps /  65.0284 MHz */
+		{
+		25000, (1 << 6) | 15, 5},	/* 17460 ps /  57.2727 MHz */
+		{
+		50000, (0 << 6) | 53, 7},	/* 33145 ps /  30.1705 MHz */
+	};
+	int i;
+
+	for (i = 0; i < sizeof(RGB514_clocks) / sizeof(*RGB514_clocks);
+	     i++)
+		if (vclk_per <= RGB514_clocks[i].limit) {
+			pll->ibm514.m = RGB514_clocks[i].m;
+			pll->ibm514.n = RGB514_clocks[i].n;
+			return 0;
+		}
+	return -EINVAL;
+}
+
+static u32 aty_pll_514_to_var(const struct fb_info *info,
+			      const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u8 df, vco_div_count, ref_div_count;
+
+	df = pll->ibm514.m >> 6;
+	vco_div_count = pll->ibm514.m & 0x3f;
+	ref_div_count = pll->ibm514.n;
+
+	return ((par->ref_clk_per * ref_div_count) << (3 - df))/
+	    		(vco_div_count + 65);
+}
+
+static void aty_set_pll_514(const struct fb_info *info,
+			    const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+	aty_st_514(0x06, 0x02, par);	/* DAC Operation */
+	aty_st_514(0x10, 0x01, par);	/* PLL Control 1 */
+	aty_st_514(0x70, 0x01, par);	/* Misc Control 1 */
+	aty_st_514(0x8f, 0x1f, par);	/* PLL Ref. Divider Input */
+	aty_st_514(0x03, 0x00, par);	/* Sync Control */
+	aty_st_514(0x05, 0x00, par);	/* Power Management */
+	aty_st_514(0x20, pll->ibm514.m, par);	/* F0 / M0 */
+	aty_st_514(0x21, pll->ibm514.n, par);	/* F1 / N0 */
+}
+
+const struct aty_dac_ops aty_dac_ibm514 = {
+	.set_dac	= aty_set_dac_514,
+};
+
+const struct aty_pll_ops aty_pll_ibm514 = {
+	.var_to_pll	= aty_var_to_pll_514,
+	.pll_to_var	= aty_pll_514_to_var,
+	.set_pll	= aty_set_pll_514,
+};
+
+
+    /*
+     *  ATI 68860-B DAC
+     */
+
+static int aty_set_dac_ATI68860_B(const struct fb_info *info,
+				  const union aty_pll *pll, u32 bpp,
+				  u32 accel)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 gModeReg, devSetupRegA, temp, mask;
+
+	gModeReg = 0;
+	devSetupRegA = 0;
+
+	switch (bpp) {
+	case 8:
+		gModeReg = 0x83;
+		devSetupRegA =
+		    0x60 | 0x00 /*(info->mach64DAC8Bit ? 0x00 : 0x01) */ ;
+		break;
+	case 15:
+		gModeReg = 0xA0;
+		devSetupRegA = 0x60;
+		break;
+	case 16:
+		gModeReg = 0xA1;
+		devSetupRegA = 0x60;
+		break;
+	case 24:
+		gModeReg = 0xC0;
+		devSetupRegA = 0x60;
+		break;
+	case 32:
+		gModeReg = 0xE3;
+		devSetupRegA = 0x60;
+		break;
+	}
+
+	if (!accel) {
+		gModeReg = 0x80;
+		devSetupRegA = 0x61;
+	}
+
+	temp = aty_ld_8(DAC_CNTL, par);
+	aty_st_8(DAC_CNTL, (temp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3,
+		 par);
+
+	aty_st_8(DAC_REGS + 2, 0x1D, par);
+	aty_st_8(DAC_REGS + 3, gModeReg, par);
+	aty_st_8(DAC_REGS, 0x02, par);
+
+	temp = aty_ld_8(DAC_CNTL, par);
+	aty_st_8(DAC_CNTL, temp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par);
+
+	if (info->fix.smem_len < ONE_MB)
+		mask = 0x04;
+	else if (info->fix.smem_len == ONE_MB)
+		mask = 0x08;
+	else
+		mask = 0x0C;
+
+	/* The following assumes that the BIOS has correctly set R7 of the
+	 * Device Setup Register A at boot time.
+	 */
+#define A860_DELAY_L	0x80
+
+	temp = aty_ld_8(DAC_REGS, par);
+	aty_st_8(DAC_REGS, (devSetupRegA | mask) | (temp & A860_DELAY_L),
+		 par);
+	temp = aty_ld_8(DAC_CNTL, par);
+	aty_st_8(DAC_CNTL, (temp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)),
+		 par);
+
+	aty_st_le32(BUS_CNTL, 0x890e20f1, par);
+	aty_st_le32(DAC_CNTL, 0x47052100, par);
+	return 0;
+}
+
+const struct aty_dac_ops aty_dac_ati68860b = {
+	.set_dac	= aty_set_dac_ATI68860_B,
+};
+
+
+    /*
+     *  AT&T 21C498 DAC
+     */
+
+static int aty_set_dac_ATT21C498(const struct fb_info *info,
+				 const union aty_pll *pll, u32 bpp,
+				 u32 accel)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 dotClock;
+	int muxmode = 0;
+	int DACMask = 0;
+
+	dotClock = 100000000 / pll->ics2595.period_in_ps;
+
+	switch (bpp) {
+	case 8:
+		if (dotClock > 8000) {
+			DACMask = 0x24;
+			muxmode = 1;
+		} else
+			DACMask = 0x04;
+		break;
+	case 15:
+		DACMask = 0x16;
+		break;
+	case 16:
+		DACMask = 0x36;
+		break;
+	case 24:
+		DACMask = 0xE6;
+		break;
+	case 32:
+		DACMask = 0xE6;
+		break;
+	}
+
+	if (1 /* info->mach64DAC8Bit */ )
+		DACMask |= 0x02;
+
+	aty_dac_waste4(par);
+	aty_st_8(DAC_REGS + 2, DACMask, par);
+
+	aty_st_le32(BUS_CNTL, 0x890e20f1, par);
+	aty_st_le32(DAC_CNTL, 0x00072000, par);
+	return muxmode;
+}
+
+const struct aty_dac_ops aty_dac_att21c498 = {
+	.set_dac	= aty_set_dac_ATT21C498,
+};
+
+
+    /*
+     *  ATI 18818 / ICS 2595 Clock Chip
+     */
+
+static int aty_var_to_pll_18818(const struct fb_info *info, u32 vclk_per,
+				u32 bpp, union aty_pll *pll)
+{
+	u32 MHz100;		/* in 0.01 MHz */
+	u32 program_bits;
+	u32 post_divider;
+
+	/* Calculate the programming word */
+	MHz100 = 100000000 / vclk_per;
+
+	program_bits = -1;
+	post_divider = 1;
+
+	if (MHz100 > MAX_FREQ_2595) {
+		MHz100 = MAX_FREQ_2595;
+		return -EINVAL;
+	} else if (MHz100 < ABS_MIN_FREQ_2595) {
+		program_bits = 0;	/* MHz100 = 257 */
+		return -EINVAL;
+	} else {
+		while (MHz100 < MIN_FREQ_2595) {
+			MHz100 *= 2;
+			post_divider *= 2;
+		}
+	}
+	MHz100 *= 1000;
+	MHz100 = (REF_DIV_2595 * MHz100) / REF_FREQ_2595;
+ 
+	MHz100 += 500;		/* + 0.5 round */
+	MHz100 /= 1000;
+
+	if (program_bits == -1) {
+		program_bits = MHz100 - N_ADJ_2595;
+		switch (post_divider) {
+		case 1:
+			program_bits |= 0x0600;
+			break;
+		case 2:
+			program_bits |= 0x0400;
+			break;
+		case 4:
+			program_bits |= 0x0200;
+			break;
+		case 8:
+		default:
+			break;
+		}
+	}
+
+	program_bits |= STOP_BITS_2595;
+
+	pll->ics2595.program_bits = program_bits;
+	pll->ics2595.locationAddr = 0;
+	pll->ics2595.post_divider = post_divider;
+	pll->ics2595.period_in_ps = vclk_per;
+
+	return 0;
+}
+
+static u32 aty_pll_18818_to_var(const struct fb_info *info,
+				const union aty_pll *pll)
+{
+	return (pll->ics2595.period_in_ps);	/* default for now */
+}
+
+static void aty_ICS2595_put1bit(u8 data, const struct atyfb_par *par)
+{
+	u8 tmp;
+
+	data &= 0x01;
+	tmp = aty_ld_8(CLOCK_CNTL, par);
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
+		 (tmp & ~0x04) | (data << 2), par);
+
+	tmp = aty_ld_8(CLOCK_CNTL, par);
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (0 << 3),
+		 par);
+
+	aty_StrobeClock(par);
+
+	tmp = aty_ld_8(CLOCK_CNTL, par);
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (1 << 3),
+		 par);
+
+	aty_StrobeClock(par);
+	return;
+}
+
+static void aty_set_pll18818(const struct fb_info *info,
+			     const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 program_bits;
+	u32 locationAddr;
+
+	u32 i;
+
+	u8 old_clock_cntl;
+	u8 old_crtc_ext_disp;
+
+	old_clock_cntl = aty_ld_8(CLOCK_CNTL, par);
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par);
+
+	old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+	aty_st_8(CRTC_GEN_CNTL + 3,
+		 old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+	mdelay(15);		/* delay for 50 (15) ms */
+
+	program_bits = pll->ics2595.program_bits;
+	locationAddr = pll->ics2595.locationAddr;
+
+	/* Program the clock chip */
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par);	/* Strobe = 0 */
+	aty_StrobeClock(par);
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 1, par);	/* Strobe = 0 */
+	aty_StrobeClock(par);
+
+	aty_ICS2595_put1bit(1, par);	/* Send start bits */
+	aty_ICS2595_put1bit(0, par);	/* Start bit */
+	aty_ICS2595_put1bit(0, par);	/* Read / ~Write */
+
+	for (i = 0; i < 5; i++) {	/* Location 0..4 */
+		aty_ICS2595_put1bit(locationAddr & 1, par);
+		locationAddr >>= 1;
+	}
+
+	for (i = 0; i < 8 + 1 + 2 + 2; i++) {
+		aty_ICS2595_put1bit(program_bits & 1, par);
+		program_bits >>= 1;
+	}
+
+	mdelay(1);		/* delay for 1 ms */
+
+	(void) aty_ld_8(DAC_REGS, par);	/* Clear DAC Counter */
+	aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
+		 old_clock_cntl | CLOCK_STROBE, par);
+
+	mdelay(50);		/* delay for 50 (15) ms */
+	aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
+		 ((pll->ics2595.locationAddr & 0x0F) | CLOCK_STROBE), par);
+	return;
+}
+
+const struct aty_pll_ops aty_pll_ati18818_1 = {
+	.var_to_pll	= aty_var_to_pll_18818,
+	.pll_to_var	= aty_pll_18818_to_var,
+	.set_pll	= aty_set_pll18818,
+};
+
+
+    /*
+     *  STG 1703 Clock Chip
+     */
+
+static int aty_var_to_pll_1703(const struct fb_info *info, u32 vclk_per,
+			       u32 bpp, union aty_pll *pll)
+{
+	u32 mhz100;		/* in 0.01 MHz */
+	u32 program_bits;
+	/* u32 post_divider; */
+	u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
+	u32 temp, tempB;
+	u16 remainder, preRemainder;
+	short divider = 0, tempA;
+
+	/* Calculate the programming word */
+	mhz100 = 100000000 / vclk_per;
+	mach64MinFreq = MIN_FREQ_2595;
+	mach64MaxFreq = MAX_FREQ_2595;
+	mach64RefFreq = REF_FREQ_2595;	/* 14.32 MHz */
+
+	/* Calculate program word */
+	if (mhz100 == 0)
+		program_bits = 0xE0;
+	else {
+		if (mhz100 < mach64MinFreq)
+			mhz100 = mach64MinFreq;
+		if (mhz100 > mach64MaxFreq)
+			mhz100 = mach64MaxFreq;
+
+		divider = 0;
+		while (mhz100 < (mach64MinFreq << 3)) {
+			mhz100 <<= 1;
+			divider += 0x20;
+		}
+
+		temp = (unsigned int) (mhz100);
+		temp = (unsigned int) (temp * (MIN_N_1703 + 2));
+		temp -= (short) (mach64RefFreq << 1);
+
+		tempA = MIN_N_1703;
+		preRemainder = 0xffff;
+
+		do {
+			tempB = temp;
+			remainder = tempB % mach64RefFreq;
+			tempB = tempB / mach64RefFreq;
+
+			if ((tempB & 0xffff) <= 127
+			    && (remainder <= preRemainder)) {
+				preRemainder = remainder;
+				divider &= ~0x1f;
+				divider |= tempA;
+				divider =
+				    (divider & 0x00ff) +
+				    ((tempB & 0xff) << 8);
+			}
+
+			temp += mhz100;
+			tempA++;
+		} while (tempA <= (MIN_N_1703 << 1));
+
+		program_bits = divider;
+	}
+
+	pll->ics2595.program_bits = program_bits;
+	pll->ics2595.locationAddr = 0;
+	pll->ics2595.post_divider = divider;	/* fuer nix */
+	pll->ics2595.period_in_ps = vclk_per;
+
+	return 0;
+}
+
+static u32 aty_pll_1703_to_var(const struct fb_info *info,
+			       const union aty_pll *pll)
+{
+	return (pll->ics2595.period_in_ps);	/* default for now */
+}
+
+static void aty_set_pll_1703(const struct fb_info *info,
+			     const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 program_bits;
+	u32 locationAddr;
+
+	char old_crtc_ext_disp;
+
+	old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+	aty_st_8(CRTC_GEN_CNTL + 3,
+		 old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+	program_bits = pll->ics2595.program_bits;
+	locationAddr = pll->ics2595.locationAddr;
+
+	/* Program clock */
+	aty_dac_waste4(par);
+
+	(void) aty_ld_8(DAC_REGS + 2, par);
+	aty_st_8(DAC_REGS + 2, (locationAddr << 1) + 0x20, par);
+	aty_st_8(DAC_REGS + 2, 0, par);
+	aty_st_8(DAC_REGS + 2, (program_bits & 0xFF00) >> 8, par);
+	aty_st_8(DAC_REGS + 2, (program_bits & 0xFF), par);
+
+	(void) aty_ld_8(DAC_REGS, par);	/* Clear DAC Counter */
+	aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+	return;
+}
+
+const struct aty_pll_ops aty_pll_stg1703 = {
+	.var_to_pll	= aty_var_to_pll_1703,
+	.pll_to_var	= aty_pll_1703_to_var,
+	.set_pll	= aty_set_pll_1703,
+};
+
+
+    /*
+     *  Chrontel 8398 Clock Chip
+     */
+
+static int aty_var_to_pll_8398(const struct fb_info *info, u32 vclk_per,
+			       u32 bpp, union aty_pll *pll)
+{
+	u32 tempA, tempB, fOut, longMHz100, diff, preDiff;
+
+	u32 mhz100;		/* in 0.01 MHz */
+	u32 program_bits;
+	/* u32 post_divider; */
+	u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
+	u16 m, n, k = 0, save_m, save_n, twoToKth;
+
+	/* Calculate the programming word */
+	mhz100 = 100000000 / vclk_per;
+	mach64MinFreq = MIN_FREQ_2595;
+	mach64MaxFreq = MAX_FREQ_2595;
+	mach64RefFreq = REF_FREQ_2595;	/* 14.32 MHz */
+
+	save_m = 0;
+	save_n = 0;
+
+	/* Calculate program word */
+	if (mhz100 == 0)
+		program_bits = 0xE0;
+	else {
+		if (mhz100 < mach64MinFreq)
+			mhz100 = mach64MinFreq;
+		if (mhz100 > mach64MaxFreq)
+			mhz100 = mach64MaxFreq;
+
+		longMHz100 = mhz100 * 256 / 100;	/* 8 bit scale this */
+
+		while (mhz100 < (mach64MinFreq << 3)) {
+			mhz100 <<= 1;
+			k++;
+		}
+
+		twoToKth = 1 << k;
+		diff = 0;
+		preDiff = 0xFFFFFFFF;
+
+		for (m = MIN_M; m <= MAX_M; m++) {
+			for (n = MIN_N; n <= MAX_N; n++) {
+				tempA = 938356;		/* 14.31818 * 65536 */
+				tempA *= (n + 8);	/* 43..256 */
+				tempB = twoToKth * 256;
+				tempB *= (m + 2);	/* 4..32 */
+				fOut = tempA / tempB;	/* 8 bit scale */
+
+				if (longMHz100 > fOut)
+					diff = longMHz100 - fOut;
+				else
+					diff = fOut - longMHz100;
+
+				if (diff < preDiff) {
+					save_m = m;
+					save_n = n;
+					preDiff = diff;
+				}
+			}
+		}
+
+		program_bits = (k << 6) + (save_m) + (save_n << 8);
+	}
+
+	pll->ics2595.program_bits = program_bits;
+	pll->ics2595.locationAddr = 0;
+	pll->ics2595.post_divider = 0;
+	pll->ics2595.period_in_ps = vclk_per;
+
+	return 0;
+}
+
+static u32 aty_pll_8398_to_var(const struct fb_info *info,
+			       const union aty_pll *pll)
+{
+	return (pll->ics2595.period_in_ps);	/* default for now */
+}
+
+static void aty_set_pll_8398(const struct fb_info *info,
+			     const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 program_bits;
+	u32 locationAddr;
+
+	char old_crtc_ext_disp;
+	char tmp;
+
+	old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+	aty_st_8(CRTC_GEN_CNTL + 3,
+		 old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+	program_bits = pll->ics2595.program_bits;
+	locationAddr = pll->ics2595.locationAddr;
+
+	/* Program clock */
+	tmp = aty_ld_8(DAC_CNTL, par);
+	aty_st_8(DAC_CNTL, tmp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par);
+
+	aty_st_8(DAC_REGS, locationAddr, par);
+	aty_st_8(DAC_REGS + 1, (program_bits & 0xff00) >> 8, par);
+	aty_st_8(DAC_REGS + 1, (program_bits & 0xff), par);
+
+	tmp = aty_ld_8(DAC_CNTL, par);
+	aty_st_8(DAC_CNTL, (tmp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3,
+		 par);
+
+	(void) aty_ld_8(DAC_REGS, par);	/* Clear DAC Counter */
+	aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+
+	return;
+}
+
+const struct aty_pll_ops aty_pll_ch8398 = {
+	.var_to_pll	= aty_var_to_pll_8398,
+	.pll_to_var	= aty_pll_8398_to_var,
+	.set_pll	= aty_set_pll_8398,
+};
+
+
+    /*
+     *  AT&T 20C408 Clock Chip
+     */
+
+static int aty_var_to_pll_408(const struct fb_info *info, u32 vclk_per,
+			      u32 bpp, union aty_pll *pll)
+{
+	u32 mhz100;		/* in 0.01 MHz */
+	u32 program_bits;
+	/* u32 post_divider; */
+	u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
+	u32 temp, tempB;
+	u16 remainder, preRemainder;
+	short divider = 0, tempA;
+
+	/* Calculate the programming word */
+	mhz100 = 100000000 / vclk_per;
+	mach64MinFreq = MIN_FREQ_2595;
+	mach64MaxFreq = MAX_FREQ_2595;
+	mach64RefFreq = REF_FREQ_2595;	/* 14.32 MHz */
+
+	/* Calculate program word */
+	if (mhz100 == 0)
+		program_bits = 0xFF;
+	else {
+		if (mhz100 < mach64MinFreq)
+			mhz100 = mach64MinFreq;
+		if (mhz100 > mach64MaxFreq)
+			mhz100 = mach64MaxFreq;
+
+		while (mhz100 < (mach64MinFreq << 3)) {
+			mhz100 <<= 1;
+			divider += 0x40;
+		}
+
+		temp = (unsigned int) mhz100;
+		temp = (unsigned int) (temp * (MIN_N_408 + 2));
+		temp -= ((short) (mach64RefFreq << 1));
+
+		tempA = MIN_N_408;
+		preRemainder = 0xFFFF;
+
+		do {
+			tempB = temp;
+			remainder = tempB % mach64RefFreq;
+			tempB = tempB / mach64RefFreq;
+			if (((tempB & 0xFFFF) <= 255)
+			    && (remainder <= preRemainder)) {
+				preRemainder = remainder;
+				divider &= ~0x3f;
+				divider |= tempA;
+				divider =
+				    (divider & 0x00FF) +
+				    ((tempB & 0xFF) << 8);
+			}
+			temp += mhz100;
+			tempA++;
+		} while (tempA <= 32);
+
+		program_bits = divider;
+	}
+
+	pll->ics2595.program_bits = program_bits;
+	pll->ics2595.locationAddr = 0;
+	pll->ics2595.post_divider = divider;	/* fuer nix */
+	pll->ics2595.period_in_ps = vclk_per;
+
+	return 0;
+}
+
+static u32 aty_pll_408_to_var(const struct fb_info *info,
+			      const union aty_pll *pll)
+{
+	return (pll->ics2595.period_in_ps);	/* default for now */
+}
+
+static void aty_set_pll_408(const struct fb_info *info,
+			    const union aty_pll *pll)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	u32 program_bits;
+	u32 locationAddr;
+
+	u8 tmpA, tmpB, tmpC;
+	char old_crtc_ext_disp;
+
+	old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+	aty_st_8(CRTC_GEN_CNTL + 3,
+		 old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+	program_bits = pll->ics2595.program_bits;
+	locationAddr = pll->ics2595.locationAddr;
+
+	/* Program clock */
+	aty_dac_waste4(par);
+	tmpB = aty_ld_8(DAC_REGS + 2, par) | 1;
+	aty_dac_waste4(par);
+	aty_st_8(DAC_REGS + 2, tmpB, par);
+
+	tmpA = tmpB;
+	tmpC = tmpA;
+	tmpA |= 8;
+	tmpB = 1;
+
+	aty_st_8(DAC_REGS, tmpB, par);
+	aty_st_8(DAC_REGS + 2, tmpA, par);
+
+	udelay(400);		/* delay for 400 us */
+
+	locationAddr = (locationAddr << 2) + 0x40;
+	tmpB = locationAddr;
+	tmpA = program_bits >> 8;
+
+	aty_st_8(DAC_REGS, tmpB, par);
+	aty_st_8(DAC_REGS + 2, tmpA, par);
+
+	tmpB = locationAddr + 1;
+	tmpA = (u8) program_bits;
+
+	aty_st_8(DAC_REGS, tmpB, par);
+	aty_st_8(DAC_REGS + 2, tmpA, par);
+
+	tmpB = locationAddr + 2;
+	tmpA = 0x77;
+
+	aty_st_8(DAC_REGS, tmpB, par);
+	aty_st_8(DAC_REGS + 2, tmpA, par);
+
+	udelay(400);		/* delay for 400 us */
+	tmpA = tmpC & (~(1 | 8));
+	tmpB = 1;
+
+	aty_st_8(DAC_REGS, tmpB, par);
+	aty_st_8(DAC_REGS + 2, tmpA, par);
+
+	(void) aty_ld_8(DAC_REGS, par);	/* Clear DAC Counter */
+	aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+	return;
+}
+
+const struct aty_pll_ops aty_pll_att20c408 = {
+	.var_to_pll	= aty_var_to_pll_408,
+	.pll_to_var	= aty_pll_408_to_var,
+	.set_pll	= aty_set_pll_408,
+};
+
+
+    /*
+     *  Unsupported DAC and Clock Chip
+     */
+
+static int aty_set_dac_unsupported(const struct fb_info *info,
+				   const union aty_pll *pll, u32 bpp,
+				   u32 accel)
+{
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+	aty_st_le32(BUS_CNTL, 0x890e20f1, par);
+	aty_st_le32(DAC_CNTL, 0x47052100, par);
+	/* new in 2.2.3p1 from Geert. ???????? */
+	aty_st_le32(BUS_CNTL, 0x590e10ff, par);
+	aty_st_le32(DAC_CNTL, 0x47012100, par);
+	return 0;
+}
+
+static int dummy(void)
+{
+	return 0;
+}
+
+const struct aty_dac_ops aty_dac_unsupported = {
+	.set_dac	= aty_set_dac_unsupported,
+};
+
+const struct aty_pll_ops aty_pll_unsupported = {
+	.var_to_pll	= (void *) dummy,
+	.pll_to_var	= (void *) dummy,
+	.set_pll	= (void *) dummy,
+};
diff --git a/drivers/video/aty/radeon_accel.c b/drivers/video/aty/radeon_accel.c
new file mode 100644
index 0000000..3ca27cb
--- /dev/null
+++ b/drivers/video/aty/radeon_accel.c
@@ -0,0 +1,316 @@
+#include "radeonfb.h"
+
+/* the accelerated functions here are patterned after the 
+ * "ACCEL_MMIO" ifdef branches in XFree86
+ * --dte
+ */
+
+static void radeon_fixup_offset(struct radeonfb_info *rinfo)
+{
+	u32 local_base;
+
+	/* *** Ugly workaround *** */
+	/*
+	 * On some platforms, the video memory is mapped at 0 in radeon chip space
+	 * (like PPCs) by the firmware. X will always move it up so that it's seen
+	 * by the chip to be at the same address as the PCI BAR.
+	 * That means that when switching back from X, there is a mismatch between
+	 * the offsets programmed into the engine. This means that potentially,
+	 * accel operations done before radeonfb has a chance to re-init the engine
+	 * will have incorrect offsets, and potentially trash system memory !
+	 *
+	 * The correct fix is for fbcon to never call any accel op before the engine
+	 * has properly been re-initialized (by a call to set_var), but this is a
+	 * complex fix. This workaround in the meantime, called before every accel
+	 * operation, makes sure the offsets are in sync.
+	 */
+
+	radeon_fifo_wait (1);
+	local_base = INREG(MC_FB_LOCATION) << 16;
+	if (local_base == rinfo->fb_local_base)
+		return;
+
+	rinfo->fb_local_base = local_base;
+
+	radeon_fifo_wait (3);
+	OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) |
+				     (rinfo->fb_local_base >> 10));
+	OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+	OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+}
+
+static void radeonfb_prim_fillrect(struct radeonfb_info *rinfo, 
+				   const struct fb_fillrect *region)
+{
+	radeon_fifo_wait(4);  
+  
+	OUTREG(DP_GUI_MASTER_CNTL,  
+		rinfo->dp_gui_master_cntl  /* contains, like GMC_DST_32BPP */
+                | GMC_BRUSH_SOLID_COLOR
+                | ROP3_P);
+	if (radeon_get_dstbpp(rinfo->depth) != DST_8BPP)
+		OUTREG(DP_BRUSH_FRGD_CLR, rinfo->pseudo_palette[region->color]);
+	else
+		OUTREG(DP_BRUSH_FRGD_CLR, region->color);
+	OUTREG(DP_WRITE_MSK, 0xffffffff);
+	OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM));
+
+	radeon_fifo_wait(2);  
+	OUTREG(DST_Y_X, (region->dy << 16) | region->dx);
+	OUTREG(DST_WIDTH_HEIGHT, (region->width << 16) | region->height);
+}
+
+void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region)
+{
+	struct radeonfb_info *rinfo = info->par;
+	struct fb_fillrect modded;
+	int vxres, vyres;
+  
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_fillrect(info, region);
+		return;
+	}
+
+	radeon_fixup_offset(rinfo);
+
+	vxres = info->var.xres_virtual;
+	vyres = info->var.yres_virtual;
+
+	memcpy(&modded, region, sizeof(struct fb_fillrect));
+
+	if(!modded.width || !modded.height ||
+	   modded.dx >= vxres || modded.dy >= vyres)
+		return;
+  
+	if(modded.dx + modded.width  > vxres) modded.width  = vxres - modded.dx;
+	if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
+
+	radeonfb_prim_fillrect(rinfo, &modded);
+}
+
+static void radeonfb_prim_copyarea(struct radeonfb_info *rinfo, 
+				   const struct fb_copyarea *area)
+{
+	int xdir, ydir;
+	u32 sx, sy, dx, dy, w, h;
+
+	w = area->width; h = area->height;
+	dx = area->dx; dy = area->dy;
+	sx = area->sx; sy = area->sy;
+	xdir = sx - dx;
+	ydir = sy - dy;
+
+	if ( xdir < 0 ) { sx += w-1; dx += w-1; }
+	if ( ydir < 0 ) { sy += h-1; dy += h-1; }
+
+	radeon_fifo_wait(3);
+	OUTREG(DP_GUI_MASTER_CNTL,
+		rinfo->dp_gui_master_cntl /* i.e. GMC_DST_32BPP */
+		| GMC_BRUSH_NONE
+		| GMC_SRC_DSTCOLOR
+		| ROP3_S 
+		| DP_SRC_SOURCE_MEMORY );
+	OUTREG(DP_WRITE_MSK, 0xffffffff);
+	OUTREG(DP_CNTL, (xdir>=0 ? DST_X_LEFT_TO_RIGHT : 0)
+			| (ydir>=0 ? DST_Y_TOP_TO_BOTTOM : 0));
+
+	radeon_fifo_wait(3);
+	OUTREG(SRC_Y_X, (sy << 16) | sx);
+	OUTREG(DST_Y_X, (dy << 16) | dx);
+	OUTREG(DST_HEIGHT_WIDTH, (h << 16) | w);
+}
+
+
+void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+	struct radeonfb_info *rinfo = info->par;
+	struct fb_copyarea modded;
+	u32 vxres, vyres;
+	modded.sx = area->sx;
+	modded.sy = area->sy;
+	modded.dx = area->dx;
+	modded.dy = area->dy;
+	modded.width  = area->width;
+	modded.height = area->height;
+  
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_copyarea(info, area);
+		return;
+	}
+
+	radeon_fixup_offset(rinfo);
+
+	vxres = info->var.xres_virtual;
+	vyres = info->var.yres_virtual;
+
+	if(!modded.width || !modded.height ||
+	   modded.sx >= vxres || modded.sy >= vyres ||
+	   modded.dx >= vxres || modded.dy >= vyres)
+		return;
+  
+	if(modded.sx + modded.width > vxres)  modded.width = vxres - modded.sx;
+	if(modded.dx + modded.width > vxres)  modded.width = vxres - modded.dx;
+	if(modded.sy + modded.height > vyres) modded.height = vyres - modded.sy;
+	if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
+  
+	radeonfb_prim_copyarea(rinfo, &modded);
+}
+
+void radeonfb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	struct radeonfb_info *rinfo = info->par;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+	radeon_engine_idle();
+
+	cfb_imageblit(info, image);
+}
+
+int radeonfb_sync(struct fb_info *info)
+{
+	struct radeonfb_info *rinfo = info->par;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return 0;
+	radeon_engine_idle();
+
+	return 0;
+}
+
+void radeonfb_engine_reset(struct radeonfb_info *rinfo)
+{
+	u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
+	u32 host_path_cntl;
+
+	radeon_engine_flush (rinfo);
+
+	clock_cntl_index = INREG(CLOCK_CNTL_INDEX);
+	mclk_cntl = INPLL(MCLK_CNTL);
+
+	OUTPLL(MCLK_CNTL, (mclk_cntl |
+			   FORCEON_MCLKA |
+			   FORCEON_MCLKB |
+			   FORCEON_YCLKA |
+			   FORCEON_YCLKB |
+			   FORCEON_MC |
+			   FORCEON_AIC));
+
+	host_path_cntl = INREG(HOST_PATH_CNTL);
+	rbbm_soft_reset = INREG(RBBM_SOFT_RESET);
+
+	if (rinfo->family == CHIP_FAMILY_R300 ||
+	    rinfo->family == CHIP_FAMILY_R350 ||
+	    rinfo->family == CHIP_FAMILY_RV350) {
+		u32 tmp;
+
+		OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset |
+					 SOFT_RESET_CP |
+					 SOFT_RESET_HI |
+					 SOFT_RESET_E2));
+		INREG(RBBM_SOFT_RESET);
+		OUTREG(RBBM_SOFT_RESET, 0);
+		tmp = INREG(RB2D_DSTCACHE_MODE);
+		OUTREG(RB2D_DSTCACHE_MODE, tmp | (1 << 17)); /* FIXME */
+	} else {
+		OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset |
+					SOFT_RESET_CP |
+					SOFT_RESET_HI |
+					SOFT_RESET_SE |
+					SOFT_RESET_RE |
+					SOFT_RESET_PP |
+					SOFT_RESET_E2 |
+					SOFT_RESET_RB);
+		INREG(RBBM_SOFT_RESET);
+		OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32)
+					~(SOFT_RESET_CP |
+					  SOFT_RESET_HI |
+					  SOFT_RESET_SE |
+					  SOFT_RESET_RE |
+					  SOFT_RESET_PP |
+					  SOFT_RESET_E2 |
+					  SOFT_RESET_RB));
+		INREG(RBBM_SOFT_RESET);
+	}
+
+	OUTREG(HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET);
+	INREG(HOST_PATH_CNTL);
+	OUTREG(HOST_PATH_CNTL, host_path_cntl);
+
+	if (rinfo->family != CHIP_FAMILY_R300 ||
+	    rinfo->family != CHIP_FAMILY_R350 ||
+	    rinfo->family != CHIP_FAMILY_RV350)
+		OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset);
+
+	OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
+	OUTPLL(MCLK_CNTL, mclk_cntl);
+}
+
+void radeonfb_engine_init (struct radeonfb_info *rinfo)
+{
+	unsigned long temp;
+
+	/* disable 3D engine */
+	OUTREG(RB3D_CNTL, 0);
+
+	radeonfb_engine_reset(rinfo);
+
+	radeon_fifo_wait (1);
+	if ((rinfo->family != CHIP_FAMILY_R300) &&
+	    (rinfo->family != CHIP_FAMILY_R350) &&
+	    (rinfo->family != CHIP_FAMILY_RV350))
+		OUTREG(RB2D_DSTCACHE_MODE, 0);
+
+	radeon_fifo_wait (3);
+	/* We re-read MC_FB_LOCATION from card as it can have been
+	 * modified by XFree drivers (ouch !)
+	 */
+	rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16;
+
+	OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) |
+				     (rinfo->fb_local_base >> 10));
+	OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+	OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+
+	radeon_fifo_wait (1);
+#if defined(__BIG_ENDIAN)
+	OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN);
+#else
+	OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
+#endif
+	radeon_fifo_wait (2);
+	OUTREG(DEFAULT_SC_TOP_LEFT, 0);
+	OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX |
+					 DEFAULT_SC_BOTTOM_MAX));
+
+	temp = radeon_get_dstbpp(rinfo->depth);
+	rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS);
+
+	radeon_fifo_wait (1);
+	OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl |
+				    GMC_BRUSH_SOLID_COLOR |
+				    GMC_SRC_DATATYPE_COLOR));
+
+	radeon_fifo_wait (7);
+
+	/* clear line drawing regs */
+	OUTREG(DST_LINE_START, 0);
+	OUTREG(DST_LINE_END, 0);
+
+	/* set brush color regs */
+	OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff);
+	OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000);
+
+	/* set source color regs */
+	OUTREG(DP_SRC_FRGD_CLR, 0xffffffff);
+	OUTREG(DP_SRC_BKGD_CLR, 0x00000000);
+
+	/* default write mask */
+	OUTREG(DP_WRITE_MSK, 0xffffffff);
+
+	radeon_engine_idle ();
+}
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
new file mode 100644
index 0000000..e8eb124
--- /dev/null
+++ b/drivers/video/aty/radeon_base.c
@@ -0,0 +1,2587 @@
+/*
+ *	drivers/video/aty/radeon_base.c
+ *
+ *	framebuffer driver for ATI Radeon chipset video boards
+ *
+ *	Copyright 2003	Ben. Herrenschmidt <benh@kernel.crashing.org>
+ *	Copyright 2000	Ani Joshi <ajoshi@kernel.crashing.org>
+ *
+ *	i2c bits from Luca Tettamanti <kronos@kronoz.cjb.net>
+ *	
+ *	Special thanks to ATI DevRel team for their hardware donations.
+ *
+ *	...Insert GPL boilerplate here...
+ *
+ *	Significant portions of this driver apdated from XFree86 Radeon
+ *	driver which has the following copyright notice:
+ *
+ *	Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
+ *                     VA Linux Systems Inc., Fremont, California.
+ *
+ *	All Rights Reserved.
+ *
+ *	Permission is hereby granted, free of charge, to any person obtaining
+ *	a copy of this software and associated documentation files (the
+ *	"Software"), to deal in the Software without restriction, including
+ *	without limitation on the rights to use, copy, modify, merge,
+ *	publish, distribute, sublicense, and/or sell copies of the Software,
+ *	and to permit persons to whom the Software is furnished to do so,
+ *	subject to the following conditions:
+ *
+ *	The above copyright notice and this permission notice (including the
+ *	next paragraph) shall be included in all copies or substantial
+ *	portions of the Software.
+ *
+ *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * 	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *	NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
+ *	THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ *	DEALINGS IN THE SOFTWARE.
+ *
+ *	XFree86 driver authors:
+ *
+ *	   Kevin E. Martin <martin@xfree86.org>
+ *	   Rickard E. Faith <faith@valinux.com>
+ *	   Alan Hourihane <alanh@fairlite.demon.co.uk>
+ *
+ */
+
+
+#define RADEON_VERSION	"0.2.0"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/fb.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_PPC_OF
+
+#include <asm/pci-bridge.h>
+#include "../macmodes.h"
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
+
+#endif /* CONFIG_PPC_OF */
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include <video/radeon.h>
+#include <linux/radeonfb.h>
+
+#include "../edid.h" // MOVE THAT TO include/video
+#include "ati_ids.h"
+#include "radeonfb.h"		    
+
+#define MAX_MAPPED_VRAM	(2048*2048*4)
+#define MIN_MAPPED_VRAM	(1024*768*1)
+
+#define CHIP_DEF(id, family, flags)					\
+	{ PCI_VENDOR_ID_ATI, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (flags) | (CHIP_FAMILY_##family) }
+
+static struct pci_device_id radeonfb_pci_table[] = {
+	/* Mobility M6 */
+	CHIP_DEF(PCI_CHIP_RADEON_LY, 	RV100,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RADEON_LZ,	RV100,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	/* Radeon VE/7000 */
+	CHIP_DEF(PCI_CHIP_RV100_QY, 	RV100,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV100_QZ, 	RV100,	CHIP_HAS_CRTC2),
+	/* Radeon IGP320M (U1) */
+	CHIP_DEF(PCI_CHIP_RS100_4336,	RS100,	CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+	/* Radeon IGP320 (A3) */
+	CHIP_DEF(PCI_CHIP_RS100_4136,	RS100,	CHIP_HAS_CRTC2 | CHIP_IS_IGP), 
+	/* IGP330M/340M/350M (U2) */
+	CHIP_DEF(PCI_CHIP_RS200_4337,	RS200,	CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+	/* IGP330/340/350 (A4) */
+	CHIP_DEF(PCI_CHIP_RS200_4137,	RS200,	CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+	/* Mobility 7000 IGP */
+	CHIP_DEF(PCI_CHIP_RS250_4437,	RS200,	CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+	/* 7000 IGP (A4+) */
+	CHIP_DEF(PCI_CHIP_RS250_4237,	RS200,	CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+	/* 8500 AIW */
+	CHIP_DEF(PCI_CHIP_R200_BB,	R200,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R200_BC,	R200,	CHIP_HAS_CRTC2),
+	/* 8700/8800 */
+	CHIP_DEF(PCI_CHIP_R200_QH,	R200,	CHIP_HAS_CRTC2),
+	/* 8500 */
+	CHIP_DEF(PCI_CHIP_R200_QL,	R200,	CHIP_HAS_CRTC2),
+	/* 9100 */
+	CHIP_DEF(PCI_CHIP_R200_QM,	R200,	CHIP_HAS_CRTC2),
+	/* Mobility M7 */
+	CHIP_DEF(PCI_CHIP_RADEON_LW,	RV200,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RADEON_LX,	RV200,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	/* 7500 */
+	CHIP_DEF(PCI_CHIP_RV200_QW,	RV200,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV200_QX,	RV200,	CHIP_HAS_CRTC2),
+	/* Mobility M9 */
+	CHIP_DEF(PCI_CHIP_RV250_Ld,	RV250,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV250_Le,	RV250,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV250_Lf,	RV250,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV250_Lg,	RV250,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	/* 9000/Pro */
+	CHIP_DEF(PCI_CHIP_RV250_If,	RV250,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV250_Ig,	RV250,	CHIP_HAS_CRTC2),
+	/* Mobility 9100 IGP (U3) */
+	CHIP_DEF(PCI_CHIP_RS300_5835,	RS300,	CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RS350_7835,	RS300,	CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+	/* 9100 IGP (A5) */
+	CHIP_DEF(PCI_CHIP_RS300_5834,	RS300,	CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+	CHIP_DEF(PCI_CHIP_RS350_7834,	RS300,	CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+	/* Mobility 9200 (M9+) */
+	CHIP_DEF(PCI_CHIP_RV280_5C61,	RV280,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV280_5C63,	RV280,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	/* 9200 */
+	CHIP_DEF(PCI_CHIP_RV280_5960,	RV280,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV280_5961,	RV280,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV280_5962,	RV280,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV280_5964,	RV280,	CHIP_HAS_CRTC2),
+	/* 9500 */
+	CHIP_DEF(PCI_CHIP_R300_AD,	R300,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R300_AE,	R300,	CHIP_HAS_CRTC2),
+	/* 9600TX / FireGL Z1 */
+	CHIP_DEF(PCI_CHIP_R300_AF,	R300,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R300_AG,	R300,	CHIP_HAS_CRTC2),
+	/* 9700/9500/Pro/FireGL X1 */
+	CHIP_DEF(PCI_CHIP_R300_ND,	R300,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R300_NE,	R300,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R300_NF,	R300,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R300_NG,	R300,	CHIP_HAS_CRTC2),
+	/* Mobility M10/M11 */
+	CHIP_DEF(PCI_CHIP_RV350_NP,	RV350,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV350_NQ,	RV350,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV350_NR,	RV350,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV350_NS,	RV350,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV350_NT,	RV350,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV350_NV,	RV350,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	/* 9600/FireGL T2 */
+	CHIP_DEF(PCI_CHIP_RV350_AP,	RV350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV350_AQ,	RV350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV360_AR,	RV350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV350_AS,	RV350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV350_AT,	RV350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV350_AV,	RV350,	CHIP_HAS_CRTC2),
+	/* 9800/Pro/FileGL X2 */
+	CHIP_DEF(PCI_CHIP_R350_AH,	R350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R350_AI,	R350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R350_AJ,	R350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R350_AK,	R350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R350_NH,	R350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R350_NI,	R350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R360_NJ,	R350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R350_NK,	R350,	CHIP_HAS_CRTC2),
+	/* Newer stuff */
+	CHIP_DEF(PCI_CHIP_RV380_3E50,	RV380,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV380_3E54,	RV380,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV380_3150,	RV380,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV380_3154,	RV380,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV370_5B60,	RV380,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV370_5B62,	RV380,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV370_5B64,	RV380,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV370_5B65,	RV380,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV370_5460,	RV380,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV370_5464,	RV380,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_R420_JH,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R420_JI,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R420_JJ,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R420_JK,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R420_JL,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R420_JM,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R420_JN,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_R420_JP,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UH,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UI,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UJ,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UK,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UQ,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UR,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UT,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_5D57,	R420,	CHIP_HAS_CRTC2),
+	/* Original Radeon/7200 */
+	CHIP_DEF(PCI_CHIP_RADEON_QD,	RADEON,	0),
+	CHIP_DEF(PCI_CHIP_RADEON_QE,	RADEON,	0),
+	CHIP_DEF(PCI_CHIP_RADEON_QF,	RADEON,	0),
+	CHIP_DEF(PCI_CHIP_RADEON_QG,	RADEON,	0),
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, radeonfb_pci_table);
+
+
+typedef struct {
+	u16 reg;
+	u32 val;
+} reg_val;
+
+
+/* these common regs are cleared before mode setting so they do not
+ * interfere with anything
+ */
+static reg_val common_regs[] = {
+	{ OVR_CLR, 0 },	
+	{ OVR_WID_LEFT_RIGHT, 0 },
+	{ OVR_WID_TOP_BOTTOM, 0 },
+	{ OV0_SCALE_CNTL, 0 },
+	{ SUBPIC_CNTL, 0 },
+	{ VIPH_CONTROL, 0 },
+	{ I2C_CNTL_1, 0 },
+	{ GEN_INT_CNTL, 0 },
+	{ CAP0_TRIG_CNTL, 0 },
+	{ CAP1_TRIG_CNTL, 0 },
+};
+
+/*
+ * globals
+ */
+        
+static char *mode_option;
+static char *monitor_layout;
+static int noaccel = 0;
+static int default_dynclk = -2;
+static int nomodeset = 0;
+static int ignore_edid = 0;
+static int mirror = 0;
+static int panel_yres = 0;
+static int force_dfp = 0;
+static int force_measure_pll = 0;
+#ifdef CONFIG_MTRR
+static int nomtrr = 0;
+#endif
+
+/*
+ * prototypes
+ */
+
+
+#ifdef CONFIG_PPC_OF
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int radeon_set_backlight_enable(int on, int level, void *data);
+static int radeon_set_backlight_level(int level, void *data);
+static struct backlight_controller radeon_backlight_controller = {
+	radeon_set_backlight_enable,
+	radeon_set_backlight_level
+};
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+#endif /* CONFIG_PPC_OF */
+
+static void radeon_unmap_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev)
+{
+	if (!rinfo->bios_seg)
+		return;
+	pci_unmap_rom(dev, rinfo->bios_seg);
+}
+
+static int __devinit radeon_map_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev)
+{
+	void __iomem *rom;
+	u16 dptr;
+	u8 rom_type;
+	size_t rom_size;
+
+	/* If this is a primary card, there is a shadow copy of the
+	 * ROM somewhere in the first meg. We will just ignore the copy
+	 * and use the ROM directly.
+	 */
+    
+    	/* Fix from ATI for problem with Radeon hardware not leaving ROM enabled */
+    	unsigned int temp;
+	temp = INREG(MPP_TB_CONFIG);
+	temp &= 0x00ffffffu;
+	temp |= 0x04 << 24;
+	OUTREG(MPP_TB_CONFIG, temp);
+	temp = INREG(MPP_TB_CONFIG);
+                                                                                                          
+	rom = pci_map_rom(dev, &rom_size);
+	if (!rom) {
+		printk(KERN_ERR "radeonfb (%s): ROM failed to map\n",
+		       pci_name(rinfo->pdev));
+		return -ENOMEM;
+	}
+	
+	rinfo->bios_seg = rom;
+
+	/* Very simple test to make sure it appeared */
+	if (BIOS_IN16(0) != 0xaa55) {
+		printk(KERN_ERR "radeonfb (%s): Invalid ROM signature %x should be"
+		       "0xaa55\n", pci_name(rinfo->pdev), BIOS_IN16(0));
+		goto failed;
+	}
+	/* Look for the PCI data to check the ROM type */
+	dptr = BIOS_IN16(0x18);
+
+	/* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM
+	 * for now, until I've verified this works everywhere. The goal here is more
+	 * to phase out Open Firmware images.
+	 *
+	 * Currently, we only look at the first PCI data, we could iteratre and deal with
+	 * them all, and we should use fb_bios_start relative to start of image and not
+	 * relative start of ROM, but so far, I never found a dual-image ATI card
+	 *
+	 * typedef struct {
+	 * 	u32	signature;	+ 0x00
+	 * 	u16	vendor;		+ 0x04
+	 * 	u16	device;		+ 0x06
+	 * 	u16	reserved_1;	+ 0x08
+	 * 	u16	dlen;		+ 0x0a
+	 * 	u8	drevision;	+ 0x0c
+	 * 	u8	class_hi;	+ 0x0d
+	 * 	u16	class_lo;	+ 0x0e
+	 * 	u16	ilen;		+ 0x10
+	 * 	u16	irevision;	+ 0x12
+	 * 	u8	type;		+ 0x14
+	 * 	u8	indicator;	+ 0x15
+	 * 	u16	reserved_2;	+ 0x16
+	 * } pci_data_t;
+	 */
+	if (BIOS_IN32(dptr) !=  (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) {
+		printk(KERN_WARNING "radeonfb (%s): PCI DATA signature in ROM"
+		       "incorrect: %08x\n", pci_name(rinfo->pdev), BIOS_IN32(dptr));
+		goto anyway;
+	}
+	rom_type = BIOS_IN8(dptr + 0x14);
+	switch(rom_type) {
+	case 0:
+		printk(KERN_INFO "radeonfb: Found Intel x86 BIOS ROM Image\n");
+		break;
+	case 1:
+		printk(KERN_INFO "radeonfb: Found Open Firmware ROM Image\n");
+		goto failed;
+	case 2:
+		printk(KERN_INFO "radeonfb: Found HP PA-RISC ROM Image\n");
+		goto failed;
+	default:
+		printk(KERN_INFO "radeonfb: Found unknown type %d ROM Image\n", rom_type);
+		goto failed;
+	}
+ anyway:
+	/* Locate the flat panel infos, do some sanity checking !!! */
+	rinfo->fp_bios_start = BIOS_IN16(0x48);
+	return 0;
+
+ failed:
+	rinfo->bios_seg = NULL;
+	radeon_unmap_ROM(rinfo, dev);
+	return -ENXIO;
+}
+
+#ifdef CONFIG_X86
+static int  __devinit radeon_find_mem_vbios(struct radeonfb_info *rinfo)
+{
+	/* I simplified this code as we used to miss the signatures in
+	 * a lot of case. It's now closer to XFree, we just don't check
+	 * for signatures at all... Something better will have to be done
+	 * if we end up having conflicts
+	 */
+        u32  segstart;
+	void __iomem *rom_base = NULL;
+                                                
+        for(segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
+                rom_base = ioremap(segstart, 0x10000);
+		if (rom_base == NULL)
+			return -ENOMEM;
+                if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa)
+	                break;
+                iounmap(rom_base);
+		rom_base = NULL;
+        }
+	if (rom_base == NULL)
+		return -ENXIO;
+
+	/* Locate the flat panel infos, do some sanity checking !!! */
+	rinfo->bios_seg = rom_base;
+	rinfo->fp_bios_start = BIOS_IN16(0x48);
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PPC_OF
+/*
+ * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
+ * tree. Hopefully, ATI OF driver is kind enough to fill these
+ */
+static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo)
+{
+	struct device_node *dp = rinfo->of_node;
+	u32 *val;
+
+	if (dp == NULL)
+		return -ENODEV;
+	val = (u32 *) get_property(dp, "ATY,RefCLK", NULL);
+	if (!val || !*val) {
+		printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n");
+		return -EINVAL;
+	}
+
+	rinfo->pll.ref_clk = (*val) / 10;
+
+	val = (u32 *) get_property(dp, "ATY,SCLK", NULL);
+	if (val && *val)
+		rinfo->pll.sclk = (*val) / 10;
+
+	val = (u32 *) get_property(dp, "ATY,MCLK", NULL);
+	if (val && *val)
+		rinfo->pll.mclk = (*val) / 10;
+
+       	return 0;
+}
+#endif /* CONFIG_PPC_OF */
+
+/*
+ * Read PLL infos from chip registers
+ */
+static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo)
+{
+	unsigned char ppll_div_sel;
+	unsigned Ns, Nm, M;
+	unsigned sclk, mclk, tmp, ref_div;
+	int hTotal, vTotal, num, denom, m, n;
+	unsigned long long hz, vclk;
+	long xtal;
+	struct timeval start_tv, stop_tv;
+	long total_secs, total_usecs;
+	int i;
+
+	/* Ugh, we cut interrupts, bad bad bad, but we want some precision
+	 * here, so... --BenH
+	 */
+
+	/* Flush PCI buffers ? */
+	tmp = INREG(DEVICE_ID);
+
+	local_irq_disable();
+
+	for(i=0; i<1000000; i++)
+		if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0)
+			break;
+
+	do_gettimeofday(&start_tv);
+
+	for(i=0; i<1000000; i++)
+		if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) != 0)
+			break;
+
+	for(i=0; i<1000000; i++)
+		if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0)
+			break;
+	
+	do_gettimeofday(&stop_tv);
+	
+	local_irq_enable();
+
+	total_secs = stop_tv.tv_sec - start_tv.tv_sec;
+	if (total_secs > 10)
+		return -1;
+	total_usecs = stop_tv.tv_usec - start_tv.tv_usec;
+	total_usecs += total_secs * 1000000;
+	if (total_usecs < 0)
+		total_usecs = -total_usecs;
+	hz = 1000000/total_usecs;
+ 
+	hTotal = ((INREG(CRTC_H_TOTAL_DISP) & 0x1ff) + 1) * 8;
+	vTotal = ((INREG(CRTC_V_TOTAL_DISP) & 0x3ff) + 1);
+	vclk = (long long)hTotal * (long long)vTotal * hz;
+
+	switch((INPLL(PPLL_REF_DIV) & 0x30000) >> 16) {
+	case 0:
+	default:
+		num = 1;
+		denom = 1;
+		break;
+	case 1:
+		n = ((INPLL(M_SPLL_REF_FB_DIV) >> 16) & 0xff);
+		m = (INPLL(M_SPLL_REF_FB_DIV) & 0xff);
+		num = 2*n;
+		denom = 2*m;
+		break;
+	case 2:
+		n = ((INPLL(M_SPLL_REF_FB_DIV) >> 8) & 0xff);
+		m = (INPLL(M_SPLL_REF_FB_DIV) & 0xff);
+		num = 2*n;
+		denom = 2*m;
+        break;
+	}
+
+	ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3;
+	radeon_pll_errata_after_index(rinfo);
+
+	n = (INPLL(PPLL_DIV_0 + ppll_div_sel) & 0x7ff);
+	m = (INPLL(PPLL_REF_DIV) & 0x3ff);
+
+	num *= n;
+	denom *= m;
+
+	switch ((INPLL(PPLL_DIV_0 + ppll_div_sel) >> 16) & 0x7) {
+	case 1:
+		denom *= 2;
+		break;
+	case 2:
+		denom *= 4;
+		break;
+	case 3:
+		denom *= 8;
+		break;
+	case 4:
+		denom *= 3;
+		break;
+	case 6:
+		denom *= 6;   
+		break;
+	case 7:
+		denom *= 12;
+		break;
+	}
+
+	vclk *= denom;
+	do_div(vclk, 1000 * num);
+	xtal = vclk;
+
+	if ((xtal > 26900) && (xtal < 27100))
+		xtal = 2700;
+	else if ((xtal > 14200) && (xtal < 14400))
+		xtal = 1432;
+	else if ((xtal > 29400) && (xtal < 29600))
+		xtal = 2950;
+	else {
+		printk(KERN_WARNING "xtal calculation failed: %ld\n", xtal);
+		return -1;
+	}
+
+	tmp = INPLL(M_SPLL_REF_FB_DIV);
+	ref_div = INPLL(PPLL_REF_DIV) & 0x3ff;
+
+	Ns = (tmp & 0xff0000) >> 16;
+	Nm = (tmp & 0xff00) >> 8;
+	M = (tmp & 0xff);
+	sclk = round_div((2 * Ns * xtal), (2 * M));
+	mclk = round_div((2 * Nm * xtal), (2 * M));
+
+	/* we're done, hopefully these are sane values */
+	rinfo->pll.ref_clk = xtal;
+	rinfo->pll.ref_div = ref_div;
+	rinfo->pll.sclk = sclk;
+	rinfo->pll.mclk = mclk;
+
+	return 0;
+}
+
+/*
+ * Retreive PLL infos by different means (BIOS, Open Firmware, register probing...)
+ */
+static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
+{
+	/*
+	 * In the case nothing works, these are defaults; they are mostly
+	 * incomplete, however.  It does provide ppll_max and _min values
+	 * even for most other methods, however.
+	 */
+	switch (rinfo->chipset) {
+	case PCI_DEVICE_ID_ATI_RADEON_QW:
+	case PCI_DEVICE_ID_ATI_RADEON_QX:
+		rinfo->pll.ppll_max = 35000;
+		rinfo->pll.ppll_min = 12000;
+		rinfo->pll.mclk = 23000;
+		rinfo->pll.sclk = 23000;
+		rinfo->pll.ref_clk = 2700;
+		break;
+	case PCI_DEVICE_ID_ATI_RADEON_QL:
+	case PCI_DEVICE_ID_ATI_RADEON_QN:
+	case PCI_DEVICE_ID_ATI_RADEON_QO:
+	case PCI_DEVICE_ID_ATI_RADEON_Ql:
+	case PCI_DEVICE_ID_ATI_RADEON_BB:
+		rinfo->pll.ppll_max = 35000;
+		rinfo->pll.ppll_min = 12000;
+		rinfo->pll.mclk = 27500;
+		rinfo->pll.sclk = 27500;
+		rinfo->pll.ref_clk = 2700;
+		break;
+	case PCI_DEVICE_ID_ATI_RADEON_Id:
+	case PCI_DEVICE_ID_ATI_RADEON_Ie:
+	case PCI_DEVICE_ID_ATI_RADEON_If:
+	case PCI_DEVICE_ID_ATI_RADEON_Ig:
+		rinfo->pll.ppll_max = 35000;
+		rinfo->pll.ppll_min = 12000;
+		rinfo->pll.mclk = 25000;
+		rinfo->pll.sclk = 25000;
+		rinfo->pll.ref_clk = 2700;
+		break;
+	case PCI_DEVICE_ID_ATI_RADEON_ND:
+	case PCI_DEVICE_ID_ATI_RADEON_NE:
+	case PCI_DEVICE_ID_ATI_RADEON_NF:
+	case PCI_DEVICE_ID_ATI_RADEON_NG:
+		rinfo->pll.ppll_max = 40000;
+		rinfo->pll.ppll_min = 20000;
+		rinfo->pll.mclk = 27000;
+		rinfo->pll.sclk = 27000;
+		rinfo->pll.ref_clk = 2700;
+		break;
+	case PCI_DEVICE_ID_ATI_RADEON_QD:
+	case PCI_DEVICE_ID_ATI_RADEON_QE:
+	case PCI_DEVICE_ID_ATI_RADEON_QF:
+	case PCI_DEVICE_ID_ATI_RADEON_QG:
+	default:
+		rinfo->pll.ppll_max = 35000;
+		rinfo->pll.ppll_min = 12000;
+		rinfo->pll.mclk = 16600;
+		rinfo->pll.sclk = 16600;
+		rinfo->pll.ref_clk = 2700;
+		break;
+	}
+	rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
+
+
+#ifdef CONFIG_PPC_OF
+	/*
+	 * Retreive PLL infos from Open Firmware first
+	 */
+       	if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
+       		printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
+		goto found;
+	}
+#endif /* CONFIG_PPC_OF */
+
+	/*
+	 * Check out if we have an X86 which gave us some PLL informations
+	 * and if yes, retreive them
+	 */
+	if (!force_measure_pll && rinfo->bios_seg) {
+		u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+
+		rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
+		rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
+		rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
+		rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
+		rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
+		rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
+
+		printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
+		goto found;
+	}
+
+	/*
+	 * We didn't get PLL parameters from either OF or BIOS, we try to
+	 * probe them
+	 */
+	if (radeon_probe_pll_params(rinfo) == 0) {
+		printk(KERN_INFO "radeonfb: Retreived PLL infos from registers\n");
+		goto found;
+	}
+
+	/*
+	 * Fall back to already-set defaults...
+	 */
+       	printk(KERN_INFO "radeonfb: Used default PLL infos\n");
+
+found:
+	/*
+	 * Some methods fail to retreive SCLK and MCLK values, we apply default
+	 * settings in this case (200Mhz). If that really happne often, we could
+	 * fetch from registers instead...
+	 */
+	if (rinfo->pll.mclk == 0)
+		rinfo->pll.mclk = 20000;
+	if (rinfo->pll.sclk == 0)
+		rinfo->pll.sclk = 20000;
+
+	printk("radeonfb: Reference=%d.%02d MHz (RefDiv=%d) Memory=%d.%02d Mhz, System=%d.%02d MHz\n",
+	       rinfo->pll.ref_clk / 100, rinfo->pll.ref_clk % 100,
+	       rinfo->pll.ref_div,
+	       rinfo->pll.mclk / 100, rinfo->pll.mclk % 100,
+	       rinfo->pll.sclk / 100, rinfo->pll.sclk % 100);
+	printk("radeonfb: PLL min %d max %d\n", rinfo->pll.ppll_min, rinfo->pll.ppll_max);
+}
+
+static int radeonfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct radeonfb_info *rinfo = info->par;
+        struct fb_var_screeninfo v;
+        int nom, den;
+	unsigned int pitch;
+
+	if (radeon_match_mode(rinfo, &v, var))
+		return -EINVAL;
+
+        switch (v.bits_per_pixel) {
+		case 0 ... 8:
+			v.bits_per_pixel = 8;
+			break;
+		case 9 ... 16:
+			v.bits_per_pixel = 16;
+			break;
+		case 17 ... 24:
+#if 0 /* Doesn't seem to work */
+			v.bits_per_pixel = 24;
+			break;
+#endif			
+			return -EINVAL;
+		case 25 ... 32:
+			v.bits_per_pixel = 32;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (var_to_depth(&v)) {
+                case 8:
+                        nom = den = 1;
+                        v.red.offset = v.green.offset = v.blue.offset = 0;
+                        v.red.length = v.green.length = v.blue.length = 8;
+                        v.transp.offset = v.transp.length = 0;
+                        break;
+		case 15:
+			nom = 2;
+			den = 1;
+			v.red.offset = 10;
+			v.green.offset = 5;
+			v.blue.offset = 0;
+			v.red.length = v.green.length = v.blue.length = 5;
+			v.transp.offset = v.transp.length = 0;
+			break;
+                case 16:
+                        nom = 2;
+                        den = 1;
+                        v.red.offset = 11;
+                        v.green.offset = 5;
+                        v.blue.offset = 0;
+                        v.red.length = 5;
+                        v.green.length = 6;
+                        v.blue.length = 5;
+                        v.transp.offset = v.transp.length = 0;
+                        break;                          
+                case 24:
+                        nom = 4;
+                        den = 1;
+                        v.red.offset = 16;
+                        v.green.offset = 8;
+                        v.blue.offset = 0;
+                        v.red.length = v.blue.length = v.green.length = 8;
+                        v.transp.offset = v.transp.length = 0;
+                        break;
+                case 32:
+                        nom = 4;
+                        den = 1;
+                        v.red.offset = 16;
+                        v.green.offset = 8;
+                        v.blue.offset = 0;
+                        v.red.length = v.blue.length = v.green.length = 8;
+                        v.transp.offset = 24;
+                        v.transp.length = 8;
+                        break;
+                default:
+                        printk ("radeonfb: mode %dx%dx%d rejected, color depth invalid\n",
+                                var->xres, var->yres, var->bits_per_pixel);
+                        return -EINVAL;
+        }
+
+	if (v.yres_virtual < v.yres)
+		v.yres_virtual = v.yres;
+	if (v.xres_virtual < v.xres)
+		v.xres_virtual = v.xres;
+                
+
+	/* XXX I'm adjusting xres_virtual to the pitch, that may help XFree
+	 * with some panels, though I don't quite like this solution
+	 */
+  	if (rinfo->info->flags & FBINFO_HWACCEL_DISABLED) {
+		v.xres_virtual = v.xres_virtual & ~7ul;
+	} else {
+		pitch = ((v.xres_virtual * ((v.bits_per_pixel + 1) / 8) + 0x3f)
+ 				& ~(0x3f)) >> 6;
+		v.xres_virtual = (pitch << 6) / ((v.bits_per_pixel + 1) / 8);
+	}
+
+	if (((v.xres_virtual * v.yres_virtual * nom) / den) > rinfo->mapped_vram)
+		return -EINVAL;
+
+	if (v.xres_virtual < v.xres)
+		v.xres = v.xres_virtual;
+
+	if (v.xoffset < 0)
+                v.xoffset = 0;
+        if (v.yoffset < 0)
+                v.yoffset = 0;
+         
+        if (v.xoffset > v.xres_virtual - v.xres)
+                v.xoffset = v.xres_virtual - v.xres - 1;
+                        
+        if (v.yoffset > v.yres_virtual - v.yres)
+                v.yoffset = v.yres_virtual - v.yres - 1;
+         
+        v.red.msb_right = v.green.msb_right = v.blue.msb_right =
+                          v.transp.offset = v.transp.length =
+                          v.transp.msb_right = 0;
+	
+        memcpy(var, &v, sizeof(v));
+
+        return 0;
+}
+
+
+static int radeonfb_pan_display (struct fb_var_screeninfo *var,
+                                 struct fb_info *info)
+{
+        struct radeonfb_info *rinfo = info->par;
+
+        if ((var->xoffset + var->xres > var->xres_virtual)
+	    || (var->yoffset + var->yres > var->yres_virtual))
+               return -EINVAL;
+                
+        if (rinfo->asleep)
+        	return 0;
+
+	radeon_fifo_wait(2);
+        OUTREG(CRTC_OFFSET, ((var->yoffset * var->xres_virtual + var->xoffset)
+			     * var->bits_per_pixel / 8) & ~7);
+        return 0;
+}
+
+
+static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
+                           unsigned long arg, struct fb_info *info)
+{
+        struct radeonfb_info *rinfo = info->par;
+	unsigned int tmp;
+	u32 value = 0;
+	int rc;
+
+	switch (cmd) {
+		/*
+		 * TODO:  set mirror accordingly for non-Mobility chipsets with 2 CRTC's
+		 *        and do something better using 2nd CRTC instead of just hackish
+		 *        routing to second output
+		 */
+		case FBIO_RADEON_SET_MIRROR:
+			if (!rinfo->is_mobility)
+				return -EINVAL;
+
+			rc = get_user(value, (__u32 __user *)arg);
+
+			if (rc)
+				return rc;
+
+			radeon_fifo_wait(2);
+			if (value & 0x01) {
+				tmp = INREG(LVDS_GEN_CNTL);
+
+				tmp |= (LVDS_ON | LVDS_BLON);
+			} else {
+				tmp = INREG(LVDS_GEN_CNTL);
+
+				tmp &= ~(LVDS_ON | LVDS_BLON);
+			}
+
+			OUTREG(LVDS_GEN_CNTL, tmp);
+
+			if (value & 0x02) {
+				tmp = INREG(CRTC_EXT_CNTL);
+				tmp |= CRTC_CRT_ON;
+
+				mirror = 1;
+			} else {
+				tmp = INREG(CRTC_EXT_CNTL);
+				tmp &= ~CRTC_CRT_ON;
+
+				mirror = 0;
+			}
+
+			OUTREG(CRTC_EXT_CNTL, tmp);
+
+			return 0;
+		case FBIO_RADEON_GET_MIRROR:
+			if (!rinfo->is_mobility)
+				return -EINVAL;
+
+			tmp = INREG(LVDS_GEN_CNTL);
+			if ((LVDS_ON | LVDS_BLON) & tmp)
+				value |= 0x01;
+
+			tmp = INREG(CRTC_EXT_CNTL);
+			if (CRTC_CRT_ON & tmp)
+				value |= 0x02;
+
+			return put_user(value, (__u32 __user *)arg);
+		default:
+			return -EINVAL;
+	}
+
+	return -EINVAL;
+}
+
+
+int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch)
+{
+        u32 val;
+	u32 tmp_pix_clks;
+	int unblank = 0;
+
+	if (rinfo->lock_blank)
+		return 0;
+
+	radeon_engine_idle();
+
+	val = INREG(CRTC_EXT_CNTL);
+        val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
+                 CRTC_VSYNC_DIS);
+        switch (blank) {
+	case FB_BLANK_VSYNC_SUSPEND:
+		val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS);
+		break;
+	case FB_BLANK_HSYNC_SUSPEND:
+		val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS);
+		break;
+	case FB_BLANK_POWERDOWN:
+		val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS |
+			CRTC_HSYNC_DIS);
+		break;
+	case FB_BLANK_NORMAL:
+		val |= CRTC_DISPLAY_DIS;
+		break;
+	case FB_BLANK_UNBLANK:
+	default:
+		unblank = 1;
+        }
+	OUTREG(CRTC_EXT_CNTL, val);
+
+
+	switch (rinfo->mon1_type) {
+	case MT_DFP:
+		if (unblank)
+			OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN),
+				~(FP_FPON | FP_TMDS_EN));
+		else {
+			if (mode_switch || blank == FB_BLANK_NORMAL)
+				break;
+			OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN));
+		}
+		break;
+	case MT_LCD:
+		del_timer_sync(&rinfo->lvds_timer);
+		val = INREG(LVDS_GEN_CNTL);
+		if (unblank) {
+			u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON
+				| LVDS_EN | (rinfo->init_state.lvds_gen_cntl
+					     & (LVDS_DIGON | LVDS_BL_MOD_EN));
+			if ((val ^ target_val) == LVDS_DISPLAY_DIS)
+				OUTREG(LVDS_GEN_CNTL, target_val);
+			else if ((val ^ target_val) != 0) {
+				OUTREG(LVDS_GEN_CNTL, target_val
+				       & ~(LVDS_ON | LVDS_BL_MOD_EN));
+				rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+				rinfo->init_state.lvds_gen_cntl |=
+					target_val & LVDS_STATE_MASK;
+				if (mode_switch) {
+					radeon_msleep(rinfo->panel_info.pwr_delay);
+					OUTREG(LVDS_GEN_CNTL, target_val);
+				}
+				else {
+					rinfo->pending_lvds_gen_cntl = target_val;
+					mod_timer(&rinfo->lvds_timer,
+					   jiffies +
+					   msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+				}
+			}
+		} else {
+			val |= LVDS_DISPLAY_DIS;
+			OUTREG(LVDS_GEN_CNTL, val);
+
+			/* We don't do a full switch-off on a simple mode switch */
+			if (mode_switch || blank == FB_BLANK_NORMAL)
+				break;
+
+			/* Asic bug, when turning off LVDS_ON, we have to make sure
+			 * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
+			 */
+			tmp_pix_clks = INPLL(PIXCLKS_CNTL);
+			if (rinfo->is_mobility || rinfo->is_IGP)
+				OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
+			val &= ~(LVDS_BL_MOD_EN);
+			OUTREG(LVDS_GEN_CNTL, val);
+			udelay(100);
+			val &= ~(LVDS_ON | LVDS_EN);
+			OUTREG(LVDS_GEN_CNTL, val);
+			val &= ~LVDS_DIGON;
+			rinfo->pending_lvds_gen_cntl = val;
+			mod_timer(&rinfo->lvds_timer,
+				  jiffies +
+				  msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+			rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+			rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK;
+			if (rinfo->is_mobility || rinfo->is_IGP)
+				OUTPLL(PIXCLKS_CNTL, tmp_pix_clks);
+		}
+		break;
+	case MT_CRT:
+		// todo: powerdown DAC
+	default:
+		break;
+	}
+
+	/* let fbcon do a soft blank for us */
+	return (blank == FB_BLANK_NORMAL) ? -EINVAL : 0;
+}
+
+static int radeonfb_blank (int blank, struct fb_info *info)
+{
+        struct radeonfb_info *rinfo = info->par;
+
+	if (rinfo->asleep)
+		return 0;
+		
+	return radeon_screen_blank(rinfo, blank, 0);
+}
+
+static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+                             unsigned blue, unsigned transp, struct fb_info *info)
+{
+        struct radeonfb_info *rinfo = info->par;
+	u32 pindex;
+	unsigned int i;
+	
+	if (regno > 255)
+		return 1;
+
+	red >>= 8;
+	green >>= 8;
+	blue >>= 8;
+	rinfo->palette[regno].red = red;
+	rinfo->palette[regno].green = green;
+	rinfo->palette[regno].blue = blue;
+
+        /* default */
+        pindex = regno;
+
+        if (!rinfo->asleep) {
+        	u32 dac_cntl2, vclk_cntl = 0;
+        	
+		radeon_fifo_wait(9);
+		if (rinfo->is_mobility) {
+			vclk_cntl = INPLL(VCLK_ECP_CNTL);
+			OUTPLL(VCLK_ECP_CNTL, vclk_cntl & ~PIXCLK_DAC_ALWAYS_ONb);
+		}
+
+		/* Make sure we are on first palette */
+		if (rinfo->has_CRTC2) {
+			dac_cntl2 = INREG(DAC_CNTL2);
+			dac_cntl2 &= ~DAC2_PALETTE_ACCESS_CNTL;
+			OUTREG(DAC_CNTL2, dac_cntl2);
+		}
+
+		if (rinfo->bpp == 16) {
+			pindex = regno * 8;
+
+			if (rinfo->depth == 16 && regno > 63)
+				return 1;
+			if (rinfo->depth == 15 && regno > 31)
+				return 1;
+
+			/* For 565, the green component is mixed one order below */
+			if (rinfo->depth == 16) {
+		                OUTREG(PALETTE_INDEX, pindex>>1);
+	       	         	OUTREG(PALETTE_DATA, (rinfo->palette[regno>>1].red << 16) |
+	                        	(green << 8) | (rinfo->palette[regno>>1].blue));
+	                	green = rinfo->palette[regno<<1].green;
+	        	}
+		}
+
+		if (rinfo->depth != 16 || regno < 32) {
+			OUTREG(PALETTE_INDEX, pindex);
+			OUTREG(PALETTE_DATA, (red << 16) | (green << 8) | blue);
+		}
+		if (rinfo->is_mobility)
+			OUTPLL(VCLK_ECP_CNTL, vclk_cntl);
+	}
+ 	if (regno < 16) {
+		u32 *pal = info->pseudo_palette;
+        	switch (rinfo->depth) {
+		case 15:
+			pal[regno] = (regno << 10) | (regno << 5) | regno;
+			break;
+		case 16:
+			pal[regno] = (regno << 11) | (regno << 5) | regno;
+			break;
+		case 24:
+			pal[regno] = (regno << 16) | (regno << 8) | regno;
+			break;
+		case 32:
+			i = (regno << 8) | regno;
+			pal[regno] = (i << 16) | i;
+			break;
+		}
+        }
+	return 0;
+}
+
+
+static void radeon_save_state (struct radeonfb_info *rinfo,
+			       struct radeon_regs *save)
+{
+	/* CRTC regs */
+	save->crtc_gen_cntl = INREG(CRTC_GEN_CNTL);
+	save->crtc_ext_cntl = INREG(CRTC_EXT_CNTL);
+	save->crtc_more_cntl = INREG(CRTC_MORE_CNTL);
+	save->dac_cntl = INREG(DAC_CNTL);
+        save->crtc_h_total_disp = INREG(CRTC_H_TOTAL_DISP);
+        save->crtc_h_sync_strt_wid = INREG(CRTC_H_SYNC_STRT_WID);
+        save->crtc_v_total_disp = INREG(CRTC_V_TOTAL_DISP);
+        save->crtc_v_sync_strt_wid = INREG(CRTC_V_SYNC_STRT_WID);
+	save->crtc_pitch = INREG(CRTC_PITCH);
+	save->surface_cntl = INREG(SURFACE_CNTL);
+
+	/* FP regs */
+	save->fp_crtc_h_total_disp = INREG(FP_CRTC_H_TOTAL_DISP);
+	save->fp_crtc_v_total_disp = INREG(FP_CRTC_V_TOTAL_DISP);
+	save->fp_gen_cntl = INREG(FP_GEN_CNTL);
+	save->fp_h_sync_strt_wid = INREG(FP_H_SYNC_STRT_WID);
+	save->fp_horz_stretch = INREG(FP_HORZ_STRETCH);
+	save->fp_v_sync_strt_wid = INREG(FP_V_SYNC_STRT_WID);
+	save->fp_vert_stretch = INREG(FP_VERT_STRETCH);
+	save->lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
+	save->lvds_pll_cntl = INREG(LVDS_PLL_CNTL);
+	save->tmds_crc = INREG(TMDS_CRC);
+	save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL);
+	save->vclk_ecp_cntl = INPLL(VCLK_ECP_CNTL);
+
+	/* PLL regs */
+	save->clk_cntl_index = INREG(CLOCK_CNTL_INDEX) & ~0x3f;
+	radeon_pll_errata_after_index(rinfo);
+	save->ppll_div_3 = INPLL(PPLL_DIV_3);
+	save->ppll_ref_div = INPLL(PPLL_REF_DIV);
+}
+
+
+static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *mode)
+{
+	int i;
+
+	radeon_fifo_wait(20);
+
+	/* Workaround from XFree */
+	if (rinfo->is_mobility) {
+	        /* A temporal workaround for the occational blanking on certain laptop
+		 * panels. This appears to related to the PLL divider registers
+		 * (fail to lock?). It occurs even when all dividers are the same
+		 * with their old settings. In this case we really don't need to
+		 * fiddle with PLL registers. By doing this we can avoid the blanking
+		 * problem with some panels.
+	         */
+		if ((mode->ppll_ref_div == (INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK)) &&
+		    (mode->ppll_div_3 == (INPLL(PPLL_DIV_3) &
+					  (PPLL_POST3_DIV_MASK | PPLL_FB3_DIV_MASK)))) {
+			/* We still have to force a switch to selected PPLL div thanks to
+			 * an XFree86 driver bug which will switch it away in some cases
+			 * even when using UseFDev */
+			OUTREGP(CLOCK_CNTL_INDEX,
+				mode->clk_cntl_index & PPLL_DIV_SEL_MASK,
+				~PPLL_DIV_SEL_MASK);
+			radeon_pll_errata_after_index(rinfo);
+			radeon_pll_errata_after_data(rinfo);
+            		return;
+		}
+	}
+
+	/* Swich VCKL clock input to CPUCLK so it stays fed while PPLL updates*/
+	OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_CPUCLK, ~VCLK_SRC_SEL_MASK);
+
+	/* Reset PPLL & enable atomic update */
+	OUTPLLP(PPLL_CNTL,
+		PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN,
+		~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN));
+
+	/* Switch to selected PPLL divider */
+	OUTREGP(CLOCK_CNTL_INDEX,
+		mode->clk_cntl_index & PPLL_DIV_SEL_MASK,
+		~PPLL_DIV_SEL_MASK);
+	radeon_pll_errata_after_index(rinfo);
+	radeon_pll_errata_after_data(rinfo);
+
+	/* Set PPLL ref. div */
+	if (rinfo->family == CHIP_FAMILY_R300 ||
+	    rinfo->family == CHIP_FAMILY_RS300 ||
+	    rinfo->family == CHIP_FAMILY_R350 ||
+	    rinfo->family == CHIP_FAMILY_RV350) {
+		if (mode->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) {
+			/* When restoring console mode, use saved PPLL_REF_DIV
+			 * setting.
+			 */
+			OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, 0);
+		} else {
+			/* R300 uses ref_div_acc field as real ref divider */
+			OUTPLLP(PPLL_REF_DIV,
+				(mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), 
+				~R300_PPLL_REF_DIV_ACC_MASK);
+		}
+	} else
+		OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, ~PPLL_REF_DIV_MASK);
+
+	/* Set PPLL divider 3 & post divider*/
+	OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_FB3_DIV_MASK);
+	OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_POST3_DIV_MASK);
+
+	/* Write update */
+	while (INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R)
+		;
+	OUTPLLP(PPLL_REF_DIV, PPLL_ATOMIC_UPDATE_W, ~PPLL_ATOMIC_UPDATE_W);
+
+	/* Wait read update complete */
+	/* FIXME: Certain revisions of R300 can't recover here.  Not sure of
+	   the cause yet, but this workaround will mask the problem for now.
+	   Other chips usually will pass at the very first test, so the
+	   workaround shouldn't have any effect on them. */
+	for (i = 0; (i < 10000 && INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); i++)
+		;
+	
+	OUTPLL(HTOTAL_CNTL, 0);
+
+	/* Clear reset & atomic update */
+	OUTPLLP(PPLL_CNTL, 0,
+		~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN));
+
+	/* We may want some locking ... oh well */
+       	radeon_msleep(5);
+
+	/* Switch back VCLK source to PPLL */
+	OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK);
+}
+
+/*
+ * Timer function for delayed LVDS panel power up/down
+ */
+static void radeon_lvds_timer_func(unsigned long data)
+{
+	struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
+
+	radeon_engine_idle();
+
+	OUTREG(LVDS_GEN_CNTL, rinfo->pending_lvds_gen_cntl);
+}
+
+/*
+ * Apply a video mode. This will apply the whole register set, including
+ * the PLL registers, to the card
+ */
+void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
+			int regs_only)
+{
+	int i;
+	int primary_mon = PRIMARY_MONITOR(rinfo);
+
+	if (nomodeset)
+		return;
+
+	if (!regs_only)
+		radeon_screen_blank(rinfo, FB_BLANK_NORMAL, 0);
+
+	radeon_fifo_wait(31);
+	for (i=0; i<10; i++)
+		OUTREG(common_regs[i].reg, common_regs[i].val);
+
+	/* Apply surface registers */
+	for (i=0; i<8; i++) {
+		OUTREG(SURFACE0_LOWER_BOUND + 0x10*i, mode->surf_lower_bound[i]);
+		OUTREG(SURFACE0_UPPER_BOUND + 0x10*i, mode->surf_upper_bound[i]);
+		OUTREG(SURFACE0_INFO + 0x10*i, mode->surf_info[i]);
+	}
+
+	OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl);
+	OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl,
+		~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS));
+	OUTREG(CRTC_MORE_CNTL, mode->crtc_more_cntl);
+	OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING);
+	OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp);
+	OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid);
+	OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp);
+	OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid);
+	OUTREG(CRTC_OFFSET, 0);
+	OUTREG(CRTC_OFFSET_CNTL, 0);
+	OUTREG(CRTC_PITCH, mode->crtc_pitch);
+	OUTREG(SURFACE_CNTL, mode->surface_cntl);
+
+	radeon_write_pll_regs(rinfo, mode);
+
+	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
+		radeon_fifo_wait(10);
+		OUTREG(FP_CRTC_H_TOTAL_DISP, mode->fp_crtc_h_total_disp);
+		OUTREG(FP_CRTC_V_TOTAL_DISP, mode->fp_crtc_v_total_disp);
+		OUTREG(FP_H_SYNC_STRT_WID, mode->fp_h_sync_strt_wid);
+		OUTREG(FP_V_SYNC_STRT_WID, mode->fp_v_sync_strt_wid);
+		OUTREG(FP_HORZ_STRETCH, mode->fp_horz_stretch);
+		OUTREG(FP_VERT_STRETCH, mode->fp_vert_stretch);
+		OUTREG(FP_GEN_CNTL, mode->fp_gen_cntl);
+		OUTREG(TMDS_CRC, mode->tmds_crc);
+		OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl);
+	}
+
+	if (!regs_only)
+		radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 0);
+
+	radeon_fifo_wait(2);
+	OUTPLL(VCLK_ECP_CNTL, mode->vclk_ecp_cntl);
+	
+	return;
+}
+
+/*
+ * Calculate the PLL values for a given mode
+ */
+static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *regs,
+				 unsigned long freq)
+{
+	const struct {
+		int divider;
+		int bitvalue;
+	} *post_div,
+	  post_divs[] = {
+		{ 1,  0 },
+		{ 2,  1 },
+		{ 4,  2 },
+		{ 8,  3 },
+		{ 3,  4 },
+		{ 16, 5 },
+		{ 6,  6 },
+		{ 12, 7 },
+		{ 0,  0 },
+	};
+	int fb_div, pll_output_freq = 0;
+	int uses_dvo = 0;
+
+	/* Check if the DVO port is enabled and sourced from the primary CRTC. I'm
+	 * not sure which model starts having FP2_GEN_CNTL, I assume anything more
+	 * recent than an r(v)100...
+	 */
+#if 1
+	/* XXX I had reports of flicker happening with the cinema display
+	 * on TMDS1 that seem to be fixed if I also forbit odd dividers in
+	 * this case. This could just be a bandwidth calculation issue, I
+	 * haven't implemented the bandwidth code yet, but in the meantime,
+	 * forcing uses_dvo to 1 fixes it and shouln't have bad side effects,
+	 * I haven't seen a case were were absolutely needed an odd PLL
+	 * divider. I'll find a better fix once I have more infos on the
+	 * real cause of the problem.
+	 */
+	while (rinfo->has_CRTC2) {
+		u32 fp2_gen_cntl = INREG(FP2_GEN_CNTL);
+		u32 disp_output_cntl;
+		int source;
+
+		/* FP2 path not enabled */
+		if ((fp2_gen_cntl & FP2_ON) == 0)
+			break;
+		/* Not all chip revs have the same format for this register,
+		 * extract the source selection
+		 */
+		if (rinfo->family == CHIP_FAMILY_R200 ||
+		    rinfo->family == CHIP_FAMILY_R300 ||
+		    rinfo->family == CHIP_FAMILY_R350 ||
+		    rinfo->family == CHIP_FAMILY_RV350) {
+			source = (fp2_gen_cntl >> 10) & 0x3;
+			/* sourced from transform unit, check for transform unit
+			 * own source
+			 */
+			if (source == 3) {
+				disp_output_cntl = INREG(DISP_OUTPUT_CNTL);
+				source = (disp_output_cntl >> 12) & 0x3;
+			}
+		} else
+			source = (fp2_gen_cntl >> 13) & 0x1;
+		/* sourced from CRTC2 -> exit */
+		if (source == 1)
+			break;
+
+		/* so we end up on CRTC1, let's set uses_dvo to 1 now */
+		uses_dvo = 1;
+		break;
+	}
+#else
+	uses_dvo = 1;
+#endif
+	if (freq > rinfo->pll.ppll_max)
+		freq = rinfo->pll.ppll_max;
+	if (freq*12 < rinfo->pll.ppll_min)
+		freq = rinfo->pll.ppll_min / 12;
+	RTRACE("freq = %lu, PLL min = %u, PLL max = %u\n",
+	       freq, rinfo->pll.ppll_min, rinfo->pll.ppll_max);
+
+	for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
+		pll_output_freq = post_div->divider * freq;
+		/* If we output to the DVO port (external TMDS), we don't allow an
+		 * odd PLL divider as those aren't supported on this path
+		 */
+		if (uses_dvo && (post_div->divider & 1))
+			continue;
+		if (pll_output_freq >= rinfo->pll.ppll_min  &&
+		    pll_output_freq <= rinfo->pll.ppll_max)
+			break;
+	}
+
+	/* If we fall through the bottom, try the "default value"
+	   given by the terminal post_div->bitvalue */
+	if ( !post_div->divider ) {
+		post_div = &post_divs[post_div->bitvalue];
+		pll_output_freq = post_div->divider * freq;
+	}
+	RTRACE("ref_div = %d, ref_clk = %d, output_freq = %d\n",
+	       rinfo->pll.ref_div, rinfo->pll.ref_clk,
+	       pll_output_freq);
+
+	/* If we fall through the bottom, try the "default value"
+	   given by the terminal post_div->bitvalue */
+	if ( !post_div->divider ) {
+		post_div = &post_divs[post_div->bitvalue];
+		pll_output_freq = post_div->divider * freq;
+	}
+	RTRACE("ref_div = %d, ref_clk = %d, output_freq = %d\n",
+	       rinfo->pll.ref_div, rinfo->pll.ref_clk,
+	       pll_output_freq);
+
+	fb_div = round_div(rinfo->pll.ref_div*pll_output_freq,
+				  rinfo->pll.ref_clk);
+	regs->ppll_ref_div = rinfo->pll.ref_div;
+	regs->ppll_div_3 = fb_div | (post_div->bitvalue << 16);
+
+	RTRACE("post div = 0x%x\n", post_div->bitvalue);
+	RTRACE("fb_div = 0x%x\n", fb_div);
+	RTRACE("ppll_div_3 = 0x%x\n", regs->ppll_div_3);
+}
+
+static int radeonfb_set_par(struct fb_info *info)
+{
+	struct radeonfb_info *rinfo = info->par;
+	struct fb_var_screeninfo *mode = &info->var;
+	struct radeon_regs *newmode;
+	int hTotal, vTotal, hSyncStart, hSyncEnd,
+	    hSyncPol, vSyncStart, vSyncEnd, vSyncPol, cSync;
+	u8 hsync_adj_tab[] = {0, 0x12, 9, 9, 6, 5};
+	u8 hsync_fudge_fp[] = {2, 2, 0, 0, 5, 5};
+	u32 sync, h_sync_pol, v_sync_pol, dotClock, pixClock;
+	int i, freq;
+	int format = 0;
+	int nopllcalc = 0;
+	int hsync_start, hsync_fudge, bytpp, hsync_wid, vsync_wid;
+	int primary_mon = PRIMARY_MONITOR(rinfo);
+	int depth = var_to_depth(mode);
+	int use_rmx = 0;
+
+	newmode = kmalloc(sizeof(struct radeon_regs), GFP_KERNEL);
+	if (!newmode)
+		return -ENOMEM;
+
+	/* We always want engine to be idle on a mode switch, even
+	 * if we won't actually change the mode
+	 */
+	radeon_engine_idle();
+
+	hSyncStart = mode->xres + mode->right_margin;
+	hSyncEnd = hSyncStart + mode->hsync_len;
+	hTotal = hSyncEnd + mode->left_margin;
+
+	vSyncStart = mode->yres + mode->lower_margin;
+	vSyncEnd = vSyncStart + mode->vsync_len;
+	vTotal = vSyncEnd + mode->upper_margin;
+	pixClock = mode->pixclock;
+
+	sync = mode->sync;
+	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+	if (primary_mon == MT_DFP || primary_mon == MT_LCD) {
+		if (rinfo->panel_info.xres < mode->xres)
+			mode->xres = rinfo->panel_info.xres;
+		if (rinfo->panel_info.yres < mode->yres)
+			mode->yres = rinfo->panel_info.yres;
+
+		hTotal = mode->xres + rinfo->panel_info.hblank;
+		hSyncStart = mode->xres + rinfo->panel_info.hOver_plus;
+		hSyncEnd = hSyncStart + rinfo->panel_info.hSync_width;
+
+		vTotal = mode->yres + rinfo->panel_info.vblank;
+		vSyncStart = mode->yres + rinfo->panel_info.vOver_plus;
+		vSyncEnd = vSyncStart + rinfo->panel_info.vSync_width;
+
+		h_sync_pol = !rinfo->panel_info.hAct_high;
+		v_sync_pol = !rinfo->panel_info.vAct_high;
+
+		pixClock = 100000000 / rinfo->panel_info.clock;
+
+		if (rinfo->panel_info.use_bios_dividers) {
+			nopllcalc = 1;
+			newmode->ppll_div_3 = rinfo->panel_info.fbk_divider |
+				(rinfo->panel_info.post_divider << 16);
+			newmode->ppll_ref_div = rinfo->panel_info.ref_divider;
+		}
+	}
+	dotClock = 1000000000 / pixClock;
+	freq = dotClock / 10; /* x100 */
+
+	RTRACE("hStart = %d, hEnd = %d, hTotal = %d\n",
+		hSyncStart, hSyncEnd, hTotal);
+	RTRACE("vStart = %d, vEnd = %d, vTotal = %d\n",
+		vSyncStart, vSyncEnd, vTotal);
+
+	hsync_wid = (hSyncEnd - hSyncStart) / 8;
+	vsync_wid = vSyncEnd - vSyncStart;
+	if (hsync_wid == 0)
+		hsync_wid = 1;
+	else if (hsync_wid > 0x3f)	/* max */
+		hsync_wid = 0x3f;
+
+	if (vsync_wid == 0)
+		vsync_wid = 1;
+	else if (vsync_wid > 0x1f)	/* max */
+		vsync_wid = 0x1f;
+
+	hSyncPol = mode->sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+	vSyncPol = mode->sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+	cSync = mode->sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
+
+	format = radeon_get_dstbpp(depth);
+	bytpp = mode->bits_per_pixel >> 3;
+
+	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD))
+		hsync_fudge = hsync_fudge_fp[format-1];
+	else
+		hsync_fudge = hsync_adj_tab[format-1];
+
+	hsync_start = hSyncStart - 8 + hsync_fudge;
+
+	newmode->crtc_gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN |
+				(format << 8);
+
+	/* Clear auto-center etc... */
+	newmode->crtc_more_cntl = rinfo->init_state.crtc_more_cntl;
+	newmode->crtc_more_cntl &= 0xfffffff0;
+	
+	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
+		newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN;
+		if (mirror)
+			newmode->crtc_ext_cntl |= CRTC_CRT_ON;
+
+		newmode->crtc_gen_cntl &= ~(CRTC_DBL_SCAN_EN |
+					   CRTC_INTERLACE_EN);
+	} else {
+		newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN |
+					CRTC_CRT_ON;
+	}
+
+	newmode->dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN |
+			   DAC_8BIT_EN;
+
+	newmode->crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) |
+				     (((mode->xres / 8) - 1) << 16));
+
+	newmode->crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
+					(hsync_wid << 16) | (h_sync_pol << 23));
+
+	newmode->crtc_v_total_disp = ((vTotal - 1) & 0xffff) |
+				    ((mode->yres - 1) << 16);
+
+	newmode->crtc_v_sync_strt_wid = (((vSyncStart - 1) & 0xfff) |
+					 (vsync_wid << 16) | (v_sync_pol  << 23));
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED)) {
+		/* We first calculate the engine pitch */
+		rinfo->pitch = ((mode->xres_virtual * ((mode->bits_per_pixel + 1) / 8) + 0x3f)
+ 				& ~(0x3f)) >> 6;
+
+		/* Then, re-multiply it to get the CRTC pitch */
+		newmode->crtc_pitch = (rinfo->pitch << 3) / ((mode->bits_per_pixel + 1) / 8);
+	} else
+		newmode->crtc_pitch = (mode->xres_virtual >> 3);
+
+	newmode->crtc_pitch |= (newmode->crtc_pitch << 16);
+
+	/*
+	 * It looks like recent chips have a problem with SURFACE_CNTL,
+	 * setting SURF_TRANSLATION_DIS completely disables the
+	 * swapper as well, so we leave it unset now.
+	 */
+	newmode->surface_cntl = 0;
+
+#if defined(__BIG_ENDIAN)
+
+	/* Setup swapping on both apertures, though we currently
+	 * only use aperture 0, enabling swapper on aperture 1
+	 * won't harm
+	 */
+	switch (mode->bits_per_pixel) {
+		case 16:
+			newmode->surface_cntl |= NONSURF_AP0_SWP_16BPP;
+			newmode->surface_cntl |= NONSURF_AP1_SWP_16BPP;
+			break;
+		case 24:	
+		case 32:
+			newmode->surface_cntl |= NONSURF_AP0_SWP_32BPP;
+			newmode->surface_cntl |= NONSURF_AP1_SWP_32BPP;
+			break;
+	}
+#endif
+
+	/* Clear surface registers */
+	for (i=0; i<8; i++) {
+		newmode->surf_lower_bound[i] = 0;
+		newmode->surf_upper_bound[i] = 0x1f;
+		newmode->surf_info[i] = 0;
+	}
+
+	RTRACE("h_total_disp = 0x%x\t   hsync_strt_wid = 0x%x\n",
+		newmode->crtc_h_total_disp, newmode->crtc_h_sync_strt_wid);
+	RTRACE("v_total_disp = 0x%x\t   vsync_strt_wid = 0x%x\n",
+		newmode->crtc_v_total_disp, newmode->crtc_v_sync_strt_wid);
+
+	rinfo->bpp = mode->bits_per_pixel;
+	rinfo->depth = depth;
+
+	RTRACE("pixclock = %lu\n", (unsigned long)pixClock);
+	RTRACE("freq = %lu\n", (unsigned long)freq);
+
+	/* We use PPLL_DIV_3 */
+	newmode->clk_cntl_index = 0x300;
+
+	/* Calculate PPLL value if necessary */
+	if (!nopllcalc)
+		radeon_calc_pll_regs(rinfo, newmode, freq);
+
+	newmode->vclk_ecp_cntl = rinfo->init_state.vclk_ecp_cntl;
+
+	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
+		unsigned int hRatio, vRatio;
+
+		if (mode->xres > rinfo->panel_info.xres)
+			mode->xres = rinfo->panel_info.xres;
+		if (mode->yres > rinfo->panel_info.yres)
+			mode->yres = rinfo->panel_info.yres;
+
+		newmode->fp_horz_stretch = (((rinfo->panel_info.xres / 8) - 1)
+					   << HORZ_PANEL_SHIFT);
+		newmode->fp_vert_stretch = ((rinfo->panel_info.yres - 1)
+					   << VERT_PANEL_SHIFT);
+
+		if (mode->xres != rinfo->panel_info.xres) {
+			hRatio = round_div(mode->xres * HORZ_STRETCH_RATIO_MAX,
+					   rinfo->panel_info.xres);
+			newmode->fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) |
+						   (newmode->fp_horz_stretch &
+						    (HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
+						     HORZ_AUTO_RATIO_INC)));
+			newmode->fp_horz_stretch |= (HORZ_STRETCH_BLEND |
+						    HORZ_STRETCH_ENABLE);
+			use_rmx = 1;
+		}
+		newmode->fp_horz_stretch &= ~HORZ_AUTO_RATIO;
+
+		if (mode->yres != rinfo->panel_info.yres) {
+			vRatio = round_div(mode->yres * VERT_STRETCH_RATIO_MAX,
+					   rinfo->panel_info.yres);
+			newmode->fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) |
+						   (newmode->fp_vert_stretch &
+						   (VERT_PANEL_SIZE | VERT_STRETCH_RESERVED)));
+			newmode->fp_vert_stretch |= (VERT_STRETCH_BLEND |
+						    VERT_STRETCH_ENABLE);
+			use_rmx = 1;
+		}
+		newmode->fp_vert_stretch &= ~VERT_AUTO_RATIO_EN;
+
+		newmode->fp_gen_cntl = (rinfo->init_state.fp_gen_cntl & (u32)
+				       ~(FP_SEL_CRTC2 |
+					 FP_RMX_HVSYNC_CONTROL_EN |
+					 FP_DFP_SYNC_SEL |
+					 FP_CRT_SYNC_SEL |
+					 FP_CRTC_LOCK_8DOT |
+					 FP_USE_SHADOW_EN |
+					 FP_CRTC_USE_SHADOW_VEND |
+					 FP_CRT_SYNC_ALT));
+
+		newmode->fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR |
+					FP_CRTC_DONT_SHADOW_HEND |
+					FP_PANEL_FORMAT);
+
+		if (IS_R300_VARIANT(rinfo) ||
+		    (rinfo->family == CHIP_FAMILY_R200)) {
+			newmode->fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK;
+			if (use_rmx)
+				newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX;
+			else
+				newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1;
+		} else
+			newmode->fp_gen_cntl |= FP_SEL_CRTC1;
+
+		newmode->lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl;
+		newmode->lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl;
+		newmode->tmds_crc = rinfo->init_state.tmds_crc;
+		newmode->tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl;
+
+		if (primary_mon == MT_LCD) {
+			newmode->lvds_gen_cntl |= (LVDS_ON | LVDS_BLON);
+			newmode->fp_gen_cntl &= ~(FP_FPON | FP_TMDS_EN);
+		} else {
+			/* DFP */
+			newmode->fp_gen_cntl |= (FP_FPON | FP_TMDS_EN);
+			newmode->tmds_transmitter_cntl &= ~(TMDS_PLLRST);
+			/* TMDS_PLL_EN bit is reversed on RV (and mobility) chips */
+			if (IS_R300_VARIANT(rinfo) ||
+			    (rinfo->family == CHIP_FAMILY_R200) || !rinfo->has_CRTC2)
+				newmode->tmds_transmitter_cntl &= ~TMDS_PLL_EN;
+			else
+				newmode->tmds_transmitter_cntl |= TMDS_PLL_EN;
+			newmode->crtc_ext_cntl &= ~CRTC_CRT_ON;
+		}
+
+		newmode->fp_crtc_h_total_disp = (((rinfo->panel_info.hblank / 8) & 0x3ff) |
+				(((mode->xres / 8) - 1) << 16));
+		newmode->fp_crtc_v_total_disp = (rinfo->panel_info.vblank & 0xffff) |
+				((mode->yres - 1) << 16);
+		newmode->fp_h_sync_strt_wid = ((rinfo->panel_info.hOver_plus & 0x1fff) |
+				(hsync_wid << 16) | (h_sync_pol << 23));
+		newmode->fp_v_sync_strt_wid = ((rinfo->panel_info.vOver_plus & 0xfff) |
+				(vsync_wid << 16) | (v_sync_pol  << 23));
+	}
+
+	/* do it! */
+	if (!rinfo->asleep) {
+		memcpy(&rinfo->state, newmode, sizeof(*newmode));
+		radeon_write_mode (rinfo, newmode, 0);
+		/* (re)initialize the engine */
+		if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+			radeonfb_engine_init (rinfo);
+	}
+	/* Update fix */
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+        	info->fix.line_length = rinfo->pitch*64;
+        else
+		info->fix.line_length = mode->xres_virtual
+			* ((mode->bits_per_pixel + 1) / 8);
+        info->fix.visual = rinfo->depth == 8 ? FB_VISUAL_PSEUDOCOLOR
+		: FB_VISUAL_DIRECTCOLOR;
+
+#ifdef CONFIG_BOOTX_TEXT
+	/* Update debug text engine */
+	btext_update_display(rinfo->fb_base_phys, mode->xres, mode->yres,
+			     rinfo->depth, info->fix.line_length);
+#endif
+
+	kfree(newmode);
+	return 0;
+}
+
+
+static struct fb_ops radeonfb_ops = {
+	.owner			= THIS_MODULE,
+	.fb_check_var		= radeonfb_check_var,
+	.fb_set_par		= radeonfb_set_par,
+	.fb_setcolreg		= radeonfb_setcolreg,
+	.fb_pan_display 	= radeonfb_pan_display,
+	.fb_blank		= radeonfb_blank,
+	.fb_ioctl		= radeonfb_ioctl,
+	.fb_sync		= radeonfb_sync,
+	.fb_fillrect		= radeonfb_fillrect,
+	.fb_copyarea		= radeonfb_copyarea,
+	.fb_imageblit		= radeonfb_imageblit,
+	.fb_cursor		= soft_cursor,
+};
+
+
+static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo)
+{
+	struct fb_info *info = rinfo->info;
+
+	info->par = rinfo;
+	info->pseudo_palette = rinfo->pseudo_palette;
+	info->flags = FBINFO_DEFAULT
+		    | FBINFO_HWACCEL_COPYAREA
+		    | FBINFO_HWACCEL_FILLRECT
+		    | FBINFO_HWACCEL_XPAN
+		    | FBINFO_HWACCEL_YPAN;
+	info->fbops = &radeonfb_ops;
+	info->screen_base = rinfo->fb_base;
+	info->screen_size = rinfo->mapped_vram;
+	/* Fill fix common fields */
+	strlcpy(info->fix.id, rinfo->name, sizeof(info->fix.id));
+        info->fix.smem_start = rinfo->fb_base_phys;
+        info->fix.smem_len = rinfo->video_ram;
+        info->fix.type = FB_TYPE_PACKED_PIXELS;
+        info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+        info->fix.xpanstep = 8;
+        info->fix.ypanstep = 1;
+        info->fix.ywrapstep = 0;
+        info->fix.type_aux = 0;
+        info->fix.mmio_start = rinfo->mmio_base_phys;
+        info->fix.mmio_len = RADEON_REGSIZE;
+	info->fix.accel = FB_ACCEL_ATI_RADEON;
+
+	fb_alloc_cmap(&info->cmap, 256, 0);
+
+	if (noaccel)
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+
+        return 0;
+}
+
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+
+/* TODO: Dbl check these tables, we don't go up to full ON backlight
+ * in these, possibly because we noticed MacOS doesn't, but I'd prefer
+ * having some more official numbers from ATI
+ */
+static int backlight_conv_m6[] = {
+	0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
+	0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
+};
+static int backlight_conv_m7[] = {
+	0x00, 0x3f, 0x4a, 0x55, 0x60, 0x6b, 0x76, 0x81,
+	0x8c, 0x97, 0xa2, 0xad, 0xb8, 0xc3, 0xce, 0xd9
+};
+
+#define BACKLIGHT_LVDS_OFF
+#undef BACKLIGHT_DAC_OFF
+
+/* We turn off the LCD completely instead of just dimming the backlight.
+ * This provides some greater power saving and the display is useless
+ * without backlight anyway.
+ */
+static int radeon_set_backlight_enable(int on, int level, void *data)
+{
+	struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
+	u32 lvds_gen_cntl, tmpPixclksCntl;
+	int* conv_table;
+
+	if (rinfo->mon1_type != MT_LCD)
+		return 0;
+
+	/* Pardon me for that hack... maybe some day we can figure
+	 * out in what direction backlight should work on a given
+	 * panel ?
+	 */
+	if ((rinfo->family == CHIP_FAMILY_RV200 ||
+	     rinfo->family == CHIP_FAMILY_RV250 ||
+	     rinfo->family == CHIP_FAMILY_RV280 ||
+	     rinfo->family == CHIP_FAMILY_RV350) &&
+	    !machine_is_compatible("PowerBook4,3") &&
+	    !machine_is_compatible("PowerBook6,3") &&
+	    !machine_is_compatible("PowerBook6,5"))
+		conv_table = backlight_conv_m7;
+	else
+		conv_table = backlight_conv_m6;
+
+	del_timer_sync(&rinfo->lvds_timer);
+	radeon_engine_idle();
+
+	lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
+	if (on && (level > BACKLIGHT_OFF)) {
+		lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
+		if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
+			lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
+			lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
+			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+			lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+			lvds_gen_cntl |= (conv_table[level] <<
+					  LVDS_BL_MOD_LEVEL_SHIFT);
+			lvds_gen_cntl |= LVDS_ON;
+			lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
+			rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
+			mod_timer(&rinfo->lvds_timer,
+				  jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+		} else {
+			lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+			lvds_gen_cntl |= (conv_table[level] <<
+					  LVDS_BL_MOD_LEVEL_SHIFT);
+			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+		}
+		rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+		rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
+			& LVDS_STATE_MASK;
+	} else {
+		/* Asic bug, when turning off LVDS_ON, we have to make sure
+		   RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
+		*/
+		tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
+		if (rinfo->is_mobility || rinfo->is_IGP)
+			OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
+		lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
+		lvds_gen_cntl |= (conv_table[0] <<
+				  LVDS_BL_MOD_LEVEL_SHIFT);
+		lvds_gen_cntl |= LVDS_DISPLAY_DIS;
+		OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+		udelay(100);
+		lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
+		OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+		lvds_gen_cntl &= ~(LVDS_DIGON);
+		rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
+		mod_timer(&rinfo->lvds_timer,
+			  jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+		if (rinfo->is_mobility || rinfo->is_IGP)
+			OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
+	}
+	rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+	rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
+
+	return 0;
+}
+
+
+static int radeon_set_backlight_level(int level, void *data)
+{
+	return radeon_set_backlight_enable(1, level, data);
+}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+
+/*
+ * This reconfigure the card's internal memory map. In theory, we'd like
+ * to setup the card's memory at the same address as it's PCI bus address,
+ * and the AGP aperture right after that so that system RAM on 32 bits
+ * machines at least, is directly accessible. However, doing so would
+ * conflict with the current XFree drivers...
+ * Ultimately, I hope XFree, GATOS and ATI binary drivers will all agree
+ * on the proper way to set this up and duplicate this here. In the meantime,
+ * I put the card's memory at 0 in card space and AGP at some random high
+ * local (0xe0000000 for now) that will be changed by XFree/DRI anyway
+ */
+#ifdef CONFIG_PPC_OF
+#undef SET_MC_FB_FROM_APERTURE
+static void fixup_memory_mappings(struct radeonfb_info *rinfo)
+{
+	u32 save_crtc_gen_cntl, save_crtc2_gen_cntl = 0;
+	u32 save_crtc_ext_cntl;
+	u32 aper_base, aper_size;
+	u32 agp_base;
+
+	/* First, we disable display to avoid interfering */
+	if (rinfo->has_CRTC2) {
+		save_crtc2_gen_cntl = INREG(CRTC2_GEN_CNTL);
+		OUTREG(CRTC2_GEN_CNTL, save_crtc2_gen_cntl | CRTC2_DISP_REQ_EN_B);
+	}
+	save_crtc_gen_cntl = INREG(CRTC_GEN_CNTL);
+	save_crtc_ext_cntl = INREG(CRTC_EXT_CNTL);
+	
+	OUTREG(CRTC_EXT_CNTL, save_crtc_ext_cntl | CRTC_DISPLAY_DIS);
+	OUTREG(CRTC_GEN_CNTL, save_crtc_gen_cntl | CRTC_DISP_REQ_EN_B);
+	mdelay(100);
+
+	aper_base = INREG(CONFIG_APER_0_BASE);
+	aper_size = INREG(CONFIG_APER_SIZE);
+
+#ifdef SET_MC_FB_FROM_APERTURE
+	/* Set framebuffer to be at the same address as set in PCI BAR */
+	OUTREG(MC_FB_LOCATION, 
+		((aper_base + aper_size - 1) & 0xffff0000) | (aper_base >> 16));
+	rinfo->fb_local_base = aper_base;
+#else
+	OUTREG(MC_FB_LOCATION, 0x7fff0000);
+	rinfo->fb_local_base = 0;
+#endif
+	agp_base = aper_base + aper_size;
+	if (agp_base & 0xf0000000)
+		agp_base = (aper_base | 0x0fffffff) + 1;
+
+	/* Set AGP to be just after the framebuffer on a 256Mb boundary. This
+	 * assumes the FB isn't mapped to 0xf0000000 or above, but this is
+	 * always the case on PPCs afaik.
+	 */
+#ifdef SET_MC_FB_FROM_APERTURE
+	OUTREG(MC_AGP_LOCATION, 0xffff0000 | (agp_base >> 16));
+#else
+	OUTREG(MC_AGP_LOCATION, 0xffffe000);
+#endif
+
+	/* Fixup the display base addresses & engine offsets while we
+	 * are at it as well
+	 */
+#ifdef SET_MC_FB_FROM_APERTURE
+	OUTREG(DISPLAY_BASE_ADDR, aper_base);
+	if (rinfo->has_CRTC2)
+		OUTREG(CRTC2_DISPLAY_BASE_ADDR, aper_base);
+	OUTREG(OV0_BASE_ADDR, aper_base);
+#else
+	OUTREG(DISPLAY_BASE_ADDR, 0);
+	if (rinfo->has_CRTC2)
+		OUTREG(CRTC2_DISPLAY_BASE_ADDR, 0);
+	OUTREG(OV0_BASE_ADDR, 0);
+#endif
+	mdelay(100);
+
+	/* Restore display settings */
+	OUTREG(CRTC_GEN_CNTL, save_crtc_gen_cntl);
+	OUTREG(CRTC_EXT_CNTL, save_crtc_ext_cntl);
+	if (rinfo->has_CRTC2)
+		OUTREG(CRTC2_GEN_CNTL, save_crtc2_gen_cntl);	
+
+	RTRACE("aper_base: %08x MC_FB_LOC to: %08x, MC_AGP_LOC to: %08x\n",
+		aper_base,
+		((aper_base + aper_size - 1) & 0xffff0000) | (aper_base >> 16),
+		0xffff0000 | (agp_base >> 16));
+}
+#endif /* CONFIG_PPC_OF */
+
+
+static void radeon_identify_vram(struct radeonfb_info *rinfo)
+{
+	u32 tmp;
+
+	/* framebuffer size */
+        if ((rinfo->family == CHIP_FAMILY_RS100) ||
+            (rinfo->family == CHIP_FAMILY_RS200) ||
+            (rinfo->family == CHIP_FAMILY_RS300)) {
+          u32 tom = INREG(NB_TOM);
+          tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024);
+
+ 		radeon_fifo_wait(6);
+          OUTREG(MC_FB_LOCATION, tom);
+          OUTREG(DISPLAY_BASE_ADDR, (tom & 0xffff) << 16);
+          OUTREG(CRTC2_DISPLAY_BASE_ADDR, (tom & 0xffff) << 16);
+          OUTREG(OV0_BASE_ADDR, (tom & 0xffff) << 16);
+
+          /* This is supposed to fix the crtc2 noise problem. */
+          OUTREG(GRPH2_BUFFER_CNTL, INREG(GRPH2_BUFFER_CNTL) & ~0x7f0000);
+
+          if ((rinfo->family == CHIP_FAMILY_RS100) ||
+              (rinfo->family == CHIP_FAMILY_RS200)) {
+             /* This is to workaround the asic bug for RMX, some versions
+                of BIOS dosen't have this register initialized correctly.
+             */
+             OUTREGP(CRTC_MORE_CNTL, CRTC_H_CUTOFF_ACTIVE_EN,
+                     ~CRTC_H_CUTOFF_ACTIVE_EN);
+          }
+        } else {
+          tmp = INREG(CONFIG_MEMSIZE);
+        }
+
+	/* mem size is bits [28:0], mask off the rest */
+	rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK;
+
+	/*
+	 * Hack to get around some busted production M6's
+	 * reporting no ram
+	 */
+	if (rinfo->video_ram == 0) {
+		switch (rinfo->pdev->device) {
+	       	case PCI_CHIP_RADEON_LY:
+		case PCI_CHIP_RADEON_LZ:
+	       		rinfo->video_ram = 8192 * 1024;
+	       		break;
+	       	default:
+	       		break;
+		}
+	}
+
+
+	/*
+	 * Now try to identify VRAM type
+	 */
+	if (rinfo->is_IGP || (rinfo->family >= CHIP_FAMILY_R300) ||
+	    (INREG(MEM_SDRAM_MODE_REG) & (1<<30)))
+		rinfo->vram_ddr = 1;
+	else
+		rinfo->vram_ddr = 0;
+
+	tmp = INREG(MEM_CNTL);
+	if (IS_R300_VARIANT(rinfo)) {
+		tmp &=  R300_MEM_NUM_CHANNELS_MASK;
+		switch (tmp) {
+		case 0:  rinfo->vram_width = 64; break;
+		case 1:  rinfo->vram_width = 128; break;
+		case 2:  rinfo->vram_width = 256; break;
+		default: rinfo->vram_width = 128; break;
+		}
+	} else if ((rinfo->family == CHIP_FAMILY_RV100) ||
+		   (rinfo->family == CHIP_FAMILY_RS100) ||
+		   (rinfo->family == CHIP_FAMILY_RS200)){
+		if (tmp & RV100_MEM_HALF_MODE)
+			rinfo->vram_width = 32;
+		else
+			rinfo->vram_width = 64;
+	} else {
+		if (tmp & MEM_NUM_CHANNELS_MASK)
+			rinfo->vram_width = 128;
+		else
+			rinfo->vram_width = 64;
+	}
+
+	/* This may not be correct, as some cards can have half of channel disabled
+	 * ToDo: identify these cases
+	 */
+
+	RTRACE("radeonfb (%s): Found %ldk of %s %d bits wide videoram\n",
+	       pci_name(rinfo->pdev),
+	       rinfo->video_ram / 1024,
+	       rinfo->vram_ddr ? "DDR" : "SDRAM",
+	       rinfo->vram_width);
+}
+
+/*
+ * Sysfs
+ */
+
+static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u8 *edid)
+{
+	if (off > EDID_LENGTH)
+		return 0;
+
+	if (off + count > EDID_LENGTH)
+		count = EDID_LENGTH - off;
+
+	memcpy(buf, edid + off, count);
+
+	return count;
+}
+
+
+static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pci_dev *pdev = to_pci_dev(dev);
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+
+	return radeon_show_one_edid(buf, off, count, rinfo->mon1_EDID);
+}
+
+
+static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pci_dev *pdev = to_pci_dev(dev);
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+
+	return radeon_show_one_edid(buf, off, count, rinfo->mon2_EDID);
+}
+
+static struct bin_attribute edid1_attr = {
+	.attr   = {
+		.name	= "edid1",
+		.owner	= THIS_MODULE,
+		.mode	= 0444,
+	},
+	.size	= EDID_LENGTH,
+	.read	= radeon_show_edid1,
+};
+
+static struct bin_attribute edid2_attr = {
+	.attr   = {
+		.name	= "edid2",
+		.owner	= THIS_MODULE,
+		.mode	= 0444,
+	},
+	.size	= EDID_LENGTH,
+	.read	= radeon_show_edid2,
+};
+
+
+static int radeonfb_pci_register (struct pci_dev *pdev,
+				  const struct pci_device_id *ent)
+{
+	struct fb_info *info;
+	struct radeonfb_info *rinfo;
+	int ret;
+
+	RTRACE("radeonfb_pci_register BEGIN\n");
+	
+	/* Enable device in PCI config */
+	ret = pci_enable_device(pdev);
+	if (ret < 0) {
+		printk(KERN_ERR "radeonfb (%s): Cannot enable PCI device\n",
+		       pci_name(pdev));
+		goto err_out;
+	}
+
+	info = framebuffer_alloc(sizeof(struct radeonfb_info), &pdev->dev);
+	if (!info) {
+		printk (KERN_ERR "radeonfb (%s): could not allocate memory\n",
+			pci_name(pdev));
+		ret = -ENOMEM;
+		goto err_disable;
+	}
+	rinfo = info->par;
+	rinfo->info = info;	
+	rinfo->pdev = pdev;
+	
+	spin_lock_init(&rinfo->reg_lock);
+	init_timer(&rinfo->lvds_timer);
+	rinfo->lvds_timer.function = radeon_lvds_timer_func;
+	rinfo->lvds_timer.data = (unsigned long)rinfo;
+
+	strcpy(rinfo->name, "ATI Radeon XX ");
+	rinfo->name[11] = ent->device >> 8;
+	rinfo->name[12] = ent->device & 0xFF;
+	rinfo->family = ent->driver_data & CHIP_FAMILY_MASK;
+	rinfo->chipset = pdev->device;
+	rinfo->has_CRTC2 = (ent->driver_data & CHIP_HAS_CRTC2) != 0;
+	rinfo->is_mobility = (ent->driver_data & CHIP_IS_MOBILITY) != 0;
+	rinfo->is_IGP = (ent->driver_data & CHIP_IS_IGP) != 0;
+
+	/* Set base addrs */
+	rinfo->fb_base_phys = pci_resource_start (pdev, 0);
+	rinfo->mmio_base_phys = pci_resource_start (pdev, 2);
+
+	/* request the mem regions */
+	ret = pci_request_regions(pdev, "radeonfb");
+	if (ret < 0) {
+		printk( KERN_ERR "radeonfb (%s): cannot reserve PCI regions."
+			"  Someone already got them?\n", pci_name(rinfo->pdev));
+		goto err_release_fb;
+	}
+
+	/* map the regions */
+	rinfo->mmio_base = ioremap(rinfo->mmio_base_phys, RADEON_REGSIZE);
+	if (!rinfo->mmio_base) {
+		printk(KERN_ERR "radeonfb (%s): cannot map MMIO\n", pci_name(rinfo->pdev));
+		ret = -EIO;
+		goto err_release_pci;
+	}
+
+	rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16;
+
+	/*
+	 * Check for errata
+	 */
+	rinfo->errata = 0;
+	if (rinfo->family == CHIP_FAMILY_R300 &&
+	    (INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK)
+	    == CFG_ATI_REV_A11)
+		rinfo->errata |= CHIP_ERRATA_R300_CG;
+
+	if (rinfo->family == CHIP_FAMILY_RV200 ||
+	    rinfo->family == CHIP_FAMILY_RS200)
+		rinfo->errata |= CHIP_ERRATA_PLL_DUMMYREADS;
+
+	if (rinfo->family == CHIP_FAMILY_RV100 ||
+	    rinfo->family == CHIP_FAMILY_RS100 ||
+	    rinfo->family == CHIP_FAMILY_RS200)
+		rinfo->errata |= CHIP_ERRATA_PLL_DELAY;
+
+#ifdef CONFIG_PPC_OF
+	/* On PPC, we obtain the OF device-node pointer to the firmware
+	 * data for this chip
+	 */
+	rinfo->of_node = pci_device_to_OF_node(pdev);
+	if (rinfo->of_node == NULL)
+		printk(KERN_WARNING "radeonfb (%s): Cannot match card to OF node !\n",
+		       pci_name(rinfo->pdev));
+
+	/* On PPC, the firmware sets up a memory mapping that tends
+	 * to cause lockups when enabling the engine. We reconfigure
+	 * the card internal memory mappings properly
+	 */
+	fixup_memory_mappings(rinfo);
+#endif /* CONFIG_PPC_OF */
+
+	/* Get VRAM size and type */
+	radeon_identify_vram(rinfo);
+
+	rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, rinfo->video_ram);
+
+	do {
+		rinfo->fb_base = ioremap (rinfo->fb_base_phys,
+					  rinfo->mapped_vram);
+	} while (   rinfo->fb_base == 0 &&
+		  ((rinfo->mapped_vram /=2) >= MIN_MAPPED_VRAM) );
+
+	if (rinfo->fb_base)
+		memset_io(rinfo->fb_base, 0, rinfo->mapped_vram);
+	else {
+		printk (KERN_ERR "radeonfb (%s): cannot map FB\n", pci_name(rinfo->pdev));
+		ret = -EIO;
+		goto err_unmap_rom;
+	}
+
+	RTRACE("radeonfb (%s): mapped %ldk videoram\n", pci_name(rinfo->pdev),
+	       rinfo->mapped_vram/1024);
+
+	/*
+	 * Map the BIOS ROM if any and retreive PLL parameters from
+	 * the BIOS. We skip that on mobility chips as the real panel
+	 * values we need aren't in the ROM but in the BIOS image in
+	 * memory. This is definitely not the best meacnism though,
+	 * we really need the arch code to tell us which is the "primary"
+	 * video adapter to use the memory image (or better, the arch
+	 * should provide us a copy of the BIOS image to shield us from
+	 * archs who would store that elsewhere and/or could initialize
+	 * more than one adapter during boot).
+	 */
+	if (!rinfo->is_mobility)
+		radeon_map_ROM(rinfo, pdev);
+
+	/*
+	 * On x86, the primary display on laptop may have it's BIOS
+	 * ROM elsewhere, try to locate it at the legacy memory hole.
+	 * We probably need to make sure this is the primary display,
+	 * but that is difficult without some arch support.
+	 */
+#ifdef CONFIG_X86
+	if (rinfo->bios_seg == NULL)
+		radeon_find_mem_vbios(rinfo);
+#endif
+
+	/* If both above failed, try the BIOS ROM again for mobility
+	 * chips
+	 */
+	if (rinfo->bios_seg == NULL && rinfo->is_mobility)
+		radeon_map_ROM(rinfo, pdev);
+
+	/* Get informations about the board's PLL */
+	radeon_get_pllinfo(rinfo);
+
+#ifdef CONFIG_FB_RADEON_I2C
+	/* Register I2C bus */
+	radeon_create_i2c_busses(rinfo);
+#endif
+
+	/* set all the vital stuff */
+	radeon_set_fbinfo (rinfo);
+
+	/* Probe screen types */
+	radeon_probe_screens(rinfo, monitor_layout, ignore_edid);
+
+	/* Build mode list, check out panel native model */
+	radeon_check_modes(rinfo, mode_option);
+
+	/* Register some sysfs stuff (should be done better) */
+	if (rinfo->mon1_EDID)
+		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
+	if (rinfo->mon2_EDID)
+		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
+
+	/* save current mode regs before we switch into the new one
+	 * so we can restore this upon __exit
+	 */
+	radeon_save_state (rinfo, &rinfo->init_state);
+	memcpy(&rinfo->state, &rinfo->init_state, sizeof(struct radeon_regs));
+
+	/* Setup Power Management capabilities */
+	if (default_dynclk < -1) {
+		/* -2 is special: means  ON on mobility chips and do not
+		 * change on others
+		 */
+		radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1);
+	} else
+		radeonfb_pm_init(rinfo, default_dynclk);
+
+	pci_set_drvdata(pdev, info);
+
+	/* Register with fbdev layer */
+	ret = register_framebuffer(info);
+	if (ret < 0) {
+		printk (KERN_ERR "radeonfb (%s): could not register framebuffer\n",
+			pci_name(rinfo->pdev));
+		goto err_unmap_fb;
+	}
+
+#ifdef CONFIG_MTRR
+	rinfo->mtrr_hdl = nomtrr ? -1 : mtrr_add(rinfo->fb_base_phys,
+						 rinfo->video_ram,
+						 MTRR_TYPE_WRCOMB, 1);
+#endif
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if (rinfo->mon1_type == MT_LCD) {
+		register_backlight_controller(&radeon_backlight_controller,
+					      rinfo, "ati");
+		register_backlight_controller(&radeon_backlight_controller,
+					      rinfo, "mnca");
+	}
+#endif
+
+	printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name);
+
+	if (rinfo->bios_seg)
+		radeon_unmap_ROM(rinfo, pdev);
+	RTRACE("radeonfb_pci_register END\n");
+
+	return 0;
+err_unmap_fb:
+	iounmap(rinfo->fb_base);
+err_unmap_rom:
+	kfree(rinfo->mon1_EDID);
+	kfree(rinfo->mon2_EDID);
+	if (rinfo->mon1_modedb)
+		fb_destroy_modedb(rinfo->mon1_modedb);
+	fb_dealloc_cmap(&info->cmap);
+#ifdef CONFIG_FB_RADEON_I2C
+	radeon_delete_i2c_busses(rinfo);
+#endif
+	if (rinfo->bios_seg)
+		radeon_unmap_ROM(rinfo, pdev);
+	iounmap(rinfo->mmio_base);
+err_release_pci:
+	pci_release_regions(pdev);
+err_release_fb:
+	framebuffer_release(info);
+err_disable:
+	pci_disable_device(pdev);
+err_out:
+	return ret;
+}
+
+
+
+static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
+{
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+ 
+        if (!rinfo)
+                return;
+ 
+	radeonfb_pm_exit(rinfo);
+
+#if 0
+	/* restore original state
+	 * 
+	 * Doesn't quite work yet, I suspect if we come from a legacy
+	 * VGA mode (or worse, text mode), we need to do some VGA black
+	 * magic here that I know nothing about. --BenH
+	 */
+        radeon_write_mode (rinfo, &rinfo->init_state, 1);
+ #endif
+
+	del_timer_sync(&rinfo->lvds_timer);
+
+#ifdef CONFIG_MTRR
+	if (rinfo->mtrr_hdl >= 0)
+		mtrr_del(rinfo->mtrr_hdl, 0, 0);
+#endif
+
+        unregister_framebuffer(info);
+
+        iounmap(rinfo->mmio_base);
+        iounmap(rinfo->fb_base);
+ 
+ 	pci_release_regions(pdev);
+
+	kfree(rinfo->mon1_EDID);
+	kfree(rinfo->mon2_EDID);
+	if (rinfo->mon1_modedb)
+		fb_destroy_modedb(rinfo->mon1_modedb);
+#ifdef CONFIG_FB_RADEON_I2C
+	radeon_delete_i2c_busses(rinfo);
+#endif        
+	fb_dealloc_cmap(&info->cmap);
+        framebuffer_release(info);
+	pci_disable_device(pdev);
+}
+
+
+static struct pci_driver radeonfb_driver = {
+	.name		= "radeonfb",
+	.id_table	= radeonfb_pci_table,
+	.probe		= radeonfb_pci_register,
+	.remove		= __devexit_p(radeonfb_pci_unregister),
+#ifdef CONFIG_PM
+	.suspend       	= radeonfb_pci_suspend,
+	.resume		= radeonfb_pci_resume,
+#endif /* CONFIG_PM */
+};
+
+#ifndef MODULE
+static int __init radeonfb_setup (char *options)
+{
+	char *this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	while ((this_opt = strsep (&options, ",")) != NULL) {
+		if (!*this_opt)
+			continue;
+
+		if (!strncmp(this_opt, "noaccel", 7)) {
+			noaccel = 1;
+		} else if (!strncmp(this_opt, "mirror", 6)) {
+			mirror = 1;
+		} else if (!strncmp(this_opt, "force_dfp", 9)) {
+			force_dfp = 1;
+		} else if (!strncmp(this_opt, "panel_yres:", 11)) {
+			panel_yres = simple_strtoul((this_opt+11), NULL, 0);
+#ifdef CONFIG_MTRR
+		} else if (!strncmp(this_opt, "nomtrr", 6)) {
+			nomtrr = 1;
+#endif
+		} else if (!strncmp(this_opt, "nomodeset", 9)) {
+			nomodeset = 1;
+		} else if (!strncmp(this_opt, "force_measure_pll", 17)) {
+			force_measure_pll = 1;
+		} else if (!strncmp(this_opt, "ignore_edid", 11)) {
+			ignore_edid = 1;
+		} else
+			mode_option = this_opt;
+	}
+	return 0;
+}
+#endif  /*  MODULE  */
+
+static int __init radeonfb_init (void)
+{
+#ifndef MODULE
+	char *option = NULL;
+
+	if (fb_get_options("radeonfb", &option))
+		return -ENODEV;
+	radeonfb_setup(option);
+#endif
+	return pci_register_driver (&radeonfb_driver);
+}
+
+
+static void __exit radeonfb_exit (void)
+{
+	pci_unregister_driver (&radeonfb_driver);
+}
+
+module_init(radeonfb_init);
+module_exit(radeonfb_exit);
+
+MODULE_AUTHOR("Ani Joshi");
+MODULE_DESCRIPTION("framebuffer driver for ATI Radeon chipset");
+MODULE_LICENSE("GPL");
+module_param(noaccel, bool, 0);
+module_param(default_dynclk, int, 0);
+MODULE_PARM_DESC(default_dynclk, "int: -2=enable on mobility only,-1=do not change,0=off,1=on");
+MODULE_PARM_DESC(noaccel, "bool: disable acceleration");
+module_param(nomodeset, bool, 0);
+MODULE_PARM_DESC(nomodeset, "bool: disable actual setting of video mode");
+module_param(mirror, bool, 0);
+MODULE_PARM_DESC(mirror, "bool: mirror the display to both monitors");
+module_param(force_dfp, bool, 0);
+MODULE_PARM_DESC(force_dfp, "bool: force display to dfp");
+module_param(ignore_edid, bool, 0);
+MODULE_PARM_DESC(ignore_edid, "bool: Ignore EDID data when doing DDC probe");
+module_param(monitor_layout, charp, 0);
+MODULE_PARM_DESC(monitor_layout, "Specify monitor mapping (like XFree86)");
+module_param(force_measure_pll, bool, 0);
+MODULE_PARM_DESC(force_measure_pll, "Force measurement of PLL (debug)");
+#ifdef CONFIG_MTRR
+module_param(nomtrr, bool, 0);
+MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
+#endif
+module_param(panel_yres, int, 0);
+MODULE_PARM_DESC(panel_yres, "int: set panel yres");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
diff --git a/drivers/video/aty/radeon_i2c.c b/drivers/video/aty/radeon_i2c.c
new file mode 100644
index 0000000..7622441
--- /dev/null
+++ b/drivers/video/aty/radeon_i2c.c
@@ -0,0 +1,265 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/io.h>
+
+#include <video/radeon.h>
+#include "radeonfb.h"
+#include "../edid.h"
+
+#define RADEON_DDC 	0x50
+
+static void radeon_gpio_setscl(void* data, int state)
+{
+	struct radeon_i2c_chan 	*chan = data;
+	struct radeonfb_info	*rinfo = chan->rinfo;
+	u32			val;
+	
+	val = INREG(chan->ddc_reg) & ~(VGA_DDC_CLK_OUT_EN);
+	if (!state)
+		val |= VGA_DDC_CLK_OUT_EN;
+
+	OUTREG(chan->ddc_reg, val);
+	(void)INREG(chan->ddc_reg);
+}
+
+static void radeon_gpio_setsda(void* data, int state)
+{
+	struct radeon_i2c_chan 	*chan = data;
+	struct radeonfb_info	*rinfo = chan->rinfo;
+	u32			val;
+	
+	val = INREG(chan->ddc_reg) & ~(VGA_DDC_DATA_OUT_EN);
+	if (!state)
+		val |= VGA_DDC_DATA_OUT_EN;
+
+	OUTREG(chan->ddc_reg, val);
+	(void)INREG(chan->ddc_reg);
+}
+
+static int radeon_gpio_getscl(void* data)
+{
+	struct radeon_i2c_chan 	*chan = data;
+	struct radeonfb_info	*rinfo = chan->rinfo;
+	u32			val;
+	
+	val = INREG(chan->ddc_reg);
+
+	return (val & VGA_DDC_CLK_INPUT) ? 1 : 0;
+}
+
+static int radeon_gpio_getsda(void* data)
+{
+	struct radeon_i2c_chan 	*chan = data;
+	struct radeonfb_info	*rinfo = chan->rinfo;
+	u32			val;
+	
+	val = INREG(chan->ddc_reg);
+
+	return (val & VGA_DDC_DATA_INPUT) ? 1 : 0;
+}
+
+static int radeon_setup_i2c_bus(struct radeon_i2c_chan *chan, const char *name)
+{
+	int rc;
+
+	strcpy(chan->adapter.name, name);
+	chan->adapter.owner		= THIS_MODULE;
+	chan->adapter.id		= I2C_ALGO_ATI;
+	chan->adapter.algo_data		= &chan->algo;
+	chan->adapter.dev.parent	= &chan->rinfo->pdev->dev;
+	chan->algo.setsda		= radeon_gpio_setsda;
+	chan->algo.setscl		= radeon_gpio_setscl;
+	chan->algo.getsda		= radeon_gpio_getsda;
+	chan->algo.getscl		= radeon_gpio_getscl;
+	chan->algo.udelay		= 40;
+	chan->algo.timeout		= 20;
+	chan->algo.data 		= chan;	
+	
+	i2c_set_adapdata(&chan->adapter, chan);
+	
+	/* Raise SCL and SDA */
+	radeon_gpio_setsda(chan, 1);
+	radeon_gpio_setscl(chan, 1);
+	udelay(20);
+
+	rc = i2c_bit_add_bus(&chan->adapter);
+	if (rc == 0)
+		dev_dbg(&chan->rinfo->pdev->dev, "I2C bus %s registered.\n", name);
+	else
+		dev_warn(&chan->rinfo->pdev->dev, "Failed to register I2C bus %s.\n", name);
+	return rc;
+}
+
+void radeon_create_i2c_busses(struct radeonfb_info *rinfo)
+{
+	rinfo->i2c[0].rinfo	= rinfo;
+	rinfo->i2c[0].ddc_reg	= GPIO_MONID;
+	radeon_setup_i2c_bus(&rinfo->i2c[0], "monid");
+
+	rinfo->i2c[1].rinfo	= rinfo;
+	rinfo->i2c[1].ddc_reg	= GPIO_DVI_DDC;
+	radeon_setup_i2c_bus(&rinfo->i2c[1], "dvi");
+
+	rinfo->i2c[2].rinfo	= rinfo;
+	rinfo->i2c[2].ddc_reg	= GPIO_VGA_DDC;
+	radeon_setup_i2c_bus(&rinfo->i2c[2], "vga");
+
+	rinfo->i2c[3].rinfo	= rinfo;
+	rinfo->i2c[3].ddc_reg	= GPIO_CRT2_DDC;
+	radeon_setup_i2c_bus(&rinfo->i2c[3], "crt2");
+}
+
+void radeon_delete_i2c_busses(struct radeonfb_info *rinfo)
+{
+	if (rinfo->i2c[0].rinfo)
+		i2c_bit_del_bus(&rinfo->i2c[0].adapter);
+	rinfo->i2c[0].rinfo = NULL;
+
+	if (rinfo->i2c[1].rinfo)
+		i2c_bit_del_bus(&rinfo->i2c[1].adapter);
+	rinfo->i2c[1].rinfo = NULL;
+
+	if (rinfo->i2c[2].rinfo)
+		i2c_bit_del_bus(&rinfo->i2c[2].adapter);
+	rinfo->i2c[2].rinfo = NULL;
+
+	if (rinfo->i2c[3].rinfo)
+		i2c_bit_del_bus(&rinfo->i2c[3].adapter);
+	rinfo->i2c[3].rinfo = NULL;
+}
+
+
+static u8 *radeon_do_probe_i2c_edid(struct radeon_i2c_chan *chan)
+{
+	u8 start = 0x0;
+	struct i2c_msg msgs[] = {
+		{
+			.addr	= RADEON_DDC,
+			.len	= 1,
+			.buf	= &start,
+		}, {
+			.addr	= RADEON_DDC,
+			.flags	= I2C_M_RD,
+			.len	= EDID_LENGTH,
+		},
+	};
+	u8 *buf;
+
+	buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+	if (!buf) {
+		dev_warn(&chan->rinfo->pdev->dev, "Out of memory!\n");
+		return NULL;
+	}
+	msgs[1].buf = buf;
+
+	if (i2c_transfer(&chan->adapter, msgs, 2) == 2)
+		return buf;
+	dev_dbg(&chan->rinfo->pdev->dev, "Unable to read EDID block.\n");
+	kfree(buf);
+	return NULL;
+}
+
+
+int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 **out_edid)
+{
+	u32 reg = rinfo->i2c[conn-1].ddc_reg;
+	u8 *edid = NULL;
+	int i, j;
+
+	OUTREG(reg, INREG(reg) & 
+			~(VGA_DDC_DATA_OUTPUT | VGA_DDC_CLK_OUTPUT));
+
+	OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN));
+	(void)INREG(reg);
+
+	for (i = 0; i < 3; i++) {
+		/* For some old monitors we need the
+		 * following process to initialize/stop DDC
+		 */
+		OUTREG(reg, INREG(reg) & ~(VGA_DDC_DATA_OUT_EN));
+		(void)INREG(reg);
+		msleep(13);
+
+		OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN));
+		(void)INREG(reg);
+		for (j = 0; j < 5; j++) {
+			msleep(10);
+			if (INREG(reg) & VGA_DDC_CLK_INPUT)
+				break;
+		}
+		if (j == 5)
+			continue;
+
+		OUTREG(reg, INREG(reg) | VGA_DDC_DATA_OUT_EN);
+		(void)INREG(reg);
+		msleep(15);
+		OUTREG(reg, INREG(reg) | VGA_DDC_CLK_OUT_EN);
+		(void)INREG(reg);
+		msleep(15);
+		OUTREG(reg, INREG(reg) & ~(VGA_DDC_DATA_OUT_EN));
+		(void)INREG(reg);
+		msleep(15);
+
+		/* Do the real work */
+		edid = radeon_do_probe_i2c_edid(&rinfo->i2c[conn-1]);
+
+		OUTREG(reg, INREG(reg) | 
+				(VGA_DDC_DATA_OUT_EN | VGA_DDC_CLK_OUT_EN));
+		(void)INREG(reg);
+		msleep(15);
+		
+		OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN));
+		(void)INREG(reg);
+		for (j = 0; j < 10; j++) {
+			msleep(10);
+			if (INREG(reg) & VGA_DDC_CLK_INPUT)
+				break;
+		}
+
+		OUTREG(reg, INREG(reg) & ~(VGA_DDC_DATA_OUT_EN));
+		(void)INREG(reg);
+		msleep(15);
+		OUTREG(reg, INREG(reg) |
+				(VGA_DDC_DATA_OUT_EN | VGA_DDC_CLK_OUT_EN));
+		(void)INREG(reg);
+		if (edid)
+			break;
+	}
+	/* Release the DDC lines when done or the Apple Cinema HD display
+	 * will switch off
+	 */
+	OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN | VGA_DDC_DATA_OUT_EN));
+	(void)INREG(reg);
+
+	if (out_edid)
+		*out_edid = edid;
+	if (!edid) {
+		RTRACE("radeonfb: I2C (port %d) ... not found\n", conn);
+		return MT_NONE;
+	}
+	if (edid[0x14] & 0x80) {
+		/* Fix detection using BIOS tables */
+		if (rinfo->is_mobility /*&& conn == ddc_dvi*/ &&
+		    (INREG(LVDS_GEN_CNTL) & LVDS_ON)) {
+			RTRACE("radeonfb: I2C (port %d) ... found LVDS panel\n", conn);
+			return MT_LCD;
+		} else {
+			RTRACE("radeonfb: I2C (port %d) ... found TMDS panel\n", conn);
+			return MT_DFP;
+		}
+	}
+       	RTRACE("radeonfb: I2C (port %d) ... found CRT display\n", conn);
+	return MT_CRT;
+}
+
diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c
new file mode 100644
index 0000000..ea7c863
--- /dev/null
+++ b/drivers/video/aty/radeon_monitor.c
@@ -0,0 +1,1010 @@
+#include "radeonfb.h"
+#include "../edid.h"
+
+static struct fb_var_screeninfo radeonfb_default_var = {
+	.xres		= 640,
+	.yres		= 480,
+	.xres_virtual	= 640,
+	.yres_virtual	= 480,
+	.bits_per_pixel = 8,
+	.red		= { .length = 8 },
+	.green		= { .length = 8 },
+	.blue		= { .length = 8 },
+	.activate	= FB_ACTIVATE_NOW,
+	.height		= -1,
+	.width		= -1,
+	.pixclock	= 39721,
+	.left_margin	= 40,
+	.right_margin	= 24,
+	.upper_margin	= 32,
+	.lower_margin	= 11,
+	.hsync_len	= 96,
+	.vsync_len	= 2,
+	.vmode		= FB_VMODE_NONINTERLACED
+};
+
+static char *radeon_get_mon_name(int type)
+{
+	char *pret = NULL;
+
+	switch (type) {
+		case MT_NONE:
+			pret = "no";
+			break;
+		case MT_CRT:
+			pret = "CRT";
+			break;
+		case MT_DFP:
+			pret = "DFP";
+			break;
+		case MT_LCD:
+			pret = "LCD";
+			break;
+		case MT_CTV:
+			pret = "CTV";
+			break;
+		case MT_STV:
+			pret = "STV";
+			break;
+	}
+
+	return pret;
+}
+
+
+#ifdef CONFIG_PPC_OF
+/*
+ * Try to find monitor informations & EDID data out of the Open Firmware
+ * device-tree. This also contains some "hacks" to work around a few machine
+ * models with broken OF probing by hard-coding known EDIDs for some Mac
+ * laptops internal LVDS panel. (XXX: not done yet)
+ */
+static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_EDID,
+					       int hdno)
+{
+        static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID",
+				     "EDID1", "EDID2",  NULL };
+	u8 *pedid = NULL;
+	u8 *pmt = NULL;
+	u8 *tmp;
+        int i, mt = MT_NONE;  
+	
+	RTRACE("analyzing OF properties...\n");
+	pmt = (u8 *)get_property(dp, "display-type", NULL);
+	if (!pmt)
+		return MT_NONE;
+	RTRACE("display-type: %s\n", pmt);
+	/* OF says "LCD" for DFP as well, we discriminate from the caller of this
+	 * function
+	 */
+	if (!strcmp(pmt, "LCD") || !strcmp(pmt, "DFP"))
+		mt = MT_DFP;
+	else if (!strcmp(pmt, "CRT"))
+		mt = MT_CRT;
+	else {
+		if (strcmp(pmt, "NONE") != 0)
+			printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n",
+			       pmt);
+		return MT_NONE;
+	}
+
+	for (i = 0; propnames[i] != NULL; ++i) {
+		pedid = (u8 *)get_property(dp, propnames[i], NULL);
+		if (pedid != NULL)
+			break;
+	}
+	/* We didn't find the EDID in the leaf node, some cards will actually
+	 * put EDID1/EDID2 in the parent, look for these (typically M6 tipb).
+	 * single-head cards have hdno == -1 and skip this step
+	 */
+	if (pedid == NULL && dp->parent && (hdno != -1))
+		pedid = get_property(dp->parent, (hdno == 0) ? "EDID1" : "EDID2", NULL);
+	if (pedid == NULL && dp->parent && (hdno == 0))
+		pedid = get_property(dp->parent, "EDID", NULL);
+	if (pedid == NULL)
+		return mt;
+
+	tmp = (u8 *)kmalloc(EDID_LENGTH, GFP_KERNEL);
+	if (!tmp)
+		return mt;
+	memcpy(tmp, pedid, EDID_LENGTH);
+	*out_EDID = tmp;
+	return mt;
+}
+
+static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_no,
+					  u8 **out_EDID)
+{
+        struct device_node *dp;
+
+	RTRACE("radeon_probe_OF_head\n");
+
+        dp = rinfo->of_node;
+        while (dp == NULL)
+		return MT_NONE;
+
+	if (rinfo->has_CRTC2) {
+		char *pname;
+		int len, second = 0;
+
+		dp = dp->child;
+		do {
+			if (!dp)
+				return MT_NONE;
+			pname = (char *)get_property(dp, "name", NULL);
+			if (!pname)
+				return MT_NONE;
+			len = strlen(pname);
+			RTRACE("head: %s (letter: %c, head_no: %d)\n",
+			       pname, pname[len-1], head_no);
+			if (pname[len-1] == 'A' && head_no == 0) {
+				int mt = radeon_parse_montype_prop(dp, out_EDID, 0);
+				/* Maybe check for LVDS_GEN_CNTL here ? I need to check out
+				 * what OF does when booting with lid closed
+				 */
+				if (mt == MT_DFP && rinfo->is_mobility)
+					mt = MT_LCD;
+				return mt;
+			} else if (pname[len-1] == 'B' && head_no == 1)
+				return radeon_parse_montype_prop(dp, out_EDID, 1);
+			second = 1;
+			dp = dp->sibling;
+		} while(!second);
+	} else {
+		if (head_no > 0)
+			return MT_NONE;
+		return radeon_parse_montype_prop(dp, out_EDID, -1);
+	}
+        return MT_NONE;
+}
+#endif /* CONFIG_PPC_OF */
+
+
+static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
+{
+	unsigned long tmp, tmp0;
+	char stmp[30];
+	int i;
+
+	if (!rinfo->bios_seg)
+		return 0;
+
+	if (!(tmp = BIOS_IN16(rinfo->fp_bios_start + 0x40))) {
+		printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
+		rinfo->panel_info.pwr_delay = 200;
+		return 0;
+	}
+
+	for(i=0; i<24; i++)
+		stmp[i] = BIOS_IN8(tmp+i+1);
+	stmp[24] = 0;
+	printk("radeonfb: panel ID string: %s\n", stmp);
+	rinfo->panel_info.xres = BIOS_IN16(tmp + 25);
+	rinfo->panel_info.yres = BIOS_IN16(tmp + 27);
+	printk("radeonfb: detected LVDS panel size from BIOS: %dx%d\n",
+		rinfo->panel_info.xres, rinfo->panel_info.yres);
+
+	rinfo->panel_info.pwr_delay = BIOS_IN16(tmp + 44);
+	RTRACE("BIOS provided panel power delay: %d\n", rinfo->panel_info.pwr_delay);
+	if (rinfo->panel_info.pwr_delay > 2000 || rinfo->panel_info.pwr_delay <= 0)
+		rinfo->panel_info.pwr_delay = 2000;
+
+	/*
+	 * Some panels only work properly with some divider combinations
+	 */
+	rinfo->panel_info.ref_divider = BIOS_IN16(tmp + 46);
+	rinfo->panel_info.post_divider = BIOS_IN8(tmp + 48);
+	rinfo->panel_info.fbk_divider = BIOS_IN16(tmp + 49);
+	if (rinfo->panel_info.ref_divider != 0 &&
+	    rinfo->panel_info.fbk_divider > 3) {
+		rinfo->panel_info.use_bios_dividers = 1;
+		printk(KERN_INFO "radeondb: BIOS provided dividers will be used\n");
+		RTRACE("ref_divider = %x\n", rinfo->panel_info.ref_divider);
+		RTRACE("post_divider = %x\n", rinfo->panel_info.post_divider);
+		RTRACE("fbk_divider = %x\n", rinfo->panel_info.fbk_divider);
+	}
+	RTRACE("Scanning BIOS table ...\n");
+	for(i=0; i<32; i++) {
+		tmp0 = BIOS_IN16(tmp+64+i*2);
+		if (tmp0 == 0)
+			break;
+		RTRACE(" %d x %d\n", BIOS_IN16(tmp0), BIOS_IN16(tmp0+2));
+		if ((BIOS_IN16(tmp0) == rinfo->panel_info.xres) &&
+		    (BIOS_IN16(tmp0+2) == rinfo->panel_info.yres)) {
+			rinfo->panel_info.hblank = (BIOS_IN16(tmp0+17) - BIOS_IN16(tmp0+19)) * 8;
+			rinfo->panel_info.hOver_plus = ((BIOS_IN16(tmp0+21) -
+							 BIOS_IN16(tmp0+19) -1) * 8) & 0x7fff;
+			rinfo->panel_info.hSync_width = BIOS_IN8(tmp0+23) * 8;
+			rinfo->panel_info.vblank = BIOS_IN16(tmp0+24) - BIOS_IN16(tmp0+26);
+			rinfo->panel_info.vOver_plus = (BIOS_IN16(tmp0+28) & 0x7ff) - BIOS_IN16(tmp0+26);
+			rinfo->panel_info.vSync_width = (BIOS_IN16(tmp0+28) & 0xf800) >> 11;
+			rinfo->panel_info.clock = BIOS_IN16(tmp0+9);
+			/* Assume high active syncs for now until ATI tells me more... maybe we
+			 * can probe register values here ?
+			 */
+			rinfo->panel_info.hAct_high = 1;
+			rinfo->panel_info.vAct_high = 1;
+			/* Mark panel infos valid */
+			rinfo->panel_info.valid = 1;
+
+			RTRACE("Found panel in BIOS table:\n");
+			RTRACE("  hblank: %d\n", rinfo->panel_info.hblank);
+			RTRACE("  hOver_plus: %d\n", rinfo->panel_info.hOver_plus);
+			RTRACE("  hSync_width: %d\n", rinfo->panel_info.hSync_width);
+			RTRACE("  vblank: %d\n", rinfo->panel_info.vblank);
+			RTRACE("  vOver_plus: %d\n", rinfo->panel_info.vOver_plus);
+			RTRACE("  vSync_width: %d\n", rinfo->panel_info.vSync_width);
+			RTRACE("  clock: %d\n", rinfo->panel_info.clock);
+				
+			return 1;
+		}
+	}
+	RTRACE("Didn't find panel in BIOS table !\n");
+
+	return 0;
+}
+
+/* Try to extract the connector informations from the BIOS. This
+ * doesn't quite work yet, but it's output is still useful for
+ * debugging
+ */
+static void __devinit radeon_parse_connector_info(struct radeonfb_info *rinfo)
+{
+	int offset, chips, connectors, tmp, i, conn, type;
+
+	static char* __conn_type_table[16] = {
+		"NONE", "Proprietary", "CRT", "DVI-I", "DVI-D", "Unknown", "Unknown",
+		"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown",
+		"Unknown", "Unknown", "Unknown"
+	};
+
+	if (!rinfo->bios_seg)
+		return;
+
+	offset = BIOS_IN16(rinfo->fp_bios_start + 0x50);
+	if (offset == 0) {
+		printk(KERN_WARNING "radeonfb: No connector info table detected\n");
+		return;
+	}
+
+	/* Don't do much more at this point but displaying the data if
+	 * DEBUG is enabled
+	 */
+	chips = BIOS_IN8(offset++) >> 4;
+	RTRACE("%d chips in connector info\n", chips);
+	for (i = 0; i < chips; i++) {
+		tmp = BIOS_IN8(offset++);
+		connectors = tmp & 0x0f;
+		RTRACE(" - chip %d has %d connectors\n", tmp >> 4, connectors);
+		for (conn = 0; ; conn++) {
+			tmp = BIOS_IN16(offset);
+			if (tmp == 0)
+				break;
+			offset += 2;
+			type = (tmp >> 12) & 0x0f;
+			RTRACE("  * connector %d of type %d (%s) : %04x\n",
+			       conn, type, __conn_type_table[type], tmp);
+		}
+	}
+}
+
+
+/*
+ * Probe physical connection of a CRT. This code comes from XFree
+ * as well and currently is only implemented for the CRT DAC, the
+ * code for the TVDAC is commented out in XFree as "non working"
+ */
+static int __devinit radeon_crt_is_connected(struct radeonfb_info *rinfo, int is_crt_dac)
+{
+    int	          connected = 0;
+
+    /* the monitor either wasn't connected or it is a non-DDC CRT.
+     * try to probe it
+     */
+    if (is_crt_dac) {
+	unsigned long ulOrigVCLK_ECP_CNTL;
+	unsigned long ulOrigDAC_CNTL;
+	unsigned long ulOrigDAC_EXT_CNTL;
+	unsigned long ulOrigCRTC_EXT_CNTL;
+	unsigned long ulData;
+	unsigned long ulMask;
+
+	ulOrigVCLK_ECP_CNTL = INPLL(VCLK_ECP_CNTL);
+
+	ulData              = ulOrigVCLK_ECP_CNTL;
+	ulData             &= ~(PIXCLK_ALWAYS_ONb
+				| PIXCLK_DAC_ALWAYS_ONb);
+	ulMask              = ~(PIXCLK_ALWAYS_ONb
+				| PIXCLK_DAC_ALWAYS_ONb);
+	OUTPLLP(VCLK_ECP_CNTL, ulData, ulMask);
+
+	ulOrigCRTC_EXT_CNTL = INREG(CRTC_EXT_CNTL);
+	ulData              = ulOrigCRTC_EXT_CNTL;
+	ulData             |= CRTC_CRT_ON;
+	OUTREG(CRTC_EXT_CNTL, ulData);
+   
+	ulOrigDAC_EXT_CNTL = INREG(DAC_EXT_CNTL);
+	ulData             = ulOrigDAC_EXT_CNTL;
+	ulData            &= ~DAC_FORCE_DATA_MASK;
+	ulData            |=  (DAC_FORCE_BLANK_OFF_EN
+			       |DAC_FORCE_DATA_EN
+			       |DAC_FORCE_DATA_SEL_MASK);
+	if ((rinfo->family == CHIP_FAMILY_RV250) ||
+	    (rinfo->family == CHIP_FAMILY_RV280))
+	    ulData |= (0x01b6 << DAC_FORCE_DATA_SHIFT);
+	else
+	    ulData |= (0x01ac << DAC_FORCE_DATA_SHIFT);
+
+	OUTREG(DAC_EXT_CNTL, ulData);
+
+	ulOrigDAC_CNTL     = INREG(DAC_CNTL);
+	ulData             = ulOrigDAC_CNTL;
+	ulData            |= DAC_CMP_EN;
+	ulData            &= ~(DAC_RANGE_CNTL_MASK
+			       | DAC_PDWN);
+	ulData            |= 0x2;
+	OUTREG(DAC_CNTL, ulData);
+
+	mdelay(1);
+
+	ulData     = INREG(DAC_CNTL);
+	connected =  (DAC_CMP_OUTPUT & ulData) ? 1 : 0;
+  
+	ulData    = ulOrigVCLK_ECP_CNTL;
+	ulMask    = 0xFFFFFFFFL;
+	OUTPLLP(VCLK_ECP_CNTL, ulData, ulMask);
+
+	OUTREG(DAC_CNTL,      ulOrigDAC_CNTL     );
+	OUTREG(DAC_EXT_CNTL,  ulOrigDAC_EXT_CNTL );
+	OUTREG(CRTC_EXT_CNTL, ulOrigCRTC_EXT_CNTL);
+    }
+
+    return connected ? MT_CRT : MT_NONE;
+}
+
+/*
+ * Parse the "monitor_layout" string if any. This code is mostly
+ * copied from XFree's radeon driver
+ */
+static int __devinit radeon_parse_monitor_layout(struct radeonfb_info *rinfo,
+						 const char *monitor_layout)
+{
+	char s1[5], s2[5];
+	int i = 0, second = 0;
+	const char *s;
+
+	if (!monitor_layout)
+		return 0;
+
+	s = monitor_layout;
+	do {
+		switch(*s) {
+		case ',':
+			s1[i] = '\0';
+			i = 0;
+			second = 1;
+			break;
+		case ' ':
+		case '\0':
+			break;
+		default:
+			if (i > 4)
+				break;
+			if (second)
+				s2[i] = *s;
+			else
+				s1[i] = *s;
+			i++;
+		}
+	} while (*s++);
+	if (second)
+		s2[i] = 0;
+	else {
+		s1[i] = 0;
+		s2[0] = 0;
+	}
+	if (strcmp(s1, "CRT") == 0)
+		rinfo->mon1_type = MT_CRT;
+	else if (strcmp(s1, "TMDS") == 0)
+		rinfo->mon1_type = MT_DFP;
+	else if (strcmp(s1, "LVDS") == 0)
+		rinfo->mon1_type = MT_LCD;
+
+	if (strcmp(s2, "CRT") == 0)
+		rinfo->mon2_type = MT_CRT;
+	else if (strcmp(s2, "TMDS") == 0)
+		rinfo->mon2_type = MT_DFP;
+	else if (strcmp(s2, "LVDS") == 0)
+		rinfo->mon2_type = MT_LCD;
+
+	return 1;
+}
+
+/*
+ * Probe display on both primary and secondary card's connector (if any)
+ * by various available techniques (i2c, OF device tree, BIOS, ...) and
+ * try to retreive EDID. The algorithm here comes from XFree's radeon
+ * driver
+ */
+void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
+				    const char *monitor_layout, int ignore_edid)
+{
+#ifdef CONFIG_FB_RADEON_I2C
+	int ddc_crt2_used = 0;	
+#endif
+	int tmp, i;
+
+	radeon_parse_connector_info(rinfo);
+
+	if (radeon_parse_monitor_layout(rinfo, monitor_layout)) {
+
+		/*
+		 * If user specified a monitor_layout option, use it instead
+		 * of auto-detecting. Maybe we should only use this argument
+		 * on the first radeon card probed or provide a way to specify
+		 * a layout for each card ?
+		 */
+
+		RTRACE("Using specified monitor layout: %s", monitor_layout);
+#ifdef CONFIG_FB_RADEON_I2C
+		if (!ignore_edid) {
+			if (rinfo->mon1_type != MT_NONE)
+				if (!radeon_probe_i2c_connector(rinfo, ddc_dvi, &rinfo->mon1_EDID)) {
+					radeon_probe_i2c_connector(rinfo, ddc_crt2, &rinfo->mon1_EDID);
+					ddc_crt2_used = 1;
+				}
+			if (rinfo->mon2_type != MT_NONE)
+				if (!radeon_probe_i2c_connector(rinfo, ddc_vga, &rinfo->mon2_EDID) &&
+				    !ddc_crt2_used)
+					radeon_probe_i2c_connector(rinfo, ddc_crt2, &rinfo->mon2_EDID);
+		}
+#endif /* CONFIG_FB_RADEON_I2C */
+		if (rinfo->mon1_type == MT_NONE) {
+			if (rinfo->mon2_type != MT_NONE) {
+				rinfo->mon1_type = rinfo->mon2_type;
+				rinfo->mon1_EDID = rinfo->mon2_EDID;
+			} else {
+				rinfo->mon1_type = MT_CRT;
+				printk(KERN_INFO "radeonfb: No valid monitor, assuming CRT on first port\n");
+			}
+			rinfo->mon2_type = MT_NONE;
+			rinfo->mon2_EDID = NULL;
+		}
+	} else {
+		/*
+		 * Auto-detecting display type (well... trying to ...)
+		 */
+		
+		RTRACE("Starting monitor auto detection...\n");
+
+#if DEBUG && defined(CONFIG_FB_RADEON_I2C)
+		{
+			u8 *EDIDs[4] = { NULL, NULL, NULL, NULL };
+			int mon_types[4] = {MT_NONE, MT_NONE, MT_NONE, MT_NONE};
+			int i;
+
+			for (i = 0; i < 4; i++)
+				mon_types[i] = radeon_probe_i2c_connector(rinfo,
+									  i+1, &EDIDs[i]);
+		}
+#endif /* DEBUG */
+		/*
+		 * Old single head cards
+		 */
+		if (!rinfo->has_CRTC2) {
+#ifdef CONFIG_PPC_OF
+			if (rinfo->mon1_type == MT_NONE)
+				rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
+									&rinfo->mon1_EDID);
+#endif /* CONFIG_PPC_OF */
+#ifdef CONFIG_FB_RADEON_I2C
+			if (rinfo->mon1_type == MT_NONE)
+				rinfo->mon1_type =
+					radeon_probe_i2c_connector(rinfo, ddc_dvi,
+								   &rinfo->mon1_EDID);
+			if (rinfo->mon1_type == MT_NONE)
+				rinfo->mon1_type =
+					radeon_probe_i2c_connector(rinfo, ddc_vga,
+								   &rinfo->mon1_EDID);
+			if (rinfo->mon1_type == MT_NONE)
+				rinfo->mon1_type =
+					radeon_probe_i2c_connector(rinfo, ddc_crt2,
+								   &rinfo->mon1_EDID);	
+#endif /* CONFIG_FB_RADEON_I2C */
+			if (rinfo->mon1_type == MT_NONE)
+				rinfo->mon1_type = MT_CRT;
+			goto bail;
+		}
+
+		/*
+		 * Check for cards with reversed DACs or TMDS controllers using BIOS
+		 */
+		if (rinfo->bios_seg &&
+		    (tmp = BIOS_IN16(rinfo->fp_bios_start + 0x50))) {
+			for (i = 1; i < 4; i++) {
+				unsigned int tmp0;
+
+				if (!BIOS_IN8(tmp + i*2) && i > 1)
+					break;
+				tmp0 = BIOS_IN16(tmp + i*2);
+				if ((!(tmp0 & 0x01)) && (((tmp0 >> 8) & 0x0f) == ddc_dvi)) {
+					rinfo->reversed_DAC = 1;
+					printk(KERN_INFO "radeonfb: Reversed DACs detected\n");
+				}
+				if ((((tmp0 >> 8) & 0x0f) == ddc_dvi) && ((tmp0 >> 4) & 0x01)) {
+					rinfo->reversed_TMDS = 1;
+					printk(KERN_INFO "radeonfb: Reversed TMDS detected\n");
+				}
+			}
+		}
+
+		/*
+		 * Probe primary head (DVI or laptop internal panel)
+		 */
+#ifdef CONFIG_PPC_OF
+		if (rinfo->mon1_type == MT_NONE)
+			rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
+								&rinfo->mon1_EDID);
+#endif /* CONFIG_PPC_OF */
+#ifdef CONFIG_FB_RADEON_I2C
+		if (rinfo->mon1_type == MT_NONE)
+			rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi,
+								      &rinfo->mon1_EDID);
+		if (rinfo->mon1_type == MT_NONE) {
+			rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_crt2,
+								      &rinfo->mon1_EDID);
+			if (rinfo->mon1_type != MT_NONE)
+				ddc_crt2_used = 1;
+		}
+#endif /* CONFIG_FB_RADEON_I2C */
+		if (rinfo->mon1_type == MT_NONE && rinfo->is_mobility &&
+		    ((rinfo->bios_seg && (INREG(BIOS_4_SCRATCH) & 4))
+		     || (INREG(LVDS_GEN_CNTL) & LVDS_ON))) {
+			rinfo->mon1_type = MT_LCD;
+			printk("Non-DDC laptop panel detected\n");
+		}
+		if (rinfo->mon1_type == MT_NONE)
+			rinfo->mon1_type = radeon_crt_is_connected(rinfo, rinfo->reversed_DAC);
+
+		/*
+		 * Probe secondary head (mostly VGA, can be DVI)
+		 */
+#ifdef CONFIG_PPC_OF
+		if (rinfo->mon2_type == MT_NONE)
+			rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1,
+								&rinfo->mon2_EDID);
+#endif /* CONFIG_PPC_OF */
+#ifdef CONFIG_FB_RADEON_I2C
+		if (rinfo->mon2_type == MT_NONE)
+			rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_vga,
+								      &rinfo->mon2_EDID);
+		if (rinfo->mon2_type == MT_NONE && !ddc_crt2_used)
+			rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_crt2,
+								      &rinfo->mon2_EDID);
+#endif /* CONFIG_FB_RADEON_I2C */
+		if (rinfo->mon2_type == MT_NONE)
+			rinfo->mon2_type = radeon_crt_is_connected(rinfo, !rinfo->reversed_DAC);
+
+		/*
+		 * If we only detected port 2, we swap them, if none detected,
+		 * assume CRT (maybe fallback to old BIOS_SCRATCH stuff ? or look
+		 * at FP registers ?)
+		 */
+		if (rinfo->mon1_type == MT_NONE) {
+			if (rinfo->mon2_type != MT_NONE) {
+				rinfo->mon1_type = rinfo->mon2_type;
+				rinfo->mon1_EDID = rinfo->mon2_EDID;
+			} else
+				rinfo->mon1_type = MT_CRT;
+			rinfo->mon2_type = MT_NONE;
+			rinfo->mon2_EDID = NULL;
+		}
+
+		/*
+		 * Deal with reversed TMDS
+		 */
+		if (rinfo->reversed_TMDS) {
+			/* Always keep internal TMDS as primary head */
+			if (rinfo->mon1_type == MT_DFP || rinfo->mon2_type == MT_DFP) {
+				int tmp_type = rinfo->mon1_type;
+				u8 *tmp_EDID = rinfo->mon1_EDID;
+				rinfo->mon1_type = rinfo->mon2_type;
+				rinfo->mon1_EDID = rinfo->mon2_EDID;
+				rinfo->mon2_type = tmp_type;
+				rinfo->mon2_EDID = tmp_EDID;
+				if (rinfo->mon1_type == MT_CRT || rinfo->mon2_type == MT_CRT)
+					rinfo->reversed_DAC ^= 1;
+			}
+		}
+	}
+	if (ignore_edid) {
+		kfree(rinfo->mon1_EDID);
+		rinfo->mon1_EDID = NULL;
+		kfree(rinfo->mon2_EDID);
+		rinfo->mon2_EDID = NULL;
+	}
+
+ bail:
+	printk(KERN_INFO "radeonfb: Monitor 1 type %s found\n",
+	       radeon_get_mon_name(rinfo->mon1_type));
+	if (rinfo->mon1_EDID)
+		printk(KERN_INFO "radeonfb: EDID probed\n");
+	if (!rinfo->has_CRTC2)
+		return;
+	printk(KERN_INFO "radeonfb: Monitor 2 type %s found\n",
+	       radeon_get_mon_name(rinfo->mon2_type));
+	if (rinfo->mon2_EDID)
+		printk(KERN_INFO "radeonfb: EDID probed\n");
+}
+
+
+/*
+ * This functions applyes any arch/model/machine specific fixups
+ * to the panel info. It may eventually alter EDID block as
+ * well or whatever is specific to a given model and not probed
+ * properly by the default code
+ */
+static void radeon_fixup_panel_info(struct radeonfb_info *rinfo)
+{
+#ifdef CONFIG_PPC_OF
+	/*
+	 * LCD Flat panels should use fixed dividers, we enfore that on
+	 * PPC only for now...
+	 */
+	if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD
+	    && rinfo->is_mobility) {
+		int ppll_div_sel;
+		u32 ppll_divn;
+		ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3;
+		radeon_pll_errata_after_index(rinfo);
+		ppll_divn = INPLL(PPLL_DIV_0 + ppll_div_sel);
+		rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
+		rinfo->panel_info.fbk_divider = ppll_divn & 0x7ff;
+		rinfo->panel_info.post_divider = (ppll_divn >> 16) & 0x7;
+		rinfo->panel_info.use_bios_dividers = 1;
+
+		printk(KERN_DEBUG "radeonfb: Using Firmware dividers 0x%08x "
+		       "from PPLL %d\n",
+		       rinfo->panel_info.fbk_divider |
+		       (rinfo->panel_info.post_divider << 16),
+		       ppll_div_sel);
+	}
+#endif /* CONFIG_PPC_OF */
+}
+
+
+/*
+ * Fill up panel infos from a mode definition, either returned by the EDID
+ * or from the default mode when we can't do any better
+ */
+static void radeon_var_to_panel_info(struct radeonfb_info *rinfo, struct fb_var_screeninfo *var)
+{
+	rinfo->panel_info.xres = var->xres;
+	rinfo->panel_info.yres = var->yres;
+	rinfo->panel_info.clock = 100000000 / var->pixclock;
+	rinfo->panel_info.hOver_plus = var->right_margin;
+	rinfo->panel_info.hSync_width = var->hsync_len;
+       	rinfo->panel_info.hblank = var->left_margin +
+		(var->right_margin + var->hsync_len);
+	rinfo->panel_info.vOver_plus = var->lower_margin;
+	rinfo->panel_info.vSync_width = var->vsync_len;
+       	rinfo->panel_info.vblank = var->upper_margin +
+		(var->lower_margin + var->vsync_len);
+	rinfo->panel_info.hAct_high =
+		(var->sync & FB_SYNC_HOR_HIGH_ACT) != 0;
+	rinfo->panel_info.vAct_high =
+		(var->sync & FB_SYNC_VERT_HIGH_ACT) != 0;
+	rinfo->panel_info.valid = 1;
+	/* We use a default of 200ms for the panel power delay, 
+	 * I need to have a real schedule() instead of mdelay's in the panel code.
+	 * we might be possible to figure out a better power delay either from
+	 * MacOS OF tree or from the EDID block (proprietary extensions ?)
+	 */
+	rinfo->panel_info.pwr_delay = 200;
+}
+
+static void radeon_videomode_to_var(struct fb_var_screeninfo *var,
+				    const struct fb_videomode *mode)
+{
+	var->xres = mode->xres;
+	var->yres = mode->yres;
+	var->xres_virtual = mode->xres;
+	var->yres_virtual = mode->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+	var->pixclock = mode->pixclock;
+	var->left_margin = mode->left_margin;
+	var->right_margin = mode->right_margin;
+	var->upper_margin = mode->upper_margin;
+	var->lower_margin = mode->lower_margin;
+	var->hsync_len = mode->hsync_len;
+	var->vsync_len = mode->vsync_len;
+	var->sync = mode->sync;
+	var->vmode = mode->vmode;
+}
+
+/*
+ * Build the modedb for head 1 (head 2 will come later), check panel infos
+ * from either BIOS or EDID, and pick up the default mode
+ */
+void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_option)
+{
+	struct fb_info * info = rinfo->info;
+	int has_default_mode = 0;
+
+	/*
+	 * Fill default var first
+	 */
+	info->var = radeonfb_default_var;
+	INIT_LIST_HEAD(&info->modelist);
+
+	/*
+	 * First check out what BIOS has to say
+	 */
+	if (rinfo->mon1_type == MT_LCD)
+		radeon_get_panel_info_BIOS(rinfo);
+
+	/*
+	 * Parse EDID detailed timings and deduce panel infos if any. Right now
+	 * we only deal with first entry returned by parse_EDID, we may do better
+	 * some day...
+	 */
+	if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type != MT_CRT
+	    && rinfo->mon1_EDID) {
+		struct fb_var_screeninfo var;
+		RTRACE("Parsing EDID data for panel info\n");
+		if (fb_parse_edid(rinfo->mon1_EDID, &var) == 0) {
+			if (var.xres >= rinfo->panel_info.xres &&
+			    var.yres >= rinfo->panel_info.yres)
+				radeon_var_to_panel_info(rinfo, &var);
+		}
+	}
+
+	/*
+	 * Do any additional platform/arch fixups to the panel infos
+	 */
+	radeon_fixup_panel_info(rinfo);
+
+	/*
+	 * If we have some valid panel infos, we setup the default mode based on
+	 * those
+	 */
+	if (rinfo->mon1_type != MT_CRT && rinfo->panel_info.valid) {
+		struct fb_var_screeninfo *var = &info->var;
+
+		RTRACE("Setting up default mode based on panel info\n");
+		var->xres = rinfo->panel_info.xres;
+		var->yres = rinfo->panel_info.yres;
+		var->xres_virtual = rinfo->panel_info.xres;
+		var->yres_virtual = rinfo->panel_info.yres;
+		var->xoffset = var->yoffset = 0;
+		var->bits_per_pixel = 8;
+		var->pixclock = 100000000 / rinfo->panel_info.clock;
+		var->left_margin = (rinfo->panel_info.hblank - rinfo->panel_info.hOver_plus
+				    - rinfo->panel_info.hSync_width);
+		var->right_margin = rinfo->panel_info.hOver_plus;
+		var->upper_margin = (rinfo->panel_info.vblank - rinfo->panel_info.vOver_plus
+				     - rinfo->panel_info.vSync_width);
+		var->lower_margin = rinfo->panel_info.vOver_plus;
+		var->hsync_len = rinfo->panel_info.hSync_width;
+		var->vsync_len = rinfo->panel_info.vSync_width;
+		var->sync = 0;
+		if (rinfo->panel_info.hAct_high)
+			var->sync |= FB_SYNC_HOR_HIGH_ACT;
+		if (rinfo->panel_info.vAct_high)
+			var->sync |= FB_SYNC_VERT_HIGH_ACT;
+		var->vmode = 0;
+		has_default_mode = 1;
+	}
+
+	/*
+	 * Now build modedb from EDID
+	 */
+	if (rinfo->mon1_EDID) {
+		fb_edid_to_monspecs(rinfo->mon1_EDID, &info->monspecs);
+		fb_videomode_to_modelist(info->monspecs.modedb,
+					 info->monspecs.modedb_len,
+					 &info->modelist);
+		rinfo->mon1_modedb = info->monspecs.modedb;
+		rinfo->mon1_dbsize = info->monspecs.modedb_len;
+	}
+
+	
+	/*
+	 * Finally, if we don't have panel infos we need to figure some (or
+	 * we try to read it from card), we try to pick a default mode
+	 * and create some panel infos. Whatever...
+	 */
+	if (rinfo->mon1_type != MT_CRT && !rinfo->panel_info.valid) {
+		struct fb_videomode	*modedb;
+		int			dbsize;
+		char			modename[32];
+
+		RTRACE("Guessing panel info...\n");
+		if (rinfo->panel_info.xres == 0 || rinfo->panel_info.yres == 0) {
+			u32 tmp = INREG(FP_HORZ_STRETCH) & HORZ_PANEL_SIZE;
+			rinfo->panel_info.xres = ((tmp >> HORZ_PANEL_SHIFT) + 1) * 8;
+			tmp = INREG(FP_VERT_STRETCH) & VERT_PANEL_SIZE;
+			rinfo->panel_info.yres = (tmp >> VERT_PANEL_SHIFT) + 1;
+		}
+		if (rinfo->panel_info.xres == 0 || rinfo->panel_info.yres == 0) {
+			printk(KERN_WARNING "radeonfb: Can't find panel size, going back to CRT\n");
+			rinfo->mon1_type = MT_CRT;
+			goto pickup_default;
+		}
+		printk(KERN_WARNING "radeonfb: Assuming panel size %dx%d\n",
+		       rinfo->panel_info.xres, rinfo->panel_info.yres);
+		modedb = rinfo->mon1_modedb;
+		dbsize = rinfo->mon1_dbsize;
+		snprintf(modename, 31, "%dx%d", rinfo->panel_info.xres, rinfo->panel_info.yres);
+		if (fb_find_mode(&info->var, info, modename,
+				 modedb, dbsize, NULL, 8) == 0) {
+			printk(KERN_WARNING "radeonfb: Can't find mode for panel size, going back to CRT\n");
+			rinfo->mon1_type = MT_CRT;
+			goto pickup_default;
+		}
+		has_default_mode = 1;
+		radeon_var_to_panel_info(rinfo, &info->var);
+	}
+
+ pickup_default:
+	/*
+	 * Apply passed-in mode option if any
+	 */
+	if (mode_option) {
+		if (fb_find_mode(&info->var, info, mode_option,
+				 info->monspecs.modedb,
+				 info->monspecs.modedb_len, NULL, 8) != 0)
+			has_default_mode = 1;
+ 	}
+
+	/*
+	 * Still no mode, let's pick up a default from the db
+	 */
+	if (!has_default_mode && info->monspecs.modedb != NULL) {
+		struct fb_monspecs *specs = &info->monspecs;
+		struct fb_videomode *modedb = NULL;
+
+		/* get preferred timing */
+		if (specs->misc & FB_MISC_1ST_DETAIL) {
+			int i;
+
+			for (i = 0; i < specs->modedb_len; i++) {
+				if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
+					modedb = &specs->modedb[i];
+					break;
+				}
+			}
+		} else {
+			/* otherwise, get first mode in database */
+			modedb = &specs->modedb[0];
+		}
+		if (modedb != NULL) {
+			info->var.bits_per_pixel = 8;
+			radeon_videomode_to_var(&info->var, modedb);
+			has_default_mode = 1;
+		}
+	}
+	if (1) {
+		struct fb_videomode mode;
+		/* Make sure that whatever mode got selected is actually in the
+		 * modelist or the kernel may die
+		 */
+		fb_var_to_videomode(&mode, &info->var);
+		fb_add_videomode(&mode, &info->modelist);
+	}
+}
+
+/*
+ * The code below is used to pick up a mode in check_var and
+ * set_var. It should be made generic
+ */
+
+/*
+ * This is used when looking for modes. We assign a "distance" value
+ * to a mode in the modedb depending how "close" it is from what we
+ * are looking for.
+ * Currently, we don't compare that much, we could do better but
+ * the current fbcon doesn't quite mind ;)
+ */
+static int radeon_compare_modes(const struct fb_var_screeninfo *var,
+				const struct fb_videomode *mode)
+{
+	int distance = 0;
+
+	distance = mode->yres - var->yres;
+	distance += (mode->xres - var->xres)/2;
+	return distance;
+}
+
+/*
+ * This function is called by check_var, it gets the passed in mode parameter, and
+ * outputs a valid mode matching the passed-in one as closely as possible.
+ * We need something better ultimately. Things like fbcon basically pass us out
+ * current mode with xres/yres hacked, while things like XFree will actually
+ * produce a full timing that we should respect as much as possible.
+ *
+ * This is why I added the FB_ACTIVATE_FIND that is used by fbcon. Without this,
+ * we do a simple spec match, that's all. With it, we actually look for a mode in
+ * either our monitor modedb or the vesa one if none
+ *
+ */
+int  radeon_match_mode(struct radeonfb_info *rinfo,
+		       struct fb_var_screeninfo *dest,
+		       const struct fb_var_screeninfo *src)
+{
+	const struct fb_videomode	*db = vesa_modes;
+	int				i, dbsize = 34;
+	int				has_rmx, native_db = 0;
+	int				distance = INT_MAX;
+	const struct fb_videomode	*candidate = NULL;
+
+	/* Start with a copy of the requested mode */
+	memcpy(dest, src, sizeof(struct fb_var_screeninfo));
+
+	/* Check if we have a modedb built from EDID */
+	if (rinfo->mon1_modedb) {
+		db = rinfo->mon1_modedb;
+		dbsize = rinfo->mon1_dbsize;
+		native_db = 1;
+	}
+
+	/* Check if we have a scaler allowing any fancy mode */
+	has_rmx = rinfo->mon1_type == MT_LCD || rinfo->mon1_type == MT_DFP;
+
+	/* If we have a scaler and are passed FB_ACTIVATE_TEST or
+	 * FB_ACTIVATE_NOW, just do basic checking and return if the
+	 * mode match
+	 */
+	if ((src->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST ||
+	    (src->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+		/* We don't have an RMX, validate timings. If we don't have
+	 	 * monspecs, we should be paranoid and not let use go above
+		 * 640x480-60, but I assume userland knows what it's doing here
+		 * (though I may be proven wrong...)
+		 */
+		if (has_rmx == 0 && rinfo->mon1_modedb)
+			if (fb_validate_mode((struct fb_var_screeninfo *)src, rinfo->info))
+				return -EINVAL;
+		return 0;
+	}
+
+	/* Now look for a mode in the database */
+	while (db) {
+		for (i = 0; i < dbsize; i++) {
+			int d;
+
+			if (db[i].yres < src->yres)
+				continue;	
+			if (db[i].xres < src->xres)
+				continue;
+			d = radeon_compare_modes(src, &db[i]);
+			/* If the new mode is at least as good as the previous one,
+			 * then it's our new candidate
+			 */
+			if (d < distance) {
+				candidate = &db[i];
+				distance = d;
+			}
+		}
+		db = NULL;
+		/* If we have a scaler, we allow any mode from the database */
+		if (native_db && has_rmx) {
+			db = vesa_modes;
+			dbsize = 34;
+			native_db = 0;
+		}
+	}
+
+	/* If we have found a match, return it */
+	if (candidate != NULL) {
+		radeon_videomode_to_var(dest, candidate);
+		return 0;
+	}
+
+	/* If we haven't and don't have a scaler, fail */
+	if (!has_rmx)
+		return -EINVAL;
+
+	return 0;
+}
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c
new file mode 100644
index 0000000..23c677e
--- /dev/null
+++ b/drivers/video/aty/radeon_pm.c
@@ -0,0 +1,2801 @@
+/*
+ *	drivers/video/aty/radeon_pm.c
+ *
+ *	Copyright 2003,2004 Ben. Herrenschmidt <benh@kernel.crashing.org>
+ *	Copyright 2004 Paul Mackerras <paulus@samba.org>
+ *
+ *	This is the power management code for ATI radeon chipsets. It contains
+ *	some dynamic clock PM enable/disable code similar to what X.org does,
+ *	some D2-state (APM-style) sleep/wakeup code for use on some PowerMacs,
+ *	and the necessary bits to re-initialize from scratch a few chips found
+ *	on PowerMacs as well. The later could be extended to more platforms
+ *	provided the memory controller configuration code be made more generic,
+ *	and you can get the proper mode register commands for your RAMs.
+ *	Those things may be found in the BIOS image...
+ */
+
+#include "radeonfb.h"
+
+#include <linux/console.h>
+#include <linux/agp_backend.h>
+
+#ifdef CONFIG_PPC_PMAC
+#include <asm/processor.h>
+#include <asm/prom.h>
+#include <asm/pmac_feature.h>
+#endif
+
+#include "ati_ids.h"
+
+static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
+{
+	u32 tmp;
+
+	/* RV100 */
+	if ((rinfo->family == CHIP_FAMILY_RV100) && (!rinfo->is_mobility)) {
+		if (rinfo->has_CRTC2) {
+			tmp = INPLL(pllSCLK_CNTL);
+			tmp &= ~SCLK_CNTL__DYN_STOP_LAT_MASK;
+			tmp |= SCLK_CNTL__CP_MAX_DYN_STOP_LAT | SCLK_CNTL__FORCEON_MASK;
+			OUTPLL(pllSCLK_CNTL, tmp);
+		}
+		tmp = INPLL(pllMCLK_CNTL);
+		tmp |= (MCLK_CNTL__FORCE_MCLKA |
+		        MCLK_CNTL__FORCE_MCLKB |
+		        MCLK_CNTL__FORCE_YCLKA |
+		        MCLK_CNTL__FORCE_YCLKB |
+			MCLK_CNTL__FORCE_AIC |
+			MCLK_CNTL__FORCE_MC);
+                OUTPLL(pllMCLK_CNTL, tmp);
+		return;
+	}
+	/* R100 */
+	if (!rinfo->has_CRTC2) {
+                tmp = INPLL(pllSCLK_CNTL);
+                tmp |= (SCLK_CNTL__FORCE_CP	| SCLK_CNTL__FORCE_HDP	|
+			SCLK_CNTL__FORCE_DISP1	| SCLK_CNTL__FORCE_TOP	|
+                        SCLK_CNTL__FORCE_E2	| SCLK_CNTL__FORCE_SE 	|
+			SCLK_CNTL__FORCE_IDCT	| SCLK_CNTL__FORCE_VIP	|
+			SCLK_CNTL__FORCE_RE	| SCLK_CNTL__FORCE_PB 	|
+			SCLK_CNTL__FORCE_TAM	| SCLK_CNTL__FORCE_TDM	|
+                        SCLK_CNTL__FORCE_RB);
+                OUTPLL(pllSCLK_CNTL, tmp);
+		return;
+	}
+	/* RV350 (M10) */
+	if (rinfo->family == CHIP_FAMILY_RV350) {
+                /* for RV350/M10, no delays are required. */
+                tmp = INPLL(pllSCLK_CNTL2);
+                tmp |= (SCLK_CNTL2__R300_FORCE_TCL |
+                        SCLK_CNTL2__R300_FORCE_GA  |
+			SCLK_CNTL2__R300_FORCE_CBA);
+                OUTPLL(pllSCLK_CNTL2, tmp);
+
+                tmp = INPLL(pllSCLK_CNTL);
+                tmp |= (SCLK_CNTL__FORCE_DISP2		| SCLK_CNTL__FORCE_CP		|
+                        SCLK_CNTL__FORCE_HDP		| SCLK_CNTL__FORCE_DISP1	|
+                        SCLK_CNTL__FORCE_TOP		| SCLK_CNTL__FORCE_E2		|
+                        SCLK_CNTL__R300_FORCE_VAP	| SCLK_CNTL__FORCE_IDCT    	|
+			SCLK_CNTL__FORCE_VIP		| SCLK_CNTL__R300_FORCE_SR	|
+			SCLK_CNTL__R300_FORCE_PX	| SCLK_CNTL__R300_FORCE_TX	|
+			SCLK_CNTL__R300_FORCE_US	| SCLK_CNTL__FORCE_TV_SCLK	|
+                        SCLK_CNTL__R300_FORCE_SU	| SCLK_CNTL__FORCE_OV0);
+                OUTPLL(pllSCLK_CNTL, tmp);
+
+                tmp = INPLL(pllSCLK_MORE_CNTL);
+		tmp |= (SCLK_MORE_CNTL__FORCE_DISPREGS	| SCLK_MORE_CNTL__FORCE_MC_GUI	|
+			SCLK_MORE_CNTL__FORCE_MC_HOST);
+                OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+		tmp = INPLL(pllMCLK_CNTL);
+		tmp |= (MCLK_CNTL__FORCE_MCLKA |
+		        MCLK_CNTL__FORCE_MCLKB |
+		        MCLK_CNTL__FORCE_YCLKA |
+		        MCLK_CNTL__FORCE_YCLKB |
+			MCLK_CNTL__FORCE_MC);
+                OUTPLL(pllMCLK_CNTL, tmp);
+
+                tmp = INPLL(pllVCLK_ECP_CNTL);
+                tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb  |
+                         VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb |
+			 VCLK_ECP_CNTL__R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF);
+                OUTPLL(pllVCLK_ECP_CNTL, tmp);
+
+                tmp = INPLL(pllPIXCLKS_CNTL);
+                tmp &= ~(PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb	|
+			 PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb	|
+			 PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb	|
+			 PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb	|
+			 PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb		|
+			 PIXCLKS_CNTL__R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF);
+                OUTPLL(pllPIXCLKS_CNTL, tmp);
+
+		return;
+	}
+	
+	/* Default */
+
+	/* Force Core Clocks */
+	tmp = INPLL(pllSCLK_CNTL);
+	tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_E2);
+
+	/* XFree doesn't do that case, but we had this code from Apple and it
+	 * seem necessary for proper suspend/resume operations
+	 */
+	if (rinfo->is_mobility) {
+		tmp |= 	SCLK_CNTL__FORCE_HDP|
+			SCLK_CNTL__FORCE_DISP1|
+			SCLK_CNTL__FORCE_DISP2|
+			SCLK_CNTL__FORCE_TOP|
+			SCLK_CNTL__FORCE_SE|
+			SCLK_CNTL__FORCE_IDCT|
+			SCLK_CNTL__FORCE_VIP|
+			SCLK_CNTL__FORCE_PB|
+			SCLK_CNTL__FORCE_RE|
+			SCLK_CNTL__FORCE_TAM|
+			SCLK_CNTL__FORCE_TDM|
+			SCLK_CNTL__FORCE_RB|
+			SCLK_CNTL__FORCE_TV_SCLK|
+			SCLK_CNTL__FORCE_SUBPIC|
+			SCLK_CNTL__FORCE_OV0;
+	}
+	else if (rinfo->family == CHIP_FAMILY_R300 ||
+		   rinfo->family == CHIP_FAMILY_R350) {
+		tmp |=  SCLK_CNTL__FORCE_HDP   |
+			SCLK_CNTL__FORCE_DISP1 |
+			SCLK_CNTL__FORCE_DISP2 |
+			SCLK_CNTL__FORCE_TOP   |
+			SCLK_CNTL__FORCE_IDCT  |
+			SCLK_CNTL__FORCE_VIP;
+	}
+    	OUTPLL(pllSCLK_CNTL, tmp);
+	radeon_msleep(16);
+
+	if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) {
+		tmp = INPLL(pllSCLK_CNTL2);
+		tmp |=  SCLK_CNTL2__R300_FORCE_TCL |
+			SCLK_CNTL2__R300_FORCE_GA  |
+			SCLK_CNTL2__R300_FORCE_CBA;
+		OUTPLL(pllSCLK_CNTL2, tmp);
+		radeon_msleep(16);
+	}
+
+	tmp = INPLL(pllCLK_PIN_CNTL);
+	tmp &= ~CLK_PIN_CNTL__SCLK_DYN_START_CNTL;
+	OUTPLL(pllCLK_PIN_CNTL, tmp);
+	radeon_msleep(15);
+
+	if (rinfo->is_IGP) {
+		/* Weird  ... X is _un_ forcing clocks here, I think it's
+		 * doing backward. Imitate it for now...
+		 */
+		tmp = INPLL(pllMCLK_CNTL);
+		tmp &= ~(MCLK_CNTL__FORCE_MCLKA |
+			 MCLK_CNTL__FORCE_YCLKA);
+		OUTPLL(pllMCLK_CNTL, tmp);
+		radeon_msleep(16);
+	}
+	/* Hrm... same shit, X doesn't do that but I have to */
+	else if (rinfo->is_mobility) {
+		tmp = INPLL(pllMCLK_CNTL);
+		tmp |= (MCLK_CNTL__FORCE_MCLKA |
+			MCLK_CNTL__FORCE_MCLKB |
+			MCLK_CNTL__FORCE_YCLKA |
+			MCLK_CNTL__FORCE_YCLKB);
+		OUTPLL(pllMCLK_CNTL, tmp);
+		radeon_msleep(16);
+
+		tmp = INPLL(pllMCLK_MISC);
+		tmp &= 	~(MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT|
+			  MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT|
+			  MCLK_MISC__MC_MCLK_DYN_ENABLE|
+			  MCLK_MISC__IO_MCLK_DYN_ENABLE);
+		OUTPLL(pllMCLK_MISC, tmp);
+		radeon_msleep(15);
+	}
+
+	if (rinfo->is_mobility) {
+		tmp = INPLL(pllSCLK_MORE_CNTL);
+		tmp |= 	SCLK_MORE_CNTL__FORCE_DISPREGS|
+			SCLK_MORE_CNTL__FORCE_MC_GUI|
+			SCLK_MORE_CNTL__FORCE_MC_HOST;
+		OUTPLL(pllSCLK_MORE_CNTL, tmp);
+		radeon_msleep(16);
+	}
+
+	tmp = INPLL(pllPIXCLKS_CNTL);
+	tmp &= ~(PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb |
+		 PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+		 PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb |
+		 PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+		 PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb|
+		 PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb|
+		 PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb);
+ 	OUTPLL(pllPIXCLKS_CNTL, tmp);
+	radeon_msleep(16);
+
+	tmp = INPLL( pllVCLK_ECP_CNTL);
+	tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+		 VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+	OUTPLL( pllVCLK_ECP_CNTL, tmp);
+	radeon_msleep(16);
+}
+
+static void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo)
+{
+	u32 tmp;
+
+	/* R100 */
+	if (!rinfo->has_CRTC2) {
+                tmp = INPLL(pllSCLK_CNTL);
+
+		if ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) > CFG_ATI_REV_A13)
+                    tmp &= ~(SCLK_CNTL__FORCE_CP	| SCLK_CNTL__FORCE_RB);
+                tmp &= ~(SCLK_CNTL__FORCE_HDP		| SCLK_CNTL__FORCE_DISP1 |
+			 SCLK_CNTL__FORCE_TOP		| SCLK_CNTL__FORCE_SE   |
+			 SCLK_CNTL__FORCE_IDCT		| SCLK_CNTL__FORCE_RE   |
+			 SCLK_CNTL__FORCE_PB		| SCLK_CNTL__FORCE_TAM  |
+			 SCLK_CNTL__FORCE_TDM);
+                OUTPLL(pllSCLK_CNTL, tmp);
+		return;
+	}
+
+	/* M10 */
+	if (rinfo->family == CHIP_FAMILY_RV350) {
+		tmp = INPLL(pllSCLK_CNTL2);
+		tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL |
+			 SCLK_CNTL2__R300_FORCE_GA  |
+			 SCLK_CNTL2__R300_FORCE_CBA);
+		tmp |=  (SCLK_CNTL2__R300_TCL_MAX_DYN_STOP_LAT |
+			 SCLK_CNTL2__R300_GA_MAX_DYN_STOP_LAT  |
+			 SCLK_CNTL2__R300_CBA_MAX_DYN_STOP_LAT);
+		OUTPLL(pllSCLK_CNTL2, tmp);
+
+		tmp = INPLL(pllSCLK_CNTL);
+		tmp &= ~(SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_CP      |
+			 SCLK_CNTL__FORCE_HDP   | SCLK_CNTL__FORCE_DISP1   |
+			 SCLK_CNTL__FORCE_TOP   | SCLK_CNTL__FORCE_E2      |
+			 SCLK_CNTL__R300_FORCE_VAP | SCLK_CNTL__FORCE_IDCT |
+			 SCLK_CNTL__FORCE_VIP   | SCLK_CNTL__R300_FORCE_SR |
+			 SCLK_CNTL__R300_FORCE_PX | SCLK_CNTL__R300_FORCE_TX |
+			 SCLK_CNTL__R300_FORCE_US | SCLK_CNTL__FORCE_TV_SCLK |
+			 SCLK_CNTL__R300_FORCE_SU | SCLK_CNTL__FORCE_OV0);
+		tmp |= SCLK_CNTL__DYN_STOP_LAT_MASK;
+		OUTPLL(pllSCLK_CNTL, tmp);
+
+		tmp = INPLL(pllSCLK_MORE_CNTL);
+		tmp &= ~SCLK_MORE_CNTL__FORCEON;
+		tmp |=  SCLK_MORE_CNTL__DISPREGS_MAX_DYN_STOP_LAT |
+			SCLK_MORE_CNTL__MC_GUI_MAX_DYN_STOP_LAT |
+			SCLK_MORE_CNTL__MC_HOST_MAX_DYN_STOP_LAT;
+		OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+		tmp = INPLL(pllVCLK_ECP_CNTL);
+		tmp |= (VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+			VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+		OUTPLL(pllVCLK_ECP_CNTL, tmp);
+
+		tmp = INPLL(pllPIXCLKS_CNTL);
+		tmp |= (PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb         |
+			PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb     |
+			PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb |
+			PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb            |
+			PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb    |
+			PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb       |
+			PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb        |
+			PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb     |
+			PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb     |
+			PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb      |
+			PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb        |
+			PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb           |
+			PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb);
+		OUTPLL(pllPIXCLKS_CNTL, tmp);
+
+		tmp = INPLL(pllMCLK_MISC);
+		tmp |= (MCLK_MISC__MC_MCLK_DYN_ENABLE |
+			MCLK_MISC__IO_MCLK_DYN_ENABLE);
+		OUTPLL(pllMCLK_MISC, tmp);
+
+		tmp = INPLL(pllMCLK_CNTL);
+		tmp |= (MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_MCLKB);
+		tmp &= ~(MCLK_CNTL__FORCE_YCLKA  |
+			 MCLK_CNTL__FORCE_YCLKB  |
+			 MCLK_CNTL__FORCE_MC);
+
+		/* Some releases of vbios have set DISABLE_MC_MCLKA
+		 * and DISABLE_MC_MCLKB bits in the vbios table.  Setting these
+		 * bits will cause H/W hang when reading video memory with dynamic
+		 * clocking enabled.
+		 */
+		if ((tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKA) &&
+		    (tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKB)) {
+			/* If both bits are set, then check the active channels */
+			tmp = INPLL(pllMCLK_CNTL);
+			if (rinfo->vram_width == 64) {
+			    if (INREG(MEM_CNTL) & R300_MEM_USE_CD_CH_ONLY)
+				tmp &= ~MCLK_CNTL__R300_DISABLE_MC_MCLKB;
+			    else
+				tmp &= ~MCLK_CNTL__R300_DISABLE_MC_MCLKA;
+			} else {
+			    tmp &= ~(MCLK_CNTL__R300_DISABLE_MC_MCLKA |
+				     MCLK_CNTL__R300_DISABLE_MC_MCLKB);
+			}
+		}
+		OUTPLL(pllMCLK_CNTL, tmp);
+		return;
+	}
+
+	/* R300 */
+	if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) {
+		tmp = INPLL(pllSCLK_CNTL);
+		tmp &= ~(SCLK_CNTL__R300_FORCE_VAP);
+		tmp |= SCLK_CNTL__FORCE_CP;
+		OUTPLL(pllSCLK_CNTL, tmp);
+		radeon_msleep(15);
+
+		tmp = INPLL(pllSCLK_CNTL2);
+		tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL |
+			 SCLK_CNTL2__R300_FORCE_GA  |
+			 SCLK_CNTL2__R300_FORCE_CBA);
+		OUTPLL(pllSCLK_CNTL2, tmp);
+	}
+
+	/* Others */
+
+	tmp = INPLL( pllCLK_PWRMGT_CNTL);
+	tmp &= ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK|
+		 CLK_PWRMGT_CNTL__DISP_DYN_STOP_LAT_MASK|
+		 CLK_PWRMGT_CNTL__DYN_STOP_MODE_MASK);
+	tmp |= CLK_PWRMGT_CNTL__ENGINE_DYNCLK_MODE_MASK |
+	       (0x01 << CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT__SHIFT);
+	OUTPLL( pllCLK_PWRMGT_CNTL, tmp);
+	radeon_msleep(15);
+
+	tmp = INPLL(pllCLK_PIN_CNTL);
+	tmp |= CLK_PIN_CNTL__SCLK_DYN_START_CNTL;
+	OUTPLL(pllCLK_PIN_CNTL, tmp);
+	radeon_msleep(15);
+
+	/* When DRI is enabled, setting DYN_STOP_LAT to zero can cause some R200
+	 * to lockup randomly, leave them as set by BIOS.
+	 */
+	tmp = INPLL(pllSCLK_CNTL);
+	tmp &= ~SCLK_CNTL__FORCEON_MASK;
+
+	/*RAGE_6::A11 A12 A12N1 A13, RV250::A11 A12, R300*/
+	if ((rinfo->family == CHIP_FAMILY_RV250 &&
+	     ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13)) ||
+	    ((rinfo->family == CHIP_FAMILY_RV100) &&
+	     ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) <= CFG_ATI_REV_A13))) {
+		tmp |= SCLK_CNTL__FORCE_CP;
+		tmp |= SCLK_CNTL__FORCE_VIP;
+	}
+	OUTPLL(pllSCLK_CNTL, tmp);
+	radeon_msleep(15);
+
+	if ((rinfo->family == CHIP_FAMILY_RV200) ||
+	    (rinfo->family == CHIP_FAMILY_RV250) ||
+	    (rinfo->family == CHIP_FAMILY_RV280)) {
+		tmp = INPLL(pllSCLK_MORE_CNTL);
+		tmp &= ~SCLK_MORE_CNTL__FORCEON;
+
+		/* RV200::A11 A12 RV250::A11 A12 */
+		if (((rinfo->family == CHIP_FAMILY_RV200) ||
+		     (rinfo->family == CHIP_FAMILY_RV250)) &&
+		    ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13))
+			tmp |= SCLK_MORE_CNTL__FORCEON;
+
+		OUTPLL(pllSCLK_MORE_CNTL, tmp);
+		radeon_msleep(15);
+	}
+	
+
+	/* RV200::A11 A12, RV250::A11 A12 */
+	if (((rinfo->family == CHIP_FAMILY_RV200) ||
+	     (rinfo->family == CHIP_FAMILY_RV250)) &&
+	    ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13)) {
+		tmp = INPLL(pllPLL_PWRMGT_CNTL);
+		tmp |= PLL_PWRMGT_CNTL__TCL_BYPASS_DISABLE;
+		OUTPLL(pllPLL_PWRMGT_CNTL, tmp);
+		radeon_msleep(15);
+	}
+
+	tmp = INPLL(pllPIXCLKS_CNTL);
+	tmp |=  PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb |
+		PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb|
+		PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+		PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb|
+		PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb|
+		PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+		PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb;
+	OUTPLL(pllPIXCLKS_CNTL, tmp);
+	radeon_msleep(15);
+		
+	tmp = INPLL(pllVCLK_ECP_CNTL);
+	tmp |=  VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+		VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb;
+	OUTPLL(pllVCLK_ECP_CNTL, tmp);
+
+	/* X doesn't do that ... hrm, we do on mobility && Macs */
+#ifdef CONFIG_PPC_OF
+	if (rinfo->is_mobility) {
+		tmp  = INPLL(pllMCLK_CNTL);
+		tmp &= ~(MCLK_CNTL__FORCE_MCLKA |
+			 MCLK_CNTL__FORCE_MCLKB |
+			 MCLK_CNTL__FORCE_YCLKA |
+			 MCLK_CNTL__FORCE_YCLKB);
+		OUTPLL(pllMCLK_CNTL, tmp);
+		radeon_msleep(15);
+
+		tmp = INPLL(pllMCLK_MISC);
+		tmp |= 	MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT|
+			MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT|
+			MCLK_MISC__MC_MCLK_DYN_ENABLE|
+			MCLK_MISC__IO_MCLK_DYN_ENABLE;
+		OUTPLL(pllMCLK_MISC, tmp);
+		radeon_msleep(15);
+	}
+#endif /* CONFIG_PPC_OF */
+}
+
+#ifdef CONFIG_PM
+
+static void OUTMC( struct radeonfb_info *rinfo, u8 indx, u32 value)
+{
+	OUTREG( MC_IND_INDEX, indx | MC_IND_INDEX__MC_IND_WR_EN);	
+	OUTREG( MC_IND_DATA, value);		
+}
+
+static u32 INMC(struct radeonfb_info *rinfo, u8 indx)
+{
+	OUTREG( MC_IND_INDEX, indx);					
+	return INREG( MC_IND_DATA);
+}
+
+static void radeon_pm_save_regs(struct radeonfb_info *rinfo, int saving_for_d3)
+{
+	rinfo->save_regs[0] = INPLL(PLL_PWRMGT_CNTL);
+	rinfo->save_regs[1] = INPLL(CLK_PWRMGT_CNTL);
+	rinfo->save_regs[2] = INPLL(MCLK_CNTL);
+	rinfo->save_regs[3] = INPLL(SCLK_CNTL);
+	rinfo->save_regs[4] = INPLL(CLK_PIN_CNTL);
+	rinfo->save_regs[5] = INPLL(VCLK_ECP_CNTL);
+	rinfo->save_regs[6] = INPLL(PIXCLKS_CNTL);
+	rinfo->save_regs[7] = INPLL(MCLK_MISC);
+	rinfo->save_regs[8] = INPLL(P2PLL_CNTL);
+	
+	rinfo->save_regs[9] = INREG(DISP_MISC_CNTL);
+	rinfo->save_regs[10] = INREG(DISP_PWR_MAN);
+	rinfo->save_regs[11] = INREG(LVDS_GEN_CNTL);
+	rinfo->save_regs[13] = INREG(TV_DAC_CNTL);
+	rinfo->save_regs[14] = INREG(BUS_CNTL1);
+	rinfo->save_regs[15] = INREG(CRTC_OFFSET_CNTL);
+	rinfo->save_regs[16] = INREG(AGP_CNTL);
+	rinfo->save_regs[17] = (INREG(CRTC_GEN_CNTL) & 0xfdffffff) | 0x04000000;
+	rinfo->save_regs[18] = (INREG(CRTC2_GEN_CNTL) & 0xfdffffff) | 0x04000000;
+	rinfo->save_regs[19] = INREG(GPIOPAD_A);
+	rinfo->save_regs[20] = INREG(GPIOPAD_EN);
+	rinfo->save_regs[21] = INREG(GPIOPAD_MASK);
+	rinfo->save_regs[22] = INREG(ZV_LCDPAD_A);
+	rinfo->save_regs[23] = INREG(ZV_LCDPAD_EN);
+	rinfo->save_regs[24] = INREG(ZV_LCDPAD_MASK);
+	rinfo->save_regs[25] = INREG(GPIO_VGA_DDC);
+	rinfo->save_regs[26] = INREG(GPIO_DVI_DDC);
+	rinfo->save_regs[27] = INREG(GPIO_MONID);
+	rinfo->save_regs[28] = INREG(GPIO_CRT2_DDC);
+
+	rinfo->save_regs[29] = INREG(SURFACE_CNTL);
+	rinfo->save_regs[30] = INREG(MC_FB_LOCATION);
+	rinfo->save_regs[31] = INREG(DISPLAY_BASE_ADDR);
+	rinfo->save_regs[32] = INREG(MC_AGP_LOCATION);
+	rinfo->save_regs[33] = INREG(CRTC2_DISPLAY_BASE_ADDR);
+
+	rinfo->save_regs[34] = INPLL(SCLK_MORE_CNTL);
+	rinfo->save_regs[35] = INREG(MEM_SDRAM_MODE_REG);
+	rinfo->save_regs[36] = INREG(BUS_CNTL);
+	rinfo->save_regs[39] = INREG(RBBM_CNTL);
+	rinfo->save_regs[40] = INREG(DAC_CNTL);
+	rinfo->save_regs[41] = INREG(HOST_PATH_CNTL);
+	rinfo->save_regs[37] = INREG(MPP_TB_CONFIG);
+	rinfo->save_regs[38] = INREG(FCP_CNTL);
+
+	if (rinfo->is_mobility) {
+		rinfo->save_regs[12] = INREG(LVDS_PLL_CNTL);
+		rinfo->save_regs[43] = INPLL(pllSSPLL_CNTL);
+		rinfo->save_regs[44] = INPLL(pllSSPLL_REF_DIV);
+		rinfo->save_regs[45] = INPLL(pllSSPLL_DIV_0);
+		rinfo->save_regs[90] = INPLL(pllSS_INT_CNTL);
+		rinfo->save_regs[91] = INPLL(pllSS_TST_CNTL);
+		rinfo->save_regs[81] = INREG(LVDS_GEN_CNTL);
+	}
+
+	if (rinfo->family >= CHIP_FAMILY_RV200) {
+		rinfo->save_regs[42] = INREG(MEM_REFRESH_CNTL);
+		rinfo->save_regs[46] = INREG(MC_CNTL);
+		rinfo->save_regs[47] = INREG(MC_INIT_GFX_LAT_TIMER);
+		rinfo->save_regs[48] = INREG(MC_INIT_MISC_LAT_TIMER);
+		rinfo->save_regs[49] = INREG(MC_TIMING_CNTL);
+		rinfo->save_regs[50] = INREG(MC_READ_CNTL_AB);
+		rinfo->save_regs[51] = INREG(MC_IOPAD_CNTL);
+		rinfo->save_regs[52] = INREG(MC_CHIP_IO_OE_CNTL_AB);
+		rinfo->save_regs[53] = INREG(MC_DEBUG);
+	}
+	rinfo->save_regs[54] = INREG(PAMAC0_DLY_CNTL);
+	rinfo->save_regs[55] = INREG(PAMAC1_DLY_CNTL);
+	rinfo->save_regs[56] = INREG(PAD_CTLR_MISC);
+	rinfo->save_regs[57] = INREG(FW_CNTL);
+
+	if (rinfo->family >= CHIP_FAMILY_R300) {
+		rinfo->save_regs[58] = INMC(rinfo, ixR300_MC_MC_INIT_WR_LAT_TIMER);
+		rinfo->save_regs[59] = INMC(rinfo, ixR300_MC_IMP_CNTL);
+		rinfo->save_regs[60] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_C0);
+		rinfo->save_regs[61] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_C1);
+		rinfo->save_regs[62] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_D0);
+		rinfo->save_regs[63] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_D1);
+		rinfo->save_regs[64] = INMC(rinfo, ixR300_MC_BIST_CNTL_3);
+		rinfo->save_regs[65] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A0);
+		rinfo->save_regs[66] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1);
+		rinfo->save_regs[67] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B0);
+		rinfo->save_regs[68] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1);
+		rinfo->save_regs[69] = INMC(rinfo, ixR300_MC_DEBUG_CNTL);
+		rinfo->save_regs[70] = INMC(rinfo, ixR300_MC_DLL_CNTL);
+		rinfo->save_regs[71] = INMC(rinfo, ixR300_MC_IMP_CNTL_0);
+		rinfo->save_regs[72] = INMC(rinfo, ixR300_MC_ELPIDA_CNTL);
+		rinfo->save_regs[96] = INMC(rinfo, ixR300_MC_READ_CNTL_CD);
+	} else {
+		rinfo->save_regs[59] = INMC(rinfo, ixMC_IMP_CNTL);
+		rinfo->save_regs[65] = INMC(rinfo, ixMC_CHP_IO_CNTL_A0);
+		rinfo->save_regs[66] = INMC(rinfo, ixMC_CHP_IO_CNTL_A1);
+		rinfo->save_regs[67] = INMC(rinfo, ixMC_CHP_IO_CNTL_B0);
+		rinfo->save_regs[68] = INMC(rinfo, ixMC_CHP_IO_CNTL_B1);
+		rinfo->save_regs[71] = INMC(rinfo, ixMC_IMP_CNTL_0);
+	}
+
+	rinfo->save_regs[73] = INPLL(pllMPLL_CNTL);
+	rinfo->save_regs[74] = INPLL(pllSPLL_CNTL);
+	rinfo->save_regs[75] = INPLL(pllMPLL_AUX_CNTL);
+	rinfo->save_regs[76] = INPLL(pllSPLL_AUX_CNTL);
+	rinfo->save_regs[77] = INPLL(pllM_SPLL_REF_FB_DIV);
+	rinfo->save_regs[78] = INPLL(pllAGP_PLL_CNTL);
+	rinfo->save_regs[79] = INREG(PAMAC2_DLY_CNTL);
+
+	rinfo->save_regs[80] = INREG(OV0_BASE_ADDR);
+	rinfo->save_regs[82] = INREG(FP_GEN_CNTL);
+	rinfo->save_regs[83] = INREG(FP2_GEN_CNTL);
+	rinfo->save_regs[84] = INREG(TMDS_CNTL);
+	rinfo->save_regs[85] = INREG(TMDS_TRANSMITTER_CNTL);
+	rinfo->save_regs[86] = INREG(DISP_OUTPUT_CNTL);
+	rinfo->save_regs[87] = INREG(DISP_HW_DEBUG);
+	rinfo->save_regs[88] = INREG(TV_MASTER_CNTL);
+	rinfo->save_regs[89] = INPLL(pllP2PLL_REF_DIV);
+	rinfo->save_regs[92] = INPLL(pllPPLL_DIV_0);
+	rinfo->save_regs[93] = INPLL(pllPPLL_CNTL);
+	rinfo->save_regs[94] = INREG(GRPH_BUFFER_CNTL);
+	rinfo->save_regs[95] = INREG(GRPH2_BUFFER_CNTL);
+	rinfo->save_regs[96] = INREG(HDP_DEBUG);
+	rinfo->save_regs[97] = INPLL(pllMDLL_CKO);
+	rinfo->save_regs[98] = INPLL(pllMDLL_RDCKA);
+	rinfo->save_regs[99] = INPLL(pllMDLL_RDCKB);
+}
+
+static void radeon_pm_restore_regs(struct radeonfb_info *rinfo)
+{
+	OUTPLL(P2PLL_CNTL, rinfo->save_regs[8] & 0xFFFFFFFE); /* First */
+	
+	OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]);
+	OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]);
+	OUTPLL(MCLK_CNTL, rinfo->save_regs[2]);
+	OUTPLL(SCLK_CNTL, rinfo->save_regs[3]);
+	OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]);
+	OUTPLL(VCLK_ECP_CNTL, rinfo->save_regs[5]);
+	OUTPLL(PIXCLKS_CNTL, rinfo->save_regs[6]);
+	OUTPLL(MCLK_MISC, rinfo->save_regs[7]);
+	if (rinfo->family == CHIP_FAMILY_RV350)
+		OUTPLL(SCLK_MORE_CNTL, rinfo->save_regs[34]);
+
+	OUTREG(SURFACE_CNTL, rinfo->save_regs[29]);
+	OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+	OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+	OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+	OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+	OUTREG(CONFIG_MEMSIZE, rinfo->video_ram);
+
+	OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+	OUTREG(DISP_PWR_MAN, rinfo->save_regs[10]);
+	OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11]);
+	OUTREG(LVDS_PLL_CNTL,rinfo->save_regs[12]);
+	OUTREG(TV_DAC_CNTL, rinfo->save_regs[13]);
+	OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
+	OUTREG(CRTC_OFFSET_CNTL, rinfo->save_regs[15]);
+	OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+	OUTREG(CRTC_GEN_CNTL, rinfo->save_regs[17]);
+	OUTREG(CRTC2_GEN_CNTL, rinfo->save_regs[18]);
+	OUTPLL(P2PLL_CNTL, rinfo->save_regs[8]);
+
+	OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
+	OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
+	OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
+	OUTREG(ZV_LCDPAD_A, rinfo->save_regs[22]);
+	OUTREG(ZV_LCDPAD_EN, rinfo->save_regs[23]);
+	OUTREG(ZV_LCDPAD_MASK, rinfo->save_regs[24]);
+	OUTREG(GPIO_VGA_DDC, rinfo->save_regs[25]);
+	OUTREG(GPIO_DVI_DDC, rinfo->save_regs[26]);
+	OUTREG(GPIO_MONID, rinfo->save_regs[27]);
+	OUTREG(GPIO_CRT2_DDC, rinfo->save_regs[28]);
+}
+
+static void radeon_pm_disable_iopad(struct radeonfb_info *rinfo)
+{		
+	OUTREG(GPIOPAD_MASK, 0x0001ffff);
+	OUTREG(GPIOPAD_EN, 0x00000400);
+	OUTREG(GPIOPAD_A, 0x00000000);		
+        OUTREG(ZV_LCDPAD_MASK, 0x00000000);
+        OUTREG(ZV_LCDPAD_EN, 0x00000000);
+      	OUTREG(ZV_LCDPAD_A, 0x00000000); 	
+	OUTREG(GPIO_VGA_DDC, 0x00030000);
+	OUTREG(GPIO_DVI_DDC, 0x00000000);
+	OUTREG(GPIO_MONID, 0x00030000);
+	OUTREG(GPIO_CRT2_DDC, 0x00000000);
+}
+
+static void radeon_pm_program_v2clk(struct radeonfb_info *rinfo)
+{
+	/* Set v2clk to 65MHz */
+	if (rinfo->family <= CHIP_FAMILY_RV280) {
+		OUTPLL(pllPIXCLKS_CNTL,
+			 __INPLL(rinfo, pllPIXCLKS_CNTL)
+			 & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK);
+	 
+		OUTPLL(pllP2PLL_REF_DIV, 0x0000000c);
+		OUTPLL(pllP2PLL_CNTL, 0x0000bf00);
+	} else {
+		OUTPLL(pllP2PLL_REF_DIV, 0x0000000c);
+		INPLL(pllP2PLL_REF_DIV);
+		OUTPLL(pllP2PLL_CNTL, 0x0000a700);
+	}
+
+	OUTPLL(pllP2PLL_DIV_0, 0x00020074 | P2PLL_DIV_0__P2PLL_ATOMIC_UPDATE_W);
+	
+	OUTPLL(pllP2PLL_CNTL, INPLL(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_SLEEP);
+	mdelay(1);
+
+	OUTPLL(pllP2PLL_CNTL, INPLL(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_RESET);
+	mdelay( 1);
+
+  	OUTPLL(pllPIXCLKS_CNTL,
+  		(INPLL(pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK)
+  		| (0x03 << PIXCLKS_CNTL__PIX2CLK_SRC_SEL__SHIFT));
+	mdelay( 1);	
+}
+
+static void radeon_pm_low_current(struct radeonfb_info *rinfo)
+{
+	u32 reg;
+
+	reg  = INREG(BUS_CNTL1);
+	if (rinfo->family <= CHIP_FAMILY_RV280) {
+		reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK;
+		reg |= BUS_CNTL1_AGPCLK_VALID | (1<<BUS_CNTL1_MOBILE_PLATFORM_SEL_SHIFT);
+	} else {
+		reg |= 0x4080;
+	}
+	OUTREG(BUS_CNTL1, reg);
+	
+	reg  = INPLL(PLL_PWRMGT_CNTL);
+	reg |= PLL_PWRMGT_CNTL_SPLL_TURNOFF | PLL_PWRMGT_CNTL_PPLL_TURNOFF |
+		PLL_PWRMGT_CNTL_P2PLL_TURNOFF | PLL_PWRMGT_CNTL_TVPLL_TURNOFF;
+	reg &= ~PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK;
+	reg &= ~PLL_PWRMGT_CNTL_MOBILE_SU;
+	OUTPLL(PLL_PWRMGT_CNTL, reg);
+	
+	reg  = INREG(TV_DAC_CNTL);
+	reg &= ~(TV_DAC_CNTL_BGADJ_MASK |TV_DAC_CNTL_DACADJ_MASK);
+	reg |=TV_DAC_CNTL_BGSLEEP | TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD |
+		TV_DAC_CNTL_BDACPD |
+		(8<<TV_DAC_CNTL_BGADJ__SHIFT) | (8<<TV_DAC_CNTL_DACADJ__SHIFT);
+	OUTREG(TV_DAC_CNTL, reg);
+	
+	reg  = INREG(TMDS_TRANSMITTER_CNTL);
+	reg &= ~(TMDS_PLL_EN | TMDS_PLLRST);
+	OUTREG(TMDS_TRANSMITTER_CNTL, reg);
+
+	reg = INREG(DAC_CNTL);
+	reg &= ~DAC_CMP_EN;
+	OUTREG(DAC_CNTL, reg);
+
+	reg = INREG(DAC_CNTL2);
+	reg &= ~DAC2_CMP_EN;
+	OUTREG(DAC_CNTL2, reg);
+	
+	reg  = INREG(TV_DAC_CNTL);
+	reg &= ~TV_DAC_CNTL_DETECT;
+	OUTREG(TV_DAC_CNTL, reg);
+}
+
+static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
+{
+
+	u32 sclk_cntl, mclk_cntl, sclk_more_cntl;
+
+	u32 pll_pwrmgt_cntl;
+	u32 clk_pwrmgt_cntl;
+	u32 clk_pin_cntl;
+	u32 vclk_ecp_cntl; 
+	u32 pixclks_cntl;
+	u32 disp_mis_cntl;
+	u32 disp_pwr_man;
+	u32 tmp;
+	
+	/* Force Core Clocks */
+	sclk_cntl = INPLL( pllSCLK_CNTL);
+	sclk_cntl |= 	SCLK_CNTL__IDCT_MAX_DYN_STOP_LAT|
+			SCLK_CNTL__VIP_MAX_DYN_STOP_LAT|
+			SCLK_CNTL__RE_MAX_DYN_STOP_LAT|
+			SCLK_CNTL__PB_MAX_DYN_STOP_LAT|
+			SCLK_CNTL__TAM_MAX_DYN_STOP_LAT|
+			SCLK_CNTL__TDM_MAX_DYN_STOP_LAT|
+			SCLK_CNTL__RB_MAX_DYN_STOP_LAT|
+			
+			SCLK_CNTL__FORCE_DISP2|
+			SCLK_CNTL__FORCE_CP|
+			SCLK_CNTL__FORCE_HDP|
+			SCLK_CNTL__FORCE_DISP1|
+			SCLK_CNTL__FORCE_TOP|
+			SCLK_CNTL__FORCE_E2|
+			SCLK_CNTL__FORCE_SE|
+			SCLK_CNTL__FORCE_IDCT|
+			SCLK_CNTL__FORCE_VIP|
+			
+			SCLK_CNTL__FORCE_PB|
+			SCLK_CNTL__FORCE_TAM|
+			SCLK_CNTL__FORCE_TDM|
+			SCLK_CNTL__FORCE_RB|
+			SCLK_CNTL__FORCE_TV_SCLK|
+			SCLK_CNTL__FORCE_SUBPIC|
+			SCLK_CNTL__FORCE_OV0;
+	if (rinfo->family <= CHIP_FAMILY_RV280)
+		sclk_cntl |= SCLK_CNTL__FORCE_RE;
+	else
+		sclk_cntl |= SCLK_CNTL__SE_MAX_DYN_STOP_LAT |
+			SCLK_CNTL__E2_MAX_DYN_STOP_LAT |
+			SCLK_CNTL__TV_MAX_DYN_STOP_LAT |
+			SCLK_CNTL__HDP_MAX_DYN_STOP_LAT |
+			SCLK_CNTL__CP_MAX_DYN_STOP_LAT;
+
+	OUTPLL( pllSCLK_CNTL, sclk_cntl);
+
+	sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL);
+	sclk_more_cntl |= 	SCLK_MORE_CNTL__FORCE_DISPREGS |
+				SCLK_MORE_CNTL__FORCE_MC_GUI |
+				SCLK_MORE_CNTL__FORCE_MC_HOST;
+
+	OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl);		
+
+	
+	mclk_cntl = INPLL( pllMCLK_CNTL);
+	mclk_cntl &= ~(	MCLK_CNTL__FORCE_MCLKA |
+			MCLK_CNTL__FORCE_MCLKB |
+			MCLK_CNTL__FORCE_YCLKA |
+			MCLK_CNTL__FORCE_YCLKB |
+			MCLK_CNTL__FORCE_MC
+		      );	
+    	OUTPLL( pllMCLK_CNTL, mclk_cntl);
+	
+	/* Force Display clocks	*/
+	vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL);
+	vclk_ecp_cntl &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb
+			   | VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+	vclk_ecp_cntl |= VCLK_ECP_CNTL__ECP_FORCE_ON;
+	OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl);
+	
+	
+	pixclks_cntl = INPLL( pllPIXCLKS_CNTL);
+	pixclks_cntl &= ~(	PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | 
+				PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb |
+				PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb|
+				PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb);
+						
+ 	OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl);
+
+	/* Switch off LVDS interface */
+	OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) &
+	       ~(LVDS_BLON | LVDS_EN | LVDS_ON | LVDS_DIGON));
+
+	/* Enable System power management */
+	pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL);
+	
+	pll_pwrmgt_cntl |= 	PLL_PWRMGT_CNTL__SPLL_TURNOFF |
+				PLL_PWRMGT_CNTL__MPLL_TURNOFF|
+				PLL_PWRMGT_CNTL__PPLL_TURNOFF|
+				PLL_PWRMGT_CNTL__P2PLL_TURNOFF|
+				PLL_PWRMGT_CNTL__TVPLL_TURNOFF;
+						
+	OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
+	
+	clk_pwrmgt_cntl	 = INPLL( pllCLK_PWRMGT_CNTL);
+	
+	clk_pwrmgt_cntl &= ~(	CLK_PWRMGT_CNTL__MPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL__SPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL__PPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL__P2PLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL__MCLK_TURNOFF|
+				CLK_PWRMGT_CNTL__SCLK_TURNOFF|
+				CLK_PWRMGT_CNTL__PCLK_TURNOFF|
+				CLK_PWRMGT_CNTL__P2CLK_TURNOFF|
+				CLK_PWRMGT_CNTL__TVPLL_PWRMGT_OFF|
+				CLK_PWRMGT_CNTL__GLOBAL_PMAN_EN|
+				CLK_PWRMGT_CNTL__ENGINE_DYNCLK_MODE|
+				CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK|
+				CLK_PWRMGT_CNTL__CG_NO1_DEBUG_MASK
+			);
+						
+	clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL__GLOBAL_PMAN_EN
+		| CLK_PWRMGT_CNTL__DISP_PM;
+	
+	OUTPLL( pllCLK_PWRMGT_CNTL, clk_pwrmgt_cntl);
+	
+	clk_pin_cntl = INPLL( pllCLK_PIN_CNTL);
+	
+	clk_pin_cntl &= ~CLK_PIN_CNTL__ACCESS_REGS_IN_SUSPEND;
+
+	/* because both INPLL and OUTPLL take the same lock, that's why. */
+	tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
+	OUTPLL( pllMCLK_MISC, tmp);
+	
+	/* AGP PLL control */
+	if (rinfo->family <= CHIP_FAMILY_RV280) {
+		OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) |  BUS_CNTL1__AGPCLK_VALID);
+
+		OUTREG(BUS_CNTL1,
+		       (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
+		       | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT));	// 440BX
+	} else {
+		OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
+		OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
+	}
+
+	OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL)
+				  & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
+	
+	clk_pin_cntl &= ~CLK_PIN_CNTL__CG_CLK_TO_OUTPIN;
+	clk_pin_cntl |= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;	
+	OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
+
+	/* Solano2M */
+	OUTREG(AGP_CNTL,
+		(INREG(AGP_CNTL) & ~(AGP_CNTL__MAX_IDLE_CLK_MASK))
+		| (0x20<<AGP_CNTL__MAX_IDLE_CLK__SHIFT));
+
+	/* ACPI mode */
+	/* because both INPLL and OUTPLL take the same lock, that's why. */
+	tmp = INPLL( pllPLL_PWRMGT_CNTL) & ~PLL_PWRMGT_CNTL__PM_MODE_SEL;
+	OUTPLL( pllPLL_PWRMGT_CNTL, tmp);
+
+
+	disp_mis_cntl = INREG(DISP_MISC_CNTL);
+	
+	disp_mis_cntl &= ~(	DISP_MISC_CNTL__SOFT_RESET_GRPH_PP | 
+				DISP_MISC_CNTL__SOFT_RESET_SUBPIC_PP | 
+				DISP_MISC_CNTL__SOFT_RESET_OV0_PP |
+				DISP_MISC_CNTL__SOFT_RESET_GRPH_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_SUBPIC_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_OV0_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_GRPH2_PP|
+				DISP_MISC_CNTL__SOFT_RESET_GRPH2_SCLK|
+				DISP_MISC_CNTL__SOFT_RESET_LVDS|
+				DISP_MISC_CNTL__SOFT_RESET_TMDS|
+				DISP_MISC_CNTL__SOFT_RESET_DIG_TMDS|
+				DISP_MISC_CNTL__SOFT_RESET_TV);
+	
+	OUTREG(DISP_MISC_CNTL, disp_mis_cntl);
+						
+	disp_pwr_man = INREG(DISP_PWR_MAN);
+	
+	disp_pwr_man &= ~(	DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN	| 
+				DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN |
+				DISP_PWR_MAN__DISP_PWR_MAN_DPMS_MASK|
+				DISP_PWR_MAN__DISP_D3_RST|
+				DISP_PWR_MAN__DISP_D3_REG_RST
+				);
+	
+	disp_pwr_man |= DISP_PWR_MAN__DISP_D3_GRPH_RST|
+					DISP_PWR_MAN__DISP_D3_SUBPIC_RST|
+					DISP_PWR_MAN__DISP_D3_OV0_RST|
+					DISP_PWR_MAN__DISP_D1D2_GRPH_RST|
+					DISP_PWR_MAN__DISP_D1D2_SUBPIC_RST|
+					DISP_PWR_MAN__DISP_D1D2_OV0_RST|
+					DISP_PWR_MAN__DIG_TMDS_ENABLE_RST|
+					DISP_PWR_MAN__TV_ENABLE_RST| 
+//					DISP_PWR_MAN__AUTO_PWRUP_EN|
+					0;
+	
+	OUTREG(DISP_PWR_MAN, disp_pwr_man);					
+							
+	clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL);
+	pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL) ;
+	clk_pin_cntl 	= INPLL( pllCLK_PIN_CNTL);
+	disp_pwr_man	= INREG(DISP_PWR_MAN);
+		
+	
+	/* D2 */
+	clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL__DISP_PM;
+	pll_pwrmgt_cntl |= PLL_PWRMGT_CNTL__MOBILE_SU | PLL_PWRMGT_CNTL__SU_SCLK_USE_BCLK;
+	clk_pin_cntl	|= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;
+	disp_pwr_man 	&= ~(DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN_MASK
+			     | DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN_MASK);
+
+	OUTPLL( pllCLK_PWRMGT_CNTL, clk_pwrmgt_cntl);
+	OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
+	OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
+	OUTREG(DISP_PWR_MAN, disp_pwr_man);
+
+	/* disable display request & disable display */
+	OUTREG( CRTC_GEN_CNTL, (INREG( CRTC_GEN_CNTL) & ~CRTC_GEN_CNTL__CRTC_EN)
+		| CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B);
+	OUTREG( CRTC2_GEN_CNTL, (INREG( CRTC2_GEN_CNTL) & ~CRTC2_GEN_CNTL__CRTC2_EN)
+		| CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B);
+
+	mdelay(17);				   
+
+}
+
+static void radeon_pm_yclk_mclk_sync(struct radeonfb_info *rinfo)
+{
+	u32 mc_chp_io_cntl_a1, mc_chp_io_cntl_b1;
+
+	mc_chp_io_cntl_a1 = INMC( rinfo, ixMC_CHP_IO_CNTL_A1)
+		& ~MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA_MASK;
+	mc_chp_io_cntl_b1 = INMC( rinfo, ixMC_CHP_IO_CNTL_B1)
+		& ~MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB_MASK;
+
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1
+	       | (1<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT));
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1
+	       | (1<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT));
+
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1);
+	OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1);
+
+	mdelay( 1);
+}
+
+static void radeon_pm_yclk_mclk_sync_m10(struct radeonfb_info *rinfo)
+{
+	u32 mc_chp_io_cntl_a1, mc_chp_io_cntl_b1;
+
+	mc_chp_io_cntl_a1 = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1)
+		& ~MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA_MASK;
+	mc_chp_io_cntl_b1 = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1)
+		& ~MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB_MASK;
+
+	OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_A1,
+	       mc_chp_io_cntl_a1 | (1<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT));
+	OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_B1,
+	       mc_chp_io_cntl_b1 | (1<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT));
+
+	OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1);
+	OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1);
+
+	mdelay( 1);
+}
+
+static void radeon_pm_program_mode_reg(struct radeonfb_info *rinfo, u16 value,
+				       u8 delay_required)
+{  
+	u32 mem_sdram_mode;
+
+	mem_sdram_mode  = INREG( MEM_SDRAM_MODE_REG);
+
+	mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_MODE_REG_MASK;
+	mem_sdram_mode |= (value<<MEM_SDRAM_MODE_REG__MEM_MODE_REG__SHIFT)
+		| MEM_SDRAM_MODE_REG__MEM_CFG_TYPE;
+	OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+	if (delay_required >= 2)
+		mdelay(1);
+
+	mem_sdram_mode |=  MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET;
+	OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+	if (delay_required >= 2)
+		mdelay(1);
+
+	mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET;
+	OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+	if (delay_required >= 2)
+		mdelay(1);
+
+	if (delay_required) {
+		do {
+			if (delay_required >= 2)
+				mdelay(1);
+		} while ((INREG(MC_STATUS)
+			  & (MC_STATUS__MEM_PWRUP_COMPL_A |
+			     MC_STATUS__MEM_PWRUP_COMPL_B)) == 0);
+	}
+}
+
+static void radeon_pm_m10_program_mode_wait(struct radeonfb_info *rinfo)
+{
+	int cnt;
+
+	for (cnt = 0; cnt < 100; ++cnt) {
+		mdelay(1);
+		if (INREG(MC_STATUS) & (MC_STATUS__MEM_PWRUP_COMPL_A
+					| MC_STATUS__MEM_PWRUP_COMPL_B))
+			break;
+	}
+}
+
+
+static void radeon_pm_enable_dll(struct radeonfb_info *rinfo)
+{  
+#define DLL_RESET_DELAY 	5
+#define DLL_SLEEP_DELAY		1
+
+	u32 cko = INPLL(pllMDLL_CKO)   | MDLL_CKO__MCKOA_SLEEP
+		| MDLL_CKO__MCKOA_RESET;
+	u32 cka = INPLL(pllMDLL_RDCKA) | MDLL_RDCKA__MRDCKA0_SLEEP
+		| MDLL_RDCKA__MRDCKA1_SLEEP | MDLL_RDCKA__MRDCKA0_RESET
+		| MDLL_RDCKA__MRDCKA1_RESET;
+	u32 ckb = INPLL(pllMDLL_RDCKB) | MDLL_RDCKB__MRDCKB0_SLEEP
+		| MDLL_RDCKB__MRDCKB1_SLEEP | MDLL_RDCKB__MRDCKB0_RESET
+		| MDLL_RDCKB__MRDCKB1_RESET;
+
+	/* Setting up the DLL range for write */
+	OUTPLL(pllMDLL_CKO,   	cko);
+	OUTPLL(pllMDLL_RDCKA,  	cka);
+	OUTPLL(pllMDLL_RDCKB,	ckb);
+
+	mdelay(DLL_RESET_DELAY*2);
+
+	cko &= ~(MDLL_CKO__MCKOA_SLEEP | MDLL_CKO__MCKOB_SLEEP);
+	OUTPLL(pllMDLL_CKO, cko);
+	mdelay(DLL_SLEEP_DELAY);
+	cko &= ~(MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET);
+	OUTPLL(pllMDLL_CKO, cko);
+	mdelay(DLL_RESET_DELAY);
+
+	cka &= ~(MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP);
+	OUTPLL(pllMDLL_RDCKA, cka);
+	mdelay(DLL_SLEEP_DELAY);
+	cka &= ~(MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET);
+	OUTPLL(pllMDLL_RDCKA, cka);
+	mdelay(DLL_RESET_DELAY);
+
+	ckb &= ~(MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP);
+	OUTPLL(pllMDLL_RDCKB, ckb);
+	mdelay(DLL_SLEEP_DELAY);
+	ckb &= ~(MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET);
+	OUTPLL(pllMDLL_RDCKB, ckb);
+	mdelay(DLL_RESET_DELAY);
+
+
+#undef DLL_RESET_DELAY
+#undef DLL_SLEEP_DELAY
+}
+
+static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo)
+{
+	u32 dll_value;
+	u32 dll_sleep_mask = 0;
+	u32 dll_reset_mask = 0;
+	u32 mc;
+
+#define DLL_RESET_DELAY 	5
+#define DLL_SLEEP_DELAY		1
+
+	OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]);
+	mc = INREG(MC_CNTL);
+	/* Check which channels are enabled */
+	switch (mc & 0x3) {
+	case 1:
+		if (mc & 0x4)
+			break;
+	case 2:
+		dll_sleep_mask |= MDLL_R300_RDCK__MRDCKB_SLEEP;
+		dll_reset_mask |= MDLL_R300_RDCK__MRDCKB_RESET;
+	case 0:
+		dll_sleep_mask |= MDLL_R300_RDCK__MRDCKA_SLEEP;
+		dll_reset_mask |= MDLL_R300_RDCK__MRDCKA_RESET;
+	}
+	switch (mc & 0x3) {
+	case 1:
+		if (!(mc & 0x4))
+			break;
+	case 2:
+		dll_sleep_mask |= MDLL_R300_RDCK__MRDCKD_SLEEP;
+		dll_reset_mask |= MDLL_R300_RDCK__MRDCKD_RESET;
+		dll_sleep_mask |= MDLL_R300_RDCK__MRDCKC_SLEEP;
+		dll_reset_mask |= MDLL_R300_RDCK__MRDCKC_RESET;
+	}
+
+	dll_value = INPLL(pllMDLL_RDCKA);
+
+	/* Power Up */
+	dll_value &= ~(dll_sleep_mask);
+	OUTPLL(pllMDLL_RDCKA, dll_value);
+	mdelay( DLL_SLEEP_DELAY);  		
+
+	dll_value &= ~(dll_reset_mask);
+	OUTPLL(pllMDLL_RDCKA, dll_value);
+	mdelay( DLL_RESET_DELAY);  		
+
+#undef DLL_RESET_DELAY 
+#undef DLL_SLEEP_DELAY
+}
+
+
+static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo)
+{
+	u32 crtcGenCntl, crtcGenCntl2, memRefreshCntl, crtc_more_cntl,
+		fp_gen_cntl, fp2_gen_cntl;
+ 
+	crtcGenCntl  = INREG( CRTC_GEN_CNTL);
+	crtcGenCntl2 = INREG( CRTC2_GEN_CNTL);
+
+	crtc_more_cntl 	= INREG( CRTC_MORE_CNTL);
+	fp_gen_cntl 	= INREG( FP_GEN_CNTL);
+	fp2_gen_cntl 	= INREG( FP2_GEN_CNTL);
+ 
+
+	OUTREG( CRTC_MORE_CNTL, 0);
+	OUTREG( FP_GEN_CNTL, 0);
+	OUTREG( FP2_GEN_CNTL,0);
+ 
+	OUTREG( CRTC_GEN_CNTL,  (crtcGenCntl | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B) );
+	OUTREG( CRTC2_GEN_CNTL, (crtcGenCntl2 | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B) );
+  
+	/* This is the code for the Aluminium PowerBooks M10 */
+	if (rinfo->family == CHIP_FAMILY_RV350) {
+		u32 sdram_mode_reg = rinfo->save_regs[35];
+		static u32 default_mrtable[] =
+			{ 0x21320032,
+			  0x21321000, 0xa1321000, 0x21321000, 0xffffffff,
+			  0x21320032, 0xa1320032, 0x21320032, 0xffffffff,
+			  0x21321002, 0xa1321002, 0x21321002, 0xffffffff,
+			  0x21320132, 0xa1320132, 0x21320132, 0xffffffff,
+			  0x21320032, 0xa1320032, 0x21320032, 0xffffffff,
+			  0x31320032 };
+
+		u32 *mrtable = default_mrtable;
+		int i, mrtable_size = ARRAY_SIZE(default_mrtable);
+
+		mdelay(30);
+
+		/* Disable refresh */
+		memRefreshCntl 	= INREG( MEM_REFRESH_CNTL)
+			& ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS;
+		OUTREG( MEM_REFRESH_CNTL, memRefreshCntl
+			| MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+		/* Configure and enable M & SPLLs */
+       		radeon_pm_enable_dll_m10(rinfo);
+		radeon_pm_yclk_mclk_sync_m10(rinfo);
+
+#ifdef CONFIG_PPC_OF
+		if (rinfo->of_node != NULL) {
+			int size;
+
+			mrtable = (u32 *)get_property(rinfo->of_node, "ATY,MRT", &size);
+			if (mrtable)
+				mrtable_size = size >> 2;
+			else
+				mrtable = default_mrtable;
+		}
+#endif /* CONFIG_PPC_OF */
+
+		/* Program the SDRAM */
+		sdram_mode_reg = mrtable[0];
+		OUTREG(MEM_SDRAM_MODE_REG, sdram_mode_reg);
+		for (i = 0; i < mrtable_size; i++) {
+			if (mrtable[i] == 0xffffffffu)
+				radeon_pm_m10_program_mode_wait(rinfo);
+			else {
+				sdram_mode_reg &= ~(MEM_SDRAM_MODE_REG__MEM_MODE_REG_MASK
+						    | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE
+						    | MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET);
+				sdram_mode_reg |= mrtable[i];
+
+				OUTREG(MEM_SDRAM_MODE_REG, sdram_mode_reg);
+				mdelay(1);
+			}
+		}
+
+		/* Restore memory refresh */
+		OUTREG(MEM_REFRESH_CNTL, memRefreshCntl);
+		mdelay(30);
+
+	}
+	/* Here come the desktop RV200 "QW" card */
+	else if (!rinfo->is_mobility && rinfo->family == CHIP_FAMILY_RV200) {
+		/* Disable refresh */
+		memRefreshCntl 	= INREG( MEM_REFRESH_CNTL)
+			& ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS;
+		OUTREG(MEM_REFRESH_CNTL, memRefreshCntl
+		       | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+		mdelay(30);
+
+		/* Reset memory */
+		OUTREG(MEM_SDRAM_MODE_REG,
+		       INREG( MEM_SDRAM_MODE_REG) & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+		radeon_pm_program_mode_reg(rinfo, 0x2002, 2);
+		radeon_pm_program_mode_reg(rinfo, 0x0132, 2);
+		radeon_pm_program_mode_reg(rinfo, 0x0032, 2);
+
+		OUTREG(MEM_SDRAM_MODE_REG,
+		       INREG(MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+		OUTREG( MEM_REFRESH_CNTL, 	memRefreshCntl);
+
+	}
+	/* The M6 */
+	else if (rinfo->is_mobility && rinfo->family == CHIP_FAMILY_RV100) {
+		/* Disable refresh */
+		memRefreshCntl = INREG(EXT_MEM_CNTL) & ~(1 << 20);
+		OUTREG( EXT_MEM_CNTL, memRefreshCntl | (1 << 20));
+ 
+		/* Reset memory */
+		OUTREG( MEM_SDRAM_MODE_REG,
+			INREG( MEM_SDRAM_MODE_REG)
+			& ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+		/* DLL */
+		radeon_pm_enable_dll(rinfo);
+
+		/* MLCK / YCLK sync */
+		radeon_pm_yclk_mclk_sync(rinfo);
+
+		/* Program Mode Register */
+		radeon_pm_program_mode_reg(rinfo, 0x2000, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x2001, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x2002, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x0132, 1);   
+		radeon_pm_program_mode_reg(rinfo, 0x0032, 1); 
+
+		/* Complete & re-enable refresh */
+		OUTREG( MEM_SDRAM_MODE_REG,
+			INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+		OUTREG(EXT_MEM_CNTL, memRefreshCntl);
+	}
+	/* And finally, the M7..M9 models, including M9+ (RV280) */
+	else if (rinfo->is_mobility) {
+
+		/* Disable refresh */
+		memRefreshCntl 	= INREG( MEM_REFRESH_CNTL)
+			& ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS;
+		OUTREG( MEM_REFRESH_CNTL, memRefreshCntl
+			| MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+		/* Reset memory */
+		OUTREG( MEM_SDRAM_MODE_REG,
+			INREG( MEM_SDRAM_MODE_REG)
+			& ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+		/* DLL */
+		radeon_pm_enable_dll(rinfo);
+
+		/* MLCK / YCLK sync */
+		radeon_pm_yclk_mclk_sync(rinfo);
+
+		/* M6, M7 and M9 so far ... */
+		if (rinfo->family <= CHIP_FAMILY_RV250) {
+			radeon_pm_program_mode_reg(rinfo, 0x2000, 1);
+			radeon_pm_program_mode_reg(rinfo, 0x2001, 1);
+			radeon_pm_program_mode_reg(rinfo, 0x2002, 1);
+			radeon_pm_program_mode_reg(rinfo, 0x0132, 1);
+			radeon_pm_program_mode_reg(rinfo, 0x0032, 1);
+		}
+		/* M9+ (iBook G4) */
+		else if (rinfo->family == CHIP_FAMILY_RV280) {
+			radeon_pm_program_mode_reg(rinfo, 0x2000, 1);
+			radeon_pm_program_mode_reg(rinfo, 0x0132, 1);
+			radeon_pm_program_mode_reg(rinfo, 0x0032, 1);
+		}
+
+		/* Complete & re-enable refresh */
+		OUTREG( MEM_SDRAM_MODE_REG,
+			INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+		OUTREG( MEM_REFRESH_CNTL, 	memRefreshCntl);
+	}
+
+	OUTREG( CRTC_GEN_CNTL, 		crtcGenCntl);
+	OUTREG( CRTC2_GEN_CNTL, 	crtcGenCntl2);
+	OUTREG( FP_GEN_CNTL, 		fp_gen_cntl);
+	OUTREG( FP2_GEN_CNTL, 		fp2_gen_cntl);
+
+	OUTREG( CRTC_MORE_CNTL, 	crtc_more_cntl);
+
+	mdelay( 15);
+}
+
+#ifdef CONFIG_PPC_OF
+
+static void radeon_pm_reset_pad_ctlr_strength(struct radeonfb_info *rinfo)
+{
+	u32 tmp, tmp2;
+	int i,j;
+
+	/* Reset the PAD_CTLR_STRENGTH & wait for it to be stable */
+	INREG(PAD_CTLR_STRENGTH);
+	OUTREG(PAD_CTLR_STRENGTH, INREG(PAD_CTLR_STRENGTH) & ~PAD_MANUAL_OVERRIDE);
+	tmp = INREG(PAD_CTLR_STRENGTH);
+	for (i = j = 0; i < 65; ++i) {
+		mdelay(1);
+		tmp2 = INREG(PAD_CTLR_STRENGTH);
+		if (tmp != tmp2) {
+			tmp = tmp2;
+			i = 0;
+			j++;
+			if (j > 10) {
+				printk(KERN_WARNING "radeon: PAD_CTLR_STRENGTH doesn't "
+				       "stabilize !\n");
+				break;
+			}
+		}
+	}
+}
+
+static void radeon_pm_all_ppls_off(struct radeonfb_info *rinfo)
+{
+	u32 tmp;
+
+	tmp = INPLL(pllPPLL_CNTL);
+	OUTPLL(pllPPLL_CNTL, tmp | 0x3);
+	tmp = INPLL(pllP2PLL_CNTL);
+	OUTPLL(pllP2PLL_CNTL, tmp | 0x3);
+	tmp = INPLL(pllSPLL_CNTL);
+	OUTPLL(pllSPLL_CNTL, tmp | 0x3);
+	tmp = INPLL(pllMPLL_CNTL);
+	OUTPLL(pllMPLL_CNTL, tmp | 0x3);
+}
+
+static void radeon_pm_start_mclk_sclk(struct radeonfb_info *rinfo)
+{
+	u32 tmp;
+
+	/* Switch SPLL to PCI source */
+	tmp = INPLL(pllSCLK_CNTL);
+	OUTPLL(pllSCLK_CNTL, tmp & ~SCLK_CNTL__SCLK_SRC_SEL_MASK);
+
+	/* Reconfigure SPLL charge pump, VCO gain, duty cycle */
+	tmp = INPLL(pllSPLL_CNTL);
+	OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+	radeon_pll_errata_after_data(rinfo);
+
+	/* Set SPLL feedback divider */
+	tmp = INPLL(pllM_SPLL_REF_FB_DIV);
+	tmp = (tmp & 0xff00fffful) | (rinfo->save_regs[77] & 0x00ff0000ul);
+	OUTPLL(pllM_SPLL_REF_FB_DIV, tmp);
+
+	/* Power up SPLL */
+	tmp = INPLL(pllSPLL_CNTL);
+	OUTPLL(pllSPLL_CNTL, tmp & ~1);
+	(void)INPLL(pllSPLL_CNTL);
+
+	mdelay(10);
+
+	/* Release SPLL reset */
+	tmp = INPLL(pllSPLL_CNTL);
+	OUTPLL(pllSPLL_CNTL, tmp & ~0x2);
+	(void)INPLL(pllSPLL_CNTL);
+
+	mdelay(10);
+
+	/* Select SCLK source  */
+	tmp = INPLL(pllSCLK_CNTL);
+	tmp &= ~SCLK_CNTL__SCLK_SRC_SEL_MASK;
+	tmp |= rinfo->save_regs[3] & SCLK_CNTL__SCLK_SRC_SEL_MASK;
+	OUTPLL(pllSCLK_CNTL, tmp);
+	(void)INPLL(pllSCLK_CNTL);
+
+	mdelay(10);
+
+	/* Reconfigure MPLL charge pump, VCO gain, duty cycle */
+	tmp = INPLL(pllMPLL_CNTL);
+	OUTREG8(CLOCK_CNTL_INDEX, pllMPLL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+	radeon_pll_errata_after_data(rinfo);
+
+	/* Set MPLL feedback divider */
+	tmp = INPLL(pllM_SPLL_REF_FB_DIV);
+	tmp = (tmp & 0xffff00fful) | (rinfo->save_regs[77] & 0x0000ff00ul);
+
+	OUTPLL(pllM_SPLL_REF_FB_DIV, tmp);
+	/* Power up MPLL */
+	tmp = INPLL(pllMPLL_CNTL);
+	OUTPLL(pllMPLL_CNTL, tmp & ~0x2);
+	(void)INPLL(pllMPLL_CNTL);
+
+	mdelay(10);
+
+	/* Un-reset MPLL */
+	tmp = INPLL(pllMPLL_CNTL);
+	OUTPLL(pllMPLL_CNTL, tmp & ~0x1);
+	(void)INPLL(pllMPLL_CNTL);
+
+	mdelay(10);
+
+	/* Select source for MCLK */
+	tmp = INPLL(pllMCLK_CNTL);
+	tmp |= rinfo->save_regs[2] & 0xffff;
+	OUTPLL(pllMCLK_CNTL, tmp);
+	(void)INPLL(pllMCLK_CNTL);
+
+	mdelay(10);
+}
+
+static void radeon_pm_m10_disable_spread_spectrum(struct radeonfb_info *rinfo)
+{
+	u32 r2ec;
+
+	/* GACK ! I though we didn't have a DDA on Radeon's anymore
+	 * here we rewrite with the same value, ... I suppose we clear
+	 * some bits that are already clear ? Or maybe this 0x2ec
+	 * register is something new ?
+	 */
+	mdelay(20);
+	r2ec = INREG(VGA_DDA_ON_OFF);
+	OUTREG(VGA_DDA_ON_OFF, r2ec);
+	mdelay(1);
+
+	/* Spread spectrum PLLL off */
+	OUTPLL(pllSSPLL_CNTL, 0xbf03);
+
+	/* Spread spectrum disabled */
+	OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90] & ~3);
+
+	/* The trace shows read & rewrite of LVDS_PLL_CNTL here with same
+	 * value, not sure what for...
+	 */
+
+	r2ec |= 0x3f0;
+	OUTREG(VGA_DDA_ON_OFF, r2ec);
+	mdelay(1);
+}
+
+static void radeon_pm_m10_enable_lvds_spread_spectrum(struct radeonfb_info *rinfo)
+{
+	u32 r2ec, tmp;
+
+	/* GACK (bis) ! I though we didn't have a DDA on Radeon's anymore
+	 * here we rewrite with the same value, ... I suppose we clear/set
+	 * some bits that are already clear/set ?
+	 */
+	r2ec = INREG(VGA_DDA_ON_OFF);
+	OUTREG(VGA_DDA_ON_OFF, r2ec);
+	mdelay(1);
+
+	/* Enable spread spectrum */
+	OUTPLL(pllSSPLL_CNTL, rinfo->save_regs[43] | 3);
+	mdelay(3);
+
+	OUTPLL(pllSSPLL_REF_DIV, rinfo->save_regs[44]);
+	OUTPLL(pllSSPLL_DIV_0, rinfo->save_regs[45]);
+	tmp = INPLL(pllSSPLL_CNTL);
+	OUTPLL(pllSSPLL_CNTL, tmp & ~0x2);
+	mdelay(6);
+	tmp = INPLL(pllSSPLL_CNTL);
+	OUTPLL(pllSSPLL_CNTL, tmp & ~0x1);
+	mdelay(5);
+
+       	OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90]);
+
+	r2ec |= 8;
+	OUTREG(VGA_DDA_ON_OFF, r2ec);
+	mdelay(20);
+
+	/* Enable LVDS interface */
+	tmp = INREG(LVDS_GEN_CNTL);
+	OUTREG(LVDS_GEN_CNTL, tmp | LVDS_EN);
+
+	/* Enable LVDS_PLL */
+	tmp = INREG(LVDS_PLL_CNTL);
+	tmp &= ~0x30000;
+	tmp |= 0x10000;
+	OUTREG(LVDS_PLL_CNTL, tmp);
+
+	OUTPLL(pllSCLK_MORE_CNTL, rinfo->save_regs[34]);
+	OUTPLL(pllSS_TST_CNTL, rinfo->save_regs[91]);
+
+	/* The trace reads that one here, waiting for something to settle down ? */
+	INREG(RBBM_STATUS);
+
+	/* Ugh ? SS_TST_DEC is supposed to be a read register in the
+	 * R300 register spec at least...
+	 */
+	tmp = INPLL(pllSS_TST_CNTL);
+	tmp |= 0x00400000;
+	OUTPLL(pllSS_TST_CNTL, tmp);
+}
+
+static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo)
+{
+	u32 tmp;
+
+	OUTREG8(CLOCK_CNTL_INDEX, pllHTOTAL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA, 0);
+	radeon_pll_errata_after_data(rinfo);
+
+	tmp = INPLL(pllVCLK_ECP_CNTL);
+	OUTPLL(pllVCLK_ECP_CNTL, tmp | 0x80);
+	mdelay(5);
+
+	tmp = INPLL(pllPPLL_REF_DIV);
+	tmp = (tmp & ~PPLL_REF_DIV_MASK) | rinfo->pll.ref_div;
+	OUTPLL(pllPPLL_REF_DIV, tmp);
+	INPLL(pllPPLL_REF_DIV);
+
+	/* Reconfigure SPLL charge pump, VCO gain, duty cycle,
+	 * probably useless since we already did it ...
+	 */
+	tmp = INPLL(pllPPLL_CNTL);
+	OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+	radeon_pll_errata_after_data(rinfo);
+
+	/* Restore our "reference" PPLL divider set by firmware
+	 * according to proper spread spectrum calculations
+	 */
+	OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]);
+
+	tmp = INPLL(pllPPLL_CNTL);
+	OUTPLL(pllPPLL_CNTL, tmp & ~0x2);
+	mdelay(5);
+
+	tmp = INPLL(pllPPLL_CNTL);
+	OUTPLL(pllPPLL_CNTL, tmp & ~0x1);
+	mdelay(5);
+
+	tmp = INPLL(pllVCLK_ECP_CNTL);
+	OUTPLL(pllVCLK_ECP_CNTL, tmp | 3);
+	mdelay(5);
+
+	tmp = INPLL(pllVCLK_ECP_CNTL);
+	OUTPLL(pllVCLK_ECP_CNTL, tmp | 3);
+	mdelay(5);
+
+	/* Switch pixel clock to firmware default div 0 */
+	OUTREG8(CLOCK_CNTL_INDEX+1, 0);
+	radeon_pll_errata_after_index(rinfo);
+	radeon_pll_errata_after_data(rinfo);
+}
+
+static void radeon_pm_m10_reconfigure_mc(struct radeonfb_info *rinfo)
+{
+	OUTREG(MC_CNTL, rinfo->save_regs[46]);
+	OUTREG(MC_INIT_GFX_LAT_TIMER, rinfo->save_regs[47]);
+	OUTREG(MC_INIT_MISC_LAT_TIMER, rinfo->save_regs[48]);
+	OUTREG(MEM_SDRAM_MODE_REG,
+	       rinfo->save_regs[35] & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+	OUTREG(MC_TIMING_CNTL, rinfo->save_regs[49]);
+	OUTREG(MEM_REFRESH_CNTL, rinfo->save_regs[42]);
+	OUTREG(MC_READ_CNTL_AB, rinfo->save_regs[50]);
+	OUTREG(MC_CHIP_IO_OE_CNTL_AB, rinfo->save_regs[52]);
+	OUTREG(MC_IOPAD_CNTL, rinfo->save_regs[51]);
+	OUTREG(MC_DEBUG, rinfo->save_regs[53]);
+
+	OUTMC(rinfo, ixR300_MC_MC_INIT_WR_LAT_TIMER, rinfo->save_regs[58]);
+	OUTMC(rinfo, ixR300_MC_IMP_CNTL, rinfo->save_regs[59]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_C0, rinfo->save_regs[60]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_C1, rinfo->save_regs[61]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_D0, rinfo->save_regs[62]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_D1, rinfo->save_regs[63]);
+	OUTMC(rinfo, ixR300_MC_BIST_CNTL_3, rinfo->save_regs[64]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_A0, rinfo->save_regs[65]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1, rinfo->save_regs[66]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_B0, rinfo->save_regs[67]);
+	OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1, rinfo->save_regs[68]);
+	OUTMC(rinfo, ixR300_MC_DEBUG_CNTL, rinfo->save_regs[69]);
+	OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]);
+	OUTMC(rinfo, ixR300_MC_IMP_CNTL_0, rinfo->save_regs[71]);
+	OUTMC(rinfo, ixR300_MC_ELPIDA_CNTL, rinfo->save_regs[72]);
+	OUTMC(rinfo, ixR300_MC_READ_CNTL_CD, rinfo->save_regs[96]);
+	OUTREG(MC_IND_INDEX, 0);
+}
+
+static void radeon_reinitialize_M10(struct radeonfb_info *rinfo)
+{
+	u32 tmp, i;
+
+	/* Restore a bunch of registers first */
+	OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+	OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+	OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+	OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+	OUTREG(OV0_BASE_ADDR, rinfo->save_regs[80]);
+	OUTREG(CONFIG_MEMSIZE, rinfo->video_ram);
+	OUTREG(BUS_CNTL, rinfo->save_regs[36]);
+	OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
+	OUTREG(MPP_TB_CONFIG, rinfo->save_regs[37]);
+	OUTREG(FCP_CNTL, rinfo->save_regs[38]);
+	OUTREG(RBBM_CNTL, rinfo->save_regs[39]);
+	OUTREG(DAC_CNTL, rinfo->save_regs[40]);
+	OUTREG(DAC_MACRO_CNTL, (INREG(DAC_MACRO_CNTL) & ~0x6) | 8);
+	OUTREG(DAC_MACRO_CNTL, (INREG(DAC_MACRO_CNTL) & ~0x6) | 8);
+
+	/* Hrm... */
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | DAC2_EXPAND_MODE);
+
+	/* Reset the PAD CTLR */
+	radeon_pm_reset_pad_ctlr_strength(rinfo);
+
+	/* Some PLLs are Read & written identically in the trace here...
+	 * I suppose it's actually to switch them all off & reset,
+	 * let's assume off is what we want. I'm just doing that for all major PLLs now.
+	 */
+	radeon_pm_all_ppls_off(rinfo);
+
+	/* Clear tiling, reset swappers */
+	INREG(SURFACE_CNTL);
+	OUTREG(SURFACE_CNTL, 0);
+
+	/* Some black magic with TV_DAC_CNTL, we should restore those from backups
+	 * rather than hard coding...
+	 */
+	tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_BGADJ_MASK;
+	tmp |= 8 << TV_DAC_CNTL_BGADJ__SHIFT;
+	OUTREG(TV_DAC_CNTL, tmp);
+
+	tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_DACADJ_MASK;
+	tmp |= 7 << TV_DAC_CNTL_DACADJ__SHIFT;
+	OUTREG(TV_DAC_CNTL, tmp);
+
+	/* More registers restored */
+	OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+	OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]);
+	OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+
+	/* Hrmmm ... What is that ? */
+	tmp = rinfo->save_regs[1]
+		& ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK |
+		    CLK_PWRMGT_CNTL__MC_BUSY);
+	OUTPLL(pllCLK_PWRMGT_CNTL, tmp);
+
+	OUTREG(PAD_CTLR_MISC, rinfo->save_regs[56]);
+	OUTREG(FW_CNTL, rinfo->save_regs[57]);
+	OUTREG(HDP_DEBUG, rinfo->save_regs[96]);
+	OUTREG(PAMAC0_DLY_CNTL, rinfo->save_regs[54]);
+	OUTREG(PAMAC1_DLY_CNTL, rinfo->save_regs[55]);
+	OUTREG(PAMAC2_DLY_CNTL, rinfo->save_regs[79]);
+
+	/* Restore Memory Controller configuration */
+	radeon_pm_m10_reconfigure_mc(rinfo);
+
+	/* Make sure CRTC's dont touch memory */
+	OUTREG(CRTC_GEN_CNTL, INREG(CRTC_GEN_CNTL)
+	       | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B);
+	OUTREG(CRTC2_GEN_CNTL, INREG(CRTC2_GEN_CNTL)
+	       | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B);
+	mdelay(30);
+
+	/* Disable SDRAM refresh */
+	OUTREG(MEM_REFRESH_CNTL, INREG(MEM_REFRESH_CNTL)
+	       | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+	/* Restore XTALIN routing (CLK_PIN_CNTL) */
+	OUTPLL(pllCLK_PIN_CNTL, rinfo->save_regs[4]);
+
+	/* Switch MCLK, YCLK and SCLK PLLs to PCI source & force them ON */
+	tmp = rinfo->save_regs[2] & 0xff000000;
+	tmp |=	MCLK_CNTL__FORCE_MCLKA |
+		MCLK_CNTL__FORCE_MCLKB |
+		MCLK_CNTL__FORCE_YCLKA |
+		MCLK_CNTL__FORCE_YCLKB |
+		MCLK_CNTL__FORCE_MC;
+	OUTPLL(pllMCLK_CNTL, tmp);
+
+	/* Force all clocks on in SCLK */
+	tmp = INPLL(pllSCLK_CNTL);
+	tmp |=	SCLK_CNTL__FORCE_DISP2|
+		SCLK_CNTL__FORCE_CP|
+		SCLK_CNTL__FORCE_HDP|
+		SCLK_CNTL__FORCE_DISP1|
+		SCLK_CNTL__FORCE_TOP|
+		SCLK_CNTL__FORCE_E2|
+		SCLK_CNTL__FORCE_SE|
+		SCLK_CNTL__FORCE_IDCT|
+		SCLK_CNTL__FORCE_VIP|
+		SCLK_CNTL__FORCE_PB|
+		SCLK_CNTL__FORCE_TAM|
+		SCLK_CNTL__FORCE_TDM|
+		SCLK_CNTL__FORCE_RB|
+		SCLK_CNTL__FORCE_TV_SCLK|
+		SCLK_CNTL__FORCE_SUBPIC|
+		SCLK_CNTL__FORCE_OV0;
+	tmp |=	SCLK_CNTL__CP_MAX_DYN_STOP_LAT  |
+		SCLK_CNTL__HDP_MAX_DYN_STOP_LAT |
+		SCLK_CNTL__TV_MAX_DYN_STOP_LAT  |
+		SCLK_CNTL__E2_MAX_DYN_STOP_LAT  |
+		SCLK_CNTL__SE_MAX_DYN_STOP_LAT  |
+		SCLK_CNTL__IDCT_MAX_DYN_STOP_LAT|
+		SCLK_CNTL__VIP_MAX_DYN_STOP_LAT |
+		SCLK_CNTL__RE_MAX_DYN_STOP_LAT  |
+		SCLK_CNTL__PB_MAX_DYN_STOP_LAT  |
+		SCLK_CNTL__TAM_MAX_DYN_STOP_LAT |
+		SCLK_CNTL__TDM_MAX_DYN_STOP_LAT |
+		SCLK_CNTL__RB_MAX_DYN_STOP_LAT;
+	OUTPLL(pllSCLK_CNTL, tmp);
+
+	OUTPLL(pllVCLK_ECP_CNTL, 0);
+	OUTPLL(pllPIXCLKS_CNTL, 0);
+	OUTPLL(pllMCLK_MISC,
+	       MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT |
+	       MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT);
+
+	mdelay(5);
+
+	/* Restore the M_SPLL_REF_FB_DIV, MPLL_AUX_CNTL and SPLL_AUX_CNTL values */
+	OUTPLL(pllM_SPLL_REF_FB_DIV, rinfo->save_regs[77]);
+	OUTPLL(pllMPLL_AUX_CNTL, rinfo->save_regs[75]);
+	OUTPLL(pllSPLL_AUX_CNTL, rinfo->save_regs[76]);
+
+	/* Now restore the major PLLs settings, keeping them off & reset though */
+	OUTPLL(pllPPLL_CNTL, rinfo->save_regs[93] | 0x3);
+	OUTPLL(pllP2PLL_CNTL, rinfo->save_regs[8] | 0x3);
+	OUTPLL(pllMPLL_CNTL, rinfo->save_regs[73] | 0x03);
+	OUTPLL(pllSPLL_CNTL, rinfo->save_regs[74] | 0x03);
+
+	/* Restore MC DLL state and switch it off/reset too  */
+	OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]);
+
+	/* Switch MDLL off & reset */
+	OUTPLL(pllMDLL_RDCKA, rinfo->save_regs[98] | 0xff);
+	mdelay(5);
+
+	/* Setup some black magic bits in PLL_PWRMGT_CNTL. Hrm... we saved
+	 * 0xa1100007... and MacOS writes 0xa1000007 ..
+	 */
+	OUTPLL(pllPLL_PWRMGT_CNTL, rinfo->save_regs[0]);
+
+	/* Restore more stuffs */
+	OUTPLL(pllHTOTAL_CNTL, 0);
+	OUTPLL(pllHTOTAL2_CNTL, 0);
+
+	/* More PLL initial configuration */
+	tmp = INPLL(pllSCLK_CNTL2); /* What for ? */
+	OUTPLL(pllSCLK_CNTL2, tmp);
+
+	tmp = INPLL(pllSCLK_MORE_CNTL);
+	tmp |= 	SCLK_MORE_CNTL__FORCE_DISPREGS |	/* a guess */
+		SCLK_MORE_CNTL__FORCE_MC_GUI |
+		SCLK_MORE_CNTL__FORCE_MC_HOST;
+	OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+	/* Now we actually start MCLK and SCLK */
+	radeon_pm_start_mclk_sclk(rinfo);
+
+	/* Full reset sdrams, this also re-inits the MDLL */
+	radeon_pm_full_reset_sdram(rinfo);
+
+	/* Fill palettes */
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x20);
+	for (i=0; i<256; i++)
+		OUTREG(PALETTE_30_DATA, 0x15555555);
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~20);
+	udelay(20);
+	for (i=0; i<256; i++)
+		OUTREG(PALETTE_30_DATA, 0x15555555);
+
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~0x20);
+	mdelay(3);
+
+	/* Restore TMDS */
+	OUTREG(FP_GEN_CNTL, rinfo->save_regs[82]);
+	OUTREG(FP2_GEN_CNTL, rinfo->save_regs[83]);
+
+	/* Set LVDS registers but keep interface & pll down */
+	OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11] &
+	       ~(LVDS_EN | LVDS_ON | LVDS_DIGON | LVDS_BLON | LVDS_BL_MOD_EN));
+	OUTREG(LVDS_PLL_CNTL, (rinfo->save_regs[12] & ~0xf0000) | 0x20000);
+
+	OUTREG(DISP_OUTPUT_CNTL, rinfo->save_regs[86]);
+
+	/* Restore GPIOPAD state */
+	OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
+	OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
+	OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
+
+	/* write some stuff to the framebuffer... */
+	for (i = 0; i < 0x8000; ++i)
+		writeb(0, rinfo->fb_base + i);
+
+	mdelay(40);
+	OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_DIGON | LVDS_ON);
+	mdelay(40);
+
+	/* Restore a few more things */
+	OUTREG(GRPH_BUFFER_CNTL, rinfo->save_regs[94]);
+	OUTREG(GRPH2_BUFFER_CNTL, rinfo->save_regs[95]);
+
+	/* Take care of spread spectrum & PPLLs now */
+	radeon_pm_m10_disable_spread_spectrum(rinfo);
+	radeon_pm_restore_pixel_pll(rinfo);
+
+	/* GRRRR... I can't figure out the proper LVDS power sequence, and the
+	 * code I have for blank/unblank doesn't quite work on some laptop models
+	 * it seems ... Hrm. What I have here works most of the time ...
+	 */
+	radeon_pm_m10_enable_lvds_spread_spectrum(rinfo);
+}
+
+static void radeon_pm_m9p_reconfigure_mc(struct radeonfb_info *rinfo)
+{
+	OUTREG(MC_CNTL, rinfo->save_regs[46]);
+	OUTREG(MC_INIT_GFX_LAT_TIMER, rinfo->save_regs[47]);
+	OUTREG(MC_INIT_MISC_LAT_TIMER, rinfo->save_regs[48]);
+	OUTREG(MEM_SDRAM_MODE_REG,
+	       rinfo->save_regs[35] & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+	OUTREG(MC_TIMING_CNTL, rinfo->save_regs[49]);
+	OUTREG(MC_READ_CNTL_AB, rinfo->save_regs[50]);
+	OUTREG(MEM_REFRESH_CNTL, rinfo->save_regs[42]);
+	OUTREG(MC_IOPAD_CNTL, rinfo->save_regs[51]);
+	OUTREG(MC_DEBUG, rinfo->save_regs[53]);
+	OUTREG(MC_CHIP_IO_OE_CNTL_AB, rinfo->save_regs[52]);
+
+	OUTMC(rinfo, ixMC_IMP_CNTL, rinfo->save_regs[59] /*0x00f460d6*/);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_A0, rinfo->save_regs[65] /*0xfecfa666*/);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, rinfo->save_regs[66] /*0x141555ff*/);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_B0, rinfo->save_regs[67] /*0xfecfa666*/);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, rinfo->save_regs[68] /*0x141555ff*/);
+	OUTMC(rinfo, ixMC_IMP_CNTL_0, rinfo->save_regs[71] /*0x00009249*/);
+	OUTREG(MC_IND_INDEX, 0);
+	OUTREG(CONFIG_MEMSIZE, rinfo->video_ram);
+
+	mdelay(20);
+}
+
+static void radeon_reinitialize_M9P(struct radeonfb_info *rinfo)
+{
+	u32 tmp, i;
+
+	/* Restore a bunch of registers first */
+	OUTREG(SURFACE_CNTL, rinfo->save_regs[29]);
+	OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+	OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+	OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+	OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+	OUTREG(OV0_BASE_ADDR, rinfo->save_regs[80]);
+	OUTREG(BUS_CNTL, rinfo->save_regs[36]);
+	OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
+	OUTREG(MPP_TB_CONFIG, rinfo->save_regs[37]);
+	OUTREG(FCP_CNTL, rinfo->save_regs[38]);
+	OUTREG(RBBM_CNTL, rinfo->save_regs[39]);
+
+	OUTREG(DAC_CNTL, rinfo->save_regs[40]);
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | DAC2_EXPAND_MODE);
+
+	/* Reset the PAD CTLR */
+	radeon_pm_reset_pad_ctlr_strength(rinfo);
+
+	/* Some PLLs are Read & written identically in the trace here...
+	 * I suppose it's actually to switch them all off & reset,
+	 * let's assume off is what we want. I'm just doing that for all major PLLs now.
+	 */
+	radeon_pm_all_ppls_off(rinfo);
+
+	/* Clear tiling, reset swappers */
+	INREG(SURFACE_CNTL);
+	OUTREG(SURFACE_CNTL, 0);
+
+	/* Some black magic with TV_DAC_CNTL, we should restore those from backups
+	 * rather than hard coding...
+	 */
+	tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_BGADJ_MASK;
+	tmp |= 6 << TV_DAC_CNTL_BGADJ__SHIFT;
+	OUTREG(TV_DAC_CNTL, tmp);
+
+	tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_DACADJ_MASK;
+	tmp |= 6 << TV_DAC_CNTL_DACADJ__SHIFT;
+	OUTREG(TV_DAC_CNTL, tmp);
+
+	OUTPLL(pllAGP_PLL_CNTL, rinfo->save_regs[78]);
+
+	OUTREG(PAMAC0_DLY_CNTL, rinfo->save_regs[54]);
+	OUTREG(PAMAC1_DLY_CNTL, rinfo->save_regs[55]);
+	OUTREG(PAMAC2_DLY_CNTL, rinfo->save_regs[79]);
+
+	OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+	OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]); /* MacOS sets that to 0 !!! */
+	OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+
+	tmp  = rinfo->save_regs[1]
+		& ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK |
+		    CLK_PWRMGT_CNTL__MC_BUSY);
+	OUTPLL(pllCLK_PWRMGT_CNTL, tmp);
+
+	OUTREG(FW_CNTL, rinfo->save_regs[57]);
+
+	/* Disable SDRAM refresh */
+	OUTREG(MEM_REFRESH_CNTL, INREG(MEM_REFRESH_CNTL)
+	       | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+	/* Restore XTALIN routing (CLK_PIN_CNTL) */
+       	OUTPLL(pllCLK_PIN_CNTL, rinfo->save_regs[4]);
+
+	/* Force MCLK to be PCI sourced and forced ON */
+	tmp = rinfo->save_regs[2] & 0xff000000;
+	tmp |=	MCLK_CNTL__FORCE_MCLKA |
+		MCLK_CNTL__FORCE_MCLKB |
+		MCLK_CNTL__FORCE_YCLKA |
+		MCLK_CNTL__FORCE_YCLKB |
+		MCLK_CNTL__FORCE_MC    |
+		MCLK_CNTL__FORCE_AIC;
+	OUTPLL(pllMCLK_CNTL, tmp);
+
+	/* Force SCLK to be PCI sourced with a bunch forced */
+	tmp =	0 |
+		SCLK_CNTL__FORCE_DISP2|
+		SCLK_CNTL__FORCE_CP|
+		SCLK_CNTL__FORCE_HDP|
+		SCLK_CNTL__FORCE_DISP1|
+		SCLK_CNTL__FORCE_TOP|
+		SCLK_CNTL__FORCE_E2|
+		SCLK_CNTL__FORCE_SE|
+		SCLK_CNTL__FORCE_IDCT|
+		SCLK_CNTL__FORCE_VIP|
+		SCLK_CNTL__FORCE_RE|
+		SCLK_CNTL__FORCE_PB|
+		SCLK_CNTL__FORCE_TAM|
+		SCLK_CNTL__FORCE_TDM|
+		SCLK_CNTL__FORCE_RB;
+	OUTPLL(pllSCLK_CNTL, tmp);
+
+	/* Clear VCLK_ECP_CNTL & PIXCLKS_CNTL  */
+	OUTPLL(pllVCLK_ECP_CNTL, 0);
+	OUTPLL(pllPIXCLKS_CNTL, 0);
+
+	/* Setup MCLK_MISC, non dynamic mode */
+	OUTPLL(pllMCLK_MISC,
+	       MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT |
+	       MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT);
+
+	mdelay(5);
+
+	/* Set back the default clock dividers */
+	OUTPLL(pllM_SPLL_REF_FB_DIV, rinfo->save_regs[77]);
+	OUTPLL(pllMPLL_AUX_CNTL, rinfo->save_regs[75]);
+	OUTPLL(pllSPLL_AUX_CNTL, rinfo->save_regs[76]);
+
+	/* PPLL and P2PLL default values & off */
+	OUTPLL(pllPPLL_CNTL, rinfo->save_regs[93] | 0x3);
+	OUTPLL(pllP2PLL_CNTL, rinfo->save_regs[8] | 0x3);
+
+	/* S and M PLLs are reset & off, configure them */
+	OUTPLL(pllMPLL_CNTL, rinfo->save_regs[73] | 0x03);
+	OUTPLL(pllSPLL_CNTL, rinfo->save_regs[74] | 0x03);
+
+	/* Default values for MDLL ... fixme */
+	OUTPLL(pllMDLL_CKO, 0x9c009c);
+	OUTPLL(pllMDLL_RDCKA, 0x08830883);
+	OUTPLL(pllMDLL_RDCKB, 0x08830883);
+	mdelay(5);
+
+	/* Restore PLL_PWRMGT_CNTL */ // XXXX
+	tmp = rinfo->save_regs[0];
+	tmp &= ~PLL_PWRMGT_CNTL_SU_SCLK_USE_BCLK;
+	tmp |= PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK;
+	OUTPLL(PLL_PWRMGT_CNTL,  tmp);
+
+	/* Clear HTOTAL_CNTL & HTOTAL2_CNTL */
+	OUTPLL(pllHTOTAL_CNTL, 0);
+	OUTPLL(pllHTOTAL2_CNTL, 0);
+
+	/* All outputs off */
+	OUTREG(CRTC_GEN_CNTL, 0x04000000);
+	OUTREG(CRTC2_GEN_CNTL, 0x04000000);
+	OUTREG(FP_GEN_CNTL, 0x00004008);
+	OUTREG(FP2_GEN_CNTL, 0x00000008);
+	OUTREG(LVDS_GEN_CNTL, 0x08000008);
+
+	/* Restore Memory Controller configuration */
+	radeon_pm_m9p_reconfigure_mc(rinfo);
+
+	/* Now we actually start MCLK and SCLK */
+	radeon_pm_start_mclk_sclk(rinfo);
+
+	/* Full reset sdrams, this also re-inits the MDLL */
+	radeon_pm_full_reset_sdram(rinfo);
+
+	/* Fill palettes */
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x20);
+	for (i=0; i<256; i++)
+		OUTREG(PALETTE_30_DATA, 0x15555555);
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~20);
+	udelay(20);
+	for (i=0; i<256; i++)
+		OUTREG(PALETTE_30_DATA, 0x15555555);
+
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~0x20);
+	mdelay(3);
+
+	/* Restore TV stuff, make sure TV DAC is down */
+	OUTREG(TV_MASTER_CNTL, rinfo->save_regs[88]);
+	OUTREG(TV_DAC_CNTL, rinfo->save_regs[13] | 0x07000000);
+
+	/* Restore GPIOS. MacOS does some magic here with one of the GPIO bits,
+	 * possibly related to the weird PLL related workarounds and to the
+	 * fact that CLK_PIN_CNTL is tweaked in ways I don't fully understand,
+	 * but we keep things the simple way here
+	 */
+	OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
+	OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
+	OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
+
+	/* Now do things with SCLK_MORE_CNTL. Force bits are already set, copy
+	 * high bits from backup
+	 */
+	tmp = INPLL(pllSCLK_MORE_CNTL) & 0x0000ffff;
+	tmp |= rinfo->save_regs[34] & 0xffff0000;
+	tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS;
+	OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+	tmp = INPLL(pllSCLK_MORE_CNTL) & 0x0000ffff;
+	tmp |= rinfo->save_regs[34] & 0xffff0000;
+	tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS;
+	OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+	OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11] &
+	       ~(LVDS_EN | LVDS_ON | LVDS_DIGON | LVDS_BLON | LVDS_BL_MOD_EN));
+	OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_BLON);
+	OUTREG(LVDS_PLL_CNTL, (rinfo->save_regs[12] & ~0xf0000) | 0x20000);
+	mdelay(20);
+
+	/* write some stuff to the framebuffer... */
+	for (i = 0; i < 0x8000; ++i)
+		writeb(0, rinfo->fb_base + i);
+
+	OUTREG(0x2ec, 0x6332a020);
+	OUTPLL(pllSSPLL_REF_DIV, rinfo->save_regs[44] /*0x3f */);
+	OUTPLL(pllSSPLL_DIV_0, rinfo->save_regs[45] /*0x000081bb */);
+	tmp = INPLL(pllSSPLL_CNTL);
+	tmp &= ~2;
+	OUTPLL(pllSSPLL_CNTL, tmp);
+	mdelay(6);
+	tmp &= ~1;
+	OUTPLL(pllSSPLL_CNTL, tmp);
+	mdelay(5);
+	tmp |= 3;
+	OUTPLL(pllSSPLL_CNTL, tmp);
+	mdelay(5);
+
+	OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90] & ~3);/*0x0020300c*/
+	OUTREG(0x2ec, 0x6332a3f0);
+	mdelay(17);
+
+	OUTPLL(pllPPLL_REF_DIV, rinfo->pll.ref_div);;
+	OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]);
+
+	mdelay(40);
+	OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_DIGON | LVDS_ON);
+	mdelay(40);
+
+	/* Restore a few more things */
+	OUTREG(GRPH_BUFFER_CNTL, rinfo->save_regs[94]);
+	OUTREG(GRPH2_BUFFER_CNTL, rinfo->save_regs[95]);
+
+	/* Restore PPLL, spread spectrum & LVDS */
+	radeon_pm_m10_disable_spread_spectrum(rinfo);
+	radeon_pm_restore_pixel_pll(rinfo);
+	radeon_pm_m10_enable_lvds_spread_spectrum(rinfo);
+}
+
+#if 0 /* Not ready yet */
+static void radeon_reinitialize_QW(struct radeonfb_info *rinfo)
+{
+	int i;
+	u32 tmp, tmp2;
+	u32 cko, cka, ckb;
+	u32 cgc, cec, c2gc;
+
+	OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+	OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+	OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+	OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+	OUTREG(BUS_CNTL, rinfo->save_regs[36]);
+	OUTREG(RBBM_CNTL, rinfo->save_regs[39]);
+
+	INREG(PAD_CTLR_STRENGTH);
+	OUTREG(PAD_CTLR_STRENGTH, INREG(PAD_CTLR_STRENGTH) & ~0x10000);
+	for (i = 0; i < 65; ++i) {
+		mdelay(1);
+		INREG(PAD_CTLR_STRENGTH);
+	}
+
+	OUTREG(DISP_TEST_DEBUG_CNTL, INREG(DISP_TEST_DEBUG_CNTL) | 0x10000000);
+	OUTREG(OV0_FLAG_CNTRL, INREG(OV0_FLAG_CNTRL) | 0x100);
+	OUTREG(CRTC_GEN_CNTL, INREG(CRTC_GEN_CNTL));
+	OUTREG(DAC_CNTL, 0xff00410a);
+	OUTREG(CRTC2_GEN_CNTL, INREG(CRTC2_GEN_CNTL));
+	OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x4000);
+
+	OUTREG(SURFACE_CNTL, rinfo->save_regs[29]);
+	OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+	OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]);
+	OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_A0, 0xf7bb4433);
+	OUTREG(MC_IND_INDEX, 0);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_B0, 0xf7bb4433);
+	OUTREG(MC_IND_INDEX, 0);
+
+	OUTREG(CRTC_MORE_CNTL, INREG(CRTC_MORE_CNTL));
+
+	tmp = INPLL(pllVCLK_ECP_CNTL);
+	OUTPLL(pllVCLK_ECP_CNTL, tmp);
+	tmp = INPLL(pllPIXCLKS_CNTL);
+	OUTPLL(pllPIXCLKS_CNTL, tmp);
+
+	OUTPLL(MCLK_CNTL, 0xaa3f0000);
+	OUTPLL(SCLK_CNTL, 0xffff0000);
+	OUTPLL(pllMPLL_AUX_CNTL, 6);
+	OUTPLL(pllSPLL_AUX_CNTL, 1);
+	OUTPLL(MDLL_CKO, 0x9f009f);
+	OUTPLL(MDLL_RDCKA, 0x830083);
+	OUTPLL(pllMDLL_RDCKB, 0x830083);
+	OUTPLL(PPLL_CNTL, 0xa433);
+	OUTPLL(P2PLL_CNTL, 0xa433);
+	OUTPLL(MPLL_CNTL, 0x0400a403);
+	OUTPLL(SPLL_CNTL, 0x0400a433);
+
+	tmp = INPLL(M_SPLL_REF_FB_DIV);
+	OUTPLL(M_SPLL_REF_FB_DIV, tmp);
+	tmp = INPLL(M_SPLL_REF_FB_DIV);
+	OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0xc);
+	INPLL(M_SPLL_REF_FB_DIV);
+
+	tmp = INPLL(MPLL_CNTL);
+	OUTREG8(CLOCK_CNTL_INDEX, MPLL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+	radeon_pll_errata_after_data(rinfo);
+
+	tmp = INPLL(M_SPLL_REF_FB_DIV);
+	OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x5900);
+
+	tmp = INPLL(MPLL_CNTL);
+	OUTPLL(MPLL_CNTL, tmp & ~0x2);
+	mdelay(1);
+	tmp = INPLL(MPLL_CNTL);
+	OUTPLL(MPLL_CNTL, tmp & ~0x1);
+	mdelay(10);
+
+	OUTPLL(MCLK_CNTL, 0xaa3f1212);
+	mdelay(1);
+
+	INPLL(M_SPLL_REF_FB_DIV);
+	INPLL(MCLK_CNTL);
+	INPLL(M_SPLL_REF_FB_DIV);
+
+	tmp = INPLL(SPLL_CNTL);
+	OUTREG8(CLOCK_CNTL_INDEX, SPLL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+	radeon_pll_errata_after_data(rinfo);
+
+	tmp = INPLL(M_SPLL_REF_FB_DIV);
+	OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x780000);
+
+	tmp = INPLL(SPLL_CNTL);
+	OUTPLL(SPLL_CNTL, tmp & ~0x1);
+	mdelay(1);
+	tmp = INPLL(SPLL_CNTL);
+	OUTPLL(SPLL_CNTL, tmp & ~0x2);
+	mdelay(10);
+
+	tmp = INPLL(SCLK_CNTL);
+	OUTPLL(SCLK_CNTL, tmp | 2);
+	mdelay(1);
+
+	cko = INPLL(pllMDLL_CKO);
+	cka = INPLL(pllMDLL_RDCKA);
+	ckb = INPLL(pllMDLL_RDCKB);
+
+	cko &= ~(MDLL_CKO__MCKOA_SLEEP | MDLL_CKO__MCKOB_SLEEP);
+	OUTPLL(pllMDLL_CKO, cko);
+	mdelay(1);
+	cko &= ~(MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET);
+	OUTPLL(pllMDLL_CKO, cko);
+	mdelay(5);
+
+	cka &= ~(MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP);
+	OUTPLL(pllMDLL_RDCKA, cka);
+	mdelay(1);
+	cka &= ~(MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET);
+	OUTPLL(pllMDLL_RDCKA, cka);
+	mdelay(5);
+
+	ckb &= ~(MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP);
+	OUTPLL(pllMDLL_RDCKB, ckb);
+	mdelay(1);
+	ckb &= ~(MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET);
+	OUTPLL(pllMDLL_RDCKB, ckb);
+	mdelay(5);
+
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, 0x151550ff);
+	OUTREG(MC_IND_INDEX, 0);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, 0x151550ff);
+	OUTREG(MC_IND_INDEX, 0);
+	mdelay(1);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, 0x141550ff);
+	OUTREG(MC_IND_INDEX, 0);
+	OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, 0x141550ff);
+	OUTREG(MC_IND_INDEX, 0);
+	mdelay(1);
+
+	OUTPLL(pllHTOTAL_CNTL, 0);
+	OUTPLL(pllHTOTAL2_CNTL, 0);
+
+	OUTREG(MEM_CNTL, 0x29002901);
+	OUTREG(MEM_SDRAM_MODE_REG, 0x45320032);	/* XXX use save_regs[35]? */
+	OUTREG(EXT_MEM_CNTL, 0x1a394333);
+	OUTREG(MEM_IO_CNTL_A1, 0x0aac0aac);
+	OUTREG(MEM_INIT_LATENCY_TIMER, 0x34444444);
+	OUTREG(MEM_REFRESH_CNTL, 0x1f1f7218);	/* XXX or save_regs[42]? */
+	OUTREG(MC_DEBUG, 0);
+	OUTREG(MEM_IO_OE_CNTL, 0x04300430);
+
+	OUTMC(rinfo, ixMC_IMP_CNTL, 0x00f460d6);
+	OUTREG(MC_IND_INDEX, 0);
+	OUTMC(rinfo, ixMC_IMP_CNTL_0, 0x00009249);
+	OUTREG(MC_IND_INDEX, 0);
+
+	OUTREG(CONFIG_MEMSIZE, rinfo->video_ram);
+
+	radeon_pm_full_reset_sdram(rinfo);
+
+	INREG(FP_GEN_CNTL);
+	OUTREG(TMDS_CNTL, 0x01000000);	/* XXX ? */
+	tmp = INREG(FP_GEN_CNTL);
+	tmp |= FP_CRTC_DONT_SHADOW_HEND | FP_CRTC_DONT_SHADOW_VPAR | 0x200;
+	OUTREG(FP_GEN_CNTL, tmp);
+
+	tmp = INREG(DISP_OUTPUT_CNTL);
+	tmp &= ~0x400;
+	OUTREG(DISP_OUTPUT_CNTL, tmp);
+
+	OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]);
+	OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]);
+	OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]);
+
+	tmp = INPLL(MCLK_MISC);
+	tmp |= MCLK_MISC__MC_MCLK_DYN_ENABLE | MCLK_MISC__IO_MCLK_DYN_ENABLE;
+	OUTPLL(MCLK_MISC, tmp);
+
+	tmp = INPLL(SCLK_CNTL);
+	OUTPLL(SCLK_CNTL, tmp);
+
+	OUTREG(CRTC_MORE_CNTL, 0);
+	OUTREG8(CRTC_GEN_CNTL+1, 6);
+	OUTREG8(CRTC_GEN_CNTL+3, 1);
+	OUTREG(CRTC_PITCH, 32);
+
+	tmp = INPLL(VCLK_ECP_CNTL);
+	OUTPLL(VCLK_ECP_CNTL, tmp);
+
+	tmp = INPLL(PPLL_CNTL);
+	OUTPLL(PPLL_CNTL, tmp);
+
+	/* palette stuff and BIOS_1_SCRATCH... */
+
+	tmp = INREG(FP_GEN_CNTL);
+	tmp2 = INREG(TMDS_TRANSMITTER_CNTL);
+	tmp |= 2;
+	OUTREG(FP_GEN_CNTL, tmp);
+	mdelay(5);
+	OUTREG(FP_GEN_CNTL, tmp);
+	mdelay(5);
+	OUTREG(TMDS_TRANSMITTER_CNTL, tmp2);
+	OUTREG(CRTC_MORE_CNTL, 0);
+	mdelay(20);
+
+	tmp = INREG(CRTC_MORE_CNTL);
+	OUTREG(CRTC_MORE_CNTL, tmp);
+
+	cgc = INREG(CRTC_GEN_CNTL);
+	cec = INREG(CRTC_EXT_CNTL);
+	c2gc = INREG(CRTC2_GEN_CNTL);
+
+	OUTREG(CRTC_H_SYNC_STRT_WID, 0x008e0580);
+	OUTREG(CRTC_H_TOTAL_DISP, 0x009f00d2);
+	OUTREG8(CLOCK_CNTL_INDEX, HTOTAL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA, 0);
+	radeon_pll_errata_after_data(rinfo);
+	OUTREG(CRTC_V_SYNC_STRT_WID, 0x00830403);
+	OUTREG(CRTC_V_TOTAL_DISP, 0x03ff0429);
+	OUTREG(FP_CRTC_H_TOTAL_DISP, 0x009f0033);
+	OUTREG(FP_H_SYNC_STRT_WID, 0x008e0080);
+	OUTREG(CRT_CRTC_H_SYNC_STRT_WID, 0x008e0080);
+	OUTREG(FP_CRTC_V_TOTAL_DISP, 0x03ff002a);
+	OUTREG(FP_V_SYNC_STRT_WID, 0x00830004);
+	OUTREG(CRT_CRTC_V_SYNC_STRT_WID, 0x00830004);
+	OUTREG(FP_HORZ_VERT_ACTIVE, 0x009f03ff);
+	OUTREG(FP_HORZ_STRETCH, 0);
+	OUTREG(FP_VERT_STRETCH, 0);
+	OUTREG(OVR_CLR, 0);
+	OUTREG(OVR_WID_LEFT_RIGHT, 0);
+	OUTREG(OVR_WID_TOP_BOTTOM, 0);
+
+	tmp = INPLL(PPLL_REF_DIV);
+	tmp = (tmp & ~PPLL_REF_DIV_MASK) | rinfo->pll.ref_div;
+	OUTPLL(PPLL_REF_DIV, tmp);
+	INPLL(PPLL_REF_DIV);
+
+	OUTREG8(CLOCK_CNTL_INDEX, PPLL_CNTL + PLL_WR_EN);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG8(CLOCK_CNTL_DATA + 1, 0xbc);
+	radeon_pll_errata_after_data(rinfo);
+
+	tmp = INREG(CLOCK_CNTL_INDEX);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG(CLOCK_CNTL_INDEX, tmp & 0xff);
+	radeon_pll_errata_after_index(rinfo);
+	radeon_pll_errata_after_data(rinfo);
+
+	OUTPLL(PPLL_DIV_0, 0x48090);
+
+	tmp = INPLL(PPLL_CNTL);
+	OUTPLL(PPLL_CNTL, tmp & ~0x2);
+	mdelay(1);
+	tmp = INPLL(PPLL_CNTL);
+	OUTPLL(PPLL_CNTL, tmp & ~0x1);
+	mdelay(10);
+
+	tmp = INPLL(VCLK_ECP_CNTL);
+	OUTPLL(VCLK_ECP_CNTL, tmp | 3);
+	mdelay(1);
+
+	tmp = INPLL(VCLK_ECP_CNTL);
+	OUTPLL(VCLK_ECP_CNTL, tmp);
+
+	c2gc |= CRTC2_DISP_REQ_EN_B;
+	OUTREG(CRTC2_GEN_CNTL, c2gc);
+	cgc |= CRTC_EN;
+	OUTREG(CRTC_GEN_CNTL, cgc);
+	OUTREG(CRTC_EXT_CNTL, cec);
+	OUTREG(CRTC_PITCH, 0xa0);
+	OUTREG(CRTC_OFFSET, 0);
+	OUTREG(CRTC_OFFSET_CNTL, 0);
+
+	OUTREG(GRPH_BUFFER_CNTL, 0x20117c7c);
+	OUTREG(GRPH2_BUFFER_CNTL, 0x00205c5c);
+
+	tmp2 = INREG(FP_GEN_CNTL);
+	tmp = INREG(TMDS_TRANSMITTER_CNTL);
+	OUTREG(0x2a8, 0x0000061b);
+	tmp |= TMDS_PLL_EN;
+	OUTREG(TMDS_TRANSMITTER_CNTL, tmp);
+	mdelay(1);
+	tmp &= ~TMDS_PLLRST;
+	OUTREG(TMDS_TRANSMITTER_CNTL, tmp);
+	tmp2 &= ~2;
+	tmp2 |= FP_TMDS_EN;
+	OUTREG(FP_GEN_CNTL, tmp2);
+	mdelay(5);
+	tmp2 |= FP_FPON;
+	OUTREG(FP_GEN_CNTL, tmp2);
+
+	OUTREG(CUR_HORZ_VERT_OFF, CUR_LOCK | 1);
+	cgc = INREG(CRTC_GEN_CNTL);
+	OUTREG(CUR_HORZ_VERT_POSN, 0xbfff0fff);
+	cgc |= 0x10000;
+	OUTREG(CUR_OFFSET, 0);
+}
+#endif /* 0 */
+
+#endif /* CONFIG_PPC_OF */
+
+static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
+{
+	u16 pwr_cmd;
+	u32 tmp;
+	int i;
+
+	if (!rinfo->pm_reg)
+		return;
+
+	/* Set the chip into appropriate suspend mode (we use D2,
+	 * D3 would require a compete re-initialization of the chip,
+	 * including PCI config registers, clocks, AGP conf, ...)
+	 */
+	if (suspend) {
+		printk(KERN_DEBUG "radeonfb (%s): switching to D2 state...\n",
+		       pci_name(rinfo->pdev));
+
+		/* Disable dynamic power management of clocks for the
+		 * duration of the suspend/resume process
+		 */
+		radeon_pm_disable_dynamic_mode(rinfo);
+
+		/* Save some registers */
+		radeon_pm_save_regs(rinfo, 0);
+
+		/* Prepare mobility chips for suspend.
+		 */
+		if (rinfo->is_mobility) {
+			/* Program V2CLK */
+			radeon_pm_program_v2clk(rinfo);
+		
+			/* Disable IO PADs */
+			radeon_pm_disable_iopad(rinfo);
+
+			/* Set low current */
+			radeon_pm_low_current(rinfo);
+
+			/* Prepare chip for power management */
+			radeon_pm_setup_for_suspend(rinfo);
+
+			if (rinfo->family <= CHIP_FAMILY_RV280) {
+				/* Reset the MDLL */
+				/* because both INPLL and OUTPLL take the same
+				 * lock, that's why. */
+				tmp = INPLL( pllMDLL_CKO) | MDLL_CKO__MCKOA_RESET
+					| MDLL_CKO__MCKOB_RESET;
+				OUTPLL( pllMDLL_CKO, tmp );
+			}
+		}
+
+		for (i = 0; i < 64; ++i)
+			pci_read_config_dword(rinfo->pdev, i * 4,
+					      &rinfo->cfg_save[i]);
+
+		/* Switch PCI power managment to D2. */
+		pci_disable_device(rinfo->pdev);
+		for (;;) {
+			pci_read_config_word(
+				rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				&pwr_cmd);
+			if (pwr_cmd & 2)
+				break;			
+			pci_write_config_word(
+				rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				(pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | 2);
+			mdelay(500);
+		}
+	} else {
+		printk(KERN_DEBUG "radeonfb (%s): switching to D0 state...\n",
+		       pci_name(rinfo->pdev));
+
+		/* Switch back PCI powermanagment to D0 */
+		mdelay(200);
+		pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0);
+		mdelay(500);
+
+		if (rinfo->family <= CHIP_FAMILY_RV250) {
+			/* Reset the SDRAM controller  */
+			radeon_pm_full_reset_sdram(rinfo);
+
+			/* Restore some registers */
+			radeon_pm_restore_regs(rinfo);
+		} else {
+			/* Restore registers first */
+			radeon_pm_restore_regs(rinfo);
+			/* init sdram controller */
+			radeon_pm_full_reset_sdram(rinfo);
+		}
+	}
+}
+
+static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo)
+{
+	int i;
+	static u32 radeon_cfg_after_resume[64];
+
+	for (i = 0; i < 64; ++i)
+		pci_read_config_dword(rinfo->pdev, i * 4,
+				      &radeon_cfg_after_resume[i]);
+
+	if (radeon_cfg_after_resume[PCI_BASE_ADDRESS_0/4]
+	    == rinfo->cfg_save[PCI_BASE_ADDRESS_0/4])
+		return 0;	/* assume everything is ok */
+
+	for (i = PCI_BASE_ADDRESS_0/4; i < 64; ++i) {
+		if (radeon_cfg_after_resume[i] != rinfo->cfg_save[i])
+			pci_write_config_dword(rinfo->pdev, i * 4,
+					       rinfo->cfg_save[i]);
+	}
+	pci_write_config_word(rinfo->pdev, PCI_CACHE_LINE_SIZE,
+			      rinfo->cfg_save[PCI_CACHE_LINE_SIZE/4]);
+	pci_write_config_word(rinfo->pdev, PCI_COMMAND,
+			      rinfo->cfg_save[PCI_COMMAND/4]);
+	return 1;
+}
+
+
+static/*extern*/ int susdisking = 0;
+
+int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+	u8 agp;
+	int i;
+
+	if (state == pdev->dev.power.power_state)
+		return 0;
+
+	printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n",
+	       pci_name(pdev), state);
+
+	/* For suspend-to-disk, we cheat here. We don't suspend anything and
+	 * let fbcon continue drawing until we are all set. That shouldn't
+	 * really cause any problem at this point, provided that the wakeup
+	 * code knows that any state in memory may not match the HW
+	 */
+	if (state != PM_SUSPEND_MEM)
+		goto done;
+	if (susdisking) {
+		printk("radeonfb (%s): suspending to disk but state = %d\n",
+		       pci_name(pdev), state);
+		goto done;
+	}
+
+	acquire_console_sem();
+
+	fb_set_suspend(info, 1);
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED)) {
+		/* Make sure engine is reset */
+		radeon_engine_idle();
+		radeonfb_engine_reset(rinfo);
+		radeon_engine_idle();
+	}
+
+	/* Blank display and LCD */
+	radeon_screen_blank(rinfo, FB_BLANK_POWERDOWN, 1);
+
+	/* Sleep */
+	rinfo->asleep = 1;
+	rinfo->lock_blank = 1;
+	del_timer_sync(&rinfo->lvds_timer);
+
+	/* Disable AGP. The AGP host should have done it, but since ordering
+	 * isn't always properly guaranteed in this specific case, let's make
+	 * sure it's disabled on card side now. Ultimately, when merging fbdev
+	 * and dri into some common infrastructure, this will be handled
+	 * more nicely. The host bridge side will (or will not) be dealt with
+	 * by the bridge AGP driver, we don't attempt to touch it here.
+	 */
+	agp = pci_find_capability(pdev, PCI_CAP_ID_AGP);
+	if (agp) {
+		u32 cmd;
+
+		pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd);
+		if (cmd & PCI_AGP_COMMAND_AGP) {
+			printk(KERN_INFO "radeonfb (%s): AGP was enabled, "
+			       "disabling ...\n",
+			       pci_name(pdev));
+			cmd &= ~PCI_AGP_COMMAND_AGP;
+			pci_write_config_dword(pdev, agp + PCI_AGP_COMMAND,
+					       cmd);
+		}
+	}
+
+	/* If we support wakeup from poweroff, we save all regs we can including cfg
+	 * space
+	 */
+	if (rinfo->pm_mode & radeon_pm_off) {
+		/* Always disable dynamic clocks or weird things are happening when
+		 * the chip goes off (basically the panel doesn't shut down properly
+		 * and we crash on wakeup),
+		 * also, we want the saved regs context to have no dynamic clocks in
+		 * it, we'll restore the dynamic clocks state on wakeup
+		 */
+		radeon_pm_disable_dynamic_mode(rinfo);
+		mdelay(50);
+		radeon_pm_save_regs(rinfo, 1);
+
+		if (rinfo->is_mobility && !(rinfo->pm_mode & radeon_pm_d2)) {
+			/* Switch off LVDS interface */
+			mdelay(1);
+			OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_BL_MOD_EN));
+			mdelay(1);
+			OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_EN | LVDS_ON));
+			OUTREG(LVDS_PLL_CNTL, (INREG(LVDS_PLL_CNTL) & ~30000) | 0x20000);
+			mdelay(20);
+			OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON));
+		}
+		// FIXME: Use PCI layer
+		for (i = 0; i < 64; ++i)
+			pci_read_config_dword(pdev, i * 4, &rinfo->cfg_save[i]);
+		pci_disable_device(pdev);
+	}
+	/* If we support D2, we go to it (should be fixed later with a flag forcing
+	 * D3 only for some laptops)
+	 */
+	if (rinfo->pm_mode & radeon_pm_d2)
+		radeon_set_suspend(rinfo, 1);
+
+	release_console_sem();
+
+ done:
+	pdev->dev.power.power_state = state;
+
+	return 0;
+}
+
+int radeonfb_pci_resume(struct pci_dev *pdev)
+{
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+	int rc = 0;
+
+	if (pdev->dev.power.power_state == 0)
+		return 0;
+
+	if (rinfo->no_schedule) {
+		if (try_acquire_console_sem())
+			return 0;
+	} else
+		acquire_console_sem();
+
+	printk(KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n",
+	       pci_name(pdev), pdev->dev.power.power_state);
+
+
+	if (pci_enable_device(pdev)) {
+		rc = -ENODEV;
+		printk(KERN_ERR "radeonfb (%s): can't enable PCI device !\n",
+		       pci_name(pdev));
+		goto bail;
+	}
+	pci_set_master(pdev);
+
+	if (pdev->dev.power.power_state == PM_SUSPEND_MEM) {
+		/* Wakeup chip. Check from config space if we were powered off
+		 * (todo: additionally, check CLK_PIN_CNTL too)
+		 */
+		if ((rinfo->pm_mode & radeon_pm_off) && radeon_restore_pci_cfg(rinfo)) {
+			if (rinfo->reinit_func != NULL)
+				rinfo->reinit_func(rinfo);
+			else {
+				printk(KERN_ERR "radeonfb (%s): can't resume radeon from"
+				       " D3 cold, need softboot !", pci_name(pdev));
+				rc = -EIO;
+				goto bail;
+			}
+		}
+		/* If we support D2, try to resume... we should check what was our
+		 * state though... (were we really in D2 state ?). Right now, this code
+		 * is only enable on Macs so it's fine.
+		 */
+		else if (rinfo->pm_mode & radeon_pm_d2)
+			radeon_set_suspend(rinfo, 0);
+
+		rinfo->asleep = 0;
+	} else
+		radeon_engine_idle();
+
+	/* Restore display & engine */
+	radeon_write_mode (rinfo, &rinfo->state, 1);
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+		radeonfb_engine_init (rinfo);
+
+	fb_pan_display(info, &info->var);
+	fb_set_cmap(&info->cmap, info);
+
+	/* Refresh */
+	fb_set_suspend(info, 0);
+
+	/* Unblank */
+	rinfo->lock_blank = 0;
+	radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 1);
+
+	/* Check status of dynclk */
+	if (rinfo->dynclk == 1)
+		radeon_pm_enable_dynamic_mode(rinfo);
+	else if (rinfo->dynclk == 0)
+		radeon_pm_disable_dynamic_mode(rinfo);
+
+	pdev->dev.power.power_state = PMSG_ON;
+
+ bail:
+	release_console_sem();
+
+	return rc;
+}
+
+#ifdef CONFIG_PPC_OF
+static void radeonfb_early_resume(void *data)
+{
+        struct radeonfb_info *rinfo = data;
+
+	rinfo->no_schedule = 1;
+	radeonfb_pci_resume(rinfo->pdev);
+	rinfo->no_schedule = 0;
+}
+#endif /* CONFIG_PPC_OF */
+
+#endif /* CONFIG_PM */
+
+void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
+{
+	/* Find PM registers in config space if any*/
+	rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM);
+
+	/* Enable/Disable dynamic clocks: TODO add sysfs access */
+	rinfo->dynclk = dynclk;
+	if (dynclk == 1) {
+		radeon_pm_enable_dynamic_mode(rinfo);
+		printk("radeonfb: Dynamic Clock Power Management enabled\n");
+	} else if (dynclk == 0) {
+		radeon_pm_disable_dynamic_mode(rinfo);
+		printk("radeonfb: Dynamic Clock Power Management disabled\n");
+	}
+
+	/* Check if we can power manage on suspend/resume. We can do
+	 * D2 on M6, M7 and M9, and we can resume from D3 cold a few other
+	 * "Mac" cards, but that's all. We need more infos about what the
+	 * BIOS does tho. Right now, all this PM stuff is pmac-only for that
+	 * reason. --BenH
+	 */
+#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF)
+	if (_machine == _MACH_Pmac && rinfo->of_node) {
+		if (rinfo->is_mobility && rinfo->pm_reg &&
+		    rinfo->family <= CHIP_FAMILY_RV250)
+			rinfo->pm_mode |= radeon_pm_d2;
+
+		/* We can restart Jasper (M10 chip in albooks), BlueStone (7500 chip
+		 * in some desktop G4s), and Via (M9+ chip on iBook G4)
+		 */
+		if (!strcmp(rinfo->of_node->name, "ATY,JasperParent")) {
+			rinfo->reinit_func = radeon_reinitialize_M10;
+			rinfo->pm_mode |= radeon_pm_off;
+		}
+#if 0 /* Not ready yet */
+		if (!strcmp(rinfo->of_node->name, "ATY,BlueStoneParent")) {
+			rinfo->reinit_func = radeon_reinitialize_QW;
+			rinfo->pm_mode |= radeon_pm_off;
+		}
+#endif
+		if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) {
+			rinfo->reinit_func = radeon_reinitialize_M9P;
+			rinfo->pm_mode |= radeon_pm_off;
+		}
+
+		/* If any of the above is set, we assume the machine can sleep/resume.
+		 * It's a bit of a "shortcut" but will work fine. Ideally, we need infos
+		 * from the platform about what happens to the chip...
+		 * Now we tell the platform about our capability
+		 */
+		if (rinfo->pm_mode != radeon_pm_none) {
+			pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, rinfo->of_node, 0, 1);
+			pmac_set_early_video_resume(radeonfb_early_resume, rinfo);
+		}
+
+#if 0
+		/* Power down TV DAC, taht saves a significant amount of power,
+		 * we'll have something better once we actually have some TVOut
+		 * support
+		 */
+		OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000);
+#endif
+	}
+#endif /* defined(CONFIG_PM) && defined(CONFIG_PPC_OF) */
+}
+
+void radeonfb_pm_exit(struct radeonfb_info *rinfo)
+{
+#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF)
+	if (rinfo->pm_mode != radeon_pm_none)
+		pmac_set_early_video_resume(NULL, NULL);
+#endif
+}
diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h
new file mode 100644
index 0000000..659bc9f
--- /dev/null
+++ b/drivers/video/aty/radeonfb.h
@@ -0,0 +1,625 @@
+#ifndef __RADEONFB_H__
+#define __RADEONFB_H__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/io.h>
+
+#ifdef CONFIG_PPC_OF
+#include <asm/prom.h>
+#endif
+
+#include <video/radeon.h>
+
+/***************************************************************
+ * Most of the definitions here are adapted right from XFree86 *
+ ***************************************************************/
+
+
+/*
+ * Chip families. Must fit in the low 16 bits of a long word
+ */
+enum radeon_family {
+	CHIP_FAMILY_UNKNOW,
+	CHIP_FAMILY_LEGACY,
+	CHIP_FAMILY_RADEON,
+	CHIP_FAMILY_RV100,
+	CHIP_FAMILY_RS100,    /* U1 (IGP320M) or A3 (IGP320)*/
+	CHIP_FAMILY_RV200,
+	CHIP_FAMILY_RS200,    /* U2 (IGP330M/340M/350M) or A4 (IGP330/340/345/350),
+				 RS250 (IGP 7000) */
+	CHIP_FAMILY_R200,
+	CHIP_FAMILY_RV250,
+	CHIP_FAMILY_RS300,    /* Radeon 9000 IGP */
+	CHIP_FAMILY_RV280,
+	CHIP_FAMILY_R300,
+	CHIP_FAMILY_R350,
+	CHIP_FAMILY_RV350,
+	CHIP_FAMILY_RV380,    /* RV370/RV380/M22/M24 */
+	CHIP_FAMILY_R420,     /* R420/R423/M18 */
+	CHIP_FAMILY_LAST,
+};
+
+#define IS_RV100_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_RV100)  || \
+				 ((rinfo)->family == CHIP_FAMILY_RV200)  || \
+				 ((rinfo)->family == CHIP_FAMILY_RS100)  || \
+				 ((rinfo)->family == CHIP_FAMILY_RS200)  || \
+				 ((rinfo)->family == CHIP_FAMILY_RV250)  || \
+				 ((rinfo)->family == CHIP_FAMILY_RV280)  || \
+				 ((rinfo)->family == CHIP_FAMILY_RS300))
+
+
+#define IS_R300_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_R300)  || \
+				((rinfo)->family == CHIP_FAMILY_RV350) || \
+				((rinfo)->family == CHIP_FAMILY_R350)  || \
+				((rinfo)->family == CHIP_FAMILY_RV380) || \
+				((rinfo)->family == CHIP_FAMILY_R420))
+
+/*
+ * Chip flags
+ */
+enum radeon_chip_flags {
+	CHIP_FAMILY_MASK	= 0x0000ffffUL,
+	CHIP_FLAGS_MASK		= 0xffff0000UL,
+	CHIP_IS_MOBILITY	= 0x00010000UL,
+	CHIP_IS_IGP		= 0x00020000UL,
+	CHIP_HAS_CRTC2		= 0x00040000UL,	
+};
+
+/*
+ * Errata workarounds
+ */
+enum radeon_errata {
+	CHIP_ERRATA_R300_CG		= 0x00000001,
+	CHIP_ERRATA_PLL_DUMMYREADS	= 0x00000002,
+	CHIP_ERRATA_PLL_DELAY		= 0x00000004,
+};
+
+
+/*
+ * Monitor types
+ */
+enum radeon_montype {
+	MT_NONE = 0,
+	MT_CRT,		/* CRT */
+	MT_LCD,		/* LCD */
+	MT_DFP,		/* DVI */
+	MT_CTV,		/* composite TV */
+	MT_STV		/* S-Video out */
+};
+
+/*
+ * DDC i2c ports
+ */
+enum ddc_type {
+	ddc_none,
+	ddc_monid,
+	ddc_dvi,
+	ddc_vga,
+	ddc_crt2,
+};
+
+/*
+ * Connector types
+ */
+enum conn_type {
+	conn_none,
+	conn_proprietary,
+	conn_crt,
+	conn_DVI_I,
+	conn_DVI_D,
+};
+
+
+/*
+ * PLL infos
+ */
+struct pll_info {
+	int ppll_max;
+	int ppll_min;
+	int sclk, mclk;
+	int ref_div;
+	int ref_clk;
+};
+
+
+/*
+ * This structure contains the various registers manipulated by this
+ * driver for setting or restoring a mode. It's mostly copied from
+ * XFree's RADEONSaveRec structure. A few chip settings might still be
+ * tweaked without beeing reflected or saved in these registers though
+ */
+struct radeon_regs {
+	/* Common registers */
+	u32		ovr_clr;
+	u32		ovr_wid_left_right;
+	u32		ovr_wid_top_bottom;
+	u32		ov0_scale_cntl;
+	u32		mpp_tb_config;
+	u32		mpp_gp_config;
+	u32		subpic_cntl;
+	u32		viph_control;
+	u32		i2c_cntl_1;
+	u32		gen_int_cntl;
+	u32		cap0_trig_cntl;
+	u32		cap1_trig_cntl;
+	u32		bus_cntl;
+	u32		surface_cntl;
+	u32		bios_5_scratch;
+
+	/* Other registers to save for VT switches or driver load/unload */
+	u32		dp_datatype;
+	u32		rbbm_soft_reset;
+	u32		clock_cntl_index;
+	u32		amcgpio_en_reg;
+	u32		amcgpio_mask;
+
+	/* Surface/tiling registers */
+	u32		surf_lower_bound[8];
+	u32		surf_upper_bound[8];
+	u32		surf_info[8];
+
+	/* CRTC registers */
+	u32		crtc_gen_cntl;
+	u32		crtc_ext_cntl;
+	u32		dac_cntl;
+	u32		crtc_h_total_disp;
+	u32		crtc_h_sync_strt_wid;
+	u32		crtc_v_total_disp;
+	u32		crtc_v_sync_strt_wid;
+	u32		crtc_offset;
+	u32		crtc_offset_cntl;
+	u32		crtc_pitch;
+	u32		disp_merge_cntl;
+	u32		grph_buffer_cntl;
+	u32		crtc_more_cntl;
+
+	/* CRTC2 registers */
+	u32		crtc2_gen_cntl;
+	u32		dac2_cntl;
+	u32		disp_output_cntl;
+	u32		disp_hw_debug;
+	u32		disp2_merge_cntl;
+	u32		grph2_buffer_cntl;
+	u32		crtc2_h_total_disp;
+	u32		crtc2_h_sync_strt_wid;
+	u32		crtc2_v_total_disp;
+	u32		crtc2_v_sync_strt_wid;
+	u32		crtc2_offset;
+	u32		crtc2_offset_cntl;
+	u32		crtc2_pitch;
+
+	/* Flat panel regs */
+	u32 		fp_crtc_h_total_disp;
+	u32		fp_crtc_v_total_disp;
+	u32		fp_gen_cntl;
+	u32		fp2_gen_cntl;
+	u32		fp_h_sync_strt_wid;
+	u32		fp2_h_sync_strt_wid;
+	u32		fp_horz_stretch;
+	u32		fp_panel_cntl;
+	u32		fp_v_sync_strt_wid;
+	u32		fp2_v_sync_strt_wid;
+	u32		fp_vert_stretch;
+	u32		lvds_gen_cntl;
+	u32		lvds_pll_cntl;
+	u32		tmds_crc;
+	u32		tmds_transmitter_cntl;
+
+	/* Computed values for PLL */
+	u32		dot_clock_freq;
+	int		feedback_div;
+	int		post_div;	
+
+	/* PLL registers */
+	u32		ppll_div_3;
+	u32		ppll_ref_div;
+	u32		vclk_ecp_cntl;
+	u32		clk_cntl_index;
+
+	/* Computed values for PLL2 */
+	u32		dot_clock_freq_2;
+	int		feedback_div_2;
+	int		post_div_2;
+
+	/* PLL2 registers */
+	u32		p2pll_ref_div;
+	u32		p2pll_div_0;
+	u32		htotal_cntl2;
+
+       	/* Palette */
+	int		palette_valid;
+};
+
+struct panel_info {
+	int xres, yres;
+	int valid;
+	int clock;
+	int hOver_plus, hSync_width, hblank;
+	int vOver_plus, vSync_width, vblank;
+	int hAct_high, vAct_high, interlaced;
+	int pwr_delay;
+	int use_bios_dividers;
+	int ref_divider;
+	int post_divider;
+	int fbk_divider;
+};
+
+struct radeonfb_info;
+
+#ifdef CONFIG_FB_RADEON_I2C
+struct radeon_i2c_chan {
+	struct radeonfb_info		*rinfo;
+	u32		 		ddc_reg;
+	struct i2c_adapter		adapter;
+	struct i2c_algo_bit_data	algo;
+};
+#endif
+
+enum radeon_pm_mode {
+	radeon_pm_none	= 0,		/* Nothing supported */
+	radeon_pm_d2	= 0x00000001,	/* Can do D2 state */
+	radeon_pm_off	= 0x00000002,	/* Can resume from D3 cold */
+};
+
+struct radeonfb_info {
+	struct fb_info		*info;
+
+	struct radeon_regs 	state;
+	struct radeon_regs	init_state;
+
+	char			name[DEVICE_NAME_SIZE];
+
+	unsigned long		mmio_base_phys;
+	unsigned long		fb_base_phys;
+
+	void __iomem		*mmio_base;
+	void __iomem		*fb_base;
+
+	unsigned long		fb_local_base;
+
+	struct pci_dev		*pdev;
+#ifdef CONFIG_PPC_OF
+	struct device_node	*of_node;
+#endif
+
+	void __iomem		*bios_seg;
+	int			fp_bios_start;
+
+	u32			pseudo_palette[17];
+	struct { u8 red, green, blue, pad; }
+				palette[256];
+
+	int			chipset;
+	u8			family;
+	u8			rev;
+	unsigned int		errata;
+	unsigned long		video_ram;
+	unsigned long		mapped_vram;
+	int			vram_width;
+	int			vram_ddr;
+
+	int			pitch, bpp, depth;
+
+	int			has_CRTC2;
+	int			is_mobility;
+	int			is_IGP;
+	int			reversed_DAC;
+	int			reversed_TMDS;
+	struct panel_info	panel_info;
+	int			mon1_type;
+	u8			*mon1_EDID;
+	struct fb_videomode	*mon1_modedb;
+	int			mon1_dbsize;
+	int			mon2_type;
+	u8		        *mon2_EDID;
+
+	u32			dp_gui_master_cntl;
+
+	struct pll_info		pll;
+
+	int			mtrr_hdl;
+
+	int			pm_reg;
+	u32			save_regs[100];
+	int			asleep;
+	int			lock_blank;
+	int			dynclk;
+	int			no_schedule;
+	enum radeon_pm_mode	pm_mode;
+	void			(*reinit_func)(struct radeonfb_info *rinfo);
+
+	/* Lock on register access */
+	spinlock_t		reg_lock;
+
+	/* Timer used for delayed LVDS operations */
+	struct timer_list	lvds_timer;
+	u32			pending_lvds_gen_cntl;
+
+#ifdef CONFIG_FB_RADEON_I2C
+	struct radeon_i2c_chan 	i2c[4];
+#endif
+
+	u32			cfg_save[64];
+};
+
+
+#define PRIMARY_MONITOR(rinfo)	(rinfo->mon1_type)
+
+
+/*
+ * Debugging stuffs
+ */
+#ifdef CONFIG_FB_RADEON_DEBUG
+#define DEBUG		1
+#else
+#define DEBUG		0
+#endif
+
+#if DEBUG
+#define RTRACE		printk
+#else
+#define RTRACE		if(0) printk
+#endif
+
+
+/*
+ * IO macros
+ */
+
+/* Note about this function: we have some rare cases where we must not schedule,
+ * this typically happen with our special "wake up early" hook which allows us to
+ * wake up the graphic chip (and thus get the console back) before everything else
+ * on some machines that support that mecanism. At this point, interrupts are off
+ * and scheduling is not permitted
+ */
+static inline void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms)
+{
+	if (rinfo->no_schedule || oops_in_progress)
+		mdelay(ms);
+	else
+		msleep(ms);
+}
+
+
+#define INREG8(addr)		readb((rinfo->mmio_base)+addr)
+#define OUTREG8(addr,val)	writeb(val, (rinfo->mmio_base)+addr)
+#define INREG(addr)		readl((rinfo->mmio_base)+addr)
+#define OUTREG(addr,val)	writel(val, (rinfo->mmio_base)+addr)
+
+static inline void _OUTREGP(struct radeonfb_info *rinfo, u32 addr,
+		       u32 val, u32 mask)
+{
+	unsigned long flags;
+	unsigned int tmp;
+
+	spin_lock_irqsave(&rinfo->reg_lock, flags);
+	tmp = INREG(addr);
+	tmp &= (mask);
+	tmp |= (val);
+	OUTREG(addr, tmp);
+	spin_unlock_irqrestore(&rinfo->reg_lock, flags);
+}
+
+#define OUTREGP(addr,val,mask)	_OUTREGP(rinfo, addr, val,mask)
+
+/*
+ * Note about PLL register accesses:
+ *
+ * I have removed the spinlock on them on purpose. The driver now
+ * expects that it will only manipulate the PLL registers in normal
+ * task environment, where radeon_msleep() will be called, protected
+ * by a semaphore (currently the console semaphore) so that no conflict
+ * will happen on the PLL register index.
+ *
+ * With the latest changes to the VT layer, this is guaranteed for all
+ * calls except the actual drawing/blits which aren't supposed to use
+ * the PLL registers anyway
+ *
+ * This is very important for the workarounds to work properly. The only
+ * possible exception to this rule is the call to unblank(), which may
+ * be done at irq time if an oops is in progress.
+ */
+static inline void radeon_pll_errata_after_index(struct radeonfb_info *rinfo)
+{
+	if (!(rinfo->errata & CHIP_ERRATA_PLL_DUMMYREADS))
+		return;
+
+	(void)INREG(CLOCK_CNTL_DATA);
+	(void)INREG(CRTC_GEN_CNTL);
+}
+
+static inline void radeon_pll_errata_after_data(struct radeonfb_info *rinfo)
+{
+	if (rinfo->errata & CHIP_ERRATA_PLL_DELAY) {
+		/* we can't deal with posted writes here ... */
+		_radeon_msleep(rinfo, 5);
+	}
+	if (rinfo->errata & CHIP_ERRATA_R300_CG) {
+		u32 save, tmp;
+		save = INREG(CLOCK_CNTL_INDEX);
+		tmp = save & ~(0x3f | PLL_WR_EN);
+		OUTREG(CLOCK_CNTL_INDEX, tmp);
+		tmp = INREG(CLOCK_CNTL_DATA);
+		OUTREG(CLOCK_CNTL_INDEX, save);
+	}
+}
+
+static inline u32 __INPLL(struct radeonfb_info *rinfo, u32 addr)
+{
+	u32 data;
+
+	OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f);
+	radeon_pll_errata_after_index(rinfo);
+	data = INREG(CLOCK_CNTL_DATA);
+	radeon_pll_errata_after_data(rinfo);
+	return data;
+}
+
+static inline void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index,
+			    u32 val)
+{
+
+	OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080);
+	radeon_pll_errata_after_index(rinfo);
+	OUTREG(CLOCK_CNTL_DATA, val);
+	radeon_pll_errata_after_data(rinfo);
+}
+
+
+static inline void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
+			     u32 val, u32 mask)
+{
+	unsigned int tmp;
+
+	tmp  = __INPLL(rinfo, index);
+	tmp &= (mask);
+	tmp |= (val);
+	__OUTPLL(rinfo, index, tmp);
+}
+
+
+#define INPLL(addr)			__INPLL(rinfo, addr)
+#define OUTPLL(index, val)		__OUTPLL(rinfo, index, val)
+#define OUTPLLP(index, val, mask)	__OUTPLLP(rinfo, index, val, mask)
+
+
+#define BIOS_IN8(v)  	(readb(rinfo->bios_seg + (v)))
+#define BIOS_IN16(v) 	(readb(rinfo->bios_seg + (v)) | \
+			  (readb(rinfo->bios_seg + (v) + 1) << 8))
+#define BIOS_IN32(v) 	(readb(rinfo->bios_seg + (v)) | \
+			  (readb(rinfo->bios_seg + (v) + 1) << 8) | \
+			  (readb(rinfo->bios_seg + (v) + 2) << 16) | \
+			  (readb(rinfo->bios_seg + (v) + 3) << 24))
+
+/*
+ * Inline utilities
+ */
+static inline int round_div(int num, int den)
+{
+        return (num + (den / 2)) / den;
+}
+
+static inline int var_to_depth(const struct fb_var_screeninfo *var)
+{
+	if (var->bits_per_pixel != 16)
+		return var->bits_per_pixel;
+	return (var->green.length == 5) ? 15 : 16;
+}
+
+static inline u32 radeon_get_dstbpp(u16 depth)
+{
+	switch (depth) {
+       	case 8:
+       		return DST_8BPP;
+       	case 15:
+       		return DST_15BPP;
+       	case 16:
+       		return DST_16BPP;
+       	case 32:
+       		return DST_32BPP;
+       	default:
+       		return 0;
+	}
+}
+
+/*
+ * 2D Engine helper routines
+ */
+static inline void radeon_engine_flush (struct radeonfb_info *rinfo)
+{
+	int i;
+
+	/* initiate flush */
+	OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
+	        ~RB2D_DC_FLUSH_ALL);
+
+	for (i=0; i < 2000000; i++) {
+		if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
+			return;
+		udelay(1);
+	}
+	printk(KERN_ERR "radeonfb: Flush Timeout !\n");
+}
+
+
+static inline void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries)
+{
+	int i;
+
+	for (i=0; i<2000000; i++) {
+		if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
+			return;
+		udelay(1);
+	}
+	printk(KERN_ERR "radeonfb: FIFO Timeout !\n");
+}
+
+
+static inline void _radeon_engine_idle(struct radeonfb_info *rinfo)
+{
+	int i;
+
+	/* ensure FIFO is empty before waiting for idle */
+	_radeon_fifo_wait (rinfo, 64);
+
+	for (i=0; i<2000000; i++) {
+		if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
+			radeon_engine_flush (rinfo);
+			return;
+		}
+		udelay(1);
+	}
+	printk(KERN_ERR "radeonfb: Idle Timeout !\n");
+}
+
+
+#define radeon_engine_idle()		_radeon_engine_idle(rinfo)
+#define radeon_fifo_wait(entries)	_radeon_fifo_wait(rinfo,entries)
+#define radeon_msleep(ms)		_radeon_msleep(rinfo,ms)
+
+
+/* I2C Functions */
+extern void radeon_create_i2c_busses(struct radeonfb_info *rinfo);
+extern void radeon_delete_i2c_busses(struct radeonfb_info *rinfo);
+extern int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 **out_edid);
+
+/* PM Functions */
+extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state);
+extern int radeonfb_pci_resume(struct pci_dev *pdev);
+extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk);
+extern void radeonfb_pm_exit(struct radeonfb_info *rinfo);
+
+/* Monitor probe functions */
+extern void radeon_probe_screens(struct radeonfb_info *rinfo,
+				 const char *monitor_layout, int ignore_edid);
+extern void radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_option);
+extern int radeon_match_mode(struct radeonfb_info *rinfo,
+			     struct fb_var_screeninfo *dest,
+			     const struct fb_var_screeninfo *src);
+
+/* Accel functions */
+extern void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region);
+extern void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+extern void radeonfb_imageblit(struct fb_info *p, const struct fb_image *image);
+extern int radeonfb_sync(struct fb_info *info);
+extern void radeonfb_engine_init (struct radeonfb_info *rinfo);
+extern void radeonfb_engine_reset(struct radeonfb_info *rinfo);
+
+/* Other functions */
+extern int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch);
+extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
+			       int reg_only);
+
+#endif /* __RADEONFB_H__ */
diff --git a/drivers/video/aty/xlinit.c b/drivers/video/aty/xlinit.c
new file mode 100644
index 0000000..92643af
--- /dev/null
+++ b/drivers/video/aty/xlinit.c
@@ -0,0 +1,354 @@
+/*
+ *  ATI Rage XL Initialization. Support for Xpert98 and Victoria
+ *  PCI cards.
+ *
+ *  Copyright (C) 2002 MontaVista Software Inc.
+ *  Author: MontaVista Software, Inc.
+ *         	stevel@mvista.com or source@mvista.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h> 
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <video/mach64.h>
+#include "atyfb.h"
+
+#define MPLL_GAIN       0xad
+#define VPLL_GAIN       0xd5
+
+enum {
+	VICTORIA = 0,
+	XPERT98,
+	NUM_XL_CARDS
+};
+
+extern const struct aty_pll_ops aty_pll_ct;
+
+#define DEFAULT_CARD XPERT98
+static int xl_card = DEFAULT_CARD;
+
+static const struct xl_card_cfg_t {
+	int ref_crystal; // 10^4 Hz
+	int mem_type;
+	int mem_size;
+	u32 mem_cntl;
+	u32 ext_mem_cntl;
+	u32 mem_addr_config;
+	u32 bus_cntl;
+	u32 dac_cntl;
+	u32 hw_debug;
+	u32 custom_macro_cntl;
+	u8  dll2_cntl;
+	u8  pll_yclk_cntl;
+} card_cfg[NUM_XL_CARDS] = {
+	// VICTORIA
+	{	2700, SDRAM, 0x800000,
+		0x10757A3B, 0x64000C81, 0x00110202, 0x7b33A040,
+		0x82010102, 0x48803800, 0x005E0179,
+		0x50, 0x25
+	},
+	// XPERT98
+	{	1432,  WRAM, 0x800000,
+		0x00165A2B, 0xE0000CF1, 0x00200213, 0x7333A001,
+		0x8000000A, 0x48833800, 0x007F0779,
+		0x10, 0x19
+	}
+};
+	  
+typedef struct {
+	u8 lcd_reg;
+	u32 val;
+} lcd_tbl_t;
+
+static const lcd_tbl_t lcd_tbl[] = {
+	{ 0x01,	0x000520C0 },
+	{ 0x08,	0x02000408 },
+	{ 0x03,	0x00000F00 },
+	{ 0x00,	0x00000000 },
+	{ 0x02,	0x00000000 },
+	{ 0x04,	0x00000000 },
+	{ 0x05,	0x00000000 },
+	{ 0x06,	0x00000000 },
+	{ 0x33,	0x00000000 },
+	{ 0x34,	0x00000000 },
+	{ 0x35,	0x00000000 },
+	{ 0x36,	0x00000000 },
+	{ 0x37,	0x00000000 }
+};
+
+static void reset_gui(struct atyfb_par *par)
+{
+	aty_st_8(GEN_TEST_CNTL+1, 0x01, par);
+	aty_st_8(GEN_TEST_CNTL+1, 0x00, par);
+	aty_st_8(GEN_TEST_CNTL+1, 0x02, par);
+	mdelay(5);
+}
+
+static void reset_sdram(struct atyfb_par *par)
+{
+	u8 temp;
+
+	temp = aty_ld_8(EXT_MEM_CNTL, par);
+	temp |= 0x02;
+	aty_st_8(EXT_MEM_CNTL, temp, par); // MEM_SDRAM_RESET = 1b
+	temp |= 0x08;
+	aty_st_8(EXT_MEM_CNTL, temp, par); // MEM_CYC_TEST    = 10b
+	temp |= 0x0c;
+	aty_st_8(EXT_MEM_CNTL, temp, par); // MEM_CYC_TEST    = 11b
+	mdelay(5);
+	temp &= 0xf3;
+	aty_st_8(EXT_MEM_CNTL, temp, par); // MEM_CYC_TEST    = 00b
+	temp &= 0xfd;
+	aty_st_8(EXT_MEM_CNTL, temp, par); // MEM_SDRAM_REST  = 0b
+	mdelay(5);
+}
+
+static void init_dll(struct atyfb_par *par)
+{
+	// enable DLL
+	aty_st_pll_ct(PLL_GEN_CNTL,
+		   aty_ld_pll_ct(PLL_GEN_CNTL, par) & 0x7f,
+		   par);
+
+	// reset DLL
+	aty_st_pll_ct(DLL_CNTL, 0x82, par);
+	aty_st_pll_ct(DLL_CNTL, 0xE2, par);
+	mdelay(5);
+	aty_st_pll_ct(DLL_CNTL, 0x82, par);
+	mdelay(6);
+}
+
+static void reset_clocks(struct atyfb_par *par, struct pll_ct *pll,
+			 int hsync_enb)
+{
+	reset_gui(par);
+	aty_st_pll_ct(MCLK_FB_DIV, pll->mclk_fb_div, par);
+	aty_st_pll_ct(SCLK_FB_DIV, pll->sclk_fb_div, par);
+
+	mdelay(15);
+	init_dll(par);
+	aty_st_8(GEN_TEST_CNTL+1, 0x00, par);
+	mdelay(5);
+	aty_st_8(CRTC_GEN_CNTL+3, 0x04, par);
+	mdelay(6);
+	reset_sdram(par);
+	aty_st_8(CRTC_GEN_CNTL+3,
+		 hsync_enb ? 0x00 : 0x04, par);
+
+	aty_st_pll_ct(SPLL_CNTL2, pll->spll_cntl2, par);
+	aty_st_pll_ct(PLL_GEN_CNTL, pll->pll_gen_cntl, par);
+	aty_st_pll_ct(PLL_VCLK_CNTL, pll->pll_vclk_cntl, par);
+}
+
+int atyfb_xl_init(struct fb_info *info)
+{
+	const struct xl_card_cfg_t * card = &card_cfg[xl_card];
+	struct atyfb_par *par = (struct atyfb_par *) info->par;
+	union aty_pll pll;
+	int i, err;
+	u32 temp;
+	
+	aty_st_8(CONFIG_STAT0, 0x85, par);
+	mdelay(10);
+
+	/*
+	 * The following needs to be set before the call
+	 * to var_to_pll() below. They'll be re-set again
+	 * to the same values in aty_init().
+	 */
+	par->ref_clk_per = 100000000UL/card->ref_crystal;
+	par->ram_type = card->mem_type;
+	info->fix.smem_len = card->mem_size;
+	if (xl_card == VICTORIA) {
+		// the MCLK, XCLK are 120MHz on victoria card
+		par->mclk_per = 1000000/120;
+		par->xclk_per = 1000000/120;
+		par->features &= ~M64F_MFB_FORCE_4;
+	}
+	
+	/*
+	 * Calculate mclk and xclk dividers, etc. The passed
+	 * pixclock and bpp values don't matter yet, the vclk
+	 * isn't programmed until later.
+	 */
+	if ((err = aty_pll_ct.var_to_pll(info, 39726, 8, &pll)))
+		return err;
+
+	aty_st_pll_ct(LVDS_CNTL0, 0x00, par);
+	aty_st_pll_ct(DLL2_CNTL, card->dll2_cntl, par);
+	aty_st_pll_ct(V2PLL_CNTL, 0x10, par);
+	aty_st_pll_ct(MPLL_CNTL, MPLL_GAIN, par);
+	aty_st_pll_ct(VPLL_CNTL, VPLL_GAIN, par);
+	aty_st_pll_ct(PLL_VCLK_CNTL, 0x00, par);
+	aty_st_pll_ct(VFC_CNTL, 0x1B, par);
+	aty_st_pll_ct(PLL_REF_DIV, pll.ct.pll_ref_div, par);
+	aty_st_pll_ct(PLL_EXT_CNTL, pll.ct.pll_ext_cntl, par);
+	aty_st_pll_ct(SPLL_CNTL2, 0x03, par);
+	aty_st_pll_ct(PLL_GEN_CNTL, 0x44, par);
+
+	reset_clocks(par, &pll.ct, 0);
+	mdelay(10);
+
+	aty_st_pll_ct(VCLK_POST_DIV, 0x03, par);
+	aty_st_pll_ct(VCLK0_FB_DIV, 0xDA, par);
+	aty_st_pll_ct(VCLK_POST_DIV, 0x0F, par);
+	aty_st_pll_ct(VCLK1_FB_DIV, 0xF5, par);
+	aty_st_pll_ct(VCLK_POST_DIV, 0x3F, par);
+	aty_st_pll_ct(PLL_EXT_CNTL, 0x40 | pll.ct.pll_ext_cntl, par);
+	aty_st_pll_ct(VCLK2_FB_DIV, 0x00, par);
+	aty_st_pll_ct(VCLK_POST_DIV, 0xFF, par);
+	aty_st_pll_ct(PLL_EXT_CNTL, 0xC0 | pll.ct.pll_ext_cntl, par);
+	aty_st_pll_ct(VCLK3_FB_DIV, 0x00, par);
+
+	aty_st_8(BUS_CNTL, 0x01, par);
+	aty_st_le32(BUS_CNTL, card->bus_cntl | 0x08000000, par);
+
+	aty_st_le32(CRTC_GEN_CNTL, 0x04000200, par);
+	aty_st_le16(CONFIG_STAT0, 0x0020, par);
+	aty_st_le32(MEM_CNTL, 0x10151A33, par);
+	aty_st_le32(EXT_MEM_CNTL, 0xE0000C01, par);
+	aty_st_le16(CRTC_GEN_CNTL+2, 0x0000, par);
+	aty_st_le32(DAC_CNTL, card->dac_cntl, par);
+	aty_st_le16(GEN_TEST_CNTL, 0x0100, par);
+	aty_st_le32(CUSTOM_MACRO_CNTL, 0x003C0171, par);
+	aty_st_le32(MEM_BUF_CNTL, 0x00382848, par);
+
+	aty_st_le32(HW_DEBUG, card->hw_debug, par);
+	aty_st_le16(MEM_ADDR_CONFIG, 0x0000, par);
+	aty_st_le16(GP_IO+2, 0x0000, par);
+	aty_st_le16(GEN_TEST_CNTL, 0x0000, par);
+	aty_st_le16(EXT_DAC_REGS+2, 0x0000, par);
+	aty_st_le32(CRTC_INT_CNTL, 0x00000000, par);
+	aty_st_le32(TIMER_CONFIG, 0x00000000, par);
+	aty_st_le32(0xEC, 0x00000000, par);
+	aty_st_le32(0xFC, 0x00000000, par);
+
+	for (i=0; i<sizeof(lcd_tbl)/sizeof(lcd_tbl_t); i++) {
+		aty_st_lcd(lcd_tbl[i].lcd_reg, lcd_tbl[i].val, par);
+	}
+
+	aty_st_le16(CONFIG_STAT0, 0x00A4, par);
+	mdelay(10);
+
+	aty_st_8(BUS_CNTL+1, 0xA0, par);
+	mdelay(10);
+	
+	reset_clocks(par, &pll.ct, 1);
+	mdelay(10);
+
+	// something about power management
+	aty_st_8(LCD_INDEX, 0x08, par);
+	aty_st_8(LCD_DATA, 0x0A, par);
+	aty_st_8(LCD_INDEX, 0x08, par);
+	aty_st_8(LCD_DATA+3, 0x02, par);
+	aty_st_8(LCD_INDEX, 0x08, par);
+	aty_st_8(LCD_DATA, 0x0B, par);
+	mdelay(2);
+	
+	// enable display requests, enable CRTC
+	aty_st_8(CRTC_GEN_CNTL+3, 0x02, par);
+	// disable display
+	aty_st_8(CRTC_GEN_CNTL, 0x40, par);
+	// disable display requests, disable CRTC
+	aty_st_8(CRTC_GEN_CNTL+3, 0x04, par);
+	mdelay(10);
+
+	aty_st_pll_ct(PLL_YCLK_CNTL, 0x25, par);
+
+	aty_st_le16(CUSTOM_MACRO_CNTL, 0x0179, par);
+	aty_st_le16(CUSTOM_MACRO_CNTL+2, 0x005E, par);
+	aty_st_le16(CUSTOM_MACRO_CNTL+2, card->custom_macro_cntl>>16, par);
+	aty_st_8(CUSTOM_MACRO_CNTL+1,
+		 (card->custom_macro_cntl>>8) & 0xff, par);
+
+	aty_st_le32(MEM_ADDR_CONFIG, card->mem_addr_config, par);
+	aty_st_le32(MEM_CNTL, card->mem_cntl, par);
+	aty_st_le32(EXT_MEM_CNTL, card->ext_mem_cntl, par);
+
+	aty_st_8(CONFIG_STAT0, 0xA0 | card->mem_type, par);
+
+	aty_st_pll_ct(PLL_YCLK_CNTL, 0x01, par);
+	mdelay(15);
+	aty_st_pll_ct(PLL_YCLK_CNTL, card->pll_yclk_cntl, par);
+	mdelay(1);
+	
+	reset_clocks(par, &pll.ct, 0);
+	mdelay(50);
+	reset_clocks(par, &pll.ct, 0);
+	mdelay(50);
+
+	// enable extended register block
+	aty_st_8(BUS_CNTL+3, 0x7B, par);
+	mdelay(1);
+	// disable extended register block
+	aty_st_8(BUS_CNTL+3, 0x73, par);
+
+	aty_st_8(CONFIG_STAT0, 0x80 | card->mem_type, par);
+
+	// disable display requests, disable CRTC
+	aty_st_8(CRTC_GEN_CNTL+3, 0x04, par);
+	// disable mapping registers in VGA aperture
+	aty_st_8(CONFIG_CNTL, aty_ld_8(CONFIG_CNTL, par) & ~0x04, par);
+	mdelay(50);
+	// enable display requests, enable CRTC
+	aty_st_8(CRTC_GEN_CNTL+3, 0x02, par);
+
+	// make GPIO's 14,15,16 all inputs
+	aty_st_8(LCD_INDEX, 0x07, par);
+	aty_st_8(LCD_DATA+3, 0x00, par);
+
+	// enable the display
+	aty_st_8(CRTC_GEN_CNTL, 0x00, par);
+	mdelay(17);
+	// reset the memory controller
+	aty_st_8(GEN_TEST_CNTL+1, 0x02, par);
+	mdelay(15);
+	aty_st_8(GEN_TEST_CNTL+1, 0x00, par);
+	mdelay(30);
+
+	// enable extended register block
+	aty_st_8(BUS_CNTL+3,
+		 (u8)(aty_ld_8(BUS_CNTL+3, par) | 0x08),
+		 par);
+	// set FIFO size to 512 (PIO)
+	aty_st_le32(GUI_CNTL,
+		    aty_ld_le32(GUI_CNTL, par) & ~0x3,
+		    par);
+
+	// enable CRT and disable lcd
+	aty_st_8(LCD_INDEX, 0x01, par);
+	temp = aty_ld_le32(LCD_DATA, par);
+	temp = (temp | 0x01) & ~0x02;
+	aty_st_le32(LCD_DATA, temp, par);
+	return 0;
+}
+