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

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

diff -u --recursive --new-file v2.1.90/linux/drivers/video/atyfb.c linux/drivers/video/atyfb.c
@@ -0,0 +1,1685 @@
+/*
+ *  linux/drivers/video/atyfb.c -- Frame buffer device for ATI/Open Firmware
+ *
+ *	Copyright (C) 1997 Geert Uytterhoeven
+ *
+ *  This driver is partly based on the PowerMac console driver:
+ *
+ *	Copyright (C) 1996 Paul Mackerras
+ *
+ *  and on the PowerMac ATI/mach64 display driver:
+ *
+ *	Copyright (C) 1997 Michael AK Tesch
+ *
+ *	      with work by Jon Howell
+ *			   Harry AC Eaton
+ *			   Anthony Tong <atong@uiuc.edu>
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License. See the file COPYING in the main directory of this archive for
+ *  more details.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/nvram.h>
+#include <linux/vc_ioctl.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+
+#include "aty.h"
+#include "fbcon.h"
+#include "fbcon-cfb8.h"
+#include "fbcon-cfb16.h"
+#include "fbcon-cfb32.h"
+
+
+static int currcon = 0;
+static struct display fb_disp;
+static struct fb_info fb_info;
+static struct { u_char red, green, blue, pad; } palette[256];
+
+static char atyfb_name[16] = "ATY Mach64";
+
+struct atyfb_par {
+    int vmode;
+    int cmode;
+    u_int vxres;	/* virtual screen size */
+    u_int vyres;
+    int xoffset;	/* virtual screen position */
+    int yoffset;
+};
+
+
+/*
+ * 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 */
+
+/*
+ * 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 */
+
+
+static int default_video_mode = VMODE_NVRAM;
+static int default_color_mode = CMODE_NVRAM;
+
+static struct atyfb_par default_par;
+static struct atyfb_par current_par;
+
+
+/*
+ * Addresses in NVRAM where video mode and pixel size are stored.
+ */
+#define NV_VMODE	0x140f
+#define NV_CMODE	0x1410
+
+/*
+ * Horizontal and vertical resolution information.
+ */
+extern struct vmode_attr {
+	int	hres;
+	int	vres;
+	int	vfreq;
+	int	interlaced;
+} vmode_attrs[VMODE_MAX];
+
+
+/*
+ * Horizontal and vertical resolution for each mode.
+ */
+static struct vmode_attr vmode_attrs[VMODE_MAX] = {
+    {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}
+};
+
+
+/*
+ * 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" */
+	{-1,	VMODE_640_480_60},	/* catch-all, must be last */
+};
+
+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;
+}
+
+struct aty_cmap_regs {
+    unsigned char windex;
+    unsigned char lut;
+    unsigned char mask;
+    unsigned char rindex;
+    unsigned char cntl;
+};
+
+typedef struct aty_regvals {
+    int offset[3];		/* first pixel address */
+
+    int crtc_h_sync_strt_wid[3];	/* depth dependant */
+    int crtc_gen_cntl[3];
+    int mem_cntl[3];
+
+    int crtc_h_tot_disp;	/* mode dependant */
+    int crtc_v_tot_disp;
+    int crtc_v_sync_strt_wid;
+    int crtc_off_pitch;
+
+    unsigned char clock_val[2];	/* vals for 20 and 21 */
+} aty_regvals;
+
+struct rage_regvals {
+    int h_total, h_sync_start, h_sync_width;
+    int v_total, v_sync_start, v_sync_width;
+    int h_sync_neg, v_sync_neg;
+};
+
+static int aty_vram_reqd(const struct atyfb_par *par);
+static struct aty_regvals *get_aty_struct(int vmode);
+
+static unsigned long frame_buffer;
+
+static int total_vram;		/* total amount of video memory, bytes */
+static int chip_type;		/* what chip type was detected */
+
+static unsigned long ati_regbase;
+static struct aty_cmap_regs *aty_cmap_regs;
+
+#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
+};
+
+    /*
+     *  Interface used by the world
+     */
+
+unsigned long atyfb_init(unsigned long mem_start);
+void atyfb_setup(char *options, int *ints);
+
+static int atyfb_open(struct fb_info *info);
+static int atyfb_release(struct fb_info *info);
+static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info);
+static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info);
+static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info);
+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,
+			  struct fb_info *info);
+static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info);
+
+
+    /*
+     *  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);
+
+
+    /*
+     *  Text console acceleration
+     */
+
+#ifdef CONFIG_FBCON_CFB8
+static struct display_switch fbcon_aty8;
+#endif
+
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+extern struct vc_mode display_info;
+extern struct fb_info *console_fb_info;
+extern int (*console_setmode_ptr)(struct vc_mode *, int);
+extern int (*console_set_cmap_ptr)(struct fb_cmap *, int, int,
+				   struct fb_info *);
+static int atyfb_console_setmode(struct vc_mode *, int);
+#endif
+
+
+    /*
+     *  Internal routines
+     */
+
+static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			 u_int *transp, struct fb_info *info);
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			 u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
+
+
+static struct fb_ops atyfb_ops = {
+    atyfb_open, atyfb_release, atyfb_get_fix, atyfb_get_var, atyfb_set_var,
+    atyfb_get_cmap, atyfb_set_cmap, atyfb_pan_display, NULL, atyfb_ioctl
+};
+
+
+static inline int aty_vram_reqd(const struct atyfb_par *par)
+{
+    return (par->vxres*par->vyres) << par->cmode;
+}
+
+extern inline unsigned aty_ld_le32(volatile unsigned long addr)
+{
+    register unsigned long temp = ati_regbase,val;
+
+    asm("lwbrx %0,%1,%2": "=r"(val):"r"(addr), "r"(temp));
+    return val;
+}
+
+extern inline void aty_st_le32(volatile unsigned long addr, unsigned val)
+{
+    register unsigned long temp = ati_regbase;
+
+    asm("stwbrx %0,%1,%2": : "r"(val), "r"(addr), "r"(temp):"memory");
+}
+
+extern inline unsigned char aty_ld_8(volatile unsigned long addr)
+{
+    return *(char *) ((long) addr + (long) ati_regbase);
+}
+
+extern inline void aty_st_8(volatile unsigned long addr, unsigned char val)
+{
+    *(unsigned char *) (addr + (unsigned long) ati_regbase) = val;
+}
+
+static void aty_st_514(int offset, char val)
+{
+    aty_WaitQueue(5);
+    aty_st_8(DAC_CNTL, 1);
+    aty_st_8(DAC_W_INDEX, offset & 0xff);	/* right addr byte */
+    aty_st_8(DAC_DATA, (offset >> 8) & 0xff);	/* left addr byte */
+    eieio();
+    aty_st_8(DAC_MASK, val);
+    eieio();
+    aty_st_8(DAC_CNTL, 0);
+}
+
+static void aty_st_pll(int offset, char val)
+{
+    aty_WaitQueue(3);
+    aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN);	/* write addr byte */
+    eieio();
+    aty_st_8(CLOCK_CNTL + 2, val);	/* write the register value */
+    eieio();
+    aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN);
+}
+
+static struct aty_regvals *get_aty_struct(int vmode)
+{
+    int v = vmode - 1;
+
+    switch (chip_type) {
+	case MACH64_GT_ID:
+	    return aty_gt_reg_init[v];
+	    break;
+	case MACH64_VT_ID:
+	    return aty_vt_reg_init[v];
+	    break;
+	default: /* default to MACH64_GX_ID */
+	    return aty_gx_reg_init[v];
+	    break;
+    }
+}
+
+static int read_aty_sense(void)
+{
+    int sense, i;
+
+    aty_st_le32(GP_IO, 0x31003100);	/* drive outputs high */
+    __delay(200);
+    aty_st_le32(GP_IO, 0);		/* turn off outputs */
+    __delay(2000);
+    i = aty_ld_le32(GP_IO);		/* get primary sense value */
+    sense = ((i & 0x3000) >> 3) | (i & 0x100);
+
+    /* drive each sense line low in turn and collect the other 2 */
+    aty_st_le32(GP_IO, 0x20000000);	/* drive A low */
+    __delay(2000);
+    i = aty_ld_le32(GP_IO);
+    sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
+    aty_st_le32(GP_IO, 0x20002000);	/* drive A high again */
+    __delay(200);
+
+    aty_st_le32(GP_IO, 0x10000000);	/* drive B low */
+    __delay(2000);
+    i = aty_ld_le32(GP_IO);
+    sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
+    aty_st_le32(GP_IO, 0x10001000);	/* drive B high again */
+    __delay(200);
+
+    aty_st_le32(GP_IO, 0x01000000);	/* drive C low */
+    __delay(2000);
+    sense |= (aty_ld_le32(GP_IO) & 0x3000) >> 12;
+    aty_st_le32(GP_IO, 0);		/* turn off outputs */
+
+    return sense;
+}
+
+static void RGB514_Program(int cmode)
+{
+    typedef struct {
+	char pixel_dly;
+	char misc2_cntl;
+	char pixel_rep;
+	char pixel_cntl_index;
+	char 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);
+    aty_st_514(0x04, pDacProgTab->pixel_dly);
+    aty_st_514(0x05, 0x00);
+
+    aty_st_514(0x2, 0x1);
+    aty_st_514(0x71, pDacProgTab->misc2_cntl);
+    aty_st_514(0x0a, pDacProgTab->pixel_rep);
+
+    aty_st_514(pDacProgTab->pixel_cntl_index, pDacProgTab->pixel_cntl_v1);
+}
+
+static void set_off_pitch(const struct atyfb_par *par)
+{
+    u32 pitch, offset;
+
+    pitch = par->vxres>>3;
+    offset = ((par->yoffset*par->vxres+par->xoffset)>>3)<<par->cmode;
+    aty_st_le32(CRTC_OFF_PITCH, pitch<<22 | offset);
+    if (chip_type == MACH64_GT_ID) {
+	/* Is this OK for other chips? */
+	aty_st_le32(DST_OFF_PITCH, pitch<<22 | offset);
+	aty_st_le32(SRC_OFF_PITCH, pitch<<22 | offset);
+    }
+}
+
+static void atyfb_set_par(struct atyfb_par *par)
+{
+    int i, hres;
+    struct aty_regvals *init = get_aty_struct(par->vmode);
+    int vram_type = aty_ld_le32(CONFIG_STAT0) & 7;
+
+    if (init == 0)	/* paranoia, shouldn't get here */
+	panic("aty: display mode %d not supported", par->vmode);
+
+    current_par = *par;
+    hres = vmode_attrs[par->vmode-1].hres;
+
+    /* clear FIFO errors */
+    aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_HOST_ERR_ACK
+			  | BUS_FIFO_ERR_ACK);
+
+    /* Reset engine */
+    i = aty_ld_le32(GEN_TEST_CNTL);
+    aty_st_le32(GEN_TEST_CNTL, i & ~GUI_ENGINE_ENABLE);
+    eieio();
+    aty_WaitIdleEmpty();
+    aty_st_le32(GEN_TEST_CNTL, i | GUI_ENGINE_ENABLE);
+    aty_WaitIdleEmpty();
+
+    if ( chip_type != MACH64_GT_ID ) {
+	i = aty_ld_le32(CRTC_GEN_CNTL);
+	aty_st_le32(CRTC_GEN_CNTL, i | CRTC_EXT_DISP_EN);
+    }
+
+    if ( chip_type == MACH64_GX_ID ) {
+	i = aty_ld_le32(GEN_TEST_CNTL);
+	aty_st_le32(GEN_TEST_CNTL, i | GEN_OVR_OUTPUT_EN );
+    }
+
+    switch (chip_type) {
+	case MACH64_VT_ID:
+	    aty_st_pll(PLL_MACRO_CNTL, 0xb5);
+	    aty_st_pll(PLL_REF_DIV, 0x2d);
+	    aty_st_pll(PLL_GEN_CNTL, 0x14);
+	    aty_st_pll(MCLK_FB_DIV, 0xbd);
+	    aty_st_pll(PLL_VCLK_CNTL, 0x0b);
+	    aty_st_pll(VCLK_POST_DIV, init->clock_val[0]);
+	    aty_st_pll(VCLK0_FB_DIV, init->clock_val[1]);
+	    aty_st_pll(VCLK1_FB_DIV, 0xd6);
+	    aty_st_pll(VCLK2_FB_DIV, 0xee);
+	    aty_st_pll(VCLK3_FB_DIV, 0xf8);
+	    aty_st_pll(PLL_XCLK_CNTL, 0x0);
+	    aty_st_pll(PLL_TEST_CTRL, 0x0);
+	    aty_st_pll(PLL_TEST_COUNT, 0x0);
+	    break;
+	case MACH64_GT_ID:
+	    if (vram_type == 5) {
+		aty_st_pll(0, 0xcd);
+		aty_st_pll(PLL_MACRO_CNTL,
+			   par->vmode >= VMODE_1024_768_60 ? 0xd3: 0xd5);
+		aty_st_pll(PLL_REF_DIV, 0x21);
+		aty_st_pll(PLL_GEN_CNTL, 0x44);
+		aty_st_pll(MCLK_FB_DIV, 0xe8);
+		aty_st_pll(PLL_VCLK_CNTL, 0x03);
+		aty_st_pll(VCLK_POST_DIV, init->offset[0]);
+		aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
+		aty_st_pll(VCLK1_FB_DIV, 0x8e);
+		aty_st_pll(VCLK2_FB_DIV, 0x9e);
+		aty_st_pll(VCLK3_FB_DIV, 0xc6);
+		aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
+		aty_st_pll(12, 0xa6);
+		aty_st_pll(13, 0x1b);
+	    } else {
+		aty_st_pll(PLL_MACRO_CNTL, 0xd5);
+		aty_st_pll(PLL_REF_DIV, 0x21);
+		aty_st_pll(PLL_GEN_CNTL, 0xc4);
+		aty_st_pll(MCLK_FB_DIV, 0xda);
+		aty_st_pll(PLL_VCLK_CNTL, 0x03);
+		/* offset actually holds clock values */
+		aty_st_pll(VCLK_POST_DIV, init->offset[0]);
+		aty_st_pll(VCLK0_FB_DIV, init->offset[1]);
+		aty_st_pll(VCLK1_FB_DIV, 0x8e);
+		aty_st_pll(VCLK2_FB_DIV, 0x9e);
+		aty_st_pll(VCLK3_FB_DIV, 0xc6);
+		aty_st_pll(PLL_TEST_CTRL, 0x0);
+		aty_st_pll(PLL_XCLK_CNTL, init->offset[2]);
+		aty_st_pll(12, 0xa0);
+		aty_st_pll(13, 0x1b);
+	    }
+	    break;
+	default:
+	    RGB514_Program(par->cmode);
+	    aty_WaitIdleEmpty();
+	    aty_st_514(0x06, 0x02);
+	    aty_st_514(0x10, 0x01);
+	    aty_st_514(0x70, 0x01);
+	    aty_st_514(0x8f, 0x1f);
+	    aty_st_514(0x03, 0x00);
+	    aty_st_514(0x05, 0x00);
+	    aty_st_514(0x20, init->clock_val[0]);
+	    aty_st_514(0x21, init->clock_val[1]);
+	    break;
+    }
+
+    aty_ld_8(DAC_REGS);	/* clear counter */
+    aty_WaitIdleEmpty();
+
+    aty_st_le32(CRTC_H_TOTAL_DISP, init->crtc_h_tot_disp);
+    aty_st_le32(CRTC_H_SYNC_STRT_WID, init->crtc_h_sync_strt_wid[par->cmode]);
+    aty_st_le32(CRTC_V_TOTAL_DISP, init->crtc_v_tot_disp);
+    aty_st_le32(CRTC_V_SYNC_STRT_WID, init->crtc_v_sync_strt_wid);
+
+    aty_st_8(CLOCK_CNTL, 0);
+    aty_st_8(CLOCK_CNTL, CLOCK_STROBE);
+
+    aty_st_le32(CRTC_VLINE_CRNT_VLINE, 0);
+
+    set_off_pitch(par);
+
+    if (chip_type == MACH64_GT_ID) {
+	aty_st_le32(BUS_CNTL, 0x7b23a040);
+
+	/* need to set DSP values !! assume sdram */
+	i = init->crtc_gen_cntl[0] - (0x100000 * par->cmode);
+	if ( vram_type == 5 )
+	    i = init->crtc_gen_cntl[1] - (0x100000 * par->cmode);
+	aty_st_le32(DSP_CONFIG, i);
+
+	i = aty_ld_le32(MEM_CNTL) & MEM_SIZE_ALIAS;
+	if ( vram_type == 5 ) {
+	    i |= ((1 * par->cmode) << 26) | 0x4215b0;
+	    aty_st_le32(DSP_ON_OFF,sgram_dsp[par->vmode-1][par->cmode]);
+
+	//aty_st_le32(CLOCK_CNTL,8192);
+	} else {
+	    i |= ((1 * par->cmode) << 26) | 0x300090;
+	    aty_st_le32(DSP_ON_OFF, init->mem_cntl[par->cmode]);
+	}
+
+	aty_st_le32(MEM_CNTL, i);
+	aty_st_le32(EXT_MEM_CNTL, 0x5000001);
+
+	/* if (total_vram > 0x400000)	
+	    i |= 0x538; this not been verified on > 4Megs!! */
+    } else {
+
+/* The magic constant below translates into:
+* 5   = No RDY delay, 1 wait st for mem write, increment during burst transfer
+* 9   = DAC access delayed, 1 wait state for DAC
+* 0   = Disables interupts for FIFO errors
+* e   = Allows FIFO to generate 14 wait states before generating error
+* 1   = DAC snooping disabled, ROM disabled
+* 0   = ROM page at 0 (disabled so doesn't matter)
+* f   = 15 ROM wait states (disabled so doesn't matter)
+* f   = 15 BUS wait states (I'm not sure this applies to PCI bus types)
+* at some point it would be good to experiment with bench marks to see if
+* we can gain some speed by fooling with the wait states etc.
+*/
+	if (chip_type == MACH64_VT_ID)
+	    aty_st_le32(BUS_CNTL, 0x680000f9);
+	else
+	    aty_st_le32(BUS_CNTL, 0x590e10ff);
+
+	switch (total_vram) {
+	    case 0x00100000:
+		aty_st_le32(MEM_CNTL, vt_mem_cntl[0][par->cmode]);
+		break;
+	    case 0x00200000:
+		aty_st_le32(MEM_CNTL, vt_mem_cntl[1][par->cmode]);
+		break;
+	    case 0x00400000:
+		aty_st_le32(MEM_CNTL, vt_mem_cntl[2][par->cmode]);
+		break;
+	    default:
+		i = aty_ld_le32(MEM_CNTL) & 0x000F;
+		aty_st_le32(MEM_CNTL,
+			    (init->mem_cntl[par->cmode] & 0xFFFFFFF0) | i);
+	}
+    }
+/* These magic constants are harder to figure out
+* on the vt chipset bit 2 set makes the screen brighter
+* and bit 15 makes the screen black! But nothing else
+* seems to matter for the vt DAC_CNTL
+*/
+    switch (chip_type) {
+	case MACH64_GT_ID:
+	    i = 0x86010102;
+	    break;
+	case MACH64_VT_ID:
+	    i = 0x87010184;
+	    break;
+	default:
+	    i = 0x47012100;
+	    break;
+    }
+
+    aty_st_le32(DAC_CNTL, i);
+    aty_st_8(DAC_MASK, 0xff);
+
+    switch (par->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:
+	default:
+	    i = CRTC_PIX_WIDTH_8BPP; break;
+    }
+
+    if (chip_type != MACH64_GT_ID) {
+	aty_st_le32(CRTC_INT_CNTL, 0x00000002);
+	aty_st_le32(GEN_TEST_CNTL, GUI_ENGINE_ENABLE | BLOCK_WRITE_ENABLE);	/* gui_en block_en */
+	i |= init->crtc_gen_cntl[par->cmode];
+    }
+    /* Gentlemen, start your crtc engine */
+    aty_st_le32(CRTC_GEN_CNTL, CRTC_EXT_DISP_EN | CRTC_ENABLE | i);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+    display_info.height = vmode_attrs[par->vmode-1].vres;
+    display_info.width = vmode_attrs[par->vmode-1].hres;
+    display_info.depth = 8<<par->cmode;
+    display_info.pitch = par->vxres<<par->cmode;
+    display_info.mode = par->vmode;
+    strcpy(display_info.name, atyfb_name);
+    display_info.fb_address =
+	iopa(((chip_type != MACH64_GT_ID) ?
+	     frame_buffer + init->offset[par->cmode] : frame_buffer));
+    display_info.cmap_adr_address = iopa((unsigned long)&aty_cmap_regs->windex);
+    display_info.cmap_data_address = iopa((unsigned long)&aty_cmap_regs->lut);
+    display_info.disp_reg_address = iopa(ati_regbase);
+#endif /* CONFIG_FB_COMPAT_XPMAC) */
+}
+
+
+    /*
+     *  Open/Release the frame buffer device
+     */
+
+static int atyfb_open(struct fb_info *info)
+
+{
+    /*
+     *  Nothing, only a usage count for the moment
+     */
+
+    MOD_INC_USE_COUNT;
+    return(0);
+}
+
+static int atyfb_release(struct fb_info *info)
+{
+    MOD_DEC_USE_COUNT;
+    return(0);
+}
+
+
+static int encode_fix(struct fb_fix_screeninfo *fix,
+		      const struct atyfb_par *par)
+{
+    struct aty_regvals *init;
+
+    memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+
+    strcpy(fix->id, atyfb_name);
+    init = get_aty_struct(par->vmode);
+    /*
+     *  FIXME: This will cause problems on non-GT chips, because the frame
+     *  buffer must be aligned to a page
+     */
+    fix->smem_start = (char *)((chip_type != MACH64_GT_ID)
+	    ? frame_buffer + init->offset[par->cmode] : frame_buffer);
+    fix->smem_len = (u32)total_vram;
+    if (fix->smem_len > 0x7ff000)
+	fix->smem_len = 0x7ff000;	/* last page is MMIO */
+    fix->mmio_start = (char *)(ati_regbase & ~0xfff);
+    fix->mmio_len = 4096;
+    fix->type = FB_TYPE_PACKED_PIXELS;
+    fix->type_aux = 0;
+    fix->line_length = par->vxres<<par->cmode;
+    fix->visual = par->cmode == CMODE_8 ? FB_VISUAL_PSEUDOCOLOR
+					: FB_VISUAL_TRUECOLOR;
+    fix->ywrapstep = 0;
+    fix->xpanstep = 8;
+    fix->ypanstep = 1;
+
+    return 0;
+}
+
+
+static int decode_var(struct fb_var_screeninfo *var,
+		      struct atyfb_par *par)
+{
+    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->vmode = VMODE_512_384_60;		/* 512x384, 60Hz */
+    else if (xres <= 640 && yres <= 480)
+	par->vmode = VMODE_640_480_67;		/* 640x480, 67Hz */
+    else if (xres <= 640 && yres <= 870)
+	par->vmode = VMODE_640_870_75P;		/* 640x870, 75Hz (portrait) */
+    else if (xres <= 768 && yres <= 576)
+	par->vmode = VMODE_768_576_50I;		/* 768x576, 50Hz (PAL full frame) */
+    else if (xres <= 800 && yres <= 600)
+	par->vmode = VMODE_800_600_75;		/* 800x600, 75Hz */
+    else if (xres <= 832 && yres <= 624)
+	par->vmode = VMODE_832_624_75;		/* 832x624, 75Hz */
+    else if (xres <= 1024 && yres <= 768)
+	par->vmode = VMODE_1024_768_75;		/* 1024x768, 75Hz */
+    else if (xres <= 1152 && yres <= 870)
+	par->vmode = VMODE_1152_870_75;		/* 1152x870, 75Hz */
+    else if (xres <= 1280 && yres <= 960)
+	par->vmode = VMODE_1280_960_75;		/* 1280x960, 75Hz */
+    else if (xres <= 1280 && yres <= 1024)
+	par->vmode = VMODE_1280_1024_75;	/* 1280x1024, 75Hz */
+    else
+	return -EINVAL;
+
+    xres = vmode_attrs[par->vmode-1].hres;
+    yres = vmode_attrs[par->vmode-1].vres;
+
+    if (var->xres_virtual <= xres)
+	par->vxres = xres;
+    else
+	par->vxres = (var->xres_virtual+7) & ~7;
+    if (var->yres_virtual <= yres)
+	par->vyres = yres;
+    else
+	par->vyres = var->yres_virtual;
+
+    par->xoffset = (var->xoffset+7) & ~7;
+    par->yoffset = var->yoffset;
+    if (par->xoffset+xres > par->vxres || par->yoffset+yres > par->vyres)
+	return -EINVAL;
+
+    if (bpp <= 8)
+	par->cmode = CMODE_8;
+    else if (bpp <= 16)
+	par->cmode = CMODE_16;
+    else if (bpp <= 32)
+	par->cmode = CMODE_32;
+    else
+	return -EINVAL;
+
+    if (aty_vram_reqd(par) > total_vram)
+	return -EINVAL;
+
+    /* Check if we know about the wanted video mode */
+    init = get_aty_struct(par->vmode);
+    if (init == NULL || init->crtc_h_sync_strt_wid[par->cmode] == 0 ||
+	(chip_type != MACH64_GT_ID &&
+	 init->crtc_gen_cntl[par->cmode] == 0) ||
+	(chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5 &&
+	 init->crtc_gen_cntl[1] == 0))
+	return -EINVAL;
+
+#if 0
+    if (!fbmon_valid_timings(pixclock, htotal, vtotal, info))
+	return -EINVAL;
+#endif
+
+    return 0;
+}
+
+static int encode_var(struct fb_var_screeninfo *var,
+		      const struct atyfb_par *par)
+{
+    memset(var, 0, sizeof(struct fb_var_screeninfo));
+
+    var->xres = vmode_attrs[par->vmode-1].hres;
+    var->yres = vmode_attrs[par->vmode-1].vres;
+    var->xres_virtual = par->vxres;
+    var->yres_virtual = par->vyres;
+    var->xoffset = par->xoffset;
+    var->yoffset = par->yoffset;
+    var->grayscale = 0;
+    switch (par->cmode) {
+	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;
+    var->height = -1;
+    var->width = -1;
+    var->accel = /* FB_ACCEL_ATY */ 0;
+    var->vmode = FB_VMODE_NONINTERLACED;
+    var->left_margin = var->right_margin = 64;	/* guesses */
+    var->upper_margin = var->lower_margin = 32;
+    var->hsync_len = 64;
+    var->vsync_len = 2;
+
+    /* no long long support in the kernel :-( */
+    /* this splittig trick will work if xres > 232 */
+    var->pixclock = 1000000000/
+	(var->left_margin+var->xres+var->right_margin+var->hsync_len);
+    var->pixclock *= 1000;
+    var->pixclock /= vmode_attrs[par->vmode-1].vfreq*
+	 (var->upper_margin+var->yres+var->lower_margin+var->vsync_len);
+    var->sync = 0;
+
+    return 0;
+}
+
+
+static void init_par(struct atyfb_par *par, int vmode, int cmode)
+{
+    par->vmode = vmode;
+    par->cmode = cmode;
+    par->vxres = vmode_attrs[vmode-1].hres;
+    par->vyres = vmode_attrs[vmode-1].vres;
+    par->xoffset = 0;
+    par->yoffset = 0;
+}
+
+
+    /*
+     *  Get the Fixed Part of the Display
+     */
+
+static int atyfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+			 struct fb_info *info)
+{
+    struct atyfb_par par;
+
+    if (con == -1)
+	par = default_par;
+    else
+	decode_var(&fb_display[con].var, &par);
+    encode_fix(fix, &par);
+    return 0;
+}
+
+
+    /*
+     *  Get the User Defined Part of the Display
+     */
+
+static int atyfb_get_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
+{
+    if (con == -1)
+	encode_var(var, &default_par);
+    else
+	*var=fb_display[con].var;
+    return 0;
+}
+
+
+    /*
+     *  Set the User Defined Part of the Display
+     */
+
+static int atyfb_set_var(struct fb_var_screeninfo *var, int con,
+			 struct fb_info *info)
+{
+    struct atyfb_par par;
+    struct display *display;
+    int oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+    int err;
+    int activate = var->activate;
+
+    if (con >= 0)
+	display = &fb_display[con];
+    else
+	display = &fb_disp;	/* used during initialization */
+
+    if ((err = decode_var(var, &par)))
+	return err;
+
+    encode_var(var, &par);
+
+    if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+	oldxres = display->var.xres;
+	oldyres = display->var.yres;
+	oldvxres = display->var.xres_virtual;
+	oldvyres = display->var.yres_virtual;
+	oldbpp = display->var.bits_per_pixel;
+	display->var = *var;
+	if (oldxres != var->xres || oldyres != var->yres ||
+	    oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+	    oldbpp != var->bits_per_pixel) {
+	    struct fb_fix_screeninfo fix;
+
+	    encode_fix(&fix, &par);
+	    display->screen_base = (u_char *)fix.smem_start;
+	    display->visual = fix.visual;
+	    display->type = fix.type;
+	    display->type_aux = fix.type_aux;
+	    display->ypanstep = fix.ypanstep;
+	    display->ywrapstep = fix.ywrapstep;
+	    display->line_length = fix.line_length;
+	    display->can_soft_blank = 1;
+	    display->inverse = 0;
+	    switch (par.cmode) {
+		case CMODE_8:
+#if 1
+		    display->dispsw = &fbcon_cfb8;
+#else
+		    display->dispsw = &fbcon_aty8;
+#endif
+		    break;
+		case CMODE_16:
+		    display->dispsw = &fbcon_cfb16;
+		    break;
+		case CMODE_32:
+		    display->dispsw = &fbcon_cfb32;
+		    break;
+		default:
+		    display->dispsw = NULL;
+		    break;
+	    }
+	    if (fb_info.changevar)
+		(*fb_info.changevar)(con);
+	}
+	if (con == currcon)
+	    atyfb_set_par(&par);
+	if (oldbpp != var->bits_per_pixel) {
+	    if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+		return err;
+	    do_install_cmap(con, info);
+	}
+    }
+
+    return 0;
+}
+
+
+    /*
+     *  Pan or Wrap the Display
+     *
+     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+     */
+
+static int atyfb_pan_display(struct fb_var_screeninfo *var, int con,
+			     struct fb_info *info)
+{
+    u32 xres, yres, xoffset, yoffset;
+    struct atyfb_par *par = &current_par;
+
+    xres = vmode_attrs[par->vmode-1].hres;
+    yres = vmode_attrs[par->vmode-1].vres;
+    xoffset = (var->xoffset+7) & ~7;
+    yoffset = var->yoffset;
+    if (xoffset+xres > par->vxres || yoffset+yres > par->vyres)
+	return -EINVAL;
+    par->xoffset = xoffset;
+    par->yoffset = yoffset;
+    set_off_pitch(par);
+    return 0;
+}
+
+    /*
+     *  Get the Colormap
+     */
+
+static int atyfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
+{
+    if (con == currcon) /* current console? */
+	return fb_get_cmap(cmap, &fb_display[con].var, kspc, atyfb_getcolreg,
+			   info);
+    else if (fb_display[con].cmap.len) /* non default colormap? */
+	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+    else
+	fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+		     cmap, kspc ? 0 : 2);
+    return 0;
+}
+
+    /*
+     *  Set the Colormap
+     */
+
+static int atyfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+			  struct fb_info *info)
+{
+    int err;
+
+    if (!fb_display[con].cmap.len) {	/* no colormap allocated? */
+	if ((err = fb_alloc_cmap(&fb_display[con].cmap,
+				 1<<fb_display[con].var.bits_per_pixel, 0)))
+	    return err;
+    }
+    if (con == currcon)			/* current console? */
+	return fb_set_cmap(cmap, &fb_display[con].var, kspc, atyfb_setcolreg,
+			   info);
+    else
+	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+    return 0;
+}
+
+
+static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info)
+{
+    return -EINVAL;
+}
+
+
+    /*
+     *  Initialisation
+     */
+
+__initfunc(unsigned long atyfb_init(unsigned long mem_start))
+{
+#ifdef __powerpc__
+    /* We don't want to be called like this. */
+    /* We rely on Open Firmware (offb) instead. */
+    return mem_start;
+#else /* !__powerpc__ */
+    /* To be merged with Bernd's mach64fb */
+    return mem_start;
+#endif /* !__powerpc__ */
+}
+
+
+unsigned long atyfb_of_init(unsigned long mem_start, struct device_node *dp)
+{
+    int i, err, sense;
+    struct fb_var_screeninfo var;
+    struct aty_regvals *init;
+    unsigned long addr;
+    unsigned char bus, devfn;
+    unsigned short cmd;
+
+    if (dp->next)
+	printk("Warning: only using first ATI card detected\n");
+    if (dp->n_addrs != 1 && dp->n_addrs != 3)
+	printk("Warning: expecting 1 or 3 addresses for ATY (got %d)",
+	       dp->n_addrs);
+
+    ati_regbase = (int)ioremap((0x7ffc00 + dp->addrs[0].address), 0x1000);
+    aty_cmap_regs = (struct aty_cmap_regs *)(ati_regbase + 0xC0);
+
+    /* enable memory-space accesses using config-space command register */
+    if (pci_device_loc(dp, &bus, &devfn) == 0) {
+	pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
+	if (cmd != 0xffff) {
+	    cmd |= PCI_COMMAND_MEMORY;
+	    pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
+	}
+    }
+    chip_type = (aty_ld_le32(CONFIG_CHIP_ID) & CFG_CHIP_TYPE);
+
+    i = aty_ld_le32(MEM_CNTL);
+    if (chip_type != MACH64_GT_ID)
+	switch (i & MEM_SIZE_ALIAS) {
+	    case MEM_SIZE_512K:
+		total_vram = 0x80000;
+		break;
+	    case MEM_SIZE_1M:
+		total_vram = 0x100000;
+		break;
+	    case MEM_SIZE_2M:
+		total_vram = 0x200000;
+		break;
+	    case MEM_SIZE_4M:
+		total_vram = 0x400000;
+		break;
+	    case MEM_SIZE_6M:
+		total_vram = 0x600000;
+		break;
+	    case MEM_SIZE_8M:
+		total_vram = 0x800000;
+		break;
+	    default:
+		total_vram = 0x80000;
+	}
+    else
+	switch (i & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
+	    case MEM_SIZE_512K:
+		total_vram = 0x80000;
+		break;
+	    case MEM_SIZE_1M:
+		total_vram = 0x100000;
+		break;
+	    case MEM_SIZE_2M_GTB:
+		total_vram = 0x200000;
+		break;
+	    case MEM_SIZE_4M_GTB:
+		total_vram = 0x400000;
+		break;
+	    case MEM_SIZE_6M_GTB:
+		total_vram = 0x600000;
+		break;
+	    case MEM_SIZE_8M_GTB:
+		total_vram = 0x800000;
+		break;
+	    default:
+		total_vram = 0x80000;
+	}
+
+#if 1
+    printk("aty_display_init: node = %p, addrs = ", dp->node);
+    printk(" %x(%x)", dp->addrs[0].address, dp->addrs[0].size);
+    printk(", intrs =");
+    for (i = 0; i < dp->n_intrs; ++i)
+    printk(" %x", dp->intrs[i].line);
+    printk("\nregbase: %x pci loc: %x:%x total_vram: %x cregs: %p\n",
+	   (int)ati_regbase, bus, devfn, total_vram, aty_cmap_regs);
+#endif
+
+    /* Map in frame buffer */
+    addr = dp->addrs[0].address;
+
+    /* use the big-endian aperture (??) */
+    addr += 0x800000;
+    frame_buffer = (unsigned long)__ioremap(addr, 0x800000, _PAGE_WRITETHRU);
+
+    if (default_video_mode != -1) {
+	sense = read_aty_sense();
+	printk("monitor sense = %x\n", sense);
+	if (default_video_mode == VMODE_NVRAM) {
+	    default_video_mode = nvram_read_byte(NV_VMODE);
+	    init = get_aty_struct(default_video_mode);
+	    if (default_video_mode <= 0 ||
+		default_video_mode > VMODE_MAX || init == 0)
+		default_video_mode = VMODE_CHOOSE;
+	}
+	if (default_video_mode == VMODE_CHOOSE)
+	    default_video_mode = map_monitor_sense(sense);
+
+	init = get_aty_struct(default_video_mode);
+	if (!init)
+	    default_video_mode = VMODE_640_480_60;
+    }
+
+    /*
+     * Reduce the pixel size if we don't have enough VRAM.
+     */
+
+    if (default_color_mode == CMODE_NVRAM)
+	default_color_mode = nvram_read_byte(NV_CMODE);
+    if (default_color_mode < CMODE_8 ||
+	default_color_mode > CMODE_32)
+	default_color_mode = CMODE_8;
+
+    init_par(&default_par, default_video_mode, default_color_mode);
+    while (aty_vram_reqd(&default_par) > total_vram) {
+	while (default_color_mode > CMODE_8 &&
+	       aty_vram_reqd(&default_par) > total_vram) {
+	    --default_color_mode;
+	    init_par(&default_par, default_video_mode, default_color_mode);
+	}
+	/*
+	 * adjust the video mode smaller if there still is not enough VRAM
+	 */
+	if (aty_vram_reqd(&default_par) > total_vram)
+	    do {
+		default_video_mode--;
+		init_par(&default_par, default_video_mode, default_color_mode);
+		init = get_aty_struct(default_video_mode);
+	    } while ((init == 0) &&
+		     (default_video_mode > VMODE_640_480_60));
+    }
+
+    if (chip_type == MACH64_GT_ID && (aty_ld_le32(CONFIG_STAT0) & 7) == 5
+	&& init->crtc_gen_cntl[1] == 0) {
+	    default_video_mode = VMODE_640_480_67;
+	    default_color_mode = CMODE_8;
+	    init_par(&default_par, default_video_mode, default_color_mode);
+    }
+
+    switch (chip_type) {
+	case MACH64_GX_ID:
+	    strcat(atyfb_name, "GX");
+	    break;
+	case MACH64_VT_ID:
+	    strcat(atyfb_name, "VT");
+	    break;
+	case MACH64_GT_ID:
+	    strcat(atyfb_name, "GT");
+	    break;
+	default:
+	    break;
+    }
+    strcpy(fb_info.modename, atyfb_name);
+    fb_info.node = -1;
+    fb_info.fbops = &atyfb_ops;
+    fb_info.disp = &fb_disp;
+    fb_info.fontname[0] = '\0';
+    fb_info.changevar = NULL;
+    fb_info.switch_con = &atyfbcon_switch;
+    fb_info.updatevar = &atyfbcon_updatevar;
+    fb_info.blank = &atyfbcon_blank;
+
+    err = register_framebuffer(&fb_info);
+    if (err < 0)
+	return mem_start;
+
+    for (i = 0; i < 16; i++) {
+	int j = color_table[i];
+	palette[i].red = default_red[j];
+	palette[i].green = default_grn[j];
+	palette[i].blue = default_blu[j];
+    }
+    atyfb_set_par(&default_par);
+    encode_var(&var, &default_par);
+    atyfb_set_var(&var, -1, &fb_info);
+
+    printk("fb%d: %s frame buffer device on %s\n", GET_FB_IDX(fb_info.node),
+	   atyfb_name, dp->full_name);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+    if (!console_fb_info) {
+	console_fb_info = &fb_info;
+	console_setmode_ptr = atyfb_console_setmode;
+	console_set_cmap_ptr = atyfb_set_cmap;
+    }
+#endif /* CONFIG_FB_COMPAT_XPMAC) */
+
+    return mem_start;
+}
+
+
+/* XXX: doesn't work yet */
+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, "vmode:", 6)) {
+	    vmode = simple_strtoul(this_opt+6, NULL, 0);
+	    if (vmode > 0 && vmode <= VMODE_MAX)
+		default_video_mode = vmode;
+	} else if (!strncmp(this_opt, "cmode:", 6)) {
+	    depth = simple_strtoul(this_opt+6, NULL, 0);
+	    switch (depth) {
+		case 8:
+		    default_color_mode = CMODE_8;
+		    break;
+		case 15:
+		case 16:
+		    default_color_mode = CMODE_16;
+		    break;
+		case 24:
+		case 32:
+		    default_color_mode = CMODE_32;
+		    break;
+	    };
+	}
+    }
+}
+
+
+static int atyfbcon_switch(int con, struct fb_info *info)
+{
+    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);
+    currcon = con;
+    decode_var(&fb_display[con].var, &par);
+    atyfb_set_par(&par);
+    /* Install new colormap */
+    do_install_cmap(con, info);
+    return 0;
+}
+
+    /*
+     *  Update the `var' structure (called by fbcon.c)
+     */
+
+static int atyfbcon_updatevar(int con, struct fb_info *info)
+{
+    current_par.yoffset = fb_display[con].var.yoffset;
+    set_off_pitch(&current_par);
+    return 0;
+}
+
+    /*
+     *  Blank the display.
+     */
+
+static void atyfbcon_blank(int blank, struct fb_info *info)
+{
+    char gen_cntl;
+
+    gen_cntl = aty_ld_8(CRTC_GEN_CNTL);
+    if (blank & VESA_VSYNC_SUSPEND)
+	    gen_cntl |= 0x8;
+    if (blank & VESA_HSYNC_SUSPEND)
+	    gen_cntl |= 0x4;
+    if ((blank & VESA_POWERDOWN) == VESA_POWERDOWN)
+	    gen_cntl |= 0x40;
+    if (blank == VESA_NO_BLANKING)
+	    gen_cntl &= ~(0x4c);
+    aty_st_8(CRTC_GEN_CNTL, gen_cntl);
+}
+
+
+    /*
+     *  Read a single color register and split it into
+     *  colors/transparent. Return != 0 for invalid regno.
+     */
+
+static int atyfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+			 u_int *transp, struct fb_info *info)
+{
+    if (regno > 255)
+	return 1;
+    *red = palette[regno].red;
+    *green = palette[regno].green;
+    *blue = palette[regno].blue;
+    return 0;
+}
+
+
+    /*
+     *  Set a single color register. The values supplied are already
+     *  rounded down to the hardware's capabilities (according to the
+     *  entries in the var structure). Return != 0 for invalid regno.
+     */
+
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			 u_int transp, struct fb_info *info)
+{
+    int i, scale;
+
+    if (regno > 255)
+	return 1;
+    palette[regno].red = red;
+    palette[regno].green = green;
+    palette[regno].blue = blue;
+    aty_WaitQueue(2);
+    i = aty_ld_8(DAC_CNTL) & 0xfc;
+    if (chip_type == MACH64_GT_ID)
+	    i |= 0x2;	/*DAC_CNTL|0x2 turns off the extra brightness for gt*/
+    aty_st_8(DAC_CNTL, i);
+    aty_st_8(DAC_REGS + DAC_MASK, 0xff);
+    eieio();
+    scale = ((chip_type != MACH64_GX_ID) &&
+	     (current_par.cmode == CMODE_16)) ? 3 : 0;
+    aty_WaitQueue(4);
+    aty_cmap_regs->windex = regno << scale;
+    eieio();
+    aty_cmap_regs->lut = red << scale;
+    eieio();
+    aty_cmap_regs->lut = green << scale;
+    eieio();
+    aty_cmap_regs->lut = blue << scale;
+    eieio();
+    if (regno < 16) {
+#ifdef CONFIG_FBCON_CFB16
+	fbcon_cfb16_cmap[regno] = (regno << 10) | (regno << 5) | regno;
+#endif
+#ifdef CONFIG_FBCON_CFB32
+	fbcon_cfb32_cmap[regno] = (regno << 24) | (regno << 16) |
+				  (regno << 8) | regno;
+#endif
+    }
+    return 0;
+}
+
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+    if (con != currcon)
+	return;
+    if (fb_display[con].cmap.len)
+	fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+		    atyfb_setcolreg, info);
+    else
+	fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+				    &fb_display[con].var, 1, atyfb_setcolreg,
+		    info);
+}
+
+
+    /*
+     *  Accelerated functions
+     */
+
+void aty_waitblit(void)
+{
+    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+}
+
+void aty_rectcopy(int srcx, int srcy, int dstx, int dsty, u_int width,
+		  u_int height)
+{
+    u_int direction = 0;
+
+    if (srcy < dsty) {
+	dsty += height - 1;
+	srcy += height - 1;
+    } else
+	direction |= DST_Y_TOP_TO_BOTTOM;
+
+    if (srcx < dstx) {
+	dstx += width - 1;
+	srcx += width - 1;
+    } else
+	direction |= DST_X_LEFT_TO_RIGHT;
+
+    aty_WaitQueue(4);
+    aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ );
+    aty_st_le32(DP_MIX, (MIX_SRC << 16) |  MIX_DST);
+    aty_st_le32(DP_SRC, FRGD_SRC_BLIT);
+
+    aty_WaitQueue(5);
+    aty_st_le32(SRC_Y_X, (srcx << 16) | (srcy & 0x0000ffff));
+    aty_st_le32(SRC_WIDTH1, width);
+    aty_st_le32(DST_CNTL, direction);
+    aty_st_le32(DST_Y_X, (dstx << 16) | (dsty & 0x0000ffff));
+    aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | (height & 0x0000ffff));
+
+    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+
+    /*
+     * Make sure that the destination trajectory is correctly set
+     * for subsequent calls.  MACH64_BIT_BLT is the only function that
+     * currently changes the destination trajectory from L->R and T->B.
+     */
+    aty_st_le32(DST_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM));
+}
+
+void aty_rectfill(int dstx, int dsty, u_int width, u_int height, u_int color)
+{
+    if (!width || !height)
+	return;
+
+    aty_WaitQueue(5);
+    aty_st_le32(DP_FRGD_CLR, color /* pGC->fgPixel */ );
+    aty_st_le32(DP_WRITE_MSK, 0x000000FF /* pGC->planemask */ );
+    aty_st_le32(DP_MIX, (MIX_SRC << 16) | MIX_DST);
+    aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR);
+
+    aty_st_le32(DST_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
+
+    aty_WaitQueue(2);
+    aty_st_le32(DST_Y_X, (((u_int)dstx << 16) | ((u_int)dsty & 0x0000ffff)));
+    aty_st_le32(DST_HEIGHT_WIDTH, (((u_int)width << 16) | height));
+
+    aty_WaitIdleEmpty();	/* Make sure that all commands have finished */
+}
+
+
+    /*
+     *  Text console acceleration
+     */
+
+static void fbcon_aty8_bmove(struct display *p, int sy, int sx, int dy, int dx,
+			     int height, int width)
+{
+    sx *= p->fontwidth;
+    sy *= p->fontheight;
+    dx *= p->fontwidth;
+    dy *= p->fontheight;
+    width *= p->fontwidth;
+    height *= p->fontheight;
+
+    aty_rectcopy(sx, sy, dx, dy, width, height);
+}
+
+static void fbcon_aty8_clear(struct vc_data *conp, struct display *p, int sy,
+			     int sx, int height, int width)
+{
+    u32 bgx = attr_bgcol_ec(p, conp);
+    bgx |= (bgx << 8);
+    bgx |= (bgx << 16);
+
+    sx *= p->fontwidth;
+    sy *= p->fontheight;
+    width *= p->fontwidth;
+    height *= p->fontheight;
+
+    aty_rectfill(sx, sy, width, height, bgx);
+}
+
+static void fbcon_aty8_putc(struct vc_data *conp, struct display *p, int c,
+			    int yy, int xx)
+{
+    aty_waitblit();
+    fbcon_cfb8_putc(conp, p, c, yy, xx);
+}
+
+static void fbcon_aty8_putcs(struct vc_data *conp, struct display *p,
+			     const char *s, int count, int yy, int xx)
+{
+    aty_waitblit();
+    fbcon_cfb8_putcs(conp, p, s, count, yy, xx);
+}
+
+static struct display_switch fbcon_aty8 = {
+    fbcon_cfb8_setup, fbcon_aty8_bmove, fbcon_aty8_clear, fbcon_aty8_putc,
+    fbcon_aty8_putcs, fbcon_cfb8_revc
+};
+
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+
+    /*
+     *  Backward compatibility mode for Xpmac
+     */
+
+static int atyfb_console_setmode(struct vc_mode *mode, int doit)
+{
+    int err;
+    struct fb_var_screeninfo var;
+    struct atyfb_par par;
+    int vmode, cmode;
+
+    if (mode->mode <= 0 || mode->mode > VMODE_MAX )
+	return -EINVAL;
+    vmode = mode->mode;
+
+    switch (mode->depth) {
+	case 24:
+	case 32:
+	    cmode = CMODE_32;
+	    break;
+	case 16:
+	    cmode = CMODE_16;
+	    break;
+	case 8:
+	case 0:			/* (default) */
+	    cmode = CMODE_8;
+	    break;
+	default:
+	    return -EINVAL;
+    }
+    init_par(&par, vmode, cmode);
+    encode_var(&var, &par);
+    if ((err = decode_var(&var, &par)))
+	return err;
+    if (doit)
+	atyfb_set_var(&var, currcon, 0);
+    return 0;
+}
+
+#endif /* CONFIG_FB_COMPAT_XPMAC */

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