patch-2.1.107 linux/drivers/video/atyfb.c

Next file: linux/drivers/video/compatcon.c
Previous file: linux/drivers/video/aty.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.106/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
@@ -1,7 +1,8 @@
 /*
- *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI/Open Firmware
+ *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
  *
  *	Copyright (C) 1997 Geert Uytterhoeven
+ *	Copyright (C) 1998 Bernd Harries
  *
  *  This driver is partly based on the PowerMac console driver:
  *
@@ -20,6 +21,14 @@
  *  more details.
  */
 
+/******************************************************************************
+
+  TODO:
+
+    - support arbitrary video modes
+
+******************************************************************************/
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -36,10 +45,14 @@
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/nvram.h>
-#include <linux/vc_ioctl.h>
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#include <asm/vc_ioctl.h>
+#endif
 #include <asm/io.h>
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
+#endif
 
 #include "aty.h"
 #include "fbcon.h"
@@ -48,20 +61,28 @@
 #include "fbcon-cfb32.h"
 
 
+#ifndef __powerpc__
+#define eieio()		/* Enforce In-order Execution of I/O */
+#endif
+
 static int currcon = 0;
 static struct display fb_disp;
-static struct fb_info fb_info;
-static struct { u_char red, green, blue, pad; } palette[256];
 
 static char atyfb_name[16] = "ATY Mach64";
 
 struct atyfb_par {
-    int vmode;
-    int cmode;
+    union {
+	/* this should contain chipset specific mode information */
+	struct {
+	    int vmode;
+	    int cmode;
+	} gx, gt, vt;
+    } hw;
     u_int vxres;	/* virtual screen size */
     u_int vyres;
     int xoffset;	/* virtual screen position */
     int yoffset;
+    int accel;
 };
 
 
@@ -103,34 +124,27 @@
 #define CMODE_32		2	/* 32 (actually 24) bits/pixel */
 
 
-static int default_video_mode = VMODE_NVRAM;
-static int default_color_mode = CMODE_NVRAM;
-
-static struct atyfb_par default_par;
-static struct atyfb_par current_par;
-
+static int default_vmode = VMODE_NVRAM;
+static int default_cmode = CMODE_NVRAM;
 
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
 /*
  * Addresses in NVRAM where video mode and pixel size are stored.
  */
 #define NV_VMODE	0x140f
 #define NV_CMODE	0x1410
+#endif /* CONFIG_PMAC || CONFIG_CHRP */
+
 
 /*
- * Horizontal and vertical resolution information.
+ * Horizontal and vertical resolution for each mode.
  */
-extern struct vmode_attr {
+static struct vmode_attr {
 	int	hres;
 	int	vres;
 	int	vfreq;
 	int	interlaced;
-} vmode_attrs[VMODE_MAX];
-
-
-/*
- * Horizontal and vertical resolution for each mode.
- */
-static struct vmode_attr vmode_attrs[VMODE_MAX] = {
+} vmode_attrs[VMODE_MAX] = {
     {512, 384, 60, 1},
     {512, 384, 60},
     {640, 480, 50, 1},
@@ -182,6 +196,7 @@
 	{0x72d, VMODE_832_624_75},	/* 16" RGB (Goldfish) */
 	{0x730, VMODE_768_576_50I},	/* PAL (Alternate) */
 	{0x73a, VMODE_1152_870_75},	/* 3rd party 19" */
+	{0x73f, VMODE_640_480_67},	/* no sense lines connected at all */
 	{-1,	VMODE_640_480_60},	/* catch-all, must be last */
 };
 
@@ -196,44 +211,59 @@
 }
 
 struct aty_cmap_regs {
-    unsigned char windex;
-    unsigned char lut;
-    unsigned char mask;
-    unsigned char rindex;
-    unsigned char cntl;
+    u8 windex;
+    u8 lut;
+    u8 mask;
+    u8 rindex;
+    u8 cntl;
 };
 
 typedef struct aty_regvals {
-    int offset[3];		/* first pixel address */
+    u32 offset[3];		/* first pixel address */
 
-    int crtc_h_sync_strt_wid[3];	/* depth dependent */
-    int crtc_gen_cntl[3];
-    int mem_cntl[3];
-
-    int crtc_h_tot_disp;	/* mode dependent */
-    int crtc_v_tot_disp;
-    int crtc_v_sync_strt_wid;
-    int crtc_off_pitch;
+    u32 crtc_h_sync_strt_wid[3];	/* depth dependent */
+    u32 crtc_gen_cntl[3];
+    u32 mem_cntl[3];
+
+    u32 crtc_h_tot_disp;	/* mode dependent */
+    u32 crtc_v_tot_disp;
+    u32 crtc_v_sync_strt_wid;
+    u32 crtc_off_pitch;
 
-    unsigned char clock_val[2];	/* vals for 20 and 21 */
+    u8 clock_val[2];	/* vals for 20 and 21 */
 } aty_regvals;
 
 struct rage_regvals {
-    int h_total, h_sync_start, h_sync_width;
-    int v_total, v_sync_start, v_sync_width;
-    int h_sync_neg, v_sync_neg;
+    u32 h_total, h_sync_start, h_sync_width;
+    u32 v_total, v_sync_start, v_sync_width;
+    u32 h_sync_neg, v_sync_neg;
 };
 
-static int aty_vram_reqd(const struct atyfb_par *par);
-static struct aty_regvals *get_aty_struct(int vmode);
-
-static unsigned long frame_buffer;
+struct fb_info_aty {
+    struct fb_info fb_info;
+    unsigned long ati_regbase_phys;
+    unsigned long ati_regbase;
+    unsigned long frame_buffer_phys;
+    unsigned long frame_buffer;
+    u8 chip_class;
+    u8 pixclock_lim_8;	/* ps, <= 8 bpp */
+    u8 pixclock_lim_hi;	/* ps, > 8 bpp */
+    u32 total_vram;
+    struct aty_cmap_regs *aty_cmap_regs;
+    struct { u8 red, green, blue, pad; } palette[256];
+    struct atyfb_par default_par;
+    struct atyfb_par current_par;
+};
 
-static int total_vram;		/* total amount of video memory, bytes */
-static int chip_type;		/* what chip type was detected */
+#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
 
-static unsigned long ati_regbase;
-static struct aty_cmap_regs *aty_cmap_regs;
+static int aty_vram_reqd(const struct atyfb_par *par);
+static struct aty_regvals *get_aty_struct(int vmode, struct fb_info_aty *info);
 
 #include "ati-gx.h"
 #include "ati-gt.h"
@@ -291,11 +321,54 @@
 	&aty_vt_reg_init_20
 };
 
+
+#define CLASS_GX	1
+#define CLASS_CT	2
+#define CLASS_VT	3
+#define CLASS_GT	4
+
+struct aty_features {
+    u16 pci_id;
+    u16 chip_type;
+    const char *name;
+    u8 chip_class;
+    u8 pixclock_lim_8;	/* MHz, <= 8 bpp (not sure about these limits!) */
+    u8 pixclock_lim_hi;	/* MHz, > 8 bpp (not sure about these limits!) */
+} aty_features[] __initdata = {
+    /* mach64GX family */
+    { 0x4758, 0x00d7, "mach64GX (ATI888GX00)", CLASS_GX, 135, 80 },
+    { 0x4358, 0x0057, "mach64CX (ATI888CX00)", CLASS_GX, 135, 80 },
+
+    /* mach64CT family */
+    { 0x4354, 0x4354, "mach64CT (ATI264CT)", CLASS_CT, 135, 80 },
+    { 0x4554, 0x4554, "mach64ET (ATI264ET)", CLASS_CT, 135, 80 },
+
+    /* mach64CT family / mach64VT class */
+    { 0x5654, 0x5654, "mach64VT (ATI264VT)", CLASS_VT, 160, 135 },
+    { 0x5655, 0x5655, "mach64VTB (ATI264VTB)", CLASS_VT, 160, 135 },
+    { 0x5656, 0x5656, "mach64VT4 (ATI264VT4)", CLASS_VT, 160, 135 },
+
+    /* mach64CT family / mach64GT (3D RAGE) class */
+    { 0x4742, 0x4742, "3D RAGE PRO (BGA, AGP)", CLASS_GT, 240, 240 },
+    { 0x4744, 0x4744, "3D RAGE PRO (BGA, AGP, 1x only)", CLASS_GT, 240, 240 },
+    { 0x4749, 0x4749, "3D RAGE PRO (BGA, PCI)", CLASS_GT, 240, 240 },
+    { 0x4750, 0x4750, "3D RAGE PRO (PQFP, PCI)", CLASS_GT, 240, 240 },
+    { 0x4751, 0x4751, "3D RAGE PRO (PQFP, PCI, limited 3D)", CLASS_GT, 240, 240 },
+    { 0x4754, 0x4754, "3D RAGE (GT)", CLASS_GT, 200, 200 },
+    { 0x4755, 0x4755, "3D RAGE II+ (GTB)", CLASS_GT, 200, 200 },
+    { 0x4756, 0x4756, "3D RAGE IIC", CLASS_GT, 200, 200 },
+    { 0x4c47, 0x4c47, "3D RAGE LT", CLASS_GT, 200, 200 },
+};
+
+
     /*
      *  Interface used by the world
      */
 
 unsigned long atyfb_init(unsigned long mem_start);
+#ifdef CONFIG_FB_OF
+void atyfb_of_init(struct device_node *dp);
+#endif
 void atyfb_setup(char *options, int *ints);
 
 static int atyfb_open(struct fb_info *info);
@@ -332,6 +405,12 @@
 #ifdef CONFIG_FBCON_CFB8
 static struct display_switch fbcon_aty8;
 #endif
+#ifdef CONFIG_FBCON_CFB16
+static struct display_switch fbcon_aty16;
+#endif
+#ifdef CONFIG_FBCON_CFB32
+static struct display_switch fbcon_aty32;
+#endif
 
 
 #ifdef CONFIG_FB_COMPAT_XPMAC
@@ -341,13 +420,19 @@
 extern int (*console_set_cmap_ptr)(struct fb_cmap *, int, int,
 				   struct fb_info *);
 static int atyfb_console_setmode(struct vc_mode *, int);
-#endif
+#endif /* CONFIG_FB_COMPAT_XPMAC */
 
 
     /*
      *  Internal routines
      */
 
+static int aty_init(struct fb_info_aty *info, const char *name);
+#ifndef CONFIG_FB_OF
+static int store_video_par(char *videopar, unsigned char m64_num);
+static char *strtoke(char *s, const char *ct);
+#endif
+
 static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 			 u_int *transp, struct fb_info *info);
 static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
@@ -357,352 +442,542 @@
 
 static struct fb_ops atyfb_ops = {
     atyfb_open, atyfb_release, atyfb_get_fix, atyfb_get_var, atyfb_set_var,
-    atyfb_get_cmap, atyfb_set_cmap, atyfb_pan_display, NULL, atyfb_ioctl
+    atyfb_get_cmap, atyfb_set_cmap, atyfb_pan_display, atyfb_ioctl
 };
 
 
 static inline int aty_vram_reqd(const struct atyfb_par *par)
 {
-    return (par->vxres*par->vyres) << par->cmode;
+    return (par->vxres*par->vyres) << par->hw.gx.cmode;
 }
 
-extern inline unsigned aty_ld_le32(volatile unsigned long addr)
+static inline u32 aty_ld_le32(volatile unsigned int regindex,
+			      struct fb_info_aty *info)
 {
-    register unsigned long temp = ati_regbase,val;
+    unsigned long temp;
+    u32 val;
 
-    asm("lwbrx %0,%1,%2": "=r"(val):"r"(addr), "r"(temp));
+#ifdef __powerpc__
+    temp = info->ati_regbase;
+    asm("lwbrx %0,%1,%2": "=r"(val):"r"(regindex), "r"(temp));
+#else
+    temp = info->ati_regbase+regindex;
+    val = le32_to_cpu(*((volatile u32 *)(temp)));
+#endif
     return val;
 }
 
-extern inline void aty_st_le32(volatile unsigned long addr, unsigned val)
+static inline void aty_st_le32(volatile unsigned int regindex, u32 val,
+			       struct fb_info_aty *info)
 {
-    register unsigned long temp = ati_regbase;
+    unsigned long temp;
 
-    asm("stwbrx %0,%1,%2": : "r"(val), "r"(addr), "r"(temp):"memory");
+#ifdef __powerpc__
+    temp = info->ati_regbase;
+    asm("stwbrx %0,%1,%2": : "r"(val), "r"(regindex), "r"(temp):"memory");
+#else
+    temp = info->ati_regbase+regindex;
+    *((volatile u32 *)(temp)) = cpu_to_le32(val);
+#endif
 }
 
-extern inline unsigned char aty_ld_8(volatile unsigned long addr)
+static inline u8 aty_ld_8(volatile unsigned int regindex,
+			  struct fb_info_aty *info)
 {
-    return *(char *) ((long) addr + (long) ati_regbase);
+    return *(volatile u8 *)(info->ati_regbase+regindex);
 }
 
-extern inline void aty_st_8(volatile unsigned long addr, unsigned char val)
+static inline void aty_st_8(volatile unsigned int regindex, u8 val,
+			    struct fb_info_aty *info)
 {
-    *(unsigned char *) (addr + (unsigned long) ati_regbase) = val;
+    *(volatile u8 *)(info->ati_regbase+regindex) = val;
 }
 
-static void aty_st_514(int offset, char val)
+    /*
+     *  All writes to draw engine registers are automatically routed through a
+     *  32-bit-wide, 16-entry-deep command FIFO ...
+     *  Register writes to registers with DWORD offsets less than 40h are not
+     *  FIFOed.
+     *  (from Chapter 5 of the Mach64 Programmer's Guide)
+     */
+
+static inline void wait_for_fifo(u16 entries, struct fb_info_aty *info)
+{
+    while ((aty_ld_le32(FIFO_STAT, info) & 0xffff) >
+	   ((u32)(0x8000 >> entries)));
+}
+
+static inline void wait_for_idle(struct fb_info_aty *info)
 {
-    aty_WaitQueue(5);
-    aty_st_8(DAC_CNTL, 1);
-    aty_st_8(DAC_W_INDEX, offset & 0xff);	/* right addr byte */
-    aty_st_8(DAC_DATA, (offset >> 8) & 0xff);	/* left addr byte */
+    wait_for_fifo(16, info);
+    while ((aty_ld_le32(GUI_STAT, info) & 1)!= 0);
+}
+
+static void reset_engine(struct fb_info_aty *info)
+{
+    /* reset engine */
+    aty_st_le32(GEN_TEST_CNTL,
+		aty_ld_le32(GEN_TEST_CNTL, info) & ~GUI_ENGINE_ENABLE, info);
+    /* enable engine */
+    aty_st_le32(GEN_TEST_CNTL,
+		aty_ld_le32(GEN_TEST_CNTL, info) | GUI_ENGINE_ENABLE, info);
+    /* ensure engine is not locked up by clearing any FIFO or */
+    /* HOST errors */
+    aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, info) | BUS_HOST_ERR_ACK |
+			  BUS_FIFO_ERR_ACK, info);
+}
+
+static void init_engine(const struct atyfb_par *par, struct fb_info_aty *info)
+{
+    u32 pitch_value;
+
+    /* determine modal information from global mode structure */
+    pitch_value = par->vxres;
+
+#if 0
+    if (par->hw.gx.cmode == CMODE_24) {
+	/* In 24 bpp, the engine is in 8 bpp - this requires that all */
+	/* horizontal coordinates and widths must be adjusted */
+	pitch_value = pitch_value * 3;
+    }
+#endif
+
+    /* Reset engine, enable, and clear any engine errors */
+    reset_engine(info);
+    /* 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, info);
+    aty_st_le32(MEM_VGA_RP_SEL, 0x00010000, info);
+
+    /* ---- Setup standard engine context ---- */
+
+    /* All GUI registers here are FIFOed - therefore, wait for */
+    /* the appropriate number of empty FIFO entries */
+    wait_for_fifo(14, info);
+
+    /* enable all registers to be loaded for context loads */
+    aty_st_le32(CONTEXT_MASK, 0xFFFFFFFF, info);
+
+    /* set destination pitch to modal pitch, set offset to zero */
+    aty_st_le32(DST_OFF_PITCH, (pitch_value / 8) << 22, info);
+
+    /* zero these registers (set them to a known state) */
+    aty_st_le32(DST_Y_X, 0, info);
+    aty_st_le32(DST_HEIGHT, 0, info);
+    aty_st_le32(DST_BRES_ERR, 0, info);
+    aty_st_le32(DST_BRES_INC, 0, info);
+    aty_st_le32(DST_BRES_DEC, 0, info);
+
+    /* set destination drawing attributes */
+    aty_st_le32(DST_CNTL, DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
+			  DST_X_LEFT_TO_RIGHT, info);
+
+    /* set source pitch to modal pitch, set offset to zero */
+    aty_st_le32(SRC_OFF_PITCH, (pitch_value / 8) << 22, info);
+
+    /* set these registers to a known state */
+    aty_st_le32(SRC_Y_X, 0, info);
+    aty_st_le32(SRC_HEIGHT1_WIDTH1, 1, info);
+    aty_st_le32(SRC_Y_X_START, 0, info);
+    aty_st_le32(SRC_HEIGHT2_WIDTH2, 1, info);
+
+    /* set source pixel retrieving attributes */
+    aty_st_le32(SRC_CNTL, SRC_LINE_X_LEFT_TO_RIGHT, info);
+
+    /* set host attributes */
+    wait_for_fifo(13, info);
+    aty_st_le32(HOST_CNTL, 0, info);
+
+    /* set pattern attributes */
+    aty_st_le32(PAT_REG0, 0, info);
+    aty_st_le32(PAT_REG1, 0, info);
+    aty_st_le32(PAT_CNTL, 0, info);
+
+    /* set scissors to modal size */
+    aty_st_le32(SC_LEFT, 0, info);
+    aty_st_le32(SC_TOP, 0, info);
+    aty_st_le32(SC_BOTTOM, par->vyres-1, info);
+    aty_st_le32(SC_RIGHT, pitch_value-1, info);
+
+    /* set background color to minimum value (usually BLACK) */
+    aty_st_le32(DP_BKGD_CLR, 0, info);
+
+    /* set foreground color to maximum value (usually WHITE) */
+    aty_st_le32(DP_FRGD_CLR, 0xFFFFFFFF, info);
+
+    /* set write mask to effect all pixel bits */
+    aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF, info);
+
+    /* set foreground mix to overpaint and background mix to */
+    /* no-effect */
+    aty_st_le32(DP_MIX, FRGD_MIX_S | BKGD_MIX_D, info);
+
+    /* set primary source pixel channel to foreground color */
+    /* register */
+    aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR, info);
+
+    /* set compare functionality to false (no-effect on */
+    /* destination) */
+    wait_for_fifo(3, info);
+    aty_st_le32(CLR_CMP_CLR, 0, info);
+    aty_st_le32(CLR_CMP_MASK, 0xFFFFFFFF, info);
+    aty_st_le32(CLR_CMP_CNTL, 0, info);
+
+    /* set pixel depth */
+    wait_for_fifo(2, info);
+    switch(par->hw.gx.cmode) {
+#ifdef CONFIG_FBCON_CFB8
+	case CMODE_8:
+	    aty_st_le32(DP_PIX_WIDTH, HOST_8BPP | SRC_8BPP | DST_8BPP |
+				      BYTE_ORDER_LSB_TO_MSB,
+	    info);
+	    aty_st_le32(DP_CHAIN_MASK, 0x8080, info);
+	    break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
+	case CMODE_16:
+	    aty_st_le32(DP_PIX_WIDTH, HOST_15BPP | SRC_15BPP | DST_15BPP |
+				      BYTE_ORDER_LSB_TO_MSB,
+	    info);
+	    aty_st_le32(DP_CHAIN_MASK, 0x4210, info);
+	    break;
+#endif
+#if 0
+	case CMODE_24:
+	    aty_st_le32(DP_PIX_WIDTH, HOST_8BPP | SRC_8BPP | DST_8BPP |
+				      BYTE_ORDER_LSB_TO_MSB,
+	    info);
+	    aty_st_le32(DP_CHAIN_MASK, 0x8080, info);
+	    break;
+#endif
+#ifdef CONFIG_FBCON_CFB32
+	case CMODE_32:
+	    aty_st_le32(DP_PIX_WIDTH, HOST_32BPP | SRC_32BPP | DST_32BPP |
+				      BYTE_ORDER_LSB_TO_MSB, info);
+	    aty_st_le32(DP_CHAIN_MASK, 0x8080, info);
+	    break;
+#endif
+    }
+    /* insure engine is idle before leaving */
+    wait_for_idle(info);
+}
+
+static void aty_st_514(int offset, u8 val, struct fb_info_aty *info)
+{
+    aty_st_8(DAC_CNTL, 1, info);
+    /* right addr byte */
+    aty_st_8(DAC_W_INDEX, offset & 0xff, info);	
+    /* left addr byte */
+    aty_st_8(DAC_DATA, (offset >> 8) & 0xff, info);
     eieio();
-    aty_st_8(DAC_MASK, val);
+    aty_st_8(DAC_MASK, val, info);
     eieio();
-    aty_st_8(DAC_CNTL, 0);
+    aty_st_8(DAC_CNTL, 0, info);
 }
 
-static void aty_st_pll(int offset, char val)
+static void aty_st_pll(int offset, u8 val, struct fb_info_aty *info)
 {
-    aty_WaitQueue(3);
-    aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN);	/* write addr byte */
+    /* write addr byte */
+    aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN, info);
     eieio();
-    aty_st_8(CLOCK_CNTL + 2, val);	/* write the register value */
+    /* write the register value */
+    aty_st_8(CLOCK_CNTL + 2, val, info);
     eieio();
-    aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN);
+    aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN, info);
 }
 
-static struct aty_regvals *get_aty_struct(int vmode)
+static struct aty_regvals *get_aty_struct(int vmode, struct fb_info_aty *info)
 {
     int v = vmode - 1;
 
-    switch (chip_type) {
-	case MACH64_GT_ID:
-	    return aty_gt_reg_init[v];
+    switch (info->chip_class) {
+	case CLASS_GX:
+	    return aty_gx_reg_init[v];
 	    break;
-	case MACH64_VT_ID:
+	case CLASS_CT:
+	case CLASS_VT:
 	    return aty_vt_reg_init[v];
 	    break;
-	default: /* default to MACH64_GX_ID */
-	    return aty_gx_reg_init[v];
+	case CLASS_GT:
+	    return aty_gt_reg_init[v];
 	    break;
+	default:
+	    /* should NOT happen */
+	    return NULL;
     }
 }
 
-static int read_aty_sense(void)
+static int read_aty_sense(struct fb_info_aty *info)
 {
     int sense, i;
 
-    aty_st_le32(GP_IO, 0x31003100);	/* drive outputs high */
+    aty_st_le32(GP_IO, 0x31003100, info);	/* drive outputs high */
     __delay(200);
-    aty_st_le32(GP_IO, 0);		/* turn off outputs */
+    aty_st_le32(GP_IO, 0, info);		/* turn off outputs */
     __delay(2000);
-    i = aty_ld_le32(GP_IO);		/* get primary sense value */
+    i = aty_ld_le32(GP_IO, info);		/* 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);	/* drive A low */
+    aty_st_le32(GP_IO, 0x20000000, info);	/* drive A low */
     __delay(2000);
-    i = aty_ld_le32(GP_IO);
+    i = aty_ld_le32(GP_IO, info);
     sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
-    aty_st_le32(GP_IO, 0x20002000);	/* drive A high again */
+    aty_st_le32(GP_IO, 0x20002000, info);	/* drive A high again */
     __delay(200);
 
-    aty_st_le32(GP_IO, 0x10000000);	/* drive B low */
+    aty_st_le32(GP_IO, 0x10000000, info);	/* drive B low */
     __delay(2000);
-    i = aty_ld_le32(GP_IO);
+    i = aty_ld_le32(GP_IO, info);
     sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
-    aty_st_le32(GP_IO, 0x10001000);	/* drive B high again */
+    aty_st_le32(GP_IO, 0x10001000, info);	/* drive B high again */
     __delay(200);
 
-    aty_st_le32(GP_IO, 0x01000000);	/* drive C low */
+    aty_st_le32(GP_IO, 0x01000000, info);	/* drive C low */
     __delay(2000);
-    sense |= (aty_ld_le32(GP_IO) & 0x3000) >> 12;
-    aty_st_le32(GP_IO, 0);		/* turn off outputs */
+    sense |= (aty_ld_le32(GP_IO, info) & 0x3000) >> 12;
+    aty_st_le32(GP_IO, 0, info);		/* turn off outputs */
 
     return sense;
 }
 
-static void RGB514_Program(int cmode)
+static void RGB514_Program(int cmode, struct fb_info_aty *info)
 {
     typedef struct {
-	char pixel_dly;
-	char misc2_cntl;
-	char pixel_rep;
-	char pixel_cntl_index;
-	char pixel_cntl_v1;
+	u8 pixel_dly;
+	u8 misc2_cntl;
+	u8 pixel_rep;
+	u8 pixel_cntl_index;
+	u8 pixel_cntl_v1;
     } RGB514_DAC_Table;
 
     static RGB514_DAC_Table RGB514DAC_Tab[8] = {
-	{0, 0x41, 0x03, 0x71, 0x45},	// 8bpp
-	{0, 0x45, 0x04, 0x0c, 0x01},	// 555
-	{0, 0x45, 0x06, 0x0e, 0x00},	// XRGB
+	{0, 0x41, 0x03, 0x71, 0x45},	/* 8bpp */
+	{0, 0x45, 0x04, 0x0c, 0x01},	/* 555 */
+	{0, 0x45, 0x06, 0x0e, 0x00},	/* XRGB */
     };
     RGB514_DAC_Table *pDacProgTab;
 
     pDacProgTab = &RGB514DAC_Tab[cmode];
 
-    aty_st_514(0x90, 0x00);
-    aty_st_514(0x04, pDacProgTab->pixel_dly);
-    aty_st_514(0x05, 0x00);
+    aty_st_514(0x90, 0x00, info);
+    aty_st_514(0x04, pDacProgTab->pixel_dly, info);
+    aty_st_514(0x05, 0x00, info);
 
-    aty_st_514(0x2, 0x1);
-    aty_st_514(0x71, pDacProgTab->misc2_cntl);
-    aty_st_514(0x0a, pDacProgTab->pixel_rep);
+    aty_st_514(0x2, 0x1, info);
+    aty_st_514(0x71, pDacProgTab->misc2_cntl, info);
+    aty_st_514(0x0a, pDacProgTab->pixel_rep, info);
 
-    aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1);
+    aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1,
+	       info);
 }
 
-static void set_off_pitch(const struct atyfb_par *par)
+static void set_off_pitch(const struct atyfb_par *par,
+			  struct fb_info_aty *info)
 {
     u32 pitch, offset;
 
     pitch = par->vxres>>3;
-    offset = ((par->yoffset*par->vxres+par->xoffset)>>3)<<par->cmode;
-    aty_st_le32(CRTC_OFF_PITCH, pitch<<22 | offset);
-    if (chip_type == MACH64_GT_ID) {
-	/* Is this OK for other chips? */
-	aty_st_le32(DST_OFF_PITCH, pitch<<22 | offset);
-	aty_st_le32(SRC_OFF_PITCH, pitch<<22 | offset);
-    }
+    offset = ((par->yoffset*par->vxres+par->xoffset)>>3)<<par->hw.gx.cmode;
+    aty_st_le32(CRTC_OFF_PITCH, pitch<<22 | offset, info);
 }
 
-static void atyfb_set_par(struct atyfb_par *par)
+static void atyfb_set_par(struct atyfb_par *par, struct fb_info_aty *info)
 {
-    int i, hres;
-    struct aty_regvals *init = get_aty_struct(par->vmode);
-    int vram_type = aty_ld_le32(CONFIG_STAT0) & 7;
+    int i, j, hres;
+    struct aty_regvals *init = get_aty_struct(par->hw.gx.vmode, info);
+    int vram_type = aty_ld_le32(CONFIG_STAT0, info) & 7;
 
     if (init == 0)	/* paranoia, shouldn't get here */
-	panic("aty: display mode %d not supported", par->vmode);
+	panic("aty: display mode %d not supported", par->hw.gx.vmode);
 
-    current_par = *par;
-    hres = vmode_attrs[par->vmode-1].hres;
+    info->current_par = *par;
+    hres = vmode_attrs[par->hw.gx.vmode-1].hres;
 
-    /* clear FIFO errors */
-    aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_HOST_ERR_ACK
-			  | BUS_FIFO_ERR_ACK);
-
-    /* Reset engine */
-    i = aty_ld_le32(GEN_TEST_CNTL);
-    aty_st_le32(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE);
-    eieio();
-    aty_WaitIdleEmpty();
-    aty_st_le32(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE);
-    aty_WaitIdleEmpty();
-
-    if ( chip_type != MACH64_GT_ID ) {
-	i = aty_ld_le32(CRTC_GEN_CNTL);
-	aty_st_le32(CRTC_GEN_CNTL, i | CRTC_EXT_DISP_EN);
-    }
-
-    if ( chip_type == MACH64_GX_ID ) {
-	i = aty_ld_le32(GEN_TEST_CNTL);
-	aty_st_le32(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN );
-    }
-
-    switch (chip_type) {
-	case MACH64_VT_ID:
-	    aty_st_pll(PLL_MACRO_CNTL, 0xb5);
-	    aty_st_pll(PLL_REF_DIV, 0x2d);
-	    aty_st_pll(PLL_GEN_CNTL, 0x14);
-	    aty_st_pll(MCLK_FB_DIV, 0xbd);
-	    aty_st_pll(PLL_VCLK_CNTL, 0x0b);
-	    aty_st_pll(VCLK_POST_DIV, init->clock_val[0]);
-	    aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]);
-	    aty_st_pll(VCLK1_FB_DIV, 0xd6);
-	    aty_st_pll(VCLK2_FB_DIV, 0xee);
-	    aty_st_pll(VCLK3_FB_DIV, 0xf8);
-	    aty_st_pll(PLL_XCLK_CNTL, 0x0);
-	    aty_st_pll(PLL_TEST_CTRL, 0x0);
-	    aty_st_pll(PLL_TEST_COUNT, 0x0);
+    if (info->chip_class != CLASS_GT) {
+	i = aty_ld_le32(CRTC_GEN_CNTL, info);
+	aty_st_le32(CRTC_GEN_CNTL, i | CRTC_EXT_DISP_EN, info);
+    }
+
+    if (info->chip_class == CLASS_GX) {
+	i = aty_ld_le32(GEN_TEST_CNTL, info);
+	aty_st_le32(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN, info);
+    }
+
+    switch (info->chip_class) {
+	case CLASS_GX:
+	    RGB514_Program(par->hw.gx.cmode, info);
+	    wait_for_idle(info);
+	    aty_st_514(0x06, 0x02, info);
+	    aty_st_514(0x10, 0x01, info);
+	    aty_st_514(0x70, 0x01, info);
+	    aty_st_514(0x8f, 0x1f, info);
+	    aty_st_514(0x03, 0x00, info);
+	    aty_st_514(0x05, 0x00, info);
+	    aty_st_514(0x20, init->clock_val[0], info);
+	    aty_st_514(0x21, init->clock_val[1], info);
+	    break;
+	case CLASS_CT:
+	case CLASS_VT:
+	    aty_st_pll(VPLL_CNTL, 0xb5, info);
+	    aty_st_pll(PLL_REF_DIV, 0x2d, info);
+	    aty_st_pll(PLL_GEN_CNTL, 0x14, info);
+	    aty_st_pll(MCLK_FB_DIV, 0xbd, info);
+	    aty_st_pll(PLL_VCLK_CNTL, 0x0b, info);
+	    aty_st_pll(VCLK_POST_DIV, init->clock_val[0], info);
+	    aty_st_pll(VCLK0_FB_DIV, init->clock_val[1], info);
+	    aty_st_pll(VCLK1_FB_DIV, 0xd6, info);
+	    aty_st_pll(VCLK2_FB_DIV, 0xee, info);
+	    aty_st_pll(VCLK3_FB_DIV, 0xf8, info);
+	    aty_st_pll(PLL_EXT_CNTL, 0x0, info);
+	    aty_st_pll(PLL_TEST_CTRL, 0x0, info);
+	    aty_st_pll(PLL_TEST_COUNT, 0x0, info);
 	    break;
-	case MACH64_GT_ID:
+	case CLASS_GT:
 	    if (vram_type == 5) {
-		aty_st_pll(0, 0xcd);
-		aty_st_pll(PLL_MACRO_CNTL,
-			   par->vmode >= VMODE_1024_768_60 ? 0xd3: 0xd5);
-		aty_st_pll(PLL_REF_DIV, 0x21);
-		aty_st_pll(PLL_GEN_CNTL, 0x44);
-		aty_st_pll(MCLK_FB_DIV, 0xe8);
-		aty_st_pll(PLL_VCLK_CNTL, 0x03);
-		aty_st_pll(VCLK_POST_DIV, init->offset[0]);
-		aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
-		aty_st_pll(VCLK1_FB_DIV, 0x8e);
-		aty_st_pll(VCLK2_FB_DIV, 0x9e);
-		aty_st_pll(VCLK3_FB_DIV, 0xc6);
-		aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
-		aty_st_pll(12, 0xa6);
-		aty_st_pll(13, 0x1b);
+		aty_st_pll(MPLL_CNTL, 0xcd, info);
+		aty_st_pll(VPLL_CNTL,
+			   par->hw.gx.vmode >= VMODE_1024_768_60 ? 0xd3
+								 : 0xd5,
+			   info);
+		aty_st_pll(PLL_REF_DIV, 0x21, info);
+		aty_st_pll(PLL_GEN_CNTL, 0x44, info);
+		aty_st_pll(MCLK_FB_DIV, 0xe8, info);
+		aty_st_pll(PLL_VCLK_CNTL, 0x03, info);
+		aty_st_pll(VCLK_POST_DIV, init->offset[0], info);
+		aty_st_pll(VCLK0_FB_DIV, init->offset[1], info);
+		aty_st_pll(VCLK1_FB_DIV, 0x8e, info);
+		aty_st_pll(VCLK2_FB_DIV, 0x9e, info);
+		aty_st_pll(VCLK3_FB_DIV, 0xc6, info);
+		aty_st_pll(PLL_EXT_CNTL, init->offset[2], info);
+		aty_st_pll(DLL_CNTL, 0xa6, info);
+		aty_st_pll(VFC_CNTL, 0x1b, info);
 	    } else {
-		aty_st_pll(PLL_MACRO_CNTL, 0xd5);
-		aty_st_pll(PLL_REF_DIV, 0x21);
-		aty_st_pll(PLL_GEN_CNTL, 0xc4);
-		aty_st_pll(MCLK_FB_DIV, 0xda);
-		aty_st_pll(PLL_VCLK_CNTL, 0x03);
+		aty_st_pll(VPLL_CNTL, 0xd5, info);
+		aty_st_pll(PLL_REF_DIV, 0x21, info);
+		aty_st_pll(PLL_GEN_CNTL, 0xc4, info);
+		aty_st_pll(MCLK_FB_DIV, 0xda, info);
+		aty_st_pll(PLL_VCLK_CNTL, 0x03, info);
 		/* offset actually holds clock values */
-		aty_st_pll(VCLK_POST_DIV, init->offset[0]);
-		aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
-		aty_st_pll(VCLK1_FB_DIV, 0x8e);
-		aty_st_pll(VCLK2_FB_DIV, 0x9e);
-		aty_st_pll(VCLK3_FB_DIV, 0xc6);
-		aty_st_pll(PLL_TEST_CTRL, 0x0);
-		aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
-		aty_st_pll(12, 0xa0);
-		aty_st_pll(13, 0x1b);
+		aty_st_pll(VCLK_POST_DIV, init->offset[0], info);
+		aty_st_pll(VCLK0_FB_DIV, init->offset[1], info);
+		aty_st_pll(VCLK1_FB_DIV, 0x8e, info);
+		aty_st_pll(VCLK2_FB_DIV, 0x9e, info);
+		aty_st_pll(VCLK3_FB_DIV, 0xc6, info);
+		aty_st_pll(PLL_TEST_CTRL, 0x0, info);
+		aty_st_pll(PLL_EXT_CNTL, init->offset[2], info);
+		aty_st_pll(DLL_CNTL, 0xa0, info);
+		aty_st_pll(VFC_CNTL, 0x1b, info);
 	    }
 	    break;
-	default:
-	    RGB514_Program(par->cmode);
-	    aty_WaitIdleEmpty();
-	    aty_st_514(0x06, 0x02);
-	    aty_st_514(0x10, 0x01);
-	    aty_st_514(0x70, 0x01);
-	    aty_st_514(0x8f, 0x1f);
-	    aty_st_514(0x03, 0x00);
-	    aty_st_514(0x05, 0x00);
-	    aty_st_514(0x20, init->clock_val[0]);
-	    aty_st_514(0x21, init->clock_val[1]);
-	    break;
     }
 
-    aty_ld_8(DAC_REGS);	/* clear counter */
-    aty_WaitIdleEmpty();
-
-    aty_st_le32(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp);
-    aty_st_le32(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[par->cmode]);
-    aty_st_le32(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp);
-    aty_st_le32(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid);
-
-    aty_st_8(CLOCK_CNTL, 0);
-    aty_st_8(CLOCK_CNTL, CLOCK_STROBE);
-
-    aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0);
-
-    set_off_pitch(par);
-
-    if (chip_type == MACH64_GT_ID) {
-	aty_st_le32(BUS_CNTL, 0x7b23a040);
-
-	/* need to set DSP values !! assume sdram */
-	i = init->crtc_gen_cntl[0] - (0x100000 * par->cmode);
-	if ( vram_type == 5 )
-	    i = init->crtc_gen_cntl[1] - (0x100000 * par->cmode);
-	aty_st_le32(DSP_CONFIG, i);
-
-	i = aty_ld_le32(MEM_CNTL) & MEM_SIZE_ALIAS;
-	if ( vram_type == 5 ) {
-	    i |= ((1 * par->cmode) << 26) | 0x4215b0;
-	    aty_st_le32(DSP_ON_OFF,sgram_dsp[par->vmode-1][par->cmode]);
-
-	//aty_st_le32(CLOCK_CNTL,8192);
-	} else {
-	    i |= ((1 * par->cmode) << 26) | 0x300090;
-	    aty_st_le32(DSP_ON_OFF, init->mem_cntl[par->cmode]);
-	}
-
-	aty_st_le32(MEM_CNTL, i);
-	aty_st_le32(EXT_MEM_CNTL, 0x5000001);
+    aty_ld_8(DAC_REGS, info);	/* clear counter */
+    wait_for_idle(info);
 
-	/* if (total_vram > 0x400000)	
-	    i |= 0x538; this not been verified on > 4Megs!! */
-    } else {
-
-/* The magic constant below translates into:
-* 5   = No RDY delay, 1 wait st for mem write, increment during burst transfer
-* 9   = DAC access delayed, 1 wait state for DAC
-* 0   = Disables interupts for FIFO errors
-* e   = Allows FIFO to generate 14 wait states before generating error
-* 1   = DAC snooping disabled, ROM disabled
-* 0   = ROM page at 0 (disabled so doesn't matter)
-* f   = 15 ROM wait states (disabled so doesn't matter)
-* f   = 15 BUS wait states (I'm not sure this applies to PCI bus types)
-* at some point it would be good to experiment with bench marks to see if
-* we can gain some speed by fooling with the wait states etc.
-*/
-	if (chip_type == MACH64_VT_ID)
-	    aty_st_le32(BUS_CNTL, 0x680000f9);
-	else
-	    aty_st_le32(BUS_CNTL, 0x590e10ff);
-
-	switch (total_vram) {
-	    case 0x00100000:
-		aty_st_le32(MEM_CNTL, vt_mem_cntl[0][par->cmode]);
-		break;
-	    case 0x00200000:
-		aty_st_le32(MEM_CNTL, vt_mem_cntl[1][par->cmode]);
-		break;
-	    case 0x00400000:
-		aty_st_le32(MEM_CNTL, vt_mem_cntl[2][par->cmode]);
-		break;
-	    default:
-		i = aty_ld_le32(MEM_CNTL) & 0x000F;
-		aty_st_le32(MEM_CNTL,
-			    (init->mem_cntl[par->cmode] & 0xFFFFFFF0) | i);
-	}
-    }
-/* These magic constants are harder to figure out
-* on the vt chipset bit 2 set makes the screen brighter
-* and bit 15 makes the screen black! But nothing else
-* seems to matter for the vt DAC_CNTL
-*/
-    switch (chip_type) {
-	case MACH64_GT_ID:
-	    i = 0x86010102;
+    aty_st_le32(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp, info);
+    aty_st_le32(CRTC_H_SYNC_STRT_WID,
+		init->crtc_h_sync_strt_wid[par->hw.gx.cmode], info);
+    aty_st_le32(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp, info);
+    aty_st_le32(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid, info);
+
+    aty_st_8(CLOCK_CNTL, 0, info);
+    aty_st_8(CLOCK_CNTL, CLOCK_STROBE, info);
+
+    aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0, info);
+
+    set_off_pitch(par, info);
+
+    switch (info->chip_class) {
+	case CLASS_GX:
+	    /* The magic constant below translates into:
+	     * 5  = No RDY delay, 1 wait st for mem write, increment during
+	     *      burst transfer
+	     * 9  = DAC access delayed, 1 wait state for DAC
+	     * 0  = Disables interupts for FIFO errors
+	     * e  = Allows FIFO to generate 14 wait states before generating
+	     *      error
+	     * 1  = DAC snooping disabled, ROM disabled
+	     * 0  = ROM page at 0 (disabled so doesn't matter)
+	     * f  = 15 ROM wait states (disabled so doesn't matter)
+	     * f  = 15 BUS wait states (I'm not sure this applies to PCI bus
+	     *      types)
+	     * at some point it would be good to experiment with bench marks to
+	     * if we can gain some speed by fooling with the wait states etc.
+	     */
+	    aty_st_le32(BUS_CNTL, 0x890e20f1 /* 0x590e10ff */, info);
+	    j = 0x47012100;
+	    j = 0x47052100;
 	    break;
-	case MACH64_VT_ID:
-	    i = 0x87010184;
+
+	case CLASS_CT:
+	case CLASS_VT:
+	    aty_st_le32(BUS_CNTL, 0x680000f9, info);
+	    switch (info->total_vram) {
+		case 0x00100000:
+		    aty_st_le32(MEM_CNTL, vt_mem_cntl[0][par->hw.gx.cmode],
+				info);
+		    break;
+		case 0x00200000:
+		    aty_st_le32(MEM_CNTL, vt_mem_cntl[1][par->hw.gx.cmode],
+				info);
+		    break;
+		case 0x00400000:
+		    aty_st_le32(MEM_CNTL, vt_mem_cntl[2][par->hw.gx.cmode],
+				info);
+		    break;
+		default:
+		    i = aty_ld_le32(MEM_CNTL, info) & 0x000F;
+		    aty_st_le32(MEM_CNTL,
+				(init->mem_cntl[par->hw.gx.cmode] &
+				 0xFFFFFFF0) | i,
+				info);
+	    }
+	    j = 0x87010184;
 	    break;
-	default:
-	    i = 0x47012100;
+
+	case CLASS_GT:
+	    aty_st_le32(BUS_CNTL, 0x7b23a040, info);
+
+	    /* need to set DSP values !! assume sdram */
+	    i = init->crtc_gen_cntl[0] - (0x100000 * par->hw.gx.cmode);
+	    if ( vram_type == 5 )
+		i = init->crtc_gen_cntl[1] - (0x100000 * par->hw.gx.cmode);
+	    aty_st_le32(DSP_CONFIG, i, info);
+
+	    i = aty_ld_le32(MEM_CNTL, info) & MEM_SIZE_ALIAS;
+	    if ( vram_type == 5 ) {
+		i |= ((1 * par->hw.gx.cmode) << 26) | 0x4215b0;
+		aty_st_le32(DSP_ON_OFF,
+			    sgram_dsp[par->hw.gx.vmode-1][par->hw.gx.cmode],
+			    info);
+
+		/* aty_st_le32(CLOCK_CNTL, 8192, info); */
+	    } else {
+		i |= ((1 * par->hw.gx.cmode) << 26) | 0x300090;
+		aty_st_le32(DSP_ON_OFF, init->mem_cntl[par->hw.gx.cmode],
+			    info);
+	    }
+	    aty_st_le32(MEM_CNTL, i, info);
+	    aty_st_le32(EXT_MEM_CNTL, 0x5000001, info);
+
+	    /* if (info->total_vram > 0x400000)	
+		i |= 0x538; this not been verified on > 4Megs!! */
+
+	    j = 0x86010102;
 	    break;
     }
 
-    aty_st_le32(DAC_CNTL, i);
-    aty_st_8(DAC_MASK, 0xff);
+    /* These magic constants (variable j) are harder to figure out
+    * on the vt chipset bit 2 set makes the screen brighter
+    * and bit 15 makes the screen black! But nothing else
+    * seems to matter for the vt DAC_CNTL
+    */
+    aty_st_le32(DAC_CNTL, j, info);
+    aty_st_8(DAC_MASK, 0xff, info);
 
-    switch (par->cmode) {
+    switch (par->hw.gx.cmode) {
 	case CMODE_16:
 	    i = CRTC_PIX_WIDTH_15BPP; break;
 	/*case CMODE_24: */
@@ -713,28 +988,35 @@
 	    i = CRTC_PIX_WIDTH_8BPP; break;
     }
 
-    if (chip_type != MACH64_GT_ID) {
-	aty_st_le32(CRTC_INT_CNTL, 0x00000002);
-	aty_st_le32(GEN_TEST_CNTL, GUI_ENGINE_ENABLE | BLOCK_WRITE_ENABLE);	/* gui_en block_en */
-	i |= init->crtc_gen_cntl[par->cmode];
+    if (info->chip_class != CLASS_GT) {
+	aty_st_le32(CRTC_INT_CNTL, 0x00000002, info);
+	aty_st_le32(GEN_TEST_CNTL, GUI_ENGINE_ENABLE | BLOCK_WRITE_ENABLE,
+		    info);	/* gui_en block_en */
+	i |= init->crtc_gen_cntl[par->hw.gx.cmode];
     }
     /* Gentlemen, start your crtc engine */
-    aty_st_le32(CRTC_GEN_CNTL, CRTC_EXT_DISP_EN | CRTC_ENABLE | i);
+    aty_st_le32(CRTC_GEN_CNTL, CRTC_EXT_DISP_EN | CRTC_ENABLE | i, info);
+
+    /* Initialize the graphics engine */
+    if (par->accel & FB_ACCELF_TEXT)
+	init_engine(par, info);
 
 #ifdef CONFIG_FB_COMPAT_XPMAC
-    display_info.height = vmode_attrs[par->vmode-1].vres;
-    display_info.width = vmode_attrs[par->vmode-1].hres;
-    display_info.depth = 8<<par->cmode;
-    display_info.pitch = par->vxres<<par->cmode;
-    display_info.mode = par->vmode;
-    strcpy(display_info.name, atyfb_name);
-    display_info.fb_address =
-	iopa(((chip_type != MACH64_GT_ID) ?
-	     frame_buffer + init->offset[par->cmode] : frame_buffer));
-    display_info.cmap_adr_address = iopa((unsigned long)&aty_cmap_regs->windex);
-    display_info.cmap_data_address = iopa((unsigned long)&aty_cmap_regs->lut);
-    display_info.disp_reg_address = iopa(ati_regbase);
-#endif /* CONFIG_FB_COMPAT_XPMAC) */
+    if (console_fb_info == &info->fb_info) {
+	display_info.height = vmode_attrs[par->hw.gx.vmode-1].vres;
+	display_info.width = vmode_attrs[par->hw.gx.vmode-1].hres;
+	display_info.depth = 8<<par->hw.gx.cmode;
+	display_info.pitch = par->vxres<<par->hw.gx.cmode;
+	display_info.mode = par->hw.gx.vmode;
+	strcpy(display_info.name, atyfb_name);
+	display_info.fb_address = info->frame_buffer_phys;
+	if (info->chip_class == CLASS_VT)
+	    display_info.fb_address += init->offset[par->hw.gx.cmode];
+	display_info.cmap_adr_address = info->ati_regbase_phys+0xc0;
+	display_info.cmap_data_address = info->ati_regbase_phys+0xc1;
+	display_info.disp_reg_address = info->ati_regbase_phys;
+    }
+#endif /* CONFIG_FB_COMPAT_XPMAC */
 }
 
 
@@ -761,30 +1043,67 @@
 
 
 static int encode_fix(struct fb_fix_screeninfo *fix,
-		      const struct atyfb_par *par)
+		      const struct atyfb_par *par, struct fb_info_aty *info)
 {
     struct aty_regvals *init;
 
     memset(fix, 0, sizeof(struct fb_fix_screeninfo));
 
     strcpy(fix->id, atyfb_name);
-    init = get_aty_struct(par->vmode);
+    init = get_aty_struct(par->hw.gx.vmode, info);
     /*
      *  FIXME: This will cause problems on non-GT chips, because the frame
      *  buffer must be aligned to a page
      */
-    fix->smem_start = (char *)((chip_type != MACH64_GT_ID)
-	    ? frame_buffer + init->offset[par->cmode] : frame_buffer);
-    fix->smem_len = (u32)total_vram;
-    if (fix->smem_len > 0x7ff000)
-	fix->smem_len = 0x7ff000;	/* last page is MMIO */
-    fix->mmio_start = (char *)(ati_regbase & ~0xfff);
-    fix->mmio_len = 4096;
+    fix->smem_start = (char *)info->frame_buffer_phys;
+    if (info->chip_class == CLASS_VT)
+	fix->smem_start += init->offset[par->hw.gx.cmode];
+    fix->smem_len = (u32)info->total_vram;
+
+#ifdef __LITTLE_ENDIAN
+    /*
+     *  Last page of 8 MB little-endian aperture is MMIO
+     *  FIXME: we should use the auxillary aperture instead so we can acces the
+     *  full 8 MB of video RAM on 8 MB boards
+     */
+    if (fix->smem_len > 0x800000-PAGE_SIZE)
+	fix->smem_len = 0x800000-PAGE_SIZE;
+#endif
+    /*
+     *  Reg Block 0 (CT-compatible block) is at ati_regbase_phys
+     *  Reg Block 1 (multimedia extensions) is at ati_regbase_phys-0x400
+     */
+    switch (info->chip_class) {
+	case CLASS_GX:
+	    fix->mmio_start = (char *)info->ati_regbase_phys;
+	    fix->mmio_len = 0x400;
+	    fix->accel = FB_ACCEL_ATI_MACH64GX;
+	    break;
+	case CLASS_CT:
+	    fix->mmio_start = (char *)info->ati_regbase_phys;
+	    fix->mmio_len = 0x400;
+	    fix->accel = FB_ACCEL_ATI_MACH64CT;
+	    break;
+	case CLASS_VT:
+	    fix->mmio_start = (char *)(info->ati_regbase_phys-0x400);
+	    fix->mmio_len = 0x800;
+	    fix->accel = FB_ACCEL_ATI_MACH64VT;
+	    break;
+	case CLASS_GT:
+	    fix->mmio_start = (char *)(info->ati_regbase_phys-0x400);
+	    fix->mmio_len = 0x800;
+	    fix->accel = FB_ACCEL_ATI_MACH64GT;
+	    break;
+	default:
+	    fix->mmio_start = NULL;
+	    fix->mmio_len = 0;
+	    fix->accel = 0;
+    }
     fix->type = FB_TYPE_PACKED_PIXELS;
     fix->type_aux = 0;
-    fix->line_length = par->vxres<<par->cmode;
-    fix->visual = par->cmode == CMODE_8 ? FB_VISUAL_PSEUDOCOLOR
-					: FB_VISUAL_TRUECOLOR;
+    fix->line_length = par->vxres<<par->hw.gx.cmode;
+    fix->visual = par->hw.gx.cmode == CMODE_8 ? FB_VISUAL_PSEUDOCOLOR
+					      : FB_VISUAL_TRUECOLOR;
     fix->ywrapstep = 0;
     fix->xpanstep = 8;
     fix->ypanstep = 1;
@@ -794,7 +1113,7 @@
 
 
 static int decode_var(struct fb_var_screeninfo *var,
-		      struct atyfb_par *par)
+		      struct atyfb_par *par, struct fb_info_aty *info)
 {
     int xres = var->xres;
     int yres = var->yres;
@@ -804,30 +1123,30 @@
     /* This should support more video modes */
 
     if (xres <= 512 && yres <= 384)
-	par->vmode = VMODE_512_384_60;		/* 512x384, 60Hz */
+	par->hw.gx.vmode = VMODE_512_384_60;	/* 512x384, 60Hz */
     else if (xres <= 640 && yres <= 480)
-	par->vmode = VMODE_640_480_67;		/* 640x480, 67Hz */
+	par->hw.gx.vmode = VMODE_640_480_67;	/* 640x480, 67Hz */
     else if (xres <= 640 && yres <= 870)
-	par->vmode = VMODE_640_870_75P;		/* 640x870, 75Hz (portrait) */
+	par->hw.gx.vmode = VMODE_640_870_75P;	/* 640x870, 75Hz (portrait) */
     else if (xres <= 768 && yres <= 576)
-	par->vmode = VMODE_768_576_50I;		/* 768x576, 50Hz (PAL full frame) */
+	par->hw.gx.vmode = VMODE_768_576_50I;	/* 768x576, 50Hz (PAL full frame) */
     else if (xres <= 800 && yres <= 600)
-	par->vmode = VMODE_800_600_75;		/* 800x600, 75Hz */
+	par->hw.gx.vmode = VMODE_800_600_75;	/* 800x600, 75Hz */
     else if (xres <= 832 && yres <= 624)
-	par->vmode = VMODE_832_624_75;		/* 832x624, 75Hz */
+	par->hw.gx.vmode = VMODE_832_624_75;	/* 832x624, 75Hz */
     else if (xres <= 1024 && yres <= 768)
-	par->vmode = VMODE_1024_768_75;		/* 1024x768, 75Hz */
+	par->hw.gx.vmode = VMODE_1024_768_75;	/* 1024x768, 75Hz */
     else if (xres <= 1152 && yres <= 870)
-	par->vmode = VMODE_1152_870_75;		/* 1152x870, 75Hz */
+	par->hw.gx.vmode = VMODE_1152_870_75;	/* 1152x870, 75Hz */
     else if (xres <= 1280 && yres <= 960)
-	par->vmode = VMODE_1280_960_75;		/* 1280x960, 75Hz */
+	par->hw.gx.vmode = VMODE_1280_960_75;	/* 1280x960, 75Hz */
     else if (xres <= 1280 && yres <= 1024)
-	par->vmode = VMODE_1280_1024_75;	/* 1280x1024, 75Hz */
+	par->hw.gx.vmode = VMODE_1280_1024_75;	/* 1280x1024, 75Hz */
     else
 	return -EINVAL;
 
-    xres = vmode_attrs[par->vmode-1].hres;
-    yres = vmode_attrs[par->vmode-1].vres;
+    xres = vmode_attrs[par->hw.gx.vmode-1].hres;
+    yres = vmode_attrs[par->hw.gx.vmode-1].vres;
 
     if (var->xres_virtual <= xres)
 	par->vxres = xres;
@@ -844,23 +1163,29 @@
 	return -EINVAL;
 
     if (bpp <= 8)
-	par->cmode = CMODE_8;
+	par->hw.gx.cmode = CMODE_8;
     else if (bpp <= 16)
-	par->cmode = CMODE_16;
+	par->hw.gx.cmode = CMODE_16;
     else if (bpp <= 32)
-	par->cmode = CMODE_32;
+	par->hw.gx.cmode = CMODE_32;
     else
 	return -EINVAL;
 
-    if (aty_vram_reqd(par) > total_vram)
+    if (var->accel_flags & FB_ACCELF_TEXT)
+	par->accel = FB_ACCELF_TEXT;
+    else
+	par->accel = 0;
+
+    if (aty_vram_reqd(par) > info->total_vram)
 	return -EINVAL;
 
     /* Check if we know about the wanted video mode */
-    init = get_aty_struct(par->vmode);
-    if (init == NULL || init->crtc_h_sync_strt_wid[par->cmode] == 0 ||
-	(chip_type != MACH64_GT_ID &&
-	 init->crtc_gen_cntl[par->cmode] == 0) ||
-	(chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5 &&
+    init = get_aty_struct(par->hw.gx.vmode, info);
+    if (init == NULL || init->crtc_h_sync_strt_wid[par->hw.gx.cmode] == 0 ||
+	(info->chip_class != CLASS_GT &&
+	 init->crtc_gen_cntl[par->hw.gx.cmode] == 0) ||
+	(info->chip_class == CLASS_GT &&
+	 (aty_ld_le32(CONFIG_STAT0, info) & 7) == 5 &&
 	 init->crtc_gen_cntl[1] == 0))
 	return -EINVAL;
 
@@ -873,18 +1198,31 @@
 }
 
 static int encode_var(struct fb_var_screeninfo *var,
-		      const struct atyfb_par *par)
+		      const struct atyfb_par *par,
+		      struct fb_info_aty *info)
 {
+    int vmode = par->hw.gx.vmode;
+    int cmode = par->hw.gx.cmode;
+    struct aty_regvals *init = get_aty_struct(vmode, info);
+    u_int h_total, h_disp;
+    u_int h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
+    u_int v_total, v_disp;
+    u_int v_sync_strt, v_sync_wid, v_sync_pol;
+    u_int xtalin, vclk;
+    u8 pll_ref_div, vclk_fb_div, vclk_post_div, pll_ext_cntl;
+
     memset(var, 0, sizeof(struct fb_var_screeninfo));
+    if (!init)
+	return -EINVAL;
 
-    var->xres = vmode_attrs[par->vmode-1].hres;
-    var->yres = vmode_attrs[par->vmode-1].vres;
+    var->xres = vmode_attrs[vmode-1].hres;
+    var->yres = vmode_attrs[vmode-1].vres;
     var->xres_virtual = par->vxres;
     var->yres_virtual = par->vyres;
     var->xoffset = par->xoffset;
     var->yoffset = par->yoffset;
     var->grayscale = 0;
-    switch (par->cmode) {
+    switch (cmode) {
 	case CMODE_8:
 	    var->bits_per_pixel = 8;
 	    var->red.offset = 0;
@@ -927,21 +1265,97 @@
     var->activate = 0;
     var->height = -1;
     var->width = -1;
-    var->accel = /* FB_ACCEL_ATY */ 0;
     var->vmode = FB_VMODE_NONINTERLACED;
-    var->left_margin = var->right_margin = 64;	/* guesses */
-    var->upper_margin = var->lower_margin = 32;
-    var->hsync_len = 64;
-    var->vsync_len = 2;
-
-    /* no long long support in the kernel :-( */
-    /* this splittig trick will work if xres > 232 */
-    var->pixclock = 1000000000/
-	(var->left_margin+var->xres+var->right_margin+var->hsync_len);
-    var->pixclock *= 1000;
-    var->pixclock /= vmode_attrs[par->vmode-1].vfreq*
-	 (var->upper_margin+var->yres+var->lower_margin+var->vsync_len);
-    var->sync = 0;
+    var->accel_flags = par->accel;
+
+    h_total = (init->crtc_h_tot_disp<<3) & 0xff8;
+    h_disp = (init->crtc_h_tot_disp>>13) & 0x7f8;
+    h_sync_strt = ((init->crtc_h_sync_strt_wid[cmode]<<3) & 0x7f8) |
+		  ((init->crtc_h_sync_strt_wid[cmode]>>1) & 0x800);
+    h_sync_dly = (init->crtc_h_sync_strt_wid[cmode]>>8) & 0x7;
+    h_sync_wid = (init->crtc_h_sync_strt_wid[cmode]>>13) & 0xf8;
+    h_sync_pol = (init->crtc_h_sync_strt_wid[cmode]>>21) & 0x1;
+
+    v_total = init->crtc_v_tot_disp & 0x7ff;
+    v_disp = (init->crtc_v_tot_disp>>16) & 0x7ff;
+    v_sync_strt = init->crtc_v_sync_strt_wid & 0x7ff;
+    v_sync_wid = (init->crtc_v_sync_strt_wid>>16) & 0x1f;
+    v_sync_pol = (init->crtc_v_sync_strt_wid>>21) & 0x1;
+
+    var->left_margin = (h_total+8)-h_sync_strt-h_sync_wid;
+    var->right_margin = h_sync_strt-(h_disp+8);
+    var->upper_margin = (v_total+1)-v_sync_strt-v_sync_wid;
+    var->lower_margin = v_sync_strt-(v_disp+1);
+    var->hsync_len = h_sync_wid;
+    var->vsync_len = v_sync_wid;
+    var->sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+		(v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT);
+
+    xtalin = 69841;	/* 14.31818 MHz */
+    switch (info->chip_class) {
+	case CLASS_GX:
+	    {
+		/* haven't read the IBM RGB514 PDF yet, so just guesses */
+		static u32 gx_vclk[VMODE_MAX] = {
+		        0,	/* vmode  1 */	
+		        0,	/* vmode  2 */	
+		        0,	/* vmode  3 */	
+		        0,	/* vmode  4 */	
+		    39722,	/* vmode  5 (25.175 MHz) */
+		    33333,	/* vmode  6 (30 MHz) */
+		        0,	/* vmode  7 */	
+		        0,	/* vmode  8 */	
+		    27778,	/* vmode  9 (36 MHz) */
+		    25000,	/* vmode 10 (40 MHz) */
+		    20000,	/* vmode 11 (50 MHz) */
+		    20000,	/* vmode 12 (50 MHz) */
+		    17544,	/* vmode 13 (57 MHz) */
+		    15385,	/* vmode 14 (65 MHz) */
+		    13333,	/* vmode 15 (75 MHz) */
+		        0,	/* vmode 16 */	
+		    12821,	/* vmode 17 (78 MHz) */
+		    10000,	/* vmode 18 (100 MHz) */
+		     7937,	/* vmode 19 (126 MHz) */
+		     7407	/* vmode 20 (135 MHz) */
+		};
+		vclk = gx_vclk[vmode-1];
+	    }
+	    break;
+	case CLASS_CT:
+	case CLASS_GT:
+	case CLASS_VT:
+	    if (info->chip_class == CLASS_GT) {
+		pll_ref_div = 0x21;
+		vclk_post_div = init->offset[0];
+		vclk_fb_div = init->offset[1];
+		pll_ext_cntl = init->offset[2];
+	    } else {
+		pll_ref_div = 0x2d;
+		vclk_post_div = init->clock_val[0];
+		vclk_fb_div = init->clock_val[1];
+		pll_ext_cntl = 0x0;
+	    }
+	    vclk = xtalin*pll_ref_div;
+	    switch (vclk_post_div & 3) {
+		case 0:
+		    vclk *= (pll_ext_cntl & 0x10) ? 3 : 1;
+		    break;
+		case 1:
+		    if (pll_ext_cntl & 0x10)
+			return -EINVAL;
+		    vclk *= 2;
+		    break;
+		case 2:
+		    vclk *= (pll_ext_cntl & 0x10) ? 6 : 4;
+		    break;
+		case 3:
+		    vclk *= (pll_ext_cntl & 0x10) ? 12 : 8;
+		    break;
+	    }
+	    vclk /= 2*vclk_fb_div;
+	    break;
+    }
+    var->pixclock = vclk;
 
     return 0;
 }
@@ -949,12 +1363,13 @@
 
 static void init_par(struct atyfb_par *par, int vmode, int cmode)
 {
-    par->vmode = vmode;
-    par->cmode = cmode;
+    par->hw.gx.vmode = vmode;
+    par->hw.gx.cmode = cmode;
     par->vxres = vmode_attrs[vmode-1].hres;
     par->vyres = vmode_attrs[vmode-1].vres;
     par->xoffset = 0;
     par->yoffset = 0;
+    par->accel = FB_ACCELF_TEXT;
 }
 
 
@@ -965,13 +1380,14 @@
 static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info)
 {
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
     struct atyfb_par par;
 
     if (con == -1)
-	par = default_par;
+	par = info2->default_par;
     else
-	decode_var(&fb_display[con].var, &par);
-    encode_fix(fix, &par);
+	decode_var(&fb_display[con].var, &par, info2);
+    encode_fix(fix, &par, info2);
     return 0;
 }
 
@@ -983,10 +1399,12 @@
 static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info)
 {
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+
     if (con == -1)
-	encode_var(var, &default_par);
+	encode_var(var, &info2->default_par, (struct fb_info_aty *)info);
     else
-	*var=fb_display[con].var;
+	*var = fb_display[con].var;
     return 0;
 }
 
@@ -998,10 +1416,10 @@
 static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info)
 {
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
     struct atyfb_par par;
     struct display *display;
-    int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
-    int err;
+    int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel, accel, err;
     int activate = var->activate;
 
     if (con >= 0)
@@ -1009,10 +1427,10 @@
     else
 	display = &fb_disp;	/* used during initialization */
 
-    if ((err = decode_var(var, &par)))
+    if ((err = decode_var(var, &par, info2)))
 	return err;
 
-    encode_var(var, &par);
+    encode_var(var, &par, (struct fb_info_aty *)info);
 
     if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 	oldxres = display->var.xres;
@@ -1020,14 +1438,15 @@
 	oldvxres = display->var.xres_virtual;
 	oldvyres = display->var.yres_virtual;
 	oldbpp = display->var.bits_per_pixel;
+	oldaccel = display->var.accel_flags;
 	display->var = *var;
 	if (oldxres != var->xres || oldyres != var->yres ||
 	    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
-	    oldbpp != var->bits_per_pixel) {
+	    oldbpp != var->bits_per_pixel || oldaccel != var->accel_flags) {
 	    struct fb_fix_screeninfo fix;
 
-	    encode_fix(&fix, &par);
-	    display->screen_base = (u_char *)fix.smem_start;
+	    encode_fix(&fix, &par, info2);
+	    display->screen_base = (char *)info2->frame_buffer;
 	    display->visual = fix.visual;
 	    display->type = fix.type;
 	    display->type_aux = fix.type_aux;
@@ -1036,29 +1455,32 @@
 	    display->line_length = fix.line_length;
 	    display->can_soft_blank = 1;
 	    display->inverse = 0;
-	    switch (par.cmode) {
+	    accel = var->accel_flags & FB_ACCELF_TEXT;
+	    switch (par.hw.gx.cmode) {
+#ifdef CONFIG_FBCON_CFB8
 		case CMODE_8:
-#if 1
-		    display->dispsw = &fbcon_cfb8;
-#else
-		    display->dispsw = &fbcon_aty8;
-#endif
+		    display->dispsw = accel ? &fbcon_aty8 : &fbcon_cfb8;
 		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB16
 		case CMODE_16:
-		    display->dispsw = &fbcon_cfb16;
+		    display->dispsw = accel ? &fbcon_aty16 : &fbcon_cfb16;
 		    break;
+#endif
+#ifdef CONFIG_FBCON_CFB32
 		case CMODE_32:
-		    display->dispsw = &fbcon_cfb32;
+		    display->dispsw = accel ? &fbcon_aty32 : &fbcon_cfb32;
 		    break;
+#endif
 		default:
 		    display->dispsw = NULL;
 		    break;
 	    }
-	    if (fb_info.changevar)
-		(*fb_info.changevar)(con);
+	    if (info->changevar)
+		(*info->changevar)(con);
 	}
 	if (con == currcon)
-	    atyfb_set_par(&par);
+	    atyfb_set_par(&par, info2);
 	if (oldbpp != var->bits_per_pixel) {
 	    if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 		return err;
@@ -1079,18 +1501,19 @@
 static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
 			     struct fb_info *info)
 {
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
     u32 xres, yres, xoffset, yoffset;
-    struct atyfb_par *par = &current_par;
+    struct atyfb_par *par = &info2->current_par;
 
-    xres = vmode_attrs[par->vmode-1].hres;
-    yres = vmode_attrs[par->vmode-1].vres;
+    xres = vmode_attrs[par->hw.gx.vmode-1].hres;
+    yres = vmode_attrs[par->hw.gx.vmode-1].vres;
     xoffset = (var->xoffset+7) & ~7;
     yoffset = var->yoffset;
     if (xoffset+xres > par->vxres || yoffset+yres > par->vyres)
 	return -EINVAL;
     par->xoffset = xoffset;
     par->yoffset = yoffset;
-    set_off_pitch(par);
+    set_off_pitch(par, info2);
     return 0;
 }
 
@@ -1146,225 +1569,328 @@
      *  Initialisation
      */
 
-__initfunc(unsigned long atyfb_init(unsigned long mem_start))
-{
-#ifdef __powerpc__
-    /* We don't want to be called like this. */
-    /* We rely on Open Firmware (offb) instead. */
-    return mem_start;
-#else /* !__powerpc__ */
-    /* To be merged with Bernd's mach64fb */
-    return mem_start;
-#endif /* !__powerpc__ */
-}
-
-
-unsigned long atyfb_of_init(unsigned long mem_start, struct device_node *dp)
+__initfunc(static int aty_init(struct fb_info_aty *info, const char *name))
 {
-    int i, err, sense;
+    u32 chip_id;
+    u32 i;
+    int j, k, err, sense;
     struct fb_var_screeninfo var;
     struct aty_regvals *init;
-    unsigned long addr;
-    unsigned char bus, devfn;
-    unsigned short cmd;
+    const char *chipname = NULL;
+    u8 rev;
 
-    if (dp->next)
-	printk("Warning: only using first ATI card detected\n");
-    if (dp->n_addrs != 1 && dp->n_addrs != 3)
-	printk("Warning: expecting 1 or 3 addresses for ATY (got %d)",
-	       dp->n_addrs);
-
-    ati_regbase = (int)ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000);
-    aty_cmap_regs = (struct aty_cmap_regs *)(ati_regbase + 0xC0);
-
-    /* enable memory-space accesses using config-space command register */
-    if (pci_device_loc(dp, &bus, &devfn) == 0) {
-	pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
-	if (cmd != 0xffff) {
-	    cmd |= PCI_COMMAND_MEMORY;
-	    pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+    info->aty_cmap_regs = (struct aty_cmap_regs *)(info->ati_regbase+0xc0);
+    chip_id = aty_ld_le32(CONFIG_CHIP_ID, info);
+    for (j = 0; j < (sizeof(aty_features)/sizeof(*aty_features)); j++)
+	if (aty_features[j].chip_type == (chip_id & CFG_CHIP_TYPE)) {
+	    chipname = aty_features[j].name;
+	    info->chip_class = aty_features[j].chip_class;
+	    info->pixclock_lim_8 = 1000000/aty_features[j].pixclock_lim_8;
+	    info->pixclock_lim_hi = 1000000/aty_features[j].pixclock_lim_hi;
 	}
+    if (!chipname) {
+	printk("atyfb: Unknown Mach64 0x%04x\n", chip_id & CFG_CHIP_TYPE);
+	return 0;
+    } else
+	printk("atyfb: %s [", chipname);
+    rev = (chip_id & CFG_CHIP_REV)>>24;
+    switch ((rev>>3) & 7) {
+	case MACH64_FND_SGS:
+	    printk("SGS");
+	    break;
+	case MACH64_FND_NEC:
+	    printk("NEC");
+	    break;
+	case MACH64_FND_UMC:
+	    printk("UMC");
+	    break;
     }
-    chip_type = (aty_ld_le32(CONFIG_CHIP_ID) & CFG_CHIP_TYPE);
+    printk(" %c%d]\n", 'A'+(rev & 7), rev>>6);
 
-    i = aty_ld_le32(MEM_CNTL);
-    if (chip_type != MACH64_GT_ID)
+    i = aty_ld_le32(MEM_CNTL, info);
+    if (info->chip_class != CLASS_GT)
 	switch (i & MEM_SIZE_ALIAS) {
 	    case MEM_SIZE_512K:
-		total_vram = 0x80000;
+		info->total_vram = 0x80000;
 		break;
 	    case MEM_SIZE_1M:
-		total_vram = 0x100000;
+		info->total_vram = 0x100000;
 		break;
 	    case MEM_SIZE_2M:
-		total_vram = 0x200000;
+		info->total_vram = 0x200000;
 		break;
 	    case MEM_SIZE_4M:
-		total_vram = 0x400000;
+		info->total_vram = 0x400000;
 		break;
 	    case MEM_SIZE_6M:
-		total_vram = 0x600000;
+		info->total_vram = 0x600000;
 		break;
 	    case MEM_SIZE_8M:
-		total_vram = 0x800000;
+		info->total_vram = 0x800000;
 		break;
 	    default:
-		total_vram = 0x80000;
+		info->total_vram = 0x80000;
 	}
     else
 	switch (i & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
 	    case MEM_SIZE_512K:
-		total_vram = 0x80000;
+		info->total_vram = 0x80000;
 		break;
 	    case MEM_SIZE_1M:
-		total_vram = 0x100000;
+		info->total_vram = 0x100000;
 		break;
 	    case MEM_SIZE_2M_GTB:
-		total_vram = 0x200000;
+		info->total_vram = 0x200000;
 		break;
 	    case MEM_SIZE_4M_GTB:
-		total_vram = 0x400000;
+		info->total_vram = 0x400000;
 		break;
 	    case MEM_SIZE_6M_GTB:
-		total_vram = 0x600000;
+		info->total_vram = 0x600000;
 		break;
 	    case MEM_SIZE_8M_GTB:
-		total_vram = 0x800000;
+		info->total_vram = 0x800000;
 		break;
 	    default:
-		total_vram = 0x80000;
+		info->total_vram = 0x80000;
 	}
+#ifdef CONFIG_ATARI	/* this is definately not the wrong way to set this */
+    if ((info->total_vram == 0x400000) || (info->total_vram == 0x800000)) {
+	/* protect GUI-regs if complete Aperture is VRAM */
+	info->total_vram -= 0x00001000;
+    }
+#endif
 
-#if 1
-    printk("aty_display_init: node = %p, addrs = ", dp->node);
-    printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size);
-    printk(", intrs =");
-    for (i = 0; i < dp->n_intrs; ++i)
-    printk(" %x", dp->intrs[i].line);
-    printk("\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %p\n",
-	   (int)ati_regbase, bus, devfn, total_vram, aty_cmap_regs);
-#endif
-
-    /* Map in frame buffer */
-    addr = dp->addrs[0].address;
-
-    /* use the big-endian aperture (??) */
-    addr += 0x800000;
-    frame_buffer = (unsigned long)__ioremap(addr, 0x800000, _PAGE_WRITETHRU);
-
-    if (default_video_mode != -1) {
-	sense = read_aty_sense();
-	printk("monitor sense = %x\n", sense);
-	if (default_video_mode == VMODE_NVRAM) {
-	    default_video_mode = nvram_read_byte(NV_VMODE);
-	    init = get_aty_struct(default_video_mode);
-	    if (default_video_mode <= 0 ||
-		default_video_mode > VMODE_MAX || init == 0)
-		default_video_mode = VMODE_CHOOSE;
-	}
-	if (default_video_mode == VMODE_CHOOSE)
-	    default_video_mode = map_monitor_sense(sense);
+#if 0
+    printk("aty_init: regbase = %lx, frame_buffer = %lx, total_vram = %x\n",
+	   info->ati_regbase, info->frame_buffer, info->total_vram);
+#endif
 
-	init = get_aty_struct(default_video_mode);
-	if (!init)
-	    default_video_mode = VMODE_640_480_60;
-    }
+    sense = read_aty_sense(info);
+    printk("monitor sense = %x\n", sense);
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
+    if (default_vmode == VMODE_NVRAM) {
+	default_vmode = nvram_read_byte(NV_VMODE);
+	init = get_aty_struct(default_vmode, info);
+	if (default_vmode <= 0 || default_vmode > VMODE_MAX || init == 0)
+	    default_vmode = VMODE_CHOOSE;
+    }
+    if (default_vmode == VMODE_CHOOSE)
+	default_vmode = map_monitor_sense(sense);
+#else /* !CONFIG_PMAC && !CONFIG_CHRP */
+    if (default_vmode == VMODE_NVRAM)
+	default_vmode = map_monitor_sense(sense);
+#endif /* !CONFIG_PMAC && !CONFIG_CHRP */
+
+    if (!(init = get_aty_struct(default_vmode, info)))
+	default_vmode = VMODE_640_480_60;
 
     /*
      * Reduce the pixel size if we don't have enough VRAM.
      */
 
-    if (default_color_mode == CMODE_NVRAM)
-	default_color_mode = nvram_read_byte(NV_CMODE);
-    if (default_color_mode < CMODE_8 ||
-	default_color_mode > CMODE_32)
-	default_color_mode = CMODE_8;
-
-    init_par(&default_par, default_video_mode, default_color_mode);
-    while (aty_vram_reqd(&default_par) > total_vram) {
-	while (default_color_mode > CMODE_8 &&
-	       aty_vram_reqd(&default_par) > total_vram) {
-	    --default_color_mode;
-	    init_par(&default_par, default_video_mode, default_color_mode);
+    if (default_cmode == CMODE_NVRAM)
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
+	default_cmode = nvram_read_byte(NV_CMODE);
+#else /* !CONFIG_PMAC && !CONFIG_CHRP */
+	default_cmode = CMODE_8;
+#endif /* !CONFIG_PMAC && !CONFIG_CHRP */
+    if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
+	default_cmode = CMODE_8;
+
+    init_par(&info->default_par, default_vmode, default_cmode);
+    while (aty_vram_reqd(&info->default_par) > info->total_vram) {
+	while (default_cmode > CMODE_8 &&
+	       aty_vram_reqd(&info->default_par) > info->total_vram) {
+	    --default_cmode;
+	    init_par(&info->default_par, default_vmode, default_cmode);
 	}
 	/*
-	 * adjust the video mode smaller if there still is not enough VRAM
+	 * Adjust the video mode smaller if there still is not enough VRAM
 	 */
-	if (aty_vram_reqd(&default_par) > total_vram)
+	if (aty_vram_reqd(&info->default_par) > info->total_vram)
 	    do {
-		default_video_mode--;
-		init_par(&default_par, default_video_mode, default_color_mode);
-		init = get_aty_struct(default_video_mode);
+		default_vmode--;
+		init_par(&info->default_par, default_vmode, default_cmode);
+		init = get_aty_struct(default_vmode, info);
 	    } while ((init == 0) &&
-		     (default_video_mode > VMODE_640_480_60));
+		     (default_vmode > VMODE_640_480_60));
     }
 
-    if (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5
+    if (info->chip_class == CLASS_GT &&
+	(aty_ld_le32(CONFIG_STAT0, info) & 7) == 5
 	&& init->crtc_gen_cntl[1] == 0) {
-	    default_video_mode = VMODE_640_480_67;
-	    default_color_mode = CMODE_8;
-	    init_par(&default_par, default_video_mode, default_color_mode);
+	    default_vmode = VMODE_640_480_67;
+	    default_cmode = CMODE_8;
+	    init_par(&info->default_par, default_vmode, default_cmode);
     }
 
-    switch (chip_type) {
-	case MACH64_GX_ID:
+    switch (info->chip_class) {
+	case CLASS_GX:
 	    strcat(atyfb_name, "GX");
 	    break;
-	case MACH64_VT_ID:
+	case CLASS_CT:
+	    strcat(atyfb_name, "CT");
+	    break;
+	case CLASS_VT:
 	    strcat(atyfb_name, "VT");
 	    break;
-	case MACH64_GT_ID:
+	case CLASS_GT:
 	    strcat(atyfb_name, "GT");
 	    break;
-	default:
-	    break;
     }
-    strcpy(fb_info.modename, atyfb_name);
-    fb_info.node = -1;
-    fb_info.fbops = &atyfb_ops;
-    fb_info.disp = &fb_disp;
-    fb_info.fontname[0] = '\0';
-    fb_info.changevar = NULL;
-    fb_info.switch_con = &atyfbcon_switch;
-    fb_info.updatevar = &atyfbcon_updatevar;
-    fb_info.blank = &atyfbcon_blank;
+    strcpy(info->fb_info.modename, atyfb_name);
+    info->fb_info.node = -1;
+    info->fb_info.fbops = &atyfb_ops;
+    info->fb_info.disp = &fb_disp;
+    info->fb_info.fontname[0] = '\0';
+    info->fb_info.changevar = NULL;
+    info->fb_info.switch_con = &atyfbcon_switch;
+    info->fb_info.updatevar = &atyfbcon_updatevar;
+    info->fb_info.blank = &atyfbcon_blank;
+
+    for (j = 0; j < 16; j++) {
+	k = color_table[j];
+	info->palette[j].red = default_red[k];
+	info->palette[j].green = default_grn[k];
+	info->palette[j].blue = default_blu[k];
+    }
 
-    err = register_framebuffer(&fb_info);
+    err = register_framebuffer(&info->fb_info);
     if (err < 0)
-	return mem_start;
+	return 0;
+    atyfb_set_par(&info->default_par, info);
+    encode_var(&var, &info->default_par, info);
+    atyfb_set_var(&var, -1, &info->fb_info);
+
+    printk("fb%d: %s frame buffer device on %s\n",
+	   GET_FB_IDX(info->fb_info.node), atyfb_name, name);
+    return 1;
+}
 
-    for (i = 0; i < 16; i++) {
-	int j = color_table[i];
-	palette[i].red = default_red[j];
-	palette[i].green = default_grn[j];
-	palette[i].blue = default_blu[j];
-    }
-    atyfb_set_par(&default_par);
-    encode_var(&var, &default_par);
-    atyfb_set_var(&var, -1, &fb_info);
+__initfunc(unsigned long atyfb_init(unsigned long mem_start))
+{
+#if defined(CONFIG_FB_OF)
+    /* We don't want to be called like this. */
+    /* We rely on Open Firmware (offb) instead. */
+#elif defined(CONFIG_PCI)
+    /* Anyone who wants to do a PCI probe for an ATI chip? */
+#elif defined(CONFIG_ATARI)
+    int m64_num;
+    struct fb_info_aty *info;
+
+    for (m64_num = 0; m64_num < mach64_count; m64_num++) {
+	if (!phys_vmembase[m64_num] || !phys_size[m64_num] ||
+	    !phys_guiregbase[m64_num]) {
+	    printk(" phys_*[%d] parameters not set => returning early. \n",
+		   m64_num);
+	    continue;
+	}
 
-    printk("fb%d: %s frame buffer device on %s\n", GET_FB_IDX(fb_info.node),
-	   atyfb_name, dp->full_name);
+	info = (struct fb_info_aty *)mem_start;
+	mem_start += sizeof(struct fb_info_aty);
+	mem_start = PAGE_ALIGN(mem_start);
 
-#ifdef CONFIG_FB_COMPAT_XPMAC
-    if (!console_fb_info) {
-	console_fb_info = &fb_info;
-	console_setmode_ptr = atyfb_console_setmode;
-	console_set_cmap_ptr = atyfb_set_cmap;
+	/*
+	 *  Map the video memory (physical address given) to somewhere in the
+	 *  kernel address space.
+	 */
+	info->frame_buffer = kernel_map(phys_vmembase[m64_num],
+					phys_size[m64_num],
+					KERNELMAP_NOCACHE_SER, &mem_start);
+	info->frame_buffer_phys = info->frame_buffer;
+	info->ati_regbase = kernel_map(phys_guiregbase[m64_num], 0x10000,
+				       KERNELMAP_NOCACHE_SER, &mem_start)
+			    +0xFC00ul;
+	info->ati_regbase_phys = info->ati_regbase;
+
+	if (!aty_init(info, "ISA bus")) {
+	    /* This is insufficient! kernel_map has added two large chunks!! */
+	    return mem_start;
+	}
     }
-#endif /* CONFIG_FB_COMPAT_XPMAC) */
-
+#endif
     return mem_start;
 }
 
+#ifdef CONFIG_FB_OF
+__initfunc(void atyfb_of_init(struct device_node *dp))
+{
+    unsigned long addr;
+    u8 bus, devfn;
+    u16 cmd;
+    struct fb_info_aty *info;
+    int i;
+
+    for (; dp; dp = dp->next) {
+	switch (dp->n_addrs) {
+	    case 1:
+	    case 3:
+		addr = dp->addrs[0].address;
+		break;
+	    case 4:
+		addr = dp->addrs[1].address;
+		break;
+	    default:
+		printk("Warning: got %d adresses for ATY:\n", dp->n_addrs);
+		for (i = 0; i < dp->n_addrs; i++)
+		    printk(" %08x-%08x", dp->addrs[i].address,
+			   dp->addrs[i].address+dp->addrs[i].size-1);
+		if (dp->n_addrs)
+		    printk("\n");
+		continue;
+	}
+
+	info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC);
+
+	info->ati_regbase = (unsigned long)ioremap(0x7ff000+addr,
+						   0x1000)+0xc00;
+	info->ati_regbase_phys = 0x7ff000+addr;
+	info->ati_regbase = (unsigned long)ioremap(info->ati_regbase_phys,
+						   0x1000);
+	info->ati_regbase_phys += 0xc00;
+	info->ati_regbase += 0xc00;
+
+	/* enable memory-space accesses using config-space command register */
+	if (pci_device_loc(dp, &bus, &devfn) == 0) {
+	    pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+	    if (cmd != 0xffff) {
+		cmd |= PCI_COMMAND_MEMORY;
+		pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+	    }
+	}
+
+#ifdef __BIG_ENDIAN
+	/* Use the big-endian aperture */
+	addr += 0x800000;
+#endif
+
+	/* Map in frame buffer */
+	info->frame_buffer_phys = addr;
+	info->frame_buffer = (unsigned long)ioremap(addr, 0x800000);
+
+	if (!aty_init(info, dp->full_name)) {
+	    kfree(info);
+	    return;
+	}
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+	if (!console_fb_info) {
+	    console_fb_info = &info->fb_info;
+	    console_setmode_ptr = atyfb_console_setmode;
+	    console_set_cmap_ptr = atyfb_set_cmap;
+	}
+#endif /* CONFIG_FB_COMPAT_XPMAC */
+    }
+}
+#endif /* CONFIG_FB_OF */
+
 
-/* XXX: doesn't work yet */
-void atyfb_setup(char *options, int *ints)
+__initfunc(void atyfb_setup(char *options, int *ints))
 {
     char *this_opt;
     int vmode;
     int depth;
-
     if (!options || !*options)
 	return;
 
@@ -1373,29 +1899,94 @@
 	if (!strncmp(this_opt, "vmode:", 6)) {
 	    vmode = simple_strtoul(this_opt+6, NULL, 0);
 	    if (vmode > 0 && vmode <= VMODE_MAX)
-		default_video_mode = vmode;
+		default_vmode = vmode;
 	} else if (!strncmp(this_opt, "cmode:", 6)) {
 	    depth = simple_strtoul(this_opt+6, NULL, 0);
 	    switch (depth) {
 		case 8:
-		    default_color_mode = CMODE_8;
+		    default_cmode = CMODE_8;
 		    break;
 		case 15:
 		case 16:
-		    default_color_mode = CMODE_16;
+		    default_cmode = CMODE_16;
 		    break;
 		case 24:
 		case 32:
-		    default_color_mode = CMODE_32;
+		    default_cmode = CMODE_32;
 		    break;
-	    };
+	    }
 	}
+#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];
+	    strncpy(mach64_str, this_opt+7, 80);
+	    if (!store_video_par(mach64_str, m64_num)) {
+		m64_num++;
+		mach64_count = m64_num;
+	    }
+	}
+#endif
     }
 }
 
+#ifdef CONFIG_ATARI
+__initfunc(static int store_video_par(char *video_str, unsigned char m64_num))
+{
+    char *p;
+    unsigned long vmembase, size, guiregbase;
+
+    printk("store_video_par() '%s' \n", video_str);
+
+    if (!(p = strtoke(video_str, ";")) || !*p)
+	goto mach64_invalid;
+    vmembase = simple_strtoul(p, NULL, 0);
+    if (!(p = strtoke(NULL, ";")) || !*p)
+	goto mach64_invalid;
+    size = simple_strtoul(p, NULL, 0);
+    if (!(p = strtoke(NULL, ";")) || !*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;
+    printk(" stored them all: $%08lX $%08lX $%08lX \n", vmembase, size,
+	   guiregbase);
+    return 0;
+
+mach64_invalid:
+    phys_vmembase[m64_num]   = 0;
+    return -1;
+}
+
+__initfunc(static char *strtoke(char *s, const char *ct))
+{
+    static char *ssave = NULL;
+    char *sbegin, *send;
+
+    sbegin  = s ? s : ssave;
+    if (!sbegin)
+	return NULL;
+    if (*sbegin == '\0') {
+	ssave = NULL;
+	return NULL;
+    }
+    send = strpbrk(sbegin, ct);
+    if (send && *send != '\0')
+	*send++ = '\0';
+    ssave = send;
+    return sbegin;
+}
+#endif /* !CONFIG_FB_OF */
 
 static int atyfbcon_switch(int con, struct fb_info *info)
 {
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
     struct atyfb_par par;
 
     /* Do we have to save the colormap? */
@@ -1403,8 +1994,8 @@
 	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
 		    atyfb_getcolreg, info);
     currcon = con;
-    decode_var(&fb_display[con].var, &par);
-    atyfb_set_par(&par);
+    decode_var(&fb_display[con].var, &par, info2);
+    atyfb_set_par(&par, info2);
     /* Install new colormap */
     do_install_cmap(con, info);
     return 0;
@@ -1416,8 +2007,10 @@
 
 static int atyfbcon_updatevar(int con, struct fb_info *info)
 {
-    current_par.yoffset = fb_display[con].var.yoffset;
-    set_off_pitch(&current_par);
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+
+    info2->current_par.yoffset = fb_display[con].var.yoffset;
+    set_off_pitch(&info2->current_par, info2);
     return 0;
 }
 
@@ -1427,9 +2020,10 @@
 
 static void atyfbcon_blank(int blank, struct fb_info *info)
 {
-    char gen_cntl;
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    u8 gen_cntl;
 
-    gen_cntl = aty_ld_8(CRTC_GEN_CNTL);
+    gen_cntl = aty_ld_8(CRTC_GEN_CNTL, info2);
     if (blank & VESA_VSYNC_SUSPEND)
 	    gen_cntl |= 0x8;
     if (blank & VESA_HSYNC_SUSPEND)
@@ -1438,7 +2032,7 @@
 	    gen_cntl |= 0x40;
     if (blank == VESA_NO_BLANKING)
 	    gen_cntl &= ~(0x4c);
-    aty_st_8(CRTC_GEN_CNTL, gen_cntl);
+    aty_st_8(CRTC_GEN_CNTL, gen_cntl, info2);
 }
 
 
@@ -1448,13 +2042,15 @@
      */
 
 static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
-			 u_int *transp, struct fb_info *info)
+			   u_int *transp, struct fb_info *info)
 {
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+
     if (regno > 255)
 	return 1;
-    *red = palette[regno].red;
-    *green = palette[regno].green;
-    *blue = palette[regno].blue;
+    *red = info2->palette[regno].red;
+    *green = info2->palette[regno].green;
+    *blue = info2->palette[regno].blue;
     return 0;
 }
 
@@ -1466,32 +2062,31 @@
      */
 
 static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			 u_int transp, struct fb_info *info)
+			   u_int transp, struct fb_info *info)
 {
+    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
     int i, scale;
 
     if (regno > 255)
 	return 1;
-    palette[regno].red = red;
-    palette[regno].green = green;
-    palette[regno].blue = blue;
-    aty_WaitQueue(2);
-    i = aty_ld_8(DAC_CNTL) & 0xfc;
-    if (chip_type == MACH64_GT_ID)
-	    i |= 0x2;	/*DAC_CNTL|0x2 turns off the extra brightness for gt*/
-    aty_st_8(DAC_CNTL, i);
-    aty_st_8(DAC_REGS + DAC_MASK, 0xff);
+    info2->palette[regno].red = red;
+    info2->palette[regno].green = green;
+    info2->palette[regno].blue = blue;
+    i = aty_ld_8(DAC_CNTL, info2) & 0xfc;
+    if (info2->chip_class == CLASS_GT)
+	i |= 0x2;	/*DAC_CNTL|0x2 turns off the extra brightness for gt*/
+    aty_st_8(DAC_CNTL, i, info2);
+    aty_st_8(DAC_REGS + DAC_MASK, 0xff, info2);
     eieio();
-    scale = ((chip_type != MACH64_GX_ID) &&
-	     (current_par.cmode == CMODE_16)) ? 3 : 0;
-    aty_WaitQueue(4);
-    aty_cmap_regs->windex = regno << scale;
+    scale = ((info2->chip_class != CLASS_GX) &&
+	     (info2->current_par.hw.gx.cmode == CMODE_16)) ? 3 : 0;
+    info2->aty_cmap_regs->windex = regno << scale;
     eieio();
-    aty_cmap_regs->lut = red << scale;
+    info2->aty_cmap_regs->lut = red << scale;
     eieio();
-    aty_cmap_regs->lut = green << scale;
+    info2->aty_cmap_regs->lut = green << scale;
     eieio();
-    aty_cmap_regs->lut = blue << scale;
+    info2->aty_cmap_regs->lut = blue << scale;
     eieio();
     if (regno < 16) {
 #ifdef CONFIG_FBCON_CFB16
@@ -1524,15 +2119,33 @@
      *  Accelerated functions
      */
 
-void aty_waitblit(void)
+static inline void draw_rect(s16 x, s16 y, u16 width, u16 height,
+			     struct fb_info_aty *info)
 {
-    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+    /* perform rectangle fill */
+    wait_for_fifo(2, info);
+    aty_st_le32(DST_Y_X, (x << 16) | y, info);
+    aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | height, info);
 }
 
-void aty_rectcopy(int srcx, int srcy, int dstx, int dsty, u_int width,
-		  u_int height)
+static inline void aty_rectcopy(int srcx, int srcy, int dstx, int dsty,
+				u_int width, u_int height,
+				struct fb_info_aty *info)
 {
-    u_int direction = 0;
+    u32 direction = DST_LAST_PEL;
+    u32 pitch_value;
+
+    if (!width || !height)
+	return;
+
+    pitch_value = info->current_par.vxres;
+#if 0
+    if (par->hw.gx.cmode == CMODE_24) {
+	/* In 24 bpp, the engine is in 8 bpp - this requires that all */
+	/* horizontal coordinates and widths must be adjusted */
+	pitch_value = pitch_value * 3;
+    }
+#endif
 
     if (srcy < dsty) {
 	dsty += height - 1;
@@ -1546,46 +2159,33 @@
     } else
 	direction |= DST_X_LEFT_TO_RIGHT;
 
-    aty_WaitQueue(4);
-    aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ );
-    aty_st_le32(DP_MIX, (MIX_SRC << 16) |  MIX_DST);
-    aty_st_le32(DP_SRC, FRGD_SRC_BLIT);
-
-    aty_WaitQueue(5);
-    aty_st_le32(SRC_Y_X, (srcx << 16) | (srcy & 0x0000ffff));
-    aty_st_le32(SRC_WIDTH1, width);
-    aty_st_le32(DST_CNTL, direction);
-    aty_st_le32(DST_Y_X, (dstx << 16) | (dsty & 0x0000ffff));
-    aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | (height & 0x0000ffff));
-
-    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
-
+    wait_for_fifo(5, info);
+    aty_st_le32(DP_SRC, FRGD_SRC_BLIT, info);
     /*
-     * Make sure that the destination trajectory is correctly set
-     * for subsequent calls.  MACH64_BIT_BLT is the only function that
-     * currently changes the destination trajectory from L->R and T->B.
-     */
-    aty_st_le32(DST_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM));
+     * ++Geert:
+     * Warning: SRC_OFF_PITCH may be thrashed by writing to other registers
+     * (e.g. CRTC_H_TOTAL_DISP, DP_SRC, DP_FRGD_CLR)
+     */
+    aty_st_le32(SRC_OFF_PITCH, (pitch_value / 8) << 22, info);
+    aty_st_le32(SRC_Y_X, (srcx << 16) | srcy, info);
+    aty_st_le32(SRC_HEIGHT1_WIDTH1, (width << 16) | height, info);
+    aty_st_le32(DST_CNTL, direction, info);
+    draw_rect(dstx, dsty, width, height, info);
 }
 
-void aty_rectfill(int dstx, int dsty, u_int width, u_int height, u_int color)
+static inline void aty_rectfill(int dstx, int dsty, u_int width, u_int height,
+				u_int color, struct fb_info_aty *info)
 {
     if (!width || !height)
 	return;
 
-    aty_WaitQueue(5);
-    aty_st_le32(DP_FRGD_CLR, color /* pGC->fgPixel */ );
-    aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ );
-    aty_st_le32(DP_MIX, (MIX_SRC << 16) | MIX_DST);
-    aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR);
-
-    aty_st_le32(DST_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
-
-    aty_WaitQueue(2);
-    aty_st_le32(DST_Y_X, (((u_int)dstx << 16) | ((u_int)dsty & 0x0000ffff)));
-    aty_st_le32(DST_HEIGHT_WIDTH, (((u_int)width << 16) | height));
-
-    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+    wait_for_fifo(3, info);
+    aty_st_le32(DP_FRGD_CLR, color, info);
+    aty_st_le32(DP_SRC, BKGD_SRC_BKGD_CLR | FRGD_SRC_FRGD_CLR | MONO_SRC_ONE,
+		info);
+    aty_st_le32(DST_CNTL, DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
+			  DST_X_LEFT_TO_RIGHT, info);
+    draw_rect(dstx, dsty, width, height, info);
 }
 
 
@@ -1593,8 +2193,8 @@
      *  Text console acceleration
      */
 
-static void fbcon_aty8_bmove(struct display *p, int sy, int sx, int dy, int dx,
-			     int height, int width)
+static void fbcon_aty_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			    int height, int width)
 {
     sx *= p->fontwidth;
     sy *= p->fontheight;
@@ -1603,11 +2203,12 @@
     width *= p->fontwidth;
     height *= p->fontheight;
 
-    aty_rectcopy(sx, sy, dx, dy, width, height);
+    aty_rectcopy(sx, sy, dx, dy, width, height,
+		 (struct fb_info_aty *)p->fb_info);
 }
 
-static void fbcon_aty8_clear(struct vc_data *conp, struct display *p, int sy,
-			     int sx, int height, int width)
+static void fbcon_aty_clear(struct vc_data *conp, struct display *p, int sy,
+			    int sx, int height, int width)
 {
     u32 bgx = attr_bgcol_ec(p, conp);
     bgx |= (bgx << 8);
@@ -1618,38 +2219,85 @@
     width *= p->fontwidth;
     height *= p->fontheight;
 
-    aty_rectfill(sx, sy, width, height, bgx);
+    aty_rectfill(sx, sy, width, height, bgx,
+		 (struct fb_info_aty *)p->fb_info);
 }
 
+#ifdef CONFIG_FBCON_CFB8
 static void fbcon_aty8_putc(struct vc_data *conp, struct display *p, int c,
 			    int yy, int xx)
 {
-    aty_waitblit();
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
     fbcon_cfb8_putc(conp, p, c, yy, xx);
 }
 
 static void fbcon_aty8_putcs(struct vc_data *conp, struct display *p,
 			     const char *s, int count, int yy, int xx)
 {
-    aty_waitblit();
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
     fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
 }
 
 static struct display_switch fbcon_aty8 = {
-    fbcon_cfb8_setup, fbcon_aty8_bmove, fbcon_aty8_clear, fbcon_aty8_putc,
-    fbcon_aty8_putcs, fbcon_cfb8_revc
+    fbcon_cfb8_setup, fbcon_aty_bmove, fbcon_aty_clear, fbcon_aty8_putc,
+    fbcon_aty8_putcs, fbcon_cfb8_revc, NULL
+};
+#endif
+
+#ifdef CONFIG_FBCON_CFB16
+static void fbcon_aty16_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx)
+{
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
+    fbcon_cfb16_putc(conp, p, c, yy, xx);
+}
+
+static void fbcon_aty16_putcs(struct vc_data *conp, struct display *p,
+			      const char *s, int count, int yy, int xx)
+{
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
+    fbcon_cfb16_putcs(conp, p, s, count, yy, xx);
+}
+
+static struct display_switch fbcon_aty16 = {
+    fbcon_cfb16_setup, fbcon_aty_bmove, fbcon_aty_clear, fbcon_aty16_putc,
+    fbcon_aty16_putcs, fbcon_cfb16_revc, NULL
+};
+#endif
+
+#ifdef CONFIG_FBCON_CFB32
+static void fbcon_aty32_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx)
+{
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
+    fbcon_cfb32_putc(conp, p, c, yy, xx);
+}
+
+static void fbcon_aty32_putcs(struct vc_data *conp, struct display *p,
+			      const char *s, int count, int yy, int xx)
+{
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
+    fbcon_cfb32_putcs(conp, p, s, count, yy, xx);
+}
+
+static struct display_switch fbcon_aty32 = {
+    fbcon_cfb32_setup, fbcon_aty_bmove, fbcon_aty_clear, fbcon_aty32_putc,
+    fbcon_aty32_putcs, fbcon_cfb32_revc, NULL
 };
+#endif
 
 
 #ifdef CONFIG_FB_COMPAT_XPMAC
 
     /*
      *  Backward compatibility mode for Xpmac
+     *
+     *  This should move to offb.c once this driver supports arbitrary video
+     *  modes
      */
 
 static int atyfb_console_setmode(struct vc_mode *mode, int doit)
 {
-    int err;
     struct fb_var_screeninfo var;
     struct atyfb_par par;
     int vmode, cmode;
@@ -1674,12 +2322,9 @@
 	    return -EINVAL;
     }
     init_par(&par, vmode, cmode);
-    encode_var(&var, &par);
-    if ((err = decode_var(&var, &par)))
-	return err;
-    if (doit)
-	atyfb_set_var(&var, currcon, 0);
-    return 0;
+    encode_var(&var, &par, (struct fb_info_aty *)console_fb_info);
+    var.activate = doit ? FB_ACTIVATE_NOW : FB_ACTIVATE_TEST;
+    return atyfb_set_var(&var, currcon, console_fb_info);
 }
 
 #endif /* CONFIG_FB_COMPAT_XPMAC */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov