diff options
Diffstat (limited to 'drivers/video/atyfb.c')
-rw-r--r-- | drivers/video/atyfb.c | 1685 |
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 = ¤t_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(¤t_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 */ |