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

Next file: linux/drivers/video/bwtwofb.c
Previous file: linux/drivers/video/atafb.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.111/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
@@ -1,7 +1,7 @@
 /*
  *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
  *
- *	Copyright (C) 1997 Geert Uytterhoeven
+ *	Copyright (C) 1997-1998 Geert Uytterhoeven
  *	Copyright (C) 1998 Bernd Harries
  *	Copyright (C) 1998 Eddie C. Dost
  *
@@ -26,26 +26,19 @@
 
   TODO:
 
-    - support arbitrary video modes
-
   (ecd):
 
-    - fix initialization and allocation of resources for cursor (and disp?).
-
     - fix initialization of cursor timer.
 
-    - add code to detect ramdac type on initialization.
-
     - add code to support cursor on all cards and all ramdacs.
 
     - make cursor parameters controllable via ioctl()s.
 
-    - handle arbitrary fonts.
-
 						(Anyone to help with all this?)
 
 ******************************************************************************/
 
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -63,10 +56,15 @@
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/nvram.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.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>
@@ -80,157 +78,76 @@
 #include "fbcon.h"
 #include "fbcon-cfb8.h"
 #include "fbcon-cfb16.h"
+#include "fbcon-cfb24.h"
 #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 char atyfb_name[16] = "ATY Mach64";
-
-struct atyfb_par {
-    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;
-};
-
+#define GUI_RESERVE	0x00001000
 
-/*
- * Video mode values.
- * These are supposed to be the same as the values that
- * Apple uses in MacOS.
- */
-#define VMODE_NVRAM		0	/* use value stored in nvram */
-#define VMODE_512_384_60I	1	/* 512x384, 60Hz interlaced (NTSC) */
-#define VMODE_512_384_60	2	/* 512x384, 60Hz */
-#define VMODE_640_480_50I	3	/* 640x480, 50Hz interlaced (PAL) */
-#define VMODE_640_480_60I	4	/* 640x480, 60Hz interlaced (NTSC) */
-#define VMODE_640_480_60	5	/* 640x480, 60Hz (VGA) */
-#define VMODE_640_480_67	6	/* 640x480, 67Hz */
-#define VMODE_640_870_75P	7	/* 640x870, 75Hz (portrait) */
-#define VMODE_768_576_50I	8	/* 768x576, 50Hz (PAL full frame) */
-#define VMODE_800_600_56	9	/* 800x600, 56Hz */
-#define VMODE_800_600_60	10	/* 800x600, 60Hz */
-#define VMODE_800_600_72	11	/* 800x600, 72Hz */
-#define VMODE_800_600_75	12	/* 800x600, 75Hz */
-#define VMODE_832_624_75	13	/* 832x624, 75Hz */
-#define VMODE_1024_768_60	14	/* 1024x768, 60Hz */
-#define VMODE_1024_768_70	15	/* 1024x768, 70Hz (or 72Hz?) */
-#define VMODE_1024_768_75V	16	/* 1024x768, 75Hz (VESA) */
-#define VMODE_1024_768_75	17	/* 1024x768, 75Hz */
-#define VMODE_1152_870_75	18	/* 1152x870, 75Hz */
-#define VMODE_1280_960_75	19	/* 1280x960, 75Hz */
-#define VMODE_1280_1024_75	20	/* 1280x1024, 75Hz */
-#define VMODE_MAX		20
-#define VMODE_CHOOSE		99	/* choose based on monitor sense */
+#define CLASS_GX	1
+#define CLASS_CT	2
+#define CLASS_VT	3
+#define CLASS_GT	4
 
-/*
- * Color mode values, used to select number of bits/pixel.
- */
-#define CMODE_NVRAM		-1	/* use value stored in nvram */
-#define CMODE_8			0	/* 8 bits/pixel */
-#define CMODE_16		1	/* 16 (actually 15) bits/pixel */
-#define CMODE_32		2	/* 32 (actually 24) bits/pixel */
 
+#ifndef __powerpc__
+#define eieio()		/* Enforce In-order Execution of I/O */
+#endif
 
-static int default_vmode = VMODE_NVRAM;
-static int default_cmode = CMODE_NVRAM;
+/* FIXME: remove the FAIL definition */
+#define FAIL(x) do { printk(x "\n"); return -EINVAL; } while (0)
 
-#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 */
 
+    /*
+     *  Elements of the Hardware specific atyfb_par structure
+     */
 
-/*
- * Horizontal and vertical resolution for each mode.
- */
-static struct vmode_attr {
-	int	hres;
-	int	vres;
-	int	vfreq;
-	int	interlaced;
-} vmode_attrs[VMODE_MAX] = {
-    {512, 384, 60, 1},
-    {512, 384, 60},
-    {640, 480, 50, 1},
-    {640, 480, 60, 1},
-    {640, 480, 60},
-    {640, 480, 67},
-    {640, 870, 75},
-    {768, 576, 50, 1},
-    {800, 600, 56},
-    {800, 600, 60},
-    {800, 600, 72},
-    {800, 600, 75},
-    {832, 624, 75},
-    {1024, 768, 60},
-    {1024, 768, 72},
-    {1024, 768, 75},
-    {1024, 768, 75},
-    {1152, 870, 75},
-    {1280, 960, 75},
-    {1280, 1024, 75}
+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 off_pitch;
+    u32 gen_cntl;
+    u32 dp_pix_width;	/* acceleration */
+    u32 dp_chain_mask;	/* acceleration */
 };
 
+struct pll_gx {
+    u8 m;
+    u8 n;
+};
 
-/*
- * We get a sense value from the monitor and use it to choose
- * what resolution to use.  This structure maps sense values
- * to display mode values (which determine the resolution and
- * frequencies).
- */
-static struct mon_map {
-	int	sense;
-	int	vmode;
-} monitor_map [] = {
-	{0x000, VMODE_1280_1024_75},	/* 21" RGB */
-	{0x114, VMODE_640_870_75P},	/* Portrait Monochrome */
-	{0x221, VMODE_512_384_60},	/* 12" RGB*/
-	{0x331, VMODE_1280_1024_75},	/* 21" RGB (Radius) */
-	{0x334, VMODE_1280_1024_75},	/* 21" mono (Radius) */
-	{0x335, VMODE_1280_1024_75},	/* 21" mono */
-	{0x40A, VMODE_640_480_60I},	/* NTSC */
-	{0x51E, VMODE_640_870_75P},	/* Portrait RGB */
-	{0x603, VMODE_832_624_75},	/* 12"-16" multiscan */
-	{0x60b, VMODE_1024_768_70},	/* 13"-19" multiscan */
-	{0x623, VMODE_1152_870_75},	/* 13"-21" multiscan */
-	{0x62b, VMODE_640_480_67},	/* 13"/14" RGB */
-	{0x700, VMODE_640_480_50I},	/* PAL */
-	{0x714, VMODE_640_480_60I},	/* NTSC */
-	{0x717, VMODE_800_600_75},	/* VGA */
-	{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 */
+struct pll_ct {
+    u8 pll_ref_div;
+    u8 pll_gen_cntl;
+    u8 mclk_fb_div;
+    u8 pll_vclk_cntl;
+    u8 vclk_post_div;
+    u8 vclk_fb_div;
+    u8 pll_ext_cntl;
+    u32 dsp_config;	/* Mach64 GTB DSP */
+    u32 dsp_on_off;	/* Mach64 GTB DSP */
 };
 
-static int map_monitor_sense(int sense)
-{
-	struct mon_map *map;
 
-	for (map = monitor_map; map->sense >= 0; ++map)
-		if (map->sense == sense)
-			break;
-	return map->vmode;
-}
+    /*
+     *  The Hardware parameters for each card
+     */
+
+struct atyfb_par {
+    struct crtc crtc;
+    union {
+	struct pll_gx gx;
+	struct pll_ct ct;
+    } pll;
+    u32 accel_flags;
+};
 
 struct aty_cmap_regs {
     u8 windex;
@@ -240,27 +157,6 @@
     u8 cntl;
 };
 
-typedef struct aty_regvals {
-    u32 offset[3];		/* first pixel address */
-
-    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;
-
-    u8 clock_val[2];	/* vals for 20 and 21 */
-} aty_regvals;
-
-struct rage_regvals {
-    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;
-};
-
 struct pci_mmap_map {
     unsigned long voff;
     unsigned long poff;
@@ -272,18 +168,18 @@
 #define DEFAULT_CURSOR_BLINK_RATE	(20)
 
 struct aty_cursor {
-	int		   enable;
-    	int		   on;
-	int		   vbl_cnt;
-	int		   blink_rate;
-	u32		   offset;
-	struct {
-	    u16 x, y;
-	}		   pos, hot, size;
-	u32		   color[2];
-	u8		   bits[8][64];
-	u8		   mask[8][64];
-	struct timer_list *timer;
+    int	enable;
+    int on;
+    int vbl_cnt;
+    int blink_rate;
+    u32 offset;
+    struct {
+        u16 x, y;
+    } pos, hot, size;
+    u32 color[2];
+    u8 bits[8][64];
+    u8 mask[8][64];
+    struct timer_list *timer;
 };
 
 struct fb_info_aty {
@@ -292,144 +188,49 @@
     unsigned long ati_regbase;
     unsigned long frame_buffer_phys;
     unsigned long frame_buffer;
+    struct display disp;
+    struct display_switch dispsw;
     struct pci_mmap_map *mmap_map;
     struct aty_cursor *cursor;
-    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;
-};
-
-#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 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"
-#include "ati-vt.h"
-
-static struct aty_regvals *aty_gt_reg_init[20] = {
-	NULL, NULL, NULL, NULL,
-	&aty_gt_reg_init_5,
-	&aty_gt_reg_init_6,
-	NULL, NULL,
-	&aty_gt_reg_init_9,
-	&aty_gt_reg_init_10,
-	&aty_gt_reg_init_11,
-	&aty_gt_reg_init_12,
-	&aty_gt_reg_init_13,
-	&aty_gt_reg_init_14,
-	&aty_gt_reg_init_15,
-	NULL,
-	&aty_gt_reg_init_17,
-	&aty_gt_reg_init_18,
-	NULL,
-	&aty_gt_reg_init_20
-};
-
-static struct aty_regvals *aty_gx_reg_init[20] = {
-	NULL, NULL, NULL, NULL,
-	&aty_gx_reg_init_6,
-	&aty_gx_reg_init_6,
-	NULL, NULL, NULL, NULL, NULL, NULL,
-	&aty_gx_reg_init_13,
-	&aty_gx_reg_init_14,
-	&aty_gx_reg_init_15,
-	NULL,
-	&aty_gx_reg_init_17,
-	&aty_gx_reg_init_18,
-	NULL,
-	&aty_gx_reg_init_20
-};
-
-static struct aty_regvals *aty_vt_reg_init[21] = {
-	NULL, NULL, NULL, NULL,
-	&aty_vt_reg_init_5,
-	&aty_vt_reg_init_6,
-	NULL, NULL, NULL,
-	&aty_vt_reg_init_10,
-	&aty_vt_reg_init_11,
-	&aty_vt_reg_init_12,
-	&aty_vt_reg_init_13,
-	&aty_vt_reg_init_14,
-	&aty_vt_reg_init_15,
-	NULL,
-	&aty_vt_reg_init_17,
-	&aty_vt_reg_init_18,
-	&aty_vt_reg_init_19,
-	&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;
+    u32 total_vram;
+    u32 pll_per;
+    u32 mclk_per;
     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 },
+#define Gx info->chip_type
+    u8 chip_rev;
+#define Rev info->chip_rev
+    u8 bus_type;
+    u8 ram_type;
+    u8 dac_type;
+    u8 clk_type;
+    u8 mem_refresh_rate;
+#ifdef __sparc__
+    u8 open;
+    u8 mmaped;
+    int vtconsole;
+    int consolecnt;
+#endif
 };
 
 
     /*
-     *  Interface used by the world
+     *  Frame buffer device API
      */
 
-void atyfb_init(void);
-#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, int user);
 static int atyfb_release(struct fb_info *info, int user);
 static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
-			 struct fb_info *info);
+			 struct fb_info *fb);
 static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
-			 struct fb_info *info);
+			 struct fb_info *fb);
 static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
-			 struct fb_info *info);
+			 struct fb_info *fb);
 static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
-			     struct fb_info *info);
+			     struct fb_info *fb);
 static int atyfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			  struct fb_info *info);
 static int atyfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
@@ -446,23 +247,50 @@
      *  Interface to the low level console driver
      */
 
-static int atyfbcon_switch(int con, struct fb_info *info);
-static int atyfbcon_updatevar(int con, struct fb_info *info);
-static void atyfbcon_blank(int blank, struct fb_info *info);
+static int atyfbcon_switch(int con, struct fb_info *fb);
+static int atyfbcon_updatevar(int con, struct fb_info *fb);
+static void atyfbcon_blank(int blank, struct fb_info *fb);
 
 
     /*
      *  Text console acceleration
      */
 
+static void fbcon_aty_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			    int height, int width);
+static void fbcon_aty_clear(struct vc_data *conp, struct display *p, int sy,
+			    int sx, int height, int width);
 #ifdef FBCON_HAS_CFB8
 static struct display_switch fbcon_aty8;
+static void fbcon_aty8_putc(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx);
+static void fbcon_aty8_putcs(struct vc_data *conp, struct display *p,
+			     const unsigned short *s, int count, int yy,
+			     int xx);
 #endif
 #ifdef FBCON_HAS_CFB16
 static struct display_switch fbcon_aty16;
+static void fbcon_aty16_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx);
+static void fbcon_aty16_putcs(struct vc_data *conp, struct display *p,
+			      const unsigned short *s, int count, int yy,
+			      int xx);
+#endif
+#ifdef FBCON_HAS_CFB24
+static struct display_switch fbcon_aty24;
+static void fbcon_aty24_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx);
+static void fbcon_aty24_putcs(struct vc_data *conp, struct display *p,
+			      const unsigned short *s, int count, int yy,
+			      int xx);
 #endif
 #ifdef FBCON_HAS_CFB32
 static struct display_switch fbcon_aty32;
+static void fbcon_aty32_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx);
+static void fbcon_aty32_putcs(struct vc_data *conp, struct display *p,
+			      const unsigned short *s, int count, int yy,
+			      int xx);
 #endif
 
 
@@ -476,12 +304,72 @@
 static char *strtoke(char *s, const char *ct);
 #endif
 
+static void reset_engine(const struct fb_info_aty *info);
+static void init_engine(const struct atyfb_par *par,
+			const struct fb_info_aty *info);
+static void aty_st_514(int offset, u8 val, const struct fb_info_aty *info);
+static void aty_st_pll(int offset, u8 val, const struct fb_info_aty *info);
+static void aty_set_crtc(const struct fb_info_aty *info,
+			 const struct crtc *crtc);
+static int aty_var_to_crtc(const struct fb_info_aty *info,
+			   const struct fb_var_screeninfo *var,
+			   struct crtc *crtc);
+static void aty_set_dac_514(const struct fb_info_aty *info, u32 bpp);
+static int aty_crtc_to_var(const struct crtc *crtc,
+			   struct fb_var_screeninfo *var);
+static void aty_set_pll_gx(const struct fb_info_aty *info,
+			   const struct pll_gx *pll);
+static int aty_var_to_pll_18818(const struct fb_var_screeninfo *var,
+				struct pll_gx *pll);
+static int aty_var_to_pll_514(const struct fb_var_screeninfo *var,
+			      struct pll_gx *pll);
+static int aty_pll_gx_to_var(const struct pll_gx *pll,
+			     struct fb_var_screeninfo *var);
+static void aty_set_pll_ct(const struct fb_info_aty *info,
+			   const struct pll_ct *pll);
+static int aty_dsp_gt(const struct fb_info_aty *info, u8 mclk_fb_div,
+		      u8 mclk_post_div, u8 vclk_fb_div, u8 vclk_post_div,
+		      u8 bpp, struct pll_ct *pll);
+static int aty_var_to_pll_ct(const struct fb_info_aty *info,
+			     const struct fb_var_screeninfo *var,
+			     struct pll_ct *pll);
+static int aty_pll_ct_to_var(const struct pll_ct *pll,
+			     struct fb_var_screeninfo *var);
+static void atyfb_set_par(const struct atyfb_par *par,
+			  struct fb_info_aty *info);
+static int atyfb_decode_var(const struct fb_var_screeninfo *var,
+			    struct atyfb_par *par,
+			    const struct fb_info_aty *info);
+static int atyfb_encode_var(struct fb_var_screeninfo *var,
+			    const struct atyfb_par *par,
+			    const struct fb_info_aty *info);
+static void set_off_pitch(struct atyfb_par *par,
+			  const struct fb_info_aty *info);
+static int encode_fix(struct fb_fix_screeninfo *fix,
+		      const struct atyfb_par *par,
+		      const struct fb_info_aty *info);
 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 *fb);
 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 *fb);
 static void do_install_cmap(int con, struct fb_info *info);
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
+static int read_aty_sense(const struct fb_info_aty *info);
+#endif
+
+
+    /*
+     *  Interface used by the world
+     */
+
+void atyfb_init(void);
+#ifdef CONFIG_FB_OF
+void atyfb_of_init(struct device_node *dp);
+#endif
+void atyfb_setup(char *options, int *ints);
+
 
+static int currcon = 0;
 
 static struct fb_ops atyfb_ops = {
     atyfb_open, atyfb_release, atyfb_get_fix, atyfb_get_var, atyfb_set_var,
@@ -493,64 +381,110 @@
 #endif
 };
 
+static char atyfb_name[16] = "ATY Mach64";
+static char fontname[40] __initdata = { 0 };
+
+static const u32 ref_clk_per = 1000000000000ULL/14318180;
+
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
+static int default_vmode = VMODE_NVRAM;
+static int default_cmode = CMODE_NVRAM;
+#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
+
+
+static struct aty_features {
+    u16 pci_id;
+    u16 chip_type;
+    const char *name;
+} aty_features[] __initdata = {
+    /* mach64GX family */
+    { 0x4758, 0x00d7, "mach64GX (ATI888GX00)" },
+    { 0x4358, 0x0057, "mach64CX (ATI888CX00)" },
+
+    /* mach64CT family */
+    { 0x4354, 0x4354, "mach64CT (ATI264CT)" },
+    { 0x4554, 0x4554, "mach64ET (ATI264ET)" },
+
+    /* mach64CT family / mach64VT class */
+    { 0x5654, 0x5654, "mach64VT (ATI264VT)" },
+    { 0x5655, 0x5655, "mach64VTB (ATI264VTB)" },
+/*  { 0x5656, 0x5656, "mach64VT4 (ATI264VT4)" }, */
+
+    /* mach64CT family / mach64GT (3D RAGE) class */
+    { 0x4c54, 0x4c54, "3D RAGE LT" },
+    { 0x4c47, 0x4c47, "3D RAGE LG" },
+    { 0x4754, 0x4754, "3D RAGE (GT)" },
+    { 0x4755, 0x4755, "3D RAGE II+ (GTB)" },
+/*  { 0x4756, 0x4756, "3D RAGE IIC" }, */
+    { 0x4742, 0x4742, "3D RAGE PRO (BGA, AGP)" },
+    { 0x4744, 0x4744, "3D RAGE PRO (BGA, AGP, 1x only)" },
+    { 0x4749, 0x4749, "3D RAGE PRO (BGA, PCI)" },
+    { 0x4750, 0x4750, "3D RAGE PRO (PQFP, PCI)" },
+    { 0x4751, 0x4751, "3D RAGE PRO (PQFP, PCI, limited 3D)" },
+};
 
-static inline int aty_vram_reqd(const struct atyfb_par *par)
-{
-    return (par->vxres*par->vyres) << par->hw.gx.cmode;
-}
 
 static inline u32 aty_ld_le32(volatile unsigned int regindex,
-			      struct fb_info_aty *info)
+			      const struct fb_info_aty *info)
 {
     unsigned long temp;
     u32 val;
 
-#ifdef __powerpc__
+#if defined(__powerpc__)
     temp = info->ati_regbase;
-    asm("lwbrx %0,%1,%2": "=r"(val):"r"(regindex), "r"(temp));
-#else
-#ifdef __sparc__v9__
+    asm("lwbrx %0,%1,%2" : "=r"(val) : "r" (regindex), "r" (temp));
+#elif defined(__sparc_v9__)
     temp = info->ati_regbase + regindex;
     asm("lduwa [%1] %2, %0" : "=r" (val) : "r" (temp), "i" (ASI_PL));
 #else
     temp = info->ati_regbase+regindex;
     val = le32_to_cpu(*((volatile u32 *)(temp)));
 #endif
-#endif
     return val;
 }
 
 static inline void aty_st_le32(volatile unsigned int regindex, u32 val,
-			       struct fb_info_aty *info)
+			       const struct fb_info_aty *info)
 {
     unsigned long temp;
 
-#ifdef __powerpc__
+#if defined(__powerpc__)
     temp = info->ati_regbase;
-    asm("stwbrx %0,%1,%2": : "r"(val), "r"(regindex), "r"(temp):"memory");
-#else
-#ifdef __sparc__v9__
+    asm("stwbrx %0,%1,%2" : : "r" (val), "r" (regindex), "r" (temp) :
+	"memory");
+#elif defined(__sparc_v9__)
     temp = info->ati_regbase + regindex;
-    asm("stwa %0, [%1] %2" : "r" (val), "r" (temp), "i" (ASI_PL) : "memory");
+    asm("stwa %0, [%1] %2" : : "r" (val), "r" (temp), "i" (ASI_PL) : "memory");
 #else
     temp = info->ati_regbase+regindex;
     *((volatile u32 *)(temp)) = cpu_to_le32(val);
 #endif
-#endif
 }
 
 static inline u8 aty_ld_8(volatile unsigned int regindex,
-			  struct fb_info_aty *info)
+			  const struct fb_info_aty *info)
 {
     return *(volatile u8 *)(info->ati_regbase+regindex);
 }
 
 static inline void aty_st_8(volatile unsigned int regindex, u8 val,
-			    struct fb_info_aty *info)
+			    const struct fb_info_aty *info)
 {
     *(volatile u8 *)(info->ati_regbase+regindex) = val;
 }
 
+
+    /*
+     *  Generic Mach64 routines
+     */
+
     /*
      *  All writes to draw engine registers are automatically routed through a
      *  32-bit-wide, 16-entry-deep command FIFO ...
@@ -559,19 +493,19 @@
      *  (from Chapter 5 of the Mach64 Programmer's Guide)
      */
 
-static inline void wait_for_fifo(u16 entries, struct fb_info_aty *info)
+static inline void wait_for_fifo(u16 entries, const 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)
+static inline void wait_for_idle(const struct fb_info_aty *info)
 {
     wait_for_fifo(16, info);
     while ((aty_ld_le32(GUI_STAT, info) & 1)!= 0);
 }
 
-static void reset_engine(struct fb_info_aty *info)
+static void reset_engine(const struct fb_info_aty *info)
 {
     /* reset engine */
     aty_st_le32(GEN_TEST_CNTL,
@@ -585,20 +519,19 @@
 			  BUS_FIFO_ERR_ACK, info);
 }
 
-static void init_engine(const struct atyfb_par *par, struct fb_info_aty *info)
+static void init_engine(const struct atyfb_par *par,
+			const struct fb_info_aty *info)
 {
     u32 pitch_value;
 
     /* determine modal information from global mode structure */
-    pitch_value = par->vxres;
+    pitch_value = par->crtc.vxres;
 
-#if 0
-    if (par->hw.gx.cmode == CMODE_24) {
+    if (par->crtc.bpp == 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);
@@ -655,7 +588,7 @@
     /* 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_BOTTOM, par->crtc.vyres-1, info);
     aty_st_le32(SC_RIGHT, pitch_value-1, info);
 
     /* set background color to minimum value (usually BLACK) */
@@ -684,48 +617,18 @@
 
     /* set pixel depth */
     wait_for_fifo(2, info);
-    switch(par->hw.gx.cmode) {
-#ifdef FBCON_HAS_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 FBCON_HAS_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 FBCON_HAS_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
-    }
+    aty_st_le32(DP_PIX_WIDTH, par->crtc.dp_pix_width, info);
+    aty_st_le32(DP_CHAIN_MASK, par->crtc.dp_chain_mask, info);
+
     /* insure engine is idle before leaving */
     wait_for_idle(info);
 }
 
-static void aty_st_514(int offset, u8 val, struct fb_info_aty *info)
+static void aty_st_514(int offset, u8 val, const struct fb_info_aty *info)
 {
     aty_st_8(DAC_CNTL, 1, info);
     /* right addr byte */
-    aty_st_8(DAC_W_INDEX, offset & 0xff, info);	
+    aty_st_8(DAC_W_INDEX, offset & 0xff, info);
     /* left addr byte */
     aty_st_8(DAC_DATA, (offset >> 8) & 0xff, info);
     eieio();
@@ -734,7 +637,7 @@
     aty_st_8(DAC_CNTL, 0, info);
 }
 
-static void aty_st_pll(int offset, u8 val, struct fb_info_aty *info)
+static void aty_st_pll(int offset, u8 val, const struct fb_info_aty *info)
 {
     /* write addr byte */
     aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN, info);
@@ -745,28 +648,28 @@
     aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN, info);
 }
 
-static struct aty_regvals *get_aty_struct(int vmode, struct fb_info_aty *info)
+#if 0 /* ecd debug */
+static u8 aty_ld_pll(int offset, const struct fb_info_aty *info)
 {
-    int v = vmode - 1;
+    u8 val;
 
-    switch (info->chip_class) {
-	case CLASS_GX:
-	    return aty_gx_reg_init[v];
-	    break;
-	case CLASS_CT:
-	case CLASS_VT:
-	    return aty_vt_reg_init[v];
-	    break;
-	case CLASS_GT:
-	    return aty_gt_reg_init[v];
-	    break;
-	default:
-	    /* should NOT happen */
-	    return NULL;
-    }
+    /* write addr byte */
+    aty_st_8(CLOCK_CNTL + 1, (offset << 2), info);
+    eieio();
+    /* read the register value */
+    val = aty_ld_8(CLOCK_CNTL + 2, info);
+    eieio();
+    return val;
 }
+#endif /* ecd debug */
+
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
+
+    /*
+     *  Apple monitor sense
+     */
 
-static int read_aty_sense(struct fb_info_aty *info)
+static int read_aty_sense(const struct fb_info_aty *info)
 {
     int sense, i;
 
@@ -800,50 +703,13 @@
     return sense;
 }
 
-static void RGB514_Program(int cmode, struct fb_info_aty *info)
-{
-    typedef struct {
-	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 */
-    };
-    RGB514_DAC_Table *pDacProgTab;
-
-    pDacProgTab = &RGB514DAC_Tab[cmode];
-
-    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, 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,
-	       info);
-}
-
-static void set_off_pitch(const struct atyfb_par *par,
-			  struct fb_info_aty *info)
-{
-    u32 pitch, offset;
+#endif /* defined(CONFIG_PMAC) || defined(CONFIG_CHRP) */
 
-    pitch = par->vxres>>3;
-    offset = ((par->yoffset*par->vxres+par->xoffset)>>3)<<par->hw.gx.cmode;
-    aty_st_le32(CRTC_OFF_PITCH, pitch<<22 | offset, info);
-}
+/* ------------------------------------------------------------------------- */
 
-/*
- * Hardware Cursor support.
- */
+    /*
+     *  Hardware Cursor support.
+     */
 
 static u8 cursor_pixel_map[2] = { 0, 15 };
 static u8 cursor_color_map[2] = { 0, 0xff };
@@ -870,6 +736,11 @@
 	if (!c)
 		return;
 
+#ifdef __sparc__
+	if (fb->mmaped && currcon == fb->vtconsole)
+		return;
+#endif
+
 	for (i = 0; i < 2; i++) {
 		c->color[i] =  (u32)red[i] << 24;
 		c->color[i] |= (u32)green[i] << 16;
@@ -880,7 +751,6 @@
 	wait_for_fifo(2, fb);
 	aty_st_le32(CUR_CLR0, c->color[0], fb);
 	aty_st_le32(CUR_CLR1, c->color[1], fb);
-	wait_for_idle(fb);
 }
 
 static void
@@ -893,6 +763,11 @@
 	if (!c)
 		return;
 
+#ifdef __sparc__
+	if (fb->mmaped && currcon == fb->vtconsole)
+		return;
+#endif
+
 	ram = (u8 *)(fb->frame_buffer + c->offset);
 
 	for (y = 0; y < c->size.y; y++) {
@@ -922,6 +797,11 @@
 	if (!c)
 		return;
 
+#ifdef __sparc__
+	if (fb->mmaped && currcon == fb->vtconsole)
+		return;
+#endif
+
 	if (c->on) {
 		x = c->pos.x - c->hot.x;
 		if (x < 0) {
@@ -952,7 +832,6 @@
 			    aty_ld_le32(GEN_TEST_CNTL, fb) & ~HWCURSOR_ENABLE,
 			    fb);
 	}
-	wait_for_idle(fb);
 }
 
 static void
@@ -986,6 +865,11 @@
 	if (!c)
 		return;
 
+#ifdef __sparc__
+	if (fb->mmaped && currcon == fb->vtconsole)
+		return;
+#endif
+
 	x *= d->fontwidth;
 	y *= d->fontheight;
 	if (c->pos.x == x && c->pos.y == y && (mode == CM_ERASE) == !c->on)
@@ -1061,425 +945,799 @@
 }
 
 
-static void atyfb_set_par(struct atyfb_par *par, struct fb_info_aty *info)
-{
-    int i, j = 0, 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->hw.gx.vmode);
-
-    info->current_par = *par;
-    hres = vmode_attrs[par->hw.gx.vmode-1].hres;
-
-    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 CLASS_GT:
-	    if (vram_type == 5) {
-		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(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], 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;
-    }
 
-    aty_ld_8(DAC_REGS, info);	/* clear counter */
-    wait_for_idle(info);
 
-    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);
+    /*
+     *  CRTC programming
+     */
 
+static void aty_set_crtc(const struct fb_info_aty *info,
+			 const struct crtc *crtc)
+{
+    aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, info);
+    aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, info);
+    aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_tot_disp, info);
+    aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid, info);
     aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0, info);
+    aty_st_le32(CRTC_OFF_PITCH, crtc->off_pitch, info);
+    aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl, 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 CLASS_VT:
-	    if (vram_type == 4) {
-		/*
-		 * What to do here? - The contents of MEM_CNTL do not
-		 * seem to match my documentation, and touching this
-		 * register makes the output unusable.
-		 * (green bars across the screen and similar effects).
-		 *
-		 * Eddie C. Dost  (ecd@skynet.be)
-		 */
-		j = 0x87010184;
-		break;
-	     }
-	     /* fallthrough */
+static int aty_var_to_crtc(const struct fb_info_aty *info,
+			   const struct fb_var_screeninfo *var,
+			   struct crtc *crtc)
+{
+    u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
+    u32 left, right, upper, lower, hslen, vslen, sync, vmode;
+    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;
+    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;
+    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;
+
+    /* convert (and round up) and validate */
+    xres = (xres+7) & ~7;
+    xoffset = (xoffset+7) & ~7;
+    vxres = (vxres+7) & ~7;
+    if (vxres < xres+xoffset)
+	vxres = xres+xoffset;
+    h_disp = xres/8-1;
+    if (h_disp > 0xff)
+	FAIL("h_disp too large");
+    h_sync_strt = h_disp+(right/8);
+    if (h_sync_strt > 0x1ff)
+	FAIL("h_sync_start too large");
+    h_sync_dly = right & 7;
+    h_sync_wid = (hslen+7)/8;
+    if (h_sync_wid > 0x1f)
+	FAIL("h_sync_wid too large");
+    h_total = h_sync_strt+h_sync_wid+(h_sync_dly+left+7)/8;
+    if (h_total > 0x1ff)
+	FAIL("h_total too large");
+    h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+
+    if (vyres < yres+yoffset)
+	vyres = yres+yoffset;
+    v_disp = yres-1;
+    if (v_disp > 0x7ff)
+	FAIL("v_disp too large");
+    v_sync_strt = v_disp+lower;
+    if (v_sync_strt > 0x7ff)
+	FAIL("v_sync_strt too large");
+    v_sync_wid = vslen;
+    if (v_sync_wid > 0x1f)
+	FAIL("v_sync_wid too large");
+    v_total = v_sync_strt+v_sync_wid+upper;
+    if (v_total > 0x7ff)
+	FAIL("v_total too large");
+    v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+    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 = 0x8080;
+    } else if (bpp <= 16) {
+	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 = 0x4210;
+    } else if ((bpp <= 24) && (Gx != GX_CHIP_ID) && (Gx != CX_CHIP_ID)) {
+	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 = 0x8080;
+    } 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 = 0x8080;
+    } else
+	FAIL("invalid bpp");
 
-	case CLASS_CT:
-	    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;
+    if (vxres*vyres*bpp/8 > info->total_vram)
+	FAIL("not enough video RAM");
 
-	case CLASS_GT:
-	    aty_st_le32(BUS_CNTL, 0x7b23a040, info);
+    if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+	FAIL("invalid vmode");
 
-	    /* 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);
+    /* output */
+    crtc->vxres = vxres;
+    crtc->vyres = vyres;
+    crtc->xoffset = xoffset;
+    crtc->yoffset = yoffset;
+    crtc->bpp = bpp;
+    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->off_pitch = ((yoffset*vxres+xoffset)*bpp/64) | (vxres<<19);
+    crtc->gen_cntl = pix_width | CRTC_EXT_DISP_EN | CRTC_ENABLE;
+    if ((Gx == CT_CHIP_ID) || (Gx == ET_CHIP_ID) ||
+	((Gx == VT_CHIP_ID) && !(Rev & 0x03)) ||
+	((Gx == GT_CHIP_ID) && !(Rev & 0x03))) {
+	/* Not VTB/GTB */
+	/* FIXME: magic FIFO values */
+	crtc->gen_cntl |= aty_ld_le32(CRTC_GEN_CNTL, info) & 0x000e0000;
+    }
+    crtc->dp_pix_width = dp_pix_width;
+    crtc->dp_chain_mask = dp_chain_mask;
 
-		/* 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);
+    return 0;
+}
 
-	    /* if (info->total_vram > 0x400000)	
-		i |= 0x538; this not been verified on > 4Megs!! */
+static void aty_set_dac_514(const struct fb_info_aty *info, u32 bpp)
+{
+    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;
 
-	    j = 0x86010102;
+    switch (bpp) {
+	case 8:
+	default:
+	    i = 0;
+	    break;
+	case 16:
+	    i = 1;
+	    break;
+	case 32:
+	    i = 2;
 	    break;
     }
+    aty_st_514(0x90, 0x00, info);		/* VRAM Mask Low */
+    aty_st_514(0x04, tab[i].pixel_dly, info);	/* Horizontal Sync Control */
+    aty_st_514(0x05, 0x00, info);		/* Power Management */
+    aty_st_514(0x02, 0x01, info);		/* Misc Clock Control */
+    aty_st_514(0x71, tab[i].misc2_cntl, info);	/* Misc Control 2 */
+    aty_st_514(0x0a, tab[i].pixel_rep, info);	/* Pixel Format */
+    aty_st_514(tab[i].pixel_cntl_index, tab[i].pixel_cntl_v1, info);
+			/* Misc Control 2 / 16 BPP Control / 32 BPP Control */
+}
+
+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;
+    u32 pix_width;
+
+    /* 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;
+    pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+    /* 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;
+    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);
 
-    /* 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->hw.gx.cmode) {
-	case CMODE_16:
-	    i = CRTC_PIX_WIDTH_15BPP; break;
-	/*case CMODE_24: */
-	case CMODE_32:
-	    i = CRTC_PIX_WIDTH_32BPP; break;
-	case CMODE_8:
+    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;
+#if 0
+	case CRTC_PIX_WIDTH_16BPP:	/* RGB 565 */
+	    bpp = 16;
+	    var->red.offset = 11;
+	    var->red.length = 5;
+	    var->green.offset = 6;
+	    var->green.length = 6;
+	    var->blue.offset = 0;
+	    var->blue.length = 5;
+	    var->transp.offset = 0;
+	    var->transp.length = 0;
+	    break;
+#endif
+	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:
-	    i = CRTC_PIX_WIDTH_8BPP; break;
+	    FAIL("Invalid pixel width");
     }
 
-    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, info);
-
-    /* Initialize the graphics engine */
-    if (par->accel & FB_ACCELF_TEXT)
-	init_engine(par, info);
+    /* output */
+    var->xres = xres;
+    var->yres = yres;
+    var->xres_virtual = crtc->vxres;
+    var->yres_virtual = crtc->vyres;
+    var->bits_per_pixel = bpp;
+    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;
 
-#ifdef 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 */
+    return 0;
 }
 
+/* ------------------------------------------------------------------------- */
 
     /*
-     *  Open/Release the frame buffer device
+     *  PLL programming (Mach64 GX family)
+     *
+     *  FIXME: use function pointer tables instead of switch statements
      */
 
-static int atyfb_open(struct fb_info *info, int user)
+static void aty_set_pll_gx(const struct fb_info_aty *info,
+			   const struct pll_gx *pll)
+{
+    switch (info->clk_type) {
+	case CLK_ATI18818_1:
+	    aty_st_8(CLOCK_CNTL, pll->m, info);
+	    break;
+	case CLK_IBMRGB514:
+	    aty_st_514(0x06, 0x02, info);	/* DAC Operation */
+	    aty_st_514(0x10, 0x01, info);	/* PLL Control 1 */
+	    aty_st_514(0x70, 0x01, info);	/* Misc Control 1 */
+	    aty_st_514(0x8f, 0x1f, info);	/* PLL Ref. Divider Input */
+	    aty_st_514(0x03, 0x00, info);	/* Sync Control */
+	    aty_st_514(0x05, 0x00, info);	/* Power Management */
+	    aty_st_514(0x20, pll->m, info);	/* F0 / M0 */
+	    aty_st_514(0x21, pll->m, info);	/* F1 / N0 */
+	    break;
+    }
+}
+
+static int aty_var_to_pll_18818(const struct fb_var_screeninfo *var,
+				struct pll_gx *pll)
+{
+    /*
+     *  FIXME: use real calculations instead of using fixed values from the old
+     *	       driver
+     */
+    static struct {
+	u32 ps_lim;	/* pixclock period rounding limit (arbitrary) */
+	u8 mode;	/* (prescsaler << 4) | Select */
+	u8 prog;	/* ref_div_count */
+    } ATI18818_clocks[] = {
+	{  7500, 0x0B, 1 },	/*  7407.4 ps = 135.00 MHz */
+	{  9000, 0x0A, 1 },	/*  7936.5 ps = 126.00 MHz */
+	{ 11000, 0x09, 1 },	/* 10000.0 ps = 100.00 MHz */
+	{ 12800, 0x0D, 1 },	/* 12500.0 ps =  80.00 MHz */
+	{ 13500, 0x0E, 1 },	/* 13333.3 ps =  75.00 MHz */
+/*	{ 14000, 0x03, 2 },*/	/* 13888.8 ps =  72.00 MHz */
+	{ 15000, 0x1B, 1 },	/* 14814.8 ps =  67.50 MHz */
+	{ 15500, 0x0F, 1 },	/* 15384.6 ps =  65.00 MHz */
+	{ 16000, 0x1A, 1 },	/* 15873.0 ps =  63.00 MHz */
+/*	{ 16000, 0x02, 2 },*/	/* 15873.0 ps =  63.00 MHz */
+/*	{ 18000, 0x01, 2 },*/	/* 17655.4 ps =  56.64 MHz */
+/*	{ 19900, 0x00, 2 },*/	/* 19860.9 ps =  50.35 MHz */
+	{ 20000, 0x07, 1 },	/* 20000.0 ps =  50.00 MHz */
+	{ 20300, 0x06, 1 },	/* 20202.0 ps =  49.50 MHz */
+	{ 22500, 0x05, 1 },	/* 22271.2 ps =  44.90 MHz */
+	{ 25000, 0x04, 1 },	/* 25000.0 ps =  40.00 MHz */
+/*	{ 28000, 0x03, 1 },*/	/* 27777.8 ps =  36.00 MHz */
+	{ 30000, 0x2B, 1 },	/* 29629,6 ps =  33.75 MHz */
+	{ 31000, 0x1F, 1 },	/* 30769.2 ps =  32.50 MHz */
+	{ 32000, 0x2A, 1 },	/* 31746.0 ps =  31.50 MHz */
+/*	{ 32000, 0x02, 1 },*/	/* 31746.0 ps =  31.50 MHz */
+/*	{ 36000, 0x01, 1 },*/	/* 35310.7 ps =  28.32 MHz */
+/*	{ 39900, 0x00, 1 },*/	/* 39714.1 ps =  25.18 MHz */
+	{ 40000, 0x17, 1 },	/* 40000.0 ps =  25.00 MHz */
+	{ 40600, 0x16, 1 },	/* 40404.0 ps =  24.75 MHz */
+	{ 45000, 0x15, 1 },	/* 44543.4 ps =  22.45 MHz */
+	{ 50000, 0x14, 1 },	/* 50000.0 ps =  20.00 MHz */
+/*	{ 56000, 0x13, 1 },*/	/* 55555.5 ps =  18.00 MHz */
+	{ 62000, 0x2F, 1 },	/* 61538.8 ps =  16.25 MHz */
+/*	{ 64000, 0x12, 1 },*/	/* 63492.0 ps =  15.75 MHz */
+    };
+    int set;
 
+    for (set = 0; set < sizeof(ATI18818_clocks)/sizeof(*ATI18818_clocks);
+	 set++)
+	if (var->pixclock <= ATI18818_clocks[set].ps_lim) {
+	    pll->m = ATI18818_clocks[set].mode;
+	    pll->n = ATI18818_clocks[set].prog;
+	    return 0;
+	}
+    return -EINVAL;
+}
+
+static int aty_var_to_pll_514(const struct fb_var_screeninfo *var,
+			      struct pll_gx *pll)
 {
     /*
-     *  Nothing, only a usage count for the moment
+     *  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;
 
-    MOD_INC_USE_COUNT;
-    return(0);
+    for (i = 0; i < sizeof(RGB514_clocks)/sizeof(*RGB514_clocks); i++)
+	if (var->pixclock <= RGB514_clocks[i].limit) {
+	    pll->m = RGB514_clocks[i].m;
+	    pll->n = RGB514_clocks[i].n;
+	    return 0;
+	}
+    return -EINVAL;
 }
 
-static int atyfb_release(struct fb_info *info, int user)
+    /* FIXME: ATI18818?? */
+
+static int aty_pll_gx_to_var(const struct pll_gx *pll,
+			     struct fb_var_screeninfo *var)
 {
-    MOD_DEC_USE_COUNT;
-    return(0);
-}
+    u8 df, vco_div_count, ref_div_count;
 
+    df = pll->m >> 6;
+    vco_div_count = pll->m & 0x3f;
+    ref_div_count = pll->n;
 
-static int encode_fix(struct fb_fix_screeninfo *fix,
-		      const struct atyfb_par *par, struct fb_info_aty *info)
-{
-    struct aty_regvals *init;
+    var->pixclock = (ref_clk_per*(vco_div_count+65)/ref_div_count)>>(3-df);
 
-    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+    return 0;
+}
 
-    strcpy(fix->id, atyfb_name);
-    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 *)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
+     *  PLL programming (Mach64 CT family)
      */
-    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;
+
+static void aty_set_pll_ct(const struct fb_info_aty *info,
+			   const struct pll_ct *pll)
+{
+#if 0 /* ecd debug */
+printk("PLL_REF_DIV:   %02x (%02x)\n",
+	pll->pll_ref_div, aty_ld_pll(PLL_REF_DIV, info));
+printk("PLL_GEN_CNTL:  %02x (%02x)\n",
+	pll->pll_gen_cntl, aty_ld_pll(PLL_GEN_CNTL, info));
+printk("MCLK_FB_DIV:   %02x (%02x)\n",
+	pll->mclk_fb_div, aty_ld_pll(MCLK_FB_DIV, info));
+printk("PLL_VCLK_CNTL: %02x (%02x)\n",
+	pll->pll_vclk_cntl, aty_ld_pll(PLL_VCLK_CNTL, info));
+printk("VCLK_POST_DIV: %02x (%02x)\n",
+	pll->vclk_post_div, aty_ld_pll(VCLK_POST_DIV, info));
+printk("VCLK0_FB_DIV:  %02x (%02x)\n",
+	pll->vclk_fb_div, aty_ld_pll(VCLK0_FB_DIV, info));
+printk("PLL_EXT_CNTL:  %02x (%02x)\n",
+	pll->pll_ext_cntl, aty_ld_pll(PLL_EXT_CNTL, info));
+#endif /* ecd debug */
+    aty_st_pll(PLL_REF_DIV, pll->pll_ref_div, info);
+    aty_st_pll(PLL_GEN_CNTL, pll->pll_gen_cntl, info);
+    aty_st_pll(MCLK_FB_DIV, pll->mclk_fb_div, info);
+    aty_st_pll(PLL_VCLK_CNTL, pll->pll_vclk_cntl, info);
+    aty_st_pll(VCLK_POST_DIV, pll->vclk_post_div, info);
+    aty_st_pll(VCLK0_FB_DIV, pll->vclk_fb_div, info);
+    aty_st_pll(PLL_EXT_CNTL, pll->pll_ext_cntl, info);
+
+    if (((Gx == GT_CHIP_ID) && (Rev & 0x03)) || (Gx == GU_CHIP_ID) ||
+	(Gx == LG_CHIP_ID) || (Gx == GB_CHIP_ID) || (Gx == GD_CHIP_ID) ||
+	(Gx == GI_CHIP_ID) || (Gx == GP_CHIP_ID) || (Gx == GQ_CHIP_ID) ||
+	(Gx == VU_CHIP_ID)) {
+	if (info->ram_type >= SDRAM)
+	    aty_st_pll(DLL_CNTL, 0xa6, info);
+	else
+	    aty_st_pll(DLL_CNTL, 0xa0, info);
+	aty_st_pll(VFC_CNTL, 0x1b, info);
+	aty_st_le32(DSP_CONFIG, pll->dsp_config, info);
+	aty_st_le32(DSP_ON_OFF, pll->dsp_on_off, info);
+    }
+}
+
+static int aty_dsp_gt(const struct fb_info_aty *info, u8 mclk_fb_div,
+		      u8 mclk_post_div, u8 vclk_fb_div, u8 vclk_post_div,
+		      u8 bpp, struct pll_ct *pll)
+{
+    u32 dsp_xclks_per_row, dsp_loop_latency, dsp_precision, dsp_off, dsp_on;
+    u32 xclks_per_row, fifo_off, fifo_on, y, fifo_size, page_size;
+
+    /* xclocks_per_row<<11 */
+    xclks_per_row = (mclk_fb_div*vclk_post_div*64<<11)/
+		    (vclk_fb_div*mclk_post_div*bpp);
+    if (xclks_per_row < (1<<11))
+	FAIL("Dotclock to high");
+    fifo_size = 24;
+    dsp_precision = 0;
+    y = (xclks_per_row*fifo_size)>>11;
+    while (y) {
+	y >>= 1;
+	dsp_precision++;
+    }
+    dsp_precision -= 5;
+    /* fifo_off<<6 */
+    fifo_off = ((xclks_per_row*(fifo_size-1))>>5)+(1<<6);
+
+    if (info->total_vram > 1*1024*1024) {
+	if (info->ram_type >= SDRAM) {
+	    /* >1 MB SDRAM */
+	    dsp_loop_latency = 8;
+	    page_size = 8;
+	} else {
+	    /* >1 MB DRAM */
+	    dsp_loop_latency = 6;
+	    page_size = 9;
+	}
+    } else {
+	if (info->ram_type >= SDRAM) {
+	    /* <2 MB SDRAM */
+	    dsp_loop_latency = 9;
+	    page_size = 10;
+	} else {
+	    /* <2 MB DRAM */
+	    dsp_loop_latency = 8;
+	    page_size = 10;
+	}
     }
-    fix->type = FB_TYPE_PACKED_PIXELS;
-    fix->type_aux = 0;
-    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;
+    /* fifo_on<<6 */
+    if (xclks_per_row >= (page_size<<11))
+	fifo_on = ((2*page_size+1)<<6)+(xclks_per_row>>5);
+    else
+	fifo_on = (3*page_size)<<6;
 
+    dsp_xclks_per_row = xclks_per_row>>dsp_precision;
+    dsp_on = fifo_on>>dsp_precision;
+    dsp_off = fifo_off>>dsp_precision;
+
+    pll->dsp_config = (dsp_xclks_per_row & 0x3fff) |
+		      ((dsp_loop_latency & 0xf)<<16) |
+		      ((dsp_precision & 7)<<20);
+    pll->dsp_on_off = (dsp_on & 0x7ff) | ((dsp_off & 0x7ff)<<16);
     return 0;
 }
 
+static int aty_var_to_pll_ct(const struct fb_info_aty *info,
+			     const struct fb_var_screeninfo *var,
+			     struct pll_ct *pll)
+{
+    u32 vclk_per, q, x;		/* x is a workaround for sparc64-linux-gcc */
+    u8 pll_ref_div, pll_gen_cntl, pll_ext_cntl;
+    u8 mclk_fb_div, mclk_post_div, mpostdiv = 0;
+    u8 vclk_fb_div, vclk_post_div, vpostdiv = 0;
+    int err;
 
-static int decode_var(struct fb_var_screeninfo *var,
-		      struct atyfb_par *par, struct fb_info_aty *info)
-{
-    int xres = var->xres;
-    int yres = var->yres;
-    int bpp = var->bits_per_pixel;
-    struct aty_regvals *init;
-
-    /* This should support more video modes */
-
-    if (xres <= 512 && yres <= 384)
-	par->hw.gx.vmode = VMODE_512_384_60;	/* 512x384, 60Hz */
-    else if (xres <= 640 && yres <= 480)
-	par->hw.gx.vmode = VMODE_640_480_67;	/* 640x480, 67Hz */
-    else if (xres <= 640 && yres <= 870)
-	par->hw.gx.vmode = VMODE_640_870_75P;	/* 640x870, 75Hz (portrait) */
-    else if (xres <= 768 && yres <= 576)
-	par->hw.gx.vmode = VMODE_768_576_50I;	/* 768x576, 50Hz (PAL full frame) */
-    else if (xres <= 800 && yres <= 600)
-	par->hw.gx.vmode = VMODE_800_600_75;	/* 800x600, 75Hz */
-    else if (xres <= 832 && yres <= 624)
-	par->hw.gx.vmode = VMODE_832_624_75;	/* 832x624, 75Hz */
-    else if (xres <= 1024 && yres <= 768)
-	par->hw.gx.vmode = VMODE_1024_768_75;	/* 1024x768, 75Hz */
-    else if (xres <= 1152 && yres <= 870)
-	par->hw.gx.vmode = VMODE_1152_870_75;	/* 1152x870, 75Hz */
-    else if (xres <= 1280 && yres <= 960)
-	par->hw.gx.vmode = VMODE_1280_960_75;	/* 1280x960, 75Hz */
-    else if (xres <= 1280 && yres <= 1024)
-	par->hw.gx.vmode = VMODE_1280_1024_75;	/* 1280x1024, 75Hz */
-    else
-	return -EINVAL;
+    x = x;			/* x is a workaround for sparc64-linux-gcc */
 
-    xres = vmode_attrs[par->hw.gx.vmode-1].hres;
-    yres = vmode_attrs[par->hw.gx.vmode-1].vres;
+    pll->pll_vclk_cntl = 0x03;	/* VCLK = PLL_VCLK/VCLKx_POST */
 
-    if (var->xres_virtual <= xres)
-	par->vxres = xres;
+    vclk_per = var->pixclock;
+    pll_ref_div = info->pll_per*2*255/ref_clk_per;
+
+    /* FIXME: use the VTB/GTB /3 post divider if it's better suited */
+    q = ref_clk_per*pll_ref_div*4/info->mclk_per;	/* actually 8*q */
+    if (q < 16*8 || q > 255*8)
+	FAIL("mclk out of range");
+    else if (q < 32*8)
+	mclk_post_div = 8;
+    else if (q < 64*8)
+	mclk_post_div = 4;
+    else if (q < 128*8)
+	mclk_post_div = 2;
     else
-	par->vxres = (var->xres_virtual+7) & ~7;
-    if (var->yres_virtual <= yres)
-	par->vyres = yres;
+	mclk_post_div = 1;
+    mclk_fb_div = q*mclk_post_div/8;
+
+    /* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */
+    q = ref_clk_per*pll_ref_div*4/vclk_per;	/* actually 8*q */
+    if (q < 16*8 || q > 255*8)
+	FAIL("vclk out of range");
+    else if (q < 32*8)
+	vclk_post_div = 8;
+    else if (q < 64*8)
+	vclk_post_div = 4;
+    else if (q < 128*8)
+	vclk_post_div = 2;
     else
-	par->vyres = var->yres_virtual;
+	vclk_post_div = 1;
+    vclk_fb_div = q*vclk_post_div/8;
 
-    par->xoffset = (var->xoffset+7) & ~7;
-    par->yoffset = var->yoffset;
-    if (par->xoffset+xres > par->vxres || par->yoffset+yres > par->vyres)
-	return -EINVAL;
+    if ((err = aty_dsp_gt(info, mclk_fb_div, mclk_post_div, vclk_fb_div,
+			  vclk_post_div, var->bits_per_pixel, pll)))
+	return err;
 
-    if (bpp <= 8)
-	par->hw.gx.cmode = CMODE_8;
-    else if (bpp <= 16)
-	par->hw.gx.cmode = CMODE_16;
-    else if (bpp <= 32)
-	par->hw.gx.cmode = CMODE_32;
+    if ((((Gx == GT_CHIP_ID) && (Rev & 0x03)) || (Gx == GU_CHIP_ID) ||
+	 (Gx == LG_CHIP_ID) || (Gx == GB_CHIP_ID) || (Gx == GD_CHIP_ID) ||
+	 (Gx == GI_CHIP_ID) || (Gx == GP_CHIP_ID) || (Gx == GQ_CHIP_ID) ||
+	 (Gx == VU_CHIP_ID)) && (info->ram_type >= SDRAM))
+	pll_gen_cntl = 0x04;
     else
-	return -EINVAL;
+	pll_gen_cntl = 0x84;
 
-    if (var->accel_flags & FB_ACCELF_TEXT)
-	par->accel = FB_ACCELF_TEXT;
+    switch (mclk_post_div) {
+	case 1:
+	    mpostdiv = 0;
+	    break;
+	case 2:
+	    mpostdiv = 1;
+	    break;
+	case 3:
+	    mpostdiv = 4;
+	    break;
+	case 4:
+	    mpostdiv = 2;
+	    break;
+	case 8:
+	    mpostdiv = 3;
+	    break;
+    }
+    pll_gen_cntl |= mpostdiv<<4;	/* mclk */
+
+    if (Gx == VT_CHIP_ID && (Rev == 0x40 || Rev == 0x48))
+	pll_ext_cntl = 0;
     else
-	par->accel = 0;
+    	pll_ext_cntl = mpostdiv;	/* xclk == mclk */
 
-    if (aty_vram_reqd(par) > info->total_vram)
-	return -EINVAL;
+    switch (vclk_post_div) {
+	case 1:
+	    vpostdiv = 0;
+	    break;
+	case 2:
+	    vpostdiv = 1;
+	    break;
+	case 3:
+	    vpostdiv = 0;
+	    pll_ext_cntl |= 0x10;
+	    break;
+	case 4:
+	    vpostdiv = 2;
+	    break;
+	case 6:
+	    vpostdiv = 2;
+	    pll_ext_cntl |= 0x10;
+	    break;
+	case 8:
+	    vpostdiv = 3;
+	    break;
+	case 12:
+	    vpostdiv = 3;
+	    pll_ext_cntl |= 0x10;
+	    break;
+    }
+    vclk_post_div = vpostdiv;
+
+    pll->pll_ref_div = pll_ref_div;
+    pll->pll_gen_cntl = pll_gen_cntl;
+    pll->mclk_fb_div = mclk_fb_div;
+    pll->vclk_post_div = vclk_post_div;
+    pll->vclk_fb_div = vclk_fb_div;
+    pll->pll_ext_cntl = pll_ext_cntl;
+    return 0;
+}
 
-    /* Check if we know about the wanted video mode */
-    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))
+static int aty_pll_ct_to_var(const struct pll_ct *pll,
+			     struct fb_var_screeninfo *var)
+{
+    u8 pll_ref_div = pll->pll_ref_div;
+    u8 vclk_fb_div = pll->vclk_fb_div;
+    u8 vclk_post_div = pll->vclk_post_div;
+    u8 pll_ext_cntl = pll->pll_ext_cntl;
+    static u8 vclk_post_div_tab[] = {
+	1, 2, 4, 8,
+	3, 0, 6, 12
+    };
+    u8 vpostdiv = vclk_post_div_tab[((pll_ext_cntl & 0x10) >> 1) |
+				    (vclk_post_div & 3)];
+    if (vpostdiv == 0)
 	return -EINVAL;
+    var->pixclock = pll_ref_div*vpostdiv*ref_clk_per/vclk_fb_div/2;
+    return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void atyfb_set_par(const struct atyfb_par *par,
+			  struct fb_info_aty *info)
+{
+    u32 i;
+
+    info->current_par = *par;
+
+    aty_set_crtc(info, &par->crtc);
+    aty_st_8(CLOCK_CNTL, 0, info);
+    aty_st_8(CLOCK_CNTL, CLOCK_STROBE, info);
+
+    if ((Gx == GX_CHIP_ID) || (Gx == CX_CHIP_ID)) {
+	switch (info->dac_type) {
+	    case DAC_IBMRGB514:
+		aty_set_dac_514(info, par->crtc.bpp);
+		break;
+	    case DAC_ATI68860_B:
+		/* FIXME */
+		break;
+	}
+	aty_set_pll_gx(info, &par->pll.gx);
+	aty_st_le32(BUS_CNTL, 0x890e20f1, info);
+	aty_st_le32(DAC_CNTL, 0x47052100, info);
+    } else {
+	aty_set_pll_ct(info, &par->pll.ct);
+	i = aty_ld_le32(MEM_CNTL, info) & 0xf30fffff;
+	if (!(Gx == VT_CHIP_ID && (Rev == 0x40 || Rev == 0x48)))
+		i |= info->mem_refresh_rate << 20;
+	switch (par->crtc.bpp) {
+	    case 8:
+	    case 24:
+		i |= 0x00000000;
+		break;
+	    case 16:
+		i |= 0x04000000;
+		break;
+	    case 32:
+		i |= 0x08000000;
+		break;
+	}
+	if ((Gx == CT_CHIP_ID) || (Gx == ET_CHIP_ID)) {
+	    aty_st_le32(DAC_CNTL, 0x87010184, info);
+	    aty_st_le32(BUS_CNTL, 0x680000f9, info);
+	} else if ((Gx == VT_CHIP_ID) || (Gx == VU_CHIP_ID)) {
+	    aty_st_le32(DAC_CNTL, 0x87010184, info);
+	    aty_st_le32(BUS_CNTL, 0x680000f9, info);
+	} else {
+	    /* GT */
+	    aty_st_le32(DAC_CNTL, 0x86010102, info);
+	    aty_st_le32(BUS_CNTL, 0x7b23a040, info);
+	    aty_st_le32(EXT_MEM_CNTL, 0x5000001, info);
+	}
+	aty_st_le32(MEM_CNTL, i, info);
+    }
+    aty_st_8(DAC_MASK, 0xff, info);
+
+    /* Initialize the graphics engine */
+    if (par->accel_flags & FB_ACCELF_TEXT)
+	init_engine(par, info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+    if (console_fb_info == &info->fb_info) {
+	struct fb_var_screeninfo var;
+	int vmode, cmode;
+	display_info.height = ((par->crtc.v_tot_disp>>16) & 0x7ff)+1;
+	display_info.width = (((par->crtc.h_tot_disp>>16) & 0xff)+1)*8;
+	display_info.depth = par->crtc.bpp;
+	display_info.pitch = par->crtc.vxres*par->crtc.bpp/8;
+	atyfb_encode_var(&var, par, info);
+	if (mac_var_to_vmode(&var, &vmode, &cmode))
+	    display_info.mode = 0;
+	else
+	    display_info.mode = vmode;
+	strcpy(display_info.name, atyfb_name);
+	display_info.fb_address = info->frame_buffer_phys;
+	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 */
+}
+
+static int atyfb_decode_var(const struct fb_var_screeninfo *var,
+			    struct atyfb_par *par,
+			    const struct fb_info_aty *info)
+{
+    int err;
+
+    if ((err = aty_var_to_crtc(info, var, &par->crtc)))
+	return err;
+    if ((Gx == GX_PCI_ID) || (Gx == CX_PCI_ID))
+	switch (info->clk_type) {
+	    case CLK_ATI18818_1:
+		err = aty_var_to_pll_18818(var, &par->pll.gx);
+		break;
+	    case CLK_IBMRGB514:
+		err = aty_var_to_pll_514(var, &par->pll.gx);
+		break;
+	}
+    else
+	err = aty_var_to_pll_ct(info, var, &par->pll.ct);
+    if (err)
+	return err;
+
+    if (var->accel_flags & FB_ACCELF_TEXT)
+	par->accel_flags = FB_ACCELF_TEXT;
+    else
+	par->accel_flags = 0;
 
 #if 0
     if (!fbmon_valid_timings(pixclock, htotal, vtotal, info))
@@ -1489,197 +1747,192 @@
     return 0;
 }
 
-static int encode_var(struct fb_var_screeninfo *var,
-		      const struct atyfb_par *par,
-		      struct fb_info_aty *info)
+static int atyfb_encode_var(struct fb_var_screeninfo *var,
+			    const struct atyfb_par *par,
+			    const 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 = 0;
-    u8 pll_ref_div, vclk_fb_div, vclk_post_div, pll_ext_cntl;
+    int err;
 
     memset(var, 0, sizeof(struct fb_var_screeninfo));
-    if (!init)
-	return -EINVAL;
 
-    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 (cmode) {
-	case CMODE_8:
-	    var->bits_per_pixel = 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 CMODE_16:	/* RGB 555 */
-	    var->bits_per_pixel = 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 CMODE_32:	/* RGB 888 */
-	    var->bits_per_pixel = 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;
-    }
-    var->red.msb_right = 0;
-    var->green.msb_right = 0;
-    var->blue.msb_right = 0;
-    var->transp.msb_right = 0;
-    var->nonstd = 0;
-    var->activate = 0;
+    if ((err = aty_crtc_to_var(&par->crtc, var)))
+	return err;
+    if ((Gx == GX_PCI_ID) || (Gx == CX_PCI_ID))
+	err = aty_pll_gx_to_var(&par->pll.gx, var);
+    else
+	err = aty_pll_ct_to_var(&par->pll.ct, var);
+    if (err)
+	return err;
+
     var->height = -1;
     var->width = -1;
-    var->vmode = FB_VMODE_NONINTERLACED;
-    var->accel_flags = par->accel;
+    var->accel_flags = par->accel_flags;
+
+    return 0;
+}
+
+
+
+static void set_off_pitch(struct atyfb_par *par,
+			  const struct fb_info_aty *info)
+{
+    u32 xoffset = par->crtc.xoffset;
+    u32 yoffset = par->crtc.yoffset;
+    u32 vxres = par->crtc.vxres;
+    u32 bpp = par->crtc.bpp;
+
+    par->crtc.off_pitch = ((yoffset*vxres+xoffset)*bpp/64) | (vxres<<19);
+    aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, info);
+}
+
+
+    /*
+     *  Open/Release the frame buffer device
+     */
+
+static int atyfb_open(struct fb_info *info, int user)
 
-    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;
+{
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)info;
+
+    if (user) {
+	if (fb->open)
+	    return -EBUSY;
+	fb->mmaped = 0;
+	fb->open = 1;
+	fb->vtconsole = -1;
+    } else {
+	fb->consolecnt++;
     }
-    var->pixclock = vclk;
+#endif
+    MOD_INC_USE_COUNT;
+    return(0);
+}
 
-    return 0;
+static int atyfb_release(struct fb_info *info, int user)
+{
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)info;
+
+    if (user) {
+	if (fb->vtconsole != -1)
+	    vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
+	fb->open = 0;
+	fb->mmaped = 0;
+	fb->vtconsole = -1;
+    } else {
+	fb->consolecnt--;
+    }
+#endif
+    MOD_DEC_USE_COUNT;
+    return(0);
 }
 
 
-static void init_par(struct atyfb_par *par, int vmode, int cmode)
+static int encode_fix(struct fb_fix_screeninfo *fix,
+		      const struct atyfb_par *par,
+		      const struct fb_info_aty *info)
 {
-    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;
+    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+
+    strcpy(fix->id, atyfb_name);
+    fix->smem_start = (char *)info->frame_buffer_phys;
+    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 auxiliary aperture instead so we can acces the
+     *  full 8 MB of video RAM on 8 MB boards
+     */
+    if (fix->smem_len > 0x800000-GUI_RESERVE)
+	fix->smem_len = 0x800000-GUI_RESERVE;
+#endif
+    /*
+     *  Reg Block 0 (CT-compatible block) is at ati_regbase_phys
+     *  Reg Block 1 (multimedia extensions) is at ati_regbase_phys-0x400
+     */
+    if ((Gx == GX_CHIP_ID) || (Gx == CX_CHIP_ID)) {
+	fix->mmio_start = (char *)info->ati_regbase_phys;
+	fix->mmio_len = 0x400;
+	fix->accel = FB_ACCEL_ATI_MACH64GX;
+    } else if ((Gx == CT_CHIP_ID) || (Gx == ET_CHIP_ID)) {
+	fix->mmio_start = (char *)info->ati_regbase_phys;
+	fix->mmio_len = 0x400;
+	fix->accel = FB_ACCEL_ATI_MACH64CT;
+    } else if ((Gx == VT_CHIP_ID) || (Gx == VU_CHIP_ID)) {
+	fix->mmio_start = (char *)(info->ati_regbase_phys-0x400);
+	fix->mmio_len = 0x800;
+	fix->accel = FB_ACCEL_ATI_MACH64VT;
+    } else {
+	fix->mmio_start = (char *)(info->ati_regbase_phys-0x400);
+	fix->mmio_len = 0x800;
+	fix->accel = FB_ACCEL_ATI_MACH64GT;
+    }
+    fix->type = FB_TYPE_PACKED_PIXELS;
+    fix->type_aux = 0;
+    fix->line_length = par->crtc.vxres*par->crtc.bpp/8;
+    fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+				     : FB_VISUAL_DIRECTCOLOR;
+    fix->ywrapstep = 0;
+    fix->xpanstep = 8;
+    fix->ypanstep = 1;
+
+    return 0;
 }
 
 
+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
+};
+
+#ifdef __sparc__
+struct fb_var_screeninfo default_var_1024x768 __initdata = {
+    /* 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, 176, 16, 28, 1, 96, 3,
+    FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+};
+
+struct fb_var_screeninfo default_var_1152x900 __initdata = {
+    /* 1152x900, 76 Hz, Non-Interlaced (110.0 MHz dotclock) */
+    1152, 900, 1152, 900, 0, 0, 8, 0,
+    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+    0, 0, -1, -1, 0, 9091, 234, 24, 34, 3, 100, 3,
+    FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+};
+
+struct fb_var_screeninfo default_var_1280x1024 __initdata = {
+    /* 1280x1024, 75 Hz, Non-Interlaced (135.00 MHz dotclock) */
+    1280, 1024, 1280, 1024, 0, 0, 8, 0,
+    {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+    0, 0, -1, -1, 0, 7408, 248, 16, 38, 1, 144, 3,
+    FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+};
+#endif
+
+
     /*
      *  Get the Fixed Part of the Display
      */
 
 static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
-			 struct fb_info *info)
+			 struct fb_info *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    const struct fb_info_aty *info = (struct fb_info_aty *)fb;
     struct atyfb_par par;
 
     if (con == -1)
-	par = info2->default_par;
+	par = info->default_par;
     else
-	decode_var(&fb_display[con].var, &par, info2);
-    encode_fix(fix, &par, info2);
+	atyfb_decode_var(&fb_display[con].var, &par, info);
+    encode_fix(fix, &par, info);
     return 0;
 }
 
@@ -1689,12 +1942,12 @@
      */
 
 static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
-			 struct fb_info *info)
+			 struct fb_info *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    const struct fb_info_aty *info = (struct fb_info_aty *)fb;
 
     if (con == -1)
-	encode_var(var, &info2->default_par, (struct fb_info_aty *)info);
+	atyfb_encode_var(var, &info->default_par, info);
     else
 	*var = fb_display[con].var;
     return 0;
@@ -1706,9 +1959,9 @@
      */
 
 static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
-			 struct fb_info *info)
+			 struct fb_info *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    struct fb_info_aty *info = (struct fb_info_aty *)fb;
     struct atyfb_par par;
     struct display *display;
     int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel, accel, err;
@@ -1717,12 +1970,12 @@
     if (con >= 0)
 	display = &fb_display[con];
     else
-	display = &fb_disp;	/* used during initialization */
+	display = fb->disp;	/* used during initialization */
 
-    if ((err = decode_var(var, &par, info2)))
+    if ((err = atyfb_decode_var(var, &par, info)))
 	return err;
 
-    encode_var(var, &par, (struct fb_info_aty *)info);
+    atyfb_encode_var(var, &par, (struct fb_info_aty *)info);
 
     if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
 	oldxres = display->var.xres;
@@ -1737,8 +1990,8 @@
 	    oldbpp != var->bits_per_pixel || oldaccel != var->accel_flags) {
 	    struct fb_fix_screeninfo fix;
 
-	    encode_fix(&fix, &par, info2);
-	    display->screen_base = (char *)info2->frame_buffer;
+	    encode_fix(&fix, &par, info);
+	    display->screen_base = (char *)info->frame_buffer;
 	    display->visual = fix.visual;
 	    display->type = fix.type;
 	    display->type_aux = fix.type_aux;
@@ -1748,20 +2001,25 @@
 	    display->can_soft_blank = 1;
 	    display->inverse = 0;
 	    accel = var->accel_flags & FB_ACCELF_TEXT;
-	    switch (par.hw.gx.cmode) {
+	    switch (par.crtc.bpp) {
 #ifdef FBCON_HAS_CFB8
-		case CMODE_8:
-		    display->dispsw = accel ? &fbcon_aty8 : &fbcon_cfb8;
+		case 8:
+		    *display->dispsw = accel ? fbcon_aty8 : fbcon_cfb8;
 		    break;
 #endif
 #ifdef FBCON_HAS_CFB16
-		case CMODE_16:
-		    display->dispsw = accel ? &fbcon_aty16 : &fbcon_cfb16;
+		case 16:
+		    *display->dispsw = accel ? fbcon_aty16 : fbcon_cfb16;
+		    break;
+#endif
+#ifdef FBCON_HAS_CFB24
+		case 24:
+		    *display->dispsw = accel ? fbcon_aty24 : fbcon_cfb24;
 		    break;
 #endif
 #ifdef FBCON_HAS_CFB32
-		case CMODE_32:
-		    display->dispsw = accel ? &fbcon_aty32 : &fbcon_cfb32;
+		case 32:
+		    *display->dispsw = accel ? fbcon_aty32 : fbcon_cfb32;
 		    break;
 #endif
 		default:
@@ -1769,19 +2027,19 @@
 		    break;
 	    }
 	    display->scrollmode = accel ? 0 : SCROLL_YREDRAW;
-	    if (info->changevar)
-		(*info->changevar)(con);
-	    if (info2->cursor) {
-	    	display->dispsw->cursor = atyfb_cursor;
-	    	display->dispsw->set_font = atyfb_set_font;
+	    if (info->fb_info.changevar)
+		(*info->fb_info.changevar)(con);
+	    if (info->cursor) {
+		display->dispsw->cursor = atyfb_cursor;
+		display->dispsw->set_font = atyfb_set_font;
 	    }
 	}
 	if (con == currcon)
-	    atyfb_set_par(&par, info2);
+	    atyfb_set_par(&par, info);
 	if (oldbpp != var->bits_per_pixel) {
 	    if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 		return err;
-	    do_install_cmap(con, info);
+	    do_install_cmap(con, &info->fb_info);
 	}
     }
 
@@ -1796,21 +2054,21 @@
      */
 
 static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
-			     struct fb_info *info)
+			     struct fb_info *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    struct fb_info_aty *info = (struct fb_info_aty *)fb;
     u32 xres, yres, xoffset, yoffset;
-    struct atyfb_par *par = &info2->current_par;
+    struct atyfb_par *par = &info->current_par;
 
-    xres = vmode_attrs[par->hw.gx.vmode-1].hres;
-    yres = vmode_attrs[par->hw.gx.vmode-1].vres;
+    xres = (((par->crtc.h_tot_disp>>16) & 0xff)+1)*8;
+    yres = ((par->crtc.v_tot_disp>>16) & 0x7ff)+1;
     xoffset = (var->xoffset+7) & ~7;
     yoffset = var->yoffset;
-    if (xoffset+xres > par->vxres || yoffset+yres > par->vyres)
+    if (xoffset+xres > par->crtc.vxres || yoffset+yres > par->crtc.vyres)
 	return -EINVAL;
-    par->xoffset = xoffset;
-    par->yoffset = yoffset;
-    set_off_pitch(par, info2);
+    par->crtc.xoffset = xoffset;
+    par->crtc.yoffset = yoffset;
+    set_off_pitch(par, info);
     return 0;
 }
 
@@ -1965,6 +2223,18 @@
 	vma->vm_file = file;
 	file->f_count++;
 	vma->vm_flags |= VM_IO;
+
+	if (!fb->mmaped) {
+		int lastconsole = 0;
+
+		if (info->display_fg)
+			lastconsole = info->display_fg->vc_num;
+		fb->mmaped = 1;
+		if (fb->consolecnt && fb_display[lastconsole].fb_info == info) {
+			fb->vtconsole = lastconsole;
+			vt_cons[lastconsole]->vc_mode = KD_GRAPHICS;
+		}
+	}
 	return 0;
 }
 #endif /* __sparc__ */
@@ -1977,179 +2247,206 @@
 {
     u32 chip_id;
     u32 i;
-    int j, k, sense;
+    int j, k;
     struct fb_var_screeninfo var;
-    struct aty_regvals *init;
+    struct display *disp;
     const char *chipname = NULL;
-    u8 rev;
+    int pll, mclk;
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
+    int sense;
+#endif
 
     info->aty_cmap_regs = (struct aty_cmap_regs *)(info->ati_regbase+0xc0);
     chip_id = aty_ld_le32(CONFIG_CHIP_ID, info);
+    Gx = chip_id & CFG_CHIP_TYPE;
+    Rev = (chip_id & CFG_CHIP_REV)>>24;
     for (j = 0; j < (sizeof(aty_features)/sizeof(*aty_features)); j++)
-	if (aty_features[j].chip_type == (chip_id & CFG_CHIP_TYPE)) {
+	if (aty_features[j].chip_type == Gx) {
 	    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;
+	    break;
 	}
     if (!chipname) {
-	printk("atyfb: Unknown Mach64 0x%04x\n", chip_id & CFG_CHIP_TYPE);
+	printk("atyfb: Unknown mach64 0x%04x\n", Gx);
 	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;
+	printk("atyfb: %s [0x%04x rev 0x%2x] ", chipname, Gx, Rev);
+    if ((Gx == GX_CHIP_ID) || (Gx == CX_CHIP_ID)) {
+	info->bus_type = (aty_ld_le32(CONFIG_STAT0, info) >> 0) & 0x07;
+	info->ram_type = (aty_ld_le32(CONFIG_STAT0, info) >> 3) & 0x07;
+	/* FIXME: clockchip/RAMDAC probing? */
+#ifdef CONFIG_ATARI
+	info->dac_type = DAC_ATI68860_B;
+	info->clk_type = CLK_ATI18818_1;
+#else
+	info->dac_type = DAC_IBMRGB514;
+	info->clk_type = CLK_IBMRGB514;
+#endif
+	/* FIXME */
+	pll = 135;
+	mclk = 50;
+    } else {
+	info->bus_type = PCI;
+	info->ram_type = (aty_ld_le32(CONFIG_STAT0, info) & 0x07);
+	info->dac_type = DAC_INTERNAL;
+	info->clk_type = CLK_INTERNAL;
+	if ((Gx == CT_CHIP_ID) || (Gx == ET_CHIP_ID)) {
+	    pll = 135;
+	    mclk = 60;
+	} else {
+	    mclk = info->ram_type >= SDRAM ? 67 : 63;
+	    if ((Gx == VT_CHIP_ID) && (Rev == 0x08)) {
+		/* VTA3 */
+		pll = 170;
+	    } else if (((Gx == VT_CHIP_ID) && ((Rev == 0x40) ||
+					       (Rev == 0x48))) ||
+		       ((Gx == VT_CHIP_ID) && ((Rev == 0x01) ||
+					       (Rev == 0x9a))) ||
+		       (Gx == VU_CHIP_ID)) {
+		/* VTA4 or VTB */
+		pll = 200;
+	    } else if (Gx == VT_CHIP_ID) {
+		/* other VT */
+		pll = 135;
+		mclk = 63;
+	    } else if ((Gx == GT_CHIP_ID) && (Rev & 0x01)) {
+		/* RAGE II */
+		pll = 170;
+	    } else if (((Gx == GT_CHIP_ID) && (Rev & 0x02)) ||
+		       (Gx == GU_CHIP_ID)) {
+		/* RAGE II+ */
+		pll = 200;
+	    } else if ((Gx == GB_CHIP_ID) || (Gx == GD_CHIP_ID) ||
+		       (Gx == GI_CHIP_ID) || (Gx == GP_CHIP_ID) ||
+		       (Gx == GQ_CHIP_ID)) {
+		/* RAGE PRO */
+		pll = 230;
+	    } else {
+		/* other RAGE */
+		pll = 135;
+		mclk = 63;
+	    }
+	}
     }
-    printk(" %c%d]\n", 'A'+(rev & 7), rev>>6);
+    if (mclk < 44)
+	info->mem_refresh_rate = 0;	/* 000 = 10 Mhz - 43 Mhz */
+    else if (mclk < 50)
+	info->mem_refresh_rate = 1;	/* 001 = 44 Mhz - 49 Mhz */
+    else if (mclk < 55)
+	info->mem_refresh_rate = 2;	/* 010 = 50 Mhz - 54 Mhz */
+    else if (mclk < 66)
+	info->mem_refresh_rate = 3;	/* 011 = 55 Mhz - 65 Mhz */
+    else if (mclk < 75)
+	info->mem_refresh_rate = 4;	/* 100 = 66 Mhz - 74 Mhz */
+    else if (mclk < 80)
+	info->mem_refresh_rate = 5;	/* 101 = 75 Mhz - 79 Mhz */
+    else if (mclk < 100)
+	info->mem_refresh_rate = 6;	/* 110 = 80 Mhz - 100 Mhz */
+    else
+	info->mem_refresh_rate = 7;	/* 111 = 100 Mhz and above */
+    printk("%d MHz PLL, %d Mhz MCLK\n", pll, mclk);
+    info->pll_per = 1000000/pll;
+    info->mclk_per = 1000000/mclk;
 
     i = aty_ld_le32(MEM_CNTL, info);
-    if (info->chip_class != CLASS_GT)
-	switch (i & MEM_SIZE_ALIAS) {
+    if (((Gx == GT_CHIP_ID) && (Rev & 0x03)) || (Gx == GU_CHIP_ID) ||
+	((Gx == VT_CHIP_ID) && (Rev & 0x01)) || (Gx == VU_CHIP_ID) ||
+	(Gx == LG_CHIP_ID) || (Gx == GB_CHIP_ID) || (Gx == GD_CHIP_ID) ||
+	(Gx == GI_CHIP_ID) || (Gx == GP_CHIP_ID) || (Gx == GQ_CHIP_ID))
+	switch (i & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
 	    case MEM_SIZE_512K:
 		info->total_vram = 0x80000;
 		break;
 	    case MEM_SIZE_1M:
 		info->total_vram = 0x100000;
 		break;
-	    case MEM_SIZE_2M:
+	    case MEM_SIZE_2M_GTB:
 		info->total_vram = 0x200000;
 		break;
-	    case MEM_SIZE_4M:
+	    case MEM_SIZE_4M_GTB:
 		info->total_vram = 0x400000;
 		break;
-	    case MEM_SIZE_6M:
+	    case MEM_SIZE_6M_GTB:
 		info->total_vram = 0x600000;
 		break;
-	    case MEM_SIZE_8M:
+	    case MEM_SIZE_8M_GTB:
 		info->total_vram = 0x800000;
 		break;
 	    default:
 		info->total_vram = 0x80000;
 	}
     else
-	switch (i & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
+	switch (i & MEM_SIZE_ALIAS) {
 	    case MEM_SIZE_512K:
 		info->total_vram = 0x80000;
 		break;
 	    case MEM_SIZE_1M:
 		info->total_vram = 0x100000;
 		break;
-	    case MEM_SIZE_2M_GTB:
+	    case MEM_SIZE_2M:
 		info->total_vram = 0x200000;
 		break;
-	    case MEM_SIZE_4M_GTB:
+	    case MEM_SIZE_4M:
 		info->total_vram = 0x400000;
 		break;
-	    case MEM_SIZE_6M_GTB:
+	    case MEM_SIZE_6M:
 		info->total_vram = 0x600000;
 		break;
-	    case MEM_SIZE_8M_GTB:
+	    case MEM_SIZE_8M:
 		info->total_vram = 0x800000;
 		break;
 	    default:
 		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 0
-    printk("aty_init: regbase = %lx, frame_buffer = %lx, total_vram = %x\n",
-	   info->ati_regbase, info->frame_buffer, info->total_vram);
-#endif
+    if (info->bus_type == ISA)
+	if ((info->total_vram == 0x400000) || (info->total_vram == 0x800000)) {
+	    /* protect GUI-regs if complete Aperture is VRAM */
+	    info->total_vram -= GUI_RESERVE;
+	}
 
-    sense = read_aty_sense(info);
-#if 0
-    printk("monitor sense = %x\n", sense);
-#endif
 #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)
+	if (default_vmode <= 0 || default_vmode > VMODE_MAX)
 	    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)))
+    if (default_vmode == VMODE_CHOOSE) {
+	sense = read_aty_sense(info);
+	default_vmode = mac_map_monitor_sense(sense);
+    }
+    if (default_vmode <= 0 || default_vmode > VMODE_MAX)
 	default_vmode = VMODE_640_480_60;
-
-    /*
-     * Reduce the pixel size if we don't have enough VRAM.
-     */
-
     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
-	 */
-	if (aty_vram_reqd(&info->default_par) > info->total_vram)
-	    do {
-		default_vmode--;
-		init_par(&info->default_par, default_vmode, default_cmode);
-		init = get_aty_struct(default_vmode, info);
-	    } while ((init == 0) &&
-		     (default_vmode > VMODE_640_480_60));
-    }
-
-    if (info->chip_class == CLASS_GT &&
-	(aty_ld_le32(CONFIG_STAT0, info) & 7) == 5
-	&& init->crtc_gen_cntl[1] == 0) {
-	    default_vmode = VMODE_640_480_67;
-	    default_cmode = CMODE_8;
-	    init_par(&info->default_par, default_vmode, default_cmode);
-    }
-
-    switch (info->chip_class) {
-	case CLASS_GX:
-	    strcat(atyfb_name, "GX");
-	    break;
-	case CLASS_CT:
-	    strcat(atyfb_name, "CT");
-	    break;
-	case CLASS_VT:
-	    strcat(atyfb_name, "VT");
-	    break;
-	case CLASS_GT:
-	    strcat(atyfb_name, "GT");
-	    break;
+    if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+	var = default_var;
+#else /* !CONFIG_PMAC && !CONFIG_CHRP */
+    var = default_var;
+#endif /* !CONFIG_PMAC && !CONFIG_CHRP */
+    var.accel_flags |= FB_ACCELF_TEXT;
+    if (atyfb_decode_var(&var, &info->default_par, info)) {
+	printk("atyfb: can't set default video mode\n");
+	return 0;
     }
+
+    if ((Gx == GX_CHIP_ID) || (Gx == CX_CHIP_ID))
+	strcat(atyfb_name, "GX");
+    else if ((Gx == CT_CHIP_ID) || (Gx == ET_CHIP_ID))
+	strcat(atyfb_name, "CT");
+    else if ((Gx == VT_CHIP_ID) || (Gx == VU_CHIP_ID))
+	strcat(atyfb_name, "VT");
+    else
+	strcat(atyfb_name, "GT");
+
+    disp = &info->disp;
+
     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.disp = disp;
+    strcpy(info->fb_info.fontname, fontname);
     info->fb_info.changevar = NULL;
     info->fb_info.switch_con = &atyfbcon_switch;
     info->fb_info.updatevar = &atyfbcon_updatevar;
@@ -2162,13 +2459,16 @@
 	info->palette[j].blue = default_blu[k];
     }
 
-    if (info->chip_class == CLASS_VT || info->chip_class == CLASS_GT) {
+    if ((Gx == VT_CHIP_ID) || (Gx == GT_CHIP_ID) || (Gx == GU_CHIP_ID) ||
+	(Gx == LG_CHIP_ID) || (Gx == GB_CHIP_ID) || (Gx == GD_CHIP_ID) ||
+	(Gx == GI_CHIP_ID) || (Gx == GP_CHIP_ID) || (Gx == GQ_CHIP_ID) ||
+	(Gx == VU_CHIP_ID)) {
 	info->cursor = kmalloc(sizeof(struct aty_cursor), GFP_ATOMIC);
-	memset(info->cursor, 0, sizeof(*info->cursor));
+	if (info->cursor)
+	    memset(info->cursor, 0, sizeof(*info->cursor));
     }
 
-    atyfb_set_par(&info->default_par, info);
-    encode_var(&var, &info->default_par, info);
+    disp->dispsw = &info->dispsw;
     atyfb_set_var(&var, -1, &info->fb_info);
 
     if (register_framebuffer(&info->fb_info) < 0)
@@ -2188,14 +2488,16 @@
     struct pci_dev *pdev;
     struct fb_info_aty *info;
     unsigned long addr;
-    int i, j;
-    u16 tmp;
 #ifdef __sparc__
     extern int con_is_present(void);
+    u32 chip_id;
+    int i, j;
 
     /* Do not attach when we have a serial console. */
     if (!con_is_present())
-	    return;
+	return;
+#else
+    u16 tmp;
 #endif
 
     for (pdev = pci_devices; pdev; pdev = pdev->next) {
@@ -2236,7 +2538,7 @@
 		/* nothing */;
 	    j = i + 1;
 
-            info->mmap_map = kmalloc(j * sizeof(*info->mmap_map), GFP_ATOMIC);
+	    info->mmap_map = kmalloc(j * sizeof(*info->mmap_map), GFP_ATOMIC);
 	    if (!info->mmap_map) {
 		printk("atyfb_init: can't alloc mmap_map\n");
 		kfree(info);
@@ -2284,8 +2586,8 @@
 	    /*
 	     * Fix PROMs idea of MEM_CNTL settings...
 	     */
-	    tmp = aty_ld_le32(CONFIG_CHIP_ID, info) & CFG_CHIP_TYPE;
-	    if (tmp == VT_CHIP_ID) {
+	    chip_id = aty_ld_le32(CONFIG_CHIP_ID, info) & CFG_CHIP_TYPE;
+	    if ((chip_id & 0xffff) == VT_CHIP_ID && !((chip_id >> 24) & 1)) {
 		u32 mem = aty_ld_le32(MEM_CNTL, info);
 		switch (mem & 0x0f) {
 		    case 3:
@@ -2318,34 +2620,35 @@
 		int height = prom_getintdefault(node, "height", 768);
 		int depth = prom_getintdefault(node, "depth", 8);
 
-		switch (depth) {
-		    case 8:
-			default_cmode = CMODE_8;
+		switch (width) {
+		    case 1024:
+			if (height == 768)
+			    default_var = default_var_1024x768;
 			break;
-		    case 16:
-			default_cmode = CMODE_16;
+		    case 1152:
+			if (height == 900)
+			    default_var = default_var_1152x900;
 			break;
-		    case 32:
-			default_cmode = CMODE_32;
+		    case 1280:
+			if (height == 1024)
+			    default_var = default_var_1280x1024;
 			break;
 		    default:
 			break;
 		}
 
-		switch (width) {
-		    case 1024:
-			if (height == 768)
-			    default_vmode = VMODE_1024_768_75;
+		switch (depth) {
+		    case 8:
+			default_var.bits_per_pixel = 8;
 			break;
-		    case 1152:
-			if (height == 870)
-			    default_vmode = VMODE_1152_870_75;
+		    case 16:
+			default_var.bits_per_pixel = 16;
 			break;
-		    case 1280:
-			if (height == 960)
-			    default_vmode = VMODE_1280_960_75;
-			else if (height == 1024)
-			    default_vmode = VMODE_1280_1024_75;
+		    case 24:
+			default_var.bits_per_pixel = 24;
+			break;
+		    case 32:
+			default_var.bits_per_pixel = 32;
 			break;
 		    default:
 			break;
@@ -2354,9 +2657,6 @@
 
 #else /* __sparc__ */
 
-	    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);
@@ -2388,7 +2688,7 @@
 	    if (!aty_init(info, "PCI")) {
 		if (info->mmap_map)
 		    kfree(info->mmap_map);
-	        kfree(info);
+		kfree(info);
 	    }
 	}
     }
@@ -2500,39 +2800,51 @@
 __initfunc(void atyfb_setup(char *options, int *ints))
 {
     char *this_opt;
-    int vmode;
-    int depth;
+
     if (!options || !*options)
 	return;
 
     for (this_opt = strtok(options, ","); this_opt;
 	 this_opt = strtok(NULL, ",")) {
+	if (!strncmp(this_opt, "font:", 5)) {
+		char *p;
+		int i;
+
+		p = this_opt + 5;
+		for (i = 0; i < sizeof(fontname) - 1; i++)
+			if (!*p || *p == ' ' || *p == ',')
+				break;
+		memcpy(fontname, this_opt + 5, i);
+		fontname[i] = 0;
+	}
+#if defined(CONFIG_PMAC) || defined(CONFIG_CHRP)
 	if (!strncmp(this_opt, "vmode:", 6)) {
-	    vmode = simple_strtoul(this_opt+6, NULL, 0);
+	    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)) {
-	    depth = simple_strtoul(this_opt+6, NULL, 0);
+	    int depth = simple_strtoul(this_opt+6, NULL, 0);
 	    switch (depth) {
 		case 8:
-		    default_cmode = CMODE_8;
+		    default_cmode = 0;
 		    break;
 		case 15:
 		case 16:
-		    default_cmode = CMODE_16;
+		    default_cmode = 1;
 		    break;
 		case 24:
 		case 32:
-		    default_cmode = CMODE_32;
+		    default_cmode = 2;
 		    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))) {
+	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);
@@ -2595,39 +2907,48 @@
 }
 #endif /* CONFIG_ATARI */
 
-static int atyfbcon_switch(int con, struct fb_info *info)
+static int atyfbcon_switch(int con, struct fb_info *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    struct fb_info_aty *info = (struct fb_info_aty *)fb;
     struct atyfb_par par;
 
     /* Do we have to save the colormap? */
     if (fb_display[currcon].cmap.len)
 	fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1,
-		    atyfb_getcolreg, info);
+		    atyfb_getcolreg, fb);
+
+    /* Erase HW Cursor */
+    if (info->cursor)
+	atyfb_cursor(&fb_display[currcon], CM_ERASE,
+		     info->cursor->pos.x, info->cursor->pos.y);
+
     currcon = con;
-    decode_var(&fb_display[con].var, &par, info2);
-    atyfb_set_par(&par, info2);
+
+    atyfb_decode_var(&fb_display[con].var, &par, info);
+    atyfb_set_par(&par, info);
+
     /* Install new colormap */
-    do_install_cmap(con, info);
+    do_install_cmap(con, fb);
+
     /* Install hw cursor */
-    if (info2->cursor) {
-	aty_set_cursor_color(info2, cursor_pixel_map, cursor_color_map,
+    if (info->cursor) {
+	aty_set_cursor_color(info, cursor_pixel_map, cursor_color_map,
 			     cursor_color_map, cursor_color_map);
-	aty_set_cursor_shape(info2);
+	aty_set_cursor_shape(info);
     }
-    return 0;
+    return 1;
 }
 
     /*
      *  Update the `var' structure (called by fbcon.c)
      */
 
-static int atyfbcon_updatevar(int con, struct fb_info *info)
+static int atyfbcon_updatevar(int con, struct fb_info *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    struct fb_info_aty *info = (struct fb_info_aty *)fb;
 
-    info2->current_par.yoffset = fb_display[con].var.yoffset;
-    set_off_pitch(&info2->current_par, info2);
+    info->current_par.crtc.yoffset = fb_display[con].var.yoffset;
+    set_off_pitch(&info->current_par, info);
     return 0;
 }
 
@@ -2635,12 +2956,12 @@
      *  Blank the display.
      */
 
-static void atyfbcon_blank(int blank, struct fb_info *info)
+static void atyfbcon_blank(int blank, struct fb_info *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    struct fb_info_aty *info = (struct fb_info_aty *)fb;
     u8 gen_cntl;
 
-    gen_cntl = aty_ld_8(CRTC_GEN_CNTL, info2);
+    gen_cntl = aty_ld_8(CRTC_GEN_CNTL, info);
     if (blank > 0)
 	switch (blank-1) {
 	    case VESA_NO_BLANKING:
@@ -2658,7 +2979,7 @@
 	}
     else
 	gen_cntl &= ~(0x4c);
-    aty_st_8(CRTC_GEN_CNTL, gen_cntl, info2);
+    aty_st_8(CRTC_GEN_CNTL, gen_cntl, info);
 }
 
 
@@ -2668,15 +2989,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 *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    struct fb_info_aty *info = (struct fb_info_aty *)fb;
 
     if (regno > 255)
 	return 1;
-    *red = info2->palette[regno].red;
-    *green = info2->palette[regno].green;
-    *blue = info2->palette[regno].blue;
+    *red = info->palette[regno].red;
+    *green = info->palette[regno].green;
+    *blue = info->palette[regno].blue;
     return 0;
 }
 
@@ -2688,36 +3009,41 @@
      */
 
 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 *fb)
 {
-    struct fb_info_aty *info2 = (struct fb_info_aty *)info;
+    struct fb_info_aty *info = (struct fb_info_aty *)fb;
     int i, scale;
 
     if (regno > 255)
 	return 1;
-    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)
+    info->palette[regno].red = red;
+    info->palette[regno].green = green;
+    info->palette[regno].blue = blue;
+    i = aty_ld_8(DAC_CNTL, info) & 0xfc;
+    if ((Gx == GT_CHIP_ID) || (Gx == GU_CHIP_ID) || (Gx == LG_CHIP_ID) ||
+	(Gx == GB_CHIP_ID) || (Gx == GD_CHIP_ID) || (Gx == GI_CHIP_ID) ||
+	(Gx == GP_CHIP_ID) || (Gx == GQ_CHIP_ID))
 	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);
+    aty_st_8(DAC_CNTL, i, info);
+    aty_st_8(DAC_REGS + DAC_MASK, 0xff, info);
     eieio();
-    scale = ((info2->chip_class != CLASS_GX) &&
-	     (info2->current_par.hw.gx.cmode == CMODE_16)) ? 3 : 0;
-    info2->aty_cmap_regs->windex = regno << scale;
+    scale = ((Gx != GX_CHIP_ID) && (Gx != CX_CHIP_ID) &&
+	     (info->current_par.crtc.bpp == 16)) ? 3 : 0;
+    info->aty_cmap_regs->windex = regno << scale;
     eieio();
-    info2->aty_cmap_regs->lut = red << scale;
+    info->aty_cmap_regs->lut = red << scale;
     eieio();
-    info2->aty_cmap_regs->lut = green << scale;
+    info->aty_cmap_regs->lut = green << scale;
     eieio();
-    info2->aty_cmap_regs->lut = blue << scale;
+    info->aty_cmap_regs->lut = blue << scale;
     eieio();
     if (regno < 16) {
 #ifdef FBCON_HAS_CFB16
 	fbcon_cfb16_cmap[regno] = (regno << 10) | (regno << 5) | regno;
 #endif
+#ifdef FBCON_HAS_CFB24
+	fbcon_cfb24_cmap[regno] = (regno << 16) | (regno << 8) | regno;
+#endif
 #ifdef FBCON_HAS_CFB32
 	fbcon_cfb32_cmap[regno] = (regno << 24) | (regno << 16) |
 				  (regno << 8) | regno;
@@ -2765,14 +3091,15 @@
     if (!width || !height)
 	return;
 
-    pitch_value = info->current_par.vxres;
-#if 0
-    if (par->hw.gx.cmode == CMODE_24) {
+    pitch_value = info->current_par.crtc.vxres;
+    if (info->current_par.crtc.bpp == 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;
+	pitch_value *= 3;
+	srcx *= 3;
+	dstx *= 3;
+	width *= 3;
     }
-#endif
 
     if (srcy < dsty) {
 	dsty += height - 1;
@@ -2806,6 +3133,13 @@
     if (!width || !height)
 	return;
 
+    if (info->current_par.crtc.bpp == 24) {
+	/* In 24 bpp, the engine is in 8 bpp - this requires that all */
+	/* horizontal coordinates and widths must be adjusted */
+	dstx *= 3;
+	width *= 3;
+    }
+
     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,
@@ -2823,6 +3157,13 @@
 static void fbcon_aty_bmove(struct display *p, int sy, int sx, int dy, int dx,
 			    int height, int width)
 {
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
     sx *= p->fontwidth;
     sy *= p->fontheight;
     dx *= p->fontwidth;
@@ -2837,7 +3178,15 @@
 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);
+    u32 bgx;
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
+    bgx = attr_bgcol_ec(p, conp);
     bgx |= (bgx << 8);
     bgx |= (bgx << 16);
 
@@ -2854,13 +3203,28 @@
 static void fbcon_aty8_putc(struct vc_data *conp, struct display *p, int c,
 			    int yy, int xx)
 {
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
     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 unsigned short *s, int count, int yy, int xx)
+			     const unsigned short *s, int count, int yy,
+			     int xx)
 {
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
     wait_for_idle((struct fb_info_aty *)p->fb_info);
     fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
 }
@@ -2868,7 +3232,7 @@
 static struct display_switch fbcon_aty8 = {
     fbcon_cfb8_setup, fbcon_aty_bmove, fbcon_aty_clear, fbcon_aty8_putc,
     fbcon_aty8_putcs, fbcon_cfb8_revc, NULL, NULL, fbcon_cfb8_clear_margins,
-    FONTWIDTH(8)
+    FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
 };
 #endif
 
@@ -2876,20 +3240,73 @@
 static void fbcon_aty16_putc(struct vc_data *conp, struct display *p, int c,
 			     int yy, int xx)
 {
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
     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 unsigned short *s, int count, int yy, int xx)
+			      const unsigned short *s, int count, int yy,
+			      int xx)
 {
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
     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, NULL, NULL, FONTWIDTH(8)
+    fbcon_aty16_putcs, fbcon_cfb16_revc, NULL, NULL, fbcon_cfb16_clear_margins,
+    FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
+};
+#endif
+
+#ifdef FBCON_HAS_CFB24
+static void fbcon_aty24_putc(struct vc_data *conp, struct display *p, int c,
+			     int yy, int xx)
+{
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
+    fbcon_cfb24_putc(conp, p, c, yy, xx);
+}
+
+static void fbcon_aty24_putcs(struct vc_data *conp, struct display *p,
+			      const unsigned short *s, int count, int yy,
+			      int xx)
+{
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
+    wait_for_idle((struct fb_info_aty *)p->fb_info);
+    fbcon_cfb24_putcs(conp, p, s, count, yy, xx);
+}
+
+static struct display_switch fbcon_aty24 = {
+    fbcon_cfb24_setup, fbcon_cfb24_bmove, fbcon_cfb24_clear, fbcon_aty24_putc,
+    fbcon_aty24_putcs, fbcon_cfb24_revc, NULL, NULL, fbcon_cfb24_clear_margins,
+    FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
 };
 #endif
 
@@ -2897,19 +3314,35 @@
 static void fbcon_aty32_putc(struct vc_data *conp, struct display *p, int c,
 			     int yy, int xx)
 {
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
     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 unsigned short *s, int count, int yy, int xx)
+			      const unsigned short *s, int count, int yy,
+			      int xx)
 {
+#ifdef __sparc__
+    struct fb_info_aty *fb = (struct fb_info_aty *)(p->fb_info);
+
+    if (fb->mmaped && currcon == fb->vtconsole)
+	return;
+#endif
+
     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, NULL, NULL, FONTWIDTH(8)
+    fbcon_aty32_putcs, fbcon_cfb32_revc, NULL, NULL, fbcon_cfb32_clear_margins,
+    FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
 };
 #endif

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