linux/drivers/video/aty/radeon_pm.c
Benjamin Herrenschmidt 0c541b4406 [PATCH] ppc32: Fix AGP and sleep again
My previous patch that added sleep support for uninorth-agp and some AGP
"off" stuff in radeonfb and aty128fb is breaking some configs.  More
specifically, it has problems with rage128 setups since the DRI code for
these in X doesn't properly re-enable AGP on wakeup or console switch
(unlike the radeon DRM).

This patch fixes the problem for pmac once for all by using a different
approach.  The AGP driver "registers" special suspend/resume callbacks with
some arch code that the fbdev's can later on call to suspend and resume
AGP, making sure it's resumed back in the same state it was when suspended.
 This is platform specific for now.  It would be too complicated to try to
do a generic implementation of this at this point due to all sort of weird
things going on with AGP on other architectures.  We'll re-work that whole
problem cleanly once we finally merge fbdev's and DRI.

In the meantime, please apply this patch which brings back some r128 based
laptops into working condition as far as system sleep is concerned.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-04-16 15:24:19 -07:00

2789 lines
84 KiB
C

/*
* drivers/video/aty/radeon_pm.c
*
* Copyright 2003,2004 Ben. Herrenschmidt <benh@kernel.crashing.org>
* Copyright 2004 Paul Mackerras <paulus@samba.org>
*
* 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 <linux/console.h>
#include <linux/agp_backend.h>
#ifdef CONFIG_PPC_PMAC
#include <asm/processor.h>
#include <asm/prom.h>
#include <asm/pmac_feature.h>
#endif
#include "ati_ids.h"
static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
{
u32 tmp;
/* 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;
}
/* 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 */
tmp = INPLL(pllSCLK_CNTL);
tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_E2);
/* 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);
OUTPLL(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);
}
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);
}
static void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo)
{
u32 tmp;
/* 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;
}
/* M10 */
if (rinfo->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;
}
/* 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 */
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);
tmp = INPLL(pllCLK_PIN_CNTL);
tmp |= CLK_PIN_CNTL__SCLK_DYN_START_CNTL;
OUTPLL(pllCLK_PIN_CNTL, tmp);
radeon_msleep(15);
/* 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);
}
/* 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;
OUTPLL(pllPLL_PWRMGT_CNTL, tmp);
radeon_msleep(15);
}
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, tmp);
radeon_msleep(15);
}
#endif /* CONFIG_PPC_OF */
}
#ifdef CONFIG_PM
static void OUTMC( struct radeonfb_info *rinfo, u8 indx, u32 value)
{
OUTREG( MC_IND_INDEX, indx | MC_IND_INDEX__MC_IND_WR_EN);
OUTREG( MC_IND_DATA, value);
}
static u32 INMC(struct radeonfb_info *rinfo, u8 indx)
{
OUTREG( MC_IND_INDEX, indx);
return INREG( MC_IND_DATA);
}
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);
rinfo->save_regs[2] = INPLL(MCLK_CNTL);
rinfo->save_regs[3] = INPLL(SCLK_CNTL);
rinfo->save_regs[4] = INPLL(CLK_PIN_CNTL);
rinfo->save_regs[5] = INPLL(VCLK_ECP_CNTL);
rinfo->save_regs[6] = INPLL(PIXCLKS_CNTL);
rinfo->save_regs[7] = INPLL(MCLK_MISC);
rinfo->save_regs[8] = INPLL(P2PLL_CNTL);
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[13] = INREG(TV_DAC_CNTL);
rinfo->save_regs[14] = INREG(BUS_CNTL1);
rinfo->save_regs[15] = INREG(CRTC_OFFSET_CNTL);
rinfo->save_regs[16] = INREG(AGP_CNTL);
rinfo->save_regs[17] = (INREG(CRTC_GEN_CNTL) & 0xfdffffff) | 0x04000000;
rinfo->save_regs[18] = (INREG(CRTC2_GEN_CNTL) & 0xfdffffff) | 0x04000000;
rinfo->save_regs[19] = INREG(GPIOPAD_A);
rinfo->save_regs[20] = INREG(GPIOPAD_EN);
rinfo->save_regs[21] = INREG(GPIOPAD_MASK);
rinfo->save_regs[22] = INREG(ZV_LCDPAD_A);
rinfo->save_regs[23] = INREG(ZV_LCDPAD_EN);
rinfo->save_regs[24] = INREG(ZV_LCDPAD_MASK);
rinfo->save_regs[25] = INREG(GPIO_VGA_DDC);
rinfo->save_regs[26] = INREG(GPIO_DVI_DDC);
rinfo->save_regs[27] = INREG(GPIO_MONID);
rinfo->save_regs[28] = INREG(GPIO_CRT2_DDC);
rinfo->save_regs[29] = INREG(SURFACE_CNTL);
rinfo->save_regs[30] = INREG(MC_FB_LOCATION);
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)
{
OUTPLL(P2PLL_CNTL, rinfo->save_regs[8] & 0xFFFFFFFE); /* First */
OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]);
OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]);
OUTPLL(MCLK_CNTL, rinfo->save_regs[2]);
OUTPLL(SCLK_CNTL, rinfo->save_regs[3]);
OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]);
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]);
OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11]);
OUTREG(LVDS_PLL_CNTL,rinfo->save_regs[12]);
OUTREG(TV_DAC_CNTL, rinfo->save_regs[13]);
OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
OUTREG(CRTC_OFFSET_CNTL, rinfo->save_regs[15]);
OUTREG(AGP_CNTL, rinfo->save_regs[16]);
OUTREG(CRTC_GEN_CNTL, rinfo->save_regs[17]);
OUTREG(CRTC2_GEN_CNTL, rinfo->save_regs[18]);
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]);
OUTREG(ZV_LCDPAD_A, rinfo->save_regs[22]);
OUTREG(ZV_LCDPAD_EN, rinfo->save_regs[23]);
OUTREG(ZV_LCDPAD_MASK, rinfo->save_regs[24]);
OUTREG(GPIO_VGA_DDC, rinfo->save_regs[25]);
OUTREG(GPIO_DVI_DDC, rinfo->save_regs[26]);
OUTREG(GPIO_MONID, rinfo->save_regs[27]);
OUTREG(GPIO_CRT2_DDC, rinfo->save_regs[28]);
}
static void radeon_pm_disable_iopad(struct radeonfb_info *rinfo)
{
OUTREG(GPIOPAD_MASK, 0x0001ffff);
OUTREG(GPIOPAD_EN, 0x00000400);
OUTREG(GPIOPAD_A, 0x00000000);
OUTREG(ZV_LCDPAD_MASK, 0x00000000);
OUTREG(ZV_LCDPAD_EN, 0x00000000);
OUTREG(ZV_LCDPAD_A, 0x00000000);
OUTREG(GPIO_VGA_DDC, 0x00030000);
OUTREG(GPIO_DVI_DDC, 0x00000000);
OUTREG(GPIO_MONID, 0x00030000);
OUTREG(GPIO_CRT2_DDC, 0x00000000);
}
static void radeon_pm_program_v2clk(struct radeonfb_info *rinfo)
{
/* Set v2clk to 65MHz */
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);
} 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(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_SLEEP);
mdelay(1);
OUTPLL(pllP2PLL_CNTL, INPLL(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_RESET);
mdelay( 1);
OUTPLL(pllPIXCLKS_CNTL,
(INPLL(pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK)
| (0x03 << PIXCLKS_CNTL__PIX2CLK_SRC_SEL__SHIFT));
mdelay( 1);
}
static void radeon_pm_low_current(struct radeonfb_info *rinfo)
{
u32 reg;
reg = INREG(BUS_CNTL1);
if (rinfo->family <= CHIP_FAMILY_RV280) {
reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK;
reg |= BUS_CNTL1_AGPCLK_VALID | (1<<BUS_CNTL1_MOBILE_PLATFORM_SEL_SHIFT);
} else {
reg |= 0x4080;
}
OUTREG(BUS_CNTL1, reg);
reg = INPLL(PLL_PWRMGT_CNTL);
reg |= PLL_PWRMGT_CNTL_SPLL_TURNOFF | PLL_PWRMGT_CNTL_PPLL_TURNOFF |
PLL_PWRMGT_CNTL_P2PLL_TURNOFF | PLL_PWRMGT_CNTL_TVPLL_TURNOFF;
reg &= ~PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK;
reg &= ~PLL_PWRMGT_CNTL_MOBILE_SU;
OUTPLL(PLL_PWRMGT_CNTL, reg);
reg = INREG(TV_DAC_CNTL);
reg &= ~(TV_DAC_CNTL_BGADJ_MASK |TV_DAC_CNTL_DACADJ_MASK);
reg |=TV_DAC_CNTL_BGSLEEP | TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD |
TV_DAC_CNTL_BDACPD |
(8<<TV_DAC_CNTL_BGADJ__SHIFT) | (8<<TV_DAC_CNTL_DACADJ__SHIFT);
OUTREG(TV_DAC_CNTL, reg);
reg = INREG(TMDS_TRANSMITTER_CNTL);
reg &= ~(TMDS_PLL_EN | TMDS_PLLRST);
OUTREG(TMDS_TRANSMITTER_CNTL, reg);
reg = INREG(DAC_CNTL);
reg &= ~DAC_CMP_EN;
OUTREG(DAC_CNTL, reg);
reg = INREG(DAC_CNTL2);
reg &= ~DAC2_CMP_EN;
OUTREG(DAC_CNTL2, reg);
reg = INREG(TV_DAC_CNTL);
reg &= ~TV_DAC_CNTL_DETECT;
OUTREG(TV_DAC_CNTL, reg);
}
static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
{
u32 sclk_cntl, mclk_cntl, sclk_more_cntl;
u32 pll_pwrmgt_cntl;
u32 clk_pwrmgt_cntl;
u32 clk_pin_cntl;
u32 vclk_ecp_cntl;
u32 pixclks_cntl;
u32 disp_mis_cntl;
u32 disp_pwr_man;
u32 tmp;
/* Force Core Clocks */
sclk_cntl = INPLL( pllSCLK_CNTL);
sclk_cntl |= 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|
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;
if (rinfo->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 |
SCLK_MORE_CNTL__FORCE_MC_GUI |
SCLK_MORE_CNTL__FORCE_MC_HOST;
OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl);
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, 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__ECP_FORCE_ON;
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);
/* Switch off LVDS interface */
OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) &
~(LVDS_BLON | LVDS_EN | LVDS_ON | LVDS_DIGON));
/* Enable System power management */
pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL);
pll_pwrmgt_cntl |= PLL_PWRMGT_CNTL__SPLL_TURNOFF |
PLL_PWRMGT_CNTL__MPLL_TURNOFF|
PLL_PWRMGT_CNTL__PPLL_TURNOFF|
PLL_PWRMGT_CNTL__P2PLL_TURNOFF|
PLL_PWRMGT_CNTL__TVPLL_TURNOFF;
OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL);
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__GLOBAL_PMAN_EN
| CLK_PWRMGT_CNTL__DISP_PM;
OUTPLL( pllCLK_PWRMGT_CNTL, clk_pwrmgt_cntl);
clk_pin_cntl = INPLL( pllCLK_PIN_CNTL);
clk_pin_cntl &= ~CLK_PIN_CNTL__ACCESS_REGS_IN_SUSPEND;
/* because both INPLL and OUTPLL take the same lock, that's why. */
tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
OUTPLL( pllMCLK_MISC, tmp);
/* AGP PLL control */
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<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX
} else {
OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
}
OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL)
& ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
clk_pin_cntl &= ~CLK_PIN_CNTL__CG_CLK_TO_OUTPIN;
clk_pin_cntl |= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;
OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
/* Solano2M */
OUTREG(AGP_CNTL,
(INREG(AGP_CNTL) & ~(AGP_CNTL__MAX_IDLE_CLK_MASK))
| (0x20<<AGP_CNTL__MAX_IDLE_CLK__SHIFT));
/* ACPI mode */
/* because both INPLL and OUTPLL take the same lock, that's why. */
tmp = INPLL( pllPLL_PWRMGT_CNTL) & ~PLL_PWRMGT_CNTL__PM_MODE_SEL;
OUTPLL( pllPLL_PWRMGT_CNTL, tmp);
disp_mis_cntl = INREG(DISP_MISC_CNTL);
disp_mis_cntl &= ~( DISP_MISC_CNTL__SOFT_RESET_GRPH_PP |
DISP_MISC_CNTL__SOFT_RESET_SUBPIC_PP |
DISP_MISC_CNTL__SOFT_RESET_OV0_PP |
DISP_MISC_CNTL__SOFT_RESET_GRPH_SCLK|
DISP_MISC_CNTL__SOFT_RESET_SUBPIC_SCLK|
DISP_MISC_CNTL__SOFT_RESET_OV0_SCLK|
DISP_MISC_CNTL__SOFT_RESET_GRPH2_PP|
DISP_MISC_CNTL__SOFT_RESET_GRPH2_SCLK|
DISP_MISC_CNTL__SOFT_RESET_LVDS|
DISP_MISC_CNTL__SOFT_RESET_TMDS|
DISP_MISC_CNTL__SOFT_RESET_DIG_TMDS|
DISP_MISC_CNTL__SOFT_RESET_TV);
OUTREG(DISP_MISC_CNTL, disp_mis_cntl);
disp_pwr_man = INREG(DISP_PWR_MAN);
disp_pwr_man &= ~( DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN |
DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN |
DISP_PWR_MAN__DISP_PWR_MAN_DPMS_MASK|
DISP_PWR_MAN__DISP_D3_RST|
DISP_PWR_MAN__DISP_D3_REG_RST
);
disp_pwr_man |= DISP_PWR_MAN__DISP_D3_GRPH_RST|
DISP_PWR_MAN__DISP_D3_SUBPIC_RST|
DISP_PWR_MAN__DISP_D3_OV0_RST|
DISP_PWR_MAN__DISP_D1D2_GRPH_RST|
DISP_PWR_MAN__DISP_D1D2_SUBPIC_RST|
DISP_PWR_MAN__DISP_D1D2_OV0_RST|
DISP_PWR_MAN__DIG_TMDS_ENABLE_RST|
DISP_PWR_MAN__TV_ENABLE_RST|
// DISP_PWR_MAN__AUTO_PWRUP_EN|
0;
OUTREG(DISP_PWR_MAN, disp_pwr_man);
clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL);
pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL) ;
clk_pin_cntl = INPLL( pllCLK_PIN_CNTL);
disp_pwr_man = INREG(DISP_PWR_MAN);
/* D2 */
clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL__DISP_PM;
pll_pwrmgt_cntl |= PLL_PWRMGT_CNTL__MOBILE_SU | PLL_PWRMGT_CNTL__SU_SCLK_USE_BCLK;
clk_pin_cntl |= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;
disp_pwr_man &= ~(DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN_MASK
| DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN_MASK);
OUTPLL( pllCLK_PWRMGT_CNTL, clk_pwrmgt_cntl);
OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
OUTREG(DISP_PWR_MAN, disp_pwr_man);
/* disable display request & disable display */
OUTREG( CRTC_GEN_CNTL, (INREG( CRTC_GEN_CNTL) & ~CRTC_GEN_CNTL__CRTC_EN)
| CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B);
OUTREG( CRTC2_GEN_CNTL, (INREG( CRTC2_GEN_CNTL) & ~CRTC2_GEN_CNTL__CRTC2_EN)
| CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B);
mdelay(17);
}
static void radeon_pm_yclk_mclk_sync(struct radeonfb_info *rinfo)
{
u32 mc_chp_io_cntl_a1, mc_chp_io_cntl_b1;
mc_chp_io_cntl_a1 = INMC( rinfo, ixMC_CHP_IO_CNTL_A1)
& ~MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA_MASK;
mc_chp_io_cntl_b1 = INMC( rinfo, ixMC_CHP_IO_CNTL_B1)
& ~MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB_MASK;
OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1
| (1<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT));
OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1
| (1<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT));
OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1);
OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1);
mdelay( 1);
}
static void radeon_pm_yclk_mclk_sync_m10(struct radeonfb_info *rinfo)
{
u32 mc_chp_io_cntl_a1, mc_chp_io_cntl_b1;
mc_chp_io_cntl_a1 = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1)
& ~MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA_MASK;
mc_chp_io_cntl_b1 = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1)
& ~MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB_MASK;
OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_A1,
mc_chp_io_cntl_a1 | (1<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT));
OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_B1,
mc_chp_io_cntl_b1 | (1<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT));
OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1);
OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1);
mdelay( 1);
}
static void radeon_pm_program_mode_reg(struct radeonfb_info *rinfo, u16 value,
u8 delay_required)
{
u32 mem_sdram_mode;
mem_sdram_mode = INREG( MEM_SDRAM_MODE_REG);
mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_MODE_REG_MASK;
mem_sdram_mode |= (value<<MEM_SDRAM_MODE_REG__MEM_MODE_REG__SHIFT)
| MEM_SDRAM_MODE_REG__MEM_CFG_TYPE;
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);
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);
}
}
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;
}
}
static void radeon_pm_enable_dll(struct radeonfb_info *rinfo)
{
#define DLL_RESET_DELAY 5
#define DLL_SLEEP_DELAY 1
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, 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
}
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;
#define DLL_RESET_DELAY 5
#define DLL_SLEEP_DELAY 1
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_value = INPLL(pllMDLL_RDCKA);
/* Power Up */
dll_value &= ~(dll_sleep_mask);
OUTPLL(pllMDLL_RDCKA, dll_value);
mdelay( DLL_SLEEP_DELAY);
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;
crtcGenCntl = INREG( CRTC_GEN_CNTL);
crtcGenCntl2 = INREG( CRTC2_GEN_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_GEN_CNTL, (crtcGenCntl | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B) );
OUTREG( CRTC2_GEN_CNTL, (crtcGenCntl2 | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B) );
/* 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);
/* DLL */
radeon_pm_enable_dll(rinfo);
/* MLCK / YCLK sync */
radeon_pm_yclk_mclk_sync(rinfo);
/* 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);
/* 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( CRTC_GEN_CNTL, crtcGenCntl);
OUTREG( CRTC2_GEN_CNTL, crtcGenCntl2);
OUTREG( FP_GEN_CNTL, fp_gen_cntl);
OUTREG( FP2_GEN_CNTL, fp2_gen_cntl);
OUTREG( CRTC_MORE_CNTL, crtc_more_cntl);
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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
radeon_pll_errata_after_data(rinfo);
/* 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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
radeon_pll_errata_after_data(rinfo);
/* 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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA, 0);
radeon_pll_errata_after_data(rinfo);
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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
radeon_pll_errata_after_data(rinfo);
/* 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);
radeon_pll_errata_after_index(rinfo);
radeon_pll_errata_after_data(rinfo);
}
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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
radeon_pll_errata_after_data(rinfo);
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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
radeon_pll_errata_after_data(rinfo);
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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA, 0);
radeon_pll_errata_after_data(rinfo);
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);
radeon_pll_errata_after_index(rinfo);
OUTREG8(CLOCK_CNTL_DATA + 1, 0xbc);
radeon_pll_errata_after_data(rinfo);
tmp = INREG(CLOCK_CNTL_INDEX);
radeon_pll_errata_after_index(rinfo);
OUTREG(CLOCK_CNTL_INDEX, tmp & 0xff);
radeon_pll_errata_after_index(rinfo);
radeon_pll_errata_after_data(rinfo);
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;
/* Set the chip into appropriate suspend mode (we use D2,
* D3 would require a compete re-initialization of the chip,
* including PCI config registers, clocks, AGP conf, ...)
*/
if (suspend) {
printk(KERN_DEBUG "radeonfb (%s): switching to D2 state...\n",
pci_name(rinfo->pdev));
/* Disable dynamic power management of clocks for the
* duration of the suspend/resume process
*/
radeon_pm_disable_dynamic_mode(rinfo);
/* Save some registers */
radeon_pm_save_regs(rinfo, 0);
/* Prepare mobility chips for suspend.
*/
if (rinfo->is_mobility) {
/* Program V2CLK */
radeon_pm_program_v2clk(rinfo);
/* Disable IO PADs */
radeon_pm_disable_iopad(rinfo);
/* Set low current */
radeon_pm_low_current(rinfo);
/* Prepare chip for power management */
radeon_pm_setup_for_suspend(rinfo);
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,
&pwr_cmd);
if (pwr_cmd & 2)
break;
pci_write_config_word(
rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
(pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | 2);
mdelay(500);
}
} else {
printk(KERN_DEBUG "radeonfb (%s): switching to D0 state...\n",
pci_name(rinfo->pdev));
/* Switch back PCI powermanagment to D0 */
mdelay(200);
pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0);
mdelay(500);
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;
}
int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct fb_info *info = pci_get_drvdata(pdev);
struct radeonfb_info *rinfo = info->par;
int i;
if (state == pdev->dev.power.power_state)
return 0;
printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n",
pci_name(pdev), state);
/* 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
*/
if (state != PM_SUSPEND_MEM)
goto done;
acquire_console_sem();
fb_set_suspend(info, 1);
if (!(info->flags & FBINFO_HWACCEL_DISABLED)) {
/* Make sure engine is reset */
radeon_engine_idle();
radeonfb_engine_reset(rinfo);
radeon_engine_idle();
}
/* Blank display and LCD */
radeon_screen_blank(rinfo, FB_BLANK_POWERDOWN, 1);
/* Sleep */
rinfo->asleep = 1;
rinfo->lock_blank = 1;
del_timer_sync(&rinfo->lvds_timer);
#ifdef CONFIG_PPC_PMAC
/* On powermac, we have hooks to properly suspend/resume AGP now,
* use them here. We'll ultimately need some generic support here,
* but the generic code isn't quite ready for that yet
*/
pmac_suspend_agp_for_card(pdev);
#endif /* CONFIG_PPC_PMAC */
/* If we support wakeup from poweroff, we save all regs we can including cfg
* space
*/
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(pdev, i * 4, &rinfo->cfg_save[i]);
pci_disable_device(pdev);
}
/* 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);
release_console_sem();
done:
pdev->dev.power.power_state = state;
return 0;
}
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;
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);
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 */
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);
/* Refresh */
fb_set_suspend(info, 0);
/* Unblank */
rinfo->lock_blank = 0;
radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 1);
#ifdef CONFIG_PPC_PMAC
/* On powermac, we have hooks to properly suspend/resume AGP now,
* use them here. We'll ultimately need some generic support here,
* but the generic code isn't quite ready for that yet
*/
pmac_resume_agp_for_card(pdev);
#endif /* CONFIG_PPC_PMAC */
/* 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 = PMSG_ON;
bail:
release_console_sem();
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;
}
/* 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
}