summaryrefslogtreecommitdiffstats
path: root/drivers/video/atyfb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/atyfb.c')
-rw-r--r--drivers/video/atyfb.c1685
1 files changed, 1685 insertions, 0 deletions
diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c
new file mode 100644
index 000000000..e1f8b3ef4
--- /dev/null
+++ b/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 */