diff options
Diffstat (limited to 'drivers/video/aty/aty128fb.c')
-rw-r--r-- | drivers/video/aty/aty128fb.c | 2485 |
1 files changed, 2485 insertions, 0 deletions
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c new file mode 100644 index 00000000000..8a4ba3bb987 --- /dev/null +++ b/drivers/video/aty/aty128fb.c @@ -0,0 +1,2485 @@ +/* $Id: aty128fb.c,v 1.1.1.1.36.1 1999/12/11 09:03:05 Exp $ + * linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128 + * + * Copyright (C) 1999-2003, Brad Douglas <brad@neruo.com> + * Copyright (C) 1999, Anthony Tong <atong@uiuc.edu> + * + * Ani Joshi / Jeff Garzik + * - Code cleanup + * + * Michel Danzer <michdaen@iiic.ethz.ch> + * - 15/16 bit cleanup + * - fix panning + * + * Benjamin Herrenschmidt + * - pmac-specific PM stuff + * - various fixes & cleanups + * + * Andreas Hundt <andi@convergence.de> + * - FB_ACTIVATE fixes + * + * Paul Mackerras <paulus@samba.org> + * - Convert to new framebuffer API, + * fix colormap setting at 16 bits/pixel (565) + * + * Paul Mundt + * - PCI hotplug + * + * Jon Smirl <jonsmirl@yahoo.com> + * - PCI ID update + * - replace ROM BIOS search + * + * Based off of Geert's atyfb.c and vfb.c. + * + * TODO: + * - monitor sensing (DDC) + * - virtual display + * - other platform support (only ppc/x86 supported) + * - hardware cursor support + * + * Please cc: your patches to brad@neruo.com. + */ + +/* + * A special note of gratitude to ATI's devrel for providing documentation, + * example code and hardware. Thanks Nitya. -atong and brad + */ + + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <asm/uaccess.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#include <linux/console.h> +#include <asm/io.h> + +#ifdef CONFIG_PPC_PMAC +#include <asm/pmac_feature.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> +#include "../macmodes.h" +#endif + +#ifdef CONFIG_PMAC_BACKLIGHT +#include <asm/backlight.h> +#endif + +#ifdef CONFIG_BOOTX_TEXT +#include <asm/btext.h> +#endif /* CONFIG_BOOTX_TEXT */ + +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif + +#include <video/aty128.h> + +/* Debug flag */ +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_DEBUG "aty128fb: %s " fmt, __FUNCTION__, ##args); +#else +#define DBG(fmt, args...) +#endif + +#ifndef CONFIG_PPC_PMAC +/* default mode */ +static struct fb_var_screeninfo default_var __initdata = { + /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ + 640, 480, 640, 480, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2, + 0, FB_VMODE_NONINTERLACED +}; + +#else /* CONFIG_PPC_PMAC */ +/* default to 1024x768 at 75Hz on PPC - this will work + * on the iMac, the usual 640x480 @ 60Hz doesn't. */ +static struct fb_var_screeninfo default_var = { + /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ + 1024, 768, 1024, 768, 0, 0, 8, 0, + {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, + 0, 0, -1, -1, 0, 12699, 160, 32, 28, 1, 96, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED +}; +#endif /* CONFIG_PPC_PMAC */ + +/* default modedb mode */ +/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */ +static struct fb_videomode defaultmode __initdata = { + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 39722, + .left_margin = 48, + .right_margin = 16, + .upper_margin = 33, + .lower_margin = 10, + .hsync_len = 96, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +/* Chip generations */ +enum { + rage_128, + rage_128_pci, + rage_128_pro, + rage_128_pro_pci, + rage_M3, + rage_M3_pci, + rage_M4, + rage_128_ultra, +}; + +/* Must match above enum */ +static const char *r128_family[] __devinitdata = { + "AGP", + "PCI", + "PRO AGP", + "PRO PCI", + "M3 AGP", + "M3 PCI", + "M4 AGP", + "Ultra AGP", +}; + +/* + * PCI driver prototypes + */ +static int aty128_probe(struct pci_dev *pdev, + const struct pci_device_id *ent); +static void aty128_remove(struct pci_dev *pdev); +static int aty128_pci_suspend(struct pci_dev *pdev, pm_message_t state); +static int aty128_pci_resume(struct pci_dev *pdev); +static int aty128_do_resume(struct pci_dev *pdev); + +/* supported Rage128 chipsets */ +static struct pci_device_id aty128_pci_tbl[] = { + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, aty128_pci_tbl); + +static struct pci_driver aty128fb_driver = { + .name = "aty128fb", + .id_table = aty128_pci_tbl, + .probe = aty128_probe, + .remove = __devexit_p(aty128_remove), + .suspend = aty128_pci_suspend, + .resume = aty128_pci_resume, +}; + +/* packed BIOS settings */ +#ifndef CONFIG_PPC +typedef struct { + u8 clock_chip_type; + u8 struct_size; + u8 accelerator_entry; + u8 VGA_entry; + u16 VGA_table_offset; + u16 POST_table_offset; + u16 XCLK; + u16 MCLK; + u8 num_PLL_blocks; + u8 size_PLL_blocks; + u16 PCLK_ref_freq; + u16 PCLK_ref_divider; + u32 PCLK_min_freq; + u32 PCLK_max_freq; + u16 MCLK_ref_freq; + u16 MCLK_ref_divider; + u32 MCLK_min_freq; + u32 MCLK_max_freq; + u16 XCLK_ref_freq; + u16 XCLK_ref_divider; + u32 XCLK_min_freq; + u32 XCLK_max_freq; +} __attribute__ ((packed)) PLL_BLOCK; +#endif /* !CONFIG_PPC */ + +/* onboard memory information */ +struct aty128_meminfo { + u8 ML; + u8 MB; + u8 Trcd; + u8 Trp; + u8 Twr; + u8 CL; + u8 Tr2w; + u8 LoopLatency; + u8 DspOn; + u8 Rloop; + const char *name; +}; + +/* various memory configurations */ +static const struct aty128_meminfo sdr_128 = + { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" }; +static const struct aty128_meminfo sdr_64 = + { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" }; +static const struct aty128_meminfo sdr_sgram = + { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" }; +static const struct aty128_meminfo ddr_sgram = + { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" }; + +static struct fb_fix_screeninfo aty128fb_fix __initdata = { + .id = "ATY Rage128", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 8, + .ypanstep = 1, + .mmio_len = 0x2000, + .accel = FB_ACCEL_ATI_RAGE128, +}; + +static char *mode_option __initdata = NULL; + +#ifdef CONFIG_PPC_PMAC +static int default_vmode __initdata = VMODE_1024_768_60; +static int default_cmode __initdata = CMODE_8; +#endif + +#ifdef CONFIG_PMAC_PBOOK +static int default_crt_on __initdata = 0; +static int default_lcd_on __initdata = 1; +#endif + +#ifdef CONFIG_MTRR +static int mtrr = 1; +#endif + +/* PLL constants */ +struct aty128_constants { + u32 ref_clk; + u32 ppll_min; + u32 ppll_max; + u32 ref_divider; + u32 xclk; + u32 fifo_width; + u32 fifo_depth; +}; + +struct aty128_crtc { + u32 gen_cntl; + u32 h_total, h_sync_strt_wid; + u32 v_total, v_sync_strt_wid; + u32 pitch; + u32 offset, offset_cntl; + u32 xoffset, yoffset; + u32 vxres, vyres; + u32 depth, bpp; +}; + +struct aty128_pll { + u32 post_divider; + u32 feedback_divider; + u32 vclk; +}; + +struct aty128_ddafifo { + u32 dda_config; + u32 dda_on_off; +}; + +/* register values for a specific mode */ +struct aty128fb_par { + struct aty128_crtc crtc; + struct aty128_pll pll; + struct aty128_ddafifo fifo_reg; + u32 accel_flags; + struct aty128_constants constants; /* PLL and others */ + void __iomem *regbase; /* remapped mmio */ + u32 vram_size; /* onboard video ram */ + int chip_gen; + const struct aty128_meminfo *mem; /* onboard mem info */ +#ifdef CONFIG_MTRR + struct { int vram; int vram_valid; } mtrr; +#endif + int blitter_may_be_busy; + int fifo_slots; /* free slots in FIFO (64 max) */ + + int pm_reg; + int crt_on, lcd_on; + struct pci_dev *pdev; + struct fb_info *next; + int asleep; + int lock_blank; + + u8 red[32]; /* see aty128fb_setcolreg */ + u8 green[64]; + u8 blue[32]; + u32 pseudo_palette[16]; /* used for TRUECOLOR */ +}; + + +#define round_div(n, d) ((n+(d/2))/d) + +static int aty128fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int aty128fb_set_par(struct fb_info *info); +static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); +static int aty128fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fb); +static int aty128fb_blank(int blank, struct fb_info *fb); +static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, struct fb_info *info); +static int aty128fb_sync(struct fb_info *info); + + /* + * Internal routines + */ + +static int aty128_encode_var(struct fb_var_screeninfo *var, + const struct aty128fb_par *par); +static int aty128_decode_var(struct fb_var_screeninfo *var, + struct aty128fb_par *par); +#if 0 +static void __init aty128_get_pllinfo(struct aty128fb_par *par, + void __iomem *bios); +static void __init __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par); +#endif +static void aty128_timings(struct aty128fb_par *par); +static void aty128_init_engine(struct aty128fb_par *par); +static void aty128_reset_engine(const struct aty128fb_par *par); +static void aty128_flush_pixel_cache(const struct aty128fb_par *par); +static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par); +static void wait_for_fifo(u16 entries, struct aty128fb_par *par); +static void wait_for_idle(struct aty128fb_par *par); +static u32 depth_to_dst(u32 depth); + +#define BIOS_IN8(v) (readb(bios + (v))) +#define BIOS_IN16(v) (readb(bios + (v)) | \ + (readb(bios + (v) + 1) << 8)) +#define BIOS_IN32(v) (readb(bios + (v)) | \ + (readb(bios + (v) + 1) << 8) | \ + (readb(bios + (v) + 2) << 16) | \ + (readb(bios + (v) + 3) << 24)) + + +static struct fb_ops aty128fb_ops = { + .owner = THIS_MODULE, + .fb_check_var = aty128fb_check_var, + .fb_set_par = aty128fb_set_par, + .fb_setcolreg = aty128fb_setcolreg, + .fb_pan_display = aty128fb_pan_display, + .fb_blank = aty128fb_blank, + .fb_ioctl = aty128fb_ioctl, + .fb_sync = aty128fb_sync, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = soft_cursor, +}; + +#ifdef CONFIG_PMAC_BACKLIGHT +static int aty128_set_backlight_enable(int on, int level, void* data); +static int aty128_set_backlight_level(int level, void* data); + +static struct backlight_controller aty128_backlight_controller = { + aty128_set_backlight_enable, + aty128_set_backlight_level +}; +#endif /* CONFIG_PMAC_BACKLIGHT */ + + /* + * Functions to read from/write to the mmio registers + * - endian conversions may possibly be avoided by + * using the other register aperture. TODO. + */ +static inline u32 _aty_ld_le32(volatile unsigned int regindex, + const struct aty128fb_par *par) +{ + return readl (par->regbase + regindex); +} + +static inline void _aty_st_le32(volatile unsigned int regindex, u32 val, + const struct aty128fb_par *par) +{ + writel (val, par->regbase + regindex); +} + +static inline u8 _aty_ld_8(unsigned int regindex, + const struct aty128fb_par *par) +{ + return readb (par->regbase + regindex); +} + +static inline void _aty_st_8(unsigned int regindex, u8 val, + const struct aty128fb_par *par) +{ + writeb (val, par->regbase + regindex); +} + +#define aty_ld_le32(regindex) _aty_ld_le32(regindex, par) +#define aty_st_le32(regindex, val) _aty_st_le32(regindex, val, par) +#define aty_ld_8(regindex) _aty_ld_8(regindex, par) +#define aty_st_8(regindex, val) _aty_st_8(regindex, val, par) + + /* + * Functions to read from/write to the pll registers + */ + +#define aty_ld_pll(pll_index) _aty_ld_pll(pll_index, par) +#define aty_st_pll(pll_index, val) _aty_st_pll(pll_index, val, par) + + +static u32 _aty_ld_pll(unsigned int pll_index, + const struct aty128fb_par *par) +{ + aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F); + return aty_ld_le32(CLOCK_CNTL_DATA); +} + + +static void _aty_st_pll(unsigned int pll_index, u32 val, + const struct aty128fb_par *par) +{ + aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN); + aty_st_le32(CLOCK_CNTL_DATA, val); +} + + +/* return true when the PLL has completed an atomic update */ +static int aty_pll_readupdate(const struct aty128fb_par *par) +{ + return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); +} + + +static void aty_pll_wait_readupdate(const struct aty128fb_par *par) +{ + unsigned long timeout = jiffies + HZ/100; // should be more than enough + int reset = 1; + + while (time_before(jiffies, timeout)) + if (aty_pll_readupdate(par)) { + reset = 0; + break; + } + + if (reset) /* reset engine?? */ + printk(KERN_DEBUG "aty128fb: PLL write timeout!\n"); +} + + +/* tell PLL to update */ +static void aty_pll_writeupdate(const struct aty128fb_par *par) +{ + aty_pll_wait_readupdate(par); + + aty_st_pll(PPLL_REF_DIV, + aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W); +} + + +/* write to the scratch register to test r/w functionality */ +static int __init register_test(const struct aty128fb_par *par) +{ + u32 val; + int flag = 0; + + val = aty_ld_le32(BIOS_0_SCRATCH); + + aty_st_le32(BIOS_0_SCRATCH, 0x55555555); + if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) { + aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA); + + if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA) + flag = 1; + } + + aty_st_le32(BIOS_0_SCRATCH, val); // restore value + return flag; +} + + +/* + * Accelerator engine functions + */ +static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par) +{ + int i; + + for (;;) { + for (i = 0; i < 2000000; i++) { + par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff; + if (par->fifo_slots >= entries) + return; + } + aty128_reset_engine(par); + } +} + + +static void wait_for_idle(struct aty128fb_par *par) +{ + int i; + + do_wait_for_fifo(64, par); + + for (;;) { + for (i = 0; i < 2000000; i++) { + if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) { + aty128_flush_pixel_cache(par); + par->blitter_may_be_busy = 0; + return; + } + } + aty128_reset_engine(par); + } +} + + +static void wait_for_fifo(u16 entries, struct aty128fb_par *par) +{ + if (par->fifo_slots < entries) + do_wait_for_fifo(64, par); + par->fifo_slots -= entries; +} + + +static void aty128_flush_pixel_cache(const struct aty128fb_par *par) +{ + int i; + u32 tmp; + + tmp = aty_ld_le32(PC_NGUI_CTLSTAT); + tmp &= ~(0x00ff); + tmp |= 0x00ff; + aty_st_le32(PC_NGUI_CTLSTAT, tmp); + + for (i = 0; i < 2000000; i++) + if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY)) + break; +} + + +static void aty128_reset_engine(const struct aty128fb_par *par) +{ + u32 gen_reset_cntl, clock_cntl_index, mclk_cntl; + + aty128_flush_pixel_cache(par); + + clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX); + mclk_cntl = aty_ld_pll(MCLK_CNTL); + + aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000); + + gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL); + aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI); + aty_ld_le32(GEN_RESET_CNTL); + aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI)); + aty_ld_le32(GEN_RESET_CNTL); + + aty_st_pll(MCLK_CNTL, mclk_cntl); + aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index); + aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl); + + /* use old pio mode */ + aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4); + + DBG("engine reset"); +} + + +static void aty128_init_engine(struct aty128fb_par *par) +{ + u32 pitch_value; + + wait_for_idle(par); + + /* 3D scaler not spoken here */ + wait_for_fifo(1, par); + aty_st_le32(SCALE_3D_CNTL, 0x00000000); + + aty128_reset_engine(par); + + pitch_value = par->crtc.pitch; + if (par->crtc.bpp == 24) { + pitch_value = pitch_value * 3; + } + + wait_for_fifo(4, par); + /* setup engine offset registers */ + aty_st_le32(DEFAULT_OFFSET, 0x00000000); + + /* setup engine pitch registers */ + aty_st_le32(DEFAULT_PITCH, pitch_value); + + /* set the default scissor register to max dimensions */ + aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF); + + /* set the drawing controls registers */ + aty_st_le32(DP_GUI_MASTER_CNTL, + GMC_SRC_PITCH_OFFSET_DEFAULT | + GMC_DST_PITCH_OFFSET_DEFAULT | + GMC_SRC_CLIP_DEFAULT | + GMC_DST_CLIP_DEFAULT | + GMC_BRUSH_SOLIDCOLOR | + (depth_to_dst(par->crtc.depth) << 8) | + GMC_SRC_DSTCOLOR | + GMC_BYTE_ORDER_MSB_TO_LSB | + GMC_DP_CONVERSION_TEMP_6500 | + ROP3_PATCOPY | + GMC_DP_SRC_RECT | + GMC_3D_FCN_EN_CLR | + GMC_DST_CLR_CMP_FCN_CLEAR | + GMC_AUX_CLIP_CLEAR | + GMC_WRITE_MASK_SET); + + wait_for_fifo(8, par); + /* clear the line drawing registers */ + aty_st_le32(DST_BRES_ERR, 0); + aty_st_le32(DST_BRES_INC, 0); + aty_st_le32(DST_BRES_DEC, 0); + + /* set brush color registers */ + aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */ + aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */ + + /* set source color registers */ + aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF); /* white */ + aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000); /* black */ + + /* default write mask */ + aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF); + + /* Wait for all the writes to be completed before returning */ + wait_for_idle(par); +} + + +/* convert depth values to their register representation */ +static u32 depth_to_dst(u32 depth) +{ + if (depth <= 8) + return DST_8BPP; + else if (depth <= 15) + return DST_15BPP; + else if (depth == 16) + return DST_16BPP; + else if (depth <= 24) + return DST_24BPP; + else if (depth <= 32) + return DST_32BPP; + + return -EINVAL; +} + +/* + * PLL informations retreival + */ + + +#ifndef __sparc__ +static void __iomem * __init aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev) +{ + u16 dptr; + u8 rom_type; + void __iomem *bios; + size_t rom_size; + + /* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */ + unsigned int temp; + temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); + temp &= 0x00ffffffu; + temp |= 0x04 << 24; + aty_st_le32(RAGE128_MPP_TB_CONFIG, temp); + temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); + + bios = pci_map_rom(dev, &rom_size); + + if (!bios) { + printk(KERN_ERR "aty128fb: ROM failed to map\n"); + return NULL; + } + + /* Very simple test to make sure it appeared */ + if (BIOS_IN16(0) != 0xaa55) { + printk(KERN_ERR "aty128fb: Invalid ROM signature %x should be 0xaa55\n", + BIOS_IN16(0)); + goto failed; + } + + /* Look for the PCI data to check the ROM type */ + dptr = BIOS_IN16(0x18); + + /* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM + * for now, until I've verified this works everywhere. The goal here is more + * to phase out Open Firmware images. + * + * Currently, we only look at the first PCI data, we could iteratre and deal with + * them all, and we should use fb_bios_start relative to start of image and not + * relative start of ROM, but so far, I never found a dual-image ATI card + * + * typedef struct { + * u32 signature; + 0x00 + * u16 vendor; + 0x04 + * u16 device; + 0x06 + * u16 reserved_1; + 0x08 + * u16 dlen; + 0x0a + * u8 drevision; + 0x0c + * u8 class_hi; + 0x0d + * u16 class_lo; + 0x0e + * u16 ilen; + 0x10 + * u16 irevision; + 0x12 + * u8 type; + 0x14 + * u8 indicator; + 0x15 + * u16 reserved_2; + 0x16 + * } pci_data_t; + */ + if (BIOS_IN32(dptr) != (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) { + printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n", + BIOS_IN32(dptr)); + goto anyway; + } + rom_type = BIOS_IN8(dptr + 0x14); + switch(rom_type) { + case 0: + printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n"); + break; + case 1: + printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n"); + goto failed; + case 2: + printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n"); + goto failed; + default: + printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", rom_type); + goto failed; + } + anyway: + return bios; + + failed: + pci_unmap_rom(dev, bios); + return NULL; +} + +static void __init aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios) +{ + unsigned int bios_hdr; + unsigned int bios_pll; + + bios_hdr = BIOS_IN16(0x48); + bios_pll = BIOS_IN16(bios_hdr + 0x30); + + par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16); + par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12); + par->constants.xclk = BIOS_IN16(bios_pll + 0x08); + par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10); + par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e); + + DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n", + par->constants.ppll_max, par->constants.ppll_min, + par->constants.xclk, par->constants.ref_divider, + par->constants.ref_clk); + +} + +#ifdef CONFIG_X86 +static void __iomem * __devinit aty128_find_mem_vbios(struct aty128fb_par *par) +{ + /* I simplified this code as we used to miss the signatures in + * a lot of case. It's now closer to XFree, we just don't check + * for signatures at all... Something better will have to be done + * if we end up having conflicts + */ + u32 segstart; + unsigned char __iomem *rom_base = NULL; + + for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) { + rom_base = ioremap(segstart, 0x10000); + if (rom_base == NULL) + return NULL; + if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa) + break; + iounmap(rom_base); + rom_base = NULL; + } + return rom_base; +} +#endif +#endif /* ndef(__sparc__) */ + +/* fill in known card constants if pll_block is not available */ +static void __init aty128_timings(struct aty128fb_par *par) +{ +#ifdef CONFIG_PPC_OF + /* instead of a table lookup, assume OF has properly + * setup the PLL registers and use their values + * to set the XCLK values and reference divider values */ + + u32 x_mpll_ref_fb_div; + u32 xclk_cntl; + u32 Nx, M; + unsigned PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 }; +#endif + + if (!par->constants.ref_clk) + par->constants.ref_clk = 2950; + +#ifdef CONFIG_PPC_OF + x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV); + xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7; + Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8; + M = x_mpll_ref_fb_div & 0x0000ff; + + par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk), + (M * PostDivSet[xclk_cntl])); + + par->constants.ref_divider = + aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; +#endif + + if (!par->constants.ref_divider) { + par->constants.ref_divider = 0x3b; + + aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e); + aty_pll_writeupdate(par); + } + aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider); + aty_pll_writeupdate(par); + + /* from documentation */ + if (!par->constants.ppll_min) + par->constants.ppll_min = 12500; + if (!par->constants.ppll_max) + par->constants.ppll_max = 25000; /* 23000 on some cards? */ + if (!par->constants.xclk) + par->constants.xclk = 0x1d4d; /* same as mclk */ + + par->constants.fifo_width = 128; + par->constants.fifo_depth = 32; + + switch (aty_ld_le32(MEM_CNTL) & 0x3) { + case 0: + par->mem = &sdr_128; + break; + case 1: + par->mem = &sdr_sgram; + break; + case 2: + par->mem = &ddr_sgram; + break; + default: + par->mem = &sdr_sgram; + } +} + + + +/* + * CRTC programming + */ + +/* Program the CRTC registers */ +static void aty128_set_crtc(const struct aty128_crtc *crtc, + const struct aty128fb_par *par) +{ + aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl); + aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total); + aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid); + aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total); + aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid); + aty_st_le32(CRTC_PITCH, crtc->pitch); + aty_st_le32(CRTC_OFFSET, crtc->offset); + aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl); + /* Disable ATOMIC updating. Is this the right place? */ + aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000)); +} + + +static int aty128_var_to_crtc(const struct fb_var_screeninfo *var, + struct aty128_crtc *crtc, + const struct aty128fb_par *par) +{ + u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst; + u32 left, right, upper, lower, hslen, vslen, sync, vmode; + u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol; + u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; + u32 depth, bytpp; + u8 mode_bytpp[7] = { 0, 0, 1, 2, 2, 3, 4 }; + + /* input */ + xres = var->xres; + yres = var->yres; + vxres = var->xres_virtual; + vyres = var->yres_virtual; + xoffset = var->xoffset; + yoffset = var->yoffset; + bpp = var->bits_per_pixel; + left = var->left_margin; + right = var->right_margin; + upper = var->upper_margin; + lower = var->lower_margin; + hslen = var->hsync_len; + vslen = var->vsync_len; + sync = var->sync; + vmode = var->vmode; + + if (bpp != 16) + depth = bpp; + else + depth = (var->green.length == 6) ? 16 : 15; + + /* check for mode eligibility + * accept only non interlaced modes */ + if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) + return -EINVAL; + + /* convert (and round up) and validate */ + xres = (xres + 7) & ~7; + xoffset = (xoffset + 7) & ~7; + + if (vxres < xres + xoffset) + vxres = xres + xoffset; + + if (vyres < yres + yoffset) + vyres = yres + yoffset; + + /* convert depth into ATI register depth */ + dst = depth_to_dst(depth); + + if (dst == -EINVAL) { + printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n"); + return -EINVAL; + } + + /* convert register depth to bytes per pixel */ + bytpp = mode_bytpp[dst]; + + /* make sure there is enough video ram for the mode */ + if ((u32)(vxres * vyres * bytpp) > par->vram_size) { + printk(KERN_ERR "aty128fb: Not enough memory for mode\n"); + return -EINVAL; + } + + h_disp = (xres >> 3) - 1; + h_total = (((xres + right + hslen + left) >> 3) - 1) & 0xFFFFL; + + v_disp = yres - 1; + v_total = (yres + upper + vslen + lower - 1) & 0xFFFFL; + + /* check to make sure h_total and v_total are in range */ + if (((h_total >> 3) - 1) > 0x1ff || (v_total - 1) > 0x7FF) { + printk(KERN_ERR "aty128fb: invalid width ranges\n"); + return -EINVAL; + } + + h_sync_wid = (hslen + 7) >> 3; + if (h_sync_wid == 0) + h_sync_wid = 1; + else if (h_sync_wid > 0x3f) /* 0x3f = max hwidth */ + h_sync_wid = 0x3f; + + h_sync_strt = (h_disp << 3) + right; + + v_sync_wid = vslen; + if (v_sync_wid == 0) + v_sync_wid = 1; + else if (v_sync_wid > 0x1f) /* 0x1f = max vwidth */ + v_sync_wid = 0x1f; + + v_sync_strt = v_disp + lower; + + h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1; + v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1; + + c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0; + + crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8); + + crtc->h_total = h_total | (h_disp << 16); + crtc->v_total = v_total | (v_disp << 16); + + crtc->h_sync_strt_wid = h_sync_strt | (h_sync_wid << 16) | + (h_sync_pol << 23); + crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) | + (v_sync_pol << 23); + + crtc->pitch = vxres >> 3; + + crtc->offset = 0; + + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) + crtc->offset_cntl = 0x00010000; + else + crtc->offset_cntl = 0; + + crtc->vxres = vxres; + crtc->vyres = vyres; + crtc->xoffset = xoffset; + crtc->yoffset = yoffset; + crtc->depth = depth; + crtc->bpp = bpp; + + return 0; +} + + +static int aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var) +{ + + /* fill in pixel info */ + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.offset = 0; + var->blue.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + var->transp.msb_right = 0; + switch (pix_width) { + case CRTC_PIX_WIDTH_8BPP: + var->bits_per_pixel = 8; + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 0; + var->green.length = 8; + var->blue.length = 8; + break; + case CRTC_PIX_WIDTH_15BPP: + var->bits_per_pixel = 16; + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.length = 5; + break; + case CRTC_PIX_WIDTH_16BPP: + var->bits_pe |