diff options
Diffstat (limited to 'drivers/radeon/radeonfb.c')
-rw-r--r-- | drivers/radeon/radeonfb.c | 1916 |
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; + |