From b4eca0a829719fc1b744168217dcc155ba4ecc4a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 10 Feb 2005 16:03:30 -0800 Subject: [PATCH] [PATCH] radeonfb update It adds the sleep support for newer powermacs, improve power saving on some laptops, makes use of the new fbdev modelist management routines, and fixes a few backlight related issues. I tested it on a thinkpad T30 and a few PPC boxes with success. It should be less invasive than the previous one (I don't try to restore the mode on exit, that is what breaks the thinkpad and possibly other stuffs that boot in VGA text mode), plus fixed a couple of bugs in the mode detection code. I also reverted the memory map fix on ppc since it doesn't work properly on some recent laptops where the firmware sets a tiled display. I'll rework that completely to update the memory map as part of the mode setting later. That should fix various issues when switching with X/DRI on x86. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/aty/ati_ids.h | 53 +- drivers/video/aty/radeon_base.c | 614 ++++--- drivers/video/aty/radeon_monitor.c | 146 +- drivers/video/aty/radeon_pm.c | 2614 +++++++++++++++++++++++----- drivers/video/aty/radeonfb.h | 230 ++- drivers/video/radeonfb.c | 2 +- include/video/radeon.h | 476 ++--- 7 files changed, 3129 insertions(+), 1006 deletions(-) diff --git a/drivers/video/aty/ati_ids.h b/drivers/video/aty/ati_ids.h index e4171869f344..13321c689cf6 100644 --- a/drivers/video/aty/ati_ids.h +++ b/drivers/video/aty/ati_ids.h @@ -4,6 +4,18 @@ * radeonfb */ +#define PCI_CHIP_RV380_3150 0x3150 +#define PCI_CHIP_RV380_3151 0x3151 +#define PCI_CHIP_RV380_3152 0x3152 +#define PCI_CHIP_RV380_3153 0x3153 +#define PCI_CHIP_RV380_3154 0x3154 +#define PCI_CHIP_RV380_3156 0x3156 +#define PCI_CHIP_RV380_3E50 0x3E50 +#define PCI_CHIP_RV380_3E51 0x3E51 +#define PCI_CHIP_RV380_3E52 0x3E52 +#define PCI_CHIP_RV380_3E53 0x3E53 +#define PCI_CHIP_RV380_3E54 0x3E54 +#define PCI_CHIP_RV380_3E56 0x3E56 #define PCI_CHIP_RS100_4136 0x4136 #define PCI_CHIP_RS200_4137 0x4137 #define PCI_CHIP_R300_AD 0x4144 @@ -52,6 +64,14 @@ #define PCI_CHIP_RV250_Ie 0x4965 #define PCI_CHIP_RV250_If 0x4966 #define PCI_CHIP_RV250_Ig 0x4967 +#define PCI_CHIP_R420_JH 0x4A48 +#define PCI_CHIP_R420_JI 0x4A49 +#define PCI_CHIP_R420_JJ 0x4A4A +#define PCI_CHIP_R420_JK 0x4A4B +#define PCI_CHIP_R420_JL 0x4A4C +#define PCI_CHIP_R420_JM 0x4A4D +#define PCI_CHIP_R420_JN 0x4A4E +#define PCI_CHIP_R420_JP 0x4A50 #define PCI_CHIP_MACH64LB 0x4C42 #define PCI_CHIP_MACH64LD 0x4C44 #define PCI_CHIP_RAGE128LE 0x4C45 @@ -73,6 +93,7 @@ #define PCI_CHIP_RV250_Le 0x4C65 #define PCI_CHIP_RV250_Lf 0x4C66 #define PCI_CHIP_RV250_Lg 0x4C67 +#define PCI_CHIP_RV250_Ln 0x4C6E #define PCI_CHIP_RAGE128MF 0x4D46 #define PCI_CHIP_RAGE128ML 0x4D4C #define PCI_CHIP_R300_ND 0x4E44 @@ -148,6 +169,21 @@ #define PCI_CHIP_RAGE128TS 0x5453 #define PCI_CHIP_RAGE128TT 0x5454 #define PCI_CHIP_RAGE128TU 0x5455 +#define PCI_CHIP_RV370_5460 0x5460 +#define PCI_CHIP_RV370_5461 0x5461 +#define PCI_CHIP_RV370_5462 0x5462 +#define PCI_CHIP_RV370_5463 0x5463 +#define PCI_CHIP_RV370_5464 0x5464 +#define PCI_CHIP_RV370_5465 0x5465 +#define PCI_CHIP_RV370_5466 0x5466 +#define PCI_CHIP_RV370_5467 0x5467 +#define PCI_CHIP_R423_UH 0x5548 +#define PCI_CHIP_R423_UI 0x5549 +#define PCI_CHIP_R423_UJ 0x554A +#define PCI_CHIP_R423_UK 0x554B +#define PCI_CHIP_R423_UQ 0x5551 +#define PCI_CHIP_R423_UR 0x5552 +#define PCI_CHIP_R423_UT 0x5554 #define PCI_CHIP_MACH64VT 0x5654 #define PCI_CHIP_MACH64VU 0x5655 #define PCI_CHIP_MACH64VV 0x5656 @@ -155,14 +191,21 @@ #define PCI_CHIP_RS300_5835 0x5835 #define PCI_CHIP_RS300_5836 0x5836 #define PCI_CHIP_RS300_5837 0x5837 +#define PCI_CHIP_RV370_5B60 0x5B60 +#define PCI_CHIP_RV370_5B61 0x5B61 +#define PCI_CHIP_RV370_5B62 0x5B62 +#define PCI_CHIP_RV370_5B63 0x5B63 +#define PCI_CHIP_RV370_5B64 0x5B64 +#define PCI_CHIP_RV370_5B65 0x5B65 +#define PCI_CHIP_RV370_5B66 0x5B66 +#define PCI_CHIP_RV370_5B67 0x5B67 #define PCI_CHIP_RV280_5960 0x5960 #define PCI_CHIP_RV280_5961 0x5961 #define PCI_CHIP_RV280_5962 0x5962 -#define PCI_CHIP_RV280_5963 0x5963 #define PCI_CHIP_RV280_5964 0x5964 -#define PCI_CHIP_RV280_5968 0x5968 -#define PCI_CHIP_RV280_5969 0x5969 -#define PCI_CHIP_RV280_596A 0x596A -#define PCI_CHIP_RV280_596B 0x596B #define PCI_CHIP_RV280_5C61 0x5C61 #define PCI_CHIP_RV280_5C63 0x5C63 +#define PCI_CHIP_R423_5D57 0x5D57 +#define PCI_CHIP_RS350_7834 0x7834 +#define PCI_CHIP_RS350_7835 0x7835 + diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index e9f12f4f6477..60917ba049bc 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -1,5 +1,7 @@ + /* - * drivers/video/radeonfb.c + * drivers/video/aty/radeon_base.c + * * framebuffer driver for ATI Radeon chipset video boards * * Copyright 2003 Ben. Herrenschmidt @@ -75,7 +77,6 @@ #ifdef CONFIG_PPC_OF -#include #include #include "../macmodes.h" @@ -150,8 +151,10 @@ static struct pci_device_id radeonfb_pci_table[] = { 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), @@ -194,6 +197,33 @@ static struct pci_device_id radeonfb_pci_table[] = { 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), @@ -233,6 +263,7 @@ static reg_val common_regs[] = { 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; @@ -290,7 +321,8 @@ static int __devinit radeon_map_ROM(struct radeonfb_info *rinfo, struct pci_dev rom = pci_map_rom(dev, &rom_size); if (!rom) { - printk(KERN_ERR "radeonfb: ROM failed to map\n"); + printk(KERN_ERR "radeonfb (%s): ROM failed to map\n", + pci_name(rinfo->pdev)); return -ENOMEM; } @@ -298,8 +330,8 @@ static int __devinit radeon_map_ROM(struct radeonfb_info *rinfo, struct pci_dev /* Very simple test to make sure it appeared */ if (BIOS_IN16(0) != 0xaa55) { - printk(KERN_ERR "radeonfb: Invalid ROM signature %x should be 0xaa55\n", - BIOS_IN16(0)); + 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 */ @@ -330,8 +362,8 @@ static int __devinit radeon_map_ROM(struct radeonfb_info *rinfo, struct pci_dev * } pci_data_t; */ if (BIOS_IN32(dptr) != (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) { - printk(KERN_WARNING "radeonfb: PCI DATA signature in ROM incorrect: %08x\n", - BIOS_IN32(dptr)); + 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); @@ -398,14 +430,11 @@ static int __devinit radeon_find_mem_vbios(struct radeonfb_info *rinfo) */ static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo) { - struct device_node *dp; + struct device_node *dp = rinfo->of_node; u32 *val; - dp = pci_device_to_OF_node(rinfo->pdev); - if (dp == NULL) { - printk(KERN_WARNING "radeonfb: Cannot match card to OF node !\n"); + 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"); @@ -488,20 +517,20 @@ static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo) denom = 1; break; case 1: - n = ((INPLL(X_MPLL_REF_FB_DIV) >> 16) & 0xff); - m = (INPLL(X_MPLL_REF_FB_DIV) & 0xff); + 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(X_MPLL_REF_FB_DIV) >> 8) & 0xff); - m = (INPLL(X_MPLL_REF_FB_DIV) & 0xff); + 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 = INREG(CLOCK_CNTL_INDEX + 1) & 0x3; + ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3; n = (INPLL(PPLL_DIV_0 + ppll_div_sel) & 0x7ff); m = (INPLL(PPLL_REF_DIV) & 0x3ff); @@ -545,7 +574,7 @@ static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo) return -1; } - tmp = INPLL(X_MPLL_REF_FB_DIV); + tmp = INPLL(M_SPLL_REF_FB_DIV); ref_div = INPLL(PPLL_REF_DIV) & 0x3ff; Ns = (tmp & 0xff0000) >> 16; @@ -625,7 +654,7 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo) rinfo->pll.ref_clk = 2700; break; } - rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & 0x3ff; + rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; #ifdef CONFIG_PPC_OF @@ -906,10 +935,11 @@ static int radeonfb_ioctl (struct inode *inode, struct file *file, unsigned int } -static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode_switch) +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; @@ -920,9 +950,6 @@ static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS | CRTC_VSYNC_DIS); switch (blank) { - case FB_BLANK_UNBLANK: - case FB_BLANK_NORMAL: - break; case FB_BLANK_VSYNC_SUSPEND: val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS); break; @@ -933,42 +960,51 @@ static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode 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 (mode_switch) - break; - if (blank == FB_BLANK_UNBLANK || - blank == FB_BLANK_NORMAL) + if (unblank) OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN), ~(FP_FPON | FP_TMDS_EN)); - else + 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 (blank == FB_BLANK_UNBLANK || - blank == FB_BLANK_NORMAL) { + if (unblank) { u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON - | LVDS_ON | (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON); + | 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) { - del_timer_sync(&rinfo->lvds_timer); - OUTREG(LVDS_GEN_CNTL, target_val & ~LVDS_ON); + 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; + rinfo->init_state.lvds_gen_cntl |= + target_val & LVDS_STATE_MASK; if (mode_switch) { - msleep(rinfo->panel_info.pwr_delay); + 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)); + jiffies + + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); } } } else { @@ -976,7 +1012,7 @@ static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode OUTREG(LVDS_GEN_CNTL, val); /* We don't do a full switch-off on a simple mode switch */ - if (mode_switch) + if (mode_switch || blank == FB_BLANK_NORMAL) break; /* Asic bug, when turning off LVDS_ON, we have to make sure @@ -985,8 +1021,16 @@ static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode tmp_pix_clks = INPLL(PIXCLKS_CNTL); if (rinfo->is_mobility || rinfo->is_IGP) OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); - val &= ~(LVDS_BLON | LVDS_ON); + 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) @@ -1003,16 +1047,14 @@ static int radeon_screen_blank (struct radeonfb_info *rinfo, int blank, int mode return (blank == FB_BLANK_NORMAL) ? -EINVAL : 0; } -int radeonfb_blank (int blank, struct fb_info *info) +static int radeonfb_blank (int blank, struct fb_info *info) { struct radeonfb_info *rinfo = info->par; if (rinfo->asleep) return 0; - radeon_screen_blank(rinfo, blank, 0); - - return 0; + return radeon_screen_blank(rinfo, blank, 0); } static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green, @@ -1097,7 +1139,7 @@ static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green, } -static void radeon_save_state (struct radeonfb_info *rinfo, struct radeon_regs *save) +void radeon_save_state (struct radeonfb_info *rinfo, struct radeon_regs *save) { /* CRTC regs */ save->crtc_gen_cntl = INREG(CRTC_GEN_CNTL); @@ -1121,8 +1163,14 @@ static void radeon_save_state (struct radeonfb_info *rinfo, struct radeon_regs * save->fp_vert_stretch = INREG(FP_VERT_STRETCH); save->lvds_gen_cntl = INREG(LVDS_GEN_CNTL); save->lvds_pll_cntl = INREG(LVDS_PLL_CNTL); - save->tmds_crc = INREG(TMDS_CRC); save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL); + save->tmds_crc = INREG(TMDS_CRC); + save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL); save->vclk_ecp_cntl = INPLL(VCLK_ECP_CNTL); + + /* PLL regs */ + save->clk_cntl_index = INREG(CLOCK_CNTL_INDEX) & ~0x3f; + save->ppll_div_3 = INPLL(PPLL_DIV_3); + save->ppll_ref_div = INPLL(PPLL_REF_DIV); } @@ -1134,19 +1182,22 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg /* Workaround from XFree */ if (rinfo->is_mobility) { - /* A temporal workaround for the occational blanking on certain laptop panels. - This appears to related to the PLL divider registers (fail to lock?). - It occurs even when all dividers are the same with their old settings. - In this case we really don't need to fiddle with PLL registers. - By doing this we can avoid the blanking problem with some panels. - */ + /* A temporal workaround for the occational blanking on certain laptop + * panels. This appears to related to the PLL divider registers + * (fail to lock?). It occurs even when all dividers are the same + * with their old settings. In this case we really don't need to + * fiddle with PLL registers. By doing this we can avoid the blanking + * problem with some panels. + */ if ((mode->ppll_ref_div == (INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK)) && (mode->ppll_div_3 == (INPLL(PPLL_DIV_3) & (PPLL_POST3_DIV_MASK | PPLL_FB3_DIV_MASK)))) { - /* We still have to force a switch to PPLL div 3 thanks to + /* We still have to force a switch to selected PPLL div thanks to * an XFree86 driver bug which will switch it away in some cases * even when using UseFDev */ - OUTREGP(CLOCK_CNTL_INDEX, PPLL_DIV_SEL_MASK, ~PPLL_DIV_SEL_MASK); + OUTREGP(CLOCK_CNTL_INDEX, + mode->clk_cntl_index & PPLL_DIV_SEL_MASK, + ~PPLL_DIV_SEL_MASK); return; } } @@ -1159,8 +1210,10 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN, ~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); - /* Switch to PPLL div 3 */ - OUTREGP(CLOCK_CNTL_INDEX, PPLL_DIV_SEL_MASK, ~PPLL_DIV_SEL_MASK); + /* Switch to selected PPLL divider */ + OUTREGP(CLOCK_CNTL_INDEX, + mode->clk_cntl_index & PPLL_DIV_SEL_MASK, + ~PPLL_DIV_SEL_MASK); /* Set PPLL ref. div */ if (rinfo->family == CHIP_FAMILY_R300 || @@ -1205,7 +1258,7 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg ~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN)); /* We may want some locking ... oh well */ - msleep(5); + radeon_msleep(5); /* Switch back VCLK source to PPLL */ OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK); @@ -1218,21 +1271,17 @@ static void radeon_lvds_timer_func(unsigned long data) { struct radeonfb_info *rinfo = (struct radeonfb_info *)data; - radeon_fifo_wait(3); + radeon_engine_idle(); OUTREG(LVDS_GEN_CNTL, rinfo->pending_lvds_gen_cntl); - if (rinfo->pending_pixclks_cntl) { - OUTPLL(PIXCLKS_CNTL, rinfo->pending_pixclks_cntl); - rinfo->pending_pixclks_cntl = 0; - } } /* * Apply a video mode. This will apply the whole register set, including * the PLL registers, to the card */ -static void radeon_write_mode (struct radeonfb_info *rinfo, - struct radeon_regs *mode) +void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode, + int regs_only) { int i; int primary_mon = PRIMARY_MONITOR(rinfo); @@ -1240,10 +1289,8 @@ static void radeon_write_mode (struct radeonfb_info *rinfo, if (nomodeset) return; - del_timer_sync(&rinfo->lvds_timer); - - radeon_screen_blank(rinfo, FB_BLANK_POWERDOWN, 1); - msleep(100); + if (!regs_only) + radeon_screen_blank(rinfo, FB_BLANK_NORMAL, 0); radeon_fifo_wait(31); for (i=0; i<10; i++) @@ -1285,7 +1332,8 @@ static void radeon_write_mode (struct radeonfb_info *rinfo, OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl); } - radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 1); + if (!regs_only) + radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 0); radeon_fifo_wait(2); OUTPLL(VCLK_ECP_CNTL, mode->vclk_ecp_cntl); @@ -1386,6 +1434,16 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs break; } + /* If we fall through the bottom, try the "default value" + given by the terminal post_div->bitvalue */ + if ( !post_div->divider ) { + post_div = &post_divs[post_div->bitvalue]; + pll_output_freq = post_div->divider * freq; + } + RTRACE("ref_div = %d, ref_clk = %d, output_freq = %d\n", + rinfo->pll.ref_div, rinfo->pll.ref_clk, + pll_output_freq); + /* If we fall through the bottom, try the "default value" given by the terminal post_div->bitvalue */ if ( !post_div->divider ) { @@ -1406,22 +1464,27 @@ static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs RTRACE("ppll_div_3 = 0x%x\n", regs->ppll_div_3); } -int radeonfb_set_par(struct fb_info *info) +static int radeonfb_set_par(struct fb_info *info) { struct radeonfb_info *rinfo = info->par; struct fb_var_screeninfo *mode = &info->var; - struct radeon_regs newmode; + struct radeon_regs *newmode; int hTotal, vTotal, hSyncStart, hSyncEnd, hSyncPol, vSyncStart, vSyncEnd, vSyncPol, cSync; u8 hsync_adj_tab[] = {0, 0x12, 9, 9, 6, 5}; u8 hsync_fudge_fp[] = {2, 2, 0, 0, 5, 5}; u32 sync, h_sync_pol, v_sync_pol, dotClock, pixClock; int i, freq; - int format = 0; + int format = 0; int nopllcalc = 0; int hsync_start, hsync_fudge, bytpp, hsync_wid, vsync_wid; int primary_mon = PRIMARY_MONITOR(rinfo); int depth = var_to_depth(mode); + int use_rmx = 0; + + newmode = kmalloc(sizeof(struct radeon_regs), GFP_KERNEL); + if (!newmode) + return -ENOMEM; /* We always want engine to be idle on a mode switch, even * if we won't actually change the mode @@ -1462,9 +1525,9 @@ int radeonfb_set_par(struct fb_info *info) if (rinfo->panel_info.use_bios_dividers) { nopllcalc = 1; - newmode.ppll_div_3 = rinfo->panel_info.fbk_divider | + newmode->ppll_div_3 = rinfo->panel_info.fbk_divider | (rinfo->panel_info.post_divider << 16); - newmode.ppll_ref_div = rinfo->panel_info.ref_divider; + newmode->ppll_ref_div = rinfo->panel_info.ref_divider; } } dotClock = 1000000000 / pixClock; @@ -1502,38 +1565,38 @@ int radeonfb_set_par(struct fb_info *info) hsync_start = hSyncStart - 8 + hsync_fudge; - newmode.crtc_gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | + newmode->crtc_gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | (format << 8); /* Clear auto-center etc... */ - newmode.crtc_more_cntl = rinfo->init_state.crtc_more_cntl; - newmode.crtc_more_cntl &= 0xfffffff0; + newmode->crtc_more_cntl = rinfo->init_state.crtc_more_cntl; + newmode->crtc_more_cntl &= 0xfffffff0; if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) { - newmode.crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN; + newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN; if (mirror) - newmode.crtc_ext_cntl |= CRTC_CRT_ON; + newmode->crtc_ext_cntl |= CRTC_CRT_ON; - newmode.crtc_gen_cntl &= ~(CRTC_DBL_SCAN_EN | + newmode->crtc_gen_cntl &= ~(CRTC_DBL_SCAN_EN | CRTC_INTERLACE_EN); } else { - newmode.crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN | + newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN | CRTC_CRT_ON; } - newmode.dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN | + newmode->dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN | DAC_8BIT_EN; - newmode.crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) | + newmode->crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) | (((mode->xres / 8) - 1) << 16)); - newmode.crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) | + newmode->crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) | (hsync_wid << 16) | (h_sync_pol << 23)); - newmode.crtc_v_total_disp = ((vTotal - 1) & 0xffff) | + newmode->crtc_v_total_disp = ((vTotal - 1) & 0xffff) | ((mode->yres - 1) << 16); - newmode.crtc_v_sync_strt_wid = (((vSyncStart - 1) & 0xfff) | + newmode->crtc_v_sync_strt_wid = (((vSyncStart - 1) & 0xfff) | (vsync_wid << 16) | (v_sync_pol << 23)); if (!(info->flags & FBINFO_HWACCEL_DISABLED)) { @@ -1542,18 +1605,18 @@ int radeonfb_set_par(struct fb_info *info) & ~(0x3f)) >> 6; /* Then, re-multiply it to get the CRTC pitch */ - newmode.crtc_pitch = (rinfo->pitch << 3) / ((mode->bits_per_pixel + 1) / 8); + newmode->crtc_pitch = (rinfo->pitch << 3) / ((mode->bits_per_pixel + 1) / 8); } else - newmode.crtc_pitch = (mode->xres_virtual >> 3); + newmode->crtc_pitch = (mode->xres_virtual >> 3); - newmode.crtc_pitch |= (newmode.crtc_pitch << 16); + newmode->crtc_pitch |= (newmode->crtc_pitch << 16); /* * It looks like recent chips have a problem with SURFACE_CNTL, * setting SURF_TRANSLATION_DIS completely disables the * swapper as well, so we leave it unset now. */ - newmode.surface_cntl = 0; + newmode->surface_cntl = 0; #if defined(__BIG_ENDIAN) @@ -1563,28 +1626,28 @@ int radeonfb_set_par(struct fb_info *info) */ switch (mode->bits_per_pixel) { case 16: - newmode.surface_cntl |= NONSURF_AP0_SWP_16BPP; - newmode.surface_cntl |= NONSURF_AP1_SWP_16BPP; + newmode->surface_cntl |= NONSURF_AP0_SWP_16BPP; + newmode->surface_cntl |= NONSURF_AP1_SWP_16BPP; break; case 24: case 32: - newmode.surface_cntl |= NONSURF_AP0_SWP_32BPP; - newmode.surface_cntl |= NONSURF_AP1_SWP_32BPP; + newmode->surface_cntl |= NONSURF_AP0_SWP_32BPP; + newmode->surface_cntl |= NONSURF_AP1_SWP_32BPP; break; } #endif /* Clear surface registers */ for (i=0; i<8; i++) { - newmode.surf_lower_bound[i] = 0; - newmode.surf_upper_bound[i] = 0x1f; - newmode.surf_info[i] = 0; + newmode->surf_lower_bound[i] = 0; + newmode->surf_upper_bound[i] = 0x1f; + newmode->surf_info[i] = 0; } RTRACE("h_total_disp = 0x%x\t hsync_strt_wid = 0x%x\n", - newmode.crtc_h_total_disp, newmode.crtc_h_sync_strt_wid); + newmode->crtc_h_total_disp, newmode->crtc_h_sync_strt_wid); RTRACE("v_total_disp = 0x%x\t vsync_strt_wid = 0x%x\n", - newmode.crtc_v_total_disp, newmode.crtc_v_sync_strt_wid); + newmode->crtc_v_total_disp, newmode->crtc_v_sync_strt_wid); rinfo->bpp = mode->bits_per_pixel; rinfo->depth = depth; @@ -1592,10 +1655,14 @@ int radeonfb_set_par(struct fb_info *info) RTRACE("pixclock = %lu\n", (unsigned long)pixClock); RTRACE("freq = %lu\n", (unsigned long)freq); + /* We use PPLL_DIV_3 */ + newmode->clk_cntl_index = 0x300; + + /* Calculate PPLL value if necessary */ if (!nopllcalc) - radeon_calc_pll_regs(rinfo, &newmode, freq); + radeon_calc_pll_regs(rinfo, newmode, freq); - newmode.vclk_ecp_cntl = rinfo->init_state.vclk_ecp_cntl; + newmode->vclk_ecp_cntl = rinfo->init_state.vclk_ecp_cntl; if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) { unsigned int hRatio, vRatio; @@ -1605,35 +1672,37 @@ int radeonfb_set_par(struct fb_info *info) if (mode->yres > rinfo->panel_info.yres) mode->yres = rinfo->panel_info.yres; - newmode.fp_horz_stretch = (((rinfo->panel_info.xres / 8) - 1) + newmode->fp_horz_stretch = (((rinfo->panel_info.xres / 8) - 1) << HORZ_PANEL_SHIFT); - newmode.fp_vert_stretch = ((rinfo->panel_info.yres - 1) + newmode->fp_vert_stretch = ((rinfo->panel_info.yres - 1) << VERT_PANEL_SHIFT); if (mode->xres != rinfo->panel_info.xres) { hRatio = round_div(mode->xres * HORZ_STRETCH_RATIO_MAX, rinfo->panel_info.xres); - newmode.fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) | - (newmode.fp_horz_stretch & + newmode->fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) | + (newmode->fp_horz_stretch & (HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH | HORZ_AUTO_RATIO_INC))); - newmode.fp_horz_stretch |= (HORZ_STRETCH_BLEND | + newmode->fp_horz_stretch |= (HORZ_STRETCH_BLEND | HORZ_STRETCH_ENABLE); + use_rmx = 1; } - newmode.fp_horz_stretch &= ~HORZ_AUTO_RATIO; + newmode->fp_horz_stretch &= ~HORZ_AUTO_RATIO; if (mode->yres != rinfo->panel_info.yres) { vRatio = round_div(mode->yres * VERT_STRETCH_RATIO_MAX, rinfo->panel_info.yres); - newmode.fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) | - (newmode.fp_vert_stretch & + newmode->fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) | + (newmode->fp_vert_stretch & (VERT_PANEL_SIZE | VERT_STRETCH_RESERVED))); - newmode.fp_vert_stretch |= (VERT_STRETCH_BLEND | + newmode->fp_vert_stretch |= (VERT_STRETCH_BLEND | VERT_STRETCH_ENABLE); + use_rmx = 1; } - newmode.fp_vert_stretch &= ~VERT_AUTO_RATIO_EN; + newmode->fp_vert_stretch &= ~VERT_AUTO_RATIO_EN; - newmode.fp_gen_cntl = (rinfo->init_state.fp_gen_cntl & (u32) + newmode->fp_gen_cntl = (rinfo->init_state.fp_gen_cntl & (u32) ~(FP_SEL_CRTC2 | FP_RMX_HVSYNC_CONTROL_EN | FP_DFP_SYNC_SEL | @@ -1643,46 +1712,56 @@ int radeonfb_set_par(struct fb_info *info) FP_CRTC_USE_SHADOW_VEND | FP_CRT_SYNC_ALT)); - newmode.fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR | - FP_CRTC_DONT_SHADOW_HEND); + newmode->fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR | + FP_CRTC_DONT_SHADOW_HEND | + FP_PANEL_FORMAT); - newmode.lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl; - newmode.lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl; - newmode.tmds_crc = rinfo->init_state.tmds_crc; - newmode.tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl; + if (IS_R300_VARIANT(rinfo) || + (rinfo->family == CHIP_FAMILY_R200)) { + newmode->fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK; + if (use_rmx) + newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX; + else + newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1; + } else + newmode->fp_gen_cntl |= FP_SEL_CRTC1; + + newmode->lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl; + newmode->lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl; + newmode->tmds_crc = rinfo->init_state.tmds_crc; + newmode->tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl; if (primary_mon == MT_LCD) { - newmode.lvds_gen_cntl |= (LVDS_ON | LVDS_BLON); - newmode.fp_gen_cntl &= ~(FP_FPON | FP_TMDS_EN); + newmode->lvds_gen_cntl |= (LVDS_ON | LVDS_BLON); + newmode->fp_gen_cntl &= ~(FP_FPON | FP_TMDS_EN); } else { /* DFP */ - newmode.fp_gen_cntl |= (FP_FPON | FP_TMDS_EN); - newmode.tmds_transmitter_cntl = (TMDS_RAN_PAT_RST | TMDS_ICHCSEL) & + newmode->fp_gen_cntl |= (FP_FPON | FP_TMDS_EN); + newmode->tmds_transmitter_cntl = (TMDS_RAN_PAT_RST | TMDS_ICHCSEL) & ~(TMDS_PLLRST); /* TMDS_PLL_EN bit is reversed on RV (and mobility) chips */ - if ((rinfo->family == CHIP_FAMILY_R300) || - (rinfo->family == CHIP_FAMILY_R350) || - (rinfo->family == CHIP_FAMILY_RV350) || + if (IS_R300_VARIANT(rinfo) || (rinfo->family == CHIP_FAMILY_R200) || !rinfo->has_CRTC2) - newmode.tmds_transmitter_cntl &= ~TMDS_PLL_EN; + newmode->tmds_transmitter_cntl &= ~TMDS_PLL_EN; else - newmode.tmds_transmitter_cntl |= TMDS_PLL_EN; - newmode.crtc_ext_cntl &= ~CRTC_CRT_ON; + newmode->tmds_transmitter_cntl |= TMDS_PLL_EN; + newmode->crtc_ext_cntl &= ~CRTC_CRT_ON; } - newmode.fp_crtc_h_total_disp = (((rinfo->panel_info.hblank / 8) & 0x3ff) | + newmode->fp_crtc_h_total_disp = (((rinfo->panel_info.hblank / 8) & 0x3ff) | (((mode->xres / 8) - 1) << 16)); - newmode.fp_crtc_v_total_disp = (rinfo->panel_info.vblank & 0xffff) | + newmode->fp_crtc_v_total_disp = (rinfo->panel_info.vblank & 0xffff) | ((mode->yres - 1) << 16); - newmode.fp_h_sync_strt_wid = ((rinfo->panel_info.hOver_plus & 0x1fff) | + newmode->fp_h_sync_strt_wid = ((rinfo->panel_info.hOver_plus & 0x1fff) | (hsync_wid << 16) | (h_sync_pol << 23)); - newmode.fp_v_sync_strt_wid = ((rinfo->panel_info.vOver_plus & 0xfff) | + newmode->fp_v_sync_strt_wid = ((rinfo->panel_info.vOver_plus & 0xfff) | (vsync_wid << 16) | (v_sync_pol << 23)); } /* do it! */ if (!rinfo->asleep) { - radeon_write_mode (rinfo, &newmode); + memcpy(&rinfo->state, newmode, sizeof(*newmode)); + radeon_write_mode (rinfo, newmode, 0); /* (re)initialize the engine */ if (!(info->flags & FBINFO_HWACCEL_DISABLED)) radeonfb_engine_init (rinfo); @@ -1702,6 +1781,7 @@ int radeonfb_set_par(struct fb_info *info) rinfo->depth, info->fix.line_length); #endif + kfree(newmode); return 0; } @@ -1812,12 +1892,14 @@ static int radeon_set_backlight_enable(int on, int level, void *data) if (on && (level > BACKLIGHT_OFF)) { lvds_gen_cntl &= ~LVDS_DISPLAY_DIS; if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) { - lvds_gen_cntl |= LVDS_BLON /* | LVDS_EN | LVDS_DIGON */; + lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON); + lvds_gen_cntl |= LVDS_BLON | LVDS_EN; OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; lvds_gen_cntl |= (conv_table[level] << LVDS_BL_MOD_LEVEL_SHIFT); lvds_gen_cntl |= LVDS_ON; + lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN); rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; mod_timer(&rinfo->lvds_timer, jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); @@ -1837,13 +1919,18 @@ static int radeon_set_backlight_enable(int on, int level, void *data) tmpPixclksCntl = INPLL(PIXCLKS_CNTL); if (rinfo->is_mobility || rinfo->is_IGP) OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb); - lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK; + lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN); lvds_gen_cntl |= (conv_table[0] << LVDS_BL_MOD_LEVEL_SHIFT); lvds_gen_cntl |= LVDS_DISPLAY_DIS; OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); - lvds_gen_cntl &= ~(LVDS_ON | LVDS_BLON /* | LVDS_EN | LVDS_DIGON */); + udelay(100); + lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN); OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl); + lvds_gen_cntl &= ~(LVDS_DIGON); + rinfo->pending_lvds_gen_cntl = lvds_gen_cntl; + mod_timer(&rinfo->lvds_timer, + jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay)); if (rinfo->is_mobility || rinfo->is_IGP) OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl); } @@ -1926,10 +2013,12 @@ static void fixup_memory_mappings(struct radeonfb_info *rinfo) OUTREG(DISPLAY_BASE_ADDR, aper_base); if (rinfo->has_CRTC2) OUTREG(CRTC2_DISPLAY_BASE_ADDR, aper_base); + OUTREG(OV0_BASE_ADDR, aper_base); #else OUTREG(DISPLAY_BASE_ADDR, 0); if (rinfo->has_CRTC2) OUTREG(CRTC2_DISPLAY_BASE_ADDR, 0); + OUTREG(OV0_BASE_ADDR, 0); #endif mdelay(100); @@ -1947,6 +2036,100 @@ static void fixup_memory_mappings(struct radeonfb_info *rinfo) #endif /* CONFIG_PPC_OF */ +static void radeon_identify_vram(struct radeonfb_info *rinfo) +{ + u32 tmp; + + /* framebuffer size */ + if ((rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200) || + (rinfo->family == CHIP_FAMILY_RS300)) { + u32 tom = INREG(NB_TOM); + tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024); + + radeon_fifo_wait(6); + OUTREG(MC_FB_LOCATION, tom); + OUTREG(DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); + OUTREG(CRTC2_DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); + OUTREG(OV0_BASE_ADDR, (tom & 0xffff) << 16); + + /* This is supposed to fix the crtc2 noise problem. */ + OUTREG(GRPH2_BUFFER_CNTL, INREG(GRPH2_BUFFER_CNTL) & ~0x7f0000); + + if ((rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200)) { + /* This is to workaround the asic bug for RMX, some versions + of BIOS dosen't have this register initialized correctly. + */ + OUTREGP(CRTC_MORE_CNTL, CRTC_H_CUTOFF_ACTIVE_EN, + ~CRTC_H_CUTOFF_ACTIVE_EN); + } + } else { + tmp = INREG(CONFIG_MEMSIZE); + } + + /* mem size is bits [28:0], mask off the rest */ + rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK; + + /* + * Hack to get around some busted production M6's + * reporting no ram + */ + if (rinfo->video_ram == 0) { + switch (rinfo->pdev->device) { + case PCI_CHIP_RADEON_LY: + case PCI_CHIP_RADEON_LZ: + rinfo->video_ram = 8192 * 1024; + break; + default: + break; + } + } + + + /* + * Now try to identify VRAM type + */ + if (rinfo->is_IGP || (rinfo->family >= CHIP_FAMILY_R300) || + (INREG(MEM_SDRAM_MODE_REG) & (1<<30))) + rinfo->vram_ddr = 1; + else + rinfo->vram_ddr = 0; + + tmp = INREG(MEM_CNTL); + if (IS_R300_VARIANT(rinfo)) { + tmp &= R300_MEM_NUM_CHANNELS_MASK; + switch (tmp) { + case 0: rinfo->vram_width = 64; break; + case 1: rinfo->vram_width = 128; break; + case 2: rinfo->vram_width = 256; break; + default: rinfo->vram_width = 128; break; + } + } else if ((rinfo->family == CHIP_FAMILY_RV100) || + (rinfo->family == CHIP_FAMILY_RS100) || + (rinfo->family == CHIP_FAMILY_RS200)){ + if (tmp & RV100_MEM_HALF_MODE) + rinfo->vram_width = 32; + else + rinfo->vram_width = 64; + } else { + if (tmp & MEM_NUM_CHANNELS_MASK) + rinfo->vram_width = 128; + else + rinfo->vram_width = 64; + } + + /* This may not be correct, as some cards can have half of channel disabled + * ToDo: identify these cases + */ + + RTRACE("radeonfb (%s): Found %ldk of %s %d bits wide videoram\n", + pci_name(rinfo->pdev), + rinfo->video_ram / 1024, + rinfo->vram_ddr ? "DDR" : "SDRAM", + rinfo->vram_width); +} + /* * Sysfs */ @@ -2012,7 +2195,6 @@ static int radeonfb_pci_register (struct pci_dev *pdev, { struct fb_info *info; struct radeonfb_info *rinfo; - u32 tmp; int ret; RTRACE("radeonfb_pci_register BEGIN\n"); @@ -2020,13 +2202,15 @@ static int radeonfb_pci_register (struct pci_dev *pdev, /* Enable device in PCI config */ ret = pci_enable_device(pdev); if (ret < 0) { - printk(KERN_ERR "radeonfb: Cannot enable PCI device\n"); + printk(KERN_ERR "radeonfb (%s): Cannot enable PCI device\n", + pci_name(pdev)); goto err_out; } info = framebuffer_alloc(sizeof(struct radeonfb_info), &pdev->dev); if (!info) { - printk (KERN_ERR "radeonfb: could not allocate memory\n"); + printk (KERN_ERR "radeonfb (%s): could not allocate memory\n", + pci_name(pdev)); ret = -ENOMEM; goto err_disable; } @@ -2055,121 +2239,39 @@ static int radeonfb_pci_register (struct pci_dev *pdev, /* request the mem regions */ ret = pci_request_regions(pdev, "radeonfb"); if (ret < 0) { - printk( KERN_ERR "radeonfb: cannot reserve PCI regions." - " Someone already got them?\n"); + printk( KERN_ERR "radeonfb (%s): cannot reserve PCI regions." + " Someone already got them?\n", pci_name(rinfo->pdev)); goto err_release_fb; } /* map the regions */ rinfo->mmio_base = ioremap(rinfo->mmio_base_phys, RADEON_REGSIZE); if (!rinfo->mmio_base) { - printk(KERN_ERR "radeonfb: cannot map MMIO\n"); + printk(KERN_ERR "radeonfb (%s): cannot map MMIO\n", pci_name(rinfo->pdev)); ret = -EIO; goto err_release_pci; } + rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; + +#ifdef CONFIG_PPC_OF + /* On PPC, we obtain the OF device-node pointer to the firmware + * data for this chip + */ + rinfo->of_node = pci_device_to_OF_node(pdev); + if (rinfo->of_node == NULL) + printk(KERN_WARNING "radeonfb (%s): Cannot match card to OF node !\n", + pci_name(rinfo->pdev)); + /* On PPC, the firmware sets up a memory mapping that tends * to cause lockups when enabling the engine. We reconfigure * the card internal memory mappings properly */ -#ifdef CONFIG_PPC_OF fixup_memory_mappings(rinfo); -#else - rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16; #endif /* CONFIG_PPC_OF */ - /* framebuffer size */ - if ((rinfo->family == CHIP_FAMILY_RS100) || - (rinfo->family == CHIP_FAMILY_RS200) || - (rinfo->family == CHIP_FAMILY_RS300)) { - u32 tom = INREG(NB_TOM); - tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024); - - radeon_fifo_wait(6); - OUTREG(MC_FB_LOCATION, tom); - OUTREG(DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); - OUTREG(CRTC2_DISPLAY_BASE_ADDR, (tom & 0xffff) << 16); - OUTREG(OV0_BASE_ADDR, (tom & 0xffff) << 16); - - /* This is supposed to fix the crtc2 noise problem. */ - OUTREG(GRPH2_BUFFER_CNTL, INREG(GRPH2_BUFFER_CNTL) & ~0x7f0000); - - if ((rinfo->family == CHIP_FAMILY_RS100) || - (rinfo->family == CHIP_FAMILY_RS200)) { - /* This is to workaround the asic bug for RMX, some versions - of BIOS dosen't have this register initialized correctly. - */ - OUTREGP(CRTC_MORE_CNTL, CRTC_H_CUTOFF_ACTIVE_EN, - ~CRTC_H_CUTOFF_ACTIVE_EN); - } - } else { - 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; - } - - /* - * Hack to get around some busted production M6's - * reporting no ram - */ - if (rinfo->video_ram == 0) { - switch (pdev->device) { - case PCI_CHIP_RADEON_LY: - case PCI_CHIP_RADEON_LZ: - rinfo->video_ram = 8192 * 1024; - break; - default: - printk (KERN_ERR "radeonfb: no video RAM reported\n"); - ret = -ENXIO; - goto err_unmap_rom; - } - } - - RTRACE("radeonfb: probed %s %ldk videoram\n", (rinfo->ram_type), (rinfo->video_ram/1024)); + /* Get VRAM size and type */ + radeon_identify_vram(rinfo); rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, rinfo->video_ram); @@ -2182,12 +2284,13 @@ static int radeonfb_pci_register (struct pci_dev *pdev, if (rinfo->fb_base) memset_io(rinfo->fb_base, 0, rinfo->mapped_vram); else { - printk (KERN_ERR "radeonfb: cannot map FB\n"); + printk (KERN_ERR "radeonfb (%s): cannot map FB\n", pci_name(rinfo->pdev)); ret = -EIO; goto err_unmap_rom; } - RTRACE("radeonfb: mapped %ldk videoram\n", rinfo->mapped_vram/1024); + RTRACE("radeonfb (%s): mapped %ldk videoram\n", pci_name(rinfo->pdev), + rinfo->mapped_vram/1024); /* * Check for required workaround for PLL accesses @@ -2254,21 +2357,22 @@ static int radeonfb_pci_register (struct pci_dev *pdev, * so we can restore this upon __exit */ radeon_save_state (rinfo, &rinfo->init_state); + memcpy(&rinfo->state, &rinfo->init_state, sizeof(struct radeon_regs)); pci_set_drvdata(pdev, info); - /* Enable PM on mobility chips */ - if (rinfo->is_mobility) { - /* Find PM registers in config space */ - rinfo->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM); - /* Enable dynamic PM of chip clocks */ - radeon_pm_enable_dynamic_mode(rinfo); - printk("radeonfb: Power Management enabled for Mobility chipsets\n"); - } + /* Setup Power Management capabilities */ + if (default_dynclk < -1) { + /* -2 is special: means ON on mobility chips and do not change on others */ + radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1); + } else + radeonfb_pm_init(rinfo, default_dynclk); + /* Register with fbdev layer */ ret = register_framebuffer(info); if (ret < 0) { - printk (KERN_ERR "radeonfb: could not register framebuffer\n"); + printk (KERN_ERR "radeonfb (%s): could not register framebuffer\n", + pci_name(rinfo->pdev)); goto err_unmap_fb; } @@ -2287,8 +2391,7 @@ static int radeonfb_pci_register (struct pci_dev *pdev, } #endif - printk ("radeonfb: %s %s %ld MB\n", rinfo->name, rinfo->ram_type, - (rinfo->video_ram/(1024*1024))); + printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name); if (rinfo->bios_seg) radeon_unmap_ROM(rinfo, pdev); @@ -2331,13 +2434,18 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev) if (!rinfo) return; + radeonfb_pm_exit(rinfo); + +#if 0 /* restore original state * - * Doesn't quite work yet, possibly because of the PPC hacking - * I do on startup, disable for now. --BenH + * Doesn't quite work yet, I suspect if we come from a legacy + * VGA mode (or worse, text mode), we need to do some VGA black + * magic here that I know nothing about. --BenH */ - radeon_write_mode (rinfo, &rinfo->init_state); - + radeon_write_mode (rinfo, &rinfo->init_state, 1); + #endif + del_timer_sync(&rinfo->lvds_timer); #ifdef CONFIG_MTRR @@ -2443,6 +2551,8 @@ MODULE_AUTHOR("Ani Joshi"); MODULE_DESCRIPTION("framebuffer driver for ATI Radeon chipset"); MODULE_LICENSE("GPL"); module_param(noaccel, bool, 0); +module_param(default_dynclk, int, -2); +MODULE_PARM_DESC(default_dynclk, "int: -2=enable on mobility only,-1=do not change,0=off,1=on"); MODULE_PARM_DESC(noaccel, "bool: disable acceleration"); module_param(nomodeset, bool, 0); MODULE_PARM_DESC(nomodeset, "bool: disable actual setting of video mode"); diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c index f55f863ba23d..9094137ccde0 100644 --- a/drivers/video/aty/radeon_monitor.c +++ b/drivers/video/aty/radeon_monitor.c @@ -1,11 +1,6 @@ #include "radeonfb.h" #include "../edid.h" -#ifdef CONFIG_PPC_OF -#include -#include -#endif /* CONFIG_PPC_OF */ - static struct fb_var_screeninfo radeonfb_default_var = { .xres = 640, .yres = 480, @@ -64,9 +59,11 @@ static char *radeon_get_mon_name(int type) * models with broken OF probing by hard-coding known EDIDs for some Mac * laptops internal LVDS panel. (XXX: not done yet) */ -static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_EDID, int hdno) +static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_EDID, + int hdno) { - static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", "EDID2", NULL }; + static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", + "EDID1", "EDID2", NULL }; u8 *pedid = NULL; u8 *pmt = NULL; u8 *tmp; @@ -122,7 +119,7 @@ static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_ RTRACE("radeon_probe_OF_head\n"); - dp = pci_device_to_OF_node(rinfo->pdev); + dp = rinfo->of_node; while (dp == NULL) return MT_NONE; @@ -502,8 +499,9 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, #endif /* CONFIG_PPC_OF */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon1_type == MT_NONE) - rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi, - &rinfo->mon1_EDID); + rinfo->mon1_type = + radeon_probe_i2c_connector(rinfo, ddc_dvi, + &rinfo->mon1_EDID); if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_vga, @@ -545,7 +543,8 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, */ #ifdef CONFIG_PPC_OF if (rinfo->mon1_type == MT_NONE) - rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, &rinfo->mon1_EDID); + rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, + &rinfo->mon1_EDID); #endif /* CONFIG_PPC_OF */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon1_type == MT_NONE) @@ -572,7 +571,8 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, */ #ifdef CONFIG_PPC_OF if (rinfo->mon2_type == MT_NONE) - rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1, &rinfo->mon2_EDID); + rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1, + &rinfo->mon2_EDID); #endif /* CONFIG_PPC_OF */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon2_type == MT_NONE) @@ -648,10 +648,10 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, */ static void radeon_fixup_panel_info(struct radeonfb_info *rinfo) { - #ifdef CONFIG_PPC_OF +#ifdef CONFIG_PPC_OF /* * LCD Flat panels should use fixed dividers, we enfore that on - * PowerMac only for now... + * PPC only for now... */ if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD && rinfo->is_mobility) { @@ -702,40 +702,24 @@ static void radeon_var_to_panel_info(struct radeonfb_info *rinfo, struct fb_var_ rinfo->panel_info.pwr_delay = 200; } -static void radeon_var_to_videomode(struct fb_videomode *mode, - const struct fb_var_screeninfo *var) -{ - mode->xres = var->xres; - mode->yres = var->yres; - mode->pixclock = var->pixclock; - mode->left_margin = var->left_margin; - mode->right_margin = var->right_margin; - mode->upper_margin = var->upper_margin; - mode->lower_margin = var->lower_margin; - mode->hsync_len = var->hsync_len; - mode->vsync_len = var->vsync_len; - mode->sync = var->sync; - mode->vmode = var->vmode; -} - static void radeon_videomode_to_var(struct fb_var_screeninfo *var, const struct fb_videomode *mode) { - var->xres = mode->xres; - var->yres = mode->yres; - var->xres_virtual = mode->xres; - var->yres_virtual = mode->yres; - var->xoffset = 0; - var->yoffset = 0; - var->pixclock = mode->pixclock; - var->left_margin = mode->left_margin; - var->right_margin = mode->right_margin; - var->upper_margin = mode->upper_margin; - var->lower_margin = mode->lower_margin; - var->hsync_len = mode->hsync_len; - var->vsync_len = mode->vsync_len; - var->sync = mode->sync; - var->vmode = mode->vmode; + var->xres = mode->xres; + var->yres = mode->yres; + var->xres_virtual = mode->xres; + var->yres_virtual = mode->yres; + var->xoffset = 0; + var->yoffset = 0; + var->pixclock = mode->pixclock; + var->left_margin = mode->left_margin; + var->right_margin = mode->right_margin; + var->upper_margin = mode->upper_margin; + var->lower_margin = mode->lower_margin; + var->hsync_len = mode->hsync_len; + var->vsync_len = mode->vsync_len; + var->sync = mode->sync; + var->vmode = mode->vmode; } /* @@ -744,12 +728,14 @@ static void radeon_videomode_to_var(struct fb_var_screeninfo *var, */ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_option) { + struct fb_info * info = rinfo->info; int has_default_mode = 0; /* * Fill default var first */ - rinfo->info->var = radeonfb_default_var; + info->var = radeonfb_default_var; + INIT_LIST_HEAD(&info->modelist); /* * First check out what BIOS has to say @@ -783,7 +769,7 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_ * those */ if (rinfo->mon1_type != MT_CRT && rinfo->panel_info.valid) { - struct fb_var_screeninfo *var = &rinfo->info->var; + struct fb_var_screeninfo *var = &info->var; RTRACE("Setting up default mode based on panel info\n"); var->xres = rinfo->panel_info.xres; @@ -814,9 +800,12 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_ * Now build modedb from EDID */ if (rinfo->mon1_EDID) { - rinfo->mon1_modedb = fb_create_modedb(rinfo->mon1_EDID, - &rinfo->mon1_dbsize); - fb_get_monitor_limits(rinfo->mon1_EDID, &rinfo->info->monspecs); + fb_edid_to_monspecs(rinfo->mon1_EDID, &info->monspecs); + fb_videomode_to_modelist(info->monspecs.modedb, + info->monspecs.modedb_len, + &info->modelist); + rinfo->mon1_modedb = info->monspecs.modedb; + rinfo->mon1_dbsize = info->monspecs.modedb_len; } @@ -847,31 +836,62 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_ modedb = rinfo->mon1_modedb; dbsize = rinfo->mon1_dbsize; snprintf(modename, 31, "%dx%d", rinfo->panel_info.xres, rinfo->panel_info.yres); - if (fb_find_mode(&rinfo->info->var, rinfo->info, modename, + if (fb_find_mode(&info->var, info, modename, modedb, dbsize, NULL, 8) == 0) { printk(KERN_WARNING "radeonfb: Can't find mode for panel size, going back to CRT\n"); rinfo->mon1_type = MT_CRT; goto pickup_default; } has_default_mode = 1; - radeon_var_to_panel_info(rinfo, &rinfo->info->var); + radeon_var_to_panel_info(rinfo, &info->var); } pickup_default: /* - * Pick up a random default mode + * Apply passed-in mode option if any */ - if (!has_default_mode || mode_option) { - struct fb_videomode default_mode; - if (has_default_mode) - radeon_var_to_videomode(&default_mode, &rinfo->info->var); - else - radeon_var_to_videomode(&default_mode, &radeonfb_default_var); - if (fb_find_mode(&rinfo->info->var, rinfo->info, mode_option, - rinfo->mon1_modedb, rinfo->mon1_dbsize, &default_mode, 8) == 0) - rinfo->info->var = radeonfb_default_var; - } + if (mode_option) { + if (fb_find_mode(&info->var, info, mode_option, + info->monspecs.modedb, + info->monspecs.modedb_len, NULL, 8) != 0) + has_default_mode = 1; + } + + /* + * Still no mode, let's pick up a default from the db + */ + if (!has_default_mode && info->monspecs.modedb != NULL) { + struct fb_monspecs *specs = &info->monspecs; + struct fb_videomode *modedb = NULL; + + /* get preferred timing */ + if (specs->misc & FB_MISC_1ST_DETAIL) { + int i; + for (i = 0; i < specs->modedb_len; i++) { + if (specs->modedb[i].flag & FB_MODE_IS_FIRST) { + modedb = &specs->modedb[i]; + break; + } + } + } else { + /* otherwise, get first mode in database */ + modedb = &specs->modedb[0]; + } + if (modedb != NULL) { + info->var.bits_per_pixel = 8; + radeon_videomode_to_var(&info->var, modedb); + has_default_mode = 1; + } + } + if (1) { + struct fb_videomode mode; + /* Make sure that whatever mode got selected is actually in the + * modelist or the kernel may die + */ + fb_var_to_videomode(&mode, &info->var); + fb_add_videomode(&mode, &info->modelist); + } } /* diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index c033fe4cd887..4f23594acbeb 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -1,207 +1,449 @@ +/* + * drivers/video/aty/radeon_pm.c + * + * Copyright 2003,2004 Ben. Herrenschmidt + * Copyright 2004 Paul Mackerras + * + * This is the power management code for ATI radeon chipsets. It contains + * some dynamic clock PM enable/disable code similar to what X.org does, + * some D2-state (APM-style) sleep/wakeup code for use on some PowerMacs, + * and the necessary bits to re-initialize from scratch a few chips found + * on PowerMacs as well. The later could be extended to more platforms + * provided the memory controller configuration code be made more generic, + * and you can get the proper mode register commands for your RAMs. + * Those things may be found in the BIOS image... + */ + #include "radeonfb.h" #include #include -/* - * Currently, only PowerMac do D2 state - */ -#define CONFIG_RADEON_HAS_D2 CONFIG_PPC_PMAC - -#ifdef CONFIG_RADEON_HAS_D2 -/* - * On PowerMac, we assume any mobility chip based machine does D2 - */ #ifdef CONFIG_PPC_PMAC -static inline int radeon_suspend_to_d2(struct radeonfb_info *rinfo, u32 state) -{ - return rinfo->is_mobility; -} -#else -static inline int radeon_suspend_to_d2(struct radeonfb_info *rinfo, u32 state) -{ - return 0; -} +#include +#include +#include #endif -#endif /* CONFIG_RADEON_HAS_D2 */ - -/* - * Radeon M6, M7 and M9 Power Management code. This code currently - * only supports the mobile chips in D2 mode, that is typically what - * is used on Apple laptops, it's based from some informations provided - * by ATI along with hours of tracing of MacOS drivers. - * - * New version of this code almost totally rewritten by ATI, many thanks - * for their support. - */ +#include "ati_ids.h" void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo) { + u32 tmp; - u32 sclk_cntl; - u32 mclk_cntl; - u32 sclk_more_cntl; - - u32 vclk_ecp_cntl; - u32 pixclks_cntl; - - /* Mobility chips only, untested on M9+/M10/11 */ - if (!rinfo->is_mobility) + /* RV100 */ + if ((rinfo->family == CHIP_FAMILY_RV100) && (!rinfo->is_mobility)) { + if (rinfo->has_CRTC2) { + tmp = INPLL(pllSCLK_CNTL); + tmp &= ~SCLK_CNTL__DYN_STOP_LAT_MASK; + tmp |= SCLK_CNTL__CP_MAX_DYN_STOP_LAT | SCLK_CNTL__FORCEON_MASK; + OUTPLL(pllSCLK_CNTL, tmp); + } + tmp = INPLL(pllMCLK_CNTL); + tmp |= (MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB | + MCLK_CNTL__FORCE_AIC | + MCLK_CNTL__FORCE_MC); + OUTPLL(pllMCLK_CNTL, tmp); + return; + } + /* R100 */ + if (!rinfo->has_CRTC2) { + tmp = INPLL(pllSCLK_CNTL); + tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_HDP | + SCLK_CNTL__FORCE_DISP1 | SCLK_CNTL__FORCE_TOP | + SCLK_CNTL__FORCE_E2 | SCLK_CNTL__FORCE_SE | + SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_VIP | + SCLK_CNTL__FORCE_RE | SCLK_CNTL__FORCE_PB | + SCLK_CNTL__FORCE_TAM | SCLK_CNTL__FORCE_TDM | + SCLK_CNTL__FORCE_RB); + OUTPLL(pllSCLK_CNTL, tmp); return; - if (rinfo->family > CHIP_FAMILY_RV250) + } + /* RV350 (M10) */ + if (rinfo->family == CHIP_FAMILY_RV350) { + /* for RV350/M10, no delays are required. */ + tmp = INPLL(pllSCLK_CNTL2); + tmp |= (SCLK_CNTL2__R300_FORCE_TCL | + SCLK_CNTL2__R300_FORCE_GA | + SCLK_CNTL2__R300_FORCE_CBA); + OUTPLL(pllSCLK_CNTL2, tmp); + + tmp = INPLL(pllSCLK_CNTL); + tmp |= (SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_CP | + SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | + SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_E2 | + SCLK_CNTL__R300_FORCE_VAP | SCLK_CNTL__FORCE_IDCT | + SCLK_CNTL__FORCE_VIP | SCLK_CNTL__R300_FORCE_SR | + SCLK_CNTL__R300_FORCE_PX | SCLK_CNTL__R300_FORCE_TX | + SCLK_CNTL__R300_FORCE_US | SCLK_CNTL__FORCE_TV_SCLK | + SCLK_CNTL__R300_FORCE_SU | SCLK_CNTL__FORCE_OV0); + OUTPLL(pllSCLK_CNTL, tmp); + + tmp = INPLL(pllSCLK_MORE_CNTL); + tmp |= (SCLK_MORE_CNTL__FORCE_DISPREGS | SCLK_MORE_CNTL__FORCE_MC_GUI | + SCLK_MORE_CNTL__FORCE_MC_HOST); + OUTPLL(pllSCLK_MORE_CNTL, tmp); + + tmp = INPLL(pllMCLK_CNTL); + tmp |= (MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB | + MCLK_CNTL__FORCE_MC); + OUTPLL(pllMCLK_CNTL, tmp); + + tmp = INPLL(pllVCLK_ECP_CNTL); + tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | + VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb | + VCLK_ECP_CNTL__R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF); + OUTPLL(pllVCLK_ECP_CNTL, tmp); + + tmp = INPLL(pllPIXCLKS_CNTL); + tmp &= ~(PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | + PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb | + PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | + PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb | + PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb | + PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb | + PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb | + PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb | + PIXCLKS_CNTL__R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF); + OUTPLL(pllPIXCLKS_CNTL, tmp); + return; + } + /* Default */ + /* Force Core Clocks */ - sclk_cntl = INPLL( pllSCLK_CNTL_M6); - sclk_cntl |= SCLK_CNTL_M6__FORCE_CP| - SCLK_CNTL_M6__FORCE_HDP| - SCLK_CNTL_M6__FORCE_DISP1| - SCLK_CNTL_M6__FORCE_DISP2| - SCLK_CNTL_M6__FORCE_TOP| - SCLK_CNTL_M6__FORCE_E2| - SCLK_CNTL_M6__FORCE_SE| - SCLK_CNTL_M6__FORCE_IDCT| - SCLK_CNTL_M6__FORCE_VIP| - SCLK_CNTL_M6__FORCE_RE| - SCLK_CNTL_M6__FORCE_PB| - SCLK_CNTL_M6__FORCE_TAM| - SCLK_CNTL_M6__FORCE_TDM| - SCLK_CNTL_M6__FORCE_RB| - SCLK_CNTL_M6__FORCE_TV_SCLK| - SCLK_CNTL_M6__FORCE_SUBPIC| - SCLK_CNTL_M6__FORCE_OV0; - OUTPLL( pllSCLK_CNTL_M6, sclk_cntl); - - - - sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL); - sclk_more_cntl |= SCLK_MORE_CNTL__FORCE_DISPREGS| - SCLK_MORE_CNTL__FORCE_MC_GUI| - SCLK_MORE_CNTL__FORCE_MC_HOST; - OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl); - - /* Force Display clocks */ - vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL); - vclk_ecp_cntl &= ~( VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | - VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); + tmp = INPLL(pllSCLK_CNTL); + tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_E2); - OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl); - - pixclks_cntl = INPLL( pllPIXCLKS_CNTL); - pixclks_cntl &= ~( PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | - PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb| - PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb | - PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb| - PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb| - PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb| - PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb); - - OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl); + /* XFree doesn't do that case, but we had this code from Apple and it + * seem necessary for proper suspend/resume operations + */ + if (rinfo->is_mobility) { + tmp |= SCLK_CNTL__FORCE_HDP| + SCLK_CNTL__FORCE_DISP1| + SCLK_CNTL__FORCE_DISP2| + SCLK_CNTL__FORCE_TOP| + SCLK_CNTL__FORCE_SE| + SCLK_CNTL__FORCE_IDCT| + SCLK_CNTL__FORCE_VIP| + SCLK_CNTL__FORCE_PB| + SCLK_CNTL__FORCE_RE| + SCLK_CNTL__FORCE_TAM| + SCLK_CNTL__FORCE_TDM| + SCLK_CNTL__FORCE_RB| + SCLK_CNTL__FORCE_TV_SCLK| + SCLK_CNTL__FORCE_SUBPIC| + SCLK_CNTL__FORCE_OV0; + } + else if (rinfo->family == CHIP_FAMILY_R300 || + rinfo->family == CHIP_FAMILY_R350) { + tmp |= SCLK_CNTL__FORCE_HDP | + SCLK_CNTL__FORCE_DISP1 | + SCLK_CNTL__FORCE_DISP2 | + SCLK_CNTL__FORCE_TOP | + SCLK_CNTL__FORCE_IDCT | + SCLK_CNTL__FORCE_VIP; + } + OUTPLL(pllSCLK_CNTL, tmp); + radeon_msleep(16); + + if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) { + tmp = INPLL(pllSCLK_CNTL2); + tmp |= SCLK_CNTL2__R300_FORCE_TCL | + SCLK_CNTL2__R300_FORCE_GA | + SCLK_CNTL2__R300_FORCE_CBA; + OUTPLL(pllSCLK_CNTL2, tmp); + radeon_msleep(16); + } + + tmp = INPLL(pllCLK_PIN_CNTL); + tmp &= ~CLK_PIN_CNTL__SCLK_DYN_START_CNTL; + OUTPLL(pllCLK_PIN_CNTL, tmp); + radeon_msleep(15); + + if (rinfo->is_IGP) { + /* Weird ... X is _un_ forcing clocks here, I think it's + * doing backward. Imitate it for now... + */ + tmp = INPLL(pllMCLK_CNTL); + tmp &= ~(MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_YCLKA); + OUTREG(pllMCLK_CNTL, tmp); + radeon_msleep(16); + } + /* Hrm... same shit, X doesn't do that but I have to */ + else if (rinfo->is_mobility) { + tmp = INPLL(pllMCLK_CNTL); + tmp |= (MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB); + OUTPLL(pllMCLK_CNTL, tmp); + radeon_msleep(16); + + tmp = INPLL(pllMCLK_MISC); + tmp &= ~(MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT| + MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT| + MCLK_MISC__MC_MCLK_DYN_ENABLE| + MCLK_MISC__IO_MCLK_DYN_ENABLE); + OUTPLL(pllMCLK_MISC, tmp); + radeon_msleep(15); + } - /* Force Memory Clocks */ - mclk_cntl = INPLL( pllMCLK_CNTL_M6); - mclk_cntl &= ~( MCLK_CNTL_M6__FORCE_MCLKA | - MCLK_CNTL_M6__FORCE_MCLKB | - MCLK_CNTL_M6__FORCE_YCLKA | - MCLK_CNTL_M6__FORCE_YCLKB ); - OUTPLL( pllMCLK_CNTL_M6, mclk_cntl); + if (rinfo->is_mobility) { + tmp = INPLL(pllSCLK_MORE_CNTL); + tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS| + SCLK_MORE_CNTL__FORCE_MC_GUI| + SCLK_MORE_CNTL__FORCE_MC_HOST; + OUTPLL(pllSCLK_MORE_CNTL, tmp); + radeon_msleep(16); + } + + tmp = INPLL(pllPIXCLKS_CNTL); + tmp &= ~(PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb| + PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb| + PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb| + PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb| + PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb); + OUTPLL(pllPIXCLKS_CNTL, tmp); + radeon_msleep(16); + + tmp = INPLL( pllVCLK_ECP_CNTL); + tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | + VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); + OUTPLL( pllVCLK_ECP_CNTL, tmp); + radeon_msleep(16); } void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo) { - u32 clk_pwrmgt_cntl; - u32 sclk_cntl; - u32 sclk_more_cntl; - u32 clk_pin_cntl; - u32 pixclks_cntl; - u32 vclk_ecp_cntl; - u32 mclk_cntl; - u32 mclk_misc; + u32 tmp; - /* Mobility chips only, untested on M9+/M10/11 */ - if (!rinfo->is_mobility) + /* R100 */ + if (!rinfo->has_CRTC2) { + tmp = INPLL(pllSCLK_CNTL); + + if ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) > CFG_ATI_REV_A13) + tmp &= ~(SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_RB); + tmp &= ~(SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | + SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_SE | + SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_RE | + SCLK_CNTL__FORCE_PB | SCLK_CNTL__FORCE_TAM | + SCLK_CNTL__FORCE_TDM); + OUTPLL(pllSCLK_CNTL, tmp); return; - if (rinfo->family > CHIP_FAMILY_RV250) - return; - - /* Set Latencies */ - clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL_M6); - - clk_pwrmgt_cntl &= ~( CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE_MASK| - CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT_MASK| - CLK_PWRMGT_CNTL_M6__DISP_DYN_STOP_LAT_MASK| - CLK_PWRMGT_CNTL_M6__DYN_STOP_MODE_MASK); - /* Mode 1 */ - clk_pwrmgt_cntl = CLK_PWRMGT_CNTL_M6__MC_CH_MODE| - CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE | - (1<family == CHIP_FAMILY_RV350) { + tmp = INPLL(pllSCLK_CNTL2); + tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL | + SCLK_CNTL2__R300_FORCE_GA | + SCLK_CNTL2__R300_FORCE_CBA); + tmp |= (SCLK_CNTL2__R300_TCL_MAX_DYN_STOP_LAT | + SCLK_CNTL2__R300_GA_MAX_DYN_STOP_LAT | + SCLK_CNTL2__R300_CBA_MAX_DYN_STOP_LAT); + OUTPLL(pllSCLK_CNTL2, tmp); + + tmp = INPLL(pllSCLK_CNTL); + tmp &= ~(SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_CP | + SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 | + SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_E2 | + SCLK_CNTL__R300_FORCE_VAP | SCLK_CNTL__FORCE_IDCT | + SCLK_CNTL__FORCE_VIP | SCLK_CNTL__R300_FORCE_SR | + SCLK_CNTL__R300_FORCE_PX | SCLK_CNTL__R300_FORCE_TX | + SCLK_CNTL__R300_FORCE_US | SCLK_CNTL__FORCE_TV_SCLK | + SCLK_CNTL__R300_FORCE_SU | SCLK_CNTL__FORCE_OV0); + tmp |= SCLK_CNTL__DYN_STOP_LAT_MASK; + OUTPLL(pllSCLK_CNTL, tmp); + + tmp = INPLL(pllSCLK_MORE_CNTL); + tmp &= ~SCLK_MORE_CNTL__FORCEON; + tmp |= SCLK_MORE_CNTL__DISPREGS_MAX_DYN_STOP_LAT | + SCLK_MORE_CNTL__MC_GUI_MAX_DYN_STOP_LAT | + SCLK_MORE_CNTL__MC_HOST_MAX_DYN_STOP_LAT; + OUTPLL(pllSCLK_MORE_CNTL, tmp); + + tmp = INPLL(pllVCLK_ECP_CNTL); + tmp |= (VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | + VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); + OUTPLL(pllVCLK_ECP_CNTL, tmp); + + tmp = INPLL(pllPIXCLKS_CNTL); + tmp |= (PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | + PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb | + PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb | + PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb | + PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb | + PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb | + PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb | + PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb | + PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb | + PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb); + OUTPLL(pllPIXCLKS_CNTL, tmp); + + tmp = INPLL(pllMCLK_MISC); + tmp |= (MCLK_MISC__MC_MCLK_DYN_ENABLE | + MCLK_MISC__IO_MCLK_DYN_ENABLE); + OUTPLL(pllMCLK_MISC, tmp); + + tmp = INPLL(pllMCLK_CNTL); + tmp |= (MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_MCLKB); + tmp &= ~(MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB | + MCLK_CNTL__FORCE_MC); + + /* Some releases of vbios have set DISABLE_MC_MCLKA + * and DISABLE_MC_MCLKB bits in the vbios table. Setting these + * bits will cause H/W hang when reading video memory with dynamic + * clocking enabled. + */ + if ((tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKA) && + (tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKB)) { + /* If both bits are set, then check the active channels */ + tmp = INPLL(pllMCLK_CNTL); + if (rinfo->vram_width == 64) { + if (INREG(MEM_CNTL) & R300_MEM_USE_CD_CH_ONLY) + tmp &= ~MCLK_CNTL__R300_DISABLE_MC_MCLKB; + else + tmp &= ~MCLK_CNTL__R300_DISABLE_MC_MCLKA; + } else { + tmp &= ~(MCLK_CNTL__R300_DISABLE_MC_MCLKA | + MCLK_CNTL__R300_DISABLE_MC_MCLKB); + } + } + OUTPLL(pllMCLK_CNTL, tmp); + return; + } - OUTPLL( pllSCLK_CNTL_M6, sclk_cntl); + /* R300 */ + if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) { + tmp = INPLL(pllSCLK_CNTL); + tmp &= ~(SCLK_CNTL__R300_FORCE_VAP); + tmp |= SCLK_CNTL__FORCE_CP; + OUTPLL(pllSCLK_CNTL, tmp); + radeon_msleep(15); + + tmp = INPLL(pllSCLK_CNTL2); + tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL | + SCLK_CNTL2__R300_FORCE_GA | + SCLK_CNTL2__R300_FORCE_CBA); + OUTPLL(pllSCLK_CNTL2, tmp); + } + /* Others */ - sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL); - sclk_more_cntl &= ~(SCLK_MORE_CNTL__FORCE_DISPREGS); - - OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl); + tmp = INPLL( pllCLK_PWRMGT_CNTL); + tmp &= ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK| + CLK_PWRMGT_CNTL__DISP_DYN_STOP_LAT_MASK| + CLK_PWRMGT_CNTL__DYN_STOP_MODE_MASK); + tmp |= CLK_PWRMGT_CNTL__ENGINE_DYNCLK_MODE_MASK | + (0x01 << CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT__SHIFT); + OUTPLL( pllCLK_PWRMGT_CNTL, tmp); + radeon_msleep(15); - - /* Enable Dynamic mode for PIXCLK & PIX2CLK */ + tmp = INPLL(pllCLK_PIN_CNTL); + tmp |= CLK_PIN_CNTL__SCLK_DYN_START_CNTL; + OUTPLL(pllCLK_PIN_CNTL, tmp); + radeon_msleep(15); - pixclks_cntl = INPLL( pllPIXCLKS_CNTL); - - pixclks_cntl|= PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | - PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb| - PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb| - PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb| - PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb| - PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb| - PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb; - - OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl); - - - vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL); + /* When DRI is enabled, setting DYN_STOP_LAT to zero can cause some R200 + * to lockup randomly, leave them as set by BIOS. + */ + tmp = INPLL(pllSCLK_CNTL); + tmp &= ~SCLK_CNTL__FORCEON_MASK; + + /*RAGE_6::A11 A12 A12N1 A13, RV250::A11 A12, R300*/ + if ((rinfo->family == CHIP_FAMILY_RV250 && + ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13)) || + ((rinfo->family == CHIP_FAMILY_RV100) && + ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) <= CFG_ATI_REV_A13))) { + tmp |= SCLK_CNTL__FORCE_CP; + tmp |= SCLK_CNTL__FORCE_VIP; + } + OUTPLL(pllSCLK_CNTL, tmp); + radeon_msleep(15); + + if ((rinfo->family == CHIP_FAMILY_RV200) || + (rinfo->family == CHIP_FAMILY_RV250) || + (rinfo->family == CHIP_FAMILY_RV280)) { + tmp = INPLL(pllSCLK_MORE_CNTL); + tmp &= ~SCLK_MORE_CNTL__FORCEON; + + /* RV200::A11 A12 RV250::A11 A12 */ + if (((rinfo->family == CHIP_FAMILY_RV200) || + (rinfo->family == CHIP_FAMILY_RV250)) && + ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13)) + tmp |= SCLK_MORE_CNTL__FORCEON; + + OUTPLL(pllSCLK_MORE_CNTL, tmp); + radeon_msleep(15); + } - vclk_ecp_cntl|= VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | - VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb; - - OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl); + /* RV200::A11 A12, RV250::A11 A12 */ + if (((rinfo->family == CHIP_FAMILY_RV200) || + (rinfo->family == CHIP_FAMILY_RV250)) && + ((INREG(CONFIG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13)) { + tmp = INPLL(pllPLL_PWRMGT_CNTL); + tmp |= PLL_PWRMGT_CNTL__TCL_BYPASS_DISABLE; + OUTREG(pllPLL_PWRMGT_CNTL, tmp); + radeon_msleep(15); + } - /* Enable Dynamic mode for MCLK */ - - mclk_cntl = INPLL( pllMCLK_CNTL_M6); - mclk_cntl |= MCLK_CNTL_M6__FORCE_MCLKA| - MCLK_CNTL_M6__FORCE_MCLKB| - MCLK_CNTL_M6__FORCE_YCLKA| - MCLK_CNTL_M6__FORCE_YCLKB; - - OUTPLL( pllMCLK_CNTL_M6, mclk_cntl); - - mclk_misc = INPLL(pllMCLK_MISC); - mclk_misc |= MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT| + tmp = INPLL(pllPIXCLKS_CNTL); + tmp |= PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | + PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb| + PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb| + PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb| + PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb| + PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb| + PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb; + OUTPLL(pllPIXCLKS_CNTL, tmp); + radeon_msleep(15); + + tmp = INPLL(pllVCLK_ECP_CNTL); + tmp |= VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | + VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb; + OUTPLL(pllVCLK_ECP_CNTL, tmp); + + /* X doesn't do that ... hrm, we do on mobility && Macs */ +#ifdef CONFIG_PPC_OF + if (rinfo->is_mobility) { + tmp = INPLL(pllMCLK_CNTL); + tmp &= ~(MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB); + OUTPLL(pllMCLK_CNTL, tmp); + radeon_msleep(15); + + tmp = INPLL(pllMCLK_MISC); + tmp |= MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT| MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT| MCLK_MISC__MC_MCLK_DYN_ENABLE| - MCLK_MISC__IO_MCLK_DYN_ENABLE; - - OUTPLL(pllMCLK_MISC, mclk_misc); + MCLK_MISC__IO_MCLK_DYN_ENABLE; + OUTPLL(pllMCLK_MISC, tmp); + radeon_msleep(15); + } +#endif /* CONFIG_PPC_OF */ } #ifdef CONFIG_PM @@ -218,7 +460,7 @@ static u32 INMC(struct radeonfb_info *rinfo, u8 indx) return INREG( MC_IND_DATA); } -static void radeon_pm_save_regs(struct radeonfb_info *rinfo) +static void radeon_pm_save_regs(struct radeonfb_info *rinfo, int saving_for_d3) { rinfo->save_regs[0] = INPLL(PLL_PWRMGT_CNTL); rinfo->save_regs[1] = INPLL(CLK_PWRMGT_CNTL); @@ -233,7 +475,6 @@ static void radeon_pm_save_regs(struct radeonfb_info *rinfo) rinfo->save_regs[9] = INREG(DISP_MISC_CNTL); rinfo->save_regs[10] = INREG(DISP_PWR_MAN); rinfo->save_regs[11] = INREG(LVDS_GEN_CNTL); - rinfo->save_regs[12] = INREG(LVDS_PLL_CNTL); rinfo->save_regs[13] = INREG(TV_DAC_CNTL); rinfo->save_regs[14] = INREG(BUS_CNTL1); rinfo->save_regs[15] = INREG(CRTC_OFFSET_CNTL); @@ -256,6 +497,93 @@ static void radeon_pm_save_regs(struct radeonfb_info *rinfo) rinfo->save_regs[31] = INREG(DISPLAY_BASE_ADDR); rinfo->save_regs[32] = INREG(MC_AGP_LOCATION); rinfo->save_regs[33] = INREG(CRTC2_DISPLAY_BASE_ADDR); + + rinfo->save_regs[34] = INPLL(SCLK_MORE_CNTL); + rinfo->save_regs[35] = INREG(MEM_SDRAM_MODE_REG); + rinfo->save_regs[36] = INREG(BUS_CNTL); + rinfo->save_regs[39] = INREG(RBBM_CNTL); + rinfo->save_regs[40] = INREG(DAC_CNTL); + rinfo->save_regs[41] = INREG(HOST_PATH_CNTL); + rinfo->save_regs[37] = INREG(MPP_TB_CONFIG); + rinfo->save_regs[38] = INREG(FCP_CNTL); + + if (rinfo->is_mobility) { + rinfo->save_regs[12] = INREG(LVDS_PLL_CNTL); + rinfo->save_regs[43] = INPLL(pllSSPLL_CNTL); + rinfo->save_regs[44] = INPLL(pllSSPLL_REF_DIV); + rinfo->save_regs[45] = INPLL(pllSSPLL_DIV_0); + rinfo->save_regs[90] = INPLL(pllSS_INT_CNTL); + rinfo->save_regs[91] = INPLL(pllSS_TST_CNTL); + rinfo->save_regs[81] = INREG(LVDS_GEN_CNTL); + } + + if (rinfo->family >= CHIP_FAMILY_RV200) { + rinfo->save_regs[42] = INREG(MEM_REFRESH_CNTL); + rinfo->save_regs[46] = INREG(MC_CNTL); + rinfo->save_regs[47] = INREG(MC_INIT_GFX_LAT_TIMER); + rinfo->save_regs[48] = INREG(MC_INIT_MISC_LAT_TIMER); + rinfo->save_regs[49] = INREG(MC_TIMING_CNTL); + rinfo->save_regs[50] = INREG(MC_READ_CNTL_AB); + rinfo->save_regs[51] = INREG(MC_IOPAD_CNTL); + rinfo->save_regs[52] = INREG(MC_CHIP_IO_OE_CNTL_AB); + rinfo->save_regs[53] = INREG(MC_DEBUG); + } + rinfo->save_regs[54] = INREG(PAMAC0_DLY_CNTL); + rinfo->save_regs[55] = INREG(PAMAC1_DLY_CNTL); + rinfo->save_regs[56] = INREG(PAD_CTLR_MISC); + rinfo->save_regs[57] = INREG(FW_CNTL); + + if (rinfo->family >= CHIP_FAMILY_R300) { + rinfo->save_regs[58] = INMC(rinfo, ixR300_MC_MC_INIT_WR_LAT_TIMER); + rinfo->save_regs[59] = INMC(rinfo, ixR300_MC_IMP_CNTL); + rinfo->save_regs[60] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_C0); + rinfo->save_regs[61] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_C1); + rinfo->save_regs[62] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_D0); + rinfo->save_regs[63] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_D1); + rinfo->save_regs[64] = INMC(rinfo, ixR300_MC_BIST_CNTL_3); + rinfo->save_regs[65] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A0); + rinfo->save_regs[66] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1); + rinfo->save_regs[67] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B0); + rinfo->save_regs[68] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1); + rinfo->save_regs[69] = INMC(rinfo, ixR300_MC_DEBUG_CNTL); + rinfo->save_regs[70] = INMC(rinfo, ixR300_MC_DLL_CNTL); + rinfo->save_regs[71] = INMC(rinfo, ixR300_MC_IMP_CNTL_0); + rinfo->save_regs[72] = INMC(rinfo, ixR300_MC_ELPIDA_CNTL); + rinfo->save_regs[96] = INMC(rinfo, ixR300_MC_READ_CNTL_CD); + } else { + rinfo->save_regs[59] = INMC(rinfo, ixMC_IMP_CNTL); + rinfo->save_regs[65] = INMC(rinfo, ixMC_CHP_IO_CNTL_A0); + rinfo->save_regs[66] = INMC(rinfo, ixMC_CHP_IO_CNTL_A1); + rinfo->save_regs[67] = INMC(rinfo, ixMC_CHP_IO_CNTL_B0); + rinfo->save_regs[68] = INMC(rinfo, ixMC_CHP_IO_CNTL_B1); + rinfo->save_regs[71] = INMC(rinfo, ixMC_IMP_CNTL_0); + } + + rinfo->save_regs[73] = INPLL(pllMPLL_CNTL); + rinfo->save_regs[74] = INPLL(pllSPLL_CNTL); + rinfo->save_regs[75] = INPLL(pllMPLL_AUX_CNTL); + rinfo->save_regs[76] = INPLL(pllSPLL_AUX_CNTL); + rinfo->save_regs[77] = INPLL(pllM_SPLL_REF_FB_DIV); + rinfo->save_regs[78] = INPLL(pllAGP_PLL_CNTL); + rinfo->save_regs[79] = INREG(PAMAC2_DLY_CNTL); + + rinfo->save_regs[80] = INREG(OV0_BASE_ADDR); + rinfo->save_regs[82] = INREG(FP_GEN_CNTL); + rinfo->save_regs[83] = INREG(FP2_GEN_CNTL); + rinfo->save_regs[84] = INREG(TMDS_CNTL); + rinfo->save_regs[85] = INREG(TMDS_TRANSMITTER_CNTL); + rinfo->save_regs[86] = INREG(DISP_OUTPUT_CNTL); + rinfo->save_regs[87] = INREG(DISP_HW_DEBUG); + rinfo->save_regs[88] = INREG(TV_MASTER_CNTL); + rinfo->save_regs[89] = INPLL(pllP2PLL_REF_DIV); + rinfo->save_regs[92] = INPLL(pllPPLL_DIV_0); + rinfo->save_regs[93] = INPLL(pllPPLL_CNTL); + rinfo->save_regs[94] = INREG(GRPH_BUFFER_CNTL); + rinfo->save_regs[95] = INREG(GRPH2_BUFFER_CNTL); + rinfo->save_regs[96] = INREG(HDP_DEBUG); + rinfo->save_regs[97] = INPLL(pllMDLL_CKO); + rinfo->save_regs[98] = INPLL(pllMDLL_RDCKA); + rinfo->save_regs[99] = INPLL(pllMDLL_RDCKB); } static void radeon_pm_restore_regs(struct radeonfb_info *rinfo) @@ -270,12 +598,15 @@ static void radeon_pm_restore_regs(struct radeonfb_info *rinfo) OUTPLL(VCLK_ECP_CNTL, rinfo->save_regs[5]); OUTPLL(PIXCLKS_CNTL, rinfo->save_regs[6]); OUTPLL(MCLK_MISC, rinfo->save_regs[7]); - + if (rinfo->family == CHIP_FAMILY_RV350) + OUTPLL(SCLK_MORE_CNTL, rinfo->save_regs[34]); + OUTREG(SURFACE_CNTL, rinfo->save_regs[29]); OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]); OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]); OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]); OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]); + OUTREG(CONFIG_MEMSIZE, rinfo->video_ram); OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]); OUTREG(DISP_PWR_MAN, rinfo->save_regs[10]); @@ -287,10 +618,8 @@ static void radeon_pm_restore_regs(struct radeonfb_info *rinfo) OUTREG(AGP_CNTL, rinfo->save_regs[16]); OUTREG(CRTC_GEN_CNTL, rinfo->save_regs[17]); OUTREG(CRTC2_GEN_CNTL, rinfo->save_regs[18]); - - // wait VBL before that one ? OUTPLL(P2PLL_CNTL, rinfo->save_regs[8]); - + OUTREG(GPIOPAD_A, rinfo->save_regs[19]); OUTREG(GPIOPAD_EN, rinfo->save_regs[20]); OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]); @@ -319,30 +648,32 @@ static void radeon_pm_disable_iopad(struct radeonfb_info *rinfo) static void radeon_pm_program_v2clk(struct radeonfb_info *rinfo) { - /* we use __INPLL and _OUTPLL and do the locking ourselves... */ - unsigned long flags; - spin_lock_irqsave(&rinfo->reg_lock, flags); /* Set v2clk to 65MHz */ - __OUTPLL(pllPIXCLKS_CNTL, - __INPLL(rinfo, pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK); + if (rinfo->family <= CHIP_FAMILY_RV280) { + OUTPLL(pllPIXCLKS_CNTL, + __INPLL(rinfo, pllPIXCLKS_CNTL) + & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK); - __OUTPLL(pllP2PLL_REF_DIV, 0x0000000c); - __OUTPLL(pllP2PLL_CNTL, 0x0000bf00); - __OUTPLL(pllP2PLL_DIV_0, 0x00020074 | P2PLL_DIV_0__P2PLL_ATOMIC_UPDATE_W); + OUTPLL(pllP2PLL_REF_DIV, 0x0000000c); + OUTPLL(pllP2PLL_CNTL, 0x0000bf00); + } else { + OUTPLL(pllP2PLL_REF_DIV, 0x0000000c); + INPLL(pllP2PLL_REF_DIV); + OUTPLL(pllP2PLL_CNTL, 0x0000a700); + } + + OUTPLL(pllP2PLL_DIV_0, 0x00020074 | P2PLL_DIV_0__P2PLL_ATOMIC_UPDATE_W); - __OUTPLL(pllP2PLL_CNTL, - __INPLL(rinfo, pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_SLEEP); + OUTPLL(pllP2PLL_CNTL, INPLL(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_SLEEP); mdelay(1); - __OUTPLL(pllP2PLL_CNTL, - __INPLL(rinfo, pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_RESET); + OUTPLL(pllP2PLL_CNTL, INPLL(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_RESET); mdelay( 1); - __OUTPLL(pllPIXCLKS_CNTL, - (__INPLL(rinfo, pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK) + OUTPLL(pllPIXCLKS_CNTL, + (INPLL(pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK) | (0x03 << PIXCLKS_CNTL__PIX2CLK_SRC_SEL__SHIFT)); mdelay( 1); - spin_unlock_irqrestore(&rinfo->reg_lock, flags); } static void radeon_pm_low_current(struct radeonfb_info *rinfo) @@ -350,8 +681,12 @@ static void radeon_pm_low_current(struct radeonfb_info *rinfo) u32 reg; reg = INREG(BUS_CNTL1); - reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK; - reg |= BUS_CNTL1_AGPCLK_VALID | (1<family <= CHIP_FAMILY_RV280) { + reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK; + reg |= BUS_CNTL1_AGPCLK_VALID | (1<family <= CHIP_FAMILY_RV280) + sclk_cntl |= SCLK_CNTL__FORCE_RE; + else + sclk_cntl |= SCLK_CNTL__SE_MAX_DYN_STOP_LAT | + SCLK_CNTL__E2_MAX_DYN_STOP_LAT | + SCLK_CNTL__TV_MAX_DYN_STOP_LAT | + SCLK_CNTL__HDP_MAX_DYN_STOP_LAT | + SCLK_CNTL__CP_MAX_DYN_STOP_LAT; + + OUTPLL( pllSCLK_CNTL, sclk_cntl); sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL); sclk_more_cntl |= SCLK_MORE_CNTL__FORCE_DISPREGS | @@ -438,18 +780,19 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo) OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl); - mclk_cntl = INPLL( pllMCLK_CNTL_M6); - mclk_cntl &= ~( MCLK_CNTL_M6__FORCE_MCLKA | - MCLK_CNTL_M6__FORCE_MCLKB | - MCLK_CNTL_M6__FORCE_YCLKA | - MCLK_CNTL_M6__FORCE_YCLKB | - MCLK_CNTL_M6__FORCE_MC + mclk_cntl = INPLL( pllMCLK_CNTL); + mclk_cntl &= ~( MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB | + MCLK_CNTL__FORCE_MC ); - OUTPLL( pllMCLK_CNTL_M6, mclk_cntl); + OUTPLL( pllMCLK_CNTL, mclk_cntl); /* Force Display clocks */ vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL); - vclk_ecp_cntl &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); + vclk_ecp_cntl &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb + | VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb); vclk_ecp_cntl |= VCLK_ECP_CNTL__ECP_FORCE_ON; OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl); @@ -480,26 +823,27 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo) OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl); - clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL_M6); + clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL); - clk_pwrmgt_cntl &= ~( CLK_PWRMGT_CNTL_M6__MPLL_PWRMGT_OFF| - CLK_PWRMGT_CNTL_M6__SPLL_PWRMGT_OFF| - CLK_PWRMGT_CNTL_M6__PPLL_PWRMGT_OFF| - CLK_PWRMGT_CNTL_M6__P2PLL_PWRMGT_OFF| - CLK_PWRMGT_CNTL_M6__MCLK_TURNOFF| - CLK_PWRMGT_CNTL_M6__SCLK_TURNOFF| - CLK_PWRMGT_CNTL_M6__PCLK_TURNOFF| - CLK_PWRMGT_CNTL_M6__P2CLK_TURNOFF| - CLK_PWRMGT_CNTL_M6__TVPLL_PWRMGT_OFF| - CLK_PWRMGT_CNTL_M6__GLOBAL_PMAN_EN| - CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE| - CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT_MASK| - CLK_PWRMGT_CNTL_M6__CG_NO1_DEBUG_MASK + clk_pwrmgt_cntl &= ~( CLK_PWRMGT_CNTL__MPLL_PWRMGT_OFF| + CLK_PWRMGT_CNTL__SPLL_PWRMGT_OFF| + CLK_PWRMGT_CNTL__PPLL_PWRMGT_OFF| + CLK_PWRMGT_CNTL__P2PLL_PWRMGT_OFF| + CLK_PWRMGT_CNTL__MCLK_TURNOFF| + CLK_PWRMGT_CNTL__SCLK_TURNOFF| + CLK_PWRMGT_CNTL__PCLK_TURNOFF| + CLK_PWRMGT_CNTL__P2CLK_TURNOFF| + CLK_PWRMGT_CNTL__TVPLL_PWRMGT_OFF| + CLK_PWRMGT_CNTL__GLOBAL_PMAN_EN| + CLK_PWRMGT_CNTL__ENGINE_DYNCLK_MODE| + CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK| + CLK_PWRMGT_CNTL__CG_NO1_DEBUG_MASK ); - clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL_M6__GLOBAL_PMAN_EN | CLK_PWRMGT_CNTL_M6__DISP_PM; + clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL__GLOBAL_PMAN_EN + | CLK_PWRMGT_CNTL__DISP_PM; - OUTPLL( pllCLK_PWRMGT_CNTL_M6, clk_pwrmgt_cntl); + OUTPLL( pllCLK_PWRMGT_CNTL, clk_pwrmgt_cntl); clk_pin_cntl = INPLL( pllCLK_PIN_CNTL); @@ -510,12 +854,19 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo) OUTPLL( pllMCLK_MISC, tmp); /* AGP PLL control */ - OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID); + if (rinfo->family <= CHIP_FAMILY_RV280) { + OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID); + + OUTREG(BUS_CNTL1, + (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK) + | (2<= 2) + mdelay(1); mem_sdram_mode |= MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET; OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode); + if (delay_required >= 2) + mdelay(1); mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET; OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode); + if (delay_required >= 2) + mdelay(1); + + if (delay_required) { + do { + if (delay_required >= 2) + mdelay(1); + } while ((INREG(MC_STATUS) + & (MC_STATUS__MEM_PWRUP_COMPL_A | + MC_STATUS__MEM_PWRUP_COMPL_B)) == 0); + } +} - if (delay_required == 1) - while( (INREG( MC_STATUS) & (MC_STATUS__MEM_PWRUP_COMPL_A | MC_STATUS__MEM_PWRUP_COMPL_B) ) == 0 ) - { }; +static void radeon_pm_m10_program_mode_wait(struct radeonfb_info *rinfo) +{ + int cnt; + + for (cnt = 0; cnt < 100; ++cnt) { + mdelay(1); + if (INREG(MC_STATUS) & (MC_STATUS__MEM_PWRUP_COMPL_A + | MC_STATUS__MEM_PWRUP_COMPL_B)) + break; + } } @@ -646,126 +1042,274 @@ static void radeon_pm_enable_dll(struct radeonfb_info *rinfo) #define DLL_RESET_DELAY 5 #define DLL_SLEEP_DELAY 1 - u32 DLL_CKO_Value = INPLL(pllMDLL_CKO) | MDLL_CKO__MCKOA_SLEEP | MDLL_CKO__MCKOA_RESET; - u32 DLL_CKA_Value = INPLL(pllMDLL_RDCKA) | MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP | MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET; - u32 DLL_CKB_Value = INPLL(pllMDLL_RDCKB) | MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP | MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET; + u32 cko = INPLL(pllMDLL_CKO) | MDLL_CKO__MCKOA_SLEEP + | MDLL_CKO__MCKOA_RESET; + u32 cka = INPLL(pllMDLL_RDCKA) | MDLL_RDCKA__MRDCKA0_SLEEP + | MDLL_RDCKA__MRDCKA1_SLEEP | MDLL_RDCKA__MRDCKA0_RESET + | MDLL_RDCKA__MRDCKA1_RESET; + u32 ckb = INPLL(pllMDLL_RDCKB) | MDLL_RDCKB__MRDCKB0_SLEEP + | MDLL_RDCKB__MRDCKB1_SLEEP | MDLL_RDCKB__MRDCKB0_RESET + | MDLL_RDCKB__MRDCKB1_RESET; /* Setting up the DLL range for write */ - OUTPLL(pllMDLL_CKO, DLL_CKO_Value); - OUTPLL(pllMDLL_RDCKA, DLL_CKA_Value); - OUTPLL(pllMDLL_RDCKB, DLL_CKB_Value); - - mdelay( DLL_RESET_DELAY); - - /* Channel A */ - - /* Power Up */ - DLL_CKO_Value &= ~(MDLL_CKO__MCKOA_SLEEP ); - OUTPLL(pllMDLL_CKO, DLL_CKO_Value); - mdelay( DLL_SLEEP_DELAY); - - DLL_CKO_Value &= ~(MDLL_CKO__MCKOA_RESET ); - OUTPLL(pllMDLL_CKO, DLL_CKO_Value); - mdelay( DLL_RESET_DELAY); - - /* Power Up */ - DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA0_SLEEP ); - OUTPLL(pllMDLL_RDCKA, DLL_CKA_Value); - mdelay( DLL_SLEEP_DELAY); - - DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA0_RESET ); - OUTPLL(pllMDLL_RDCKA, DLL_CKA_Value); - mdelay( DLL_RESET_DELAY); - - /* Power Up */ - DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA1_SLEEP); - OUTPLL(pllMDLL_RDCKA, DLL_CKA_Value); - mdelay( DLL_SLEEP_DELAY); - - DLL_CKA_Value &= ~(MDLL_RDCKA__MRDCKA1_RESET); - OUTPLL(pllMDLL_RDCKA, DLL_CKA_Value); - mdelay( DLL_RESET_DELAY); - + OUTPLL(pllMDLL_CKO, cko); + OUTPLL(pllMDLL_RDCKA, cka); + OUTPLL(pllMDLL_RDCKB, ckb); + + mdelay(DLL_RESET_DELAY*2); + + cko &= ~(MDLL_CKO__MCKOA_SLEEP | MDLL_CKO__MCKOB_SLEEP); + OUTPLL(pllMDLL_CKO, cko); + mdelay(DLL_SLEEP_DELAY); + cko &= ~(MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET); + OUTPLL(pllMDLL_CKO, cko); + mdelay(DLL_RESET_DELAY); + + cka &= ~(MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP); + OUTPLL(pllMDLL_RDCKA, cka); + mdelay(DLL_SLEEP_DELAY); + cka &= ~(MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET); + OUTPLL(pllMDLL_RDCKA, cka); + mdelay(DLL_RESET_DELAY); + + ckb &= ~(MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP); + OUTPLL(pllMDLL_RDCKB, ckb); + mdelay(DLL_SLEEP_DELAY); + ckb &= ~(MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET); + OUTPLL(pllMDLL_RDCKB, ckb); + mdelay(DLL_RESET_DELAY); + + +#undef DLL_RESET_DELAY +#undef DLL_SLEEP_DELAY +} - /* Channel B */ +static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo) +{ + u32 dll_value; + u32 dll_sleep_mask = 0; + u32 dll_reset_mask = 0; + u32 mc; - /* Power Up */ - DLL_CKO_Value &= ~(MDLL_CKO__MCKOB_SLEEP ); - OUTPLL(pllMDLL_CKO, DLL_CKO_Value); - mdelay( DLL_SLEEP_DELAY); - - DLL_CKO_Value &= ~(MDLL_CKO__MCKOB_RESET ); - OUTPLL(pllMDLL_CKO, DLL_CKO_Value); - mdelay( DLL_RESET_DELAY); +#define DLL_RESET_DELAY 5 +#define DLL_SLEEP_DELAY 1 - /* Power Up */ - DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB0_SLEEP); - OUTPLL(pllMDLL_RDCKB, DLL_CKB_Value); - mdelay( DLL_SLEEP_DELAY); + OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]); + mc = INREG(MC_CNTL); + /* Check which channels are enabled */ + switch (mc & 0x3) { + case 1: + if (mc & 0x4) + break; + case 2: + dll_sleep_mask |= MDLL_R300_RDCK__MRDCKB_SLEEP; + dll_reset_mask |= MDLL_R300_RDCK__MRDCKB_RESET; + case 0: + dll_sleep_mask |= MDLL_R300_RDCK__MRDCKA_SLEEP; + dll_reset_mask |= MDLL_R300_RDCK__MRDCKA_RESET; + } + switch (mc & 0x3) { + case 1: + if (!(mc & 0x4)) + break; + case 2: + dll_sleep_mask |= MDLL_R300_RDCK__MRDCKD_SLEEP; + dll_reset_mask |= MDLL_R300_RDCK__MRDCKD_RESET; + dll_sleep_mask |= MDLL_R300_RDCK__MRDCKC_SLEEP; + dll_reset_mask |= MDLL_R300_RDCK__MRDCKC_RESET; + } - DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB0_RESET); - OUTPLL(pllMDLL_RDCKB, DLL_CKB_Value); - mdelay( DLL_RESET_DELAY); + dll_value = INPLL(pllMDLL_RDCKA); /* Power Up */ - DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB1_SLEEP); - OUTPLL(pllMDLL_RDCKB, DLL_CKB_Value); + dll_value &= ~(dll_sleep_mask); + OUTPLL(pllMDLL_RDCKA, dll_value); mdelay( DLL_SLEEP_DELAY); - DLL_CKB_Value &= ~(MDLL_RDCKB__MRDCKB1_RESET); - OUTPLL(pllMDLL_RDCKB, DLL_CKB_Value); + dll_value &= ~(dll_reset_mask); + OUTPLL(pllMDLL_RDCKA, dll_value); mdelay( DLL_RESET_DELAY); #undef DLL_RESET_DELAY #undef DLL_SLEEP_DELAY } + static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) { - u32 crtcGenCntl, crtcGenCntl2, memRefreshCntl, crtc_more_cntl, fp_gen_cntl, fp2_gen_cntl; + u32 crtcGenCntl, crtcGenCntl2, memRefreshCntl, crtc_more_cntl, + fp_gen_cntl, fp2_gen_cntl; crtcGenCntl = INREG( CRTC_GEN_CNTL); crtcGenCntl2 = INREG( CRTC2_GEN_CNTL); - memRefreshCntl = INREG( MEM_REFRESH_CNTL); crtc_more_cntl = INREG( CRTC_MORE_CNTL); fp_gen_cntl = INREG( FP_GEN_CNTL); fp2_gen_cntl = INREG( FP2_GEN_CNTL); - OUTREG( CRTC_MORE_CNTL, 0); - OUTREG( FP_GEN_CNTL, 0); - OUTREG( FP2_GEN_CNTL, 0); + OUTREG( CRTC_MORE_CNTL, 0); + OUTREG( FP_GEN_CNTL, 0); + OUTREG( FP2_GEN_CNTL,0); OUTREG( CRTC_GEN_CNTL, (crtcGenCntl | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B) ); OUTREG( CRTC2_GEN_CNTL, (crtcGenCntl2 | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B) ); - /* Disable refresh */ - OUTREG( MEM_REFRESH_CNTL, memRefreshCntl | MEM_REFRESH_CNTL__MEM_REFRESH_DIS); + /* This is the code for the Aluminium PowerBooks M10 */ + if (rinfo->family == CHIP_FAMILY_RV350) { + u32 sdram_mode_reg = rinfo->save_regs[35]; + static u32 default_mrtable[] = + { 0x21320032, + 0x21321000, 0xa1321000, 0x21321000, 0xffffffff, + 0x21320032, 0xa1320032, 0x21320032, 0xffffffff, + 0x21321002, 0xa1321002, 0x21321002, 0xffffffff, + 0x21320132, 0xa1320132, 0x21320132, 0xffffffff, + 0x21320032, 0xa1320032, 0x21320032, 0xffffffff, + 0x31320032 }; + + u32 *mrtable = default_mrtable; + int i, mrtable_size = ARRAY_SIZE(default_mrtable); + + mdelay(30); + + /* Disable refresh */ + memRefreshCntl = INREG( MEM_REFRESH_CNTL) + & ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS; + OUTREG( MEM_REFRESH_CNTL, memRefreshCntl + | MEM_REFRESH_CNTL__MEM_REFRESH_DIS); + + /* Configure and enable M & SPLLs */ + radeon_pm_enable_dll_m10(rinfo); + radeon_pm_yclk_mclk_sync_m10(rinfo); + +#ifdef CONFIG_PPC_OF + if (rinfo->of_node != NULL) { + int size; + + mrtable = (u32 *)get_property(rinfo->of_node, "ATY,MRT", &size); + if (mrtable) + mrtable_size = size >> 2; + else + mrtable = default_mrtable; + } +#endif /* CONFIG_PPC_OF */ + + /* Program the SDRAM */ + sdram_mode_reg = mrtable[0]; + OUTREG(MEM_SDRAM_MODE_REG, sdram_mode_reg); + for (i = 0; i < mrtable_size; i++) { + if (mrtable[i] == 0xffffffffu) + radeon_pm_m10_program_mode_wait(rinfo); + else { + sdram_mode_reg &= ~(MEM_SDRAM_MODE_REG__MEM_MODE_REG_MASK + | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE + | MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET); + sdram_mode_reg |= mrtable[i]; + + OUTREG(MEM_SDRAM_MODE_REG, sdram_mode_reg); + mdelay(1); + } + } + + /* Restore memory refresh */ + OUTREG(MEM_REFRESH_CNTL, memRefreshCntl); + mdelay(30); + + } + /* Here come the desktop RV200 "QW" card */ + else if (!rinfo->is_mobility && rinfo->family == CHIP_FAMILY_RV200) { + /* Disable refresh */ + memRefreshCntl = INREG( MEM_REFRESH_CNTL) + & ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS; + OUTREG(MEM_REFRESH_CNTL, memRefreshCntl + | MEM_REFRESH_CNTL__MEM_REFRESH_DIS); + mdelay(30); + + /* Reset memory */ + OUTREG(MEM_SDRAM_MODE_REG, + INREG( MEM_SDRAM_MODE_REG) & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); + + radeon_pm_program_mode_reg(rinfo, 0x2002, 2); + radeon_pm_program_mode_reg(rinfo, 0x0132, 2); + radeon_pm_program_mode_reg(rinfo, 0x0032, 2); + + OUTREG(MEM_SDRAM_MODE_REG, + INREG(MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); + + OUTREG( MEM_REFRESH_CNTL, memRefreshCntl); + + } + /* The M6 */ + else if (rinfo->is_mobility && rinfo->family == CHIP_FAMILY_RV100) { + /* Disable refresh */ + memRefreshCntl = INREG(EXT_MEM_CNTL) & ~(1 << 20); + OUTREG( EXT_MEM_CNTL, memRefreshCntl | (1 << 20)); - /* Reset memory */ - OUTREG( MEM_SDRAM_MODE_REG, - INREG( MEM_SDRAM_MODE_REG) & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); // Init Not Complete + /* Reset memory */ + OUTREG( MEM_SDRAM_MODE_REG, + INREG( MEM_SDRAM_MODE_REG) + & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); - /* DLL */ - radeon_pm_enable_dll(rinfo); + /* DLL */ + radeon_pm_enable_dll(rinfo); - // MLCK /YCLK sync - radeon_pm_yclk_mclk_sync(rinfo); + /* MLCK / YCLK sync */ + radeon_pm_yclk_mclk_sync(rinfo); - /* M6, M7 and M9 so far ... */ - if (rinfo->is_mobility && rinfo->family <= CHIP_FAMILY_RV250) { + /* Program Mode Register */ radeon_pm_program_mode_reg(rinfo, 0x2000, 1); radeon_pm_program_mode_reg(rinfo, 0x2001, 1); radeon_pm_program_mode_reg(rinfo, 0x2002, 1); radeon_pm_program_mode_reg(rinfo, 0x0132, 1); radeon_pm_program_mode_reg(rinfo, 0x0032, 1); - } - OUTREG( MEM_SDRAM_MODE_REG, - INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); // Init Complete + /* Complete & re-enable refresh */ + OUTREG( MEM_SDRAM_MODE_REG, + INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); + + OUTREG(EXT_MEM_CNTL, memRefreshCntl); + } + /* And finally, the M7..M9 models, including M9+ (RV280) */ + else if (rinfo->is_mobility) { + + /* Disable refresh */ + memRefreshCntl = INREG( MEM_REFRESH_CNTL) + & ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS; + OUTREG( MEM_REFRESH_CNTL, memRefreshCntl + | MEM_REFRESH_CNTL__MEM_REFRESH_DIS); + + /* Reset memory */ + OUTREG( MEM_SDRAM_MODE_REG, + INREG( MEM_SDRAM_MODE_REG) + & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); + + /* DLL */ + radeon_pm_enable_dll(rinfo); + + /* MLCK / YCLK sync */ + radeon_pm_yclk_mclk_sync(rinfo); + + /* M6, M7 and M9 so far ... */ + if (rinfo->family <= CHIP_FAMILY_RV250) { + radeon_pm_program_mode_reg(rinfo, 0x2000, 1); + radeon_pm_program_mode_reg(rinfo, 0x2001, 1); + radeon_pm_program_mode_reg(rinfo, 0x2002, 1); + radeon_pm_program_mode_reg(rinfo, 0x0132, 1); + radeon_pm_program_mode_reg(rinfo, 0x0032, 1); + } + /* M9+ (iBook G4) */ + else if (rinfo->family == CHIP_FAMILY_RV280) { + radeon_pm_program_mode_reg(rinfo, 0x2000, 1); + radeon_pm_program_mode_reg(rinfo, 0x0132, 1); + radeon_pm_program_mode_reg(rinfo, 0x0032, 1); + } + + /* Complete & re-enable refresh */ + OUTREG( MEM_SDRAM_MODE_REG, + INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); - OUTREG( MEM_REFRESH_CNTL, memRefreshCntl); + OUTREG( MEM_REFRESH_CNTL, memRefreshCntl); + } OUTREG( CRTC_GEN_CNTL, crtcGenCntl); OUTREG( CRTC2_GEN_CNTL, crtcGenCntl2); @@ -777,10 +1321,1076 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) mdelay( 15); } +#ifdef CONFIG_PPC_OF + +static void radeon_pm_reset_pad_ctlr_strength(struct radeonfb_info *rinfo) +{ + u32 tmp, tmp2; + int i,j; + + /* Reset the PAD_CTLR_STRENGTH & wait for it to be stable */ + INREG(PAD_CTLR_STRENGTH); + OUTREG(PAD_CTLR_STRENGTH, INREG(PAD_CTLR_STRENGTH) & ~PAD_MANUAL_OVERRIDE); + tmp = INREG(PAD_CTLR_STRENGTH); + for (i = j = 0; i < 65; ++i) { + mdelay(1); + tmp2 = INREG(PAD_CTLR_STRENGTH); + if (tmp != tmp2) { + tmp = tmp2; + i = 0; + j++; + if (j > 10) { + printk(KERN_WARNING "radeon: PAD_CTLR_STRENGTH doesn't " + "stabilize !\n"); + break; + } + } + } +} + +static void radeon_pm_all_ppls_off(struct radeonfb_info *rinfo) +{ + u32 tmp; + + tmp = INPLL(pllPPLL_CNTL); + OUTPLL(pllPPLL_CNTL, tmp | 0x3); + tmp = INPLL(pllP2PLL_CNTL); + OUTPLL(pllP2PLL_CNTL, tmp | 0x3); + tmp = INPLL(pllSPLL_CNTL); + OUTPLL(pllSPLL_CNTL, tmp | 0x3); + tmp = INPLL(pllMPLL_CNTL); + OUTPLL(pllMPLL_CNTL, tmp | 0x3); +} + +static void radeon_pm_start_mclk_sclk(struct radeonfb_info *rinfo) +{ + u32 tmp; + + /* Switch SPLL to PCI source */ + tmp = INPLL(pllSCLK_CNTL); + OUTPLL(pllSCLK_CNTL, tmp & ~SCLK_CNTL__SCLK_SRC_SEL_MASK); + + /* Reconfigure SPLL charge pump, VCO gain, duty cycle */ + tmp = INPLL(pllSPLL_CNTL); + OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + + /* Set SPLL feedback divider */ + tmp = INPLL(pllM_SPLL_REF_FB_DIV); + tmp = (tmp & 0xff00fffful) | (rinfo->save_regs[77] & 0x00ff0000ul); + OUTPLL(pllM_SPLL_REF_FB_DIV, tmp); + + /* Power up SPLL */ + tmp = INPLL(pllSPLL_CNTL); + OUTPLL(pllSPLL_CNTL, tmp & ~1); + (void)INPLL(pllSPLL_CNTL); + + mdelay(10); + + /* Release SPLL reset */ + tmp = INPLL(pllSPLL_CNTL); + OUTPLL(pllSPLL_CNTL, tmp & ~0x2); + (void)INPLL(pllSPLL_CNTL); + + mdelay(10); + + /* Select SCLK source */ + tmp = INPLL(pllSCLK_CNTL); + tmp &= ~SCLK_CNTL__SCLK_SRC_SEL_MASK; + tmp |= rinfo->save_regs[3] & SCLK_CNTL__SCLK_SRC_SEL_MASK; + OUTPLL(pllSCLK_CNTL, tmp); + (void)INPLL(pllSCLK_CNTL); + + mdelay(10); + + /* Reconfigure MPLL charge pump, VCO gain, duty cycle */ + tmp = INPLL(pllMPLL_CNTL); + OUTREG8(CLOCK_CNTL_INDEX, pllMPLL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + + /* Set MPLL feedback divider */ + tmp = INPLL(pllM_SPLL_REF_FB_DIV); + tmp = (tmp & 0xffff00fful) | (rinfo->save_regs[77] & 0x0000ff00ul); + + OUTPLL(pllM_SPLL_REF_FB_DIV, tmp); + /* Power up MPLL */ + tmp = INPLL(pllMPLL_CNTL); + OUTPLL(pllMPLL_CNTL, tmp & ~0x2); + (void)INPLL(pllMPLL_CNTL); + + mdelay(10); + + /* Un-reset MPLL */ + tmp = INPLL(pllMPLL_CNTL); + OUTPLL(pllMPLL_CNTL, tmp & ~0x1); + (void)INPLL(pllMPLL_CNTL); + + mdelay(10); + + /* Select source for MCLK */ + tmp = INPLL(pllMCLK_CNTL); + tmp |= rinfo->save_regs[2] & 0xffff; + OUTPLL(pllMCLK_CNTL, tmp); + (void)INPLL(pllMCLK_CNTL); + + mdelay(10); +} + +static void radeon_pm_m10_disable_spread_spectrum(struct radeonfb_info *rinfo) +{ + u32 r2ec; + + /* GACK ! I though we didn't have a DDA on Radeon's anymore + * here we rewrite with the same value, ... I suppose we clear + * some bits that are already clear ? Or maybe this 0x2ec + * register is something new ? + */ + mdelay(20); + r2ec = INREG(VGA_DDA_ON_OFF); + OUTREG(VGA_DDA_ON_OFF, r2ec); + mdelay(1); + + /* Spread spectrum PLLL off */ + OUTPLL(pllSSPLL_CNTL, 0xbf03); + + /* Spread spectrum disabled */ + OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90] & ~3); + + /* The trace shows read & rewrite of LVDS_PLL_CNTL here with same + * value, not sure what for... + */ + + r2ec |= 0x3f0; + OUTREG(VGA_DDA_ON_OFF, r2ec); + mdelay(1); +} + +static void radeon_pm_m10_enable_lvds_spread_spectrum(struct radeonfb_info *rinfo) +{ + u32 r2ec, tmp; + + /* GACK (bis) ! I though we didn't have a DDA on Radeon's anymore + * here we rewrite with the same value, ... I suppose we clear/set + * some bits that are already clear/set ? + */ + r2ec = INREG(VGA_DDA_ON_OFF); + OUTREG(VGA_DDA_ON_OFF, r2ec); + mdelay(1); + + /* Enable spread spectrum */ + OUTPLL(pllSSPLL_CNTL, rinfo->save_regs[43] | 3); + mdelay(3); + + OUTPLL(pllSSPLL_REF_DIV, rinfo->save_regs[44]); + OUTPLL(pllSSPLL_DIV_0, rinfo->save_regs[45]); + tmp = INPLL(pllSSPLL_CNTL); + OUTPLL(pllSSPLL_CNTL, tmp & ~0x2); + mdelay(6); + tmp = INPLL(pllSSPLL_CNTL); + OUTPLL(pllSSPLL_CNTL, tmp & ~0x1); + mdelay(5); + + OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90]); + + r2ec |= 8; + OUTREG(VGA_DDA_ON_OFF, r2ec); + mdelay(20); + + /* Enable LVDS interface */ + tmp = INREG(LVDS_GEN_CNTL); + OUTREG(LVDS_GEN_CNTL, tmp | LVDS_EN); + + /* Enable LVDS_PLL */ + tmp = INREG(LVDS_PLL_CNTL); + tmp &= ~0x30000; + tmp |= 0x10000; + OUTREG(LVDS_PLL_CNTL, tmp); + + OUTPLL(pllSCLK_MORE_CNTL, rinfo->save_regs[34]); + OUTPLL(pllSS_TST_CNTL, rinfo->save_regs[91]); + + /* The trace reads that one here, waiting for something to settle down ? */ + INREG(RBBM_STATUS); + + /* Ugh ? SS_TST_DEC is supposed to be a read register in the + * R300 register spec at least... + */ + tmp = INPLL(pllSS_TST_CNTL); + tmp |= 0x00400000; + OUTPLL(pllSS_TST_CNTL, tmp); +} + +static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo) +{ + u32 tmp; + + OUTREG8(CLOCK_CNTL_INDEX, pllHTOTAL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA, 0); + + tmp = INPLL(pllVCLK_ECP_CNTL); + OUTPLL(pllVCLK_ECP_CNTL, tmp | 0x80); + mdelay(5); + + tmp = INPLL(pllPPLL_REF_DIV); + tmp = (tmp & ~PPLL_REF_DIV_MASK) | rinfo->pll.ref_div; + OUTPLL(pllPPLL_REF_DIV, tmp); + INPLL(pllPPLL_REF_DIV); + + /* Reconfigure SPLL charge pump, VCO gain, duty cycle, + * probably useless since we already did it ... + */ + tmp = INPLL(pllPPLL_CNTL); + OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + + /* Not sure what was intended here ... */ + tmp = INREG(CLOCK_CNTL_INDEX); + OUTREG(CLOCK_CNTL_INDEX, tmp); + + /* Restore our "reference" PPLL divider set by firmware + * according to proper spread spectrum calculations + */ + OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]); + + tmp = INPLL(pllPPLL_CNTL); + OUTPLL(pllPPLL_CNTL, tmp & ~0x2); + mdelay(5); + + tmp = INPLL(pllPPLL_CNTL); + OUTPLL(pllPPLL_CNTL, tmp & ~0x1); + mdelay(5); + + tmp = INPLL(pllVCLK_ECP_CNTL); + OUTPLL(pllVCLK_ECP_CNTL, tmp | 3); + mdelay(5); + + tmp = INPLL(pllVCLK_ECP_CNTL); + OUTPLL(pllVCLK_ECP_CNTL, tmp | 3); + mdelay(5); + + /* Switch pixel clock to firmware default div 0 */ + OUTREG8(CLOCK_CNTL_INDEX+1, 0); +} + +static void radeon_pm_m10_reconfigure_mc(struct radeonfb_info *rinfo) +{ + OUTREG(MC_CNTL, rinfo->save_regs[46]); + OUTREG(MC_INIT_GFX_LAT_TIMER, rinfo->save_regs[47]); + OUTREG(MC_INIT_MISC_LAT_TIMER, rinfo->save_regs[48]); + OUTREG(MEM_SDRAM_MODE_REG, + rinfo->save_regs[35] & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); + OUTREG(MC_TIMING_CNTL, rinfo->save_regs[49]); + OUTREG(MEM_REFRESH_CNTL, rinfo->save_regs[42]); + OUTREG(MC_READ_CNTL_AB, rinfo->save_regs[50]); + OUTREG(MC_CHIP_IO_OE_CNTL_AB, rinfo->save_regs[52]); + OUTREG(MC_IOPAD_CNTL, rinfo->save_regs[51]); + OUTREG(MC_DEBUG, rinfo->save_regs[53]); + + OUTMC(rinfo, ixR300_MC_MC_INIT_WR_LAT_TIMER, rinfo->save_regs[58]); + OUTMC(rinfo, ixR300_MC_IMP_CNTL, rinfo->save_regs[59]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_C0, rinfo->save_regs[60]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_C1, rinfo->save_regs[61]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_D0, rinfo->save_regs[62]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_D1, rinfo->save_regs[63]); + OUTMC(rinfo, ixR300_MC_BIST_CNTL_3, rinfo->save_regs[64]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_A0, rinfo->save_regs[65]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1, rinfo->save_regs[66]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_B0, rinfo->save_regs[67]); + OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1, rinfo->save_regs[68]); + OUTMC(rinfo, ixR300_MC_DEBUG_CNTL, rinfo->save_regs[69]); + OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]); + OUTMC(rinfo, ixR300_MC_IMP_CNTL_0, rinfo->save_regs[71]); + OUTMC(rinfo, ixR300_MC_ELPIDA_CNTL, rinfo->save_regs[72]); + OUTMC(rinfo, ixR300_MC_READ_CNTL_CD, rinfo->save_regs[96]); + OUTREG(MC_IND_INDEX, 0); +} + +static void radeon_reinitialize_M10(struct radeonfb_info *rinfo) +{ + u32 tmp, i; + + /* Restore a bunch of registers first */ + OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]); + OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]); + OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]); + OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]); + OUTREG(OV0_BASE_ADDR, rinfo->save_regs[80]); + OUTREG(CONFIG_MEMSIZE, rinfo->video_ram); + OUTREG(BUS_CNTL, rinfo->save_regs[36]); + OUTREG(BUS_CNTL1, rinfo->save_regs[14]); + OUTREG(MPP_TB_CONFIG, rinfo->save_regs[37]); + OUTREG(FCP_CNTL, rinfo->save_regs[38]); + OUTREG(RBBM_CNTL, rinfo->save_regs[39]); + OUTREG(DAC_CNTL, rinfo->save_regs[40]); + OUTREG(DAC_MACRO_CNTL, (INREG(DAC_MACRO_CNTL) & ~0x6) | 8); + OUTREG(DAC_MACRO_CNTL, (INREG(DAC_MACRO_CNTL) & ~0x6) | 8); + + /* Hrm... */ + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | DAC2_EXPAND_MODE); + + /* Reset the PAD CTLR */ + radeon_pm_reset_pad_ctlr_strength(rinfo); + + /* Some PLLs are Read & written identically in the trace here... + * I suppose it's actually to switch them all off & reset, + * let's assume off is what we want. I'm just doing that for all major PLLs now. + */ + radeon_pm_all_ppls_off(rinfo); + + /* Clear tiling, reset swappers */ + INREG(SURFACE_CNTL); + OUTREG(SURFACE_CNTL, 0); + + /* Some black magic with TV_DAC_CNTL, we should restore those from backups + * rather than hard coding... + */ + tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_BGADJ_MASK; + tmp |= 8 << TV_DAC_CNTL_BGADJ__SHIFT; + OUTREG(TV_DAC_CNTL, tmp); + + tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_DACADJ_MASK; + tmp |= 7 << TV_DAC_CNTL_DACADJ__SHIFT; + OUTREG(TV_DAC_CNTL, tmp); + + /* More registers restored */ + OUTREG(AGP_CNTL, rinfo->save_regs[16]); + OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]); + OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]); + + /* Hrmmm ... What is that ? */ + tmp = rinfo->save_regs[1] + & ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK | + CLK_PWRMGT_CNTL__MC_BUSY); + OUTPLL(pllCLK_PWRMGT_CNTL, tmp); + + OUTREG(PAD_CTLR_MISC, rinfo->save_regs[56]); + OUTREG(FW_CNTL, rinfo->save_regs[57]); + OUTREG(HDP_DEBUG, rinfo->save_regs[96]); + OUTREG(PAMAC0_DLY_CNTL, rinfo->save_regs[54]); + OUTREG(PAMAC1_DLY_CNTL, rinfo->save_regs[55]); + OUTREG(PAMAC2_DLY_CNTL, rinfo->save_regs[79]); + + /* Restore Memory Controller configuration */ + radeon_pm_m10_reconfigure_mc(rinfo); + + /* Make sure CRTC's dont touch memory */ + OUTREG(CRTC_GEN_CNTL, INREG(CRTC_GEN_CNTL) + | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B); + OUTREG(CRTC2_GEN_CNTL, INREG(CRTC2_GEN_CNTL) + | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B); + mdelay(30); + + /* Disable SDRAM refresh */ + OUTREG(MEM_REFRESH_CNTL, INREG(MEM_REFRESH_CNTL) + | MEM_REFRESH_CNTL__MEM_REFRESH_DIS); + + /* Restore XTALIN routing (CLK_PIN_CNTL) */ + OUTPLL(pllCLK_PIN_CNTL, rinfo->save_regs[4]); + + /* Switch MCLK, YCLK and SCLK PLLs to PCI source & force them ON */ + tmp = rinfo->save_regs[2] & 0xff000000; + tmp |= MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB | + MCLK_CNTL__FORCE_MC; + OUTPLL(pllMCLK_CNTL, tmp); + + /* Force all clocks on in SCLK */ + tmp = INPLL(pllSCLK_CNTL); + tmp |= SCLK_CNTL__FORCE_DISP2| + SCLK_CNTL__FORCE_CP| + SCLK_CNTL__FORCE_HDP| + SCLK_CNTL__FORCE_DISP1| + SCLK_CNTL__FORCE_TOP| + SCLK_CNTL__FORCE_E2| + SCLK_CNTL__FORCE_SE| + SCLK_CNTL__FORCE_IDCT| + SCLK_CNTL__FORCE_VIP| + SCLK_CNTL__FORCE_PB| + SCLK_CNTL__FORCE_TAM| + SCLK_CNTL__FORCE_TDM| + SCLK_CNTL__FORCE_RB| + SCLK_CNTL__FORCE_TV_SCLK| + SCLK_CNTL__FORCE_SUBPIC| + SCLK_CNTL__FORCE_OV0; + tmp |= SCLK_CNTL__CP_MAX_DYN_STOP_LAT | + SCLK_CNTL__HDP_MAX_DYN_STOP_LAT | + SCLK_CNTL__TV_MAX_DYN_STOP_LAT | + SCLK_CNTL__E2_MAX_DYN_STOP_LAT | + SCLK_CNTL__SE_MAX_DYN_STOP_LAT | + SCLK_CNTL__IDCT_MAX_DYN_STOP_LAT| + SCLK_CNTL__VIP_MAX_DYN_STOP_LAT | + SCLK_CNTL__RE_MAX_DYN_STOP_LAT | + SCLK_CNTL__PB_MAX_DYN_STOP_LAT | + SCLK_CNTL__TAM_MAX_DYN_STOP_LAT | + SCLK_CNTL__TDM_MAX_DYN_STOP_LAT | + SCLK_CNTL__RB_MAX_DYN_STOP_LAT; + OUTPLL(pllSCLK_CNTL, tmp); + + OUTPLL(pllVCLK_ECP_CNTL, 0); + OUTPLL(pllPIXCLKS_CNTL, 0); + OUTPLL(pllMCLK_MISC, + MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT | + MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT); + + mdelay(5); + + /* Restore the M_SPLL_REF_FB_DIV, MPLL_AUX_CNTL and SPLL_AUX_CNTL values */ + OUTPLL(pllM_SPLL_REF_FB_DIV, rinfo->save_regs[77]); + OUTPLL(pllMPLL_AUX_CNTL, rinfo->save_regs[75]); + OUTPLL(pllSPLL_AUX_CNTL, rinfo->save_regs[76]); + + /* Now restore the major PLLs settings, keeping them off & reset though */ + OUTPLL(pllPPLL_CNTL, rinfo->save_regs[93] | 0x3); + OUTPLL(pllP2PLL_CNTL, rinfo->save_regs[8] | 0x3); + OUTPLL(pllMPLL_CNTL, rinfo->save_regs[73] | 0x03); + OUTPLL(pllSPLL_CNTL, rinfo->save_regs[74] | 0x03); + + /* Restore MC DLL state and switch it off/reset too */ + OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]); + + /* Switch MDLL off & reset */ + OUTPLL(pllMDLL_RDCKA, rinfo->save_regs[98] | 0xff); + mdelay(5); + + /* Setup some black magic bits in PLL_PWRMGT_CNTL. Hrm... we saved + * 0xa1100007... and MacOS writes 0xa1000007 .. + */ + OUTPLL(pllPLL_PWRMGT_CNTL, rinfo->save_regs[0]); + + /* Restore more stuffs */ + OUTPLL(pllHTOTAL_CNTL, 0); + OUTPLL(pllHTOTAL2_CNTL, 0); + + /* More PLL initial configuration */ + tmp = INPLL(pllSCLK_CNTL2); /* What for ? */ + OUTPLL(pllSCLK_CNTL2, tmp); + + tmp = INPLL(pllSCLK_MORE_CNTL); + tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS | /* a guess */ + SCLK_MORE_CNTL__FORCE_MC_GUI | + SCLK_MORE_CNTL__FORCE_MC_HOST; + OUTPLL(pllSCLK_MORE_CNTL, tmp); + + /* Now we actually start MCLK and SCLK */ + radeon_pm_start_mclk_sclk(rinfo); + + /* Full reset sdrams, this also re-inits the MDLL */ + radeon_pm_full_reset_sdram(rinfo); + + /* Fill palettes */ + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x20); + for (i=0; i<256; i++) + OUTREG(PALETTE_30_DATA, 0x15555555); + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~20); + udelay(20); + for (i=0; i<256; i++) + OUTREG(PALETTE_30_DATA, 0x15555555); + + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~0x20); + mdelay(3); + + /* Restore TMDS */ + OUTREG(FP_GEN_CNTL, rinfo->save_regs[82]); + OUTREG(FP2_GEN_CNTL, rinfo->save_regs[83]); + + /* Set LVDS registers but keep interface & pll down */ + OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11] & + ~(LVDS_EN | LVDS_ON | LVDS_DIGON | LVDS_BLON | LVDS_BL_MOD_EN)); + OUTREG(LVDS_PLL_CNTL, (rinfo->save_regs[12] & ~0xf0000) | 0x20000); + + OUTREG(DISP_OUTPUT_CNTL, rinfo->save_regs[86]); + + /* Restore GPIOPAD state */ + OUTREG(GPIOPAD_A, rinfo->save_regs[19]); + OUTREG(GPIOPAD_EN, rinfo->save_regs[20]); + OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]); + + /* write some stuff to the framebuffer... */ + for (i = 0; i < 0x8000; ++i) + writeb(0, rinfo->fb_base + i); + + mdelay(40); + OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_DIGON | LVDS_ON); + mdelay(40); + + /* Restore a few more things */ + OUTREG(GRPH_BUFFER_CNTL, rinfo->save_regs[94]); + OUTREG(GRPH2_BUFFER_CNTL, rinfo->save_regs[95]); + + /* Take care of spread spectrum & PPLLs now */ + radeon_pm_m10_disable_spread_spectrum(rinfo); + radeon_pm_restore_pixel_pll(rinfo); + + /* GRRRR... I can't figure out the proper LVDS power sequence, and the + * code I have for blank/unblank doesn't quite work on some laptop models + * it seems ... Hrm. What I have here works most of the time ... + */ + radeon_pm_m10_enable_lvds_spread_spectrum(rinfo); +} + +static void radeon_pm_m9p_reconfigure_mc(struct radeonfb_info *rinfo) +{ + OUTREG(MC_CNTL, rinfo->save_regs[46]); + OUTREG(MC_INIT_GFX_LAT_TIMER, rinfo->save_regs[47]); + OUTREG(MC_INIT_MISC_LAT_TIMER, rinfo->save_regs[48]); + OUTREG(MEM_SDRAM_MODE_REG, + rinfo->save_regs[35] & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE); + OUTREG(MC_TIMING_CNTL, rinfo->save_regs[49]); + OUTREG(MC_READ_CNTL_AB, rinfo->save_regs[50]); + OUTREG(MEM_REFRESH_CNTL, rinfo->save_regs[42]); + OUTREG(MC_IOPAD_CNTL, rinfo->save_regs[51]); + OUTREG(MC_DEBUG, rinfo->save_regs[53]); + OUTREG(MC_CHIP_IO_OE_CNTL_AB, rinfo->save_regs[52]); + + OUTMC(rinfo, ixMC_IMP_CNTL, rinfo->save_regs[59] /*0x00f460d6*/); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_A0, rinfo->save_regs[65] /*0xfecfa666*/); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, rinfo->save_regs[66] /*0x141555ff*/); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_B0, rinfo->save_regs[67] /*0xfecfa666*/); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, rinfo->save_regs[68] /*0x141555ff*/); + OUTMC(rinfo, ixMC_IMP_CNTL_0, rinfo->save_regs[71] /*0x00009249*/); + OUTREG(MC_IND_INDEX, 0); + OUTREG(CONFIG_MEMSIZE, rinfo->video_ram); + + mdelay(20); +} + +static void radeon_reinitialize_M9P(struct radeonfb_info *rinfo) +{ + u32 tmp, i; + + /* Restore a bunch of registers first */ + OUTREG(SURFACE_CNTL, rinfo->save_regs[29]); + OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]); + OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]); + OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]); + OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]); + OUTREG(OV0_BASE_ADDR, rinfo->save_regs[80]); + OUTREG(BUS_CNTL, rinfo->save_regs[36]); + OUTREG(BUS_CNTL1, rinfo->save_regs[14]); + OUTREG(MPP_TB_CONFIG, rinfo->save_regs[37]); + OUTREG(FCP_CNTL, rinfo->save_regs[38]); + OUTREG(RBBM_CNTL, rinfo->save_regs[39]); + + OUTREG(DAC_CNTL, rinfo->save_regs[40]); + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | DAC2_EXPAND_MODE); + + /* Reset the PAD CTLR */ + radeon_pm_reset_pad_ctlr_strength(rinfo); + + /* Some PLLs are Read & written identically in the trace here... + * I suppose it's actually to switch them all off & reset, + * let's assume off is what we want. I'm just doing that for all major PLLs now. + */ + radeon_pm_all_ppls_off(rinfo); + + /* Clear tiling, reset swappers */ + INREG(SURFACE_CNTL); + OUTREG(SURFACE_CNTL, 0); + + /* Some black magic with TV_DAC_CNTL, we should restore those from backups + * rather than hard coding... + */ + tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_BGADJ_MASK; + tmp |= 6 << TV_DAC_CNTL_BGADJ__SHIFT; + OUTREG(TV_DAC_CNTL, tmp); + + tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_DACADJ_MASK; + tmp |= 6 << TV_DAC_CNTL_DACADJ__SHIFT; + OUTREG(TV_DAC_CNTL, tmp); + + OUTPLL(pllAGP_PLL_CNTL, rinfo->save_regs[78]); + + OUTREG(PAMAC0_DLY_CNTL, rinfo->save_regs[54]); + OUTREG(PAMAC1_DLY_CNTL, rinfo->save_regs[55]); + OUTREG(PAMAC2_DLY_CNTL, rinfo->save_regs[79]); + + OUTREG(AGP_CNTL, rinfo->save_regs[16]); + OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]); /* MacOS sets that to 0 !!! */ + OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]); + + tmp = rinfo->save_regs[1] + & ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK | + CLK_PWRMGT_CNTL__MC_BUSY); + OUTPLL(pllCLK_PWRMGT_CNTL, tmp); + + OUTREG(FW_CNTL, rinfo->save_regs[57]); + + /* Disable SDRAM refresh */ + OUTREG(MEM_REFRESH_CNTL, INREG(MEM_REFRESH_CNTL) + | MEM_REFRESH_CNTL__MEM_REFRESH_DIS); + + /* Restore XTALIN routing (CLK_PIN_CNTL) */ + OUTPLL(pllCLK_PIN_CNTL, rinfo->save_regs[4]); + + /* Force MCLK to be PCI sourced and forced ON */ + tmp = rinfo->save_regs[2] & 0xff000000; + tmp |= MCLK_CNTL__FORCE_MCLKA | + MCLK_CNTL__FORCE_MCLKB | + MCLK_CNTL__FORCE_YCLKA | + MCLK_CNTL__FORCE_YCLKB | + MCLK_CNTL__FORCE_MC | + MCLK_CNTL__FORCE_AIC; + OUTPLL(pllMCLK_CNTL, tmp); + + /* Force SCLK to be PCI sourced with a bunch forced */ + tmp = 0 | + SCLK_CNTL__FORCE_DISP2| + SCLK_CNTL__FORCE_CP| + SCLK_CNTL__FORCE_HDP| + SCLK_CNTL__FORCE_DISP1| + SCLK_CNTL__FORCE_TOP| + SCLK_CNTL__FORCE_E2| + SCLK_CNTL__FORCE_SE| + SCLK_CNTL__FORCE_IDCT| + SCLK_CNTL__FORCE_VIP| + SCLK_CNTL__FORCE_RE| + SCLK_CNTL__FORCE_PB| + SCLK_CNTL__FORCE_TAM| + SCLK_CNTL__FORCE_TDM| + SCLK_CNTL__FORCE_RB; + OUTPLL(pllSCLK_CNTL, tmp); + + /* Clear VCLK_ECP_CNTL & PIXCLKS_CNTL */ + OUTPLL(pllVCLK_ECP_CNTL, 0); + OUTPLL(pllPIXCLKS_CNTL, 0); + + /* Setup MCLK_MISC, non dynamic mode */ + OUTPLL(pllMCLK_MISC, + MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT | + MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT); + + mdelay(5); + + /* Set back the default clock dividers */ + OUTPLL(pllM_SPLL_REF_FB_DIV, rinfo->save_regs[77]); + OUTPLL(pllMPLL_AUX_CNTL, rinfo->save_regs[75]); + OUTPLL(pllSPLL_AUX_CNTL, rinfo->save_regs[76]); + + /* PPLL and P2PLL default values & off */ + OUTPLL(pllPPLL_CNTL, rinfo->save_regs[93] | 0x3); + OUTPLL(pllP2PLL_CNTL, rinfo->save_regs[8] | 0x3); + + /* S and M PLLs are reset & off, configure them */ + OUTPLL(pllMPLL_CNTL, rinfo->save_regs[73] | 0x03); + OUTPLL(pllSPLL_CNTL, rinfo->save_regs[74] | 0x03); + + /* Default values for MDLL ... fixme */ + OUTPLL(pllMDLL_CKO, 0x9c009c); + OUTPLL(pllMDLL_RDCKA, 0x08830883); + OUTPLL(pllMDLL_RDCKB, 0x08830883); + mdelay(5); + + /* Restore PLL_PWRMGT_CNTL */ // XXXX + tmp = rinfo->save_regs[0]; + tmp &= ~PLL_PWRMGT_CNTL_SU_SCLK_USE_BCLK; + tmp |= PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK; + OUTPLL(PLL_PWRMGT_CNTL, tmp); + + /* Clear HTOTAL_CNTL & HTOTAL2_CNTL */ + OUTPLL(pllHTOTAL_CNTL, 0); + OUTPLL(pllHTOTAL2_CNTL, 0); + + /* All outputs off */ + OUTREG(CRTC_GEN_CNTL, 0x04000000); + OUTREG(CRTC2_GEN_CNTL, 0x04000000); + OUTREG(FP_GEN_CNTL, 0x00004008); + OUTREG(FP2_GEN_CNTL, 0x00000008); + OUTREG(LVDS_GEN_CNTL, 0x08000008); + + /* Restore Memory Controller configuration */ + radeon_pm_m9p_reconfigure_mc(rinfo); + + /* Now we actually start MCLK and SCLK */ + radeon_pm_start_mclk_sclk(rinfo); + + /* Full reset sdrams, this also re-inits the MDLL */ + radeon_pm_full_reset_sdram(rinfo); + + /* Fill palettes */ + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x20); + for (i=0; i<256; i++) + OUTREG(PALETTE_30_DATA, 0x15555555); + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~20); + udelay(20); + for (i=0; i<256; i++) + OUTREG(PALETTE_30_DATA, 0x15555555); + + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~0x20); + mdelay(3); + + /* Restore TV stuff, make sure TV DAC is down */ + OUTREG(TV_MASTER_CNTL, rinfo->save_regs[88]); + OUTREG(TV_DAC_CNTL, rinfo->save_regs[13] | 0x07000000); + + /* Restore GPIOS. MacOS does some magic here with one of the GPIO bits, + * possibly related to the weird PLL related workarounds and to the + * fact that CLK_PIN_CNTL is tweaked in ways I don't fully understand, + * but we keep things the simple way here + */ + OUTREG(GPIOPAD_A, rinfo->save_regs[19]); + OUTREG(GPIOPAD_EN, rinfo->save_regs[20]); + OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]); + + /* Now do things with SCLK_MORE_CNTL. Force bits are already set, copy + * high bits from backup + */ + tmp = INPLL(pllSCLK_MORE_CNTL) & 0x0000ffff; + tmp |= rinfo->save_regs[34] & 0xffff0000; + tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS; + OUTPLL(pllSCLK_MORE_CNTL, tmp); + + tmp = INPLL(pllSCLK_MORE_CNTL) & 0x0000ffff; + tmp |= rinfo->save_regs[34] & 0xffff0000; + tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS; + OUTPLL(pllSCLK_MORE_CNTL, tmp); + + OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11] & + ~(LVDS_EN | LVDS_ON | LVDS_DIGON | LVDS_BLON | LVDS_BL_MOD_EN)); + OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_BLON); + OUTREG(LVDS_PLL_CNTL, (rinfo->save_regs[12] & ~0xf0000) | 0x20000); + mdelay(20); + + /* write some stuff to the framebuffer... */ + for (i = 0; i < 0x8000; ++i) + writeb(0, rinfo->fb_base + i); + + OUTREG(0x2ec, 0x6332a020); + OUTPLL(pllSSPLL_REF_DIV, rinfo->save_regs[44] /*0x3f */); + OUTPLL(pllSSPLL_DIV_0, rinfo->save_regs[45] /*0x000081bb */); + tmp = INPLL(pllSSPLL_CNTL); + tmp &= ~2; + OUTPLL(pllSSPLL_CNTL, tmp); + mdelay(6); + tmp &= ~1; + OUTPLL(pllSSPLL_CNTL, tmp); + mdelay(5); + tmp |= 3; + OUTPLL(pllSSPLL_CNTL, tmp); + mdelay(5); + + OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90] & ~3);/*0x0020300c*/ + OUTREG(0x2ec, 0x6332a3f0); + mdelay(17); + + OUTPLL(pllPPLL_REF_DIV, rinfo->pll.ref_div);; + OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]); + + mdelay(40); + OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_DIGON | LVDS_ON); + mdelay(40); + + /* Restore a few more things */ + OUTREG(GRPH_BUFFER_CNTL, rinfo->save_regs[94]); + OUTREG(GRPH2_BUFFER_CNTL, rinfo->save_regs[95]); + + /* Restore PPLL, spread spectrum & LVDS */ + radeon_pm_m10_disable_spread_spectrum(rinfo); + radeon_pm_restore_pixel_pll(rinfo); + radeon_pm_m10_enable_lvds_spread_spectrum(rinfo); +} + +#if 0 /* Not ready yet */ +static void radeon_reinitialize_QW(struct radeonfb_info *rinfo) +{ + int i; + u32 tmp, tmp2; + u32 cko, cka, ckb; + u32 cgc, cec, c2gc; + + OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]); + OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]); + OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]); + OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]); + OUTREG(BUS_CNTL, rinfo->save_regs[36]); + OUTREG(RBBM_CNTL, rinfo->save_regs[39]); + + INREG(PAD_CTLR_STRENGTH); + OUTREG(PAD_CTLR_STRENGTH, INREG(PAD_CTLR_STRENGTH) & ~0x10000); + for (i = 0; i < 65; ++i) { + mdelay(1); + INREG(PAD_CTLR_STRENGTH); + } + + OUTREG(DISP_TEST_DEBUG_CNTL, INREG(DISP_TEST_DEBUG_CNTL) | 0x10000000); + OUTREG(OV0_FLAG_CNTRL, INREG(OV0_FLAG_CNTRL) | 0x100); + OUTREG(CRTC_GEN_CNTL, INREG(CRTC_GEN_CNTL)); + OUTREG(DAC_CNTL, 0xff00410a); + OUTREG(CRTC2_GEN_CNTL, INREG(CRTC2_GEN_CNTL)); + OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x4000); + + OUTREG(SURFACE_CNTL, rinfo->save_regs[29]); + OUTREG(AGP_CNTL, rinfo->save_regs[16]); + OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]); + OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]); + + OUTMC(rinfo, ixMC_CHP_IO_CNTL_A0, 0xf7bb4433); + OUTREG(MC_IND_INDEX, 0); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_B0, 0xf7bb4433); + OUTREG(MC_IND_INDEX, 0); + + OUTREG(CRTC_MORE_CNTL, INREG(CRTC_MORE_CNTL)); + + tmp = INPLL(pllVCLK_ECP_CNTL); + OUTPLL(pllVCLK_ECP_CNTL, tmp); + tmp = INPLL(pllPIXCLKS_CNTL); + OUTPLL(pllPIXCLKS_CNTL, tmp); + + OUTPLL(MCLK_CNTL, 0xaa3f0000); + OUTPLL(SCLK_CNTL, 0xffff0000); + OUTPLL(pllMPLL_AUX_CNTL, 6); + OUTPLL(pllSPLL_AUX_CNTL, 1); + OUTPLL(MDLL_CKO, 0x9f009f); + OUTPLL(MDLL_RDCKA, 0x830083); + OUTPLL(pllMDLL_RDCKB, 0x830083); + OUTPLL(PPLL_CNTL, 0xa433); + OUTPLL(P2PLL_CNTL, 0xa433); + OUTPLL(MPLL_CNTL, 0x0400a403); + OUTPLL(SPLL_CNTL, 0x0400a433); + + tmp = INPLL(M_SPLL_REF_FB_DIV); + OUTPLL(M_SPLL_REF_FB_DIV, tmp); + tmp = INPLL(M_SPLL_REF_FB_DIV); + OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0xc); + INPLL(M_SPLL_REF_FB_DIV); + + tmp = INPLL(MPLL_CNTL); + OUTREG8(CLOCK_CNTL_INDEX, MPLL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + + tmp = INPLL(M_SPLL_REF_FB_DIV); + OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x5900); + + tmp = INPLL(MPLL_CNTL); + OUTPLL(MPLL_CNTL, tmp & ~0x2); + mdelay(1); + tmp = INPLL(MPLL_CNTL); + OUTPLL(MPLL_CNTL, tmp & ~0x1); + mdelay(10); + + OUTPLL(MCLK_CNTL, 0xaa3f1212); + mdelay(1); + + INPLL(M_SPLL_REF_FB_DIV); + INPLL(MCLK_CNTL); + INPLL(M_SPLL_REF_FB_DIV); + + tmp = INPLL(SPLL_CNTL); + OUTREG8(CLOCK_CNTL_INDEX, SPLL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff); + + tmp = INPLL(M_SPLL_REF_FB_DIV); + OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x780000); + + tmp = INPLL(SPLL_CNTL); + OUTPLL(SPLL_CNTL, tmp & ~0x1); + mdelay(1); + tmp = INPLL(SPLL_CNTL); + OUTPLL(SPLL_CNTL, tmp & ~0x2); + mdelay(10); + + tmp = INPLL(SCLK_CNTL); + OUTPLL(SCLK_CNTL, tmp | 2); + mdelay(1); + + cko = INPLL(pllMDLL_CKO); + cka = INPLL(pllMDLL_RDCKA); + ckb = INPLL(pllMDLL_RDCKB); + + cko &= ~(MDLL_CKO__MCKOA_SLEEP | MDLL_CKO__MCKOB_SLEEP); + OUTPLL(pllMDLL_CKO, cko); + mdelay(1); + cko &= ~(MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET); + OUTPLL(pllMDLL_CKO, cko); + mdelay(5); + + cka &= ~(MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP); + OUTPLL(pllMDLL_RDCKA, cka); + mdelay(1); + cka &= ~(MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET); + OUTPLL(pllMDLL_RDCKA, cka); + mdelay(5); + + ckb &= ~(MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP); + OUTPLL(pllMDLL_RDCKB, ckb); + mdelay(1); + ckb &= ~(MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET); + OUTPLL(pllMDLL_RDCKB, ckb); + mdelay(5); + + OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, 0x151550ff); + OUTREG(MC_IND_INDEX, 0); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, 0x151550ff); + OUTREG(MC_IND_INDEX, 0); + mdelay(1); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, 0x141550ff); + OUTREG(MC_IND_INDEX, 0); + OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, 0x141550ff); + OUTREG(MC_IND_INDEX, 0); + mdelay(1); + + OUTPLL(pllHTOTAL_CNTL, 0); + OUTPLL(pllHTOTAL2_CNTL, 0); + + OUTREG(MEM_CNTL, 0x29002901); + OUTREG(MEM_SDRAM_MODE_REG, 0x45320032); /* XXX use save_regs[35]? */ + OUTREG(EXT_MEM_CNTL, 0x1a394333); + OUTREG(MEM_IO_CNTL_A1, 0x0aac0aac); + OUTREG(MEM_INIT_LATENCY_TIMER, 0x34444444); + OUTREG(MEM_REFRESH_CNTL, 0x1f1f7218); /* XXX or save_regs[42]? */ + OUTREG(MC_DEBUG, 0); + OUTREG(MEM_IO_OE_CNTL, 0x04300430); + + OUTMC(rinfo, ixMC_IMP_CNTL, 0x00f460d6); + OUTREG(MC_IND_INDEX, 0); + OUTMC(rinfo, ixMC_IMP_CNTL_0, 0x00009249); + OUTREG(MC_IND_INDEX, 0); + + OUTREG(CONFIG_MEMSIZE, rinfo->video_ram); + + radeon_pm_full_reset_sdram(rinfo); + + INREG(FP_GEN_CNTL); + OUTREG(TMDS_CNTL, 0x01000000); /* XXX ? */ + tmp = INREG(FP_GEN_CNTL); + tmp |= FP_CRTC_DONT_SHADOW_HEND | FP_CRTC_DONT_SHADOW_VPAR | 0x200; + OUTREG(FP_GEN_CNTL, tmp); + + tmp = INREG(DISP_OUTPUT_CNTL); + tmp &= ~0x400; + OUTREG(DISP_OUTPUT_CNTL, tmp); + + OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]); + OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]); + OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]); + + tmp = INPLL(MCLK_MISC); + tmp |= MCLK_MISC__MC_MCLK_DYN_ENABLE | MCLK_MISC__IO_MCLK_DYN_ENABLE; + OUTPLL(MCLK_MISC, tmp); + + tmp = INPLL(SCLK_CNTL); + OUTPLL(SCLK_CNTL, tmp); + + OUTREG(CRTC_MORE_CNTL, 0); + OUTREG8(CRTC_GEN_CNTL+1, 6); + OUTREG8(CRTC_GEN_CNTL+3, 1); + OUTREG(CRTC_PITCH, 32); + + tmp = INPLL(VCLK_ECP_CNTL); + OUTPLL(VCLK_ECP_CNTL, tmp); + + tmp = INPLL(PPLL_CNTL); + OUTPLL(PPLL_CNTL, tmp); + + /* palette stuff and BIOS_1_SCRATCH... */ + + tmp = INREG(FP_GEN_CNTL); + tmp2 = INREG(TMDS_TRANSMITTER_CNTL); + tmp |= 2; + OUTREG(FP_GEN_CNTL, tmp); + mdelay(5); + OUTREG(FP_GEN_CNTL, tmp); + mdelay(5); + OUTREG(TMDS_TRANSMITTER_CNTL, tmp2); + OUTREG(CRTC_MORE_CNTL, 0); + mdelay(20); + + tmp = INREG(CRTC_MORE_CNTL); + OUTREG(CRTC_MORE_CNTL, tmp); + + cgc = INREG(CRTC_GEN_CNTL); + cec = INREG(CRTC_EXT_CNTL); + c2gc = INREG(CRTC2_GEN_CNTL); + + OUTREG(CRTC_H_SYNC_STRT_WID, 0x008e0580); + OUTREG(CRTC_H_TOTAL_DISP, 0x009f00d2); + OUTREG8(CLOCK_CNTL_INDEX, HTOTAL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA, 0); + OUTREG(CRTC_V_SYNC_STRT_WID, 0x00830403); + OUTREG(CRTC_V_TOTAL_DISP, 0x03ff0429); + OUTREG(FP_CRTC_H_TOTAL_DISP, 0x009f0033); + OUTREG(FP_H_SYNC_STRT_WID, 0x008e0080); + OUTREG(CRT_CRTC_H_SYNC_STRT_WID, 0x008e0080); + OUTREG(FP_CRTC_V_TOTAL_DISP, 0x03ff002a); + OUTREG(FP_V_SYNC_STRT_WID, 0x00830004); + OUTREG(CRT_CRTC_V_SYNC_STRT_WID, 0x00830004); + OUTREG(FP_HORZ_VERT_ACTIVE, 0x009f03ff); + OUTREG(FP_HORZ_STRETCH, 0); + OUTREG(FP_VERT_STRETCH, 0); + OUTREG(OVR_CLR, 0); + OUTREG(OVR_WID_LEFT_RIGHT, 0); + OUTREG(OVR_WID_TOP_BOTTOM, 0); + + tmp = INPLL(PPLL_REF_DIV); + tmp = (tmp & ~PPLL_REF_DIV_MASK) | rinfo->pll.ref_div; + OUTPLL(PPLL_REF_DIV, tmp); + INPLL(PPLL_REF_DIV); + + OUTREG8(CLOCK_CNTL_INDEX, PPLL_CNTL + PLL_WR_EN); + OUTREG8(CLOCK_CNTL_DATA + 1, 0xbc); + + tmp = INREG(CLOCK_CNTL_INDEX); + OUTREG(CLOCK_CNTL_INDEX, tmp & 0xff); + + OUTPLL(PPLL_DIV_0, 0x48090); + + tmp = INPLL(PPLL_CNTL); + OUTPLL(PPLL_CNTL, tmp & ~0x2); + mdelay(1); + tmp = INPLL(PPLL_CNTL); + OUTPLL(PPLL_CNTL, tmp & ~0x1); + mdelay(10); + + tmp = INPLL(VCLK_ECP_CNTL); + OUTPLL(VCLK_ECP_CNTL, tmp | 3); + mdelay(1); + + tmp = INPLL(VCLK_ECP_CNTL); + OUTPLL(VCLK_ECP_CNTL, tmp); + + c2gc |= CRTC2_DISP_REQ_EN_B; + OUTREG(CRTC2_GEN_CNTL, c2gc); + cgc |= CRTC_EN; + OUTREG(CRTC_GEN_CNTL, cgc); + OUTREG(CRTC_EXT_CNTL, cec); + OUTREG(CRTC_PITCH, 0xa0); + OUTREG(CRTC_OFFSET, 0); + OUTREG(CRTC_OFFSET_CNTL, 0); + + OUTREG(GRPH_BUFFER_CNTL, 0x20117c7c); + OUTREG(GRPH2_BUFFER_CNTL, 0x00205c5c); + + tmp2 = INREG(FP_GEN_CNTL); + tmp = INREG(TMDS_TRANSMITTER_CNTL); + OUTREG(0x2a8, 0x0000061b); + tmp |= TMDS_PLL_EN; + OUTREG(TMDS_TRANSMITTER_CNTL, tmp); + mdelay(1); + tmp &= ~TMDS_PLLRST; + OUTREG(TMDS_TRANSMITTER_CNTL, tmp); + tmp2 &= ~2; + tmp2 |= FP_TMDS_EN; + OUTREG(FP_GEN_CNTL, tmp2); + mdelay(5); + tmp2 |= FP_FPON; + OUTREG(FP_GEN_CNTL, tmp2); + + OUTREG(CUR_HORZ_VERT_OFF, CUR_LOCK | 1); + cgc = INREG(CRTC_GEN_CNTL); + OUTREG(CUR_HORZ_VERT_POSN, 0xbfff0fff); + cgc |= 0x10000; + OUTREG(CUR_OFFSET, 0); +} +#endif /* 0 */ + +#endif /* CONFIG_PPC_OF */ + static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) { u16 pwr_cmd; u32 tmp; + int i; if (!rinfo->pm_reg) return; @@ -796,13 +2406,13 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) * duration of the suspend/resume process */ radeon_pm_disable_dynamic_mode(rinfo); + /* Save some registers */ - radeon_pm_save_regs(rinfo); + radeon_pm_save_regs(rinfo, 0); - /* Prepare mobility chips for suspend. Only do that on <= RV250 chips that - * have been tested + /* Prepare mobility chips for suspend. */ - if (rinfo->is_mobility && rinfo->family <= CHIP_FAMILY_RV250) { + if (rinfo->is_mobility) { /* Program V2CLK */ radeon_pm_program_v2clk(rinfo); @@ -815,13 +2425,22 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) /* Prepare chip for power management */ radeon_pm_setup_for_suspend(rinfo); - /* Reset the MDLL */ - /* because both INPLL and OUTPLL take the same lock, that's why. */ - tmp = INPLL( pllMDLL_CKO) | MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET; - OUTPLL( pllMDLL_CKO, tmp ); + if (rinfo->family <= CHIP_FAMILY_RV280) { + /* Reset the MDLL */ + /* because both INPLL and OUTPLL take the same + * lock, that's why. */ + tmp = INPLL( pllMDLL_CKO) | MDLL_CKO__MCKOA_RESET + | MDLL_CKO__MCKOB_RESET; + OUTPLL( pllMDLL_CKO, tmp ); + } } + for (i = 0; i < 64; ++i) + pci_read_config_dword(rinfo->pdev, i * 4, + &rinfo->cfg_save[i]); + /* Switch PCI power managment to D2. */ + pci_disable_device(rinfo->pdev); for (;;) { pci_read_config_word( rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, @@ -841,37 +2460,74 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0); mdelay(500); - /* Reset the SDRAM controller */ - radeon_pm_full_reset_sdram(rinfo); - - /* Restore some registers */ - radeon_pm_restore_regs(rinfo); - radeon_pm_enable_dynamic_mode(rinfo); + if (rinfo->family <= CHIP_FAMILY_RV250) { + /* Reset the SDRAM controller */ + radeon_pm_full_reset_sdram(rinfo); + + /* Restore some registers */ + radeon_pm_restore_regs(rinfo); + } else { + /* Restore registers first */ + radeon_pm_restore_regs(rinfo); + /* init sdram controller */ + radeon_pm_full_reset_sdram(rinfo); + } + } +} + +static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo) +{ + int i; + static u32 radeon_cfg_after_resume[64]; + + for (i = 0; i < 64; ++i) + pci_read_config_dword(rinfo->pdev, i * 4, + &radeon_cfg_after_resume[i]); + + if (radeon_cfg_after_resume[PCI_BASE_ADDRESS_0/4] + == rinfo->cfg_save[PCI_BASE_ADDRESS_0/4]) + return 0; /* assume everything is ok */ + + for (i = PCI_BASE_ADDRESS_0/4; i < 64; ++i) { + if (radeon_cfg_after_resume[i] != rinfo->cfg_save[i]) + pci_write_config_dword(rinfo->pdev, i * 4, + rinfo->cfg_save[i]); } + pci_write_config_word(rinfo->pdev, PCI_CACHE_LINE_SIZE, + rinfo->cfg_save[PCI_CACHE_LINE_SIZE/4]); + pci_write_config_word(rinfo->pdev, PCI_COMMAND, + rinfo->cfg_save[PCI_COMMAND/4]); + return 1; } + +static/*extern*/ int susdisking = 0; + int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state) { struct fb_info *info = pci_get_drvdata(pdev); struct radeonfb_info *rinfo = info->par; + int i; - /* We don't do anything but D2, for now we return 0, but - * we may want to change that. How do we know if the BIOS - * can properly take care of D3 ? Also, with swsusp, we - * know we'll be rebooted, ... - */ + if (state == pdev->dev.power.power_state) + return 0; - printk(KERN_DEBUG "radeonfb: suspending to state: %d...\n", state); - - acquire_console_sem(); + printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n", + pci_name(pdev), state); - /* Userland should do this but doesn't... bridge gets suspended - * too late. Unfortunately, that works only when AGP is built-in, - * not for a module. + /* For suspend-to-disk, we cheat here. We don't suspend anything and + * let fbcon continue drawing until we are all set. That shouldn't + * really cause any problem at this point, provided that the wakeup + * code knows that any state in memory may not match the HW */ -#ifdef CONFIG_AGP - agp_enable(0); -#endif + if (state != PM_SUSPEND_MEM) + goto done; + if (susdisking) { + printk("suspending to disk but state = %d\n", state); + goto done; + } + + acquire_console_sem(); fb_set_suspend(info, 1); @@ -883,21 +2539,52 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state) } /* Blank display and LCD */ - radeonfb_blank(VESA_POWERDOWN, info); + radeon_screen_blank(rinfo, FB_BLANK_POWERDOWN, 1); /* Sleep */ rinfo->asleep = 1; rinfo->lock_blank = 1; + del_timer_sync(&rinfo->lvds_timer); - /* Suspend the chip to D2 state when supported + /* If we support wakeup from poweroff, we save all regs we can including cfg + * space */ -#ifdef CONFIG_RADEON_HAS_D2 - if (radeon_suspend_to_d2(rinfo, state)) + if (rinfo->pm_mode & radeon_pm_off) { + /* Always disable dynamic clocks or weird things are happening when + * the chip goes off (basically the panel doesn't shut down properly + * and we crash on wakeup), + * also, we want the saved regs context to have no dynamic clocks in + * it, we'll restore the dynamic clocks state on wakeup + */ + radeon_pm_disable_dynamic_mode(rinfo); + mdelay(50); + radeon_pm_save_regs(rinfo, 1); + + if (rinfo->is_mobility && !(rinfo->pm_mode & radeon_pm_d2)) { + /* Switch off LVDS interface */ + mdelay(1); + OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_BL_MOD_EN)); + mdelay(1); + OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_EN | LVDS_ON)); + OUTREG(LVDS_PLL_CNTL, (INREG(LVDS_PLL_CNTL) & ~30000) | 0x20000); + mdelay(20); + OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON)); + + // FIXME: Use PCI layer + for (i = 0; i < 64; ++i) + pci_read_config_dword(rinfo->pdev, i * 4, + &rinfo->cfg_save[i]); + } + } + /* If we support D2, we go to it (should be fixed later with a flag forcing + * D3 only for some laptops) + */ + if (rinfo->pm_mode & radeon_pm_d2) radeon_set_suspend(rinfo, 1); -#endif /* CONFIG_RADEON_HAS_D2 */ release_console_sem(); + done: pdev->dev.power.power_state = state; return 0; @@ -907,22 +2594,59 @@ int radeonfb_pci_resume(struct pci_dev *pdev) { struct fb_info *info = pci_get_drvdata(pdev); struct radeonfb_info *rinfo = info->par; + int rc = 0; if (pdev->dev.power.power_state == 0) return 0; - acquire_console_sem(); + if (rinfo->no_schedule) { + if (try_acquire_console_sem()) + return 0; + } else + acquire_console_sem(); + + printk(KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n", + pci_name(pdev), pdev->dev.power.power_state); - /* Wakeup chip */ -#ifdef CONFIG_RADEON_HAS_D2 - if (radeon_suspend_to_d2(rinfo, 0)) - radeon_set_suspend(rinfo, 0); -#endif /* CONFIG_RADEON_HAS_D2 */ - rinfo->asleep = 0; + if (pci_enable_device(pdev)) { + rc = -ENODEV; + printk(KERN_ERR "radeonfb (%s): can't enable PCI device !\n", + pci_name(pdev)); + goto bail; + } + pci_set_master(pdev); + + if (pdev->dev.power.power_state == PM_SUSPEND_MEM) { + /* Wakeup chip. Check from config space if we were powered off + * (todo: additionally, check CLK_PIN_CNTL too) + */ + if ((rinfo->pm_mode & radeon_pm_off) && radeon_restore_pci_cfg(rinfo)) { + if (rinfo->reinit_func != NULL) + rinfo->reinit_func(rinfo); + else { + printk(KERN_ERR "radeonfb (%s): can't resume radeon from" + " D3 cold, need softboot !", pci_name(pdev)); + rc = -EIO; + goto bail; + } + } + /* If we support D2, try to resume... we should check what was our + * state though... (were we really in D2 state ?). Right now, this code + * is only enable on Macs so it's fine. + */ + else if (rinfo->pm_mode & radeon_pm_d2) + radeon_set_suspend(rinfo, 0); + + rinfo->asleep = 0; + } else + radeon_engine_idle(); /* Restore display & engine */ - radeonfb_set_par(info); + radeon_write_mode (rinfo, &rinfo->state, 1); + if (!(info->flags & FBINFO_HWACCEL_DISABLED)) + radeonfb_engine_init (rinfo); + fb_pan_display(info, &info->var); fb_set_cmap(&info->cmap, info); @@ -931,15 +2655,107 @@ int radeonfb_pci_resume(struct pci_dev *pdev) /* Unblank */ rinfo->lock_blank = 0; - radeonfb_blank(0, info); + radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 1); - release_console_sem(); + /* Check status of dynclk */ + if (rinfo->dynclk == 1) + radeon_pm_enable_dynamic_mode(rinfo); + else if (rinfo->dynclk == 0) + radeon_pm_disable_dynamic_mode(rinfo); pdev->dev.power.power_state = 0; - printk(KERN_DEBUG "radeonfb: resumed !\n"); + bail: + release_console_sem(); - return 0; + return rc; +} + +#ifdef CONFIG_PPC_OF +static void radeonfb_early_resume(void *data) +{ + struct radeonfb_info *rinfo = data; + + rinfo->no_schedule = 1; + radeonfb_pci_resume(rinfo->pdev); + rinfo->no_schedule = 0; } +#endif /* CONFIG_PPC_OF */ #endif /* CONFIG_PM */ + +void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk) +{ + /* Find PM registers in config space if any*/ + rinfo->pm_reg = pci_find_capability(rinfo->pdev, PCI_CAP_ID_PM); + + /* Enable/Disable dynamic clocks: TODO add sysfs access */ + rinfo->dynclk = dynclk; + if (dynclk == 1) { + radeon_pm_enable_dynamic_mode(rinfo); + printk("radeonfb: Dynamic Clock Power Management enabled\n"); + } else if (dynclk == 0) { + radeon_pm_disable_dynamic_mode(rinfo); + printk("radeonfb: Dynamic Clock Power Management disabled\n"); + } + + /* Check if we can power manage on suspend/resume. We can do + * D2 on M6, M7 and M9, and we can resume from D3 cold a few other + * "Mac" cards, but that's all. We need more infos about what the + * BIOS does tho. Right now, all this PM stuff is pmac-only for that + * reason. --BenH + */ +#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF) + if (_machine == _MACH_Pmac && rinfo->of_node) { + if (rinfo->is_mobility && rinfo->pm_reg && + rinfo->family <= CHIP_FAMILY_RV250) + rinfo->pm_mode |= radeon_pm_d2; + + /* We can restart Jasper (M10 chip in albooks), BlueStone (7500 chip + * in some desktop G4s), and Via (M9+ chip on iBook G4) + */ + if (!strcmp(rinfo->of_node->name, "ATY,JasperParent")) { + rinfo->reinit_func = radeon_reinitialize_M10; + rinfo->pm_mode |= radeon_pm_off; + } +#if 0 /* Not ready yet */ + if (!strcmp(rinfo->of_node->name, "ATY,BlueStoneParent")) { + rinfo->reinit_func = radeon_reinitialize_QW; + rinfo->pm_mode |= radeon_pm_off; + } +#endif + if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) { + rinfo->reinit_func = radeon_reinitialize_M9P; + rinfo->pm_mode |= radeon_pm_off; + /* Workaround not used for now */ + rinfo->m9p_workaround = 1; + } + + /* If any of the above is set, we assume the machine can sleep/resume. + * It's a bit of a "shortcut" but will work fine. Ideally, we need infos + * from the platform about what happens to the chip... + * Now we tell the platform about our capability + */ + if (rinfo->pm_mode != radeon_pm_none) { + pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, rinfo->of_node, 0, 1); + pmac_set_early_video_resume(radeonfb_early_resume, rinfo); + } + +#if 0 + /* Power down TV DAC, taht saves a significant amount of power, + * we'll have something better once we actually have some TVOut + * support + */ + OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000); +#endif + } +#endif /* defined(CONFIG_PM) && defined(CONFIG_PPC_OF) */ +} + +void radeonfb_pm_exit(struct radeonfb_info *rinfo) +{ +#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF) + if (rinfo->pm_mode != radeon_pm_none) + pmac_set_early_video_resume(NULL, NULL); +#endif +} diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index 7377cb7b14b9..a01aa344c99e 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -16,8 +16,17 @@ #include +#ifdef CONFIG_PPC_OF +#include +#endif + #include