diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/aty/radeon_base.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/video/aty/radeon_base.c')
-rw-r--r-- | drivers/video/aty/radeon_base.c | 2587 |
1 files changed, 2587 insertions, 0 deletions
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c new file mode 100644 index 00000000000..e8eb124754b --- /dev/null +++ b/drivers/video/aty/radeon_base.c @@ -0,0 +1,2587 @@ +/* + * drivers/video/aty/radeon_base.c + * + * framebuffer driver for ATI Radeon chipset video boards + * + * Copyright 2003 Ben. Herrenschmidt <benh@kernel.crashing.org> + * Copyright 2000 Ani Joshi <ajoshi@kernel.crashing.org> + * + * i2c bits from Luca Tettamanti <kronos@kronoz.cjb.net> + * + * Special thanks to ATI DevRel team for their hardware donations. + * + * ...Insert GPL boilerplate here... + * + * Significant portions of this driver apdated from XFree86 Radeon + * driver which has the following copyright notice: + * + * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and + * VA Linux Systems Inc., Fremont, California. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR + * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * XFree86 driver authors: + * + * Kevin E. Martin <martin@xfree86.org> + * Rickard E. Faith <faith@valinux.com> + * Alan Hourihane <alanh@fairlite.demon.co.uk> + * + */ + + +#define RADEON_VERSION "0.2.0" + +#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/delay.h> +#include <linux/time.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/vmalloc.h> +#include <linux/device.h> +#include <linux/i2c.h> + +#include <asm/io.h> +#include <asm/uaccess.h> + +#ifdef CONFIG_PPC_OF + +#include <asm/pci-bridge.h> +#include "../macmodes.h" + +#ifdef CONFIG_PMAC_BACKLIGHT +#include <asm/backlight.h> +#endif + +#ifdef CONFIG_BOOTX_TEXT +#include <asm/btext.h> +#endif + +#endif /* CONFIG_PPC_OF */ + +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif + +#include <video/radeon.h> +#include <linux/radeonfb.h> + +#include "../edid.h" // MOVE THAT TO include/video +#include "ati_ids.h" +#include "radeonfb.h" + +#define MAX_MAPPED_VRAM (2048*2048*4) +#define MIN_MAPPED_VRAM (1024*768*1) + +#define CHIP_DEF(id, family, flags) \ + { PCI_VENDOR_ID_ATI, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (flags) | (CHIP_FAMILY_##family) } + +static struct pci_device_id radeonfb_pci_table[] = { + /* Mobility M6 */ + CHIP_DEF(PCI_CHIP_RADEON_LY, RV100, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RADEON_LZ, RV100, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + /* Radeon VE/7000 */ + CHIP_DEF(PCI_CHIP_RV100_QY, RV100, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV100_QZ, RV100, CHIP_HAS_CRTC2), + /* Radeon IGP320M (U1) */ + CHIP_DEF(PCI_CHIP_RS100_4336, RS100, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY), + /* Radeon IGP320 (A3) */ + CHIP_DEF(PCI_CHIP_RS100_4136, RS100, CHIP_HAS_CRTC2 | CHIP_IS_IGP), + /* IGP330M/340M/350M (U2) */ + CHIP_DEF(PCI_CHIP_RS200_4337, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY), + /* IGP330/340/350 (A4) */ + CHIP_DEF(PCI_CHIP_RS200_4137, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP), + /* Mobility 7000 IGP */ + CHIP_DEF(PCI_CHIP_RS250_4437, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY), + /* 7000 IGP (A4+) */ + CHIP_DEF(PCI_CHIP_RS250_4237, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP), + /* 8500 AIW */ + CHIP_DEF(PCI_CHIP_R200_BB, R200, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R200_BC, R200, CHIP_HAS_CRTC2), + /* 8700/8800 */ + CHIP_DEF(PCI_CHIP_R200_QH, R200, CHIP_HAS_CRTC2), + /* 8500 */ + CHIP_DEF(PCI_CHIP_R200_QL, R200, CHIP_HAS_CRTC2), + /* 9100 */ + CHIP_DEF(PCI_CHIP_R200_QM, R200, CHIP_HAS_CRTC2), + /* Mobility M7 */ + CHIP_DEF(PCI_CHIP_RADEON_LW, RV200, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RADEON_LX, RV200, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + /* 7500 */ + CHIP_DEF(PCI_CHIP_RV200_QW, RV200, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV200_QX, RV200, CHIP_HAS_CRTC2), + /* Mobility M9 */ + CHIP_DEF(PCI_CHIP_RV250_Ld, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV250_Le, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV250_Lf, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV250_Lg, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + /* 9000/Pro */ + CHIP_DEF(PCI_CHIP_RV250_If, RV250, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV250_Ig, RV250, CHIP_HAS_CRTC2), + /* Mobility 9100 IGP (U3) */ + CHIP_DEF(PCI_CHIP_RS300_5835, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RS350_7835, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY), + /* 9100 IGP (A5) */ + CHIP_DEF(PCI_CHIP_RS300_5834, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP), + CHIP_DEF(PCI_CHIP_RS350_7834, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP), + /* Mobility 9200 (M9+) */ + CHIP_DEF(PCI_CHIP_RV280_5C61, RV280, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV280_5C63, RV280, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + /* 9200 */ + CHIP_DEF(PCI_CHIP_RV280_5960, RV280, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV280_5961, RV280, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV280_5962, RV280, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV280_5964, RV280, CHIP_HAS_CRTC2), + /* 9500 */ + CHIP_DEF(PCI_CHIP_R300_AD, R300, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R300_AE, R300, CHIP_HAS_CRTC2), + /* 9600TX / FireGL Z1 */ + CHIP_DEF(PCI_CHIP_R300_AF, R300, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R300_AG, R300, CHIP_HAS_CRTC2), + /* 9700/9500/Pro/FireGL X1 */ + CHIP_DEF(PCI_CHIP_R300_ND, R300, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R300_NE, R300, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R300_NF, R300, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R300_NG, R300, CHIP_HAS_CRTC2), + /* Mobility M10/M11 */ + CHIP_DEF(PCI_CHIP_RV350_NP, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV350_NQ, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV350_NR, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV350_NS, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV350_NT, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV350_NV, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + /* 9600/FireGL T2 */ + CHIP_DEF(PCI_CHIP_RV350_AP, RV350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV350_AQ, RV350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV360_AR, RV350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV350_AS, RV350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV350_AT, RV350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV350_AV, RV350, CHIP_HAS_CRTC2), + /* 9800/Pro/FileGL X2 */ + CHIP_DEF(PCI_CHIP_R350_AH, R350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R350_AI, R350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R350_AJ, R350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R350_AK, R350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R350_NH, R350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R350_NI, R350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R360_NJ, R350, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R350_NK, R350, CHIP_HAS_CRTC2), + /* Newer stuff */ + CHIP_DEF(PCI_CHIP_RV380_3E50, RV380, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV380_3E54, RV380, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV380_3150, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV380_3154, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV370_5B60, RV380, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV370_5B62, RV380, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV370_5B64, RV380, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV370_5B65, RV380, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_RV370_5460, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_RV370_5464, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_R420_JH, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R420_JI, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R420_JJ, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R420_JK, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R420_JL, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R420_JM, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R420_JN, R420, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY), + CHIP_DEF(PCI_CHIP_R420_JP, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_UH, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_UI, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_UJ, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_UK, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_UQ, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_UR, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_UT, R420, CHIP_HAS_CRTC2), + CHIP_DEF(PCI_CHIP_R423_5D57, R420, CHIP_HAS_CRTC2), + /* Original Radeon/7200 */ + CHIP_DEF(PCI_CHIP_RADEON_QD, RADEON, 0), + CHIP_DEF(PCI_CHIP_RADEON_QE, RADEON, 0), + CHIP_DEF(PCI_CHIP_RADEON_QF, RADEON, 0), + CHIP_DEF(PCI_CHIP_RADEON_QG, RADEON, 0), + { 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 + */ +static 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 }, + { CAP1_TRIG_CNTL, 0 }, +}; + +/* + * globals + */ + +static char *mode_option; +static char *monitor_layout; +static int noaccel = 0; +static int default_dynclk = -2; +static int nomodeset = 0; +static int ignore_edid = 0; +static int mirror = 0; +static int panel_yres = 0; +static int force_dfp = 0; +static int force_measure_pll = 0; +#ifdef CONFIG_MTRR +static int nomtrr = 0; +#endif + +/* + * prototypes + */ + + +#ifdef CONFIG_PPC_OF + +#ifdef CONFIG_PMAC_BACKLIGHT +static int radeon_set_backlight_enable(int on, int level, void *data); +static int radeon_set_backlight_level(int level, void *data); +static struct backlight_controller radeon_backlight_controller = { + radeon_set_backlight_enable, + radeon_set_backlight_level +}; +#endif /* CONFIG_PMAC_BACKLIGHT */ + +#endif /* CONFIG_PPC_OF */ + +static void radeon_unmap_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev) +{ + if (!rinfo->bios_seg) + return; + pci_unmap_rom(dev, rinfo->bios_seg); +} + +static int __devinit radeon_map_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev) +{ + void __iomem *rom; + u16 dptr; + u8 rom_type; + size_t rom_size; + + /* If this is a primary card, there is a shadow copy of the + * ROM somewhere in the first meg. We will just ignore the copy + * and use the ROM directly. + */ + + /* Fix from ATI for problem with Radeon hardware not leaving ROM enabled */ + unsigned int temp; + temp = INREG(MPP_TB_CONFIG); + temp &= 0x00ffffffu; + temp |= 0x04 << 24; + OUTREG(MPP_TB_CONFIG, temp); + temp = INREG(MPP_TB_CONFIG); + + rom = pci_map_rom(dev, &rom_size); + if (!rom) { + printk(KERN_ERR "radeonfb (%s): ROM failed to map\n", + pci_name(rinfo->pdev)); + return -ENOMEM; + } + + rinfo->bios_seg = rom; + + /* Very simple test to make sure it appeared */ + if (BIOS_IN16(0) != 0xaa55) { + printk(KERN_ERR "radeonfb (%s): Invalid ROM signature %x should be" + "0xaa55\n", pci_name(rinfo->pdev), 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 "radeonfb (%s): PCI DATA signature in ROM" + "incorrect: %08x\n", pci_name(rinfo->pdev), BIOS_IN32(dptr)); + goto anyway; + } + rom_type = BIOS_IN8(dptr + 0x14); + switch(rom_type) { + case 0: + printk(KERN_INFO "radeonfb: Found Intel x86 BIOS ROM Image\n"); + break; + case 1: + printk(KERN_INFO "radeonfb: Found Open Firmware ROM Image\n"); + goto failed; + case 2: + printk(KERN_INFO "radeonfb: Found HP PA-RISC ROM Image\n"); + goto failed; + default: + printk(KERN_INFO "radeonfb: Found unknown type %d ROM Image\n", rom_type); + goto failed; + } + anyway: + /* Locate the flat panel infos, do some sanity checking !!! */ + rinfo->fp_bios_start = BIOS_IN16(0x48); + return 0; + + failed: + rinfo->bios_seg = NULL; + radeon_unmap_ROM(rinfo, dev); + return -ENXIO; +} + +#ifdef CONFIG_X86 +static int __devinit radeon_find_mem_vbios(struct radeonfb_info *rinfo) +{ + /* 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; + void __iomem *rom_base = NULL; + + for(segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) { + rom_base = ioremap(segstart, 0x10000); + if (rom_base == NULL) + return -ENOMEM; + if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa) + break; + iounmap(rom_base); + rom_base = NULL; + } + if (rom_base == NULL) + return -ENXIO; + + /* Locate the flat panel infos, do some sanity checking !!! */ + rinfo->bios_seg = rom_base; + rinfo->fp_bios_start = BIOS_IN16(0x48); + + return 0; +} +#endif + +#ifdef CONFIG_PPC_OF +/* + * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device + * tree. Hopefully, ATI OF driver is kind enough to fill these + */ +static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo) +{ + struct device_node *dp = rinfo->of_node; + u32 *val; + + if (dp == NULL) + return -ENODEV; + val = (u32 *) get_property(dp, "ATY,RefCLK", NULL); + if (!val || !*val) { + printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n"); + return -EINVAL; + } + + rinfo->pll.ref_clk = (*val) / 10; + + val = (u32 *) get_property(dp, "ATY,SCLK", NULL); + if (val && *val) + rinfo->pll.sclk = (*val) / 10; + + val = (u32 *) get_property(dp, "ATY,MCLK", NULL); + if (val && *val) + rinfo->pll.mclk = (*val) / 10; + + return 0; +} +#endif /* CONFIG_PPC_OF */ + +/* + * Read PLL infos from chip registers + */ +static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo) +{ + unsigned char ppll_div_sel; + unsigned Ns, Nm, M; + unsigned sclk, mclk, tmp, ref_div; + int hTotal, vTotal, num, denom, m, n; + unsigned long long hz, vclk; + long xtal; + struct timeval start_tv, stop_tv; + long total_secs, total_usecs; + int i; + + /* Ugh, we cut interrupts, bad bad bad, but we want some precision + * here, so... --BenH + */ + + /* Flush PCI buffers ? */ + tmp = INREG(DEVICE_ID); + + local_irq_disable(); + + for(i=0; i<1000000; i++) + if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0) + break; + + do_gettimeofday(&start_tv); + + for(i=0; i<1000000; i++) + if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) != 0) + break; + + for(i=0; i<1000000; i++) + if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0) + break; + + do_gettimeofday(&stop_tv); + + local_irq_enable(); + + total_secs = stop_tv.tv_sec - start_tv.tv_sec; + if (total_secs > 10) + return -1; + total_usecs = stop_tv.tv_usec - start_tv.tv_usec; + total_usecs += total_secs * 1000000; + if (total_usecs < 0) + total_usecs = -total_usecs; + hz = 1000000/total_usecs; + + hTotal = ((INREG(CRTC_H_TOTAL_DISP) & 0x1ff) + 1) * 8; + vTotal = ((INREG(CRTC_V_TOTAL_DISP) & 0x3ff) + 1); + vclk = (long long)hTotal * (long long)vTotal * hz; + + switch((INPLL(PPLL_REF_DIV) & 0x30000) >> 16) { + case 0: + default: + num = 1; + denom = 1; + break; + case 1: + n = ((INPLL(M_SPLL_REF_FB_DIV) >> 16) & 0xff); + m = (INPLL(M_SPLL_REF_FB_DIV) & 0xff); + num = 2*n; + denom = 2*m; + break; + case 2: + n = ((INPLL(M_SPLL_REF_FB_DIV) >> 8) & 0xff); + m = (INPLL(M_SPLL_REF_FB_DIV) & 0xff); + num = 2*n; + denom = 2*m; + break; + } + + ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3; + radeon_pll_errata_after_index(rinfo); + + n = (INPLL(PPLL_DIV_0 + ppll_div_sel) & 0x7ff); + m = (INPLL(PPLL_REF_DIV) & 0x3ff); + + num *= n; + denom *= m; + + switch ((INPLL(PPLL_DIV_0 + ppll_div_sel) >> 16) & 0x7) { + case 1: + denom *= 2; + break; + case 2: + denom *= 4; + break; + case 3: + denom *= 8; + break; + case 4: + denom *= 3; + break; + case 6: + denom *= 6; + break; + case 7: + denom *= 12; + break; + } + + vclk *= denom; + do_div(vclk, 1000 * num); + xtal = vclk; + + if ((xtal > 26900) && (xtal < 27100)) + xtal = 2700; + else if ((xtal > 14200) && (xtal < 14400)) + xtal = 1432; + else if ((xtal > 29400) && (xtal < 29600)) + xtal = 2950; + else { + printk(KERN_WARNING "xtal calculation failed: %ld\n", xtal); + return -1; + } + + tmp = INPLL(M_SPLL_REF_FB_DIV); + ref_div = INPLL(PPLL_REF_DIV) & 0x3ff; + + Ns = (tmp & 0xff0000) >> 16; + Nm = (tmp & 0xff00) >> 8; + M = (tmp & 0xff); + sclk = round_div((2 * Ns * xtal), (2 * M)); + mclk = round_div((2 * Nm * xtal), (2 * M)); + + /* we're done, hopefully these are sane values */ + rinfo->pll.ref_clk = xtal; + rinfo->pll.ref_div = ref_div; + rinfo->pll.sclk = sclk; + rinfo->pll.mclk = mclk; + + return 0; +} + +/* + * Retreive PLL infos by different means (BIOS, Open Firmware, register probing...) + */ +static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo) +{ + /* + * In the case nothing works, these are defaults; they are mostly + * incomplete, however. It does provide ppll_max and _min values + * even for most other methods, however. + */ + switch (rinfo->chipset) { + case PCI_DEVICE_ID_ATI_RADEON_QW: + case PCI_DEVICE_ID_ATI_RADEON_QX: + rinfo->pll.ppll_max = 35000; + rinfo->pll.ppll_min = 12000; + rinfo->pll.mclk = 23000; + rinfo->pll.sclk = 23000; + rinfo->pll.ref_clk = 2700; + break; + case PCI_DEVICE_ID_ATI_RADEON_QL: + case PCI_DEVICE_ID_ATI_RADEON_QN: + case PCI_DEVICE_ID_ATI_RADEON_QO: + case PCI_DEVICE_ID_ATI_RADEON_Ql: + case PCI_DEVICE_ID_ATI_RADEON_BB: + rinfo->pll.ppll_max = 35000; + rinfo->pll.ppll_min = 12000; + rinfo->pll.mclk = 27500; + rinfo->pll.sclk = 27500; + rinfo->pll.ref_clk = 2700; + break; + case PCI_DEVICE_ID_ATI_RADEON_Id: + case PCI_DEVICE_ID_ATI_RADEON_Ie: + case PCI_DEVICE_ID_ATI_RADEON_If: + case PCI_DEVICE_ID_ATI_RADEON_Ig: + rinfo->pll.ppll_max = 35000; + rinfo->pll.ppll_min = 12000; + rinfo->pll.mclk = 25000; + rinfo->pll.sclk = 25000; + rinfo->pll.ref_clk = 2700; + break; + case PCI_DEVICE_ID_ATI_RADEON_ND: + case PCI_DEVICE_ID_ATI_RADEON_NE: + case PCI_DEVICE_ID_ATI_RADEON_NF: + case PCI_DEVICE_ID_ATI_RADEON_NG: + rinfo->pll.ppll_max = 40000; + rinfo->pll.ppll_min = 20000; + rinfo->pll.mclk = 27000; + rinfo->pll.sclk = 27000; + rinfo->pll.ref_clk = 2700; + break; + case PCI_DEVICE_ID_ATI_RADEON_QD: + case PCI_DEVICE_ID_ATI_RADEON_QE: + case PCI_DEVICE_ID_ATI_RADEON_QF: + case PCI_DEVICE_ID_ATI_RADEON_QG: + default: + rinfo->pll.ppll_max = 35000; + rinfo->pll.ppll_min = 12000; + rinfo->pll.mclk = 16600; + rinfo->pll.sclk = 16600; + rinfo->pll.ref_clk = 2700; + break; + } + rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; + + +#ifdef CONFIG_PPC_OF + /* + * Retreive PLL infos from Open Firmware first + */ + if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) { + printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n"); + goto found; + } +#endif /* CONFIG_PPC_OF */ + + /* + * Check out if we have an X86 which gave us some PLL informations + * and if yes, retreive them + */ + if (!force_measure_pll && rinfo->bios_seg) { + u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30); + + rinfo->pll.sclk = BIOS_IN16(pll_info_block + 0x08); + rinfo->pll.mclk = BIOS_IN16(pll_info_block + 0x0a); + rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 0x0e); + rinfo->pll.ref_div = BIOS_IN16(pll_info_block + 0x10); + rinfo->pll.ppll_min = BIOS_IN32(pll_info_block + 0x12); + rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 0x16); + + printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n"); + goto found; + } + + /* + * We didn't get PLL parameters from either OF or BIOS, we try to + * probe them + */ + if (radeon_probe_pll_params(rinfo) == 0) { + printk(KERN_INFO "radeonfb: Retreived PLL infos from registers\n"); + goto found; + } + + /* + * Fall back to already-set defaults... + */ + printk(KERN_INFO "radeonfb: Used default PLL infos\n"); + +found: + /* + * Some methods fail to retreive SCLK and MCLK values, we apply default + * settings in this case (200Mhz). If that really happne often, we could + * fetch from registers instead... + */ + if (rinfo->pll.mclk == 0) + rinfo->pll.mclk = 20000; + if (rinfo->pll.sclk == 0) + rinfo->pll.sclk = 20000; + + printk("radeonfb: Reference=%d.%02d MHz (RefDiv=%d) Memory=%d.%02d Mhz, System=%d.%02d MHz\n", + rinfo->pll.ref_clk / 100, rinfo->pll.ref_clk % 100, + rinfo->pll.ref_div, + rinfo->pll.mclk / 100, rinfo->pll.mclk % 100, + rinfo->pll.sclk / 100, rinfo->pll.sclk % 100); + printk("radeonfb: PLL min %d max %d\n", rinfo->pll.ppll_min, rinfo->pll.ppll_max); +} + +static int radeonfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct radeonfb_info *rinfo = info->par; + struct fb_var_screeninfo v; + int nom, den; + unsigned int pitch; + + if (radeon_match_mode(rinfo, &v, var)) + return -EINVAL; + + switch (v.bits_per_pixel) { + case 0 ... 8: + v.bits_per_pixel = 8; + break; + case 9 ... 16: + v.bits_per_pixel = 16; + break; + case 17 ... 24: +#if 0 /* Doesn't seem to work */ + v.bits_per_pixel = 24; + break; +#endif + return -EINVAL; + case 25 ... 32: + v.bits_per_pixel = 32; + break; + default: + return -EINVAL; + } + + switch (var_to_depth(&v)) { + case 8: + nom = den = 1; + v.red.offset = v.green.offset = v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 8; + v.transp.offset = v.transp.length = 0; + break; + case 15: + nom = 2; + den = 1; + v.red.offset = 10; + v.green.offset = 5; + v.blue.offset = 0; + v.red.length = v.green.length = v.blue.length = 5; + v.transp.offset = v.transp.length = 0; + break; + case 16: + nom = 2; + den = 1; + v.red.offset = 11; + v.green.offset = 5; + v.blue.offset = 0; + v.red.length = 5; + v.green.length = 6; + v.blue.length = 5; + v.transp.offset = v.transp.length = 0; + break; + case 24: + nom = 4; + den = 1; + v.red.offset = 16; + v.green.offset = 8; + v.blue.offset = 0; + v.red.length = v.blue.length = v.green.length = 8; + v.transp.offset = v.transp.length = 0; + break; + case 32: + nom = 4; + den = 1; + v.red.offset = 16; + v.green.offset = 8; + v.blue.offset = 0; + v.red.length = v.blue.length = v.green.length = 8; + v.transp.offset = 24; + v.transp.length = 8; + break; + default: + printk ("radeonfb: mode %dx%dx%d rejected, color depth invalid\n", + var->xres, var->yres, var->bits_per_pixel); + return -EINVAL; + } + + if (v.yres_virtual < v.yres) + v.yres_virtual = v.yres; + if (v.xres_virtual < v.xres) + v.xres_virtual = v.xres; + + + /* XXX I'm adjusting xres_virtual to the pitch, that may help XFree + * with some panels, though I don't quite like this solution + */ + if (rinfo->info->flags & FBINFO_HWACCEL_DISABLED) { + v.xres_virtual = v.xres_virtual & ~7ul; + } else { + pitch = ((v.xres_virtual * ((v.bits_per_pixel + 1) / 8) + 0x3f) + & ~(0x3f)) >> 6; + v.xres_virtual = (pitch << 6) / ((v.bits_per_pixel + 1) / 8); + } + + if (((v.xres_virtual * v.yres_virtual * nom) / den) > rinfo->mapped_vram) + return -EINVAL; + + if (v.xres_virtual < v.xres) + v.xres = v.xres_virtual; + + if (v.xoffset < 0) + v.xoffset = 0; + if (v.yoffset < 0) + v.yoffset = 0; + + if (v.xoffset > v.xres_virtual - v.xres) + v.xoffset = v.xres_virtual - v.xres - 1; + + if (v.yoffset > v.yres_virtual - v.yres) + v.yoffset = v.yres_virtual - v.yres - 1; + + v.red.msb_right = v.green.msb_right = v.blue.msb_right = + v.transp.offset = v.transp.length = + v.transp.msb_right = 0; + + memcpy(var, &v, sizeof(v)); + + return 0; +} + + +static int radeonfb_pan_display (struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct radeonfb_info *rinfo = info->par; + + if ((var->xoffset + var->xres > var->xres_virtual) + || (var->yoffset + var->yres > var->yres_virtual)) + return -EINVAL; + + if (rinfo->asleep) + return 0; + + radeon_fifo_wait(2); + OUTREG(CRTC_OFFSET, ((var->yoffset * var->xres_virtual + var->xoffset) + * var->bits_per_pixel / 8) & ~7); + return 0; +} + + +static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, struct fb_info *info) +{ + struct radeonfb_info *rinfo = info->par; + unsigned int tmp; + u32 value = 0; + int rc; + + switch (cmd) { + /* + * TODO: set mirror accordingly for non-Mobility chipsets with 2 CRTC's + * and do something better using 2nd CRTC instead of just hackish + * routing to second output + */ + case FBIO_RADEON_SET_MIRROR: + if (!rinfo->is_mobility) + return -EINVAL; + + rc = get_user(value, (__u32 __user *)arg); + + if (rc) + return rc; + + radeon_fifo_wait(2); + if (value & 0x01) { + tmp = INREG(LVDS_GEN_CNTL); + + tmp |= (LVDS_ON | LVDS_BLON); + } else { + tmp = INREG(LVDS_GEN_CNTL); + + tmp &= ~(LVDS_ON | LVDS_BLON); + } + + OUTREG(LVDS_GEN_CNTL, tmp); + + if (value & 0x02) { + tmp = INREG(CRTC_EXT_CNTL); + tmp |= CRTC_CRT_ON; + + mirror = 1; + } else { + tmp = INREG(CRTC_EXT_CNTL); + tmp &= ~CRTC_CRT_ON; + + mirror = 0; + } + + OUTREG(CRTC_EXT_CNTL, tmp); + + return 0; + case FBIO_RADEON_GET_MIRROR: + if (!rinfo->is_mobility) + return -EINVAL; + + tmp = INREG(LVDS_GEN_CNTL); + if ((LVDS_ON | LVDS_BLON) & tmp) + value |= 0x01; + + tmp = INREG(CRTC_EXT_CNTL); + if (CRTC_CRT_ON & tmp) + value |= 0x02; + + return put_user(value, (__u32 __user *)arg); + default: + return -EINVAL; + } + + return -EINVAL; +} + + +int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch) +{ + u32 val; + u32 tmp_pix_clks; + int unblank = 0; + + if (rinfo->lock_blank) + return 0; + + radeon_engine_idle(); + + val = INREG(CRTC_EXT_CNTL); + val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS | + CRTC_VSYNC_DIS); + switch (blank) { + case FB_BLANK_VSYNC_SUSPEND: + val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS); + break; + case FB_BLANK_HSYNC_SUSPEND: + val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS); + break; + case FB_BLANK_POWERDOWN: + val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | + CRTC_HSYNC_DIS); + break; + case FB_BLANK_NORMAL: + val |= CRTC_DISPLAY_DIS; + break; + case FB_BLANK_UNBLANK: + default: + unblank = 1; + } + OUTREG(CRTC_EXT_CNTL, val); + + + switch (rinfo->mon1_type) { + case MT_DFP: + if (unblank) + OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN), + ~(FP_FPON | FP_TMDS_EN)); + else { + if (mode_switch || blank == FB_BLANK_NORMAL) + break; + OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN)); + } + break; + case MT_LCD: + del_timer_sync(&rinfo->lvds_timer); + val = INREG(LVDS_GEN_CNTL); + if (unblank) { + u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON + | LVDS_EN | (rinfo->init_state.lvds_gen_cntl + & (LVDS_DIGON | LVDS_BL_MOD_EN)); + if ((val ^ target_val) == LVDS_DISPLAY_DIS) + OUTREG(LVDS_GEN_CNTL, target_val); + else if ((val ^ target_val) != 0) { + OUTREG(LVDS_GEN_CNTL, target_val + & ~(LVDS_ON | LVDS_BL_MOD_EN)); + rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; + rinfo->init_state.lvds_gen_cntl |= + target_val & LVDS_STATE_MASK; + if (mode_switch) { + radeon_msleep(rinfo->panel_info.pwr_delay); + OUTREG(LVDS_GEN_CNTL, target_val); + } + else { + rinfo->pending_lvds_gen_cntl = target_val; + mod_timer(&rinfo->lvds_timer, + jiffies + + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); + } + } + } else { + val |= LVDS_DISPLAY_DIS; + OUTREG(LVDS_GEN_CNTL, val); + + /* We don't do a full switch-off on a simple mode switch */ + if (mode_switch || blank == FB_BLANK_NORMAL) + break; + + /* Asic bug, when turning off LVDS_ON, we have to make sure + * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off + */ + tmp_pix_clks = INPLL(PIXCLKS_CNTL); + if (rinfo->is_mobility || rinfo->is_IGP) + OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); + val &= ~(LVDS_BL_MOD_EN); + OUTREG(LVDS_GEN_CNTL, val); + udelay(100); + val &= ~(LVDS_ON | LVDS_EN); + OUTREG(LVDS_GEN_CNTL, val); + val &= ~LVDS_DIGON; + rinfo->pending_lvds_gen_cntl = val; + mod_timer(&rinfo->lvds_timer, + jiffies + + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); + rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK; + rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK; + if (rinfo->is_mobility || rinfo->is_IGP) + OUTPLL(PIXCLKS_CNTL, tmp_pix_clks); + } + break; + case MT_CRT: + // todo: powerdown DAC + default: + break; + } + + /* let fbcon do a soft blank for us */ + return (blank == FB_BLANK_NORMAL) ? -EINVAL : 0; +} + +static int radeonfb_blank (int blank, struct fb_info *info) +{ + struct radeonfb_info *rinfo = info->par; + + if (rinfo->asleep) + return 0; + + return radeon_screen_blank(rinfo, blank, 0); +} + +static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + struct radeonfb_info *rinfo = info->par; + u32 pindex; + unsigned int i; + + if (regno > 255) + return 1; + + red >>= 8; + green >>= 8; + blue >>= 8; + rinfo->palette[regno].red = red; + rinfo->palette[regno].green = green; + rinfo->palette[regno].blue = blue; + + /* default */ + pindex = regno; + + if (!rinfo->asleep) { + u32 dac_cntl2, vclk_cntl = 0; + + radeon_fifo_wait(9); + if (rinfo->is_mobility) { + vclk_cntl = INPLL(VCLK_ECP_CNTL); + OUTPLL(VCLK_ECP_CNTL, vclk_cntl & ~PIXCLK_DAC_ALWAYS_ONb); + } + + /* Make sure we are on first palette */ + if (rinfo->has_CRTC2) { + dac_cntl2 = INREG(DAC_CNTL2); + dac_cntl2 &= ~DAC2_PALETTE_ACCESS_CNTL; + OUTREG(DAC_CNTL2, dac_cntl2); |