summaryrefslogtreecommitdiffstats
path: root/drivers/radeon/radeonfb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/radeon/radeonfb.c')
-rw-r--r--drivers/radeon/radeonfb.c1916
1 files changed, 1916 insertions, 0 deletions
diff --git a/drivers/radeon/radeonfb.c b/drivers/radeon/radeonfb.c
new file mode 100644
index 0000000000..c4f3b52f0f
--- /dev/null
+++ b/drivers/radeon/radeonfb.c
@@ -0,0 +1,1916 @@
+/*
+ * drivers/video/radeonfb.c
+ * framebuffer driver for ATI Radeon chipset video boards
+ *
+ * Copyright 2000 Ani Joshi <ajoshi@unixbox.com>
+ *
+ *
+ * ChangeLog:
+ * 2000-08-03 initial version 0.0.1
+ * 2000-09-10 more bug fixes, public release 0.0.5
+ * 2001-02-19 mode bug fixes, 0.0.7
+ * 2001-07-05 fixed scrolling issues, engine initialization,
+ * and minor mode tweaking, 0.0.9
+ *
+ *
+ * Special thanks to ATI DevRel team for their hardware donations.
+ *
+ */
+
+
+#define RADEON_VERSION "0.0.9"
+
+
+#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/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+#include <video/fbcon-cfb32.h>
+
+#include "radeon.h"
+
+
+#define DEBUG 0
+
+#if DEBUG
+#define RTRACE printk
+#else
+#define RTRACE if(0) printk
+#endif
+
+
+
+enum radeon_chips {
+ RADEON_QD,
+ RADEON_QE,
+ RADEON_QF,
+ RADEON_QG
+};
+
+
+static struct pci_device_id radeonfb_pci_table[] __devinitdata = {
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_QD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QD},
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_QE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QE},
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_QF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QF},
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_RADEON_QG, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QG},
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, radeonfb_pci_table);
+
+
+typedef struct {
+ u16 reg;
+ u32 val;
+} reg_val;
+
+
+/* these common regs are cleared before mode setting so they do not
+ * interfere with anything
+ */
+reg_val common_regs[] = {
+ { OVR_CLR, 0 },
+ { OVR_WID_LEFT_RIGHT, 0 },
+ { OVR_WID_TOP_BOTTOM, 0 },
+ { OV0_SCALE_CNTL, 0 },
+ { SUBPIC_CNTL, 0 },
+ { VIPH_CONTROL, 0 },
+ { I2C_CNTL_1, 0 },
+ { GEN_INT_CNTL, 0 },
+ { CAP0_TRIG_CNTL, 0 },
+};
+
+#define COMMON_REGS_SIZE = (sizeof(common_regs)/sizeof(common_regs[0]))
+
+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;
+
+
+struct pll_info {
+ int ppll_max;
+ int ppll_min;
+ int xclk;
+ int ref_div;
+ int ref_clk;
+};
+
+
+struct ram_info {
+ int ml;
+ int mb;
+ int trcd;
+ int trp;
+ int twr;
+ int cl;
+ int tr2w;
+ int loop_latency;
+ int rloop;
+};
+
+
+struct radeon_regs {
+ u32 crtc_h_total_disp;
+ u32 crtc_h_sync_strt_wid;
+ u32 crtc_v_total_disp;
+ u32 crtc_v_sync_strt_wid;
+ u32 crtc_pitch;
+ u32 flags;
+ u32 pix_clock;
+ int xres, yres;
+ int bpp;
+ u32 crtc_gen_cntl;
+ u32 crtc_ext_cntl;
+ u32 dac_cntl;
+ u32 dda_config;
+ u32 dda_on_off;
+ u32 ppll_div_3;
+ u32 ppll_ref_div;
+};
+
+
+struct radeonfb_info {
+ struct fb_info info;
+
+ struct radeon_regs state;
+ struct radeon_regs init_state;
+
+ char name[10];
+ char ram_type[12];
+
+ u32 mmio_base_phys;
+ u32 fb_base_phys;
+
+ u32 mmio_base;
+ u32 fb_base;
+
+ struct pci_dev *pdev;
+
+ struct display disp;
+ int currcon;
+ struct display *currcon_display;
+
+ struct { u8 red, green, blue, pad; } palette[256];
+
+ int chipset;
+ int video_ram;
+ u8 rev;
+ int pitch, bpp, depth;
+ int xres, yres, pixclock;
+
+ u32 dp_gui_master_cntl;
+
+ struct pll_info pll;
+ int pll_output_freq, post_div, fb_div;
+
+ struct ram_info ram;
+
+#if defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32)
+ union {
+#if defined(FBCON_HAS_CFB16)
+ u_int16_t cfb16[16];
+#endif
+#if defined(FBCON_HAS_CFB32)
+ u_int32_t cfb32[16];
+#endif
+ } con_cmap;
+#endif
+};
+
+
+static struct fb_var_screeninfo radeonfb_default_var = {
+ 640, 480, 640, 480, 0, 0, 8, 0,
+ {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 39721, 40, 24, 32, 11, 96, 2,
+ 0, FB_VMODE_NONINTERLACED
+};
+
+
+/*
+ * IO macros
+ */
+
+#define INREG8(addr) readb((rinfo->mmio_base)+addr)
+#define OUTREG8(addr,val) writeb(val, (rinfo->mmio_base)+addr)
+#define INREG(addr) readl((rinfo->mmio_base)+addr)
+#define OUTREG(addr,val) writel(val, (rinfo->mmio_base)+addr)
+
+#define OUTPLL(addr,val) OUTREG8(CLOCK_CNTL_INDEX, (addr & 0x0000001f) | 0x00000080); \
+ OUTREG(CLOCK_CNTL_DATA, val)
+#define OUTPLLP(addr,val,mask) \
+ do { \
+ unsigned int _tmp = INPLL(addr); \
+ _tmp &= (mask); \
+ _tmp |= (val); \
+ OUTPLL(addr, _tmp); \
+ } while (0)
+
+#define OUTREGP(addr,val,mask) \
+ do { \
+ unsigned int _tmp = INREG(addr); \
+ _tmp &= (mask); \
+ _tmp |= (val); \
+ OUTREG(addr, _tmp); \
+ } while (0)
+
+
+static __inline__ u32 _INPLL(struct radeonfb_info *rinfo, u32 addr)
+{
+ OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000001f);
+ return (INREG(CLOCK_CNTL_DATA));
+}
+
+#define INPLL(addr) _INPLL(rinfo, addr)
+
+
+/*
+ * 2D engine routines
+ */
+
+static __inline__ void radeon_engine_flush (struct radeonfb_info *rinfo)
+{
+ int i;
+
+ /* initiate flush */
+ OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
+ ~RB2D_DC_FLUSH_ALL);
+
+ for (i=0; i < 2000000; i++) {
+ if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
+ break;
+ }
+}
+
+
+static __inline__ void _radeon_fifo_wait (struct radeonfb_info *rinfo, int entries)
+{
+ int i;
+
+ for (i=0; i<2000000; i++)
+ if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
+ return;
+}
+
+
+static __inline__ void _radeon_engine_idle (struct radeonfb_info *rinfo)
+{
+ int i;
+
+ /* ensure FIFO is empty before waiting for idle */
+ _radeon_fifo_wait (rinfo, 64);
+
+ for (i=0; i<2000000; i++) {
+ if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
+ radeon_engine_flush (rinfo);
+ return;
+ }
+ }
+}
+
+
+#define radeon_engine_idle() _radeon_engine_idle(rinfo)
+#define radeon_fifo_wait(entries) _radeon_fifo_wait(rinfo,entries)
+
+
+
+/*
+ * helper routines
+ */
+
+static __inline__ u32 radeon_get_dstbpp(u16 depth)
+{
+ switch (depth) {
+ case 8:
+ return DST_8BPP;
+ case 15:
+ return DST_15BPP;
+ case 16:
+ return DST_16BPP;
+ case 32:
+ return DST_32BPP;
+ default:
+ return 0;
+ }
+}
+
+
+static void _radeon_engine_reset(struct radeonfb_info *rinfo)
+{
+ u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
+
+ radeon_engine_flush (rinfo);
+
+ clock_cntl_index = INREG(CLOCK_CNTL_INDEX);
+ mclk_cntl = INPLL(MCLK_CNTL);
+
+ OUTPLL(MCLK_CNTL, (mclk_cntl |
+ FORCEON_MCLKA |
+ FORCEON_MCLKB |
+ FORCEON_YCLKA |
+ FORCEON_YCLKB |
+ FORCEON_MC |
+ FORCEON_AIC));
+ rbbm_soft_reset = INREG(RBBM_SOFT_RESET);
+
+ OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset |
+ SOFT_RESET_CP |
+ SOFT_RESET_HI |
+ SOFT_RESET_SE |
+ SOFT_RESET_RE |
+ SOFT_RESET_PP |
+ SOFT_RESET_E2 |
+ SOFT_RESET_RB |
+ SOFT_RESET_HDP);
+ INREG(RBBM_SOFT_RESET);
+ OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32)
+ ~(SOFT_RESET_CP |
+ SOFT_RESET_HI |
+ SOFT_RESET_SE |
+ SOFT_RESET_RE |
+ SOFT_RESET_PP |
+ SOFT_RESET_E2 |
+ SOFT_RESET_RB |
+ SOFT_RESET_HDP));
+ INREG(RBBM_SOFT_RESET);
+
+ OUTPLL(MCLK_CNTL, mclk_cntl);
+ OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
+ OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset);
+
+ return;
+}
+
+#define radeon_engine_reset() _radeon_engine_reset(rinfo)
+
+
+static __inline__ u8 radeon_get_post_div_bitval(int post_div)
+{
+ switch (post_div) {
+ case 1:
+ return 0x00;
+ case 2:
+ return 0x01;
+ case 3:
+ return 0x04;
+ case 4:
+ return 0x02;
+ case 6:
+ return 0x06;
+ case 8:
+ return 0x03;
+ case 12:
+ return 0x07;
+ default:
+ return 0x02;
+ }
+}
+
+
+
+static __inline__ int round_div(int num, int den)
+{
+ return (num + (den / 2)) / den;
+}
+
+
+
+static __inline__ int min_bits_req(int val)
+{
+ int bits_req = 0;
+
+ if (val == 0)
+ bits_req = 1;
+
+ while (val) {
+ val >>= 1;
+ bits_req++;
+ }
+
+ return (bits_req);
+}
+
+
+static __inline__ int _max(int val1, int val2)
+{
+ if (val1 >= val2)
+ return val1;
+ else
+ return val2;
+}
+
+
+
+/*
+ * globals
+ */
+
+static char fontname[40] __initdata;
+static char *mode_option __initdata;
+static char noaccel __initdata = 0;
+
+#ifdef FBCON_HAS_CFB8
+static struct display_switch fbcon_radeon8;
+#endif
+
+
+/*
+ * prototypes
+ */
+
+static int radeonfb_get_fix (struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info);
+static int radeonfb_get_var (struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int radeonfb_set_var (struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int radeonfb_get_cmap (struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+static int radeonfb_set_cmap (struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+static int radeonfb_pan_display (struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg, int con, struct fb_info *info);
+static int radeonfb_switch (int con, struct fb_info *info);
+static int radeonfb_updatevar (int con, struct fb_info *info);
+static void radeonfb_blank (int blank, struct fb_info *info);
+static int radeon_get_cmap_len (const struct fb_var_screeninfo *var);
+static int radeon_getcolreg (unsigned regno, unsigned *red, unsigned *green,
+ unsigned *blue, unsigned *transp,
+ struct fb_info *info);
+static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp, struct fb_info *info);
+static void radeon_set_dispsw (struct radeonfb_info *rinfo);
+static void radeon_save_state (struct radeonfb_info *rinfo,
+ struct radeon_regs *save);
+static void radeon_engine_init (struct radeonfb_info *rinfo);
+static void radeon_load_video_mode (struct radeonfb_info *rinfo,
+ struct fb_var_screeninfo *mode);
+static void radeon_write_mode (struct radeonfb_info *rinfo,
+ struct radeon_regs *mode);
+static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo);
+static int __devinit radeon_init_disp (struct radeonfb_info *rinfo);
+static int radeon_init_disp_var (struct radeonfb_info *rinfo);
+static int radeonfb_pci_register (struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev);
+static char *radeon_find_rom(struct radeonfb_info *rinfo);
+static void radeon_get_pllinfo(struct radeonfb_info *rinfo, char *bios_seg);
+
+
+static struct fb_ops radeon_fb_ops = {
+ fb_get_fix: radeonfb_get_fix,
+ fb_get_var: radeonfb_get_var,
+ fb_set_var: radeonfb_set_var,
+ fb_get_cmap: radeonfb_get_cmap,
+ fb_set_cmap: radeonfb_set_cmap,
+ fb_pan_display: radeonfb_pan_display,
+ fb_ioctl: radeonfb_ioctl,
+};
+
+
+static struct pci_driver radeonfb_driver = {
+ name: "radeonfb",
+ id_table: radeonfb_pci_table,
+ probe: radeonfb_pci_register,
+ remove: radeonfb_pci_unregister,
+};
+
+
+int __init radeonfb_init (void)
+{
+ return pci_module_init (&radeonfb_driver);
+}
+
+
+void __exit radeonfb_exit (void)
+{
+ pci_unregister_driver (&radeonfb_driver);
+}
+
+
+int __init radeonfb_setup (char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ for (this_opt = strtok (options, ","); this_opt;
+ this_opt = strtok (NULL, ",")) {
+ if (!strncmp (this_opt, "font:", 5)) {
+ char *p;
+ int i;
+
+ p = this_opt + 5;
+ for (i=0; i<sizeof (fontname) - 1; i++)
+ if (!*p || *p == ' ' || *p == ',')
+ break;
+ memcpy(fontname, this_opt + 5, i);
+ } else if (!strncmp(this_opt, "noaccel", 7)) {
+ noaccel = 1;
+ }
+ else mode_option = this_opt;
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+module_init(radeonfb_init);
+module_exit(radeonfb_exit);
+#endif
+
+
+MODULE_AUTHOR("Ani Joshi");
+MODULE_DESCRIPTION("framebuffer driver for ATI Radeon chipset");
+
+
+
+static int radeonfb_pci_register (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct radeonfb_info *rinfo;
+ u32 tmp;
+ int i, j;
+ char *bios_seg = NULL;
+
+ rinfo = kmalloc (sizeof (struct radeonfb_info), GFP_KERNEL);
+ if (!rinfo) {
+ printk ("radeonfb: could not allocate memory\n");
+ return -ENODEV;
+ }
+
+ memset (rinfo, 0, sizeof (struct radeonfb_info));
+
+ /* enable device */
+ {
+ int err;
+
+ if ((err = pci_enable_device(pdev))) {
+ printk("radeonfb: cannot enable device\n");
+ kfree (rinfo);
+ return -ENODEV;
+ }
+ }
+
+ /* set base addrs */
+ rinfo->fb_base_phys = pci_resource_start (pdev, 0);
+ rinfo->mmio_base_phys = pci_resource_start (pdev, 2);
+
+ /* request the mem regions */
+ if (!request_mem_region (rinfo->fb_base_phys,
+ pci_resource_len(pdev, 0), "radeonfb")) {
+ printk ("radeonfb: cannot reserve FB region\n");
+ kfree (rinfo);
+ return -ENODEV;
+ }
+
+ if (!request_mem_region (rinfo->mmio_base_phys,
+ pci_resource_len(pdev, 2), "radeonfb")) {
+ printk ("radeonfb: cannot reserve MMIO region\n");
+ release_mem_region (rinfo->fb_base_phys,
+ pci_resource_len(pdev, 0));
+ kfree (rinfo);
+ return -ENODEV;
+ }
+
+ /* map the regions */
+ rinfo->mmio_base = (u32) ioremap (rinfo->mmio_base_phys,
+ RADEON_REGSIZE);
+ if (!rinfo->mmio_base) {
+ printk ("radeonfb: cannot map MMIO\n");
+ release_mem_region (rinfo->mmio_base_phys,
+ pci_resource_len(pdev, 2));
+ release_mem_region (rinfo->fb_base_phys,
+ pci_resource_len(pdev, 0));
+ kfree (rinfo);
+ return -ENODEV;
+ }
+
+ /* chipset */
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_RADEON_QD:
+ strcpy(rinfo->name, "Radeon QD ");
+ break;
+ case PCI_DEVICE_ID_RADEON_QE:
+ strcpy(rinfo->name, "Radeon QE ");
+ break;
+ case PCI_DEVICE_ID_RADEON_QF:
+ strcpy(rinfo->name, "Radeon QF ");
+ break;
+ case PCI_DEVICE_ID_RADEON_QG:
+ strcpy(rinfo->name, "Radeon QG ");
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ /* framebuffer size */
+ tmp = INREG(CONFIG_MEMSIZE);
+
+ /* mem size is bits [28:0], mask off the rest */
+ rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK;
+
+ /* ram type */
+ tmp = INREG(MEM_SDRAM_MODE_REG);
+ switch ((MEM_CFG_TYPE & tmp) >> 30) {
+ case 0:
+ /* SDR SGRAM (2:1) */
+ strcpy(rinfo->ram_type, "SDR SGRAM");
+ rinfo->ram.ml = 4;
+ rinfo->ram.mb = 4;
+ rinfo->ram.trcd = 1;
+ rinfo->ram.trp = 2;
+ rinfo->ram.twr = 1;
+ rinfo->ram.cl = 2;
+ rinfo->ram.loop_latency = 16;
+ rinfo->ram.rloop = 16;
+
+ break;
+ case 1:
+ /* DDR SGRAM */
+ strcpy(rinfo->ram_type, "DDR SGRAM");
+ rinfo->ram.ml = 4;
+ rinfo->ram.mb = 4;
+ rinfo->ram.trcd = 3;
+ rinfo->ram.trp = 3;
+ rinfo->ram.twr = 2;
+ rinfo->ram.cl = 3;
+ rinfo->ram.tr2w = 1;
+ rinfo->ram.loop_latency = 16;
+ rinfo->ram.rloop = 16;
+
+ break;
+ default:
+ /* 64-bit SDR SGRAM */
+ strcpy(rinfo->ram_type, "SDR SGRAM 64");
+ rinfo->ram.ml = 4;
+ rinfo->ram.mb = 8;
+ rinfo->ram.trcd = 3;
+ rinfo->ram.trp = 3;
+ rinfo->ram.twr = 1;
+ rinfo->ram.cl = 3;
+ rinfo->ram.tr2w = 1;
+ rinfo->ram.loop_latency = 17;
+ rinfo->ram.rloop = 17;
+
+ break;
+ }
+
+ bios_seg = radeon_find_rom(rinfo);
+ radeon_get_pllinfo(rinfo, bios_seg);
+
+ printk("radeonfb: ref_clk=%d, ref_div=%d, xclk=%d\n",
+ rinfo->pll.ref_clk, rinfo->pll.ref_div, rinfo->pll.xclk);
+
+ RTRACE("radeonfb: probed %s %dk videoram\n", (rinfo->ram_type), (rinfo->video_ram/1024));
+
+ rinfo->fb_base = (u32) ioremap (rinfo->fb_base_phys,
+ rinfo->video_ram);
+ if (!rinfo->fb_base) {
+ printk ("radeonfb: cannot map FB\n");
+ iounmap ((void*)rinfo->mmio_base);
+ release_mem_region (rinfo->mmio_base_phys,
+ pci_resource_len(pdev, 2));
+ release_mem_region (rinfo->fb_base_phys,
+ pci_resource_len(pdev, 0));
+ kfree (rinfo);
+ return -ENODEV;
+ }
+
+ /* XXX turn off accel for now, blts aren't working right */
+ noaccel = 1;
+
+ /* set all the vital stuff */
+ radeon_set_fbinfo (rinfo);
+
+ /* save current mode regs before we switch into the new one
+ * so we can restore this upon __exit
+ */
+ radeon_save_state (rinfo, &rinfo->init_state);
+
+ /* init palette */
+ for (i=0; i<16; i++) {
+ j = color_table[i];
+ rinfo->palette[i].red = default_red[j];
+ rinfo->palette[i].green = default_grn[j];
+ rinfo->palette[i].blue = default_blu[j];
+ }
+
+ pdev->driver_data = rinfo;
+
+ if (register_framebuffer ((struct fb_info *) rinfo) < 0) {
+ printk ("radeonfb: could not register framebuffer\n");
+ iounmap ((void*)rinfo->fb_base);
+ iounmap ((void*)rinfo->mmio_base);
+ release_mem_region (rinfo->mmio_base_phys,
+ pci_resource_len(pdev, 2));
+ release_mem_region (rinfo->fb_base_phys,
+ pci_resource_len(pdev, 0));
+ kfree (rinfo);
+ return -ENODEV;
+ }
+
+ if (!noaccel) {
+ /* initialize the engine */
+ radeon_engine_init (rinfo);
+ }
+
+ printk ("radeonfb: ATI Radeon %s %d MB\n", rinfo->ram_type,
+ (rinfo->video_ram/(1024*1024)));
+
+ return 0;
+}
+
+
+
+static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
+{
+ struct radeonfb_info *rinfo = pdev->driver_data;
+
+ if (!rinfo)
+ return;
+
+ /* restore original state */
+ radeon_write_mode (rinfo, &rinfo->init_state);
+
+ unregister_framebuffer ((struct fb_info *) rinfo);
+
+ iounmap ((void*)rinfo->mmio_base);
+ iounmap ((void*)rinfo->fb_base);
+
+ release_mem_region (rinfo->mmio_base_phys,
+ pci_resource_len(pdev, 2));
+ release_mem_region (rinfo->fb_base_phys,
+ pci_resource_len(pdev, 0));
+
+ kfree (rinfo);
+}
+
+
+
+static char *radeon_find_rom(struct radeonfb_info *rinfo)
+{
+ u32 segstart;
+ char *rom_base;
+ char *rom;
+ int stage;
+ int i;
+ char aty_rom_sig[] = "761295520";
+ char radeon_sig[] = "RG6";
+
+#if defined(__i386__)
+ for(segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
+ stage = 1;
+
+ rom_base = (char *)ioremap(segstart, 0x1000);
+
+ if ((*rom_base == 0x55) && (((*(rom_base + 1)) & 0xff) == 0xaa))
+ stage = 2;
+
+
+ if (stage != 2) {
+ iounmap(rom_base);
+ continue;
+ }
+
+ rom = rom_base;
+
+ for (i = 0; (i < 128 - strlen(aty_rom_sig)) && (stage != 3); i++) {
+ if (aty_rom_sig[0] == *rom)
+ if (strncmp(aty_rom_sig, rom,
+ strlen(aty_rom_sig)) == 0)
+ stage = 3;
+ rom++;
+ }
+ if (stage != 3) {
+ iounmap(rom_base);
+ continue;
+ }
+ rom = rom_base;
+
+ for (i = 0; (i < 512) && (stage != 4); i++) {
+ if (radeon_sig[0] == *rom)
+ if (strncmp(radeon_sig, rom,
+ strlen(radeon_sig)) == 0)
+ stage = 4;
+ rom++;
+ }
+ if (stage != 4) {
+ iounmap(rom_base);
+ continue;
+ }
+
+ return rom_base;
+ }
+#endif
+ return NULL;
+}
+
+
+
+static void radeon_get_pllinfo(struct radeonfb_info *rinfo, char *bios_seg)
+{
+ void *bios_header;
+ void *header_ptr;
+ u16 bios_header_offset, pll_info_offset;
+ PLL_BLOCK pll;
+
+ if (bios_seg) {
+ bios_header = bios_seg + 0x48L;
+ header_ptr = bios_header;
+
+ bios_header_offset = readw(header_ptr);
+ bios_header = bios_seg + bios_header_offset;
+ bios_header += 0x30;
+
+ header_ptr = bios_header;
+ pll_info_offset = readw(header_ptr);
+ header_ptr = bios_seg + pll_info_offset;
+
+ memcpy_fromio(&pll, header_ptr, 50);
+
+ rinfo->pll.xclk = (u32)pll.XCLK;
+ rinfo->pll.ref_clk = (u32)pll.PCLK_ref_freq;
+ rinfo->pll.ref_div = (u32)pll.PCLK_ref_divider;
+ rinfo->pll.ppll_min = pll.PCLK_min_freq;
+ rinfo->pll.ppll_max = pll.PCLK_max_freq;
+ } else {
+ /* no BIOS or BIOS not found, use defaults */
+
+ rinfo->pll.ppll_max = 35000;
+ rinfo->pll.ppll_min = 12000;
+ rinfo->pll.xclk = 16600;
+ rinfo->pll.ref_div = 67;
+ rinfo->pll.ref_clk = 2700;
+ }
+}
+
+static void radeon_engine_init (struct radeonfb_info *rinfo)
+{
+ u32 temp;
+
+ /* disable 3D engine */
+ OUTREG(RB3D_CNTL, 0);
+
+ radeon_engine_reset ();
+
+ radeon_fifo_wait (1);
+ OUTREG(DSTCACHE_MODE, 0);
+
+ /* XXX */
+ rinfo->pitch = ((rinfo->xres * (rinfo->depth / 8) + 0x3f)) >> 6;
+
+ radeon_fifo_wait (1);
+ temp = INREG(DEFAULT_PITCH_OFFSET);
+ OUTREG(DEFAULT_PITCH_OFFSET, ((temp & 0xc0000000) |
+ (rinfo->pitch << 0x16)));
+
+ radeon_fifo_wait (1);
+ OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
+
+ radeon_fifo_wait (1);
+ OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX |
+ DEFAULT_SC_BOTTOM_MAX));
+
+ temp = radeon_get_dstbpp(rinfo->depth);
+ rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS);
+ radeon_fifo_wait (1);
+ OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl |
+ GMC_BRUSH_SOLID_COLOR |
+ GMC_SRC_DATATYPE_COLOR));
+
+ radeon_fifo_wait (7);
+
+ /* clear line drawing regs */
+ OUTREG(DST_LINE_START, 0);
+ OUTREG(DST_LINE_END, 0);
+
+ /* set brush color regs */
+ OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff);
+ OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000);
+
+ /* set source color regs */
+ OUTREG(DP_SRC_FRGD_CLR, 0xffffffff);
+ OUTREG(DP_SRC_BKGD_CLR, 0x00000000);
+
+ /* default write mask */
+ OUTREG(DP_WRITE_MSK, 0xffffffff);
+
+ radeon_engine_idle ();
+}
+
+
+
+static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo)
+{
+ struct fb_info *info;
+
+ info = &rinfo->info;
+
+ strcpy (info->modename, rinfo->name);
+ info->node = -1;
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->fbops = &radeon_fb_ops;
+ info->display_fg = NULL;
+ strncpy (info->fontname, fontname, sizeof (info->fontname));
+ info->fontname[sizeof (info->fontname) - 1] = 0;
+ info->changevar = NULL;
+ info->switch_con = radeonfb_switch;
+ info->updatevar = radeonfb_updatevar;
+ info->blank = radeonfb_blank;
+
+ if (radeon_init_disp (rinfo) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+
+static int __devinit radeon_init_disp (struct radeonfb_info *rinfo)
+{
+ struct fb_info *info;
+ struct display *disp;
+
+ info = &rinfo->info;
+ disp = &rinfo->disp;
+
+ disp->var = radeonfb_default_var;
+ info->disp = disp;
+
+ radeon_set_dispsw (rinfo);
+
+ if (noaccel)
+ disp->scrollmode = SCROLL_YREDRAW;
+ else
+ disp->scrollmode = 0;
+
+ rinfo->currcon_display = disp;
+
+ if ((radeon_init_disp_var (rinfo)) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+
+static int radeon_init_disp_var (struct radeonfb_info *rinfo)
+{
+#ifndef MODULE
+ if (mode_option)
+ fb_find_mode (&rinfo->disp.var, &rinfo->info, mode_option,
+ NULL, 0, NULL, 8);
+ else
+#endif
+ fb_find_mode (&rinfo->disp.var, &rinfo->info, "640x480-8@60",
+ NULL, 0, NULL, 0);
+
+ if (noaccel)
+ rinfo->disp.var.accel_flags &= ~FB_ACCELF_TEXT;
+ else
+ rinfo->disp.var.accel_flags |= FB_ACCELF_TEXT;
+
+ return 0;
+}
+
+
+
+static void radeon_set_dispsw (struct radeonfb_info *rinfo)
+{
+ struct display *disp = &rinfo->disp;
+ int accel;
+
+ accel = disp->var.accel_flags & FB_ACCELF_TEXT;
+
+ disp->dispsw_data = NULL;
+
+ disp->screen_base = (char*)rinfo->fb_base;
+ disp->type = FB_TYPE_PACKED_PIXELS;
+ disp->type_aux = 0;
+ disp->ypanstep = 1;
+ disp->ywrapstep = 0;
+ disp->can_soft_blank = 1;
+ disp->inverse = 0;
+
+ rinfo->depth = disp->var.bits_per_pixel;
+ switch (disp->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+ case 8:
+ disp->dispsw = accel ? &fbcon_radeon8 : &fbcon_cfb8;
+ disp->visual = FB_VISUAL_PSEUDOCOLOR;
+ disp->line_length = disp->var.xres_virtual;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB16
+ case 16:
+ disp->dispsw = &fbcon_cfb16;
+ disp->dispsw_data = &rinfo->con_cmap.cfb16;
+ disp->visual = FB_VISUAL_DIRECTCOLOR;
+ disp->line_length = disp->var.xres_virtual * 2;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB32
+ case 32:
+ disp->dispsw = &fbcon_cfb32;
+ disp->dispsw_data = &rinfo->con_cmap.cfb32;
+ disp->visual = FB_VISUAL_DIRECTCOLOR;
+ disp->line_length = disp->var.xres_virtual * 4;
+ break;
+#endif
+ default:
+ printk ("radeonfb: setting fbcon_dummy renderer\n");
+ disp->dispsw = &fbcon_dummy;
+ }
+
+ return;
+}
+
+
+
+/*
+ * fb ops
+ */
+
+static int radeonfb_get_fix (struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
+ struct display *disp;
+
+ disp = (con < 0) ? rinfo->info.disp : &fb_display[con];
+
+ memset (fix, 0, sizeof (struct fb_fix_screeninfo));
+ strcpy (fix->id, rinfo->name);
+
+ fix->smem_start = rinfo->fb_base_phys;
+ fix->smem_len = rinfo->video_ram;
+
+ fix->type = disp->type;
+ fix->type_aux = disp->type_aux;
+ fix->visual = disp->visual;
+
+ fix->xpanstep = 1;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+
+ fix->line_length = disp->line_length;
+
+ fix->mmio_start = rinfo->mmio_base_phys;
+ fix->mmio_len = RADEON_REGSIZE;
+ if (noaccel)
+ fix->accel = FB_ACCEL_NONE;
+ else
+ fix->accel = 40; /* XXX */
+
+ return 0;
+}
+
+
+
+static int radeonfb_get_var (struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
+
+ *var = (con < 0) ? rinfo->disp.var : fb_display[con].var;
+
+ return 0;
+}
+
+
+
+static int radeonfb_set_var (struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
+ struct display *disp;
+ struct fb_var_screeninfo v;
+ int nom, den, i, accel;
+ unsigned chgvar = 0;
+ static struct {
+ int xres, yres;
+ } modes[] = {
+ {
+ 1600, 1280}, {
+ 1280, 1024}, {
+ 1024, 768}, {
+ 800, 600}, {
+ 640, 480}, {
+ -1, -1}
+ };
+
+ disp = (con < 0) ? rinfo->info.disp : &fb_display[con];
+
+ accel = var->accel_flags & FB_ACCELF_TEXT;
+
+ if (con >= 0) {
+ chgvar = ((disp->var.xres != var->xres) ||
+ (disp->var.yres != var->yres) ||
+ (disp->var.xres_virtual != var->xres_virtual) ||
+ (disp->var.yres_virtual != var->yres_virtual) ||
+ memcmp (&disp->var.red, &var->red, sizeof (var->red)) ||
+ memcmp (&disp->var.green, &var->green, sizeof (var->green)) ||
+ memcmp (&disp->var.blue, &var->blue, sizeof (var->blue)));
+ }
+
+ memcpy (&v, var, sizeof (v));
+
+ switch (v.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+ case 8:
+ v.bits_per_pixel = 8;
+ disp->dispsw = accel ? &fbcon_radeon8 : &fbcon_cfb8;
+ nom = den = 1;
+ disp->line_length = v.xres_virtual;
+ disp->visual = FB_VISUAL_PSEUDOCOLOR;
+ v.red.offset = v.green.offset = v.blue.offset = 0;
+ v.red.length = v.green.length = v.blue.length = 8;
+ break;
+#endif
+
+#ifdef FBCON_HAS_CFB16
+ case 16:
+ v.bits_per_pixel = 16;
+ disp->dispsw = &fbcon_cfb16;
+ disp->dispsw_data = &rinfo->con_cmap.cfb16;
+ nom = 2;
+ den = 1;
+ disp->line_length = v.xres_virtual * 2;
+ disp->visual = FB_VISUAL_DIRECTCOLOR;
+ v.red.offset = 11;
+ v.green.offset = 5;